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
26 changes: 26 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,32 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.2.0] - 2025-07-30

### Added

- 📋 **OpenAPI Schema Support** - Added nullable `schema` column to services table for storing OpenAPI JSON schemas
- 📚 **Service Documentation Routes** - New routes for viewing individual service documentation
- `/doc/:serviceName` - Swagger UI for specific service schemas
- `/openapi/:serviceName` - JSON endpoint for service OpenAPI schemas
- 🔧 **Schema Validation** - Added JSON validation for OpenAPI schemas in service creation/update
- 📖 **Default Service Schemas** - Included sample OpenAPI schemas for hello and calculator services
- 🌐 **Enhanced Root Endpoint** - Added documentation links in root API response

### Changed

- 🗄️ **Database Schema** - Added `schema` column to services table with proper migration
- 📋 **Service Creation/Update** - Enhanced APIs to handle OpenAPI schema field
- 🏗️ **Type Definitions** - Updated `ServiceConfig` interface to include optional `schema` field
- 📊 **Service Responses** - Updated service listing endpoints to include schema information

### Technical Details

- Database migration adds nullable `schema` TEXT column to services table
- All service CRUD operations now support schema field
- TypeScript interfaces updated across the codebase
- Swagger UI integration for per-service documentation

## [1.1.0] - 2025-07-24

### Added
Expand Down
24 changes: 11 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -275,15 +275,17 @@ For enhanced security, NanoEdgeRT implements **IP-based access controls**:

### System Endpoints

| Endpoint | Method | Description | Access | Performance |
| --------------- | ------ | -------------------------------- | ---------------- | -------------------------- |
| `/` | GET | Welcome message and service list | `0.0.0.0:8000` | **~67µs** (14,990 ops/sec) |
| `/static/*` | GET | Serve static files | `0.0.0.0:8000` | **~67µs** (14,990 ops/sec) |
| `/health` | GET | Health check and service status | `0.0.0.0:8000` | **~73µs** (13,730 ops/sec) |
| `/admin` | GET | 🎨 **Modern Dashboard UI** | `127.0.0.1:8000` | **~150µs** (6,600 ops/sec) |
| `/docs` | GET | 🎨 **Swagger UI documentation** | `127.0.0.1:8000` | **~166µs** (6,010 ops/sec) |
| `/swagger` | GET | Swagger UI documentation (alias) | `127.0.0.1:8000` | **~166µs** (6,010 ops/sec) |
| `/openapi.json` | GET | OpenAPI 3.0.3 specification | `127.0.0.1:8000` | **~166µs** (6,010 ops/sec) |
| Endpoint | Method | Description | Access | Performance |
| ----------------------- | ------ | ---------------------------------- | ---------------- | -------------------------- |
| `/` | GET | Welcome message and service list | `0.0.0.0:8000` | **~67µs** (14,990 ops/sec) |
| `/static/*` | GET | Serve static files | `0.0.0.0:8000` | **~67µs** (14,990 ops/sec) |
| `/health` | GET | Health check and service status | `0.0.0.0:8000` | **~73µs** (13,730 ops/sec) |
| `/admin` | GET | 🎨 **Modern Dashboard UI** | `127.0.0.1:8000` | **~150µs** (6,600 ops/sec) |
| `/docs` | GET | 🎨 **Swagger UI documentation** | `127.0.0.1:8000` | **~166µs** (6,010 ops/sec) |
| `/swagger` | GET | Swagger UI documentation (alias) | `127.0.0.1:8000` | **~166µs** (6,010 ops/sec) |
| `/openapi.json` | GET | OpenAPI 3.0.3 specification | `127.0.0.1:8000` | **~166µs** (6,010 ops/sec) |
| `/doc/:serviceName` | GET | 📋 **Service-specific Swagger UI** | `0.0.0.0:8000` | **~180µs** (5,500 ops/sec) |
| `/openapi/:serviceName` | GET | Service OpenAPI schema JSON | `0.0.0.0:8000` | **~180µs** (5,500 ops/sec) |

### Dynamic Admin API Endpoints (Authentication Required)

Expand Down Expand Up @@ -317,7 +319,6 @@ deno task test
# Run specific test suites
deno task test:unit # Unit tests
deno task test:integration # Integration tests
deno task test:e2e # End-to-end tests

# Run tests in watch mode
deno task test:watch
Expand All @@ -330,7 +331,6 @@ deno task bench

