@simplysm/service-server 13.0.0-beta.6

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 (280) hide show
  1. package/.cache/typecheck-node.tsbuildinfo +1 -0
  2. package/README.md +587 -0
  3. package/dist/auth/auth-token-payload.js +1 -0
  4. package/dist/auth/auth-token-payload.js.map +7 -0
  5. package/dist/auth/auth.decorators.js +46 -0
  6. package/dist/auth/auth.decorators.js.map +7 -0
  7. package/dist/auth/jwt-manager.js +35 -0
  8. package/dist/auth/jwt-manager.js.map +7 -0
  9. package/dist/core/service-base.js +47 -0
  10. package/dist/core/service-base.js.map +7 -0
  11. package/dist/core/service-executor.js +46 -0
  12. package/dist/core/service-executor.js.map +7 -0
  13. package/dist/core-common/src/common.types.d.ts +74 -0
  14. package/dist/core-common/src/common.types.d.ts.map +1 -0
  15. package/dist/core-common/src/env.d.ts +6 -0
  16. package/dist/core-common/src/env.d.ts.map +1 -0
  17. package/dist/core-common/src/errors/argument-error.d.ts +25 -0
  18. package/dist/core-common/src/errors/argument-error.d.ts.map +1 -0
  19. package/dist/core-common/src/errors/not-implemented-error.d.ts +29 -0
  20. package/dist/core-common/src/errors/not-implemented-error.d.ts.map +1 -0
  21. package/dist/core-common/src/errors/sd-error.d.ts +27 -0
  22. package/dist/core-common/src/errors/sd-error.d.ts.map +1 -0
  23. package/dist/core-common/src/errors/timeout-error.d.ts +31 -0
  24. package/dist/core-common/src/errors/timeout-error.d.ts.map +1 -0
  25. package/dist/core-common/src/extensions/arr-ext.d.ts +15 -0
  26. package/dist/core-common/src/extensions/arr-ext.d.ts.map +1 -0
  27. package/dist/core-common/src/extensions/arr-ext.helpers.d.ts +19 -0
  28. package/dist/core-common/src/extensions/arr-ext.helpers.d.ts.map +1 -0
  29. package/dist/core-common/src/extensions/arr-ext.types.d.ts +215 -0
  30. package/dist/core-common/src/extensions/arr-ext.types.d.ts.map +1 -0
  31. package/dist/core-common/src/extensions/map-ext.d.ts +57 -0
  32. package/dist/core-common/src/extensions/map-ext.d.ts.map +1 -0
  33. package/dist/core-common/src/extensions/set-ext.d.ts +36 -0
  34. package/dist/core-common/src/extensions/set-ext.d.ts.map +1 -0
  35. package/dist/core-common/src/features/debounce-queue.d.ts +53 -0
  36. package/dist/core-common/src/features/debounce-queue.d.ts.map +1 -0
  37. package/dist/core-common/src/features/event-emitter.d.ts +66 -0
  38. package/dist/core-common/src/features/event-emitter.d.ts.map +1 -0
  39. package/dist/core-common/src/features/serial-queue.d.ts +47 -0
  40. package/dist/core-common/src/features/serial-queue.d.ts.map +1 -0
  41. package/dist/core-common/src/index.d.ts +32 -0
  42. package/dist/core-common/src/index.d.ts.map +1 -0
  43. package/dist/core-common/src/types/date-only.d.ts +152 -0
  44. package/dist/core-common/src/types/date-only.d.ts.map +1 -0
  45. package/dist/core-common/src/types/date-time.d.ts +96 -0
  46. package/dist/core-common/src/types/date-time.d.ts.map +1 -0
  47. package/dist/core-common/src/types/lazy-gc-map.d.ts +80 -0
  48. package/dist/core-common/src/types/lazy-gc-map.d.ts.map +1 -0
  49. package/dist/core-common/src/types/time.d.ts +68 -0
  50. package/dist/core-common/src/types/time.d.ts.map +1 -0
  51. package/dist/core-common/src/types/uuid.d.ts +35 -0
  52. package/dist/core-common/src/types/uuid.d.ts.map +1 -0
  53. package/dist/core-common/src/utils/bytes.d.ts +51 -0
  54. package/dist/core-common/src/utils/bytes.d.ts.map +1 -0
  55. package/dist/core-common/src/utils/date-format.d.ts +90 -0
  56. package/dist/core-common/src/utils/date-format.d.ts.map +1 -0
  57. package/dist/core-common/src/utils/json.d.ts +34 -0
  58. package/dist/core-common/src/utils/json.d.ts.map +1 -0
  59. package/dist/core-common/src/utils/num.d.ts +60 -0
  60. package/dist/core-common/src/utils/num.d.ts.map +1 -0
  61. package/dist/core-common/src/utils/obj.d.ts +258 -0
  62. package/dist/core-common/src/utils/obj.d.ts.map +1 -0
  63. package/dist/core-common/src/utils/path.d.ts +23 -0
  64. package/dist/core-common/src/utils/path.d.ts.map +1 -0
  65. package/dist/core-common/src/utils/primitive.d.ts +18 -0
  66. package/dist/core-common/src/utils/primitive.d.ts.map +1 -0
  67. package/dist/core-common/src/utils/str.d.ts +103 -0
  68. package/dist/core-common/src/utils/str.d.ts.map +1 -0
  69. package/dist/core-common/src/utils/template-strings.d.ts +84 -0
  70. package/dist/core-common/src/utils/template-strings.d.ts.map +1 -0
  71. package/dist/core-common/src/utils/transferable.d.ts +47 -0
  72. package/dist/core-common/src/utils/transferable.d.ts.map +1 -0
  73. package/dist/core-common/src/utils/wait.d.ts +19 -0
  74. package/dist/core-common/src/utils/wait.d.ts.map +1 -0
  75. package/dist/core-common/src/utils/xml.d.ts +36 -0
  76. package/dist/core-common/src/utils/xml.d.ts.map +1 -0
  77. package/dist/core-common/src/zip/sd-zip.d.ts +80 -0
  78. package/dist/core-common/src/zip/sd-zip.d.ts.map +1 -0
  79. package/dist/core-node/src/features/fs-watcher.d.ts +70 -0
  80. package/dist/core-node/src/features/fs-watcher.d.ts.map +1 -0
  81. package/dist/core-node/src/index.d.ts +7 -0
  82. package/dist/core-node/src/index.d.ts.map +1 -0
  83. package/dist/core-node/src/utils/fs.d.ts +197 -0
  84. package/dist/core-node/src/utils/fs.d.ts.map +1 -0
  85. package/dist/core-node/src/utils/path.d.ts +75 -0
  86. package/dist/core-node/src/utils/path.d.ts.map +1 -0
  87. package/dist/core-node/src/worker/create-worker.d.ts +23 -0
  88. package/dist/core-node/src/worker/create-worker.d.ts.map +1 -0
  89. package/dist/core-node/src/worker/types.d.ts +67 -0
  90. package/dist/core-node/src/worker/types.d.ts.map +1 -0
  91. package/dist/core-node/src/worker/worker.d.ts +27 -0
  92. package/dist/core-node/src/worker/worker.d.ts.map +1 -0
  93. package/dist/index.js +20 -0
  94. package/dist/index.js.map +7 -0
  95. package/dist/legacy/v1-auto-update-handler.js +38 -0
  96. package/dist/legacy/v1-auto-update-handler.js.map +7 -0
  97. package/dist/orm-common/src/db-context.d.ts +669 -0
  98. package/dist/orm-common/src/db-context.d.ts.map +1 -0
  99. package/dist/orm-common/src/errors/db-transaction-error.d.ts +51 -0
  100. package/dist/orm-common/src/errors/db-transaction-error.d.ts.map +1 -0
  101. package/dist/orm-common/src/exec/executable.d.ts +79 -0
  102. package/dist/orm-common/src/exec/executable.d.ts.map +1 -0
  103. package/dist/orm-common/src/exec/queryable.d.ts +708 -0
  104. package/dist/orm-common/src/exec/queryable.d.ts.map +1 -0
  105. package/dist/orm-common/src/exec/search-parser.d.ts +72 -0
  106. package/dist/orm-common/src/exec/search-parser.d.ts.map +1 -0
  107. package/dist/orm-common/src/expr/expr-unit.d.ts +25 -0
  108. package/dist/orm-common/src/expr/expr-unit.d.ts.map +1 -0
  109. package/dist/orm-common/src/expr/expr.d.ts +1369 -0
  110. package/dist/orm-common/src/expr/expr.d.ts.map +1 -0
  111. package/dist/orm-common/src/index.d.ts +32 -0
  112. package/dist/orm-common/src/index.d.ts.map +1 -0
  113. package/dist/orm-common/src/models/system-migration.d.ts +10 -0
  114. package/dist/orm-common/src/models/system-migration.d.ts.map +1 -0
  115. package/dist/orm-common/src/query-builder/base/expr-renderer-base.d.ts +95 -0
  116. package/dist/orm-common/src/query-builder/base/expr-renderer-base.d.ts.map +1 -0
  117. package/dist/orm-common/src/query-builder/base/query-builder-base.d.ts +66 -0
  118. package/dist/orm-common/src/query-builder/base/query-builder-base.d.ts.map +1 -0
  119. package/dist/orm-common/src/query-builder/mssql/mssql-expr-renderer.d.ts +84 -0
  120. package/dist/orm-common/src/query-builder/mssql/mssql-expr-renderer.d.ts.map +1 -0
  121. package/dist/orm-common/src/query-builder/mssql/mssql-query-builder.d.ts +45 -0
  122. package/dist/orm-common/src/query-builder/mssql/mssql-query-builder.d.ts.map +1 -0
  123. package/dist/orm-common/src/query-builder/mysql/mysql-expr-renderer.d.ts +84 -0
  124. package/dist/orm-common/src/query-builder/mysql/mysql-expr-renderer.d.ts.map +1 -0
  125. package/dist/orm-common/src/query-builder/mysql/mysql-query-builder.d.ts +54 -0
  126. package/dist/orm-common/src/query-builder/mysql/mysql-query-builder.d.ts.map +1 -0
  127. package/dist/orm-common/src/query-builder/postgresql/postgresql-expr-renderer.d.ts +84 -0
  128. package/dist/orm-common/src/query-builder/postgresql/postgresql-expr-renderer.d.ts.map +1 -0
  129. package/dist/orm-common/src/query-builder/postgresql/postgresql-query-builder.d.ts +52 -0
  130. package/dist/orm-common/src/query-builder/postgresql/postgresql-query-builder.d.ts.map +1 -0
  131. package/dist/orm-common/src/query-builder/query-builder.d.ts +7 -0
  132. package/dist/orm-common/src/query-builder/query-builder.d.ts.map +1 -0
  133. package/dist/orm-common/src/schema/factory/column-builder.d.ts +394 -0
  134. package/dist/orm-common/src/schema/factory/column-builder.d.ts.map +1 -0
  135. package/dist/orm-common/src/schema/factory/index-builder.d.ts +151 -0
  136. package/dist/orm-common/src/schema/factory/index-builder.d.ts.map +1 -0
  137. package/dist/orm-common/src/schema/factory/relation-builder.d.ts +337 -0
  138. package/dist/orm-common/src/schema/factory/relation-builder.d.ts.map +1 -0
  139. package/dist/orm-common/src/schema/procedure-builder.d.ts +202 -0
  140. package/dist/orm-common/src/schema/procedure-builder.d.ts.map +1 -0
  141. package/dist/orm-common/src/schema/table-builder.d.ts +259 -0
  142. package/dist/orm-common/src/schema/table-builder.d.ts.map +1 -0
  143. package/dist/orm-common/src/schema/view-builder.d.ts +183 -0
  144. package/dist/orm-common/src/schema/view-builder.d.ts.map +1 -0
  145. package/dist/orm-common/src/types/column.d.ts +172 -0
  146. package/dist/orm-common/src/types/column.d.ts.map +1 -0
  147. package/dist/orm-common/src/types/db.d.ts +175 -0
  148. package/dist/orm-common/src/types/db.d.ts.map +1 -0
  149. package/dist/orm-common/src/types/expr.d.ts +474 -0
  150. package/dist/orm-common/src/types/expr.d.ts.map +1 -0
  151. package/dist/orm-common/src/types/query-def.d.ts +351 -0
  152. package/dist/orm-common/src/types/query-def.d.ts.map +1 -0
  153. package/dist/orm-common/src/utils/result-parser.d.ts +38 -0
  154. package/dist/orm-common/src/utils/result-parser.d.ts.map +1 -0
  155. package/dist/orm-node/src/connections/mssql-db-conn.d.ts +44 -0
  156. package/dist/orm-node/src/connections/mssql-db-conn.d.ts.map +1 -0
  157. package/dist/orm-node/src/connections/mysql-db-conn.d.ts +38 -0
  158. package/dist/orm-node/src/connections/mysql-db-conn.d.ts.map +1 -0
  159. package/dist/orm-node/src/connections/postgresql-db-conn.d.ts +39 -0
  160. package/dist/orm-node/src/connections/postgresql-db-conn.d.ts.map +1 -0
  161. package/dist/orm-node/src/db-conn-factory.d.ts +25 -0
  162. package/dist/orm-node/src/db-conn-factory.d.ts.map +1 -0
  163. package/dist/orm-node/src/index.d.ts +9 -0
  164. package/dist/orm-node/src/index.d.ts.map +1 -0
  165. package/dist/orm-node/src/node-db-context-executor.d.ts +77 -0
  166. package/dist/orm-node/src/node-db-context-executor.d.ts.map +1 -0
  167. package/dist/orm-node/src/pooled-db-conn.d.ts +79 -0
  168. package/dist/orm-node/src/pooled-db-conn.d.ts.map +1 -0
  169. package/dist/orm-node/src/sd-orm.d.ts +78 -0
  170. package/dist/orm-node/src/sd-orm.d.ts.map +1 -0
  171. package/dist/orm-node/src/types/db-conn.d.ts +159 -0
  172. package/dist/orm-node/src/types/db-conn.d.ts.map +1 -0
  173. package/dist/protocol/protocol-wrapper.js +64 -0
  174. package/dist/protocol/protocol-wrapper.js.map +7 -0
  175. package/dist/service-common/src/index.d.ts +8 -0
  176. package/dist/service-common/src/index.d.ts.map +1 -0
  177. package/dist/service-common/src/protocol/protocol.types.d.ts +100 -0
  178. package/dist/service-common/src/protocol/protocol.types.d.ts.map +1 -0
  179. package/dist/service-common/src/protocol/service-protocol.d.ts +63 -0
  180. package/dist/service-common/src/protocol/service-protocol.d.ts.map +1 -0
  181. package/dist/service-common/src/service-types/auto-update-service.types.d.ts +17 -0
  182. package/dist/service-common/src/service-types/auto-update-service.types.d.ts.map +1 -0
  183. package/dist/service-common/src/service-types/crypto-service.types.d.ts +22 -0
  184. package/dist/service-common/src/service-types/crypto-service.types.d.ts.map +1 -0
  185. package/dist/service-common/src/service-types/orm-service.types.d.ts +30 -0
  186. package/dist/service-common/src/service-types/orm-service.types.d.ts.map +1 -0
  187. package/dist/service-common/src/service-types/smtp-service.types.d.ts +55 -0
  188. package/dist/service-common/src/service-types/smtp-service.types.d.ts.map +1 -0
  189. package/dist/service-common/src/types.d.ts +43 -0
  190. package/dist/service-common/src/types.d.ts.map +1 -0
  191. package/dist/service-server/src/auth/auth-token-payload.d.ts +6 -0
  192. package/dist/service-server/src/auth/auth-token-payload.d.ts.map +1 -0
  193. package/dist/service-server/src/auth/auth.decorators.d.ts +19 -0
  194. package/dist/service-server/src/auth/auth.decorators.d.ts.map +1 -0
  195. package/dist/service-server/src/auth/jwt-manager.d.ts +10 -0
  196. package/dist/service-server/src/auth/jwt-manager.d.ts.map +1 -0
  197. package/dist/service-server/src/core/service-base.d.ts +19 -0
  198. package/dist/service-server/src/core/service-base.d.ts.map +1 -0
  199. package/dist/service-server/src/core/service-executor.d.ts +18 -0
  200. package/dist/service-server/src/core/service-executor.d.ts.map +1 -0
  201. package/dist/service-server/src/index.d.ts +20 -0
  202. package/dist/service-server/src/index.d.ts.map +1 -0
  203. package/dist/service-server/src/legacy/v1-auto-update-handler.d.ts +8 -0
  204. package/dist/service-server/src/legacy/v1-auto-update-handler.d.ts.map +1 -0
  205. package/dist/service-server/src/protocol/protocol-wrapper.d.ts +25 -0
  206. package/dist/service-server/src/protocol/protocol-wrapper.d.ts.map +1 -0
  207. package/dist/service-server/src/service-server.d.ts +29 -0
  208. package/dist/service-server/src/service-server.d.ts.map +1 -0
  209. package/dist/service-server/src/services/auto-update-service.d.ts +9 -0
  210. package/dist/service-server/src/services/auto-update-service.d.ts.map +1 -0
  211. package/dist/service-server/src/services/crypto-service.d.ts +10 -0
  212. package/dist/service-server/src/services/crypto-service.d.ts.map +1 -0
  213. package/dist/service-server/src/services/orm-service.d.ts +28 -0
  214. package/dist/service-server/src/services/orm-service.d.ts.map +1 -0
  215. package/dist/service-server/src/services/smtp-service.d.ts +7 -0
  216. package/dist/service-server/src/services/smtp-service.d.ts.map +1 -0
  217. package/dist/service-server/src/transport/http/http-request-handler.d.ts +12 -0
  218. package/dist/service-server/src/transport/http/http-request-handler.d.ts.map +1 -0
  219. package/dist/service-server/src/transport/http/static-file-handler.d.ts +9 -0
  220. package/dist/service-server/src/transport/http/static-file-handler.d.ts.map +1 -0
  221. package/dist/service-server/src/transport/http/upload-handler.d.ts +10 -0
  222. package/dist/service-server/src/transport/http/upload-handler.d.ts.map +1 -0
  223. package/dist/service-server/src/transport/socket/service-socket.d.ts +41 -0
  224. package/dist/service-server/src/transport/socket/service-socket.d.ts.map +1 -0
  225. package/dist/service-server/src/transport/socket/websocket-handler.d.ts +18 -0
  226. package/dist/service-server/src/transport/socket/websocket-handler.d.ts.map +1 -0
  227. package/dist/service-server/src/types/server-options.d.ts +15 -0
  228. package/dist/service-server/src/types/server-options.d.ts.map +1 -0
  229. package/dist/service-server/src/utils/config-manager.d.ts +7 -0
  230. package/dist/service-server/src/utils/config-manager.d.ts.map +1 -0
  231. package/dist/service-server/src/workers/service-protocol.worker.d.ts +15 -0
  232. package/dist/service-server/src/workers/service-protocol.worker.d.ts.map +1 -0
  233. package/dist/service-server.js +165 -0
  234. package/dist/service-server.js.map +7 -0
  235. package/dist/services/auto-update-service.js +39 -0
  236. package/dist/services/auto-update-service.js.map +7 -0
  237. package/dist/services/crypto-service.js +32 -0
  238. package/dist/services/crypto-service.js.map +7 -0
  239. package/dist/services/orm-service.js +186 -0
  240. package/dist/services/orm-service.js.map +7 -0
  241. package/dist/services/smtp-service.js +47 -0
  242. package/dist/services/smtp-service.js.map +7 -0
  243. package/dist/transport/http/http-request-handler.js +57 -0
  244. package/dist/transport/http/http-request-handler.js.map +7 -0
  245. package/dist/transport/http/static-file-handler.js +57 -0
  246. package/dist/transport/http/static-file-handler.js.map +7 -0
  247. package/dist/transport/http/upload-handler.js +71 -0
  248. package/dist/transport/http/upload-handler.js.map +7 -0
  249. package/dist/transport/socket/service-socket.js +105 -0
  250. package/dist/transport/socket/service-socket.js.map +7 -0
  251. package/dist/transport/socket/websocket-handler.js +144 -0
  252. package/dist/transport/socket/websocket-handler.js.map +7 -0
  253. package/dist/types/server-options.js +1 -0
  254. package/dist/types/server-options.js.map +7 -0
  255. package/dist/utils/config-manager.js +62 -0
  256. package/dist/utils/config-manager.js.map +7 -0
  257. package/dist/workers/service-protocol.worker.js +15 -0
  258. package/dist/workers/service-protocol.worker.js.map +7 -0
  259. package/package.json +44 -0
  260. package/src/auth/auth-token-payload.ts +6 -0
  261. package/src/auth/auth.decorators.ts +71 -0
  262. package/src/auth/jwt-manager.ts +44 -0
  263. package/src/core/service-base.ts +66 -0
  264. package/src/core/service-executor.ts +70 -0
  265. package/src/index.ts +38 -0
  266. package/src/legacy/v1-auto-update-handler.ts +64 -0
  267. package/src/protocol/protocol-wrapper.ts +75 -0
  268. package/src/service-server.ts +235 -0
  269. package/src/services/auto-update-service.ts +52 -0
  270. package/src/services/crypto-service.ts +38 -0
  271. package/src/services/orm-service.ts +170 -0
  272. package/src/services/smtp-service.ts +58 -0
  273. package/src/transport/http/http-request-handler.ts +69 -0
  274. package/src/transport/http/static-file-handler.ts +66 -0
  275. package/src/transport/http/upload-handler.ts +87 -0
  276. package/src/transport/socket/service-socket.ts +136 -0
  277. package/src/transport/socket/websocket-handler.ts +181 -0
  278. package/src/types/server-options.ts +15 -0
  279. package/src/utils/config-manager.ts +70 -0
  280. package/src/workers/service-protocol.worker.ts +15 -0
