@simplysm/service-server 13.0.100 → 14.0.4

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.
Files changed (88) hide show
  1. package/README.md +79 -133
  2. package/dist/auth/auth-token-payload.js +2 -1
  3. package/dist/auth/auth-token-payload.js.map +1 -6
  4. package/dist/auth/jwt-manager.js +21 -21
  5. package/dist/auth/jwt-manager.js.map +1 -6
  6. package/dist/core/define-service.d.ts +12 -12
  7. package/dist/core/define-service.d.ts.map +1 -1
  8. package/dist/core/define-service.js +77 -63
  9. package/dist/core/define-service.js.map +1 -6
  10. package/dist/core/service-executor.d.ts.map +1 -1
  11. package/dist/core/service-executor.js +42 -32
  12. package/dist/core/service-executor.js.map +1 -6
  13. package/dist/index.d.ts +0 -1
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +11 -2
  16. package/dist/index.js.map +1 -6
  17. package/dist/legacy/v1-auto-update-handler.d.ts +2 -2
  18. package/dist/legacy/v1-auto-update-handler.js +42 -35
  19. package/dist/legacy/v1-auto-update-handler.js.map +1 -6
  20. package/dist/protocol/protocol-wrapper.d.ts +9 -9
  21. package/dist/protocol/protocol-wrapper.js +64 -46
  22. package/dist/protocol/protocol-wrapper.js.map +1 -6
  23. package/dist/service-server.d.ts +2 -1
  24. package/dist/service-server.d.ts.map +1 -1
  25. package/dist/service-server.js +183 -165
  26. package/dist/service-server.js.map +1 -6
  27. package/dist/services/auto-update-service.js +35 -34
  28. package/dist/services/auto-update-service.js.map +1 -6
  29. package/dist/services/orm-service.js +114 -120
  30. package/dist/services/orm-service.js.map +1 -6
  31. package/dist/transport/http/http-request-handler.d.ts.map +1 -1
  32. package/dist/transport/http/http-request-handler.js +58 -46
  33. package/dist/transport/http/http-request-handler.js.map +1 -6
  34. package/dist/transport/http/static-file-handler.js +42 -39
  35. package/dist/transport/http/static-file-handler.js.map +1 -6
  36. package/dist/transport/http/upload-handler.d.ts.map +1 -1
  37. package/dist/transport/http/upload-handler.js +60 -55
  38. package/dist/transport/http/upload-handler.js.map +1 -6
  39. package/dist/transport/socket/service-socket.d.ts +13 -13
  40. package/dist/transport/socket/service-socket.js +132 -108
  41. package/dist/transport/socket/service-socket.js.map +1 -6
  42. package/dist/transport/socket/websocket-handler.d.ts +9 -13
  43. package/dist/transport/socket/websocket-handler.d.ts.map +1 -1
  44. package/dist/transport/socket/websocket-handler.js +148 -139
  45. package/dist/transport/socket/websocket-handler.js.map +1 -6
  46. package/dist/types/server-options.d.ts +1 -1
  47. package/dist/types/server-options.d.ts.map +1 -1
  48. package/dist/types/server-options.js +2 -1
  49. package/dist/types/server-options.js.map +1 -6
  50. package/dist/utils/config-manager.js +48 -46
  51. package/dist/utils/config-manager.js.map +1 -6
  52. package/dist/workers/service-protocol.worker.js +8 -11
  53. package/dist/workers/service-protocol.worker.js.map +1 -6
  54. package/docs/auth.md +28 -16
  55. package/docs/core.md +113 -54
  56. package/docs/legacy.md +21 -0
  57. package/docs/main.md +81 -0
  58. package/docs/protocol.md +31 -0
  59. package/docs/services.md +49 -44
  60. package/docs/transport.md +81 -76
  61. package/docs/types.md +31 -0
  62. package/docs/utilities.md +27 -0
  63. package/package.json +12 -13
  64. package/src/auth/jwt-manager.ts +2 -2
  65. package/src/core/define-service.ts +19 -19
  66. package/src/core/service-executor.ts +23 -17
  67. package/src/index.ts +10 -12
  68. package/src/legacy/v1-auto-update-handler.ts +10 -10
  69. package/src/protocol/protocol-wrapper.ts +16 -16
  70. package/src/service-server.ts +51 -43
  71. package/src/services/auto-update-service.ts +1 -1
  72. package/src/services/orm-service.ts +7 -7
  73. package/src/transport/http/http-request-handler.ts +16 -10
  74. package/src/transport/http/static-file-handler.ts +8 -8
  75. package/src/transport/http/upload-handler.ts +16 -9
  76. package/src/transport/socket/service-socket.ts +22 -22
  77. package/src/transport/socket/websocket-handler.ts +50 -70
  78. package/src/types/server-options.ts +1 -1
  79. package/src/utils/config-manager.ts +11 -11
  80. package/dist/services/smtp-client-service.d.ts +0 -8
  81. package/dist/services/smtp-client-service.d.ts.map +0 -1
  82. package/dist/services/smtp-client-service.js +0 -46
  83. package/dist/services/smtp-client-service.js.map +0 -6
  84. package/docs/server.md +0 -126
  85. package/src/services/smtp-client-service.ts +0 -59
  86. package/tests/define-service.spec.ts +0 -66
  87. package/tests/orm-service.spec.ts +0 -83
  88. package/tests/service-executor.spec.ts +0 -114
