@simplysm/service-server 14.0.4 → 14.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +121 -56
- package/docs/auth.md +33 -30
- package/docs/built-in-services.md +61 -0
- package/docs/config-manager.md +24 -0
- package/docs/http-handlers.md +83 -0
- package/docs/legacy-v1.md +25 -0
- package/docs/protocol-wrapper.md +37 -0
- package/docs/server-options.md +31 -0
- package/docs/service-definition.md +151 -0
- package/docs/service-executor.md +42 -0
- package/docs/service-server.md +80 -0
- package/docs/service-socket.md +73 -0
- package/docs/websocket-handler.md +58 -0
- package/package.json +6 -6
- package/docs/core.md +0 -192
- package/docs/legacy.md +0 -21
- package/docs/main.md +0 -81
- package/docs/protocol.md +0 -31
- package/docs/services.md +0 -63
- package/docs/transport.md +0 -169
- package/docs/types.md +0 -31
- package/docs/utilities.md +0 -27
package/README.md
CHANGED
|
@@ -1,63 +1,133 @@
|
|
|
1
1
|
# @simplysm/service-server
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
|
15
|
-
|
|
16
|
-
| `
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
|
22
|
-
|
|
23
|
-
| `
|
|
24
|
-
| `
|
|
25
|
-
| `
|
|
26
|
-
| `
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
|
32
|
-
|
|
33
|
-
| `
|
|
34
|
-
| `
|
|
35
|
-
| `
|
|
36
|
-
| `
|
|
37
|
-
| `
|
|
38
|
-
| `
|
|
39
|
-
| `
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
3
|
+
Fastify-based RPC server with WebSocket support, JWT authentication, service definitions, file upload/download, static file serving, and built-in ORM/auto-update services. Depends on `@simplysm/service-common` for shared protocol and types.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @simplysm/service-server
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## API Overview
|
|
12
|
+
|
|
13
|
+
### Server Options
|
|
14
|
+
| API | Type | Description |
|
|
15
|
+
|-----|------|-------------|
|
|
16
|
+
| `ServiceServerOptions` | `interface` | Server configuration (rootPath, port, SSL, auth, services) |
|
|
17
|
+
|
|
18
|
+
-> See [docs/server-options.md](./docs/server-options.md) for details.
|
|
19
|
+
|
|
20
|
+
### Authentication (JWT)
|
|
21
|
+
| API | Type | Description |
|
|
22
|
+
|-----|------|-------------|
|
|
23
|
+
| `AuthTokenPayload` | `interface` | JWT token payload with roles and custom data |
|
|
24
|
+
| `signJwt` | `function` | Signs a JWT token (HS256, 12h expiry) |
|
|
25
|
+
| `verifyJwt` | `function` | Verifies a JWT token |
|
|
26
|
+
| `decodeJwt` | `function` | Decodes a JWT token without verification |
|
|
27
|
+
|
|
28
|
+
-> See [docs/auth.md](./docs/auth.md) for details.
|
|
29
|
+
|
|
30
|
+
### Service Definition/Context
|
|
31
|
+
| API | Type | Description |
|
|
32
|
+
|-----|------|-------------|
|
|
33
|
+
| `ServiceContext` | `interface` | Service execution context (server, socket, auth, config) |
|
|
34
|
+
| `createServiceContext` | `function` | Creates a service context instance |
|
|
35
|
+
| `getServiceAuthPermissions` | `function` | Reads auth permissions from an auth-wrapped function |
|
|
36
|
+
| `auth` | `function` | Authentication wrapper for service factories and methods |
|
|
37
|
+
| `ServiceDefinition` | `interface` | Service definition with name and factory |
|
|
38
|
+
| `defineService` | `function` | Defines a named service with a factory function |
|
|
39
|
+
| `ServiceMethods` | `type` | Extracts method signatures from a ServiceDefinition |
|
|
40
|
+
|
|
41
|
+
-> See [docs/service-definition.md](./docs/service-definition.md) for details.
|
|
42
|
+
|
|
43
|
+
### Service Executor
|
|
44
|
+
| API | Type | Description |
|
|
45
|
+
|-----|------|-------------|
|
|
46
|
+
| `executeServiceMethod` | `function` | Executes a service method with auth checking |
|
|
47
|
+
|
|
48
|
+
-> See [docs/service-executor.md](./docs/service-executor.md) for details.
|
|
49
|
+
|
|
50
|
+
### WebSocket Handler
|
|
51
|
+
| API | Type | Description |
|
|
52
|
+
|-----|------|-------------|
|
|
53
|
+
| `WebSocketHandler` | `interface` | Multi-connection WebSocket handler with event broadcasting |
|
|
54
|
+
| `createWebSocketHandler` | `function` | Creates a WebSocket handler instance |
|
|
55
|
+
|
|
56
|
+
-> See [docs/websocket-handler.md](./docs/websocket-handler.md) for details.
|
|
57
|
+
|
|
58
|
+
### Service Socket
|
|
59
|
+
| API | Type | Description |
|
|
60
|
+
|-----|------|-------------|
|
|
61
|
+
| `ServiceSocket` | `interface` | Single WebSocket connection with protocol and events |
|
|
62
|
+
| `createServiceSocket` | `function` | Creates a service socket instance |
|
|
63
|
+
|
|
64
|
+
-> See [docs/service-socket.md](./docs/service-socket.md) for details.
|
|
65
|
+
|
|
66
|
+
### HTTP Handlers
|
|
67
|
+
| API | Type | Description |
|
|
68
|
+
|-----|------|-------------|
|
|
69
|
+
| `handleHttpRequest` | `function` | Handles HTTP RPC requests (GET/POST) |
|
|
70
|
+
| `handleUpload` | `function` | Handles multipart file uploads |
|
|
71
|
+
| `handleStaticFile` | `function` | Serves static files with security guards |
|
|
72
|
+
|
|
73
|
+
-> See [docs/http-handlers.md](./docs/http-handlers.md) for details.
|
|
74
|
+
|
|
75
|
+
### Protocol Wrapper
|
|
76
|
+
| API | Type | Description |
|
|
77
|
+
|-----|------|-------------|
|
|
78
|
+
| `ServerProtocolWrapper` | `interface` | Server-side protocol wrapper with worker thread offloading |
|
|
79
|
+
| `createServerProtocolWrapper` | `function` | Creates a server protocol wrapper instance |
|
|
80
|
+
|
|
81
|
+
-> See [docs/protocol-wrapper.md](./docs/protocol-wrapper.md) for details.
|
|
82
|
+
|
|
83
|
+
### ORM/AutoUpdate Services
|
|
84
|
+
| API | Type | Description |
|
|
85
|
+
|-----|------|-------------|
|
|
86
|
+
| `OrmService` | `const (ServiceDefinition)` | Built-in ORM service definition |
|
|
87
|
+
| `OrmServiceType` | `type` | ORM service method signatures |
|
|
88
|
+
| `AutoUpdateService` | `const (ServiceDefinition)` | Built-in auto-update service definition |
|
|
89
|
+
| `AutoUpdateServiceType` | `type` | Auto-update service method signatures |
|
|
90
|
+
|
|
91
|
+
-> See [docs/built-in-services.md](./docs/built-in-services.md) for details.
|
|
92
|
+
|
|
93
|
+
### Config Manager
|
|
94
|
+
| API | Type | Description |
|
|
95
|
+
|-----|------|-------------|
|
|
96
|
+
| `getConfig` | `function` | Loads JSON config with file-watching and caching |
|
|
97
|
+
|
|
98
|
+
-> See [docs/config-manager.md](./docs/config-manager.md) for details.
|
|
99
|
+
|
|
100
|
+
### Legacy V1
|
|
101
|
+
| API | Type | Description |
|
|
102
|
+
|-----|------|-------------|
|
|
103
|
+
| `handleV1Connection` | `function` | V1 legacy WebSocket handler (auto-update only) |
|
|
104
|
+
|
|
105
|
+
-> See [docs/legacy-v1.md](./docs/legacy-v1.md) for details.
|
|
106
|
+
|
|
107
|
+
### Main ServiceServer
|
|
108
|
+
| API | Type | Description |
|
|
109
|
+
|-----|------|-------------|
|
|
110
|
+
| `ServiceServer` | `class` | Main server class (Fastify + WebSocket + auth) |
|
|
111
|
+
| `createServiceServer` | `function` | Factory function to create a ServiceServer |
|
|
112
|
+
|
|
113
|
+
-> See [docs/service-server.md](./docs/service-server.md) for details.
|
|
114
|
+
|
|
115
|
+
## Usage Examples
|
|
43
116
|
|
|
44
117
|
### Basic Server Setup
|
|
45
118
|
|
|
46
|
-
```
|
|
119
|
+
```typescript
|
|
47
120
|
import { createServiceServer, defineService, auth } from "@simplysm/service-server";
|
|
48
121
|
|
|
49
|
-
// Define a service
|
|
50
122
|
const GreetService = defineService("Greet", (ctx) => ({
|
|
51
123
|
hello(name: string) {
|
|
52
|
-
return
|
|
124
|
+
return \`Hello, \${name}!\`;
|
|
53
125
|
},
|
|
54
|
-
// Protected method requiring authentication
|
|
55
126
|
secret: auth(["admin"], (msg: string) => {
|
|
56
|
-
return
|
|
127
|
+
return \`Secret for \${ctx.authInfo}: \${msg}\`;
|
|
57
128
|
}),
|
|
58
129
|
}));
|
|
59
130
|
|
|
60
|
-
// Create and start the server
|
|
61
131
|
const server = createServiceServer({
|
|
62
132
|
rootPath: process.cwd(),
|
|
63
133
|
port: 3000,
|
|
@@ -70,40 +140,35 @@ await server.listen();
|
|
|
70
140
|
|
|
71
141
|
### JWT Authentication
|
|
72
142
|
|
|
73
|
-
```
|
|
143
|
+
```typescript
|
|
74
144
|
import { signJwt, verifyJwt } from "@simplysm/service-server";
|
|
75
145
|
|
|
76
|
-
// Sign a token
|
|
77
146
|
const token = await signJwt("my-secret", {
|
|
78
147
|
roles: ["admin"],
|
|
79
148
|
data: { userId: 1 },
|
|
80
149
|
});
|
|
81
150
|
|
|
82
|
-
// Verify a token
|
|
83
151
|
const payload = await verifyJwt("my-secret", token);
|
|
84
152
|
```
|
|
85
153
|
|
|
86
154
|
### Event Broadcasting
|
|
87
155
|
|
|
88
|
-
```
|
|
156
|
+
```typescript
|
|
89
157
|
import { defineEvent } from "@simplysm/service-common";
|
|
90
158
|
|
|
91
159
|
const NotifyEvent = defineEvent<{ userId: number }, { text: string }>("Notify");
|
|
92
|
-
|
|
93
|
-
// Emit from server
|
|
94
160
|
await server.emitEvent(NotifyEvent, (info) => info.userId === 42, { text: "Hello!" });
|
|
95
161
|
```
|
|
96
162
|
|
|
97
|
-
### ORM Service
|
|
163
|
+
### Built-in ORM Service
|
|
98
164
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
```ts
|
|
165
|
+
```typescript
|
|
102
166
|
import { createServiceServer, OrmService } from "@simplysm/service-server";
|
|
103
167
|
|
|
104
168
|
const server = createServiceServer({
|
|
105
169
|
rootPath: process.cwd(),
|
|
106
170
|
port: 3000,
|
|
171
|
+
auth: { jwtSecret: "my-secret" },
|
|
107
172
|
services: [OrmService],
|
|
108
173
|
});
|
|
109
174
|
```
|
package/docs/auth.md
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Authentication (JWT)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
## `AuthTokenPayload`
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
JWT token payload extending `JWTPayload` from `jose`. Contains roles and custom authentication data.
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
```ts
|
|
10
|
-
interface AuthTokenPayload<TAuthInfo = unknown> extends JWTPayload {
|
|
7
|
+
```typescript
|
|
8
|
+
export interface AuthTokenPayload<TAuthInfo = unknown> extends JWTPayload {
|
|
11
9
|
roles: string[];
|
|
12
10
|
data: TAuthInfo;
|
|
13
11
|
}
|
|
@@ -15,17 +13,16 @@ interface AuthTokenPayload<TAuthInfo = unknown> extends JWTPayload {
|
|
|
15
13
|
|
|
16
14
|
| Field | Type | Description |
|
|
17
15
|
|-------|------|-------------|
|
|
18
|
-
| `roles` | `string[]` |
|
|
19
|
-
| `data` | `TAuthInfo` | Custom authentication data (
|
|
20
|
-
|
|
21
|
-
Inherits all standard JWT fields from `JWTPayload` (e.g., `iss`, `sub`, `aud`, `exp`, `nbf`, `iat`, `jti`).
|
|
16
|
+
| `roles` | `string[]` | User roles for permission checking |
|
|
17
|
+
| `data` | `TAuthInfo` | Custom authentication data (user info, etc.) |
|
|
18
|
+
| *(inherited from JWTPayload)* | | Standard JWT claims (`iss`, `sub`, `aud`, `exp`, `nbf`, `iat`, `jti`) |
|
|
22
19
|
|
|
23
|
-
## signJwt
|
|
20
|
+
## `signJwt`
|
|
24
21
|
|
|
25
|
-
|
|
22
|
+
Signs a JWT token using HS256 algorithm with 12-hour expiration.
|
|
26
23
|
|
|
27
|
-
```
|
|
28
|
-
async function signJwt<TAuthInfo = unknown>(
|
|
24
|
+
```typescript
|
|
25
|
+
export async function signJwt<TAuthInfo = unknown>(
|
|
29
26
|
jwtSecret: string,
|
|
30
27
|
payload: AuthTokenPayload<TAuthInfo>,
|
|
31
28
|
): Promise<string>;
|
|
@@ -33,17 +30,17 @@ async function signJwt<TAuthInfo = unknown>(
|
|
|
33
30
|
|
|
34
31
|
| Parameter | Type | Description |
|
|
35
32
|
|-----------|------|-------------|
|
|
36
|
-
| `jwtSecret` | `string` |
|
|
33
|
+
| `jwtSecret` | `string` | JWT signing secret |
|
|
37
34
|
| `payload` | `AuthTokenPayload<TAuthInfo>` | Token payload with roles and data |
|
|
38
35
|
|
|
39
|
-
Returns
|
|
36
|
+
**Returns:** `Promise<string>` -- Signed JWT token string.
|
|
40
37
|
|
|
41
|
-
## verifyJwt
|
|
38
|
+
## `verifyJwt`
|
|
42
39
|
|
|
43
|
-
|
|
40
|
+
Verifies a JWT token and returns the decoded payload. Throws on expired or invalid tokens.
|
|
44
41
|
|
|
45
|
-
```
|
|
46
|
-
async function verifyJwt<TAuthInfo = unknown>(
|
|
42
|
+
```typescript
|
|
43
|
+
export async function verifyJwt<TAuthInfo = unknown>(
|
|
47
44
|
jwtSecret: string,
|
|
48
45
|
token: string,
|
|
49
46
|
): Promise<AuthTokenPayload<TAuthInfo>>;
|
|
@@ -51,21 +48,27 @@ async function verifyJwt<TAuthInfo = unknown>(
|
|
|
51
48
|
|
|
52
49
|
| Parameter | Type | Description |
|
|
53
50
|
|-----------|------|-------------|
|
|
54
|
-
| `jwtSecret` | `string` |
|
|
55
|
-
| `token` | `string` | JWT token string |
|
|
51
|
+
| `jwtSecret` | `string` | JWT verification secret |
|
|
52
|
+
| `token` | `string` | JWT token string to verify |
|
|
56
53
|
|
|
57
|
-
Returns
|
|
54
|
+
**Returns:** `Promise<AuthTokenPayload<TAuthInfo>>` -- Decoded token payload.
|
|
58
55
|
|
|
59
|
-
|
|
56
|
+
**Throws:**
|
|
57
|
+
- `Error("Token expired")` when the token has expired
|
|
58
|
+
- `Error("Invalid token")` for any other verification failure
|
|
60
59
|
|
|
61
|
-
|
|
60
|
+
## `decodeJwt`
|
|
62
61
|
|
|
63
|
-
|
|
64
|
-
|
|
62
|
+
Decodes a JWT token without verification. Useful for reading claims before verification.
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
export function decodeJwt<TAuthInfo = unknown>(
|
|
66
|
+
token: string,
|
|
67
|
+
): AuthTokenPayload<TAuthInfo>;
|
|
65
68
|
```
|
|
66
69
|
|
|
67
70
|
| Parameter | Type | Description |
|
|
68
71
|
|-----------|------|-------------|
|
|
69
|
-
| `token` | `string` | JWT token string |
|
|
72
|
+
| `token` | `string` | JWT token string to decode |
|
|
70
73
|
|
|
71
|
-
Returns
|
|
74
|
+
**Returns:** `AuthTokenPayload<TAuthInfo>` -- Decoded token payload (not verified).
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# ORM/AutoUpdate Services
|
|
2
|
+
|
|
3
|
+
## `OrmService`
|
|
4
|
+
|
|
5
|
+
Built-in ORM service definition. Provides database connection, transaction management, and query execution over the WebSocket RPC layer. Requires authentication (wrapped with `auth()`).
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
export const OrmService: ServiceDefinition;
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
The ORM service is registered with the name `"Orm"` and requires WebSocket transport (HTTP is not supported).
|
|
12
|
+
|
|
13
|
+
Database connections are tracked per socket. When a WebSocket connection closes, all associated DB connections are automatically cleaned up.
|
|
14
|
+
|
|
15
|
+
Methods (matching the `OrmService` interface from `@simplysm/service-common`):
|
|
16
|
+
|
|
17
|
+
| Method | Parameters | Return | Description |
|
|
18
|
+
|--------|-----------|--------|-------------|
|
|
19
|
+
| `getInfo` | `opt: DbConnOptions & { configName: string }` | `Promise<{ dialect: Dialect; database?: string; schema?: string }>` | Gets database info from server config |
|
|
20
|
+
| `connect` | `opt: DbConnOptions & { configName: string }` | `Promise<number>` | Opens a DB connection. Returns connection ID |
|
|
21
|
+
| `close` | `connId: number` | `Promise<void>` | Closes a DB connection |
|
|
22
|
+
| `beginTransaction` | `connId: number, isolationLevel?: IsolationLevel` | `Promise<void>` | Begins a transaction |
|
|
23
|
+
| `commitTransaction` | `connId: number` | `Promise<void>` | Commits a transaction |
|
|
24
|
+
| `rollbackTransaction` | `connId: number` | `Promise<void>` | Rolls back a transaction |
|
|
25
|
+
| `executeParametrized` | `connId: number, query: string, params?: unknown[]` | `Promise<unknown[][]>` | Executes a parameterized query |
|
|
26
|
+
| `executeDefs` | `connId: number, defs: QueryDef[], options?: (ResultMeta \| undefined)[]` | `Promise<unknown[][]>` | Executes query definitions with optional result parsing |
|
|
27
|
+
| `bulkInsert` | `connId: number, tableName: string, columnDefs: Record<string, ColumnMeta>, records: Record<string, unknown>[]` | `Promise<void>` | Performs bulk insert |
|
|
28
|
+
|
|
29
|
+
Configuration: Reads from the `"orm"` section of the server config file (`.config.json`).
|
|
30
|
+
|
|
31
|
+
## `OrmServiceType`
|
|
32
|
+
|
|
33
|
+
Type alias for the ORM service methods. Useful for client-side type sharing.
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
export type OrmServiceType = ServiceMethods<typeof OrmService>;
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## `AutoUpdateService`
|
|
40
|
+
|
|
41
|
+
Built-in auto-update service definition. Provides version lookup for client applications. Does not require authentication.
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
export const AutoUpdateService: ServiceDefinition;
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Registered with the name `"AutoUpdate"`. Scans `{clientPath}/{platform}/updates/` for versioned files (`.exe` for desktop, `.apk` for Android).
|
|
48
|
+
|
|
49
|
+
Methods:
|
|
50
|
+
|
|
51
|
+
| Method | Parameters | Return | Description |
|
|
52
|
+
|--------|-----------|--------|-------------|
|
|
53
|
+
| `getLastVersion` | `platform: string` | `Promise<{ version: string; downloadPath: string } \| undefined>` | Returns the latest version info for the given platform. Uses `semver.maxSatisfying` to find the highest version |
|
|
54
|
+
|
|
55
|
+
## `AutoUpdateServiceType`
|
|
56
|
+
|
|
57
|
+
Type alias for the auto-update service methods.
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
export type AutoUpdateServiceType = ServiceMethods<typeof AutoUpdateService>;
|
|
61
|
+
```
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Config Manager
|
|
2
|
+
|
|
3
|
+
## `getConfig`
|
|
4
|
+
|
|
5
|
+
Loads a JSON configuration file with automatic caching and file-watching for live reload.
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
export async function getConfig<TConfig>(
|
|
9
|
+
filePath: string,
|
|
10
|
+
): Promise<TConfig | undefined>;
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
| Parameter | Type | Description |
|
|
14
|
+
|-----------|------|-------------|
|
|
15
|
+
| `filePath` | `string` | Absolute path to the JSON config file |
|
|
16
|
+
|
|
17
|
+
**Returns:** `Promise<TConfig | undefined>` -- Parsed config object, or `undefined` if the file does not exist.
|
|
18
|
+
|
|
19
|
+
Behavior:
|
|
20
|
+
- **Caching:** Uses `LazyGcMap` with 10-minute GC interval and 1-hour expiry
|
|
21
|
+
- **File watching:** Registers a file watcher on first load. Config is automatically reloaded when the file changes
|
|
22
|
+
- **Expiry:** When a cache entry expires, the associated file watcher is cleaned up
|
|
23
|
+
- **Deletion:** If the config file is deleted, the cache entry and watcher are removed
|
|
24
|
+
- Used internally by `ServiceContext.getConfig()` to load root and client config files
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# HTTP Handlers
|
|
2
|
+
|
|
3
|
+
## `handleHttpRequest`
|
|
4
|
+
|
|
5
|
+
Handles HTTP RPC requests. Supports both GET (with JSON query parameter) and POST (with JSON array body) methods.
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
export async function handleHttpRequest<TAuthInfo = unknown>(
|
|
9
|
+
req: FastifyRequest,
|
|
10
|
+
reply: FastifyReply,
|
|
11
|
+
jwtSecret: string | undefined,
|
|
12
|
+
runMethod: (def: {
|
|
13
|
+
serviceName: string;
|
|
14
|
+
methodName: string;
|
|
15
|
+
params: unknown[];
|
|
16
|
+
http: { clientName: string; authTokenPayload?: AuthTokenPayload<TAuthInfo> };
|
|
17
|
+
}) => Promise<unknown>,
|
|
18
|
+
): Promise<void>;
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
| Parameter | Type | Description |
|
|
22
|
+
|-----------|------|-------------|
|
|
23
|
+
| `req` | `FastifyRequest` | Fastify request (expects `/:service/:method` route params) |
|
|
24
|
+
| `reply` | `FastifyReply` | Fastify reply |
|
|
25
|
+
| `jwtSecret` | `string \| undefined` | JWT secret for token verification |
|
|
26
|
+
| `runMethod` | callback | Service method executor |
|
|
27
|
+
|
|
28
|
+
Request requirements:
|
|
29
|
+
- Header `x-sd-client-name` is required
|
|
30
|
+
- GET: requires `?json=<encoded-params>` query parameter
|
|
31
|
+
- POST: requires JSON array body
|
|
32
|
+
- Authorization header (optional): `Bearer <token>`
|
|
33
|
+
|
|
34
|
+
## `handleUpload`
|
|
35
|
+
|
|
36
|
+
Handles multipart file uploads. Saves files to `{rootPath}/www/uploads/` with UUID-based filenames.
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
export async function handleUpload(
|
|
40
|
+
req: FastifyRequest,
|
|
41
|
+
reply: FastifyReply,
|
|
42
|
+
rootPath: string,
|
|
43
|
+
jwtSecret: string | undefined,
|
|
44
|
+
): Promise<void>;
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
| Parameter | Type | Description |
|
|
48
|
+
|-----------|------|-------------|
|
|
49
|
+
| `req` | `FastifyRequest` | Fastify multipart request |
|
|
50
|
+
| `reply` | `FastifyReply` | Fastify reply |
|
|
51
|
+
| `rootPath` | `string` | Server root path |
|
|
52
|
+
| `jwtSecret` | `string \| undefined` | JWT secret for authentication |
|
|
53
|
+
|
|
54
|
+
Behavior:
|
|
55
|
+
- Requires multipart request and valid Authorization header
|
|
56
|
+
- Saves each file with a UUID filename preserving the original extension
|
|
57
|
+
- Returns `ServiceUploadResult[]` on success
|
|
58
|
+
- Cleans up all files on error (both partially written and already saved)
|
|
59
|
+
|
|
60
|
+
## `handleStaticFile`
|
|
61
|
+
|
|
62
|
+
Serves static files from `{rootPath}/www/` with security guards.
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
export async function handleStaticFile(
|
|
66
|
+
req: FastifyRequest,
|
|
67
|
+
reply: FastifyReply,
|
|
68
|
+
rootPath: string,
|
|
69
|
+
urlPath: string,
|
|
70
|
+
): Promise<void>;
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
| Parameter | Type | Description |
|
|
74
|
+
|-----------|------|-------------|
|
|
75
|
+
| `req` | `FastifyRequest` | Fastify request |
|
|
76
|
+
| `reply` | `FastifyReply` | Fastify reply |
|
|
77
|
+
| `rootPath` | `string` | Server root path |
|
|
78
|
+
| `urlPath` | `string` | Decoded URL path (without leading slash) |
|
|
79
|
+
|
|
80
|
+
Security:
|
|
81
|
+
- Path traversal attack prevention (rejects paths outside `{rootPath}/www/`)
|
|
82
|
+
- Hidden file access denied (files starting with `.` return 403)
|
|
83
|
+
- Directory requests redirect to add trailing slash, then serve `index.html`
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Legacy V1
|
|
2
|
+
|
|
3
|
+
## `handleV1Connection`
|
|
4
|
+
|
|
5
|
+
Handles V1 legacy WebSocket connections. Only supports the `SdAutoUpdateService.getLastVersion` command. All other requests return an upgrade-required error.
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
export function handleV1Connection(
|
|
9
|
+
socket: WebSocket,
|
|
10
|
+
autoUpdateMethods: { getLastVersion: (platform: string) => Promise<any> },
|
|
11
|
+
clientNameSetter?: (clientName: string | undefined) => void,
|
|
12
|
+
): void;
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
| Parameter | Type | Description |
|
|
16
|
+
|-----------|------|-------------|
|
|
17
|
+
| `socket` | `WebSocket` (from `ws`) | Raw WebSocket connection |
|
|
18
|
+
| `autoUpdateMethods` | `{ getLastVersion: (platform: string) => Promise<any> }` | Auto-update method implementations |
|
|
19
|
+
| `clientNameSetter` | `((clientName: string \| undefined) => void)?` | Callback to set the legacy client name on the context |
|
|
20
|
+
|
|
21
|
+
V1 protocol:
|
|
22
|
+
- Sends `{ name: "connected" }` on connection
|
|
23
|
+
- Expects JSON messages with `{ uuid, command, params, clientName? }` format
|
|
24
|
+
- Responds with `{ name: "response", reqUuid, state: "success"|"error", body }` format
|
|
25
|
+
- Only `SdAutoUpdateService.getLastVersion` is supported; all other commands return `UPGRADE_REQUIRED` error
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Protocol Wrapper
|
|
2
|
+
|
|
3
|
+
## `ServerProtocolWrapper`
|
|
4
|
+
|
|
5
|
+
Server-side protocol wrapper that automatically offloads heavy message encoding/decoding to a worker thread. Light operations are processed on the main thread.
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
export interface ServerProtocolWrapper {
|
|
9
|
+
encode(uuid: string, message: ServiceMessage): Promise<{ chunks: Bytes[]; totalSize: number }>;
|
|
10
|
+
decode(bytes: Bytes): Promise<ServiceMessageDecodeResult<ServiceMessage>>;
|
|
11
|
+
dispose(): void;
|
|
12
|
+
}
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
| Method | Parameters | Return | Description |
|
|
16
|
+
|--------|-----------|--------|-------------|
|
|
17
|
+
| `encode` | `uuid: string, message: ServiceMessage` | `Promise<{ chunks: Bytes[]; totalSize: number }>` | Encodes a message. Offloads to worker for messages with `Uint8Array` bodies |
|
|
18
|
+
| `decode` | `bytes: Bytes` | `Promise<ServiceMessageDecodeResult<ServiceMessage>>` | Decodes binary data. Offloads to worker for payloads > 30KB |
|
|
19
|
+
| `dispose` | none | `void` | Disposes the underlying protocol's GC timer |
|
|
20
|
+
|
|
21
|
+
## `createServerProtocolWrapper`
|
|
22
|
+
|
|
23
|
+
Creates a `ServerProtocolWrapper` instance.
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
export function createServerProtocolWrapper(): ServerProtocolWrapper;
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
**Returns:** `ServerProtocolWrapper`
|
|
30
|
+
|
|
31
|
+
Worker thread details:
|
|
32
|
+
- Uses `@simplysm/core-node` `Worker` (Node.js `worker_threads`)
|
|
33
|
+
- Shared singleton worker instance across all protocol wrappers
|
|
34
|
+
- Worker memory limit: 4096 MB (old generation)
|
|
35
|
+
- Size threshold for worker offloading: 30KB
|
|
36
|
+
- Encode uses worker when body contains `Uint8Array` elements
|
|
37
|
+
- Decode uses worker when total bytes exceed 30KB
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Server Options
|
|
2
|
+
|
|
3
|
+
## `ServiceServerOptions`
|
|
4
|
+
|
|
5
|
+
Server configuration options.
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
export interface ServiceServerOptions {
|
|
9
|
+
rootPath: string;
|
|
10
|
+
port: number;
|
|
11
|
+
ssl?: {
|
|
12
|
+
pfxBytes: Uint8Array;
|
|
13
|
+
passphrase: string;
|
|
14
|
+
};
|
|
15
|
+
auth?: {
|
|
16
|
+
jwtSecret: string;
|
|
17
|
+
} | false;
|
|
18
|
+
services: ServiceDefinition[];
|
|
19
|
+
}
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
| Field | Type | Description |
|
|
23
|
+
|-------|------|-------------|
|
|
24
|
+
| `rootPath` | `string` | Root directory path. Static files are served from `{rootPath}/www/`, config loaded from `{rootPath}/.config.json` |
|
|
25
|
+
| `port` | `number` | Server listen port |
|
|
26
|
+
| `ssl` | `{ pfxBytes: Uint8Array; passphrase: string }?` | SSL/TLS configuration using PFX certificate |
|
|
27
|
+
| `ssl.pfxBytes` | `Uint8Array` | PFX certificate file contents |
|
|
28
|
+
| `ssl.passphrase` | `string` | PFX certificate passphrase |
|
|
29
|
+
| `auth` | `{ jwtSecret: string } \| false` | Authentication configuration. `undefined` = auto-detect (error if auth-wrapped services exist). `false` = explicitly disable auth checks. `{ jwtSecret }` = enable JWT auth |
|
|
30
|
+
| `auth.jwtSecret` | `string` | JWT signing/verification secret (HS256) |
|
|
31
|
+
| `services` | `ServiceDefinition[]` | Array of service definitions to register |
|