Skip to content

Commit 760a869

Browse files
committed
chore: 2.5.0 add functions, fixed minor bugs
1 parent 3c31d25 commit 760a869

20 files changed

+1935
-33
lines changed

CHANGELOG.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,43 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [2.5.0] - 2025-08-03
9+
10+
### 🚀 Function Management System
11+
12+
### Added
13+
14+
- 🎯 **Function Management API** - Complete CRUD operations for serverless functions
15+
- `POST /admin-api/v2/functions` - Create new functions with code and permissions
16+
- `GET /admin-api/v2/functions` - List all functions
17+
- `GET /admin-api/v2/functions/{name}` - Get specific function details
18+
- `PUT /admin-api/v2/functions/{name}` - Update existing functions
19+
- `DELETE /admin-api/v2/functions/{name}` - Delete functions
20+
- 🔄 **Function Execution Engine** - Execute functions in isolated Deno workers
21+
- `POST /functions/v2/{name}` - Execute functions with parameters
22+
- Support for both streaming (generators) and non-streaming responses
23+
- Proper error handling and timeout management
24+
- Configurable execution timeout via database config
25+
- 💾 **Database Schema Enhancement** - New functions table with comprehensive metadata
26+
- Function code storage in database
27+
- Permissions management (read, write, env, run)
28+
- Enable/disable function toggle
29+
- Creation and update timestamps
30+
- 🛡️ **Security & Isolation** - Functions run in sandboxed Deno workers
31+
- Granular permission control per function
32+
- Isolated execution environment
33+
- Resource cleanup and timeout protection
34+
- 📊 **Streaming Support** - Generator functions for real-time data streaming
35+
- Server-sent events (SSE) for streaming responses
36+
- Progress tracking for long-running operations
37+
- Proper stream lifecycle management
38+
39+
### Enhanced
40+
41+
- 🔧 **Database Configuration** - Added `function_execution_timeout` setting
42+
- 📚 **OpenAPI Documentation** - Complete API documentation for function management
43+
- 🧪 **Test Coverage** - Comprehensive unit and integration tests for function system
44+
845
## [2.1.0] - 2025-08-01
946

1047
### 🎨 Frontend Hosting Feature

README.md

Lines changed: 119 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# 🚀 NanoEdgeRT v2.1
1+
# 🚀 NanoEdgeRT v2.5
22

