feat: init

This commit is contained in:
EdgeOne Pages
2025-12-31 17:08:26 +08:00
commit d22628f972
19 changed files with 7647 additions and 0 deletions

40
.gitignore vendored Normal file
View File

@@ -0,0 +1,40 @@
# prod
dist/
# dev
.yarn/
!.yarn/releases
.vscode/*
!.vscode/launch.json
!.vscode/*.code-snippets
.idea/workspace.xml
.idea/usage.statistics.xml
.idea/shelf
# deps
node_modules/
.wrangler
# env
.env
.env.production
.dev.vars
# logs
logs/
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
# misc
.DS_Store
# Tencent Cloud EdgeOne
.env
.edgeone
.edgeone/*
.tef_dist/*

3
.prettierrc Normal file
View File

@@ -0,0 +1,3 @@
{
"singleQuote": true
}

307
README.md Normal file
View File

@@ -0,0 +1,307 @@
# EdgeOne Pages Hono Application
This is a modern Web application built on the [Hono](https://hono.dev/) framework, deployed on the EdgeOne Pages platform.
Live demo: https://hono-template.edgeone.app
## Deploy
[![Deploy with EdgeOne Pages](https://cdnstatic.tencentcs.com/edgeone/pages/deploy.svg)](https://edgeone.ai/pages/new?from=github&template=hono)
## 🚀 Project Features
- **Modular Route Design** - Clear route organization structure
- **Server-Side Rendering** - Page rendering using JSX and HTML templates
- **File Upload** - File upload functionality support
- **Book Management** - Example CRUD operations
- **Error Handling** - Beautiful 404 and 500 error pages
- **TypeScript Support** - Complete type definitions
## 📁 Project Structure
```
functions/
├── index.tsx # Main entry file
├── [[default]].ts # EdgeOne Functions default route
├── env.ts # Environment type definitions
├── components/ # Components directory
│ └── Layout.tsx # Page layout component
└── routers/ # Route modules
├── index.ts # Unified route exports
├── book.tsx # Book related routes
├── ssr.tsx # Server-side rendering routes
└── upload.ts # File upload routes
```
## 🛣️ Route Details
### Static Routes
| Path | Method | Description |
|------|------|------|
| `/` | GET | Static home page, serves `index.html` from public directory |
**Examples:**
- `https://hono.edgeone.app/` - Static home page
### SSR Routes (`/ssr`)
| Path | Method | Description |
|------|------|------|
| `/ssr/:name` | GET | Dynamic SSR page, displays personalized welcome message |
**Examples:**
- `https://hono.edgeone.app/ssr/john` - Shows "Hello john!" page
### Book Management Routes (`/book`)
| Path | Method | Description |
|------|------|------|
| `/book` | GET | Get all books list page |
| `/book/:id` | GET | Get specific book details page |
| `/book` | POST | Create new book (API endpoint) |
**Examples:**
- `https://hono.edgeone.app/book` - Book list
- `https://hono.edgeone.app/book/1` - Details of the first book
**Create Book API Request Example:**
```bash
curl -X POST https://hono.edgeone.app/book \
-H "Content-Type: application/json" \
-d '{
"title": "New Book Title",
"author": "Author Name"
}'
```
**Supported Features:**
- CORS cross-origin support
### File Upload Routes (`/upload`)
| Path | Method | Description |
|------|------|------|
| `/upload` | POST | File upload endpoint |
**Example:**
```bash
curl -X POST https://hono.edgeone.app/upload \
-F "file=@example.txt"
```
## 📖 Detailed API Documentation
### Basic Information
- **Base URL**: `https://hono.edgeone.app`
- **Content-Type**: `application/json`
- **Encoding**: UTF-8
### API Details
#### 1. File Upload
**Endpoint**: `POST /upload`
**Description**: Upload files to server
**Request Format**: `multipart/form-data`
**Request Parameters**:
- `file` (required): File to upload
**curl Request Examples**:
```bash
# Upload text file
curl -X POST https://hono.edgeone.app/upload \
-F "file=@/path/to/your/file.txt"
# Upload image file
curl -X POST https://hono.edgeone.app/upload \
-F "file=@/path/to/image.jpg"
# Upload with custom filename
curl -X POST https://hono.edgeone.app/upload \
-F "file=@document.pdf;filename=my-document.pdf"
```
**Response Example**:
```json
{
"success": true,
"message": "File uploaded successfully",
"fileName": "file.txt"
}
```
**Error Response**:
```json
{
"success": false,
"message": "No file provided"
}
```
#### 2. Create Book
**Endpoint**: `POST /book`
**Description**: Create new book record
**Request Parameters**:
```json
{
"title": "Book Title",
"author": "Author Name"
}
```
**Parameter Description**:
- `title` (optional): Book title, defaults to "Untitled"
- `author` (optional): Author name, defaults to "Unknown"
**curl Request Examples**:
```bash
# Create book with complete information
curl -X POST https://hono.edgeone.app/book \
-H "Content-Type: application/json" \
-d '{
"title": "Dream of the Red Chamber",
"author": "Cao Xueqin"
}'
# Create book with only title
curl -X POST https://hono.edgeone.app/book \
-H "Content-Type: application/json" \
-d '{
"title": "New Book Title"
}'
# Create empty book (using defaults)
curl -X POST https://hono.edgeone.app/book \
-H "Content-Type: application/json" \
-d '{}'
```
**Response Example**:
```json
{
"success": true,
"message": "Book created successfully",
"book": {
"id": "abc123def",
"title": "Book Title",
"author": "Author Name",
"createdAt": "2023-12-01T10:00:00.000Z"
}
}
```
#### 3. Get Book Information
**curl Request Examples**:
```bash
# Get all books list
curl -X GET https://hono.edgeone.app/book
# Get specific book details
curl -X GET https://hono.edgeone.app/book/1
# Get personal page
curl -X GET https://hono.edgeone.app/john
```
### Error Code Description
| Error Code | HTTP Status Code | Description |
|-----------|-------------|------|
| `VALIDATION_ERROR` | 400 | Request parameter validation failed |
| `FILE_UPLOAD_ERROR` | 400 | File upload failed |
| `NOT_FOUND` | 404 | Resource not found |
| `INTERNAL_ERROR` | 500 | Internal server error |
### Rate Limiting
- All API endpoints currently have no rate limiting
- Client-side request frequency control is recommended
### CORS Support
All API endpoints support cross-origin access, response headers include:
- `Access-Control-Allow-Origin: *`
- `Access-Control-Allow-Methods: POST, GET, OPTIONS`
- `Access-Control-Allow-Headers: Content-Type, Authorization`
## 🔧 Development
### Local Development
```bash
# Install dependencies
npm install
# Start development server
edgeone pages dev
```
## 🌐 Environment Variables
The project uses the following environment variables and global objects:
- `my_kv` - KV storage instance for data persistence
## 🛡️ Security Features
### IP Restriction (Optional)
The project includes IP restriction middleware configuration (commented by default), which can limit access sources:
```typescript
app.use('*', ipRestriction(/* configuration */));
```
## 📝 API Response Format
### Success Response
```json
{
"success": true,
"message": "Operation successful",
"data": {}
}
```
### Error Response
```json
{
"error": "ERROR_CODE",
"message": "Error description"
}
```
## 🎨 UI Design
The project adopts modern UI design:
- Responsive layout
- System font stack
- Card-style design
- Unified color theme
- Elegant error pages
## 📦 Dependencies
- **hono** - Web framework
- **@edgeone/ef-types** - EdgeOne Functions type definitions
- **edgeone** - EdgeOne CLI tool
## 🤝 Contributing
Welcome to submit Issues and Pull Requests to improve this project.
## 📄 License
MIT License

306
README_zh-CN.md Normal file
View File

@@ -0,0 +1,306 @@
# EdgeOne Pages Hono 应用程序
这是一个基于 [Hono](https://hono.dev/) 框架构建的现代 Web 应用程序,部署在 EdgeOne Pages 平台上。
在线演示https://hono.edgeone.site
## 部署
[![使用 EdgeOne Pages 部署](https://cdnstatic.tencentcs.com/edgeone/pages/deploy.svg)](https://console.cloud.tencent.com/edgeone/pages/new?template=hono)
## 🚀 项目特性
- **模块化路由设计** - 清晰的路由组织结构
- **服务端渲染** - 使用 JSX 和 HTML 模板进行页面渲染
- **文件上传** - 文件上传功能支持
- **图书管理** - 示例 CRUD 操作
- **错误处理** - 精美的 404 和 500 错误页面
- **TypeScript 支持** - 完整的类型定义
## 📁 项目结构
```
functions/
├── index.tsx # 主入口文件
├── [[default]].ts # EdgeOne Functions 默认路由
├── env.ts # 环境类型定义
├── components/ # 组件目录
│ └── Layout.tsx # 页面布局组件
└── routers/ # 路由模块
├── index.ts # 统一路由导出
├── book.tsx # 图书相关路由
├── ssr.tsx # 服务端渲染路由
└── upload.ts # 文件上传路由
```
## 🛣️ 路由详情
### 静态路由
| 路径 | 方法 | 描述 |
|------|------|------|
| `/` | GET | 静态首页,从 public 目录提供 `index.html` |
**示例:**
- `https://hono.edgeone.app/` - 静态首页
### SSR 路由 (`/ssr`)
| 路径 | 方法 | 描述 |
|------|------|------|
| `/ssr/:name` | GET | 动态 SSR 页面,显示个性化欢迎消息 |
**示例:**
- `https://hono.edgeone.app/ssr/john` - 显示 "Hello john!" 页面
### 图书管理路由 (`/book`)
| 路径 | 方法 | 描述 |
|------|------|------|
| `/book` | GET | 获取所有图书列表页面 |
| `/book/:id` | GET | 获取特定图书详情页面 |
| `/book` | POST | 创建新图书API 端点) |
**示例:**
- `https://hono.edgeone.app/book` - 图书列表
- `https://hono.edgeone.app/book/1` - 第一本书的详情
**创建图书 API 请求示例:**
```bash
curl -X POST https://hono.edgeone.app/book \
-H "Content-Type: application/json" \
-d '{
"title": "新书标题",
"author": "作者姓名"
}'
```
**支持的功能:**
- CORS 跨域支持
### 文件上传路由 (`/upload`)
| 路径 | 方法 | 描述 |
|------|------|------|
| `/upload` | POST | 文件上传端点 |
**示例:**
```bash
curl -X POST https://hono.edgeone.app/upload \
-F "file=@example.txt"
```
## 📖 详细 API 文档
### 基本信息
- **Base URL**: `https://hono.edgeone.app`
- **Content-Type**: `application/json`
- **编码**: UTF-8
### API 详情
#### 1. 文件上传
**端点**: `POST /upload`
**描述**: 上传文件到服务器
**请求格式**: `multipart/form-data`
**请求参数**:
- `file` (必需): 要上传的文件
**curl 请求示例**:
```bash
# 上传文本文件
curl -X POST https://hono.edgeone.app/upload \
-F "file=@/path/to/your/file.txt"
# 上传图片文件
curl -X POST https://hono.edgeone.app/upload \
-F "file=@/path/to/image.jpg"
# 上传并自定义文件名
curl -X POST https://hono.edgeone.app/upload \
-F "file=@document.pdf;filename=my-document.pdf"
```
**响应示例**:
```json
{
"success": true,
"message": "文件上传成功",
"fileName": "file.txt"
}
```
**错误响应**:
```json
{
"success": false,
"message": "未提供文件"
}
```
#### 2. 创建图书
**端点**: `POST /book`
**描述**: 创建新的图书记录
**请求参数**:
```json
{
"title": "图书标题",
"author": "作者姓名"
}
```
**参数说明**:
- `title` (可选): 图书标题,默认为 "Untitled"
- `author` (可选): 作者姓名,默认为 "Unknown"
**curl 请求示例**:
```bash
# 创建包含完整信息的图书
curl -X POST https://hono.edgeone.app/book \
-H "Content-Type: application/json" \
-d '{
"title": "红楼梦",
"author": "曹雪芹"
}'
# 只创建标题的图书
curl -X POST https://hono.edgeone.app/book \
-H "Content-Type: application/json" \
-d '{
"title": "新书标题"
}'
# 创建空图书(使用默认值)
curl -X POST https://hono.edgeone.app/book \
-H "Content-Type: application/json" \
-d '{}'
```
**响应示例**:
```json
{
"success": true,
"message": "图书创建成功",
"book": {
"id": "abc123def",
"title": "图书标题",
"author": "作者姓名",
"createdAt": "2023-12-01T10:00:00.000Z"
}
}
```
#### 3. 获取图书信息
**curl 请求示例**:
```bash
# 获取所有图书列表
curl -X GET https://hono.edgeone.app/book
# 获取特定图书详情
curl -X GET https://hono.edgeone.app/book/1
# 获取个人页面
curl -X GET https://hono.edgeone.app/john
```
### 错误码说明
| 错误码 | HTTP 状态码 | 描述 |
|-----------|-------------|------|
| `VALIDATION_ERROR` | 400 | 请求参数验证失败 |
| `FILE_UPLOAD_ERROR` | 400 | 文件上传失败 |
| `NOT_FOUND` | 404 | 资源未找到 |
| `INTERNAL_ERROR` | 500 | 内部服务器错误 |
### 频率限制
- 目前所有 API 端点均无频率限制
- 建议客户端进行请求频率控制
### CORS 支持
所有 API 端点均支持跨域访问,响应头包含:
- `Access-Control-Allow-Origin: *`
- `Access-Control-Allow-Methods: POST, GET, OPTIONS`
- `Access-Control-Allow-Headers: Content-Type, Authorization`
## 🔧 开发
### 本地开发
```bash
# 安装依赖
npm install
# 启动开发服务器
edgeone pages dev
```
## 🌐 环境变量
项目使用以下环境变量和全局对象:
- `my_kv` - KV 存储实例,用于数据持久化
## 🛡️ 安全特性
### IP 限制(可选)
项目包含 IP 限制中间件配置(默认注释),可以限制访问来源:
```typescript
app.use('*', ipRestriction(/* 配置 */));
```
## 📝 API 响应格式
### 成功响应
```json
{
"success": true,
"message": "操作成功",
"data": {}
}
```
### 错误响应
```json
{
"error": "ERROR_CODE",
"message": "错误描述"
}
```
## 🎨 UI 设计
项目采用现代化 UI 设计:
- 响应式布局
- 系统字体栈
- 卡片式设计
- 统一色彩主题
- 优雅的错误页面
## 📦 依赖
- **hono** - Web 框架
- **@edgeone/ef-types** - EdgeOne Functions 类型定义
- **edgeone** - EdgeOne CLI 工具
## 🤝 贡献
欢迎提交 Issues 和 Pull Requests 来改进这个项目。
## <20><> 许可证
MIT License

