@simplysm/service-server 13.0.71 → 13.0.74

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 CHANGED
@@ -2,78 +2,61 @@
2
2
 
3
3
  Simplysm package - service module (server)
4
4
 
5
+ Provides a full-featured HTTP/WebSocket server built on Fastify for hosting Simplysm services. Includes JWT-based authentication, ORM integration, file upload/static serving, SMTP email, and auto-update capabilities.
6
+
5
7
  ## Installation
6
8
 
9
+ ```bash
7
10
  pnpm add @simplysm/service-server
8
-
9
- ## Source Index
10
-
11
- ### Types
12
-
13
- | Source | Exports | Description | Test |
14
- |--------|---------|-------------|------|
15
- | `src/types/server-options.ts` | `ServiceServerOptions` | Configuration interface for the service server (port, SSL, auth, services) | - |
16
-
17
- ### Auth
18
-
19
- | Source | Exports | Description | Test |
20
- |--------|---------|-------------|------|
21
- | `src/auth/auth-token-payload.ts` | `AuthTokenPayload` | JWT payload interface extending JWTPayload with roles and auth data | - |
22
- | `src/auth/jwt-manager.ts` | `signJwt`, `verifyJwt`, `decodeJwt` | Sign, verify, and decode HS256 JWT tokens using the jose library | - |
23
-
24
- ### Core
25
-
26
- | Source | Exports | Description | Test |
27
- |--------|---------|-------------|------|
28
- | `src/core/define-service.ts` | `ServiceContext`, `createServiceContext`, `getServiceAuthPermissions`, `auth`, `ServiceDefinition`, `defineService`, `ServiceMethods` | Define services with typed context, auth wrappers, and method type extraction | `define-service.spec.ts` |
29
- | `src/core/service-executor.ts` | `runServiceMethod` | Resolve a service method call and enforce auth permission checks | `service-executor.spec.ts` |
30
-
31
- ### Transport - Socket
32
-
33
- | Source | Exports | Description | Test |
34
- |--------|---------|-------------|------|
35
- | `src/transport/socket/websocket-handler.ts` | `WebSocketHandler`, `createWebSocketHandler` | Manage multiple WebSocket connections, route messages, and broadcast events | - |
36
- | `src/transport/socket/service-socket.ts` | `ServiceSocket`, `createServiceSocket` | Manage a single WebSocket connection with protocol encoding and keep-alive | - |
37
-
38
- ### Transport - HTTP
39
-
40
- | Source | Exports | Description | Test |
41
- |--------|---------|-------------|------|
42
- | `src/transport/http/http-request-handler.ts` | `handleHttpRequest` | Handle HTTP GET/POST API requests with JWT auth and parameter parsing | - |
43
- | `src/transport/http/upload-handler.ts` | `handleUpload` | Handle multipart file uploads with JWT auth and UUID-based storage | - |
44
- | `src/transport/http/static-file-handler.ts` | `handleStaticFile` | Serve static files from the www directory with path traversal protection | - |
45
-
46
- ### Protocol
47
-
48
- | Source | Exports | Description | Test |
49
- |--------|---------|-------------|------|
50
- | `src/protocol/protocol-wrapper.ts` | `ProtocolWrapper`, `createProtocolWrapper` | Encode/decode service messages with automatic worker thread offloading | - |
51
-
52
- ### Services
53
-
54
- | Source | Exports | Description | Test |
55
- |--------|---------|-------------|------|
56
- | `src/services/orm-service.ts` | `OrmService`, `OrmServiceType` | Built-in service exposing ORM database operations over WebSocket | `orm-service.spec.ts` |
57
- | `src/services/auto-update-service.ts` | `AutoUpdateService`, `AutoUpdateServiceType` | Built-in service for serving the latest app update package by platform | - |
58
-
59
- ### Utils
60
-
61
- | Source | Exports | Description | Test |
62
- |--------|---------|-------------|------|
63
- | `src/utils/config-manager.ts` | `getConfig` | Load and cache JSON config files with live-reload via file watcher | - |
64
-
65
- ### Legacy
66
-
67
- | Source | Exports | Description | Test |
68
- |--------|---------|-------------|------|
69
- | `src/legacy/v1-auto-update-handler.ts` | `handleV1Connection` | Handle V1 legacy WebSocket clients for auto-update only | - |
70
-
71
- ### Main
72
-
73
- | Source | Exports | Description | Test |
74
- |--------|---------|-------------|------|
75
- | `src/service-server.ts` | `ServiceServer`, `createServiceServer` | Main Fastify-based HTTP/WebSocket server with routing and graceful shutdown | - |
76
-
77
- ## License
78
-
79
- Apache-2.0
11
+ ```
12
+
13
+ ## Architecture Overview
14
+
15
+ The package is organized into four areas:
16
+
17
+ - **Auth** — JWT token signing, verification, and decoding (`signJwt`, `verifyJwt`, `decodeJwt`, `AuthTokenPayload`).
18
+ - **Services** Service definition API (`defineService`, `auth`, `ServiceContext`, `runServiceMethod`) and three ready-to-use built-in services (`OrmService`, `AutoUpdateService`, `SmtpClientService`).
19
+ - **Transport** — WebSocket connection management (`WebSocketHandler`, `ServiceSocket`), HTTP route handlers (`handleHttpRequest`, `handleUpload`, `handleStaticFile`), and the binary protocol layer (`ProtocolWrapper`). Also includes the legacy v1 compatibility handler.
20
+ - **Server** — The `ServiceServer` class and `createServiceServer` factory that wire everything together, plus the `getConfig` utility for reading `.config.json`.
21
+
22
+ ## Quick Start
23
+
24
+ ```typescript
25
+ import { createServiceServer, defineService, OrmService } from "@simplysm/service-server";
26
+
27
+ const GreetService = defineService("Greet", (ctx) => ({
28
+ hello: (name: string) => `Hello, ${name}!`,
29
+ }));
30
+
31
+ const server = createServiceServer({
32
+ rootPath: "./dist",
33
+ port: 3000,
34
+ auth: { jwtSecret: "my-secret" },
35
+ services: [GreetService, OrmService],
36
+ });
37
+
38
+ await server.listen();
39
+ ```
40
+
41
+ ## Exported Types
42
+
43
+ | Type | Description |
44
+ |---|---|
45
+ | `ServiceServerOptions` | Options object for `ServiceServer` / `createServiceServer` |
46
+ | `AuthTokenPayload<TAuthInfo>` | JWT payload with `roles` and typed `data` |
47
+ | `ServiceContext<TAuthInfo>` | Context injected into every service factory |
48
+ | `ServiceDefinition<TMethods>` | Return type of `defineService` |
49
+ | `ServiceMethods<TDefinition>` | Extracts method map type from a `ServiceDefinition` |
50
+ | `WebSocketHandler` | Manages all active WebSocket connections |
51
+ | `ServiceSocket` | Represents a single active WebSocket connection |
52
+ | `ProtocolWrapper` | Encode/decode service messages with optional worker offload |
53
+ | `OrmServiceType` | Method map type for `OrmService` |
54
+ | `AutoUpdateServiceType` | Method map type for `AutoUpdateService` |
55
+ | `SmtpClientServiceType` | Method map type for `SmtpClientService` |
56
+
57
+ ## Detailed Documentation
58
+
59
+ - [docs/auth.md](docs/auth.md) `AuthTokenPayload`, `signJwt`, `verifyJwt`, `decodeJwt`
60
+ - [docs/services.md](docs/services.md) `defineService`, `auth`, `ServiceContext`, `runServiceMethod`, `OrmService`, `AutoUpdateService`, `SmtpClientService`
61
+ - [docs/transport.md](docs/transport.md) — `WebSocketHandler`, `ServiceSocket`, `handleHttpRequest`, `handleUpload`, `handleStaticFile`, `ProtocolWrapper`, legacy handler
62
+ - [docs/server.md](docs/server.md) — `ServiceServerOptions`, `ServiceServer`, `createServiceServer`, `getConfig`
package/dist/index.d.ts CHANGED
@@ -11,6 +11,7 @@ export * from "./transport/http/static-file-handler";
11
11
  export * from "./protocol/protocol-wrapper";