package/docs/transport.md CHANGED
@@ -1,58 +1,58 @@
1
1
  # Transport
2
2
 
3
+ WebSocket and HTTP transport handlers for routing client messages to services.
4
+
3
5
  ## WebSocket Transport
4
6
 
5
- ### `WebSocketHandler`
7
+ ### WebSocketHandler
6
8
 
7
- WebSocket handler interface. Manages multiple WebSocket connections, routes messages to services, and handles event broadcasting.
9
+ Manages multiple WebSocket connections, routes messages to services, and handles event broadcasting.
8
10
 
9
- ```typescript
11
+ ```ts
10
12
  interface WebSocketHandler {
11
13
  addSocket(socket: WebSocket, clientId: string, clientName: string, connReq: FastifyRequest): void;
12
14
  closeAll(): void;
13
- broadcastReload(clientName: string | undefined, changedFileSet: Set<string>): Promise<void>;
14
- emit<TInfo, TData>(
15
- eventDef: ServiceEventDef<TInfo, TData>,
16
- infoSelector: (item: TInfo) => boolean,
17
- data: TData,
18
- ): Promise<void>;
15
+ emit<TInfo, TData>(eventDef: ServiceEventDef<TInfo, TData>, infoSelector: (item: TInfo) => boolean, data: TData): Promise<void>;
19
16
  }
20
17
  ```
21
18
 
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 |
19
+ | Method | Parameters | Return | Description |
20
+ |--------|-----------|--------|-------------|
21
+ | `addSocket` | `socket: WebSocket`, `clientId: string`, `clientName: string`, `connReq: FastifyRequest` | `void` | Register a new WebSocket connection |
22
+ | `closeAll` | -- | `void` | Close all managed connections |
23
+ | `emit` | `eventDef`, `infoSelector`, `data` | `Promise<void>` | Broadcast an event to matching listeners across all sockets |
28
24
 
29
- ### `createWebSocketHandler`
25
+ ### createWebSocketHandler
30
26
 
31
- Create a WebSocket handler instance.
27
+ Create a `WebSocketHandler` instance.
32
28
 
33
- ```typescript
29
+ ```ts
34
30
  function createWebSocketHandler(
35
31
  runMethod: (def: {
36
32
  serviceName: string;
37
33
  methodName: string;
38
34
  params: unknown[];
39
- socket: ServiceSocket;
35
+ socket?: ServiceSocket;
40
36
  }) => Promise<unknown>,
41
- jwtSecret?: string,
37
+ jwtSecret: string | undefined,
42
38
  ): WebSocketHandler;
43
39
  ```
44
40
 
45
- ### `ServiceSocket`
41
+ | Parameter | Type | Description |
42
+ |-----------|------|-------------|
43
+ | `runMethod` | `(def) => Promise<unknown>` | Callback to execute a service method |
44
+ | `jwtSecret` | `string \| undefined` | JWT secret for authenticating socket connections. `undefined` disables auth. |
45
+
46
+ ### ServiceSocket
46
47
 