1
functions/[[default]].ts Normal file
View File

@@ -0,0 +1 @@
export { onRequest } from './index';

View File

@@ -0,0 +1,138 @@
import { html } from 'hono/html';
export interface SiteData {
title: string;
children?: any;
}
export const Layout = (props: SiteData) =>
html`
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>${props.title}</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
sans-serif;
line-height: 1.6;
margin: 0;
padding: 20px;
background-color: #f5f5f5;
}
.container {
max-width: 800px;
margin: 0 auto;
background: white;
padding: 40px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
h1 {
color: #333;
border-bottom: 2px solid #007acc;
padding-bottom: 10px;
}
.nav-links {
margin: 20px 0;
}
.nav-links ul {
list-style: none;
padding: 0;
}
.nav-links li {
margin: 10px 0;
}
.nav-links a {
color: #007acc;
text-decoration: none;
padding: 8px 16px;
border-radius: 4px;
transition: background-color 0.2s;
}
.nav-links a:hover {
background-color: #f0f8ff;
}
.back-link {
margin-top: 30px;
padding-top: 20px;
border-top: 1px solid #eee;
}
.back-link a {
color: #666;
text-decoration: none;
}
.back-link a:hover {
color: #007acc;
}
pre {
background-color: #f8f9fa;
border: 1px solid #e9ecef;
border-radius: 4px;
padding: 16px;
overflow-x: auto;
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
font-size: 14px;
line-height: 1.4;
}
code {
background-color: #f8f9fa;
padding: 2px 4px;
border-radius: 3px;
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
font-size: 0.9em;
}
pre code {
background-color: transparent;
padding: 0;
}
h2 {
color: #2c3e50;
margin-top: 32px;
margin-bottom: 16px;
border-bottom: 1px solid #eee;
padding-bottom: 8px;
}
h3 {
color: #34495e;
margin-top: 24px;
margin-bottom: 12px;
}
ul li {
margin-bottom: 8px;
}
strong {
color: #2c3e50;
}
.highlight-box {
background-color: #f8f9fa;
border: 1px solid #e9ecef;
border-radius: 6px;
padding: 20px;
margin: 20px 0;
}
.highlight-box h3 {
margin-top: 0;
color: #495057;
}
</style>
</head>
<body>
<div class="container">${props.children}</div>
</body>
</html>
`;
export const Content = (props: { siteData: SiteData; name: string; children?: any }) => (
<Layout {...props.siteData}>
<h1>{props.name}</h1>
{props.children || (
<p>
Welcome to our Hono application. This page was rendered server-side with
JSX.
</p>
)}
</Layout>
);