12
12
  export * from "./services/orm-service";
13
13
  export * from "./services/auto-update-service";
14
+ export * from "./services/smtp-client-service";
14
15
  export * from "./utils/config-manager";
15
16
  export * from "./legacy/v1-auto-update-handler";
16
17
  export * from "./service-server";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["..\\src\\index.ts"],"names":[],"mappings":"AACA,cAAc,wBAAwB,CAAC;AAGvC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,oBAAoB,CAAC;AAGnC,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AAGxC,cAAc,sCAAsC,CAAC;AACrD,cAAc,mCAAmC,CAAC;AAGlD,cAAc,uCAAuC,CAAC;AACtD,cAAc,iCAAiC,CAAC;AAChD,cAAc,sCAAsC,CAAC;AAGrD,cAAc,6BAA6B,CAAC;AAG5C,cAAc,wBAAwB,CAAC;AACvC,cAAc,gCAAgC,CAAC;AAG/C,cAAc,wBAAwB,CAAC;AAGvC,cAAc,iCAAiC,CAAC;AAGhD,cAAc,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["..\\src\\index.ts"],"names":[],"mappings":"AACA,cAAc,wBAAwB,CAAC;AAGvC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,oBAAoB,CAAC;AAGnC,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AAGxC,cAAc,sCAAsC,CAAC;AACrD,cAAc,mCAAmC,CAAC;AAGlD,cAAc,uCAAuC,CAAC;AACtD,cAAc,iCAAiC,CAAC;AAChD,cAAc,sCAAsC,CAAC;AAGrD,cAAc,6BAA6B,CAAC;AAG5C,cAAc,wBAAwB,CAAC;AACvC,cAAc,gCAAgC,CAAC;AAC/C,cAAc,gCAAgC,CAAC;AAG/C,cAAc,wBAAwB,CAAC;AAGvC,cAAc,iCAAiC,CAAC;AAGhD,cAAc,kBAAkB,CAAC"}