@@ -0,0 +1,57 @@
1
+ import path from "path";
2
+ import { fsExists, fsStat } from "@simplysm/core-node";
3
+ import { createConsola } from "consola";
4
+ const logger = createConsola().withTag("service-server:StaticFileHandler");
5
+ class StaticFileHandler {
6
+ constructor(_server) {
7
+ this._server = _server;
8
+ }
9
+ async handle(req, reply, urlPath) {
10
+ let targetFilePath = path.resolve(this._server.options.rootPath, "www", urlPath);
11
+ const allowedRootPath = path.resolve(this._server.options.rootPath, "www");
12
+ if (!targetFilePath.startsWith(allowedRootPath)) {
13
+ throw new Error("Access denied");
14
+ }
15
+ if (await fsExists(targetFilePath) && (await fsStat(targetFilePath)).isDirectory()) {
16
+ targetFilePath = path.resolve(targetFilePath, "index.html");
17
+ }
18
+ if (path.basename(targetFilePath).startsWith(".")) {
19
+ const errorMessage = "\uD30C\uC77C\uC744 \uC0AC\uC6A9\uD560 \uAD8C\uD55C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.";
20
+ this._responseErrorHtml(reply, 403, errorMessage);
21
+ logger.warn(`[403] ${errorMessage} (${targetFilePath})`);
22
+ return;
23
+ }
24
+ const filename = path.basename(targetFilePath);
25
+ const directory = path.dirname(targetFilePath);
26
+ try {
27
+ return await reply.sendFile(filename, directory);
28
+ } catch (err) {
29
+ const error = err;
30
+ if (error.code === "ENOENT") {
31
+ const errorMessage = "\uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.";
32
+ this._responseErrorHtml(reply, 404, errorMessage);
33
+ logger.warn(`[404] ${errorMessage} (${targetFilePath})`);
34
+ } else {
35
+ const errorMessage = "\uD30C\uC77C \uC804\uC1A1 \uC911 \uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4.";
36
+ this._responseErrorHtml(reply, 500, errorMessage);
37
+ logger.error(`[500] ${errorMessage}`, err);
38
+ }
39
+ }
40
+ }
41
+ _responseErrorHtml(reply, code, message) {
42
+ reply.status(code).type("text/html").send(`
43
+ <!DOCTYPE html>
44
+ <html>
45
+ <head>
46
+ <meta name="viewport" content="width=device-width, initial-scale=1">
47
+ <meta charset="UTF-8">
48
+ <title>${code}: ${message}</title>
49
+ </head>
50
+ <body>${code}: ${message}</body>
51
+ </html>`);
52
+ }
53
+ }
54
+ export {
55
+ StaticFileHandler
56
+ };
57
+ //# sourceMappingURL=static-file-handler.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/transport/http/static-file-handler.ts"],
4
+ "sourcesContent": ["import path from \"path\";\nimport { fsExists, fsStat } from \"@simplysm/core-node\";\nimport type { ServiceServer } from \"../../service-server\";\nimport type { FastifyReply, FastifyRequest } from \"fastify\";\nimport { createConsola } from \"consola\";\n\nconst logger = createConsola().withTag(\"service-server:StaticFileHandler\");\n\nexport class StaticFileHandler {\n constructor(private readonly _server: ServiceServer) {}\n\n async handle(req: FastifyRequest, reply: FastifyReply, urlPath: string): Promise<void> {\n let targetFilePath = path.resolve(this._server.options.rootPath, \"www\", urlPath);\n const allowedRootPath = path.resolve(this._server.options.rootPath, \"www\");\n\n // targetPath \uBCF4\uC548 \uBC29\uC5B4 (Path Traversal \uBC29\uC9C0)\n if (!targetFilePath.startsWith(allowedRootPath)) {\n throw new Error(\"Access denied\");\n }\n\n // \uB514\uB809\uD1A0\uB9AC\uBA74 index.html\uB85C\n if ((await fsExists(targetFilePath)) && (await fsStat(targetFilePath)).isDirectory()) {\n targetFilePath = path.resolve(targetFilePath, \"index.html\");\n }\n\n // \uAD8C\uD55C \uD655\uC778 (\uC228\uAE40 \uD30C\uC77C \uB4F1)\n if (path.basename(targetFilePath).startsWith(\".\")) {\n const errorMessage = \"\uD30C\uC77C\uC744 \uC0AC\uC6A9\uD560 \uAD8C\uD55C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.\";\n this._responseErrorHtml(reply, 403, errorMessage);\n logger.warn(`[403] ${errorMessage} (${targetFilePath})`);\n return;\n }\n\n // \uD30C\uC77C \uC804\uC1A1\n const filename = path.basename(targetFilePath);\n const directory = path.dirname(targetFilePath);\n\n try {\n return await reply.sendFile(filename, directory);\n } catch (err: unknown) {\n const error = err as { code?: string };\n if (error.code === \"ENOENT\") {\n const errorMessage = \"\uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.\";\n this._responseErrorHtml(reply, 404, errorMessage);\n logger.warn(`[404] ${errorMessage} (${targetFilePath})`);\n } else {\n const errorMessage = \"\uD30C\uC77C \uC804\uC1A1 \uC911 \uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4.\";\n this._responseErrorHtml(reply, 500, errorMessage);\n logger.error(`[500] ${errorMessage}`, err);\n }\n }\n }\n\n private _responseErrorHtml(reply: FastifyReply, code: number, message: string) {\n reply.status(code).type(\"text/html\").send(`\n<!DOCTYPE html>\n<html>\n<head>\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <meta charset=\"UTF-8\">\n <title>${code}: ${message}</title>\n</head>\n<body>${code}: ${message}</body>\n</html>`);\n }\n}\n"],
5
+ "mappings": "AAAA,OAAO,UAAU;AACjB,SAAS,UAAU,cAAc;AAGjC,SAAS,qBAAqB;AAE9B,MAAM,SAAS,cAAc,EAAE,QAAQ,kCAAkC;AAElE,MAAM,kBAAkB;AAAA,EAC7B,YAA6B,SAAwB;AAAxB;AAAA,EAAyB;AAAA,EAEtD,MAAM,OAAO,KAAqB,OAAqB,SAAgC;AACrF,QAAI,iBAAiB,KAAK,QAAQ,KAAK,QAAQ,QAAQ,UAAU,OAAO,OAAO;AAC/E,UAAM,kBAAkB,KAAK,QAAQ,KAAK,QAAQ,QAAQ,UAAU,KAAK;AAGzE,QAAI,CAAC,eAAe,WAAW,eAAe,GAAG;AAC/C,YAAM,IAAI,MAAM,eAAe;AAAA,IACjC;AAGA,QAAK,MAAM,SAAS,cAAc,MAAO,MAAM,OAAO,cAAc,GAAG,YAAY,GAAG;AACpF,uBAAiB,KAAK,QAAQ,gBAAgB,YAAY;AAAA,IAC5D;AAGA,QAAI,KAAK,SAAS,cAAc,EAAE,WAAW,GAAG,GAAG;AACjD,YAAM,eAAe;AACrB,WAAK,mBAAmB,OAAO,KAAK,YAAY;AAChD,aAAO,KAAK,SAAS,YAAY,KAAK,cAAc,GAAG;AACvD;AAAA,IACF;AAGA,UAAM,WAAW,KAAK,SAAS,cAAc;AAC7C,UAAM,YAAY,KAAK,QAAQ,cAAc;AAE7C,QAAI;AACF,aAAO,MAAM,MAAM,SAAS,UAAU,SAAS;AAAA,IACjD,SAAS,KAAc;AACrB,YAAM,QAAQ;AACd,UAAI,MAAM,SAAS,UAAU;AAC3B,cAAM,eAAe;AACrB,aAAK,mBAAmB,OAAO,KAAK,YAAY;AAChD,eAAO,KAAK,SAAS,YAAY,KAAK,cAAc,GAAG;AAAA,MACzD,OAAO;AACL,cAAM,eAAe;AACrB,aAAK,mBAAmB,OAAO,KAAK,YAAY;AAChD,eAAO,MAAM,SAAS,YAAY,IAAI,GAAG;AAAA,MAC3C;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,mBAAmB,OAAqB,MAAc,SAAiB;AAC7E,UAAM,OAAO,IAAI,EAAE,KAAK,WAAW,EAAE,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,aAMjC,IAAI,KAAK,OAAO;AAAA;AAAA,QAErB,IAAI,KAAK,OAAO;AAAA,QAChB;AAAA,EACN;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,71 @@
1
+ import path from "path";
2
+ import { createWriteStream } from "fs";
3
+ import { pipeline } from "stream/promises";
4
+ import { Uuid } from "@simplysm/core-common";
5
+ import { fsMkdir, fsStat, fsRm } from "@simplysm/core-node";
6
+ import { createConsola } from "consola";
7
+ const logger = createConsola().withTag("service-server:UploadHandler");
8
+ class UploadHandler {
9
+ constructor(_server, _jwt) {
10
+ this._server = _server;
11
+ this._jwt = _jwt;
12
+ }
13
+ async handle(req, reply) {
14
+ if (!req.isMultipart()) {
15
+ reply.status(400).send("Multipart request expected");
16
+ return;
17
+ }
18
+ try {
19
+ const authHeader = req.headers.authorization;
20
+ if (authHeader == null) {
21
+ throw new Error("\uC778\uC99D \uD1A0\uD070\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
22
+ }
23
+ const token = authHeader.split(" ")[1];
24
+ await this._jwt.verify(token);
25
+ } catch (err) {
26
+ reply.status(401).send({
27
+ error: "Unauthorized",
28
+ message: err instanceof Error ? err.message : String(err)
29
+ });
30
+ return;
31
+ }
32
+ const result = [];
33
+ const uploadDir = path.resolve(this._server.options.rootPath, "www", "uploads");
34
+ await fsMkdir(uploadDir);
35
+ let currentSavePath;
36
+ try {
37
+ for await (const part of req.parts()) {
38
+ if (part.type === "file") {
39
+ const originalFilename = part.filename;
40
+ const extension = path.extname(originalFilename);
41
+ const saveName = `${Uuid.new().toString()}${extension}`;
42
+ currentSavePath = path.join(uploadDir, saveName);
43
+ await pipeline(part.file, createWriteStream(currentSavePath));
44
+ if (part.file.truncated) {
45
+ throw new Error(`File limit exceeded: ${originalFilename}`);
46
+ }
47
+ const stats = await fsStat(currentSavePath);
48
+ result.push({
49
+ path: `uploads/${saveName}`,
50
+ filename: originalFilename,
51
+ size: stats.size
52
+ });
53
+ currentSavePath = void 0;
54
+ }
55
+ }
56
+ reply.send(result);
57
+ } catch (err) {
58
+ logger.error("Upload Error", err);
59
+ if (currentSavePath != null) {
60
+ await fsRm(currentSavePath).catch(() => {
61
+ });
62
+ logger.warn(`Incomplete file deleted: ${currentSavePath}`);
63
+ }
64
+ reply.code(500).send("Upload Failed");
65
+ }
66
+ }
67
+ }
68
+ export {
69
+ UploadHandler
70
+ };
71
+ //# sourceMappingURL=upload-handler.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/transport/http/upload-handler.ts"],
4
+ "sourcesContent": ["import path from \"path\";\nimport { createWriteStream } from \"fs\";\nimport { pipeline } from \"stream/promises\";\nimport { Uuid } from \"@simplysm/core-common\";\nimport { fsMkdir, fsStat, fsRm } from \"@simplysm/core-node\";\nimport type { ServiceServer } from \"../../service-server\";\nimport type { FastifyReply, FastifyRequest } from \"fastify\";\nimport type { ServiceUploadResult } from \"@simplysm/service-common\";\nimport type { JwtManager } from \"../../auth/jwt-manager\";\nimport { createConsola } from \"consola\";\n\nconst logger = createConsola().withTag(\"service-server:UploadHandler\");\n\nexport class UploadHandler {\n constructor(\n private readonly _server: ServiceServer,\n private readonly _jwt: JwtManager,\n ) {}\n\n async handle(req: FastifyRequest, reply: FastifyReply): Promise<void> {\n if (!req.isMultipart()) {\n reply.status(400).send(\"Multipart request expected\");\n return;\n }\n\n // \uC778\uC99D \uAC80\uC0AC\n try {\n const authHeader = req.headers.authorization;\n if (authHeader == null) {\n throw new Error(\"\uC778\uC99D \uD1A0\uD070\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.\");\n }\n const token = authHeader.split(\" \")[1];\n await this._jwt.verify(token);\n } catch (err) {\n reply.status(401).send({\n error: \"Unauthorized\",\n message: err instanceof Error ? err.message : String(err),\n });\n return;\n }\n\n const result: ServiceUploadResult[] = [];\n const uploadDir = path.resolve(this._server.options.rootPath, \"www\", \"uploads\");\n\n await fsMkdir(uploadDir);\n\n let currentSavePath: string | undefined;\n\n try {\n for await (const part of req.parts()) {\n if (part.type === \"file\") {\n const originalFilename = part.filename;\n const extension = path.extname(originalFilename);\n const saveName = `${Uuid.new().toString()}${extension}`;\n currentSavePath = path.join(uploadDir, saveName);\n\n await pipeline(part.file, createWriteStream(currentSavePath));\n\n if (part.file.truncated) {\n throw new Error(`File limit exceeded: ${originalFilename}`);\n }\n\n const stats = await fsStat(currentSavePath);\n\n result.push({\n path: `uploads/${saveName}`,\n filename: originalFilename,\n size: stats.size,\n });\n\n currentSavePath = undefined;\n }\n }\n\n reply.send(result);\n } catch (err) {\n logger.error(\"Upload Error\", err);\n\n if (currentSavePath != null) {\n await fsRm(currentSavePath).catch(() => {});\n logger.warn(`Incomplete file deleted: ${currentSavePath}`);\n }\n\n reply.code(500).send(\"Upload Failed\");\n }\n }\n}\n"],
5
+ "mappings": "AAAA,OAAO,UAAU;AACjB,SAAS,yBAAyB;AAClC,SAAS,gBAAgB;AACzB,SAAS,YAAY;AACrB,SAAS,SAAS,QAAQ,YAAY;AAKtC,SAAS,qBAAqB;AAE9B,MAAM,SAAS,cAAc,EAAE,QAAQ,8BAA8B;AAE9D,MAAM,cAAc;AAAA,EACzB,YACmB,SACA,MACjB;AAFiB;AACA;AAAA,EAChB;AAAA,EAEH,MAAM,OAAO,KAAqB,OAAoC;AACpE,QAAI,CAAC,IAAI,YAAY,GAAG;AACtB,YAAM,OAAO,GAAG,EAAE,KAAK,4BAA4B;AACnD;AAAA,IACF;AAGA,QAAI;AACF,YAAM,aAAa,IAAI,QAAQ;AAC/B,UAAI,cAAc,MAAM;AACtB,cAAM,IAAI,MAAM,2DAAc;AAAA,MAChC;AACA,YAAM,QAAQ,WAAW,MAAM,GAAG,EAAE,CAAC;AACrC,YAAM,KAAK,KAAK,OAAO,KAAK;AAAA,IAC9B,SAAS,KAAK;AACZ,YAAM,OAAO,GAAG,EAAE,KAAK;AAAA,QACrB,OAAO;AAAA,QACP,SAAS,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,MAC1D,CAAC;AACD;AAAA,IACF;AAEA,UAAM,SAAgC,CAAC;AACvC,UAAM,YAAY,KAAK,QAAQ,KAAK,QAAQ,QAAQ,UAAU,OAAO,SAAS;AAE9E,UAAM,QAAQ,SAAS;AAEvB,QAAI;AAEJ,QAAI;AACF,uBAAiB,QAAQ,IAAI,MAAM,GAAG;AACpC,YAAI,KAAK,SAAS,QAAQ;AACxB,gBAAM,mBAAmB,KAAK;AAC9B,gBAAM,YAAY,KAAK,QAAQ,gBAAgB;AAC/C,gBAAM,WAAW,GAAG,KAAK,IAAI,EAAE,SAAS,CAAC,GAAG,SAAS;AACrD,4BAAkB,KAAK,KAAK,WAAW,QAAQ;AAE/C,gBAAM,SAAS,KAAK,MAAM,kBAAkB,eAAe,CAAC;AAE5D,cAAI,KAAK,KAAK,WAAW;AACvB,kBAAM,IAAI,MAAM,wBAAwB,gBAAgB,EAAE;AAAA,UAC5D;AAEA,gBAAM,QAAQ,MAAM,OAAO,eAAe;AAE1C,iBAAO,KAAK;AAAA,YACV,MAAM,WAAW,QAAQ;AAAA,YACzB,UAAU;AAAA,YACV,MAAM,MAAM;AAAA,UACd,CAAC;AAED,4BAAkB;AAAA,QACpB;AAAA,MACF;AAEA,YAAM,KAAK,MAAM;AAAA,IACnB,SAAS,KAAK;AACZ,aAAO,MAAM,gBAAgB,GAAG;AAEhC,UAAI,mBAAmB,MAAM;AAC3B,cAAM,KAAK,eAAe,EAAE,MAAM,MAAM;AAAA,QAAC,CAAC;AAC1C,eAAO,KAAK,4BAA4B,eAAe,EAAE;AAAA,MAC3D;AAEA,YAAM,KAAK,GAAG,EAAE,KAAK,eAAe;AAAA,IACtC;AAAA,EACF;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,105 @@
1
+ import { DateTime, EventEmitter } from "@simplysm/core-common";
2
+ import { clearInterval } from "node:timers";
3
+ import { createConsola } from "consola";
4
+ import { WebSocket } from "ws";
5
+ import { ProtocolWrapper } from "../../protocol/protocol-wrapper";
6
+ const logger = createConsola().withTag("service-server:ServiceSocket");
7
+ class ServiceSocket extends EventEmitter {
8
+ constructor(_socket, _clientId, clientName, connReq) {
9
+ super();
10
+ this._socket = _socket;
11
+ this._clientId = _clientId;
12
+ this.clientName = clientName;
13
+ this.connReq = connReq;
14
+ this._socket.on("close", this._onClose.bind(this));
15
+ this._socket.on("error", this._onError.bind(this));
16
+ this._socket.on("message", this._onMessage.bind(this));
17
+ this._socket.on("pong", () => {
18
+ this._isAlive = true;
19
+ });
20
+ this._pingTimer = setInterval(() => {
21
+ if (!this._isAlive) {
22
+ this.close();
23
+ return;
24
+ }
25
+ this._isAlive = false;
26
+ this._socket.ping();
27
+ }, this._PING_INTERVAL);
28
+ }
29
+ _PING_INTERVAL = 5e3;
30
+ // 5초마다 핑 전송
31
+ _PONG_PACKET = new Uint8Array([2]);
32
+ _protocol = new ProtocolWrapper();
33
+ _listenerInfos = [];
34
+ _isAlive = true;
35
+ _pingTimer;
36
+ connectedAtDateTime = new DateTime();
37
+ authTokenPayload;
38
+ close() {
39
+ this._socket.terminate();
40
+ }
41
+ async send(uuid, msg) {
42
+ return this._send(uuid, msg);
43
+ }
44
+ async _send(uuid, msg) {
45
+ if (this._socket.readyState !== WebSocket.OPEN) return 0;
46
+ const { chunks } = await this._protocol.encode(uuid, msg);
47
+ for (const chunk of chunks) {
48
+ this._socket.send(chunk);
49
+ }
50
+ return chunks.reduce((acc, item) => acc + item.length, 0);
51
+ }
52
+ addEventListener(key, eventName, info) {
53
+ this._listenerInfos.push({ key, eventName, info });
54
+ }
55
+ removeEventListener(key) {
56
+ const idx = this._listenerInfos.findIndex((item) => item.key === key);
57
+ if (idx >= 0) {
58
+ this._listenerInfos.splice(idx, 1);
59
+ }
60
+ }
61
+ getEventListeners(eventName) {
62
+ return this._listenerInfos.filter((item) => item.eventName === eventName).map((item) => ({ key: item.key, info: item.info }));
63
+ }
64
+ filterEventTargetKeys(targetKeys) {
65
+ return this._listenerInfos.filter((item) => targetKeys.includes(item.key)).map((item) => item.key);
66
+ }
67
+ _onError(err) {
68
+ logger.error("WebSocket \uD074\uB77C\uC774\uC5B8\uD2B8 \uC624\uB958 \uBC1C\uC0DD", err);
69
+ this.emit("error", err);
70
+ }
71
+ _onClose(code) {
72
+ clearInterval(this._pingTimer);
73
+ this._protocol.dispose();
74
+ this.emit("close", code);
75
+ }
76
+ async _onMessage(msgBuffer) {
77
+ try {
78
+ if (msgBuffer.length === 1 && msgBuffer[0] === 1) {
79
+ if (this._socket.readyState === WebSocket.OPEN) {
80
+ this._socket.send(this._PONG_PACKET);
81
+ }
82
+ return;
83
+ }
84
+ const decodeResult = await this._protocol.decode(msgBuffer);
85
+ if (decodeResult.type === "progress") {
86
+ await this._send(decodeResult.uuid, {
87
+ name: "progress",
88
+ body: {
89
+ totalSize: decodeResult.totalSize,
90
+ completedSize: decodeResult.completedSize
91
+ }
92
+ });
93
+ } else {
94
+ const msg = decodeResult.message;
95
+ this.emit("message", { uuid: decodeResult.uuid, msg });
96
+ }
97
+ } catch (err) {
98
+ logger.error("WebSocket \uBA54\uC2DC\uC9C0 \uCC98\uB9AC \uC911 \uC624\uB958 \uBC1C\uC0DD", err);
99
+ }
100
+ }
101
+ }
102
+ export {
103
+ ServiceSocket
104
+ };
105
+ //# sourceMappingURL=service-socket.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/transport/socket/service-socket.ts"],
4
+ "sourcesContent": ["import type { Bytes } from \"@simplysm/core-common\";\nimport { DateTime, EventEmitter } from \"@simplysm/core-common\";\nimport type { FastifyRequest } from \"fastify\";\nimport { clearInterval } from \"node:timers\";\nimport { createConsola } from \"consola\";\nimport { WebSocket } from \"ws\";\nimport type { AuthTokenPayload } from \"../../auth/auth-token-payload\";\nimport { ProtocolWrapper } from \"../../protocol/protocol-wrapper\";\nimport type { ServiceClientMessage, ServiceServerMessage, ServiceServerRawMessage } from \"@simplysm/service-common\";\n\nconst logger = createConsola().withTag(\"service-server:ServiceSocket\");\n\nexport class ServiceSocket extends EventEmitter<{\n error: Error;\n close: number;\n message: { uuid: string; msg: ServiceClientMessage };\n}> {\n private readonly _PING_INTERVAL = 5000; // 5\uCD08\uB9C8\uB2E4 \uD551 \uC804\uC1A1\n private readonly _PONG_PACKET = new Uint8Array([0x02]);\n\n private readonly _protocol = new ProtocolWrapper();\n private readonly _listenerInfos: { eventName: string; key: string; info: unknown }[] = [];\n\n private _isAlive = true;\n private readonly _pingTimer: NodeJS.Timeout;\n\n readonly connectedAtDateTime = new DateTime();\n\n authTokenPayload?: AuthTokenPayload;\n\n constructor(\n private readonly _socket: WebSocket,\n private readonly _clientId: string,\n public readonly clientName: string,\n public readonly connReq: FastifyRequest,\n ) {\n super();\n\n this._socket.on(\"close\", this._onClose.bind(this));\n this._socket.on(\"error\", this._onError.bind(this));\n this._socket.on(\"message\", this._onMessage.bind(this));\n\n this._socket.on(\"pong\", () => {\n this._isAlive = true;\n });\n\n this._pingTimer = setInterval(() => {\n if (!this._isAlive) {\n this.close();\n return;\n }\n\n this._isAlive = false;\n this._socket.ping();\n }, this._PING_INTERVAL);\n }\n\n close() {\n this._socket.terminate();\n }\n\n async send(uuid: string, msg: ServiceServerMessage) {\n return this._send(uuid, msg);\n }\n\n private async _send(uuid: string, msg: ServiceServerRawMessage) {\n if (this._socket.readyState !== WebSocket.OPEN) return 0;\n\n const { chunks } = await this._protocol.encode(uuid, msg);\n for (const chunk of chunks) {\n this._socket.send(chunk);\n }\n\n return chunks.reduce((acc, item) => acc + item.length, 0);\n }\n\n addEventListener(key: string, eventName: string, info: unknown) {\n this._listenerInfos.push({ key, eventName, info });\n }\n\n removeEventListener(key: string) {\n const idx = this._listenerInfos.findIndex((item) => item.key === key);\n if (idx >= 0) {\n this._listenerInfos.splice(idx, 1);\n }\n }\n\n getEventListeners(eventName: string) {\n return this._listenerInfos\n .filter((item) => item.eventName === eventName)\n .map((item) => ({ key: item.key, info: item.info }));\n }\n\n filterEventTargetKeys(targetKeys: string[]) {\n return this._listenerInfos.filter((item) => targetKeys.includes(item.key)).map((item) => item.key);\n }\n\n private _onError(err: Error) {\n logger.error(\"WebSocket \uD074\uB77C\uC774\uC5B8\uD2B8 \uC624\uB958 \uBC1C\uC0DD\", err);\n this.emit(\"error\", err);\n }\n\n private _onClose(code: number) {\n clearInterval(this._pingTimer);\n this._protocol.dispose();\n this.emit(\"close\", code);\n }\n\n private async _onMessage(msgBuffer: Bytes) {\n try {\n // ping\uC5D0 \uB300\uD55C pong\uCC98\uB9AC\n if (msgBuffer.length === 1 && msgBuffer[0] === 0x01) {\n if (this._socket.readyState === WebSocket.OPEN) {\n this._socket.send(this._PONG_PACKET);\n }\n return;\n }\n\n const decodeResult = await this._protocol.decode(msgBuffer);\n if (decodeResult.type === \"progress\") {\n await this._send(decodeResult.uuid, {\n name: \"progress\",\n body: {\n totalSize: decodeResult.totalSize,\n completedSize: decodeResult.completedSize,\n },\n });\n } else {\n const msg = decodeResult.message as ServiceClientMessage;\n this.emit(\"message\", { uuid: decodeResult.uuid, msg });\n }\n } catch (err) {\n logger.error(\"WebSocket \uBA54\uC2DC\uC9C0 \uCC98\uB9AC \uC911 \uC624\uB958 \uBC1C\uC0DD\", err);\n }\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,144 @@
1
+ import { Uuid } from "@simplysm/core-common";
2
+ import { ServiceSocket } from "./service-socket";
3
+ import { createConsola } from "consola";
4
+ const logger = createConsola().withTag("service-server:WebSocketHandler");
5
+ class WebSocketHandler {
6
+ constructor(_executor, _jwt) {
7
+ this._executor = _executor;
8
+ this._jwt = _jwt;
9
+ }
10
+ _socketMap = /* @__PURE__ */ new Map();
11
+ addSocket(socket, clientId, clientName, connReq) {
12
+ try {
13
+ const serviceSocket = new ServiceSocket(socket, clientId, clientName, connReq);
14
+ const prevServiceSocket = this._socketMap.get(clientId);
15
+ if (prevServiceSocket != null) {
16
+ prevServiceSocket.close();
17
+ const connectionDateTimeText = prevServiceSocket.connectedAtDateTime.toFormatString("yyyy:MM:dd HH:mm:ss.fff");
18
+ logger.debug(`\uD074\uB77C\uC774\uC5B8\uD2B8 \uAE30\uC874\uC5F0\uACB0 \uB04A\uC74C: ${clientId}: ${connectionDateTimeText}`);
19
+ }
20
+ this._socketMap.set(clientId, serviceSocket);
21
+ serviceSocket.on("close", (code) => {
22
+ logger.debug(`\uD074\uB77C\uC774\uC5B8\uD2B8 \uC5F0\uACB0 \uB04A\uAE40: (code: ${code})`);
23
+ if (this._socketMap.get(clientId) !== serviceSocket) return;
24
+ this._socketMap.delete(clientId);
25
+ });
26
+ serviceSocket.on("message", async ({ uuid, msg }) => {
27
+ logger.debug("\uC694\uCCAD \uC218\uC2E0", msg);
28
+ const sentSize = await this._processRequest(serviceSocket, uuid, msg);
29
+ logger.debug(`\uC751\uB2F5 \uC804\uC1A1 (size: ${sentSize})`);
30
+ });
31
+ logger.debug("\uD074\uB77C\uC774\uC5B8\uD2B8 \uC5F0\uACB0\uB428", {
32
+ clientId,
33
+ remoteAddress: connReq.socket.remoteAddress,
34
+ socketSize: this._socketMap.size
35
+ });
36
+ } catch (err) {
37
+ logger.error("\uC5F0\uACB0 \uCC98\uB9AC \uC911 \uC624\uB958 \uBC1C\uC0DD", err);
38
+ socket.terminate();
39
+ }
40
+ }
41
+ closeAll() {
42
+ for (const serviceSocket of this._socketMap.values()) {
43
+ serviceSocket.close();
44
+ }
45
+ }
46
+ async broadcastReload(clientName, changedFileSet) {
47
+ for (const serviceSocket of this._socketMap.values()) {
48
+ await serviceSocket.send(Uuid.new().toString(), {
49
+ name: "reload",
50
+ body: {
51
+ clientName,
52
+ changedFileSet
53
+ }
54
+ });
55
+ }
56
+ }
57
+ async emitToServer(eventType, infoSelector, data) {
58
+ const eventName = eventType.prototype.eventName;
59
+ const targetKeys = Array.from(this._socketMap.values()).flatMap((subSock) => subSock.getEventListeners(eventName)).filter((item) => infoSelector(item.info)).map((item) => item.key);
60
+ for (const subSock of this._socketMap.values()) {
61
+ const subTargetKeys = subSock.filterEventTargetKeys(targetKeys);
62
+ if (subTargetKeys.length > 0) {
63
+ await subSock.send(Uuid.new().toString(), {
64
+ name: "evt:on",
65
+ body: {
66
+ keys: subTargetKeys,
67
+ data
68
+ }
69
+ });
70
+ }
71
+ }
72
+ }
73
+ async _processRequest(serviceSocket, uuid, message) {
74
+ try {
75
+ if (message.name.includes(".") && Array.isArray(message.body)) {
76
+ const [serviceName, methodName] = message.name.split(".");
77
+ const result = await this._executor.runMethod({
78
+ serviceName,
79
+ methodName,
80
+ params: message.body,
81
+ socket: serviceSocket
82
+ });
83
+ return await serviceSocket.send(uuid, { name: "response", body: result });
84
+ } else if (message.name === "evt:add") {
85
+ const { key, name, info } = message.body;
86
+ serviceSocket.addEventListener(key, name, info);
87
+ return await serviceSocket.send(uuid, { name: "response" });
88
+ } else if (message.name === "evt:remove") {
89
+ const { key } = message.body;
90
+ serviceSocket.removeEventListener(key);
91
+ return await serviceSocket.send(uuid, { name: "response" });
92
+ } else if (message.name === "evt:gets") {
93
+ const { name } = message.body;
94
+ const infos = Array.from(this._socketMap.values()).flatMap((subSock) => subSock.getEventListeners(name));
95
+ return await serviceSocket.send(uuid, { name: "response", body: infos });
96
+ } else if (message.name === "evt:emit") {
97
+ const { keys, data } = message.body;
98
+ for (const subSock of this._socketMap.values()) {
99
+ const targetKeys = subSock.filterEventTargetKeys(keys);
100
+ if (targetKeys.length > 0) {
101
+ await subSock.send(uuid, {
102
+ name: "evt:on",
103
+ body: {
104
+ keys: targetKeys,
105
+ data
106
+ }
107
+ });
108
+ }
109
+ }
110
+ return await serviceSocket.send(uuid, { name: "response" });
111
+ } else if (message.name === "auth") {
112
+ const token = message.body;
113
+ serviceSocket.authTokenPayload = await this._jwt.verify(token);
114
+ return await serviceSocket.send(uuid, { name: "response" });
115
+ } else {
116
+ const err = new Error("\uC694\uCCAD\uC774 \uC798\uBABB\uB418\uC5C8\uC2B5\uB2C8\uB2E4.");
117
+ return await serviceSocket.send(uuid, {
118
+ name: "error",
119
+ body: {
120
+ name: err.name,
121
+ message: err.message,
122
+ stack: err.stack,
123
+ code: "BAD_MESSAGE"
124
+ }
125
+ });
126
+ }
127
+ } catch (err) {
128
+ const error = err instanceof Error ? err : new Error(typeof err === "string" ? err : "\uC54C \uC218 \uC5C6\uB294 \uC624\uB958\uAC00 \uBC1C\uC0DD\uD558\uC600\uC2B5\uB2C8\uB2E4.");
129
+ return serviceSocket.send(uuid, {
130
+ name: "error",
131
+ body: {
132
+ name: error.name,
133
+ message: error.message,
134
+ code: "INTERNAL_ERROR",
135
+ stack: error.stack
136
+ }
137
+ });
138
+ }
139
+ }
140
+ }
141
+ export {
142
+ WebSocketHandler
143
+ };
144
+ //# sourceMappingURL=websocket-handler.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/transport/socket/websocket-handler.ts"],
4
+ "sourcesContent": ["import type { WebSocket } from \"ws\";\nimport type { Type } from \"@simplysm/core-common\";\nimport { Uuid } from \"@simplysm/core-common\";\nimport type { ServiceEventListener, ServiceClientMessage } from \"@simplysm/service-common\";\nimport type { ServiceExecutor } from \"../../core/service-executor\";\nimport { ServiceSocket } from \"./service-socket\";\nimport type { JwtManager } from \"../../auth/jwt-manager\";\nimport type { FastifyRequest } from \"fastify\";\nimport { createConsola } from \"consola\";\n\nconst logger = createConsola().withTag(\"service-server:WebSocketHandler\");\n\nexport class WebSocketHandler {\n private readonly _socketMap = new Map<string, ServiceSocket>();\n\n constructor(\n private readonly _executor: ServiceExecutor,\n private readonly _jwt: JwtManager,\n ) {}\n\n addSocket(socket: WebSocket, clientId: string, clientName: string, connReq: FastifyRequest) {\n try {\n const serviceSocket = new ServiceSocket(socket, clientId, clientName, connReq);\n\n // \uAE30\uC874 \uC5F0\uACB0 \uB04A\uAE30\n const prevServiceSocket = this._socketMap.get(clientId);\n if (prevServiceSocket != null) {\n prevServiceSocket.close();\n\n const connectionDateTimeText = prevServiceSocket.connectedAtDateTime.toFormatString(\"yyyy:MM:dd HH:mm:ss.fff\");\n logger.debug(`\uD074\uB77C\uC774\uC5B8\uD2B8 \uAE30\uC874\uC5F0\uACB0 \uB04A\uC74C: ${clientId}: ${connectionDateTimeText}`);\n }\n\n this._socketMap.set(clientId, serviceSocket);\n\n serviceSocket.on(\"close\", (code) => {\n logger.debug(`\uD074\uB77C\uC774\uC5B8\uD2B8 \uC5F0\uACB0 \uB04A\uAE40: (code: ${code})`);\n\n if (this._socketMap.get(clientId) !== serviceSocket) return;\n this._socketMap.delete(clientId);\n });\n\n serviceSocket.on(\"message\", async ({ uuid, msg }) => {\n logger.debug(\"\uC694\uCCAD \uC218\uC2E0\", msg);\n const sentSize = await this._processRequest(serviceSocket, uuid, msg);\n logger.debug(`\uC751\uB2F5 \uC804\uC1A1 (size: ${sentSize})`);\n });\n\n logger.debug(\"\uD074\uB77C\uC774\uC5B8\uD2B8 \uC5F0\uACB0\uB428\", {\n clientId,\n remoteAddress: connReq.socket.remoteAddress,\n socketSize: this._socketMap.size,\n });\n } catch (err) {\n logger.error(\"\uC5F0\uACB0 \uCC98\uB9AC \uC911 \uC624\uB958 \uBC1C\uC0DD\", err);\n socket.terminate();\n }\n }\n\n closeAll() {\n for (const serviceSocket of this._socketMap.values()) {\n serviceSocket.close();\n }\n }\n\n async broadcastReload(clientName: string | undefined, changedFileSet: Set<string>) {\n for (const serviceSocket of this._socketMap.values()) {\n await serviceSocket.send(Uuid.new().toString(), {\n name: \"reload\",\n body: {\n clientName,\n changedFileSet,\n },\n });\n }\n }\n\n async emitToServer<T extends ServiceEventListener<unknown, unknown>>(\n eventType: Type<T>,\n infoSelector: (item: T[\"$info\"]) => boolean,\n data: T[\"$data\"],\n ) {\n const eventName = eventType.prototype.eventName as string;\n const targetKeys = Array.from(this._socketMap.values())\n .flatMap((subSock) => subSock.getEventListeners(eventName))\n .filter((item) => infoSelector(item.info as T[\"$info\"]))\n .map((item) => item.key);\n\n for (const subSock of this._socketMap.values()) {\n const subTargetKeys = subSock.filterEventTargetKeys(targetKeys);\n if (subTargetKeys.length > 0) {\n await subSock.send(Uuid.new().toString(), {\n name: \"evt:on\",\n body: {\n keys: subTargetKeys,\n data,\n },\n });\n }\n }\n }\n\n private async _processRequest(\n serviceSocket: ServiceSocket,\n uuid: string,\n message: ServiceClientMessage,\n ): Promise<number> {\n try {\n if (message.name.includes(\".\") && Array.isArray(message.body)) {\n const [serviceName, methodName] = message.name.split(\".\");\n\n const result = await this._executor.runMethod({\n serviceName,\n methodName,\n params: message.body,\n socket: serviceSocket,\n });\n\n return await serviceSocket.send(uuid, { name: \"response\", body: result });\n } else if (message.name === \"evt:add\") {\n const { key, name, info } = message.body as { key: string; name: string; info: unknown };\n serviceSocket.addEventListener(key, name, info);\n return await serviceSocket.send(uuid, { name: \"response\" });\n } else if (message.name === \"evt:remove\") {\n const { key } = message.body as { key: string };\n serviceSocket.removeEventListener(key);\n return await serviceSocket.send(uuid, { name: \"response\" });\n } else if (message.name === \"evt:gets\") {\n const { name } = message.body as { name: string };\n const infos = Array.from(this._socketMap.values()).flatMap((subSock) => subSock.getEventListeners(name));\n return await serviceSocket.send(uuid, { name: \"response\", body: infos });\n } else if (message.name === \"evt:emit\") {\n const { keys, data } = message.body as { keys: string[]; data: unknown };\n\n for (const subSock of this._socketMap.values()) {\n const targetKeys = subSock.filterEventTargetKeys(keys);\n if (targetKeys.length > 0) {\n await subSock.send(uuid, {\n name: \"evt:on\",\n body: {\n keys: targetKeys,\n data,\n },\n });\n }\n }\n\n return await serviceSocket.send(uuid, { name: \"response\" });\n } else if (message.name === \"auth\") {\n const token = message.body;\n serviceSocket.authTokenPayload = await this._jwt.verify(token);\n return await serviceSocket.send(uuid, { name: \"response\" });\n } else {\n const err = new Error(\"\uC694\uCCAD\uC774 \uC798\uBABB\uB418\uC5C8\uC2B5\uB2C8\uB2E4.\");\n\n return await serviceSocket.send(uuid, {\n name: \"error\",\n body: {\n name: err.name,\n message: err.message,\n stack: err.stack,\n code: \"BAD_MESSAGE\",\n },\n });\n }\n } catch (err) {\n const error =\n err instanceof Error ? err : new Error(typeof err === \"string\" ? err : \"\uC54C \uC218 \uC5C6\uB294 \uC624\uB958\uAC00 \uBC1C\uC0DD\uD558\uC600\uC2B5\uB2C8\uB2E4.\");\n\n return serviceSocket.send(uuid, {\n name: \"error\",\n body: {\n name: error.name,\n message: error.message,\n code: \"INTERNAL_ERROR\",\n stack: error.stack,\n },\n });\n }\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=server-options.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": [],
4
+ "sourcesContent": [],
5
+ "mappings": "",
6
+ "names": []
7
+ }
@@ -0,0 +1,62 @@
1
+ import { LazyGcMap } from "@simplysm/core-common";
2
+ import { fsExists, fsReadJson, FsWatcher } from "@simplysm/core-node";
3
+ import path from "path";
4
+ import { createConsola } from "consola";
5
+ const logger = createConsola().withTag("service-server:ConfigManager");
6
+ class ConfigManager {
7
+ // 값: Config 객체, 키: 파일 경로
8
+ static _cache = new LazyGcMap({
9
+ gcInterval: 10 * 60 * 1e3,
10
+ // 10분마다
11
+ expireTime: 60 * 60 * 1e3,
12
+ // 1시간 만료
13
+ onExpire: async (filePath) => {
14
+ logger.debug(`\uC124\uC815 \uCE90\uC2DC \uB9CC\uB8CC \uBC0F \uAC10\uC2DC \uD574\uC81C: ${path.basename(filePath)}`);
15
+ await ConfigManager._closeWatcher(filePath);
16
+ }
17
+ });
18
+ static _watchers = /* @__PURE__ */ new Map();
19
+ static async getConfig(filePath) {
20
+ if (this._cache.has(filePath)) {
21
+ return this._cache.get(filePath);
22
+ }
23
+ if (!await fsExists(filePath)) return void 0;
24
+ const config = await fsReadJson(filePath);
25
+ this._cache.set(filePath, config);
26
+ if (!this._watchers.has(filePath)) {
27
+ try {
28
+ const watcher = await FsWatcher.watch([filePath]);
29
+ this._watchers.set(filePath, watcher);
30
+ watcher.onChange({ delay: 100 }, async () => {
31
+ if (!await fsExists(filePath)) {
32
+ this._cache.delete(filePath);
33
+ await this._closeWatcher(filePath);
34
+ logger.debug(`\uC124\uC815 \uD30C\uC77C \uC0AD\uC81C\uB428: ${path.basename(filePath)}`);
35
+ return;
36
+ }
37
+ try {
38
+ const newConfig = await fsReadJson(filePath);
39
+ this._cache.set(filePath, newConfig);
40
+ logger.debug(`\uC124\uC815 \uD30C\uC77C \uC2E4\uC2DC\uAC04 \uAC31\uC2E0: ${path.basename(filePath)}`);
41
+ } catch (err) {
42
+ logger.warn(`\uC124\uC815 \uD30C\uC77C \uAC31\uC2E0 \uC2E4\uD328: ${filePath}`, err);
43
+ }
44
+ });
45
+ } catch (err) {
46
+ logger.error(`\uAC10\uC2DC \uC2E4\uD328: ${filePath}`, err);
47
+ }
48
+ }
49
+ return config;
50
+ }
51
+ static async _closeWatcher(filePath) {
52
+ const watcher = this._watchers.get(filePath);
53
+ if (watcher != null) {
54
+ await watcher.close();
55
+ this._watchers.delete(filePath);
56
+ }
57
+ }
58
+ }
59
+ export {
60
+ ConfigManager
61
+ };
62
+ //# sourceMappingURL=config-manager.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/utils/config-manager.ts"],
4
+ "sourcesContent": ["import { LazyGcMap } from \"@simplysm/core-common\";\nimport { fsExists, fsReadJson, FsWatcher } from \"@simplysm/core-node\";\nimport path from \"path\";\nimport { createConsola } from \"consola\";\n\nconst logger = createConsola().withTag(\"service-server:ConfigManager\");\n\nexport class ConfigManager {\n // \uAC12: Config \uAC1D\uCCB4, \uD0A4: \uD30C\uC77C \uACBD\uB85C\n private static readonly _cache = new LazyGcMap<string, unknown>({\n gcInterval: 10 * 60 * 1000, // 10\uBD84\uB9C8\uB2E4\n expireTime: 60 * 60 * 1000, // 1\uC2DC\uAC04 \uB9CC\uB8CC\n onExpire: async (filePath) => {\n logger.debug(`\uC124\uC815 \uCE90\uC2DC \uB9CC\uB8CC \uBC0F \uAC10\uC2DC \uD574\uC81C: ${path.basename(filePath)}`);\n await ConfigManager._closeWatcher(filePath);\n },\n });\n\n private static readonly _watchers = new Map<string, FsWatcher>();\n\n static async getConfig<T>(filePath: string): Promise<T | undefined> {\n // 1. \uCE90\uC2DC \uC801\uC911 (\uC2DC\uAC04 \uC790\uB3D9 \uAC31\uC2E0)\n if (this._cache.has(filePath)) {\n return this._cache.get(filePath) as T;\n }\n\n if (!(await fsExists(filePath))) return undefined;\n\n // 2. \uB85C\uB4DC \uBC0F \uCE90\uC2DC\n const config = await fsReadJson(filePath);\n this._cache.set(filePath, config);\n\n // 3. Watcher \uB4F1\uB85D\n if (!this._watchers.has(filePath)) {\n try {\n const watcher = await FsWatcher.watch([filePath]);\n this._watchers.set(filePath, watcher);\n\n watcher.onChange({ delay: 100 }, async () => {\n if (!(await fsExists(filePath))) {\n this._cache.delete(filePath);\n await this._closeWatcher(filePath);\n logger.debug(`\uC124\uC815 \uD30C\uC77C \uC0AD\uC81C\uB428: ${path.basename(filePath)}`);\n return;\n }\n\n try {\n const newConfig = await fsReadJson(filePath);\n this._cache.set(filePath, newConfig);\n logger.debug(`\uC124\uC815 \uD30C\uC77C \uC2E4\uC2DC\uAC04 \uAC31\uC2E0: ${path.basename(filePath)}`);\n } catch (err) {\n logger.warn(`\uC124\uC815 \uD30C\uC77C \uAC31\uC2E0 \uC2E4\uD328: ${filePath}`, err);\n }\n });\n } catch (err) {\n logger.error(`\uAC10\uC2DC \uC2E4\uD328: ${filePath}`, err);\n }\n }\n\n return config as T;\n }\n\n private static async _closeWatcher(filePath: string) {\n const watcher = this._watchers.get(filePath);\n if (watcher != null) {\n await watcher.close();\n this._watchers.delete(filePath);\n }\n }\n}\n"],
5
+ "mappings": "AAAA,SAAS,iBAAiB;AAC1B,SAAS,UAAU,YAAY,iBAAiB;AAChD,OAAO,UAAU;AACjB,SAAS,qBAAqB;AAE9B,MAAM,SAAS,cAAc,EAAE,QAAQ,8BAA8B;AAE9D,MAAM,cAAc;AAAA;AAAA,EAEzB,OAAwB,SAAS,IAAI,UAA2B;AAAA,IAC9D,YAAY,KAAK,KAAK;AAAA;AAAA,IACtB,YAAY,KAAK,KAAK;AAAA;AAAA,IACtB,UAAU,OAAO,aAAa;AAC5B,aAAO,MAAM,4EAAqB,KAAK,SAAS,QAAQ,CAAC,EAAE;AAC3D,YAAM,cAAc,cAAc,QAAQ;AAAA,IAC5C;AAAA,EACF,CAAC;AAAA,EAED,OAAwB,YAAY,oBAAI,IAAuB;AAAA,EAE/D,aAAa,UAAa,UAA0C;AAElE,QAAI,KAAK,OAAO,IAAI,QAAQ,GAAG;AAC7B,aAAO,KAAK,OAAO,IAAI,QAAQ;AAAA,IACjC;AAEA,QAAI,CAAE,MAAM,SAAS,QAAQ,EAAI,QAAO;AAGxC,UAAM,SAAS,MAAM,WAAW,QAAQ;AACxC,SAAK,OAAO,IAAI,UAAU,MAAM;AAGhC,QAAI,CAAC,KAAK,UAAU,IAAI,QAAQ,GAAG;AACjC,UAAI;AACF,cAAM,UAAU,MAAM,UAAU,MAAM,CAAC,QAAQ,CAAC;AAChD,aAAK,UAAU,IAAI,UAAU,OAAO;AAEpC,gBAAQ,SAAS,EAAE,OAAO,IAAI,GAAG,YAAY;AAC3C,cAAI,CAAE,MAAM,SAAS,QAAQ,GAAI;AAC/B,iBAAK,OAAO,OAAO,QAAQ;AAC3B,kBAAM,KAAK,cAAc,QAAQ;AACjC,mBAAO,MAAM,iDAAc,KAAK,SAAS,QAAQ,CAAC,EAAE;AACpD;AAAA,UACF;AAEA,cAAI;AACF,kBAAM,YAAY,MAAM,WAAW,QAAQ;AAC3C,iBAAK,OAAO,IAAI,UAAU,SAAS;AACnC,mBAAO,MAAM,8DAAiB,KAAK,SAAS,QAAQ,CAAC,EAAE;AAAA,UACzD,SAAS,KAAK;AACZ,mBAAO,KAAK,wDAAgB,QAAQ,IAAI,GAAG;AAAA,UAC7C;AAAA,QACF,CAAC;AAAA,MACH,SAAS,KAAK;AACZ,eAAO,MAAM,8BAAU,QAAQ,IAAI,GAAG;AAAA,MACxC;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,aAAqB,cAAc,UAAkB;AACnD,UAAM,UAAU,KAAK,UAAU,IAAI,QAAQ;AAC3C,QAAI,WAAW,MAAM;AACnB,YAAM,QAAQ,MAAM;AACpB,WAAK,UAAU,OAAO,QAAQ;AAAA,IAChC;AAAA,EACF;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,15 @@
1
+ import { createWorker } from "@simplysm/core-node";
2
+ import { ServiceProtocol } from "@simplysm/service-common";
3
+ const protocol = new ServiceProtocol();
4
+ var service_protocol_worker_default = createWorker({
5
+ encode: (uuid, message) => {
6
+ return protocol.encode(uuid, message);
7
+ },
8
+ decode: (bytes) => {
9
+ return protocol.decode(bytes);
10
+ }
11
+ });
12
+ export {
13
+ service_protocol_worker_default as default
14
+ };
15
+ //# sourceMappingURL=service-protocol.worker.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/workers/service-protocol.worker.ts"],
4
+ "sourcesContent": ["import { createWorker } from \"@simplysm/core-node\";\nimport type { Bytes } from \"@simplysm/core-common\";\nimport type { ServiceMessageDecodeResult, ServiceMessage } from \"@simplysm/service-common\";\nimport { ServiceProtocol } from \"@simplysm/service-common\";\n\nconst protocol = new ServiceProtocol();\n\nexport default createWorker({\n encode: (uuid: string, message: ServiceMessage): { chunks: Bytes[]; totalSize: number } => {\n return protocol.encode(uuid, message);\n },\n decode: (bytes: Bytes): ServiceMessageDecodeResult<ServiceMessage> => {\n return protocol.decode(bytes);\n },\n});\n"],
5
+ "mappings": "AAAA,SAAS,oBAAoB;AAG7B,SAAS,uBAAuB;AAEhC,MAAM,WAAW,IAAI,gBAAgB;AAErC,IAAO,kCAAQ,aAAa;AAAA,EAC1B,QAAQ,CAAC,MAAc,YAAoE;AACzF,WAAO,SAAS,OAAO,MAAM,OAAO;AAAA,EACtC;AAAA,EACA,QAAQ,CAAC,UAA6D;AACpE,WAAO,SAAS,OAAO,KAAK;AAAA,EAC9B;AACF,CAAC;",
6
+ "names": []
7
+ }
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@simplysm/service-server",
3
+ "sideEffects": false,
4
+ "version": "13.0.0-beta.6",
5
+ "description": "심플리즘 패키지 - 서비스 모듈 (server)",
6
+ "author": "김석래",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/kslhunter/simplysm.git",
10
+ "directory": "packages/service-server"
11
+ },
12
+ "license": "Apache-2.0",
13
+ "type": "module",
14
+ "main": "./dist/index.js",
15
+ "types": "./dist/index.d.ts",
16
+ "dependencies": {
17
+ "@fastify/cors": "^11.2.0",
18
+ "@fastify/helmet": "^13.0.2",
19
+ "@fastify/middie": "^9.1.0",
20
+ "@fastify/multipart": "^9.4.0",
21
+ "@fastify/reply-from": "^12.5.0",
22
+ "@fastify/static": "^9.0.0",
23
+ "@fastify/websocket": "^11.2.0",
24
+ "@simplysm/core-common": "workspace:*",
25
+ "@simplysm/core-node": "workspace:*",
26
+ "@simplysm/orm-common": "workspace:*",
27
+ "@simplysm/orm-node": "workspace:*",
28
+ "@simplysm/service-common": "workspace:*",
29
+ "bufferutil": "^4.1.0",
30
+ "consola": "^3.4.2",
31
+ "fastify": "^5.7.1",
32
+ "jose": "^6.1.3",
33
+ "mime": "^4.1.0",
34
+ "nodemailer": "^7.0.12",
35
+ "semver": "^7.7.3",
36
+ "utf-8-validate": "^6.0.6",
37
+ "ws": "^8.19.0"
38
+ },
39
+ "devDependencies": {
40
+ "@types/nodemailer": "^7.0.5",
41
+ "@types/semver": "^7.7.1",
42
+ "@types/ws": "^8.18.1"
43
+ }
44
+ }
@@ -0,0 +1,6 @@
1
+ import type { JWTPayload } from "jose";
2
+
3
+ export interface AuthTokenPayload<TAuthInfo = unknown> extends JWTPayload {
4
+ roles: string[];
5
+ data: TAuthInfo;
6
+ }