26
functions/env.ts Normal file
View File

@@ -0,0 +1,26 @@
interface ListResult {
complete: boolean;
cursor: string;
keys: Array<ListKey>;
}
interface ListKey {
key: string;
}
declare class KVNamespace {
put(
key: string,
value: string | ArrayBuffer | ArrayBufferView | ReadableStream
): Promise<void>;
get(
key: string,
object?: { type: string }
): Promise<string | object | ArrayBuffer | ReadableStream>;
delete(key: string): Promise<void>;
list(config: {
prefix?: string;
limit?: number;
cursor?: string;
}): Promise<ListResult>;
}
export type { KVNamespace };

182
functions/index.tsx Normal file
View File

@@ -0,0 +1,182 @@
import { Context, Hono } from 'hono';
// import { ipRestriction } from 'hono/ip-restriction';
import type { KVNamespace } from './env';
import { book, upload, ssr } from './routers/index';
declare global {
let my_kv: KVNamespace;
}
const app = new Hono().basePath('/');
// Register route modules
app.route('/book', book);
app.route('/upload', upload);
app.route('/ssr', ssr);
// IP restriction middleware (optional)
// app.use(
// '*',
// ipRestriction(
// c => ({
// remote: {
// // @ts-expect-error
// address: c.req.raw.eo.clientIp,
// addressType:
// String(
// // @ts-expect-error
// c.req.raw.eo.clientIp
// ).indexOf('::') === -1
// ? 'IPv4'
// : 'IPv6'
// }
// }),
// {
// denyList: [],
// allowList: [ '127.0.0.1', '::1']
// }
// )
// );
const notFound = async (c: Context) => {
return c.html(
`
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>404 - Page Not Found</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
line-height: 1.6;
margin: 0;
padding: 20px;
background-color: #f5f5f5;
text-align: center;
}
.container {
max-width: 600px;
margin: 100px auto;
background: white;
padding: 40px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
h1 {
color: #e74c3c;
font-size: 72px;
margin: 0;
}
h2 {
color: #333;
margin: 20px 0;
}
</style>
</head>
<body>
<div class="container">
<h1>404</h1>
<h2>Page Not Found</h2>
<p>The page you are looking for might have been removed, had its name changed, or is temporarily unavailable.</p>
<p><a href="/">← Go back to home</a></p>
</div>
</body>
</html>
`,
404
);
};
// Fallback to static directory
app.notFound(async (c) => {
const url = new URL(c.req.url);
if (url.pathname === '/') {
url.pathname = '/index.html';
}
try {
const res = await fetch(url.toString(), {
headers: c.req.header()
});
if (res.ok) {
const contentType = res.headers.get('Content-Type')!;
const body = await res.arrayBuffer();
return new Response(body, {
status: res.status,
headers: {
'Content-Type': contentType,
'Cache-Control': 'public, max-age=3600',
},
});
}
} catch (error) {
return notFound(c);
}
return notFound(c);
});
app.onError((err, c) => {
return c.html(
`
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>500 - Internal Server Error</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
line-height: 1.6;
margin: 0;
padding: 20px;
background-color: #f5f5f5;
text-align: center;
}
.container {
max-width: 600px;
margin: 100px auto;
background: white;
padding: 40px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
h1 {
color: #e74c3c;
font-size: 72px;
margin: 0;
}
h2 {
color: #333;
margin: 20px 0;
}
</style>
</head>
<body>
<div class="container">
<h1>500</h1>
<h2>Internal Server Error</h2>
<p>Something went wrong on our server. Please try again later.</p>
<p>Error: ${err.message}</p>
<p><a href="/">← Go back to home</a></p>
</div>
</body>
</html>
`,
500
);
});
// EdgeOne Functions export
export function onRequest(context: {
request: Request;
params: Record<string, string>;
env: Record<string, any>;
}): Response | Promise<Response> {
return app.fetch(context.request, context.env);
}