package/dist/index.js CHANGED
@@ -11,6 +11,7 @@ export * from "./transport/http/static-file-handler.js";
11
11
  export * from "./protocol/protocol-wrapper.js";
12
12
  export * from "./services/orm-service.js";
13
13
  export * from "./services/auto-update-service.js";
14
+ export * from "./services/smtp-client-service.js";
14
15
  export * from "./utils/config-manager.js";
15
16
  export * from "./legacy/v1-auto-update-handler.js";
16
17
  export * from "./service-server.js";
package/dist/index.js.map CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/index.ts"],
4
- "mappings": "AACA,cAAc;AAGd,cAAc;AACd,cAAc;AAGd,cAAc;AACd,cAAc;AAGd,cAAc;AACd,cAAc;AAGd,cAAc;AACd,cAAc;AACd,cAAc;AAGd,cAAc;AAGd,cAAc;AACd,cAAc;AAGd,cAAc;AAGd,cAAc;AAGd,cAAc;",
4
+ "mappings": "AACA,cAAc;AAGd,cAAc;AACd,cAAc;AAGd,cAAc;AACd,cAAc;AAGd,cAAc;AACd,cAAc;AAGd,cAAc;AACd,cAAc;AACd,cAAc;AAGd,cAAc;AAGd,cAAc;AACd,cAAc;AACd,cAAc;AAGd,cAAc;AAGd,cAAc;AAGd,cAAc;",
5
5
  "names": []
6
6
  }