33
[![CI](https://github.com/LemonHX/NanoEdgeRT/actions/workflows/ci.yml/badge.svg)](https://github.com/LemonHX/NanoEdgeRT/actions/workflows/ci.yml)
44
[![Deno](https://img.shields.io/badge/Deno-000000?style=for-the-badge&logo=deno&logoColor=white)](https://deno.land/)
@@ -7,13 +7,14 @@
77

88
**Next-Generation Edge Function Runtime** - A lightweight, high-performance platform built with Deno and SQLite for deploying and managing serverless functions at the edge with enterprise-grade security and developer experience.
99

10-
> 🏆 **Enterprise Ready**: Sub-millisecond response times, 5,000+ ops/sec throughput, JWT authentication, versioned APIs, frontend hosting, and auto-generated documentation!
10+
> 🏆 **Enterprise Ready**: Sub-millisecond response times, 5,000+ ops/sec throughput, JWT authentication, versioned APIs, serverless functions, frontend hosting, and auto-generated documentation!
1111
1212
## ✨ Key Features
1313

1414
### 🔗 Versioned API Architecture
1515

1616
- **Service API**: `/api/v2/{serviceName}/*` - Public service endpoints
17+
- **Function API**: `/functions/v2/{functionName}` - Serverless function execution
1718
- **Admin API**: `/admin-api/v2/*` - JWT-protected administrative operations
1819
- **Documentation API**: `/api/docs/{serviceName}` - Service-specific documentation
1920

@@ -47,6 +48,15 @@
4748
- **Custom Server Logic** - JavaScript server files handle dynamic routing
4849
- **Static Asset Serving** - Optimized static file serving with proper MIME types
4950

51+
### ⚡ Function Management
52+
53+
- **Serverless Functions** - Deploy and execute JavaScript functions on-demand
54+
- **Generator Support** - Built-in support for streaming responses with generator functions
55+
- **Isolated Execution** - Functions run in secure Deno Workers with configurable permissions
56+
- **Database-Driven** - Functions stored and managed in SQLite database
57+
- **Real-time Streaming** - Support for both regular and streaming function responses
58+
- **Error Handling** - Comprehensive error handling and timeout protection
59+
5060
## 🏗️ Architecture Overview
5161

5262
```mermaid
@@ -55,6 +65,7 @@ graph TB
5565
Gateway --> Auth{JWT Auth Required?}
5666
Auth -->|Admin API| JWT[JWT Validation]
5767
Auth -->|Service API| ServiceRouter[Service Router]
68+
Auth -->|Function API| FunctionRouter[Function Router]
5869
Auth -->|Public| PublicRouter[Public Routes]
5970
JWT -->|Valid| AdminRouter[Admin Router]
6071
JWT -->|Invalid| Error[401 Unauthorized]
@@ -65,26 +76,38 @@ graph TB
6576
PublicRouter --> Static["/static"]
6677
6778
AdminRouter --> AdminAPI["/admin-api/v2/*"]
79+
AdminRouter --> FunctionAdmin["/admin-api/v2/functions/*"]
6880
ServiceRouter --> ServiceAPI["/api/v2/*"]
6981
ServiceRouter --> ServiceDocs["/api/docs/*"]
82+
FunctionRouter --> FunctionAPI["/functions/v2/*"]
7083
7184
AdminAPI --> Database[(SQLite Database)]
7285
ServiceAPI --> ServiceManager[Service Manager]
86+
FunctionAPI --> FunctionManager[Function Manager]
7387
ServiceDocs --> SwaggerUI[Swagger UI]
7488
7589
ServiceManager --> Worker1[Service Worker :8001]
7690
ServiceManager --> Worker2[Service Worker :8002]
7791
ServiceManager --> WorkerN[Service Worker :800N]
7892
93+
FunctionManager --> FuncWorker1[Function Worker]
94+
FunctionManager --> FuncWorker2[Function Worker]
95+
FunctionManager --> FuncWorkerN[Function Worker]
96+
7997
subgraph "Database Layer"
8098
Database --> Services[Services Table]
99+
Database --> Functions[Functions Table]
81100
Database --> Config[Config Table]
82101
Database --> Ports[Ports Table]
83102
end
84103
85104
Worker1 --> ServiceInstance1[hello service]
86105
Worker2 --> ServiceInstance2[calculator service]
87106
WorkerN --> ServiceInstanceN[custom services...]
107+
108+
FuncWorker1 --> FunctionInstance1[serverless function]
109+
FuncWorker2 --> FunctionInstance2[generator function]
110+
FuncWorkerN --> FunctionInstanceN[custom functions...]
88111
```
89112

90113
## 🚀 Quick Start
@@ -163,6 +186,17 @@ export JWT_TOKEN="your-admin-jwt-token"
163186
| `/admin-api/v2/services/{name}` | DELETE | Delete service | `curl -X DELETE -H "Authorization: Bearer $JWT_TOKEN" http://localhost:8000/admin-api/v2/services/my-service` |
164187
| `/admin-api/v2/host-frontend` | POST | Deploy frontend application | See [Frontend Hosting](#-frontend-hosting-deployment) |
165188

189+
#### Function Management
190+
191+
| Endpoint | Method | Description | Example |
192+
| -------------------------------- | ------ | --------------------- | ---------------------------------------------------------------------------------------------------------------------- |
193+
| `/admin-api/v2/functions` | GET | List all functions | `curl -H "Authorization: Bearer $JWT_TOKEN" http://localhost:8000/admin-api/v2/functions` |
194+
| `/admin-api/v2/functions` | POST | Create new function | See [Function Creation](#-function-creation) |
195+
| `/admin-api/v2/functions/{name}` | GET | Get specific function | `curl -H "Authorization: Bearer $JWT_TOKEN" http://localhost:8000/admin-api/v2/functions/my-function` |
196+
| `/admin-api/v2/functions/{name}` | PUT | Update function | See [Function Updates](#-function-updates) |
197+
| `/admin-api/v2/functions/{name}` | DELETE | Delete function | `curl -X DELETE -H "Authorization: Bearer $JWT_TOKEN" http://localhost:8000/admin-api/v2/functions/my-function` |
198+
| `/functions/v2/{name}` | POST | Execute function | `curl -X POST -H "Content-Type: application/json" -d '{"key":"value"}' http://localhost:8000/functions/v2/my-function` |
199+
166200
#### Configuration Management
167201

168202
| Endpoint | Method | Description | Example |
@@ -287,11 +321,93 @@ curl -X PUT \
287321
| `available_port_end` | number | Service port range end | 8999 |
288322
| `jwt_secret` | string | JWT signing secret | "default-secret-change-me" |
289323

324+
## ⚡ Function Management
325+
326+
### 🆕 Function Creation
327+
328+
Create serverless functions that execute on-demand with configurable permissions and isolation.
329+
330+
```bash
331+
curl -X POST \
332+
-H "Authorization: Bearer $JWT_TOKEN" \
333+
-H "Content-Type: application/json" \
334+
-d '{
335+
"name": "hello-function",
336+
"code": "export default function(input) { return { message: `Hello, ${input.name || \"World\"}!` }; }",
337+
"enabled": true,
338+
"permissions": {
339+
"read": [],
340+
"write": [],
341+
"env": [],
342+
"run": []
343+
},
344+
"description": "Simple greeting function"
345+
}' \
346+
http://localhost:8000/admin-api/v2/functions
347+
```
348+
349+
### 🔄 Function Updates
350+
351+
```bash
352+
curl -X PUT \
353+
-H "Authorization: Bearer $JWT_TOKEN" \
354+
-H "Content-Type: application/json" \
355+
-d '{
356+
"code": "export default function(input) { return { message: `Updated: Hello, ${input.name}!` }; }",
357+
"enabled": true,
358+
"description": "Updated greeting function"
359+
}' \
360+
http://localhost:8000/admin-api/v2/functions/hello-function
361+
```
362+
363+
### 🚀 Function Execution
364+
365+
Execute functions with JSON payload:
366+
367+
```bash
368+
# Regular function execution
369+
curl -X POST \
370+
-H "Content-Type: application/json" \
371+
-d '{"name": "Alice"}' \
372+
http://localhost:8000/functions/v2/hello-function
373+
374+
# Generator function with streaming response
375+
curl -X POST \
376+
-H "Content-Type: application/json" \
377+
-d '{}' \
378+
http://localhost:8000/functions/v2/streaming-function
379+
```
380+
381+
### 🎯 Generator Functions
382+
383+
Functions can be generator functions for streaming responses:
384+
385+
```javascript
386+
export default function* streamingFunction() {
387+
yield "Starting process...";
388+
yield "Processing data...";
389+
yield "Almost done...";
390+
return "Completed!";
391+
}
392+
```
393+
394+
The response will be a Server-Sent Events stream:
395+
396+
```
397+
data: "Starting process..."
398+
399+
data: "Processing data..."
400+
401+
data: "Almost done..."
402+
403+
data: [DONE]"Completed!"
404+
```
405+
290406
## 🌐 Frontend Hosting Deployment
291407

292408
### 🚀 Deploy Frontend Applications
293409

294-
NanoEdgeRT v2.1 introduces powerful frontend hosting capabilities, allowing you to deploy full-stack applications with custom server logic and static assets.
410+
NanoEdgeRT v2.5 introduces powerful frontend hosting capabilities, allowing you to deploy full-stack applications with custom server logic and static assets.
295411

296412
#### How It Works
297413

database/api.function.ts

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
import type { Context } from "hono";
2+
import type { Hono } from "hono";
3+
import {
4+
createFunction,
5+
DatabaseContext,
6+
deleteFunction,
7+
getAllFunctions,
8+
getFunction,
9+
updateFunction,
10+
} from "./dto.ts";
11+
12+
// Setup function API routes
13+
export function setupFunctionAPIRoutes(app: Hono, _dbContext: DatabaseContext) {
14+
// Functions routes
15+
app.get("/functions", getAllFunctionsHandler);
16+
app.get("/functions/:name", getFunctionHandler);
17+
app.post("/functions", createFunctionHandler);
18+
app.put("/functions/:name", updateFunctionHandler);
19+
app.delete("/functions/:name", deleteFunctionHandler);
20+
}
21+
22+
// Function handlers
23+
async function getAllFunctionsHandler(c: Context): Promise<Response> {
24+
const dbContext = c.get("dbContext");
25+
try {
26+
const functions = await getAllFunctions(dbContext);
27+
return c.json({ functions });
28+
} catch (error) {
29+
console.error("Get all functions error:", error);
30+
return c.json(
31+
{
32+
error: "Failed to get functions",
33+
message: error instanceof Error ? error.message : String(error),
34+
},
35+
500,
36+
);
37+
}
38+
}
39+
40+
async function getFunctionHandler(c: Context): Promise<Response> {
41+
const dbContext = c.get("dbContext");
42+
const functionName = c.req.param("name");
43+
44+
try {
45+
const func = await getFunction(dbContext, functionName);
46+
if (!func) {
47+
return c.json({ error: "Function not found" }, 404);
48+
}
49+
return c.json(func);
50+
} catch (error) {
51+
console.error("Get function error:", error);
52+
return c.json(
53+
{
54+
error: "Failed to get function",
55+
message: error instanceof Error ? error.message : String(error),
56+
},
57+
500,
58+
);
59+
}
60+
}
61+
62+
async function createFunctionHandler(c: Context): Promise<Response> {
63+
const dbContext = c.get("dbContext");
64+
65+
try {
66+
const body = await c.req.json();
67+
const { name, code, enabled = true, permissions, description } = body;
68+
69+
if (!name || !code) {
70+
return c.json({ error: "Name and code are required" }, 400);
71+
}
72+
73+
await createFunction(dbContext, {
74+
name,
75+
code,
76+
enabled,
77+
permissions: permissions || {
78+
read: [],
79+
write: [],
80+
env: [],
81+
run: [],
82+
},
83+
description,
84+
});
85+
86+
return c.json({ message: "Function created successfully", name }, 201);
87+
} catch (error) {
88+
console.error("Create function error:", error);
89+
return c.json(
90+
{
91+
error: "Failed to create function",
92+
message: error instanceof Error ? error.message : String(error),
93+
},
94+
500,
95+
);
96+
}
97+
}
98+
99+
async function updateFunctionHandler(c: Context): Promise<Response> {
100+
const dbContext = c.get("dbContext");
101+
const functionName = c.req.param("name");
102+
103+
try {
104+
const body = await c.req.json();
105+
const { code, enabled, permissions, description } = body;
106+
107+
await updateFunction(dbContext, functionName, {
108+
code,
109+
enabled,
110+
permissions,
111+
description,
112+
});
113+
114+
return c.json({ message: "Function updated successfully", ...body });
115+
} catch (error) {
116+
console.error("Update function error:", error);
117+
return c.json(
118+
{
119+
error: "Failed to update function",
120+
message: error instanceof Error ? error.message : String(error),
121+
},
122+
500,
123+
);
124+
}
125+
}
126+
127+
async function deleteFunctionHandler(c: Context): Promise<Response> {
128+
const dbContext = c.get("dbContext");
129+
const functionName = c.req.param("name");
130+
131+
try {
132+
await deleteFunction(dbContext, functionName);
133+
return c.json({ message: "Function deleted successfully" });
134+
} catch (error) {
135+
console.error("Delete function error:", error);
136+
return c.json(
137+
{
138+
error: "Failed to delete function",
139+
message: error instanceof Error ? error.message : String(error),
140+
},
141+
500,
142+
);
143+
}
144+
}
File renamed without changes.

0 commit comments

Comments
 (0)