@simplysm/service-server 13.0.98 → 13.0.99
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 +127 -62
- package/docs/auth.md +18 -7
- package/docs/core.md +57 -85
- package/docs/server.md +73 -153
- package/docs/services.md +30 -148
- package/docs/transport.md +56 -44
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# @simplysm/service-server
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Simplysm package - service module (server)
|
|
4
|
+
|
|
5
|
+
Fastify-based service server with WebSocket support, JWT authentication, and built-in ORM/SMTP/auto-update services. Works with `@simplysm/service-client` for client-server communication.
|
|
4
6
|
|
|
5
7
|
## Installation
|
|
6
8
|
|
|
@@ -8,68 +10,108 @@ Service module (server) -- Fastify-based service server with WebSocket support,
|
|
|
8
10
|
npm install @simplysm/service-server
|
|
9
11
|
```
|
|
10
12
|
|
|
11
|
-
##
|
|
13
|
+
## API Overview
|
|
12
14
|
|
|
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
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
15
|
+
### Types
|
|
16
|
+
| API | Type | Description |
|
|
17
|
+
|-----|------|-------------|
|
|
18
|
+
| `ServiceServerOptions` | interface | Server config (rootPath, port, ssl, auth, services) |
|
|
19
|
+
|
|
20
|
+
-> See [docs/server.md](./docs/server.md) for details.
|
|
21
|
+
|
|
22
|
+
### Auth
|
|
23
|
+
| API | Type | Description |
|
|
24
|
+
|-----|------|-------------|
|
|
25
|
+
| `AuthTokenPayload` | interface | JWT payload with roles and custom data |
|
|
26
|
+
| `signJwt` | function | Sign a JWT token (HS256, 12h expiry) |
|
|
27
|
+
| `verifyJwt` | function | Verify and decode a JWT token |
|
|
28
|
+
| `decodeJwt` | function | Decode JWT without verification |
|
|
29
|
+
|
|
30
|
+
-> See [docs/auth.md](./docs/auth.md) for details.
|
|
31
|
+
|
|
32
|
+
### Core
|
|
33
|
+
| API | Type | Description |
|
|
34
|
+
|-----|------|-------------|
|
|
35
|
+
| `ServiceContext` | interface | Context with server, socket, auth, config access |
|
|
36
|
+
| `createServiceContext` | function | Create a service context |
|
|
37
|
+
| `ServiceDefinition` | interface | Service definition (name, factory, auth) |
|
|
38
|
+
| `defineService` | function | Define a service with name and factory |
|
|
39
|
+
| `auth` | function | Auth wrapper for services/methods (login/roles) |
|
|
40
|
+
| `getServiceAuthPermissions` | function | Read auth permissions from wrapped function |
|
|
41
|
+
| `ServiceMethods` | type | Extract method types from ServiceDefinition |
|
|
42
|
+
| `executeServiceMethod` | function | Execute service method with auth checks |
|
|
43
|
+
|
|
44
|
+
-> See [docs/core.md](./docs/core.md) for details.
|
|
45
|
+
|
|
46
|
+
### Transport - Socket
|
|
47
|
+
| API | Type | Description |
|
|
48
|
+
|-----|------|-------------|
|
|
49
|
+
| `WebSocketHandler` | interface | Multi-connection WebSocket manager |
|
|
50
|
+
| `createWebSocketHandler` | function | Create WebSocket handler |
|
|
51
|
+
| `ServiceSocket` | interface | Single WebSocket connection manager |
|
|
52
|
+
| `createServiceSocket` | function | Create service socket |
|
|
53
|
+
|
|
54
|
+
-> See [docs/transport.md](./docs/transport.md) for details.
|
|
55
|
+
|
|
56
|
+
### Transport - HTTP
|
|
57
|
+
| API | Type | Description |
|
|
58
|
+
|-----|------|-------------|
|
|
59
|
+
| `handleHttpRequest` | function | Handle HTTP API requests |
|
|
60
|
+
| `handleUpload` | function | Handle file upload (multipart) |
|
|
61
|
+
| `handleStaticFile` | function | Serve static files from www/ |
|
|
62
|
+
|
|
63
|
+
-> See [docs/transport.md](./docs/transport.md) for details.
|
|
64
|
+
|
|
65
|
+
### Protocol
|
|
66
|
+
| API | Type | Description |
|
|
67
|
+
|-----|------|-------------|
|
|
68
|
+
| `ServerProtocolWrapper` | interface | Server protocol with worker thread offloading |
|
|
69
|
+
| `createServerProtocolWrapper` | function | Create server protocol wrapper |
|
|
70
|
+
|
|
71
|
+
-> See [docs/transport.md](./docs/transport.md) for details.
|
|
72
|
+
|
|
73
|
+
### Services
|
|
74
|
+
| API | Type | Description |
|
|
75
|
+
|-----|------|-------------|
|
|
76
|
+
| `OrmService` | const | Built-in ORM service (DB connection, queries) |
|
|
77
|
+
| `AutoUpdateService` | const | Built-in auto-update service |
|
|
78
|
+
| `SmtpClientService` | const | Built-in SMTP email service |
|
|
79
|
+
|
|
80
|
+
-> See [docs/services.md](./docs/services.md) for details.
|
|
81
|
+
|
|
82
|
+
### Utils
|
|
83
|
+
| API | Type | Description |
|
|
84
|
+
|-----|------|-------------|
|
|
85
|
+
| `getConfig` | function | Read JSON config with caching and live-reload |
|
|
58
86
|
|
|
59
|
-
|
|
87
|
+
-> See [docs/server.md](./docs/server.md) for details.
|
|
88
|
+
|
|
89
|
+
### Main
|
|
90
|
+
| API | Type | Description |
|
|
91
|
+
|-----|------|-------------|
|
|
92
|
+
| `ServiceServer` | class | Main server (Fastify, WebSocket, auth, events) |
|
|
93
|
+
| `createServiceServer` | function | Factory to create ServiceServer |
|
|
94
|
+
|
|
95
|
+
-> See [docs/server.md](./docs/server.md) for details.
|
|
96
|
+
|
|
97
|
+
### Legacy
|
|
98
|
+
| API | Type | Description |
|
|
99
|
+
|-----|------|-------------|
|
|
100
|
+
| `handleV1Connection` | function | V1 legacy client handler (auto-update only) |
|
|
101
|
+
|
|
102
|
+
-> See [docs/server.md](./docs/server.md) for details.
|
|
103
|
+
|
|
104
|
+
## Usage Examples
|
|
105
|
+
|
|
106
|
+
### Basic Server Setup
|
|
60
107
|
|
|
61
108
|
```typescript
|
|
62
|
-
import {
|
|
63
|
-
|
|
64
|
-
defineService,
|
|
65
|
-
auth,
|
|
66
|
-
OrmService,
|
|
67
|
-
AutoUpdateService,
|
|
68
|
-
} from "@simplysm/service-server";
|
|
109
|
+
import { createServiceServer, defineService, auth } from "@simplysm/service-server";
|
|
110
|
+
import { OrmService, AutoUpdateService } from "@simplysm/service-server";
|
|
69
111
|
|
|
70
112
|
// Define a custom service
|
|
71
113
|
const HealthService = defineService("Health", (ctx) => ({
|
|
72
|
-
check: () => ({ status: "ok" }),
|
|
114
|
+
check: () => ({ status: "ok", time: new Date().toISOString() }),
|
|
73
115
|
}));
|
|
74
116
|
|
|
75
117
|
// Define an authenticated service
|
|
@@ -89,10 +131,33 @@ const server = createServiceServer({
|
|
|
89
131
|
await server.listen();
|
|
90
132
|
```
|
|
91
133
|
|
|
92
|
-
|
|
134
|
+
### JWT Authentication
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
import { signJwt, verifyJwt } from "@simplysm/service-server";
|
|
93
138
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
139
|
+
// Sign token (in login handler)
|
|
140
|
+
const token = await server.signAuthToken({
|
|
141
|
+
roles: ["user", "admin"],
|
|
142
|
+
data: { userId: 123, name: "John" },
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
// Verify token
|
|
146
|
+
const payload = await server.verifyAuthToken(token);
|
|
147
|
+
console.log(payload.data.name); // "John"
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Server-Side Event Emission
|
|
151
|
+
|
|
152
|
+
```typescript
|
|
153
|
+
import { defineEvent } from "@simplysm/service-common";
|
|
154
|
+
|
|
155
|
+
const OrderUpdated = defineEvent<{ orderId: number }, { status: string }>("OrderUpdated");
|
|
156
|
+
|
|
157
|
+
// Emit to all listeners with matching orderId
|
|
158
|
+
await server.emitEvent(
|
|
159
|
+
OrderUpdated,
|
|
160
|
+
(info) => info.orderId === 123,
|
|
161
|
+
{ status: "shipped" },
|
|
162
|
+
);
|
|
163
|
+
```
|
package/docs/auth.md
CHANGED
|
@@ -4,7 +4,7 @@ JWT-based authentication utilities using the `jose` library (HS256 algorithm).
|
|
|
4
4
|
|
|
5
5
|
## `AuthTokenPayload`
|
|
6
6
|
|
|
7
|
-
JWT token payload
|
|
7
|
+
JWT token payload with roles and custom data. Extends `JWTPayload` from `jose`.
|
|
8
8
|
|
|
9
9
|
```typescript
|
|
10
10
|
interface AuthTokenPayload<TAuthInfo = unknown> extends JWTPayload {
|
|
@@ -13,9 +13,14 @@ interface AuthTokenPayload<TAuthInfo = unknown> extends JWTPayload {
|
|
|
13
13
|
}
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
+
| Field | Type | Description |
|
|
17
|
+
|-------|------|-------------|
|
|
18
|
+
| `roles` | `string[]` | User roles for permission checking |
|
|
19
|
+
| `data` | `TAuthInfo` | Custom auth data (user info, etc.) |
|
|
20
|
+
|
|
16
21
|
## `signJwt`
|
|
17
22
|
|
|
18
|
-
Sign a JWT token.
|
|
23
|
+
Sign a JWT token with HS256 algorithm. Token expires in 12 hours.
|
|
19
24
|
|
|
20
25
|
```typescript
|
|
21
26
|
async function signJwt<TAuthInfo = unknown>(
|
|
@@ -24,9 +29,14 @@ async function signJwt<TAuthInfo = unknown>(
|
|
|
24
29
|
): Promise<string>;
|
|
25
30
|
```
|
|
26
31
|
|
|
32
|
+
| Parameter | Type | Description |
|
|
33
|
+
|-----------|------|-------------|
|
|
34
|
+
| `jwtSecret` | `string` | Secret key for signing |
|
|
35
|
+
| `payload` | `AuthTokenPayload<TAuthInfo>` | Token payload |
|
|
36
|
+
|
|
27
37
|
## `verifyJwt`
|
|
28
38
|
|
|
29
|
-
Verify a JWT token
|
|
39
|
+
Verify and decode a JWT token. Throws on expired or invalid tokens.
|
|
30
40
|
|
|
31
41
|
```typescript
|
|
32
42
|
async function verifyJwt<TAuthInfo = unknown>(
|
|
@@ -35,13 +45,14 @@ async function verifyJwt<TAuthInfo = unknown>(
|
|
|
35
45
|
): Promise<AuthTokenPayload<TAuthInfo>>;
|
|
36
46
|
```
|
|
37
47
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
48
|
+
| Parameter | Type | Description |
|
|
49
|
+
|-----------|------|-------------|
|
|
50
|
+
| `jwtSecret` | `string` | Secret key for verification |
|
|
51
|
+
| `token` | `string` | JWT token string |
|
|
41
52
|
|
|
42
53
|
## `decodeJwt`
|
|
43
54
|
|
|
44
|
-
Decode a JWT token without verification.
|
|
55
|
+
Decode a JWT token without verification (for reading payload only).
|
|
45
56
|
|
|
46
57
|
```typescript
|
|
47
58
|
function decodeJwt<TAuthInfo = unknown>(token: string): AuthTokenPayload<TAuthInfo>;
|
package/docs/core.md
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
# Core
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
## `ServiceContext`
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
Context object passed to service factory functions.
|
|
5
|
+
Context object passed to service factory functions. Provides access to server, socket, auth info, and configuration.
|
|
8
6
|
|
|
9
7
|
```typescript
|
|
10
8
|
interface ServiceContext<TAuthInfo = unknown> {
|
|
@@ -14,7 +12,9 @@ interface ServiceContext<TAuthInfo = unknown> {
|
|
|
14
12
|
clientName: string;
|
|
15
13
|
authTokenPayload?: AuthTokenPayload<TAuthInfo>;
|
|
16
14
|
};
|
|
17
|
-
legacy?: {
|
|
15
|
+
legacy?: {
|
|
16
|
+
clientName?: string;
|
|
17
|
+
};
|
|
18
18
|
|
|
19
19
|
get authInfo(): TAuthInfo | undefined;
|
|
20
20
|
get clientName(): string | undefined;
|
|
@@ -23,13 +23,20 @@ interface ServiceContext<TAuthInfo = unknown> {
|
|
|
23
23
|
}
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
| Property | Type | Description |
|
|
27
|
+
|----------|------|-------------|
|
|
28
|
+
| `server` | `ServiceServer<TAuthInfo>` | Server instance |
|
|
29
|
+
| `socket` | `ServiceSocket` | WebSocket connection (if via WebSocket) |
|
|
30
|
+
| `http` | `object` | HTTP request info (if via HTTP) |
|
|
31
|
+
| `legacy` | `object` | V1 legacy context (auto-update only) |
|
|
32
|
+
| `authInfo` | `TAuthInfo \| undefined` | Authenticated user data |
|
|
33
|
+
| `clientName` | `string \| undefined` | Client name |
|
|
34
|
+
| `clientPath` | `string \| undefined` | Resolved client path on disk |
|
|
35
|
+
| `getConfig()` | `<T>(section) => Promise<T>` | Read config from `.config.json` files |
|
|
36
|
+
|
|
37
|
+
## `createServiceContext`
|
|
31
38
|
|
|
32
|
-
|
|
39
|
+
Create a service context instance.
|
|
33
40
|
|
|
34
41
|
```typescript
|
|
35
42
|
function createServiceContext<TAuthInfo = unknown>(
|
|
@@ -40,44 +47,9 @@ function createServiceContext<TAuthInfo = unknown>(
|
|
|
40
47
|
): ServiceContext<TAuthInfo>;
|
|
41
48
|
```
|
|
42
49
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
## Auth Helpers
|
|
46
|
-
|
|
47
|
-
### `getServiceAuthPermissions`
|
|
48
|
-
|
|
49
|
-
Read auth permissions from an `auth()`-wrapped function. Returns `undefined` if not wrapped.
|
|
50
|
-
|
|
51
|
-
```typescript
|
|
52
|
-
function getServiceAuthPermissions(fn: Function): string[] | undefined;
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
### `auth`
|
|
56
|
-
|
|
57
|
-
Auth wrapper for service factories and methods.
|
|
58
|
-
|
|
59
|
-
```typescript
|
|
60
|
-
// Login required (no specific roles)
|
|
61
|
-
function auth<TFunction extends (...args: any[]) => any>(fn: TFunction): TFunction;
|
|
62
|
-
|
|
63
|
-
// Login required with specific roles
|
|
64
|
-
function auth<TFunction extends (...args: any[]) => any>(
|
|
65
|
-
permissions: string[],
|
|
66
|
-
fn: TFunction,
|
|
67
|
-
): TFunction;
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
**Usage levels:**
|
|
71
|
-
- Service-level: `auth((ctx) => ({ ... }))` -- all methods require login
|
|
72
|
-
- Service-level with roles: `auth(["admin"], (ctx) => ({ ... }))`
|
|
73
|
-
- Method-level: `auth(() => result)` -- this method requires login
|
|
74
|
-
- Method-level with roles: `auth(["admin"], () => result)`
|
|
75
|
-
|
|
76
|
-
---
|
|
50
|
+
## `ServiceDefinition`
|
|
77
51
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
### `ServiceDefinition`
|
|
52
|
+
Service definition object. Contains name, factory function, and optional auth permissions.
|
|
81
53
|
|
|
82
54
|
```typescript
|
|
83
55
|
interface ServiceDefinition<TMethods = Record<string, (...args: any[]) => any>> {
|
|
@@ -87,7 +59,13 @@ interface ServiceDefinition<TMethods = Record<string, (...args: any[]) => any>>
|
|
|
87
59
|
}
|
|
88
60
|
```
|
|
89
61
|
|
|
90
|
-
|
|
62
|
+
| Field | Type | Description |
|
|
63
|
+
|-------|------|-------------|
|
|
64
|
+
| `name` | `string` | Service name |
|
|
65
|
+
| `factory` | `(ctx: ServiceContext) => TMethods` | Factory function that creates method object |
|
|
66
|
+
| `authPermissions` | `string[]` | Required permissions (from `auth()` wrapper) |
|
|
67
|
+
|
|
68
|
+
## `defineService`
|
|
91
69
|
|
|
92
70
|
Define a service with a name and factory function.
|
|
93
71
|
|
|
@@ -98,40 +76,48 @@ function defineService<TMethods extends Record<string, (...args: any[]) => any>>
|
|
|
98
76
|
): ServiceDefinition<TMethods>;
|
|
99
77
|
```
|
|
100
78
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
}));
|
|
106
|
-
|
|
107
|
-
const UserService = defineService("User", auth((ctx) => ({
|
|
108
|
-
getProfile: () => ctx.authInfo,
|
|
109
|
-
adminOnly: auth(["admin"], () => "admin"),
|
|
110
|
-
})));
|
|
111
|
-
```
|
|
79
|
+
| Parameter | Type | Description |
|
|
80
|
+
|-----------|------|-------------|
|
|
81
|
+
| `name` | `string` | Service name |
|
|
82
|
+
| `factory` | `(ctx: ServiceContext) => TMethods` | Factory function |
|
|
112
83
|
|
|
113
|
-
|
|
84
|
+
## `auth`
|
|
114
85
|
|
|
115
|
-
|
|
86
|
+
Auth wrapper for service factories and methods. Can be applied at service-level or method-level with optional role requirements.
|
|
116
87
|
|
|
117
88
|
```typescript
|
|
118
|
-
|
|
119
|
-
|
|
89
|
+
function auth<TFunction extends (...args: any[]) => any>(fn: TFunction): TFunction;
|
|
90
|
+
function auth<TFunction extends (...args: any[]) => any>(
|
|
91
|
+
permissions: string[],
|
|
92
|
+
fn: TFunction,
|
|
93
|
+
): TFunction;
|
|
120
94
|
```
|
|
121
95
|
|
|
122
|
-
|
|
96
|
+
| Overload | Description |
|
|
97
|
+
|----------|-------------|
|
|
98
|
+
| `auth(fn)` | Require login (any authenticated user) |
|
|
99
|
+
| `auth(["admin"], fn)` | Require specific roles |
|
|
100
|
+
|
|
101
|
+
## `getServiceAuthPermissions`
|
|
102
|
+
|
|
103
|
+
Read auth permissions from an `auth()`-wrapped function. Returns `undefined` if not wrapped.
|
|
104
|
+
|
|
123
105
|
```typescript
|
|
124
|
-
|
|
125
|
-
// Client: client.getService<UserServiceType>("User");
|
|
106
|
+
function getServiceAuthPermissions(fn: Function): string[] | undefined;
|
|
126
107
|
```
|
|
127
108
|
|
|
128
|
-
|
|
109
|
+
## `ServiceMethods`
|
|
110
|
+
|
|
111
|
+
Extract method signatures from a `ServiceDefinition` for client-side type sharing.
|
|
129
112
|
|
|
130
|
-
|
|
113
|
+
```typescript
|
|
114
|
+
type ServiceMethods<TDefinition> =
|
|
115
|
+
TDefinition extends ServiceDefinition<infer M> ? M : never;
|
|
116
|
+
```
|
|
131
117
|
|
|
132
|
-
|
|
118
|
+
## `executeServiceMethod`
|
|
133
119
|
|
|
134
|
-
Execute a service method with
|
|
120
|
+
Execute a service method with authentication and authorization checks.
|
|
135
121
|
|
|
136
122
|
```typescript
|
|
137
123
|
async function executeServiceMethod(
|
|
@@ -145,17 +131,3 @@ async function executeServiceMethod(
|
|
|
145
131
|
},
|
|
146
132
|
): Promise<unknown>;
|
|
147
133
|
```
|
|
148
|
-
|
|
149
|
-
**Behavior:**
|
|
150
|
-
1. Finds the service definition by name
|
|
151
|
-
2. Validates the client name (path traversal guard)
|
|
152
|
-
3. Creates a `ServiceContext`
|
|
153
|
-
4. Invokes the factory to create the method object
|
|
154
|
-
5. Checks auth permissions (method-level first, then service-level fallback)
|
|
155
|
-
6. Executes the method with provided params
|
|
156
|
-
|
|
157
|
-
Throws:
|
|
158
|
-
- `"Service [name] not found."` if service is not registered
|
|
159
|
-
- `"Method [service.method] not found."` if method does not exist
|
|
160
|
-
- `"Login is required."` if auth is required but no token is present
|
|
161
|
-
- `"Insufficient permissions."` if the user lacks required roles
|
package/docs/server.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# Server
|
|
2
2
|
|
|
3
|
-
## ServiceServerOptions
|
|
3
|
+
## `ServiceServerOptions`
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Configuration options for `ServiceServer`.
|
|
6
6
|
|
|
7
7
|
```typescript
|
|
8
8
|
interface ServiceServerOptions {
|
|
@@ -19,188 +19,108 @@ interface ServiceServerOptions {
|
|
|
19
19
|
}
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
| Field | Type | Description |
|
|
23
|
+
|-------|------|-------------|
|
|
24
|
+
| `rootPath` | `string` | Root path for static files, uploads, and config |
|
|
25
|
+
| `port` | `number` | Server port |
|
|
26
|
+
| `ssl` | `object` | SSL/TLS config (PFX certificate) |
|
|
27
|
+
| `ssl.pfxBytes` | `Uint8Array` | PFX certificate bytes |
|
|
28
|
+
| `ssl.passphrase` | `string` | Certificate passphrase |
|
|
29
|
+
| `auth` | `object` | Authentication config |
|
|
30
|
+
| `auth.jwtSecret` | `string` | JWT signing secret |
|
|
31
|
+
| `services` | `ServiceDefinition[]` | Service definitions to register |
|
|
23
32
|
|
|
24
|
-
|
|
33
|
+
## `ServiceServer`
|
|
25
34
|
|
|
26
|
-
|
|
27
|
-
interface ServerProtocolWrapper {
|
|
28
|
-
encode(uuid: string, message: ServiceMessage): Promise<{ chunks: Bytes[]; totalSize: number }>;
|
|
29
|
-
decode(bytes: Bytes): Promise<ServiceMessageDecodeResult<ServiceMessage>>;
|
|
30
|
-
dispose(): void;
|
|
31
|
-
}
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
### `createServerProtocolWrapper`
|
|
35
|
-
|
|
36
|
-
```typescript
|
|
37
|
-
function createServerProtocolWrapper(): ServerProtocolWrapper;
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
**Behavior:**
|
|
41
|
-
- Messages with `Uint8Array` body or arrays containing `Uint8Array` are encoded via worker thread
|
|
42
|
-
- Messages larger than 30KB are decoded via worker thread
|
|
43
|
-
- Worker is a lazy singleton shared across all protocol wrappers (4GB memory limit)
|
|
44
|
-
- Small messages are processed on the main thread using `createServiceProtocol()`
|
|
45
|
-
|
|
46
|
-
---
|
|
47
|
-
|
|
48
|
-
## Config Utilities
|
|
49
|
-
|
|
50
|
-
### `getConfig`
|
|
51
|
-
|
|
52
|
-
Read and cache a JSON config file with automatic live-reload via file watcher.
|
|
53
|
-
|
|
54
|
-
```typescript
|
|
55
|
-
async function getConfig<TConfig>(filePath: string): Promise<TConfig | undefined>;
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
**Behavior:**
|
|
59
|
-
- Returns `undefined` if file does not exist
|
|
60
|
-
- Caches loaded config in a `LazyGcMap` (expires after 1 hour, GC runs every 10 minutes)
|
|
61
|
-
- Registers a file watcher that live-reloads config on changes
|
|
62
|
-
- Watcher is cleaned up when the cache entry expires
|
|
63
|
-
|
|
64
|
-
---
|
|
65
|
-
|
|
66
|
-
## Legacy
|
|
67
|
-
|
|
68
|
-
### `handleV1Connection`
|
|
69
|
-
|
|
70
|
-
V1 legacy client handler. Only auto-update is supported; all other requests return an upgrade-required error.
|
|
71
|
-
|
|
72
|
-
```typescript
|
|
73
|
-
function handleV1Connection(
|
|
74
|
-
socket: WebSocket,
|
|
75
|
-
autoUpdateMethods: { getLastVersion: (platform: string) => Promise<any> },
|
|
76
|
-
clientNameSetter?: (clientName: string | undefined) => void,
|
|
77
|
-
): void;
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
---
|
|
81
|
-
|
|
82
|
-
## ServiceServer
|
|
83
|
-
|
|
84
|
-
Main server class. Extends `EventEmitter<{ ready: void; close: void }>`.
|
|
35
|
+
Fastify-based service server with WebSocket support, JWT authentication, and built-in services. Extends `EventEmitter`.
|
|
85
36
|
|
|
86
37
|
```typescript
|
|
87
|
-
class ServiceServer<TAuthInfo = unknown> extends EventEmitter<{
|
|
38
|
+
class ServiceServer<TAuthInfo = unknown> extends EventEmitter<{
|
|
39
|
+
ready: void;
|
|
40
|
+
close: void;
|
|
41
|
+
}> {
|
|
88
42
|
isOpen: boolean;
|
|
89
43
|
readonly fastify: FastifyInstance;
|
|
90
44
|
readonly options: ServiceServerOptions;
|
|
91
45
|
|
|
92
46
|
constructor(options: ServiceServerOptions);
|
|
93
47
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
48
|
+
listen(): Promise<void>;
|
|
49
|
+
close(): Promise<void>;
|
|
50
|
+
broadcastReload(clientName: string | undefined, changedFileSet: Set<string>): Promise<void>;
|
|
51
|
+
emitEvent<TInfo, TData>(
|
|
98
52
|
eventDef: ServiceEventDef<TInfo, TData>,
|
|
99
53
|
infoSelector: (item: TInfo) => boolean,
|
|
100
54
|
data: TData,
|
|
101
55
|
): Promise<void>;
|
|
102
|
-
|
|
103
|
-
|
|
56
|
+
signAuthToken(payload: AuthTokenPayload<TAuthInfo>): Promise<string>;
|
|
57
|
+
verifyAuthToken(token: string): Promise<AuthTokenPayload<TAuthInfo>>;
|
|
104
58
|
}
|
|
105
59
|
```
|
|
106
60
|
|
|
107
|
-
|
|
61
|
+
| Property | Type | Description |
|
|
62
|
+
|----------|------|-------------|
|
|
63
|
+
| `isOpen` | `boolean` | Whether server is listening |
|
|
64
|
+
| `fastify` | `FastifyInstance` | Underlying Fastify instance |
|
|
65
|
+
| `options` | `ServiceServerOptions` | Server options |
|
|
66
|
+
|
|
67
|
+
| Method | Description |
|
|
68
|
+
|--------|-------------|
|
|
69
|
+
| `listen()` | Start the server (registers plugins, routes, WebSocket, graceful shutdown) |
|
|
70
|
+
| `close()` | Stop the server (close all connections) |
|
|
71
|
+
| `broadcastReload()` | Broadcast reload to all connected clients |
|
|
72
|
+
| `emitEvent()` | Emit event to matching clients |
|
|
73
|
+
| `signAuthToken()` | Sign a JWT token |
|
|
74
|
+
| `verifyAuthToken()` | Verify and decode a JWT token |
|
|
75
|
+
|
|
76
|
+
### Registered Routes
|
|
77
|
+
|
|
78
|
+
| Route | Method | Description |
|
|
79
|
+
|-------|--------|-------------|
|
|
80
|
+
| `/api/:service/:method` | GET/POST | HTTP service method endpoint |
|
|
81
|
+
| `/upload` | POST | File upload endpoint (multipart) |
|
|
82
|
+
| `/ws` | WebSocket | WebSocket service endpoint (Protocol V2) |
|
|
83
|
+
| `/` | WebSocket | WebSocket endpoint (V1 legacy + V2) |
|
|
84
|
+
| `/*` | GET | Static file serving from `www/` |
|
|
85
|
+
|
|
86
|
+
### Registered Plugins
|
|
87
|
+
|
|
88
|
+
- `@fastify/websocket` - WebSocket support
|
|
89
|
+
- `@fastify/helmet` - Security headers (CSP, HSTS)
|
|
90
|
+
- `@fastify/multipart` - File upload
|
|
91
|
+
- `@fastify/static` - Static file serving
|
|
92
|
+
- `@fastify/cors` - CORS support
|
|
108
93
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
- `@fastify/websocket` -- WebSocket support
|
|
112
|
-
- `@fastify/helmet` -- Security headers
|
|
113
|
-
- `@fastify/multipart` -- File upload support
|
|
114
|
-
- `@fastify/static` -- Static file serving
|
|
115
|
-
- `@fastify/cors` -- Cross-origin resource sharing
|
|
116
|
-
|
|
117
|
-
**Routes:**
|
|
118
|
-
- `GET/POST /api/:service/:method` -- HTTP API endpoint
|
|
119
|
-
- `POST /upload` -- File upload endpoint
|
|
120
|
-
- `GET /ws` or `GET /` (WebSocket) -- WebSocket connection (V2 protocol with V1 legacy fallback)
|
|
121
|
-
- `GET/POST/PUT/DELETE/PATCH/HEAD /*` -- Static file wildcard handler
|
|
122
|
-
|
|
123
|
-
Registers graceful shutdown handlers for `SIGINT` and `SIGTERM` (10s timeout before force exit).
|
|
124
|
-
|
|
125
|
-
### `close`
|
|
126
|
-
|
|
127
|
-
Close all WebSocket connections and shut down the Fastify server.
|
|
128
|
-
|
|
129
|
-
### `broadcastReload`
|
|
130
|
-
|
|
131
|
-
Broadcast a reload message to all connected WebSocket clients.
|
|
132
|
-
|
|
133
|
-
```typescript
|
|
134
|
-
async broadcastReload(
|
|
135
|
-
clientName: string | undefined,
|
|
136
|
-
changedFileSet: Set<string>,
|
|
137
|
-
): Promise<void>;
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
### `emitEvent`
|
|
94
|
+
## `createServiceServer`
|
|
141
95
|
|
|
142
|
-
|
|
96
|
+
Factory function to create a `ServiceServer` instance.
|
|
143
97
|
|
|
144
98
|
```typescript
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
data: TData,
|
|
149
|
-
): Promise<void>;
|
|
99
|
+
function createServiceServer<TAuthInfo = unknown>(
|
|
100
|
+
options: ServiceServerOptions,
|
|
101
|
+
): ServiceServer<TAuthInfo>;
|
|
150
102
|
```
|
|
151
103
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
Sign a JWT auth token.
|
|
155
|
-
|
|
156
|
-
```typescript
|
|
157
|
-
async signAuthToken(payload: AuthTokenPayload<TAuthInfo>): Promise<string>;
|
|
158
|
-
```
|
|
104
|
+
## Utils
|
|
159
105
|
|
|
160
|
-
### `
|
|
106
|
+
### `getConfig`
|
|
161
107
|
|
|
162
|
-
|
|
108
|
+
Read JSON configuration from file with caching and live-reload via file watcher. Cache expires after 1 hour.
|
|
163
109
|
|
|
164
110
|
```typescript
|
|
165
|
-
async
|
|
111
|
+
async function getConfig<TConfig>(filePath: string): Promise<TConfig | undefined>;
|
|
166
112
|
```
|
|
167
113
|
|
|
168
|
-
##
|
|
169
|
-
|
|
170
|
-
Factory function.
|
|
114
|
+
## Legacy
|
|
171
115
|
|
|
172
|
-
|
|
173
|
-
function createServiceServer<TAuthInfo = unknown>(
|
|
174
|
-
options: ServiceServerOptions,
|
|
175
|
-
): ServiceServer<TAuthInfo>;
|
|
176
|
-
```
|
|
116
|
+
### `handleV1Connection`
|
|
177
117
|
|
|
178
|
-
|
|
118
|
+
V1 legacy client handler. Only auto-update is supported; all other requests return an upgrade-required error.
|
|
179
119
|
|
|
180
120
|
```typescript
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
AutoUpdateService,
|
|
187
|
-
SmtpClientService,
|
|
188
|
-
} from "@simplysm/service-server";
|
|
189
|
-
|
|
190
|
-
const MyService = defineService("My", auth((ctx) => ({
|
|
191
|
-
hello: (name: string) => `Hello, ${name}!`,
|
|
192
|
-
})));
|
|
193
|
-
|
|
194
|
-
const server = createServiceServer({
|
|
195
|
-
rootPath: "/app",
|
|
196
|
-
port: 3000,
|
|
197
|
-
auth: { jwtSecret: process.env.JWT_SECRET! },
|
|
198
|
-
services: [MyService, OrmService, AutoUpdateService, SmtpClientService],
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
server.on("ready", () => {
|
|
202
|
-
console.log("Server is ready");
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
await server.listen();
|
|
121
|
+
function handleV1Connection(
|
|
122
|
+
socket: WebSocket,
|
|
123
|
+
autoUpdateMethods: { getLastVersion: (platform: string) => Promise<any> },
|
|
124
|
+
clientNameSetter?: (clientName: string | undefined) => void,
|
|
125
|
+
): void;
|
|
206
126
|
```
|
package/docs/services.md
CHANGED
|
@@ -2,175 +2,57 @@
|
|
|
2
2
|
|
|
3
3
|
Pre-defined service definitions ready to use with `ServiceServer`.
|
|
4
4
|
|
|
5
|
-
## OrmService
|
|
5
|
+
## `OrmService`
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
```typescript
|
|
10
|
-
import { OrmService, type OrmServiceType } from "@simplysm/service-server";
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
### Definition
|
|
7
|
+
Server-side ORM service implementation. Requires authentication. Manages database connections per WebSocket socket (using `WeakMap`). Supports MySQL, MSSQL, and PostgreSQL via `@simplysm/orm-node`.
|
|
14
8
|
|
|
15
9
|
```typescript
|
|
16
10
|
const OrmService: ServiceDefinition;
|
|
17
11
|
```
|
|
18
12
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
### Methods
|
|
13
|
+
Implements the `OrmService` interface from `@simplysm/service-common`:
|
|
22
14
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
rollbackTransaction(connId: number): Promise<void>;
|
|
35
|
-
executeParametrized(connId: number, query: string, params?: unknown[]): Promise<unknown[][]>;
|
|
36
|
-
executeDefs(
|
|
37
|
-
connId: number,
|
|
38
|
-
defs: QueryDef[],
|
|
39
|
-
options?: (ResultMeta | undefined)[],
|
|
40
|
-
): Promise<unknown[][]>;
|
|
41
|
-
bulkInsert(
|
|
42
|
-
connId: number,
|
|
43
|
-
tableName: string,
|
|
44
|
-
columnDefs: Record<string, ColumnMeta>,
|
|
45
|
-
records: Record<string, unknown>[],
|
|
46
|
-
): Promise<void>;
|
|
47
|
-
}
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
**Behavior:**
|
|
51
|
-
- Database connections are tracked per WebSocket socket using a `WeakMap`
|
|
52
|
-
- Connections are automatically cleaned up when the socket closes
|
|
53
|
-
- Configuration is loaded from `.config.json` under the `"orm"` section
|
|
54
|
-
- Supports `mssql-azure` dialect (mapped to `mssql` for query building)
|
|
55
|
-
|
|
56
|
-
**Configuration example** (`.config.json`):
|
|
57
|
-
```json
|
|
58
|
-
{
|
|
59
|
-
"orm": {
|
|
60
|
-
"myDb": {
|
|
61
|
-
"dialect": "mysql",
|
|
62
|
-
"host": "localhost",
|
|
63
|
-
"port": 3306,
|
|
64
|
-
"database": "mydb",
|
|
65
|
-
"user": "root",
|
|
66
|
-
"password": "secret"
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
```
|
|
15
|
+
| Method | Description |
|
|
16
|
+
|--------|-------------|
|
|
17
|
+
| `getInfo()` | Get database dialect and connection info from config |
|
|
18
|
+
| `connect()` | Open a new database connection, returns connection ID |
|
|
19
|
+
| `close()` | Close a database connection |
|
|
20
|
+
| `beginTransaction()` | Begin transaction |
|
|
21
|
+
| `commitTransaction()` | Commit transaction |
|
|
22
|
+
| `rollbackTransaction()` | Rollback transaction |
|
|
23
|
+
| `executeParametrized()` | Execute parameterized SQL |
|
|
24
|
+
| `executeDefs()` | Execute QueryDef array (builds SQL via QueryBuilder) |
|
|
25
|
+
| `bulkInsert()` | Bulk insert records |
|
|
71
26
|
|
|
72
|
-
|
|
27
|
+
**Note:** ORM service requires WebSocket connection (cannot be used over HTTP).
|
|
73
28
|
|
|
74
|
-
## AutoUpdateService
|
|
29
|
+
## `AutoUpdateService`
|
|
75
30
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
```typescript
|
|
79
|
-
import { AutoUpdateService, type AutoUpdateServiceType } from "@simplysm/service-server";
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
### Definition
|
|
31
|
+
Server-side auto-update service implementation. Scans the client's platform-specific `updates/` directory for version files.
|
|
83
32
|
|
|
84
33
|
```typescript
|
|
85
34
|
const AutoUpdateService: ServiceDefinition;
|
|
86
35
|
```
|
|
87
36
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
### Methods
|
|
91
|
-
|
|
92
|
-
```typescript
|
|
93
|
-
interface AutoUpdateServiceType {
|
|
94
|
-
getLastVersion(platform: string): Promise<
|
|
95
|
-
| { version: string; downloadPath: string }
|
|
96
|
-
| undefined
|
|
97
|
-
>;
|
|
98
|
-
}
|
|
99
|
-
```
|
|
37
|
+
Implements the `AutoUpdateService` interface from `@simplysm/service-common`:
|
|
100
38
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
- Other platforms: looks for `.exe` files
|
|
105
|
-
- Returns the highest semver version found
|
|
106
|
-
- Returns `undefined` if no updates directory or no valid versions exist
|
|
39
|
+
| Method | Description |
|
|
40
|
+
|--------|-------------|
|
|
41
|
+
| `getLastVersion(platform)` | Find latest version file for platform (win32, android, etc.) |
|
|
107
42
|
|
|
108
|
-
|
|
43
|
+
Supported platforms and file extensions:
|
|
44
|
+
- `android`: `.apk` files
|
|
45
|
+
- Other platforms: `.exe` files
|
|
109
46
|
|
|
110
|
-
## SmtpClientService
|
|
47
|
+
## `SmtpClientService`
|
|
111
48
|
|
|
112
|
-
SMTP email sending service.
|
|
113
|
-
|
|
114
|
-
```typescript
|
|
115
|
-
import { SmtpClientService, type SmtpClientServiceType } from "@simplysm/service-server";
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
### Definition
|
|
49
|
+
Server-side SMTP email sending service. Uses `nodemailer` under the hood.
|
|
119
50
|
|
|
120
51
|
```typescript
|
|
121
52
|
const SmtpClientService: ServiceDefinition;
|
|
122
53
|
```
|
|
123
54
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
```typescript
|
|
129
|
-
interface SmtpClientServiceType {
|
|
130
|
-
send(options: SmtpClientSendOption): Promise<string>;
|
|
131
|
-
sendByConfig(configName: string, options: SmtpClientSendByDefaultOption): Promise<string>;
|
|
132
|
-
}
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
**`send`** -- Send email with explicit SMTP configuration. Returns the message ID.
|
|
136
|
-
|
|
137
|
-
**`sendByConfig`** -- Send email using server-side SMTP configuration. Configuration is loaded from `.config.json` under the `"smtp"` section.
|
|
138
|
-
|
|
139
|
-
**Configuration example** (`.config.json`):
|
|
140
|
-
```json
|
|
141
|
-
{
|
|
142
|
-
"smtp": {
|
|
143
|
-
"default": {
|
|
144
|
-
"senderName": "My App",
|
|
145
|
-
"senderEmail": "noreply@example.com",
|
|
146
|
-
"user": "smtp-user",
|
|
147
|
-
"pass": "smtp-pass",
|
|
148
|
-
"host": "smtp.example.com",
|
|
149
|
-
"port": 587,
|
|
150
|
-
"secure": false
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
---
|
|
157
|
-
|
|
158
|
-
## Usage
|
|
159
|
-
|
|
160
|
-
Register built-in services when creating the server:
|
|
161
|
-
|
|
162
|
-
```typescript
|
|
163
|
-
import {
|
|
164
|
-
createServiceServer,
|
|
165
|
-
OrmService,
|
|
166
|
-
AutoUpdateService,
|
|
167
|
-
SmtpClientService,
|
|
168
|
-
} from "@simplysm/service-server";
|
|
169
|
-
|
|
170
|
-
const server = createServiceServer({
|
|
171
|
-
rootPath: "/app",
|
|
172
|
-
port: 3000,
|
|
173
|
-
auth: { jwtSecret: "secret" },
|
|
174
|
-
services: [OrmService, AutoUpdateService, SmtpClientService],
|
|
175
|
-
});
|
|
176
|
-
```
|
|
55
|
+
| Method | Description |
|
|
56
|
+
|--------|-------------|
|
|
57
|
+
| `send(options)` | Send email with full SMTP options |
|
|
58
|
+
| `sendByDefault(options)` | Send email using server's default SMTP config |
|
package/docs/transport.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
### `WebSocketHandler`
|
|
6
6
|
|
|
7
|
-
Manages multiple WebSocket connections, routes messages to services, and handles event broadcasting.
|
|
7
|
+
WebSocket handler interface. Manages multiple WebSocket connections, routes messages to services, and handles event broadcasting.
|
|
8
8
|
|
|
9
9
|
```typescript
|
|
10
10
|
interface WebSocketHandler {
|
|
@@ -19,31 +19,32 @@ interface WebSocketHandler {
|
|
|
19
19
|
}
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
+
| Method | Description |
|
|
23
|
+
|--------|-------------|
|
|
24
|
+
| `addSocket()` | Add a new WebSocket connection |
|
|
25
|
+
| `closeAll()` | Close all active connections |
|
|
26
|
+
| `broadcastReload()` | Broadcast reload message to all clients |
|
|
27
|
+
| `emit()` | Emit event to matching clients |
|
|
28
|
+
|
|
22
29
|
### `createWebSocketHandler`
|
|
23
30
|
|
|
31
|
+
Create a WebSocket handler instance.
|
|
32
|
+
|
|
24
33
|
```typescript
|
|
25
34
|
function createWebSocketHandler(
|
|
26
35
|
runMethod: (def: {
|
|
27
36
|
serviceName: string;
|
|
28
37
|
methodName: string;
|
|
29
38
|
params: unknown[];
|
|
30
|
-
socket
|
|
39
|
+
socket: ServiceSocket;
|
|
31
40
|
}) => Promise<unknown>,
|
|
32
|
-
jwtSecret
|
|
41
|
+
jwtSecret?: string,
|
|
33
42
|
): WebSocketHandler;
|
|
34
43
|
```
|
|
35
44
|
|
|
36
|
-
**Behavior:**
|
|
37
|
-
- Routes incoming messages to service methods, auth, and event operations
|
|
38
|
-
- Manages a map of connected `ServiceSocket` instances by client ID
|
|
39
|
-
- Replaces existing connections for the same client ID
|
|
40
|
-
- Handles `auth`, `evt:add`, `evt:remove`, `evt:gets`, `evt:emit`, and service method requests
|
|
41
|
-
|
|
42
|
-
---
|
|
43
|
-
|
|
44
45
|
### `ServiceSocket`
|
|
45
46
|
|
|
46
|
-
Manages a single WebSocket connection with protocol encoding/decoding, ping/pong keep-alive, and event listener tracking.
|
|
47
|
+
Service socket interface. Manages a single WebSocket connection with protocol encoding/decoding, ping/pong keep-alive, and event listener tracking.
|
|
47
48
|
|
|
48
49
|
```typescript
|
|
49
50
|
interface ServiceSocket {
|
|
@@ -58,15 +59,33 @@ interface ServiceSocket {
|
|
|
58
59
|
removeListener(key: string): void;
|
|
59
60
|
getEventListeners(eventName: string): Array<{ key: string; info: unknown }>;
|
|
60
61
|
filterEventTargetKeys(targetKeys: string[]): string[];
|
|
61
|
-
|
|
62
62
|
on(event: "error", handler: (err: Error) => void): void;
|
|
63
63
|
on(event: "close", handler: (code: number) => void): void;
|
|
64
64
|
on(event: "message", handler: (data: { uuid: string; msg: ServiceClientMessage }) => void): void;
|
|
65
65
|
}
|
|
66
66
|
```
|
|
67
67
|
|
|
68
|
+
| Property | Type | Description |
|
|
69
|
+
|----------|------|-------------|
|
|
70
|
+
| `connectedAtDateTime` | `DateTime` | Connection time |
|
|
71
|
+
| `clientName` | `string` | Client name |
|
|
72
|
+
| `connReq` | `FastifyRequest` | Original Fastify request |
|
|
73
|
+
| `authTokenPayload` | `AuthTokenPayload` | Authenticated token payload |
|
|
74
|
+
|
|
75
|
+
| Method | Description |
|
|
76
|
+
|--------|-------------|
|
|
77
|
+
| `close()` | Close the WebSocket connection |
|
|
78
|
+
| `send()` | Send a message to the client |
|
|
79
|
+
| `addListener()` | Register an event listener |
|
|
80
|
+
| `removeListener()` | Remove an event listener |
|
|
81
|
+
| `getEventListeners()` | Get all listeners for an event name |
|
|
82
|
+
| `filterEventTargetKeys()` | Filter target keys that exist in this socket |
|
|
83
|
+
| `on()` | Register event handlers (error, close, message) |
|
|
84
|
+
|
|
68
85
|
### `createServiceSocket`
|
|
69
86
|
|
|
87
|
+
Create a service socket instance.
|
|
88
|
+
|
|
70
89
|
```typescript
|
|
71
90
|
function createServiceSocket(
|
|
72
91
|
socket: WebSocket,
|
|
@@ -76,20 +95,11 @@ function createServiceSocket(
|
|
|
76
95
|
): ServiceSocket;
|
|
77
96
|
```
|
|
78
97
|
|
|
79
|
-
**Behavior:**
|
|
80
|
-
- Wraps raw WebSocket with protocol encoding/decoding via `ServerProtocolWrapper`
|
|
81
|
-
- Sends ping every 5s; terminates if pong not received
|
|
82
|
-
- Handles raw ping/pong packets (`0x01` ping, `0x02` pong)
|
|
83
|
-
- Tracks event listeners per socket for event broadcasting
|
|
84
|
-
- Sends progress notifications for chunked message reception
|
|
85
|
-
|
|
86
|
-
---
|
|
87
|
-
|
|
88
98
|
## HTTP Transport
|
|
89
99
|
|
|
90
100
|
### `handleHttpRequest`
|
|
91
101
|
|
|
92
|
-
Handle HTTP API requests
|
|
102
|
+
Handle HTTP API requests. Routes `POST/GET /api/:service/:method` to service methods.
|
|
93
103
|
|
|
94
104
|
```typescript
|
|
95
105
|
async function handleHttpRequest<TAuthInfo = unknown>(
|
|
@@ -105,15 +115,9 @@ async function handleHttpRequest<TAuthInfo = unknown>(
|
|
|
105
115
|
): Promise<void>;
|
|
106
116
|
```
|
|
107
117
|
|
|
108
|
-
**Behavior:**
|
|
109
|
-
- Requires `x-sd-client-name` header
|
|
110
|
-
- GET: reads params from `?json=...` query parameter
|
|
111
|
-
- POST: reads params from request body (must be an array)
|
|
112
|
-
- Parses `Authorization: Bearer <token>` header if present
|
|
113
|
-
|
|
114
118
|
### `handleUpload`
|
|
115
119
|
|
|
116
|
-
Handle
|
|
120
|
+
Handle file upload requests. Accepts multipart form data with auth token.
|
|
117
121
|
|
|
118
122
|
```typescript
|
|
119
123
|
async function handleUpload(
|
|
@@ -124,15 +128,9 @@ async function handleUpload(
|
|
|
124
128
|
): Promise<void>;
|
|
125
129
|
```
|
|
126
130
|
|
|
127
|
-
**Behavior:**
|
|
128
|
-
- Requires authentication (JWT token in Authorization header)
|
|
129
|
-
- Saves files to `{rootPath}/www/uploads/` with UUID filenames
|
|
130
|
-
- Returns `ServiceUploadResult[]` with path, filename, and size
|
|
131
|
-
- Cleans up incomplete files on error
|
|
132
|
-
|
|
133
131
|
### `handleStaticFile`
|
|
134
132
|
|
|
135
|
-
Handle static file serving.
|
|
133
|
+
Handle static file serving. Serves files from `www/` directory with security checks (path traversal protection, hidden file blocking).
|
|
136
134
|
|
|
137
135
|
```typescript
|
|
138
136
|
async function handleStaticFile(
|
|
@@ -143,10 +141,24 @@ async function handleStaticFile(
|
|
|
143
141
|
): Promise<void>;
|
|
144
142
|
```
|
|
145
143
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
-
|
|
151
|
-
|
|
152
|
-
|
|
144
|
+
## Protocol
|
|
145
|
+
|
|
146
|
+
### `ServerProtocolWrapper`
|
|
147
|
+
|
|
148
|
+
Server-side protocol wrapper interface. Automatically offloads heavy encoding/decoding to a worker thread (>30KB threshold).
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
interface ServerProtocolWrapper {
|
|
152
|
+
encode(uuid: string, message: ServiceMessage): Promise<{ chunks: Bytes[]; totalSize: number }>;
|
|
153
|
+
decode(bytes: Bytes): Promise<ServiceMessageDecodeResult<ServiceMessage>>;
|
|
154
|
+
dispose(): void;
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### `createServerProtocolWrapper`
|
|
159
|
+
|
|
160
|
+
Create a server protocol wrapper instance.
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
function createServerProtocolWrapper(): ServerProtocolWrapper;
|
|
164
|
+
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@simplysm/service-server",
|
|
3
|
-
"version": "13.0.
|
|
3
|
+
"version": "13.0.99",
|
|
4
4
|
"description": "Simplysm package - service module (server)",
|
|
5
5
|
"author": "simplysm",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -36,11 +36,11 @@
|
|
|
36
36
|
"semver": "^7.7.4",
|
|
37
37
|
"utf-8-validate": "^6.0.6",
|
|
38
38
|
"ws": "^8.19.0",
|
|
39
|
-
"@simplysm/core-
|
|
40
|
-
"@simplysm/
|
|
41
|
-
"@simplysm/
|
|
42
|
-
"@simplysm/service-common": "13.0.
|
|
43
|
-
"@simplysm/orm-
|
|
39
|
+
"@simplysm/core-common": "13.0.99",
|
|
40
|
+
"@simplysm/orm-node": "13.0.99",
|
|
41
|
+
"@simplysm/core-node": "13.0.99",
|
|
42
|
+
"@simplysm/service-common": "13.0.99",
|
|
43
|
+
"@simplysm/orm-common": "13.0.99"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"@types/nodemailer": "^7.0.11",
|