105
functions/routers/book.tsx Normal file
View File

@@ -0,0 +1,105 @@
import { Hono } from 'hono';
import { Content } from '../components/Layout';
const book = new Hono();
// Get all books list
book.get('/', (c) => {
const props = {
name: '📚 Book Library',
siteData: {
title: 'Books - Hono App',
},
children: (
<div>
<p>Browse our collection of books</p>
<div style="margin: 20px 0;">
<div style="padding: 15px; margin: 10px 0; background: #f8f9fa; border-radius: 5px; border-left: 4px solid #007acc;">
<h3><a href="/book/1">The Art of Programming</a></h3>
<p>A comprehensive guide to software development</p>
</div>
<div style="padding: 15px; margin: 10px 0; background: #f8f9fa; border-radius: 5px; border-left: 4px solid #007acc;">
<h3><a href="/book/2">JavaScript: The Good Parts</a></h3>
<p>Essential JavaScript programming techniques</p>
</div>
<div style="padding: 15px; margin: 10px 0; background: #f8f9fa; border-radius: 5px; border-left: 4px solid #007acc;">
<h3><a href="/book/3">Clean Code</a></h3>
<p>Writing maintainable and readable code</p>
</div>
</div>
<div class="back-link">
<a href="/"> Back to home</a>
</div>
</div>
),
};
return c.html(Content(props));
});
// Get specific book
book.get('/:id', (c) => {
const id = c.req.param('id');
const books: Record<string, any> = {
'1': { title: 'The Art of Programming', author: 'John Doe', description: 'A comprehensive guide to software development principles and practices.' },
'2': { title: 'JavaScript: The Good Parts', author: 'Douglas Crockford', description: 'Essential JavaScript programming techniques and best practices.' },
'3': { title: 'Clean Code', author: 'Robert C. Martin', description: 'A handbook of agile software craftsmanship.' }
};
const bookData = books[id];
if (!bookData) {
const props = {
name: 'Book Not Found',
siteData: {
title: 'Book Not Found',
},
children: (
<div>
<p>The book with ID {id} was not found.</p>
<div class="back-link">
<a href="/book"> Back to books</a>
</div>
</div>
),
};
return c.html(Content(props), 404);
}
const props = {
name: `📖 ${bookData.title}`,
siteData: {
title: `${bookData.title} - Book Details`,
},
children: (
<div>
<div style="background: #f8f9fa; padding: 20px; border-radius: 5px; margin: 20px 0;">
<p><strong>Author:</strong> {bookData.author}</p>
<p><strong>Book ID:</strong> {id}</p>
<p><strong>Description:</strong> {bookData.description}</p>
</div>
<div class="back-link">
<a href="/book"> Back to books</a>
</div>
</div>
),
};
return c.html(Content(props));
});
// Create new book (API endpoint)
book.post('/', async (c) => {
const body = await c.req.json();
return c.json({
success: true,
message: 'Book created successfully',
book: {
id: Math.random().toString(36).substr(2, 9),
title: body.title || 'Untitled',
author: body.author || 'Unknown',
createdAt: new Date().toISOString()
}
});
});
export default book;