@@ -0,0 +1,8 @@
1
+ import { type ServiceMethods } from "../core/define-service";
2
+ import type { SmtpClientSendByDefaultOption, SmtpClientSendOption } from "@simplysm/service-common";
3
+ export declare const SmtpClientService: import("..").ServiceDefinition<{
4
+ send(options: SmtpClientSendOption): Promise<string>;
5
+ sendByConfig(configName: string, options: SmtpClientSendByDefaultOption): Promise<string>;
6
+ }>;
7
+ export type SmtpClientServiceType = ServiceMethods<typeof SmtpClientService>;
8
+ //# sourceMappingURL=smtp-client-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"smtp-client-service.d.ts","sourceRoot":"","sources":["..\\..\\src\\services\\smtp-client-service.ts"],"names":[],"mappings":"AACA,OAAO,EAAiB,KAAK,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC5E,OAAO,KAAK,EAEV,6BAA6B,EAC7B,oBAAoB,EACrB,MAAM,0BAA0B,CAAC;AAElC,eAAO,MAAM,iBAAiB;kBACR,oBAAoB,GAAG,OAAO,CAAC,MAAM,CAAC;6BA6B3B,MAAM,WAAW,6BAA6B,GAAG,OAAO,CAAC,MAAM,CAAC;EAkB9F,CAAC;AAEJ,MAAM,MAAM,qBAAqB,GAAG,cAAc,CAAC,OAAO,iBAAiB,CAAC,CAAC"}
@@ -0,0 +1,46 @@
1
+ import nodemailer from "nodemailer";
2
+ import { defineService } from "../core/define-service.js";
3
+ const SmtpClientService = defineService("SmtpClient", (ctx) => ({
4
+ async send(options) {
5
+ return new Promise((resolve, reject) => {
6
+ const transport = nodemailer.createTransport({
7
+ host: options.host,
8
+ port: options.port,
9
+ secure: options.secure,
10
+ auth: options.user != null ? {
11
+ user: options.user,
12
+ pass: options.pass
13
+ } : void 0,
14
+ tls: {
15
+ rejectUnauthorized: false
16
+ }
17
+ });
18
+ transport.sendMail(options, (err, info) => {
19
+ if (err) {
20
+ reject(err);
21
+ return;
22
+ }
23
+ resolve(info.messageId);
24
+ });
25
+ });
26
+ },
27
+ async sendByConfig(configName, options) {
28
+ const config = (await ctx.getConfig("smtp"))[configName];
29
+ if (config == null) {
30
+ throw new Error(`SMTP config not found: ${configName}`);
31
+ }
32
+ return this.send({
33
+ user: config.user,
34
+ pass: config.pass,
35
+ host: config.host,
36
+ port: config.port,
37
+ secure: config.secure,
38
+ from: `"${config.senderName}" <${config.senderEmail ?? config.user}>`,
39
+ ...options
40
+ });
41
+ }
42
+ }));
43
+ export {
44
+ SmtpClientService
45
+ };
46
+ //# sourceMappingURL=smtp-client-service.js.map
@@ -0,0 +1,6 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/services/smtp-client-service.ts"],
4
+ "mappings": "AAAA,OAAO,gBAAgB;AACvB,SAAS,qBAA0C;AAO5C,MAAM,oBAAoB,cAAc,cAAc,CAAC,SAAS;AAAA,EACrE,MAAM,KAAK,SAAgD;AACzD,WAAO,IAAI,QAAgB,CAAC,SAAS,WAAW;AAC9C,YAAM,YAAY,WAAW,gBAAgB;AAAA,QAC3C,MAAM,QAAQ;AAAA,QACd,MAAM,QAAQ;AAAA,QACd,QAAQ,QAAQ;AAAA,QAChB,MACE,QAAQ,QAAQ,OACZ;AAAA,UACE,MAAM,QAAQ;AAAA,UACd,MAAM,QAAQ;AAAA,QAChB,IACA;AAAA,QACN,KAAK;AAAA,UACH,oBAAoB;AAAA,QACtB;AAAA,MACF,CAAC;AAED,gBAAU,SAAS,SAAuC,CAAC,KAAK,SAAS;AACvE,YAAI,KAAK;AACP,iBAAO,GAAG;AACV;AAAA,QACF;AAEA,gBAAQ,KAAK,SAAS;AAAA,MACxB,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,aAAa,YAAoB,SAAyD;AAC9F,UAAM,UACJ,MAAM,IAAI,UAA+D,MAAM,GAC/E,UAAU;AACZ,QAAI,UAAU,MAAM;AAClB,YAAM,IAAI,MAAM,0BAA0B,UAAU,EAAE;AAAA,IACxD;AAEA,WAAO,KAAK,KAAK;AAAA,MACf,MAAM,OAAO;AAAA,MACb,MAAM,OAAO;AAAA,MACb,MAAM,OAAO;AAAA,MACb,MAAM,OAAO;AAAA,MACb,QAAQ,OAAO;AAAA,MACf,MAAM,IAAI,OAAO,UAAU,MAAM,OAAO,eAAe,OAAO,IAAI;AAAA,MAClE,GAAG;AAAA,IACL,CAAC;AAAA,EACH;AACF,EAAE;",
5
+ "names": []
6
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@simplysm/service-server",
3
- "version": "13.0.71",
3
+ "version": "13.0.74",
4
4
  "description": "Simplysm package - service module (server)",