47
- Service socket interface. Manages a single WebSocket connection with protocol encoding/decoding, ping/pong keep-alive, and event listener tracking.
48
+ Represents a single WebSocket connection with protocol encoding/decoding, ping/pong keep-alive, and event listener tracking.
48
49
 
49
- ```typescript
50
+ ```ts
50
51
  interface ServiceSocket {
51
52
  readonly connectedAtDateTime: DateTime;
52
53
  readonly clientName: string;
53
54
  readonly connReq: FastifyRequest;
54
55
  authTokenPayload?: AuthTokenPayload;
55
-
56
56
  close(): void;
57
57
  send(uuid: string, msg: ServiceServerMessage): Promise<number>;
58
58
  addListener(key: string, eventName: string, info: unknown): void;
@@ -65,28 +65,27 @@ interface ServiceSocket {
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
-
85
- ### `createServiceSocket`
86
-
87
- Create a service socket instance.
88
-
89
- ```typescript
68
+ | Member | Kind | Type | Description |
69
+ |--------|------|------|-------------|
70
+ | `connectedAtDateTime` | property | `DateTime` | Connection timestamp |
71
+ | `clientName` | property | `string` | Client identifier |
72
+ | `connReq` | property | `FastifyRequest` | Original Fastify request |
73
+ | `authTokenPayload` | property | `AuthTokenPayload?` | Authenticated token payload (set after auth message) |
74
+ | `close` | method | `() => void` | Close the socket |
75
+ | `send` | method | `(uuid, msg) => Promise<number>` | Send a server message; returns number of bytes sent |
76
+ | `addListener` | method | `(key, eventName, info) => void` | Register an event listener on this socket |
77
+ | `removeListener` | method | `(key) => void` | Remove an event listener by key |
78
+ | `getEventListeners` | method | `(eventName) => Array<{ key; info }>` | Get all listeners for an event name |
79
+ | `filterEventTargetKeys` | method | `(targetKeys) => string[]` | Filter target keys to only those on this socket |
80
+ | `on("error")` | method | `(handler) => void` | Subscribe to error events |
81
+ | `on("close")` | method | `(handler) => void` | Subscribe to close events (with close code) |
82
+ | `on("message")` | method | `(handler) => void` | Subscribe to decoded message events |
83
+
84
+ ### createServiceSocket
85
+
86
+ Create a `ServiceSocket` instance wrapping a raw WebSocket.
87
+
88
+ ```ts
90
89
  function createServiceSocket(
91
90
  socket: WebSocket,
92
91
  clientId: string,
@@ -95,13 +94,20 @@ function createServiceSocket(
95
94
  ): ServiceSocket;
96
95
  ```
97
96
 
97
+ | Parameter | Type | Description |
98
+ |-----------|------|-------------|
99
+ | `socket` | `WebSocket` | Raw WebSocket instance |
100
+ | `clientId` | `string` | Unique connection identifier |
101
+ | `clientName` | `string` | Client name |
102
+ | `connReq` | `FastifyRequest` | Fastify request that initiated the upgrade |
103
+
98
104
  ## HTTP Transport
99
105
 
100
- ### `handleHttpRequest`
106
+ ### handleHttpRequest
101
107
 
102
- Handle HTTP API requests. Routes `POST/GET /api/:service/:method` to service methods.
108
+ Handle an HTTP RPC request. Parses the request body, verifies authentication, executes the service method, and sends the response.
103
109
 
104
- ```typescript
110
+ ```ts
105
111
  async function handleHttpRequest<TAuthInfo = unknown>(
106
112
  req: FastifyRequest,
107
113
  reply: FastifyReply,
@@ -115,11 +121,18 @@ async function handleHttpRequest<TAuthInfo = unknown>(
115
121
  ): Promise<void>;
116
122
  ```
117
123
 
118
- ### `handleUpload`
124
+ | Parameter | Type | Description |
125
+ |-----------|------|-------------|
126
+ | `req` | `FastifyRequest` | Fastify request |
127
+ | `reply` | `FastifyReply` | Fastify reply |
128
+ | `jwtSecret` | `string \| undefined` | JWT secret. `undefined` disables auth verification. |
129
+ | `runMethod` | `(def) => Promise<unknown>` | Callback to execute the service method |
119
130
 
120
- Handle file upload requests. Accepts multipart form data with auth token.
131
+ ### handleUpload
121
132
 
122
- ```typescript
133
+ Handle a file upload request. Saves uploaded files to the server's root path and returns upload results.
134
+
135
+ ```ts
123
136
  async function handleUpload(
124
137
  req: FastifyRequest,
125
138
  reply: FastifyReply,
@@ -128,11 +141,18 @@ async function handleUpload(
128
141
  ): Promise<void>;
129
142
  ```
130
143
 
131
- ### `handleStaticFile`
144
+ | Parameter | Type | Description |
145
+ |-----------|------|-------------|
146
+ | `req` | `FastifyRequest` | Fastify request (multipart) |
147
+ | `reply` | `FastifyReply` | Fastify reply |
148
+ | `rootPath` | `string` | Server root path for file storage |
149
+ | `jwtSecret` | `string \| undefined` | JWT secret for auth verification |
150
+
151
+ ### handleStaticFile
132
152
 
133
- Handle static file serving. Serves files from `www/` directory with security checks (path traversal protection, hidden file blocking).
153
+ Serve a static file from the server's root path.
134
154
 
135
- ```typescript
155
+ ```ts
136
156
  async function handleStaticFile(
137
157
  req: FastifyRequest,
138
158
  reply: FastifyReply,
@@ -141,24 +161,9 @@ async function handleStaticFile(
141
161
  ): Promise<void>;
142
162
  ```
143
163
 
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
- ```
164
+ | Parameter | Type | Description |
165
+ |-----------|------|-------------|
166
+ | `req` | `FastifyRequest` | Fastify request |
167
+ | `reply` | `FastifyReply` | Fastify reply |
168
+ | `rootPath` | `string` | Server root directory |
169
+ | `urlPath` | `string` | URL path portion to resolve |
package/docs/types.md ADDED
@@ -0,0 +1,31 @@
1
+ # Types
2
+
3
+ ## ServiceServerOptions
4
+
5
+ Server configuration options passed to `createServiceServer`.
6
+
7
+ ```ts
8
+ 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 for static files and uploads |
25
+ | `port` | `number` | Server listening port |
26
+ | `ssl` | `{ pfxBytes: Uint8Array; passphrase: string }?` | SSL/TLS configuration using a PFX certificate |
27
+ | `ssl.pfxBytes` | `Uint8Array` | PFX certificate bytes |
28
+ | `ssl.passphrase` | `string` | PFX passphrase |
29
+ | `auth` | `{ jwtSecret: string } \| false` | JWT authentication configuration. Set to `false` to disable authentication. |
30
+ | `auth.jwtSecret` | `string` | Secret key for JWT signing and verification |
31
+ | `services` | `ServiceDefinition[]` | Array of service definitions to register |
@@ -0,0 +1,27 @@
1
+ # Utilities
2
+
3
+ ## getConfig
4
+
5
+ Read a configuration section from a JSON file.
6
+
7
+ ```ts
8
+ async function getConfig<TConfig>(filePath: string): Promise<TConfig | undefined>;
9
+ ```
10
+
11
+ | Parameter | Type | Description |
12
+ |-----------|------|-------------|
13
+ | `filePath` | `string` | Path to the configuration JSON file |
14
+
15
+ Returns the parsed configuration object typed as `TConfig`, or `undefined` if the file does not exist.
16
+
17
+ ```ts
18
+ import { getConfig } from "@simplysm/service-server";
19
+
20
+ interface DbConfig {
21
+ host: string;
22
+ port: number;
23
+ database: string;
24
+ }
25
+
26
+ const dbConfig = await getConfig<DbConfig>("config/db.json");
27
+ ```
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@simplysm/service-server",
3
- "version": "13.0.100",
4
- "description": "Simplysm package - service module (server)",
5
- "author": "simplysm",
3
+ "version": "14.0.4",
4
+ "description": "심플리즘 패키지 - 서비스 (server)",
5
+ "author": "심플리즘",
6
6
  "license": "Apache-2.0",
7
7
  "repository": {
8
8
  "type": "git",
@@ -14,9 +14,8 @@
14
14
  "types": "./dist/index.d.ts",
15
15
  "files": [
16
16
  "dist",
17
- "docs",
18
17
  "src",
19
- "tests"
18
+ "docs"
20
19
  ],
21
20
  "sideEffects": false,
22
21
  "dependencies": {
@@ -29,18 +28,18 @@
29
28
  "@fastify/websocket": "^11.2.0",
30
29
  "bufferutil": "^4.1.0",
31
30
  "consola": "^3.4.2",
32
- "fastify": "^5.8.2",
31
+ "fastify": "^5.8.4",
33
32
  "jose": "^6.2.2",
34
33
  "mime": "^4.1.0",
35
- "nodemailer": "^8.0.3",
34
+ "nodemailer": "^8.0.4",
36
35
  "semver": "^7.7.4",
37
36
  "utf-8-validate": "^6.0.6",
38
- "ws": "^8.19.0",
39
- "@simplysm/core-common": "13.0.100",
40
- "@simplysm/core-node": "13.0.100",
41
- "@simplysm/orm-common": "13.0.100",
42
- "@simplysm/service-common": "13.0.100",
43
- "@simplysm/orm-node": "13.0.100"
37
+ "ws": "^8.20.0",
38
+ "@simplysm/core-common": "14.0.4",
39
+ "@simplysm/orm-common": "14.0.4",
40
+ "@simplysm/orm-node": "14.0.4",
41
+ "@simplysm/service-common": "14.0.4",
42
+ "@simplysm/core-node": "14.0.4"
44
43
  },
45
44
  "devDependencies": {
46
45
  "@types/nodemailer": "^7.0.11",
@@ -25,9 +25,9 @@ export async function verifyJwt<TAuthInfo = unknown>(
25
25
  return payload as AuthTokenPayload<TAuthInfo>;
26
26
  } catch (err) {
27
27
  if (err != null && typeof err === "object" && "code" in err && err.code === "ERR_JWT_EXPIRED") {
28
- throw new Error("Token has expired.");
28
+ throw new Error("토큰이 만료되었습니다.");
29
29
  }
30
- throw new Error("Invalid token.");
30
+ throw new Error("유효하지 않은 토큰입니다.");
31
31
  }
32
32
  }
33
33
 
@@ -5,7 +5,7 @@ import { obj } from "@simplysm/core-common";
5
5
  import { getConfig } from "../utils/config-manager";
6
6
  import path from "path";
7
7
 
8
- // ── Context ──
8
+ // ── 컨텍스트 ──
9
9
 
10
10
  export interface ServiceContext<TAuthInfo = unknown> {
11
11
  server: ServiceServer<TAuthInfo>;
@@ -15,7 +15,7 @@ export interface ServiceContext<TAuthInfo = unknown> {
15
15
  authTokenPayload?: AuthTokenPayload<TAuthInfo>;
16
16
  };
17
17
 
18
- /** V1 legacy context (auto-update only) */
18
+ /** V1 레거시 컨텍스트 (자동 업데이트 전용) */
19
19
  legacy?: {
20
20
  clientName?: string;
21
21
  };
@@ -49,7 +49,7 @@ export function createServiceContext<TAuthInfo = unknown>(
49
49
  if (name == null) return undefined;
50
50
 
51
51
  if (name === "" || name.includes("..") || name.includes("/") || name.includes("\\")) {
52
- throw new Error(`Invalid client name: ${name}`);
52
+ throw new Error(`유효하지 않은 클라이언트 이름: ${name}`);
53
53
  }
54
54
 
55
55
  return name;
@@ -79,28 +79,28 @@ export function createServiceContext<TAuthInfo = unknown>(
79
79
  }
80
80
 
81
81
  const config = configParent[section];
82
- if (config == null) throw new Error(`Configuration section not found: ${section}`);
82
+ if (config == null) throw new Error(`설정 섹션을 찾을 없습니다: ${section}`);
83
83
  return config;
84
84
  },
85
85
  };
86
86
  }
87
87
 
88
- // ── Auth ──
88
+ // ── 인증 ──
89
89
 
90
90
  const AUTH_PERMISSIONS = Symbol("authPermissions");
91
91
 
92
- /** Read auth permissions from an auth()-wrapped function. Returns undefined if not wrapped. */
92
+ /** auth()로 래핑된 함수에서 인증 권한을 읽는다. 래핑되지 않은 경우 undefined를 반환한다. */
93
93
  export function getServiceAuthPermissions(fn: Function): string[] | undefined {
94
94
  return (fn as unknown as Record<symbol, unknown>)[AUTH_PERMISSIONS] as string[] | undefined;
95
95
  }
96
96
 
97
97
  /**
98
- * Auth wrapper for service factories and methods.
98
+ * 서비스 팩토리 메서드용 인증 래퍼.
99
99
  *
100
- * - Service-level: `auth((ctx) => ({ ... }))` — all methods require login
101
- * - Service-level with roles: `auth(["admin"], (ctx) => ({ ... }))`
102
- * - Method-level: `auth(() => result)` — this method requires login
103
- * - Method-level with roles: `auth(["admin"], () => result)`
100
+ * - 서비스 수준: `auth((ctx) => ({ ... }))` — 모든 메서드에 로그인 필요
101
+ * - 서비스 수준 (역할 지정): `auth(["admin"], (ctx) => ({ ... }))`
102
+ * - 메서드 수준: `auth(() => result)` — 해당 메서드에 로그인 필요
103
+ * - 메서드 수준 (역할 지정): `auth(["admin"], () => result)`
104
104
  */
105
105
  export function auth<TFunction extends (...args: any[]) => any>(fn: TFunction): TFunction;
106
106
  export function auth<TFunction extends (...args: any[]) => any>(
@@ -111,14 +111,14 @@ export function auth(permissionsOrFn: string[] | Function, maybeFn?: Function):
111
111
  const permissions = Array.isArray(permissionsOrFn) ? permissionsOrFn : [];
112
112
  const fn = Array.isArray(permissionsOrFn) ? maybeFn! : permissionsOrFn;
113
113
 
114
- // Create wrapper that preserves call behavior
114
+ // 호출 동작을 유지하는 래퍼 생성
115
115
  const wrapper = (...args: unknown[]) => fn(...args);
116
116
  (wrapper as unknown as Record<symbol, unknown>)[AUTH_PERMISSIONS] = permissions;
117
117
 
118
118
  return wrapper;
119
119
  }
120
120
 
121
- // ── Service Definition ──
121
+ // ── 서비스 정의 ──
122
122
 
123
123
  export interface ServiceDefinition<TMethods = Record<string, (...args: any[]) => any>> {
124
124
  name: string;
@@ -127,15 +127,15 @@ export interface ServiceDefinition<TMethods = Record<string, (...args: any[]) =>
127
127
  }
128
128
 
129
129
  /**
130
- * Define a service with a name and factory function.
130
+ * 이름과 팩토리 함수로 서비스를 정의한다.
131
131
  *
132
132
  * @example
133
- * // Basic service
133
+ * // 기본 서비스
134
134
  * const HealthService = defineService("Health", (ctx) => ({
135
135
  * check: () => ({ status: "ok" }),
136
136
  * }));
137
137
  *
138
- * // Service with auth
138
+ * // 인증이 필요한 서비스
139
139
  * const UserService = defineService("User", auth((ctx) => ({
140
140
  * getProfile: () => ctx.authInfo,
141
141
  * adminOnly: auth(["admin"], () => "admin"),
@@ -152,14 +152,14 @@ export function defineService<TMethods extends Record<string, (...args: any[]) =
152
152
  };
153
153
  }
154
154
 
155
- // ── Type Utility ──
155
+ // ── 타입 유틸리티 ──
156
156
 
157
157
  /**
158
- * Extract method signatures from a ServiceDefinition for client-side type sharing.
158
+ * 클라이언트 타입 공유를 위해 ServiceDefinition에서 메서드 시그니처를 추출한다.
159
159
  *
160
160
  * @example
161
161
  * export type UserServiceType = ServiceMethods<typeof UserService>;
162
- * // Client: client.getService<UserServiceType>("User");
162
+ * // 클라이언트: client.getService<UserServiceType>("User");
163
163
  */
164
164
  export type ServiceMethods<TDefinition> =
165
165
  TDefinition extends ServiceDefinition<infer M> ? M : never;
@@ -13,55 +13,61 @@ export async function executeServiceMethod(
13
13
  http?: { clientName: string; authTokenPayload?: AuthTokenPayload };
14
14
  },
15
15
  ): Promise<unknown> {
16
- // Find service definition
16
+ // 서비스 정의 검색
17
17
  const serviceDef = server.options.services.find((item) => item.name === def.serviceName);
18
18
 
19
19
  if (serviceDef == null) {
20
- throw new Error(`Service [${def.serviceName}] not found.`);
20
+ throw new Error(`서비스 [${def.serviceName}] 찾을 수 없습니다.`);
21
21
  }
22
22
 
23
- // Request validation (gatekeeper)
23
+ // 요청 유효성 검증 (게이트키퍼)
24
24
  const clientName = def.socket?.clientName ?? def.http?.clientName;
25
25
  if (clientName != null) {
26
26
  if (clientName.includes("..") || clientName.includes("/") || clientName.includes("\\")) {
27
- throw new Error(`[Security] Invalid client name: ${clientName}`);
27
+ throw new Error(`[보안] 유효하지 않은 클라이언트 이름: ${clientName}`);
28
28
  }
29
29
  }
30
30
 
31
- // Create context
31
+ // 컨텍스트 생성
32
32
  const ctx = createServiceContext(server, def.socket, def.http);
33
33
 
34
- // Invoke factory to create method object
34
+ // 팩토리를 호출하여 메서드 객체 생성
35
35
  const methods = serviceDef.factory(ctx);
36
36
 
37
- // Find method
37
+ // 메서드 검색
38
38
  const method = (methods as Record<string, unknown>)[def.methodName];
39
39
  if (typeof method !== "function") {
40
- throw new Error(`Method [${def.serviceName}.${def.methodName}] not found.`);
40
+ throw new Error(`메서드 [${def.serviceName}.${def.methodName}] 찾을 수 없습니다.`);
41
41
  }
42
42
 
43
- // Auth check
44
- if (server.options.auth != null) {
45
- // Check method-level auth first, fallback to service-level
46
- const methodPerms = getServiceAuthPermissions(method);
47
- const requiredPerms = methodPerms ?? serviceDef.authPermissions;
43
+ // 인증 확인
44
+ const methodPerms = getServiceAuthPermissions(method);
45
+ const requiredPerms = methodPerms ?? serviceDef.authPermissions;
48
46
 
49
- if (requiredPerms != null) {
47
+ if (requiredPerms != null) {
48
+ if (server.options.auth === undefined) {
49
+ // auth 설정 누락 — 설정 오류
50
+ throw new Error("auth 설정이 필요합니다. auth 서비스를 사용하려면 서버 옵션에 auth를 설정하세요.");
51
+ }
52
+
53
+ if (server.options.auth !== false) {
54
+ // auth가 설정되어 있으면 인증 검사 수행
50
55
  const authTokenPayload = def.socket?.authTokenPayload ?? def.http?.authTokenPayload;
51
56
 
52
57
  if (authTokenPayload == null) {
53
- throw new Error("Login is required.");
58
+ throw new Error("로그인이 필요합니다.");
54
59
  }
55
60
 
56
61
  if (requiredPerms.length > 0) {
57
62
  const hasPerm = requiredPerms.some((perm) => authTokenPayload.roles.includes(perm));
58
63
  if (!hasPerm) {
59
- throw new Error("Insufficient permissions.");
64
+ throw new Error("권한이 부족합니다.");
60
65
  }
61
66
  }
62
67
  }
68
+ // auth === false → 의도적 비활성화, 인증 스킵
63
69
  }
64
70
 
65
- // Execute
71
+ // 실행
66
72
  return await method(...def.params);
67
73
  }
package/src/index.ts CHANGED
@@ -1,36 +1,34 @@
1
- // Types
1
+ // 타입
2
2
  export * from "./types/server-options";
3
3
 
4
- // Auth
4
+ // 인증
5
5
  export * from "./auth/auth-token-payload";
6
6
  export * from "./auth/jwt-manager";
7
7
 
8
- // Core
8
+ // 코어
9
9
  export * from "./core/define-service";
10
10
  export * from "./core/service-executor";
11
11
 
12
- // Transport - Socket
12
+ // 전송 계층 - Socket
13
13
  export * from "./transport/socket/websocket-handler";
14
14
  export * from "./transport/socket/service-socket";
15
15
 
16
- // Transport - HTTP
16
+ // 전송 계층 - HTTP
17
17
  export * from "./transport/http/http-request-handler";
18
18
  export * from "./transport/http/upload-handler";
19
19
  export * from "./transport/http/static-file-handler";
20
20
 
21
- // Protocol
21
+ // 프로토콜
22
22
  export * from "./protocol/protocol-wrapper";
23
23
 
24
- // Services
24
+ // 서비스
25
25
  export * from "./services/orm-service";
26
26
  export * from "./services/auto-update-service";
27
- export * from "./services/smtp-client-service";
28
-
29
- // Utils
27
+ // 유틸리티
30
28
  export * from "./utils/config-manager";
31
29
 
32
- // Legacy
30
+ // 레거시
33
31
  export * from "./legacy/v1-auto-update-handler";
34
32
 
35
- // Main
33
+ // 메인
36
34
  export * from "./service-server";
@@ -18,27 +18,27 @@ interface IV1Response {
18
18
  }
19
19
 
20
20
  /**
21
- * V1 legacy client handler (only auto-update supported).
22
- * All other requests return an upgrade-required error.
21
+ * V1 레거시 클라이언트 핸들러 (자동 업데이트만 지원).
22
+ * 모든 요청은 업그레이드 필요 에러를 반환한다.
23
23
  */
24
24
  export function handleV1Connection(
25
25
  socket: WebSocket,
26
26
  autoUpdateMethods: { getLastVersion: (platform: string) => Promise<any> },
27
27
  clientNameSetter?: (clientName: string | undefined) => void,
28
28
  ) {
29
- // Notify connection established
29
+ // 연결 성립 알림
30
30
  socket.send(JSON.stringify({ name: "connected" }));
31
31
 
32
- socket.on("message", (data) => {
32
+ socket.on("message", async (data) => {
33
33
  try {
34
34
  const msg = JSON.parse(data.toString()) as IV1Request;
35
35
 
36
- // Only allow SdAutoUpdateService.getLastVersion
36
+ // SdAutoUpdateService.getLastVersion만 허용
37
37
  if (msg.command === "SdAutoUpdateService.getLastVersion") {
38
- // Set legacy context
38
+ // 레거시 컨텍스트 설정
39
39
  clientNameSetter?.(msg.clientName);
40
40
 
41
- const result = autoUpdateMethods.getLastVersion(msg.params[0] as string);
41
+ const result = await autoUpdateMethods.getLastVersion(msg.params[0] as string);
42
42
 
43
43
  const response: IV1Response = {
44
44
  name: "response",
@@ -48,20 +48,20 @@ export function handleV1Connection(
48
48
  };
49
49
  socket.send(JSON.stringify(response));
50
50
  } else {
51
- // All other requests prompt for upgrade
51
+ // 모든 요청은 업그레이드 요구
52
52
  const response: IV1Response = {
53
53
  name: "response",
54
54
  reqUuid: msg.uuid,
55
55
  state: "error",
56
56
  body: {
57
- message: "App upgrade is required.",
57
+ message: " 업그레이드가 필요합니다.",
58
58
  code: "UPGRADE_REQUIRED",
59
59
  },
60
60
  };
61
61
  socket.send(JSON.stringify(response));
62
62
  }
63
63
  } catch (err) {
64
- logger.warn("V1 message processing error", err);
64
+ logger.warn("V1 메시지 처리 에러", err);
65
65
  }
66
66
  });
67
67
  }