View File

@@ -0,0 +1,3 @@
export { default as book } from './book';
export { default as upload } from './upload';
export { default as ssr } from './ssr';

18
functions/routers/ssr.tsx Normal file
View File

@@ -0,0 +1,18 @@
import { Hono } from 'hono';
import { Layout, Content } from '../components/Layout';
const ssr = new Hono();
// Dynamic page route
ssr.get('/:name', (c) => {
const { name } = c.req.param();
const props = {
name: name,
siteData: {
title: `Hello ${name} - JSX Sample`,
},
};
return c.html(<Content {...props} />);
});
export default ssr;

View File

@@ -0,0 +1,17 @@
import { Hono } from 'hono';
const upload = new Hono();
// File upload endpoint
upload.post('/', async (c) => {
const body = await c.req.parseBody();
console.log(body['file'], 'File uploaded');
return c.json({
success: true,
message: 'File uploaded successfully',
fileName: body['file']
});
});
export default upload;

5755
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

18
package.json Normal file
View File

@@ -0,0 +1,18 @@
{
"name": "edgeone-pages-hono-app",
"scripts": {
"deploy": "rm -rf ./node_modules && edgeone pages deploy ./ -n edgeone-hono-app -t ",
"sync-docs": "node scripts/sync-docs.js",
"build": "npm run sync-docs"
},
"dependencies": {
"highlight.js": "^11.11.1",
"hono": "^4.7.10",
"marked": "^15.0.12",
"marked-highlight": "^2.2.1"
},
"devDependencies": {
"@edgeone/ef-types": "^1.0.5",
"edgeone": "^1.0.21"
}
}

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

345
public/index.html Normal file
View File