5
5
  "author": "simplysm",
6
6
  "license": "Apache-2.0",
@@ -21,7 +21,7 @@
21
21
  "dependencies": {
22
22
  "@fastify/cors": "^11.2.0",
23
23
  "@fastify/helmet": "^13.0.2",
24
- "@fastify/middie": "^9.1.0",
24
+ "@fastify/middie": "^9.2.0",
25
25
  "@fastify/multipart": "^9.4.0",
26
26
  "@fastify/reply-from": "^12.6.0",
27
27
  "@fastify/static": "^9.0.0",
@@ -31,16 +31,18 @@
31
31
  "fastify": "^5.7.4",
32
32
  "jose": "^6.1.3",
33
33
  "mime": "^4.1.0",
34
+ "nodemailer": "^8.0.1",
34
35
  "semver": "^7.7.4",
35
36
  "utf-8-validate": "^6.0.6",
36
37
  "ws": "^8.19.0",
37
- "@simplysm/core-node": "13.0.71",
38
- "@simplysm/core-common": "13.0.71",
39
- "@simplysm/orm-common": "13.0.71",
40
- "@simplysm/orm-node": "13.0.71",
41
- "@simplysm/service-common": "13.0.71"
38
+ "@simplysm/core-common": "13.0.74",
39
+ "@simplysm/core-node": "13.0.74",
40
+ "@simplysm/orm-common": "13.0.74",
41
+ "@simplysm/orm-node": "13.0.74",
42
+ "@simplysm/service-common": "13.0.74"
42
43
  },
43
44
  "devDependencies": {
45
+ "@types/nodemailer": "^6.4.23",
44
46
  "@types/semver": "^7.7.1",
45
47
  "@types/ws": "^8.18.1"
46
48
  }
package/src/index.ts CHANGED
@@ -24,6 +24,7 @@ export * from "./protocol/protocol-wrapper";
24
24
  // Services
25
25
  export * from "./services/orm-service";
26
26
  export * from "./services/auto-update-service";
27
+ export * from "./services/smtp-client-service";
27
28
 
28
29
  // Utils
29
30
  export * from "./utils/config-manager";
@@ -0,0 +1,59 @@
1
+ import nodemailer from "nodemailer";
2
+ import { defineService, type ServiceMethods } from "../core/define-service";
3
+ import type {
4
+ SmtpClientDefaultConfig,
5
+ SmtpClientSendByDefaultOption,
6
+ SmtpClientSendOption,
7
+ } from "@simplysm/service-common";
8
+
9
+ export const SmtpClientService = defineService("SmtpClient", (ctx) => ({
10
+ async send(options: SmtpClientSendOption): Promise<string> {
11
+ return new Promise<string>((resolve, reject) => {
12
+ const transport = nodemailer.createTransport({
13
+ host: options.host,
14
+ port: options.port,
15
+ secure: options.secure,
16
+ auth:
17
+ options.user != null
18
+ ? {
19
+ user: options.user,
20
+ pass: options.pass,
21
+ }
22
+ : undefined,
23
+ tls: {
24
+ rejectUnauthorized: false,
25
+ },
26
+ });
27
+
28
+ transport.sendMail(options as nodemailer.SendMailOptions, (err, info) => {
29
+ if (err) {
30
+ reject(err);
31
+ return;
32
+ }
33
+
34
+ resolve(info.messageId);
35
+ });
36
+ });
37
+ },
38
+
39
+ async sendByConfig(configName: string, options: SmtpClientSendByDefaultOption): Promise<string> {
40
+ const config = (
41
+ await ctx.getConfig<Record<string, SmtpClientDefaultConfig | undefined>>("smtp")
42
+ )[configName];
43
+ if (config == null) {
44
+ throw new Error(`SMTP config not found: ${configName}`);
45
+ }
46
+
47
+ return this.send({
48
+ user: config.user,
49
+ pass: config.pass,
50
+ host: config.host,
51
+ port: config.port,
52
+ secure: config.secure,
53
+ from: `"${config.senderName}" <${config.senderEmail ?? config.user}>`,
54
+ ...options,
55
+ });
56
+ },
57
+ }));
58
+
59
+ export type SmtpClientServiceType = ServiceMethods<typeof SmtpClientService>;