- **Unit Tests**: Test individual components in isolation
- **Integration Tests**: Test component interactions
- **E2E Tests**: Test complete user workflows
- **Benchmarks**: Performance testing

### 🎯 Test Results
Expand All @@ -341,7 +341,6 @@ deno task bench
| ------------------------ | ------------ | ----------- | ---------------------- | -------------------------------------- |
| 🧪 **Unit Tests** | **27/27** | ✅ **100%** | Individual components | Config, Auth, Swagger, Service Manager |
| 🔗 **Integration Tests** | **2/2** | ✅ **100%** | Component interactions | Server startup, Service communication |
| 🌐 **E2E Tests** | **0/4** | ✅ **100%** | End-to-end workflows | Require running server |
| **📊 TOTAL** | **🎯 29/29** | **✅ 100%** | **Complete coverage** | **Database-driven system operational** |

#### 📋 Detailed Test Breakdown
Expand Down Expand Up @@ -484,7 +483,6 @@ POST /_admin/api/services
- [ ] **Service Templates** - Pre-built service templates for common use cases
- [ ] **WebSocket Support** - Real-time communication support
- [ ] **Service Versioning** - Multiple versions of services running simultaneously
- [ ] **Database Migrations** - Automated database schema migrations

## 🤝 Contributing

Expand Down
112 changes: 112 additions & 0 deletions database/sqlite3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export interface ServiceTable {
enabled: boolean;
jwt_check: boolean;
permissions: string; // JSON string
schema?: string; // JSON string for OpenAPI schema (nullable)
port?: number; // Allocated port for the service
created_at?: string;
updated_at?: string;
Expand Down Expand Up @@ -69,6 +70,7 @@ export async function initializeDatabase(dbInstance: Kysely<Database> = db) {
"text",
(col) => col.notNull().defaultTo('{"read":[],"write":[],"env":[],"run":[]}'),
)
.addColumn("schema", "text") // Nullable JSON string for OpenAPI schema
.addColumn("port", "integer") // Allocated port for the service
.addColumn("created_at", "text", (col) => col.notNull())
.addColumn("updated_at", "text", (col) => col.notNull())
Expand Down Expand Up @@ -247,13 +249,122 @@ export async function initializeDatabase(dbInstance: Kysely<Database> = db) {
);
}`;

// OpenAPI schema for hello service
const helloSchema = JSON.stringify({
openapi: "3.0.0",
info: {
title: "Hello Service",
version: "1.0.0",
description: "A simple greeting service",
},
paths: {
"/": {
get: {
summary: "Get a greeting message",
parameters: [
{
name: "name",
in: "query",
description: "Name to greet",
required: false,
schema: {
type: "string",
default: "World",
},
},
],
responses: {
"200": {
description: "Successful greeting",
content: {
"application/json": {
schema: {
type: "object",
properties: {
message: { type: "string" },
timestamp: { type: "string", format: "date-time" },
method: { type: "string" },
path: { type: "string" },
},
},
},
},
},
},
},
},
},
});

// OpenAPI schema for calculator service
const calculatorSchema = JSON.stringify({
openapi: "3.0.0",
info: {
title: "Calculator Service",
version: "1.0.0",
description: "A simple mathematical calculator service",
},
paths: {
"/": {
get: {
summary: "Evaluate a mathematical expression",
parameters: [
{
name: "expr",
in: "query",
description: "Mathematical expression to evaluate (e.g., 2+2, 10*5)",
required: true,
schema: {
type: "string",
example: "2+2",
},
},
],
responses: {
"200": {
description: "Successful calculation",
content: {
"application/json": {
schema: {
type: "object",
properties: {
expression: { type: "string" },
result: { type: "number" },
timestamp: { type: "string", format: "date-time" },
},
},
},
},
},
"400": {
description: "Invalid expression or missing parameter",
content: {
"application/json": {
schema: {
type: "object",
properties: {
error: { type: "string" },
message: { type: "string" },
example: { type: "string" },
},
},
},
},
},
},
},
},
},
});

const defaultServices = [
{
name: "hello",
code: helloService,
enabled: true,
jwt_check: false,
permissions: JSON.stringify({ read: [], write: [], env: [], run: [] }),
schema: helloSchema,
created_at: new Date().toISOString(),
updated_at: new Date().toISOString(),
},
Expand All @@ -263,6 +374,7 @@ export async function initializeDatabase(dbInstance: Kysely<Database> = db) {
enabled: true,
jwt_check: false,
permissions: JSON.stringify({ read: [], write: [], env: [], run: [] }),
schema: calculatorSchema,
created_at: new Date().toISOString(),
updated_at: new Date().toISOString(),
},
Expand Down
4 changes: 2 additions & 2 deletions deno.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "nanoedgert",
"version": "1.1.0",
"version": "1.2.0",
"description": "A lightweight, high-performance edge function runtime built with Deno",
"imports": {
"hono": "https://esm.sh/hono@4.8.9",
Expand All @@ -10,7 +10,7 @@
"@hono/zod-openapi": "https://esm.sh/@hono/zod-openapi@1.0.2?deps=hono@4.8.9,zod@3.23.8",
"zod": "https://esm.sh/zod@3.23.8",
"kysely": "npm:kysely@^0.27.2",
"@hono/node-server/serve-static": "https://esm.sh/@hono/node-server/serve-static@0.5.2?deps=hono@4.8.9"
"@hono/node-server/serve-static": "https://esm.sh/@hono/node-server/serve-static?deps=hono@4.8.9"
},
"tasks": {
"start": "deno run --allow-all --unstable-worker-options main.ts",
Expand Down
4 changes: 4 additions & 0 deletions deno.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Config } from "./types.ts";
import { Config } from "./database-config.ts";

export async function loadConfig(configPath: string = "./nanoedge/config.json"): Promise<Config> {
try {
Expand Down
43 changes: 42 additions & 1 deletion src/database-config.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,41 @@
import { db } from "../database/sqlite3.ts";
import { Config, ServiceConfig, ServicePermissions } from "./types.ts";
import { Kysely } from "kysely";
import type { ConfigTable, PortTable, ServiceTable } from "../database/sqlite3.ts";

// Type definitions (moved from types.ts)
export interface ServicePermissions {
read: string[];
write: string[];
env: string[];
run: string[];
}

export interface ServiceConfig {
name: string;
path?: string;
enable: boolean;
jwt_check: boolean;
build_command?: string;
permissions: ServicePermissions;
code?: string;
schema?: string;
}

export interface ServiceInstance {
config: ServiceConfig;
worker?: Worker;
port: number;
status: "starting" | "running" | "stopped" | "error";
}

export interface Config {
available_port_start: number;
available_port_end: number;
services: ServiceConfig[];
jwt_secret?: string;
main_port?: number;
}

interface Database {
services: ServiceTable;
config: ConfigTable;
Expand Down Expand Up @@ -60,6 +93,7 @@ export class DatabaseConfig {
jwt_check: Boolean(row.jwt_check),
permissions: JSON.parse(row.permissions) as ServicePermissions,
code: row.code,
schema: row.schema, // Include schema field
}));

this.config = {
Expand Down Expand Up @@ -97,6 +131,7 @@ export class DatabaseConfig {
enabled?: boolean;
jwt_check?: boolean;
permissions?: ServicePermissions;
schema?: string;
}): Promise<void> {
const now = new Date().toISOString();

Expand All @@ -115,6 +150,7 @@ export class DatabaseConfig {
run: [],
},
),
schema: service.schema, // Include schema field (nullable)
created_at: now,
updated_at: now,
})
Expand All @@ -129,12 +165,14 @@ export class DatabaseConfig {
enabled?: boolean;
jwt_check?: boolean;
permissions?: ServicePermissions;
schema?: string;
}): Promise<void> {
const updateData: Partial<{
code: string;
enabled: boolean;
jwt_check: boolean;
permissions: string;
schema: string;
updated_at: string;
}> = {
updated_at: new Date().toISOString(),
Expand All @@ -146,6 +184,7 @@ export class DatabaseConfig {
if (updates.permissions !== undefined) {
updateData.permissions = JSON.stringify(updates.permissions);
}
if (updates.schema !== undefined) updateData.schema = updates.schema;

await this.dbInstance
.updateTable("services")
Expand Down Expand Up @@ -175,6 +214,7 @@ export class DatabaseConfig {
enabled: boolean;
jwt_check: boolean;
permissions: ServicePermissions;
schema?: string;
created_at?: string;
updated_at?: string;
}>
Expand All @@ -200,6 +240,7 @@ export class DatabaseConfig {
enabled: boolean;
jwt_check: boolean;
permissions: ServicePermissions;
schema?: string;
created_at?: string;
updated_at?: string;
} | null
Expand Down
Loading
Loading