@@ -0,0 +1,345 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>EdgeOne Pages Hono Application</title>
<link rel="stylesheet" href="./style.css">
<link rel="icon" href="./favicon.ico" type="image/x-icon">
</head>
<body>
<div class="container">
<h1>EdgeOne Pages Hono Application</h1>
<p>This is a modern Web application built on the <a href="https://hono.dev/" target="_blank" rel="noopener noreferrer">Hono</a> framework, deployed on the EdgeOne Pages platform.</p>
<p>Live demo: <a href="https://hono.edgeone.app" target="_blank" rel="noopener noreferrer">https://hono.edgeone.app</a></p>
<h2>Deploy</h2>
<p><a href="https://edgeone.ai/pages/new?from=github&template=hono" target="_blank" rel="noopener noreferrer"><img src="https://cdnstatic.tencentcs.com/edgeone/pages/deploy.svg" alt="Deploy with EdgeOne Pages" /></a></p>
<h2>🚀 Project Features</h2>
<ul>
<li><strong>Modular Route Design</strong> - Clear route organization structure</li>
<li><strong>Server-Side Rendering</strong> - Page rendering using JSX and HTML templates</li>
<li><strong>File Upload</strong> - File upload functionality support</li>
<li><strong>Book Management</strong> - Example CRUD operations</li>
<li><strong>Error Handling</strong> - Beautiful 404 and 500 error pages</li>
<li><strong>TypeScript Support</strong> - Complete type definitions</li>
</ul>
<h2>📁 Project Structure</h2>
<pre><code>functions/
├── index.tsx # Main entry file
├── [[default]].ts # EdgeOne Functions default route
├── env.ts # Environment type definitions
├── components/ # Components directory
│ └── Layout.tsx # Page layout component
└── routers/ # Route modules
├── index.ts # Unified route exports
├── book.tsx # Book related routes
├── ssr.tsx # Server-side rendering routes
└── upload.ts # File upload routes
</code></pre>
<h2>🛣️ Route Details</h2>
<h3>Static Routes</h3>
<table>
<thead>
<tr>
<th>Path</th>
<th>Method</th>
<th>Description</th>
</tr>
</thead>
<tbody><tr>
<td><code>/</code></td>
<td>GET</td>
<td>Static home page, serves <code>index.html</code> from public directory</td>
</tr>
</tbody></table>
<p><strong>Examples:</strong></p>
<ul>
<li><code>https://hono.edgeone.app/</code> - Static home page</li>
</ul>
<h3>SSR Routes (<code>/ssr</code>)</h3>
<table>
<thead>
<tr>
<th>Path</th>
<th>Method</th>
<th>Description</th>
</tr>
</thead>
<tbody><tr>
<td><code>/ssr/:name</code></td>
<td>GET</td>
<td>Dynamic SSR page, displays personalized welcome message</td>
</tr>
</tbody></table>
<p><strong>Examples:</strong></p>
<ul>
<li><code>https://hono.edgeone.app/ssr/john</code> - Shows &quot;Hello john!&quot; page</li>
</ul>
<h3>Book Management Routes (<code>/book</code>)</h3>
<table>
<thead>
<tr>
<th>Path</th>
<th>Method</th>
<th>Description</th>
</tr>
</thead>
<tbody><tr>
<td><code>/book</code></td>
<td>GET</td>
<td>Get all books list page</td>
</tr>
<tr>
<td><code>/book/:id</code></td>
<td>GET</td>
<td>Get specific book details page</td>
</tr>
<tr>
<td><code>/book</code></td>
<td>POST</td>
<td>Create new book (API endpoint)</td>
</tr>
</tbody></table>
<p><strong>Examples:</strong></p>
<ul>
<li><code>https://hono.edgeone.app/book</code> - Book list</li>
<li><code>https://hono.edgeone.app/book/1</code> - Details of the first book</li>
</ul>
<p><strong>Create Book API Request Example:</strong></p>
<pre><code class="language-bash">curl -X POST https://hono.edgeone.app/book \
-H <span class="hljs-string">&quot;Content-Type: application/json&quot;</span> \
-d <span class="hljs-string">&#x27;{
&quot;title&quot;: &quot;New Book Title&quot;,
&quot;author&quot;: &quot;Author Name&quot;
}&#x27;</span>
</code></pre>
<p><strong>Supported Features:</strong></p>
<ul>
<li>CORS cross-origin support</li>
</ul>
<h3>File Upload Routes (<code>/upload</code>)</h3>
<table>
<thead>
<tr>
<th>Path</th>
<th>Method</th>
<th>Description</th>
</tr>
</thead>
<tbody><tr>
<td><code>/upload</code></td>
<td>POST</td>
<td>File upload endpoint</td>
</tr>
</tbody></table>
<p><strong>Example:</strong></p>
<pre><code class="language-bash">curl -X POST https://hono.edgeone.app/upload \
-F <span class="hljs-string">&quot;file=@example.txt&quot;</span>
</code></pre>
<h2>📖 Detailed API Documentation</h2>
<h3>Basic Information</h3>
<ul>
<li><strong>Base URL</strong>: <code>https://hono.edgeone.app</code></li>
<li><strong>Content-Type</strong>: <code>application/json</code></li>
<li><strong>Encoding</strong>: UTF-8</li>
</ul>
<h3>API Details</h3>
<h4>1. File Upload</h4>
<p><strong>Endpoint</strong>: <code>POST /upload</code></p>
<p><strong>Description</strong>: Upload files to server</p>
<p><strong>Request Format</strong>: <code>multipart/form-data</code></p>
<p><strong>Request Parameters</strong>:</p>
<ul>
<li><code>file</code> (required): File to upload</li>
</ul>
<p><strong>curl Request Examples</strong>:</p>
<pre><code class="language-bash"><span class="hljs-comment"># Upload text file</span>
curl -X POST https://hono.edgeone.app/upload \
-F <span class="hljs-string">&quot;file=@/path/to/your/file.txt&quot;</span>
<span class="hljs-comment"># Upload image file</span>
curl -X POST https://hono.edgeone.app/upload \
-F <span class="hljs-string">&quot;file=@/path/to/image.jpg&quot;</span>
<span class="hljs-comment"># Upload with custom filename</span>
curl -X POST https://hono.edgeone.app/upload \
-F <span class="hljs-string">&quot;file=@document.pdf;filename=my-document.pdf&quot;</span>
</code></pre>
<p><strong>Response Example</strong>:</p>
<pre><code class="language-json"><span class="hljs-punctuation">{</span>
<span class="hljs-attr">&quot;success&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">&quot;message&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;File uploaded successfully&quot;</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">&quot;fileName&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;file.txt&quot;</span>
<span class="hljs-punctuation">}</span>
</code></pre>
<p><strong>Error Response</strong>:</p>
<pre><code class="language-json"><span class="hljs-punctuation">{</span>
<span class="hljs-attr">&quot;success&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">false</span></span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">&quot;message&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;No file provided&quot;</span>
<span class="hljs-punctuation">}</span>
</code></pre>
<h4>2. Create Book</h4>
<p><strong>Endpoint</strong>: <code>POST /book</code></p>
<p><strong>Description</strong>: Create new book record</p>
<p><strong>Request Parameters</strong>:</p>
<pre><code class="language-json"><span class="hljs-punctuation">{</span>
<span class="hljs-attr">&quot;title&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;Book Title&quot;</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">&quot;author&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;Author Name&quot;</span>
<span class="hljs-punctuation">}</span>
</code></pre>
<p><strong>Parameter Description</strong>:</p>
<ul>
<li><code>title</code> (optional): Book title, defaults to &quot;Untitled&quot;</li>
<li><code>author</code> (optional): Author name, defaults to &quot;Unknown&quot;</li>
</ul>
<p><strong>curl Request Examples</strong>:</p>
<pre><code class="language-bash"><span class="hljs-comment"># Create book with complete information</span>
curl -X POST https://hono.edgeone.app/book \
-H <span class="hljs-string">&quot;Content-Type: application/json&quot;</span> \
-d <span class="hljs-string">&#x27;{
&quot;title&quot;: &quot;Dream of the Red Chamber&quot;,
&quot;author&quot;: &quot;Cao Xueqin&quot;
}&#x27;</span>
<span class="hljs-comment"># Create book with only title</span>
curl -X POST https://hono.edgeone.app/book \
-H <span class="hljs-string">&quot;Content-Type: application/json&quot;</span> \
-d <span class="hljs-string">&#x27;{
&quot;title&quot;: &quot;New Book Title&quot;
}&#x27;</span>
<span class="hljs-comment"># Create empty book (using defaults)</span>
curl -X POST https://hono.edgeone.app/book \
-H <span class="hljs-string">&quot;Content-Type: application/json&quot;</span> \
-d <span class="hljs-string">&#x27;{}&#x27;</span>
</code></pre>
<p><strong>Response Example</strong>:</p>
<pre><code class="language-json"><span class="hljs-punctuation">{</span>
<span class="hljs-attr">&quot;success&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">&quot;message&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;Book created successfully&quot;</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">&quot;book&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span>
<span class="hljs-attr">&quot;id&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;abc123def&quot;</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">&quot;title&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;Book Title&quot;</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">&quot;author&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;Author Name&quot;</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">&quot;createdAt&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;2023-12-01T10:00:00.000Z&quot;</span>
<span class="hljs-punctuation">}</span>
<span class="hljs-punctuation">}</span>
</code></pre>
<h4>3. Get Book Information</h4>
<p><strong>curl Request Examples</strong>:</p>
<pre><code class="language-bash"><span class="hljs-comment"># Get all books list</span>
curl -X GET https://hono.edgeone.app/book
<span class="hljs-comment"># Get specific book details</span>
curl -X GET https://hono.edgeone.app/book/1
<span class="hljs-comment"># Get personal page</span>
curl -X GET https://hono.edgeone.app/john
</code></pre>
<h3>Error Code Description</h3>
<table>
<thead>
<tr>
<th>Error Code</th>
<th>HTTP Status Code</th>
<th>Description</th>
</tr>
</thead>
<tbody><tr>
<td><code>VALIDATION_ERROR</code></td>
<td>400</td>
<td>Request parameter validation failed</td>
</tr>
<tr>
<td><code>FILE_UPLOAD_ERROR</code></td>
<td>400</td>
<td>File upload failed</td>
</tr>
<tr>
<td><code>NOT_FOUND</code></td>
<td>404</td>
<td>Resource not found</td>
</tr>
<tr>
<td><code>INTERNAL_ERROR</code></td>
<td>500</td>
<td>Internal server error</td>
</tr>
</tbody></table>
<h3>Rate Limiting</h3>
<ul>
<li>All API endpoints currently have no rate limiting</li>
<li>Client-side request frequency control is recommended</li>
</ul>
<h3>CORS Support</h3>
<p>All API endpoints support cross-origin access, response headers include:</p>
<ul>
<li><code>Access-Control-Allow-Origin: *</code></li>
<li><code>Access-Control-Allow-Methods: POST, GET, OPTIONS</code></li>
<li><code>Access-Control-Allow-Headers: Content-Type, Authorization</code></li>
</ul>
<h2>🔧 Development</h2>
<h3>Local Development</h3>
<pre><code class="language-bash"><span class="hljs-comment"># Install dependencies</span>
npm install
<span class="hljs-comment"># Start development server</span>
npm run dev
</code></pre>
<h3>Deployment</h3>
<pre><code class="language-bash"><span class="hljs-comment"># Deploy to EdgeOne</span>
npm run deploy
</code></pre>
<h2>🌐 Environment Variables</h2>
<p>The project uses the following environment variables and global objects:</p>
<ul>
<li><code>my_kv</code> - KV storage instance for data persistence</li>
</ul>
<h2>🛡️ Security Features</h2>
<h3>IP Restriction (Optional)</h3>
<p>The project includes IP restriction middleware configuration (commented by default), which can limit access sources:</p>
<pre><code class="language-typescript">app.<span class="hljs-title function_">use</span>(<span class="hljs-string">&#x27;*&#x27;</span>, <span class="hljs-title function_">ipRestriction</span>(<span class="hljs-comment">/* configuration */</span>));
</code></pre>
<h2>📝 API Response Format</h2>
<h3>Success Response</h3>
<pre><code class="language-json"><span class="hljs-punctuation">{</span>
<span class="hljs-attr">&quot;success&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">true</span></span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">&quot;message&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;Operation successful&quot;</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">&quot;data&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span><span class="hljs-punctuation">}</span>
<span class="hljs-punctuation">}</span>
</code></pre>
<h3>Error Response</h3>
<pre><code class="language-json"><span class="hljs-punctuation">{</span>
<span class="hljs-attr">&quot;error&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;ERROR_CODE&quot;</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">&quot;message&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;Error description&quot;</span>
<span class="hljs-punctuation">}</span>
</code></pre>
<h2>🎨 UI Design</h2>
<p>The project adopts modern UI design:</p>
<ul>
<li>Responsive layout</li>
<li>System font stack</li>
<li>Card-style design</li>
<li>Unified color theme</li>
<li>Elegant error pages</li>
</ul>
<h2>📦 Dependencies</h2>
<ul>
<li><strong>hono</strong> - Web framework</li>
<li><strong>@edgeone/ef-types</strong> - EdgeOne Functions type definitions</li>
<li><strong>edgeone</strong> - EdgeOne CLI tool</li>
</ul>
<h2>🤝 Contributing</h2>
<p>Welcome to submit Issues and Pull Requests to improve this project.</p>
<h2>📄 License</h2>
<p>MIT License</p>
<div class="highlight-box">
<h3>📝 Development Notes</h3>
<p>This is a complete example application demonstrating various features and best practices of the Hono framework on EdgeOne Functions.</p>
<p>The project structure is clear, code is well organized, and suitable as a starting template for other projects.</p>
</div>
</div>
</body>
</html>

244
public/style.css Normal file
View File

@@ -0,0 +1,244 @@
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto',
sans-serif;
line-height: 1.6;
color: #333;
max-width: 900px;
margin: 0 auto;
padding: 20px;
background-color: #f8fafc;
}
.container {
background: white;
padding: 40px;
border-radius: 12px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
border: 1px solid #e2e8f0;
}
h1 {
color: #1a202c;
border-bottom: 3px solid #3182ce;
padding-bottom: 15px;
margin-bottom: 30px;
font-size: 2.5rem;
font-weight: 700;
}
h2 {
color: #2d3748;
margin-top: 40px;
margin-bottom: 20px;
font-size: 1.8rem;
font-weight: 600;
border-bottom: 2px solid #e2e8f0;
padding-bottom: 8px;
}
h3 {
color: #4a5568;
margin-top: 30px;
margin-bottom: 15px;
font-size: 1.4rem;
font-weight: 600;
}
h4 {
color: #718096;
margin-top: 25px;
margin-bottom: 12px;
font-size: 1.2rem;
font-weight: 600;
}
a {
color: #3182ce;
text-decoration: none;
border-bottom: 1px solid transparent;
transition: all 0.2s ease;
}
a:hover {
color: #2c5282;
border-bottom-color: #3182ce;
}
ul, ol {
padding-left: 24px;
margin-bottom: 16px;
}
li {
margin-bottom: 8px;
line-height: 1.6;
}
p {
margin-bottom: 16px;
line-height: 1.7;
}
/* 表格样式 */
table {
width: 100%;
border-collapse: collapse;
margin: 20px 0;
background: white;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
th {
background: #4a5568;
color: white;
padding: 12px 16px;
text-align: left;
font-weight: 600;
font-size: 0.9rem;
text-transform: uppercase;
letter-spacing: 0.5px;
}
td {
padding: 12px 16px;
border-bottom: 1px solid #e2e8f0;
vertical-align: top;
}
tr:hover {
background-color: #f7fafc;
}
tr:last-child td {
border-bottom: none;
}
code {
background: #f1f5f9;
color: #e53e3e;
padding: 3px 6px;
border-radius: 4px;
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
font-size: 0.9em;
font-weight: 500;
}
pre {
background: #1a202c;
color: #e2e8f0;
padding: 20px;
border-radius: 8px;
overflow-x: auto;
margin: 20px 0;
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
font-size: 0.9rem;
line-height: 1.5;
border: 1px solid #2d3748;
}
pre code {
background: none;
color: inherit;
padding: 0;
border-radius: 0;
font-size: inherit;
}
.hljs {
background: #1a202c !important;
color: #e2e8f0 !important;
}
.highlight-box {
background: linear-gradient(135deg, #e6fffa 0%, #f0fff4 100%);
border-left: 4px solid #38a169;
padding: 24px;
margin: 30px 0;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(56, 161, 105, 0.1);
}
.highlight-box h3 {
color: #2f855a;
margin-top: 0;
margin-bottom: 12px;
}
.highlight-box p {
color: #2d3748;
margin-bottom: 8px;
}
.highlight-box p:last-child {
margin-bottom: 0;
}
img {
max-width: 100%;
height: auto;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
blockquote {
border-left: 4px solid #cbd5e0;
padding-left: 20px;
margin: 20px 0;
font-style: italic;
color: #4a5568;
background: #f7fafc;
padding: 16px 20px;
border-radius: 0 8px 8px 0;
}
@media (max-width: 768px) {
body {
padding: 10px;
}
.container {
padding: 20px;
}
h1 {
font-size: 2rem;
}
h2 {
font-size: 1.5rem;
}
table {
font-size: 0.9rem;
}
th, td {
padding: 8px 12px;
}
pre {
padding: 15px;
font-size: 0.8rem;
}
}
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 4px;
}
::-webkit-scrollbar-thumb {
background: #cbd5e0;
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: #a0aec0;
}

124
scripts/sync-docs.js Normal file
View File

@@ -0,0 +1,124 @@
#!/usr/bin/env node
const fs = require('fs');
const path = require('path');
const { marked } = require('marked');
const { markedHighlight } = require('marked-highlight');
const hljs = require('highlight.js');
marked.use(markedHighlight({
langPrefix: 'hljs language-',
highlight(code, lang) {
const language = hljs.getLanguage(lang) ? lang : 'plaintext';
return hljs.highlight(code, { language }).value;
}
}));
const renderer = new marked.Renderer();
renderer.link = function(token) {
const href = token.href;
const title = token.title;
const text = token.text;
const titleAttr = title ? ` title="${title}"` : '';
if (text.includes('<img') || text.match(/^!\[.*\]\(.*\)$/)) {
if (text.match(/^!\[(.*?)\]\((.*?)\)$/)) {
const imgMatch = text.match(/^!\[(.*?)\]\((.*?)\)$/);
const altText = imgMatch[1];
const imgSrc = imgMatch[2];
return `<a href="${href}"${titleAttr} target="_blank" rel="noopener noreferrer"><img src="${imgSrc}" alt="${altText}" /></a>`;
}
return `<a href="${href}"${titleAttr} target="_blank" rel="noopener noreferrer">${text}</a>`;
}
return `<a href="${href}"${titleAttr} target="_blank" rel="noopener noreferrer">${text}</a>`;
};
marked.setOptions({
breaks: false,
gfm: true,
headerIds: true,
mangle: false,
pedantic: false,
sanitize: false,
silent: false,
smartLists: true,
smartypants: false,
xhtml: false,
renderer: renderer
});
const readmePath = path.join(__dirname, '../README.md');
const readmeContent = fs.readFileSync(readmePath, 'utf-8');
function parseReadme(content) {
const titleMatch = content.match(/^# (.+)$/m);
const title = titleMatch ? titleMatch[1] : 'EdgeOne Pages Hono Application';
const descMatch = content.match(/This is a modern Web application[^\n]+/);
const description = descMatch ? descMatch[0] : '';
const liveDemoMatch = content.match(/Live demo: (.+)$/m);
const liveDemo = liveDemoMatch ? liveDemoMatch[0] : '';
const hasDeployButton = content.includes('Deploy with EdgeOne Pages');
const htmlContent = marked(content);
return {
title,
description,
liveDemo,
hasDeployButton,
htmlContent
};
}
function generateHTML(data) {
return `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>${data.title}</title>
<link rel="stylesheet" href="./style.css">
</head>
<body>
<div class="container">
${data.htmlContent}
<div class="highlight-box">
<h3>📝 Development Notes</h3>
<p>This is a complete example application demonstrating various features and best practices of the Hono framework on EdgeOne Functions.</p>
<p>The project structure is clear, code is well organized, and suitable as a starting template for other projects.</p>
</div>
</div>
</body>
</html>
`;
}
function main() {
try {
console.log('📖 Reading README.md...');
const parsedData = parseReadme(readmeContent);
console.log('🔄 Generating HTML with marked...');
const htmlContent = generateHTML(parsedData);
console.log('💾 Writing to public/index.html...');
const outputPath = path.join(__dirname, '../public/index.html');
fs.writeFileSync(outputPath, htmlContent, 'utf-8');
console.log('✅ Successfully converted README.md to HTML using marked');
} catch (error) {
console.error('❌ Error converting docs:', error.message);
process.exit(1);
}
}
if (require.main === module) {
main();
}
module.exports = { parseReadme, generateHTML };

15
tsconfig.json Normal file
View File

@@ -0,0 +1,15 @@
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "Bundler",
"strict": true,
"skipLibCheck": true,
"lib": [
"ESNext"
],
"jsx": "react-jsx",
"jsxImportSource": "hono/jsx",
"types": ["@edgeone/ef-types"]
},
}