@fivepixels-js/lite-api 0.2.1

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 (372) hide show
  1. package/LICENSE +93 -0
  2. package/README.md +537 -0
  3. package/dist/app.d.ts +10 -0
  4. package/dist/app.d.ts.map +1 -0
  5. package/dist/app.js +70 -0
  6. package/dist/app.js.map +1 -0
  7. package/dist/auth/identity.repository.d.ts +62 -0
  8. package/dist/auth/identity.repository.d.ts.map +1 -0
  9. package/dist/auth/identity.repository.js +202 -0
  10. package/dist/auth/identity.repository.js.map +1 -0
  11. package/dist/auth/identity.routes.d.ts +5 -0
  12. package/dist/auth/identity.routes.d.ts.map +1 -0
  13. package/dist/auth/identity.routes.js +56 -0
  14. package/dist/auth/identity.routes.js.map +1 -0
  15. package/dist/auth/identity.schemas.d.ts +2711 -0
  16. package/dist/auth/identity.schemas.d.ts.map +1 -0
  17. package/dist/auth/identity.schemas.js +117 -0
  18. package/dist/auth/identity.schemas.js.map +1 -0
  19. package/dist/auth/identity.service.d.ts +37 -0
  20. package/dist/auth/identity.service.d.ts.map +1 -0
  21. package/dist/auth/identity.service.js +131 -0
  22. package/dist/auth/identity.service.js.map +1 -0
  23. package/dist/auth/password.service.d.ts +15 -0
  24. package/dist/auth/password.service.d.ts.map +1 -0
  25. package/dist/auth/password.service.js +116 -0
  26. package/dist/auth/password.service.js.map +1 -0
  27. package/dist/auth/token.service.d.ts +38 -0
  28. package/dist/auth/token.service.d.ts.map +1 -0
  29. package/dist/auth/token.service.js +54 -0
  30. package/dist/auth/token.service.js.map +1 -0
  31. package/dist/cli/cli.d.ts +3 -0
  32. package/dist/cli/cli.d.ts.map +1 -0
  33. package/dist/cli/cli.js +132 -0
  34. package/dist/cli/cli.js.map +1 -0
  35. package/dist/cli/commands/backup.d.ts +4 -0
  36. package/dist/cli/commands/backup.d.ts.map +1 -0
  37. package/dist/cli/commands/backup.js +24 -0
  38. package/dist/cli/commands/backup.js.map +1 -0
  39. package/dist/cli/commands/init.d.ts +4 -0
  40. package/dist/cli/commands/init.d.ts.map +1 -0
  41. package/dist/cli/commands/init.js +23 -0
  42. package/dist/cli/commands/init.js.map +1 -0
  43. package/dist/cli/commands/migrate.d.ts +4 -0
  44. package/dist/cli/commands/migrate.d.ts.map +1 -0
  45. package/dist/cli/commands/migrate.js +20 -0
  46. package/dist/cli/commands/migrate.js.map +1 -0
  47. package/dist/cli/commands/start.d.ts +4 -0
  48. package/dist/cli/commands/start.d.ts.map +1 -0
  49. package/dist/cli/commands/start.js +13 -0
  50. package/dist/cli/commands/start.js.map +1 -0
  51. package/dist/cli/commands/status.d.ts +4 -0
  52. package/dist/cli/commands/status.d.ts.map +1 -0
  53. package/dist/cli/commands/status.js +48 -0
  54. package/dist/cli/commands/status.js.map +1 -0
  55. package/dist/cli/index.d.ts +3 -0
  56. package/dist/cli/index.d.ts.map +1 -0
  57. package/dist/cli/index.js +4 -0
  58. package/dist/cli/index.js.map +1 -0
  59. package/dist/cli/io.d.ts +6 -0
  60. package/dist/cli/io.d.ts.map +1 -0
  61. package/dist/cli/io.js +9 -0
  62. package/dist/cli/io.js.map +1 -0
  63. package/dist/community/contracts.d.ts +29 -0
  64. package/dist/community/contracts.d.ts.map +1 -0
  65. package/dist/community/contracts.js +41 -0
  66. package/dist/community/contracts.js.map +1 -0
  67. package/dist/community/error-handler.d.ts +3 -0
  68. package/dist/community/error-handler.d.ts.map +1 -0
  69. package/dist/community/error-handler.js +66 -0
  70. package/dist/community/error-handler.js.map +1 -0
  71. package/dist/community/errors.d.ts +10 -0
  72. package/dist/community/errors.d.ts.map +1 -0
  73. package/dist/community/errors.js +39 -0
  74. package/dist/community/errors.js.map +1 -0
  75. package/dist/community/etag.d.ts +5 -0
  76. package/dist/community/etag.d.ts.map +1 -0
  77. package/dist/community/etag.js +31 -0
  78. package/dist/community/etag.js.map +1 -0
  79. package/dist/community/input.d.ts +10 -0
  80. package/dist/community/input.d.ts.map +1 -0
  81. package/dist/community/input.js +48 -0
  82. package/dist/community/input.js.map +1 -0
  83. package/dist/community/mutation-route.d.ts +7 -0
  84. package/dist/community/mutation-route.d.ts.map +1 -0
  85. package/dist/community/mutation-route.js +47 -0
  86. package/dist/community/mutation-route.js.map +1 -0
  87. package/dist/community/schemas.d.ts +807 -0
  88. package/dist/community/schemas.d.ts.map +1 -0
  89. package/dist/community/schemas.js +100 -0
  90. package/dist/community/schemas.js.map +1 -0
  91. package/dist/community/system.schemas.d.ts +40 -0
  92. package/dist/community/system.schemas.d.ts.map +1 -0
  93. package/dist/community/system.schemas.js +30 -0
  94. package/dist/community/system.schemas.js.map +1 -0
  95. package/dist/config/config.d.ts +43 -0
  96. package/dist/config/config.d.ts.map +1 -0
  97. package/dist/config/config.js +208 -0
  98. package/dist/config/config.js.map +1 -0
  99. package/dist/config/data-path.d.ts +2 -0
  100. package/dist/config/data-path.d.ts.map +1 -0
  101. package/dist/config/data-path.js +16 -0
  102. package/dist/config/data-path.js.map +1 -0
  103. package/dist/config/jwt-secret.d.ts +3 -0
  104. package/dist/config/jwt-secret.d.ts.map +1 -0
  105. package/dist/config/jwt-secret.js +67 -0
  106. package/dist/config/jwt-secret.js.map +1 -0
  107. package/dist/db/backup.d.ts +10 -0
  108. package/dist/db/backup.d.ts.map +1 -0
  109. package/dist/db/backup.js +51 -0
  110. package/dist/db/backup.js.map +1 -0
  111. package/dist/db/connection.d.ts +15 -0
  112. package/dist/db/connection.d.ts.map +1 -0
  113. package/dist/db/connection.js +82 -0
  114. package/dist/db/connection.js.map +1 -0
  115. package/dist/db/guid-order.d.ts +2 -0
  116. package/dist/db/guid-order.d.ts.map +1 -0
  117. package/dist/db/guid-order.js +9 -0
  118. package/dist/db/guid-order.js.map +1 -0
  119. package/dist/db/migrate.d.ts +37 -0
  120. package/dist/db/migrate.d.ts.map +1 -0
  121. package/dist/db/migrate.js +145 -0
  122. package/dist/db/migrate.js.map +1 -0
  123. package/dist/db/migrations/001_schema_migrations.sql +6 -0
  124. package/dist/db/migrations/002_identity.sql +95 -0
  125. package/dist/db/migrations/003_projects.sql +62 -0
  126. package/dist/db/migrations/004_feedback_core.sql +236 -0
  127. package/dist/db/migrations/005_project_member_management.sql +55 -0
  128. package/dist/db/migrations/006_align_credentials.sql +33 -0
  129. package/dist/db/migrations/007_align_storage_values.sql +78 -0
  130. package/dist/db/storage-migration.d.ts +9 -0
  131. package/dist/db/storage-migration.d.ts.map +1 -0
  132. package/dist/db/storage-migration.js +79 -0
  133. package/dist/db/storage-migration.js.map +1 -0
  134. package/dist/db/transaction.d.ts +9 -0
  135. package/dist/db/transaction.d.ts.map +1 -0
  136. package/dist/db/transaction.js +14 -0
  137. package/dist/db/transaction.js.map +1 -0
  138. package/dist/errors/error-handler.d.ts +3 -0
  139. package/dist/errors/error-handler.d.ts.map +1 -0
  140. package/dist/errors/error-handler.js +50 -0
  141. package/dist/errors/error-handler.js.map +1 -0
  142. package/dist/feedbacks/feedback-workflow.service.d.ts +59 -0
  143. package/dist/feedbacks/feedback-workflow.service.d.ts.map +1 -0
  144. package/dist/feedbacks/feedback-workflow.service.js +89 -0
  145. package/dist/feedbacks/feedback-workflow.service.js.map +1 -0
  146. package/dist/feedbacks/feedback.input.d.ts +4 -0
  147. package/dist/feedbacks/feedback.input.d.ts.map +1 -0
  148. package/dist/feedbacks/feedback.input.js +25 -0
  149. package/dist/feedbacks/feedback.input.js.map +1 -0
  150. package/dist/feedbacks/feedback.repository.d.ts +48 -0
  151. package/dist/feedbacks/feedback.repository.d.ts.map +1 -0
  152. package/dist/feedbacks/feedback.repository.js +238 -0
  153. package/dist/feedbacks/feedback.repository.js.map +1 -0
  154. package/dist/feedbacks/feedback.routes.d.ts +4 -0
  155. package/dist/feedbacks/feedback.routes.d.ts.map +1 -0
  156. package/dist/feedbacks/feedback.routes.js +78 -0
  157. package/dist/feedbacks/feedback.routes.js.map +1 -0
  158. package/dist/feedbacks/feedback.schemas.d.ts +642 -0
  159. package/dist/feedbacks/feedback.schemas.d.ts.map +1 -0
  160. package/dist/feedbacks/feedback.schemas.js +131 -0
  161. package/dist/feedbacks/feedback.schemas.js.map +1 -0
  162. package/dist/feedbacks/feedback.service.d.ts +23 -0
  163. package/dist/feedbacks/feedback.service.d.ts.map +1 -0
  164. package/dist/feedbacks/feedback.service.js +74 -0
  165. package/dist/feedbacks/feedback.service.js.map +1 -0
  166. package/dist/feedbacks/feedback.types.d.ts +64 -0
  167. package/dist/feedbacks/feedback.types.d.ts.map +1 -0
  168. package/dist/feedbacks/feedback.types.js +2 -0
  169. package/dist/feedbacks/feedback.types.js.map +1 -0
  170. package/dist/page-elements/page-element.input.d.ts +7 -0
  171. package/dist/page-elements/page-element.input.d.ts.map +1 -0
  172. package/dist/page-elements/page-element.input.js +59 -0
  173. package/dist/page-elements/page-element.input.js.map +1 -0
  174. package/dist/page-elements/page-element.repository.d.ts +21 -0
  175. package/dist/page-elements/page-element.repository.d.ts.map +1 -0
  176. package/dist/page-elements/page-element.repository.js +123 -0
  177. package/dist/page-elements/page-element.repository.js.map +1 -0
  178. package/dist/page-elements/page-element.schemas.d.ts +97 -0
  179. package/dist/page-elements/page-element.schemas.d.ts.map +1 -0
  180. package/dist/page-elements/page-element.schemas.js +40 -0
  181. package/dist/page-elements/page-element.schemas.js.map +1 -0
  182. package/dist/page-elements/page-element.types.d.ts +24 -0
  183. package/dist/page-elements/page-element.types.d.ts.map +1 -0
  184. package/dist/page-elements/page-element.types.js +2 -0
  185. package/dist/page-elements/page-element.types.js.map +1 -0
  186. package/dist/plugins/auth.plugin.d.ts +3 -0
  187. package/dist/plugins/auth.plugin.d.ts.map +1 -0
  188. package/dist/plugins/auth.plugin.js +28 -0
  189. package/dist/plugins/auth.plugin.js.map +1 -0
  190. package/dist/plugins/community-api.plugin.d.ts +5 -0
  191. package/dist/plugins/community-api.plugin.d.ts.map +1 -0
  192. package/dist/plugins/community-api.plugin.js +25 -0
  193. package/dist/plugins/community-api.plugin.js.map +1 -0
  194. package/dist/plugins/cors.plugin.d.ts +3 -0
  195. package/dist/plugins/cors.plugin.d.ts.map +1 -0
  196. package/dist/plugins/cors.plugin.js +19 -0
  197. package/dist/plugins/cors.plugin.js.map +1 -0
  198. package/dist/plugins/swagger.plugin.d.ts +3 -0
  199. package/dist/plugins/swagger.plugin.d.ts.map +1 -0
  200. package/dist/plugins/swagger.plugin.js +59 -0
  201. package/dist/plugins/swagger.plugin.js.map +1 -0
  202. package/dist/plugins/user-auth.plugin.d.ts +33 -0
  203. package/dist/plugins/user-auth.plugin.d.ts.map +1 -0
  204. package/dist/plugins/user-auth.plugin.js +62 -0
  205. package/dist/plugins/user-auth.plugin.js.map +1 -0
  206. package/dist/projects/members/project-member.repository.d.ts +49 -0
  207. package/dist/projects/members/project-member.repository.d.ts.map +1 -0
  208. package/dist/projects/members/project-member.repository.js +215 -0
  209. package/dist/projects/members/project-member.repository.js.map +1 -0
  210. package/dist/projects/members/project-member.routes.d.ts +4 -0
  211. package/dist/projects/members/project-member.routes.d.ts.map +1 -0
  212. package/dist/projects/members/project-member.routes.js +82 -0
  213. package/dist/projects/members/project-member.routes.js.map +1 -0
  214. package/dist/projects/members/project-member.schemas.d.ts +2735 -0
  215. package/dist/projects/members/project-member.schemas.d.ts.map +1 -0
  216. package/dist/projects/members/project-member.schemas.js +138 -0
  217. package/dist/projects/members/project-member.schemas.js.map +1 -0
  218. package/dist/projects/members/project-member.service.d.ts +29 -0
  219. package/dist/projects/members/project-member.service.d.ts.map +1 -0
  220. package/dist/projects/members/project-member.service.js +92 -0
  221. package/dist/projects/members/project-member.service.js.map +1 -0
  222. package/dist/projects/project-access.policy.d.ts +11 -0
  223. package/dist/projects/project-access.policy.d.ts.map +1 -0
  224. package/dist/projects/project-access.policy.js +30 -0
  225. package/dist/projects/project-access.policy.js.map +1 -0
  226. package/dist/projects/project-access.repository.d.ts +12 -0
  227. package/dist/projects/project-access.repository.d.ts.map +1 -0
  228. package/dist/projects/project-access.repository.js +45 -0
  229. package/dist/projects/project-access.repository.js.map +1 -0
  230. package/dist/projects/project-access.types.d.ts +9 -0
  231. package/dist/projects/project-access.types.d.ts.map +1 -0
  232. package/dist/projects/project-access.types.js +2 -0
  233. package/dist/projects/project-access.types.js.map +1 -0
  234. package/dist/projects/project-mutation.service.d.ts +15 -0
  235. package/dist/projects/project-mutation.service.d.ts.map +1 -0
  236. package/dist/projects/project-mutation.service.js +39 -0
  237. package/dist/projects/project-mutation.service.js.map +1 -0
  238. package/dist/projects/project.repository.d.ts +49 -0
  239. package/dist/projects/project.repository.d.ts.map +1 -0
  240. package/dist/projects/project.repository.js +155 -0
  241. package/dist/projects/project.repository.js.map +1 -0
  242. package/dist/projects/project.routes.d.ts +4 -0
  243. package/dist/projects/project.routes.d.ts.map +1 -0
  244. package/dist/projects/project.routes.js +45 -0
  245. package/dist/projects/project.routes.js.map +1 -0
  246. package/dist/projects/project.schemas.d.ts +2044 -0
  247. package/dist/projects/project.schemas.d.ts.map +1 -0
  248. package/dist/projects/project.schemas.js +90 -0
  249. package/dist/projects/project.schemas.js.map +1 -0
  250. package/dist/projects/project.service.d.ts +40 -0
  251. package/dist/projects/project.service.d.ts.map +1 -0
  252. package/dist/projects/project.service.js +128 -0
  253. package/dist/projects/project.service.js.map +1 -0
  254. package/dist/replies/reply-mutation.service.d.ts +19 -0
  255. package/dist/replies/reply-mutation.service.d.ts.map +1 -0
  256. package/dist/replies/reply-mutation.service.js +51 -0
  257. package/dist/replies/reply-mutation.service.js.map +1 -0
  258. package/dist/replies/reply.input.d.ts +4 -0
  259. package/dist/replies/reply.input.d.ts.map +1 -0
  260. package/dist/replies/reply.input.js +33 -0
  261. package/dist/replies/reply.input.js.map +1 -0
  262. package/dist/replies/reply.repository.d.ts +40 -0
  263. package/dist/replies/reply.repository.d.ts.map +1 -0
  264. package/dist/replies/reply.repository.js +173 -0
  265. package/dist/replies/reply.repository.js.map +1 -0
  266. package/dist/replies/reply.routes.d.ts +4 -0
  267. package/dist/replies/reply.routes.d.ts.map +1 -0
  268. package/dist/replies/reply.routes.js +64 -0
  269. package/dist/replies/reply.routes.js.map +1 -0
  270. package/dist/replies/reply.schemas.d.ts +251 -0
  271. package/dist/replies/reply.schemas.d.ts.map +1 -0
  272. package/dist/replies/reply.schemas.js +51 -0
  273. package/dist/replies/reply.schemas.js.map +1 -0
  274. package/dist/replies/reply.service.d.ts +19 -0
  275. package/dist/replies/reply.service.d.ts.map +1 -0
  276. package/dist/replies/reply.service.js +50 -0
  277. package/dist/replies/reply.service.js.map +1 -0
  278. package/dist/replies/reply.types.d.ts +31 -0
  279. package/dist/replies/reply.types.d.ts.map +1 -0
  280. package/dist/replies/reply.types.js +2 -0
  281. package/dist/replies/reply.types.js.map +1 -0
  282. package/dist/report-cases/report-case-workflow.service.d.ts +39 -0
  283. package/dist/report-cases/report-case-workflow.service.d.ts.map +1 -0
  284. package/dist/report-cases/report-case-workflow.service.js +103 -0
  285. package/dist/report-cases/report-case-workflow.service.js.map +1 -0
  286. package/dist/report-cases/report-case.input.d.ts +4 -0
  287. package/dist/report-cases/report-case.input.d.ts.map +1 -0
  288. package/dist/report-cases/report-case.input.js +29 -0
  289. package/dist/report-cases/report-case.input.js.map +1 -0
  290. package/dist/report-cases/report-case.repository.d.ts +45 -0
  291. package/dist/report-cases/report-case.repository.d.ts.map +1 -0
  292. package/dist/report-cases/report-case.repository.js +185 -0
  293. package/dist/report-cases/report-case.repository.js.map +1 -0
  294. package/dist/report-cases/report-case.routes.d.ts +4 -0
  295. package/dist/report-cases/report-case.routes.d.ts.map +1 -0
  296. package/dist/report-cases/report-case.routes.js +94 -0
  297. package/dist/report-cases/report-case.routes.js.map +1 -0
  298. package/dist/report-cases/report-case.schemas.d.ts +294 -0
  299. package/dist/report-cases/report-case.schemas.d.ts.map +1 -0
  300. package/dist/report-cases/report-case.schemas.js +77 -0
  301. package/dist/report-cases/report-case.schemas.js.map +1 -0
  302. package/dist/report-cases/report-case.service.d.ts +21 -0
  303. package/dist/report-cases/report-case.service.d.ts.map +1 -0
  304. package/dist/report-cases/report-case.service.js +60 -0
  305. package/dist/report-cases/report-case.service.js.map +1 -0
  306. package/dist/report-cases/report-case.types.d.ts +39 -0
  307. package/dist/report-cases/report-case.types.d.ts.map +1 -0
  308. package/dist/report-cases/report-case.types.js +2 -0
  309. package/dist/report-cases/report-case.types.js.map +1 -0
  310. package/dist/routes/health.route.d.ts +4 -0
  311. package/dist/routes/health.route.d.ts.map +1 -0
  312. package/dist/routes/health.route.js +39 -0
  313. package/dist/routes/health.route.js.map +1 -0
  314. package/dist/routes/version.route.d.ts +3 -0
  315. package/dist/routes/version.route.d.ts.map +1 -0
  316. package/dist/routes/version.route.js +22 -0
  317. package/dist/routes/version.route.js.map +1 -0
  318. package/dist/runtime/index.d.ts +8 -0
  319. package/dist/runtime/index.d.ts.map +1 -0
  320. package/dist/runtime/index.js +11 -0
  321. package/dist/runtime/index.js.map +1 -0
  322. package/dist/server.d.ts +11 -0
  323. package/dist/server.d.ts.map +1 -0
  324. package/dist/server.js +66 -0
  325. package/dist/server.js.map +1 -0
  326. package/dist/storage/values.d.ts +3 -0
  327. package/dist/storage/values.d.ts.map +1 -0
  328. package/dist/storage/values.js +30 -0
  329. package/dist/storage/values.js.map +1 -0
  330. package/dist/users/current-user.routes.d.ts +3 -0
  331. package/dist/users/current-user.routes.d.ts.map +1 -0
  332. package/dist/users/current-user.routes.js +9 -0
  333. package/dist/users/current-user.routes.js.map +1 -0
  334. package/dist/users/current-user.schemas.d.ts +641 -0
  335. package/dist/users/current-user.schemas.d.ts.map +1 -0
  336. package/dist/users/current-user.schemas.js +21 -0
  337. package/dist/users/current-user.schemas.js.map +1 -0
  338. package/dist/users/user-search.repository.d.ts +19 -0
  339. package/dist/users/user-search.repository.d.ts.map +1 -0
  340. package/dist/users/user-search.repository.js +55 -0
  341. package/dist/users/user-search.repository.js.map +1 -0
  342. package/dist/users/user-search.routes.d.ts +4 -0
  343. package/dist/users/user-search.routes.d.ts.map +1 -0
  344. package/dist/users/user-search.routes.js +19 -0
  345. package/dist/users/user-search.routes.js.map +1 -0
  346. package/dist/users/user-search.schemas.d.ts +693 -0
  347. package/dist/users/user-search.schemas.d.ts.map +1 -0
  348. package/dist/users/user-search.schemas.js +41 -0
  349. package/dist/users/user-search.schemas.js.map +1 -0
  350. package/dist/users/user-search.service.d.ts +20 -0
  351. package/dist/users/user-search.service.d.ts.map +1 -0
  352. package/dist/users/user-search.service.js +41 -0
  353. package/dist/users/user-search.service.js.map +1 -0
  354. package/dist/users/user.schemas.d.ts +47 -0
  355. package/dist/users/user.schemas.d.ts.map +1 -0
  356. package/dist/users/user.schemas.js +13 -0
  357. package/dist/users/user.schemas.js.map +1 -0
  358. package/dist/users/user.types.d.ts +6 -0
  359. package/dist/users/user.types.d.ts.map +1 -0
  360. package/dist/users/user.types.js +2 -0
  361. package/dist/users/user.types.js.map +1 -0
  362. package/dist/version.d.ts +3 -0
  363. package/dist/version.d.ts.map +1 -0
  364. package/dist/version.js +5 -0
  365. package/dist/version.js.map +1 -0
  366. package/docs/guides/community-api-quickstart.md +171 -0
  367. package/docs/guides/feature-structure.md +138 -0
  368. package/docs/guides/independent-cli.md +73 -0
  369. package/docs/guides/non-attachment-api-contract.md +138 -0
  370. package/docs/plans/selfdev-schema-alignment.md +61 -0
  371. package/package.json +76 -0
  372. package/sql/fivepixels-lite-init.sql +518 -0
@@ -0,0 +1,41 @@
1
+ import { communityErrorResponses, communityResponseSchema, nullableStringSchema, uuidSchema, } from '../community/schemas.js';
2
+ const nullableBooleanSchema = {
3
+ anyOf: [{ type: 'boolean' }, { type: 'null' }],
4
+ };
5
+ export const userSearchItemSchema = {
6
+ type: 'object',
7
+ additionalProperties: false,
8
+ required: ['id', 'userName', 'profileImageUrl', 'isProjectMember'],
9
+ properties: {
10
+ id: uuidSchema,
11
+ userName: { type: 'string', minLength: 1, maxLength: 100 },
12
+ profileImageUrl: nullableStringSchema,
13
+ isProjectMember: nullableBooleanSchema,
14
+ },
15
+ };
16
+ export const searchUsersSchema = {
17
+ tags: ['users'],
18
+ summary: '활성 사용자 검색',
19
+ security: [{ userJwt: [] }],
20
+ querystring: {
21
+ type: 'object',
22
+ additionalProperties: false,
23
+ properties: {
24
+ query: {
25
+ type: 'string',
26
+ description: '사용자 이름 검색어. 생략하거나 공백이면 활성 사용자 전체를 페이지 단위로 조회합니다.',
27
+ },
28
+ projectId: uuidSchema,
29
+ page: { type: 'integer', default: 1 },
30
+ pageSize: { type: 'integer', default: 20 },
31
+ },
32
+ },
33
+ response: {
34
+ 200: communityResponseSchema({
35
+ type: 'array',
36
+ items: userSearchItemSchema,
37
+ }),
38
+ ...communityErrorResponses,
39
+ },
40
+ };
41
+ //# sourceMappingURL=user-search.schemas.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user-search.schemas.js","sourceRoot":"","sources":["../../src/users/user-search.schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,uBAAuB,EACvB,uBAAuB,EACvB,oBAAoB,EACpB,UAAU,GACX,MAAM,yBAAyB,CAAC;AAEjC,MAAM,qBAAqB,GAAG;IAC5B,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;CACtC,CAAC;AAEX,MAAM,CAAC,MAAM,oBAAoB,GAAG;IAClC,IAAI,EAAE,QAAQ;IACd,oBAAoB,EAAE,KAAK;IAC3B,QAAQ,EAAE,CAAC,IAAI,EAAE,UAAU,EAAE,iBAAiB,EAAE,iBAAiB,CAAC;IAClE,UAAU,EAAE;QACV,EAAE,EAAE,UAAU;QACd,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,EAAE,SAAS,EAAE,GAAG,EAAE;QAC1D,eAAe,EAAE,oBAAoB;QACrC,eAAe,EAAE,qBAAqB;KACvC;CACO,CAAC;AAEX,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAC/B,IAAI,EAAE,CAAC,OAAO,CAAC;IACf,OAAO,EAAE,WAAW;IACpB,QAAQ,EAAE,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;IAC3B,WAAW,EAAE;QACX,IAAI,EAAE,QAAQ;QACd,oBAAoB,EAAE,KAAK;QAC3B,UAAU,EAAE;YACV,KAAK,EAAE;gBACL,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,kDAAkD;aAChE;YACD,SAAS,EAAE,UAAU;YACrB,IAAI,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE;YACrC,QAAQ,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,EAAE,EAAE;SAC3C;KACF;IACD,QAAQ,EAAE;QACR,GAAG,EAAE,uBAAuB,CAAC;YAC3B,IAAI,EAAE,OAAO;YACb,KAAK,EAAE,oBAAoB;SAC5B,CAAC;QACF,GAAG,uBAAuB;KAC3B;CACO,CAAC"}
@@ -0,0 +1,20 @@
1
+ import { type PaginationMetadata } from '../community/contracts.js';
2
+ import type { ProjectAccessRepository } from '../projects/project-access.repository.js';
3
+ import type { UserSearchItemRecord, UserSearchRepository } from './user-search.repository.js';
4
+ export interface UserSearchQuery {
5
+ page?: number;
6
+ pageSize?: number;
7
+ projectId?: string;
8
+ query?: string;
9
+ }
10
+ export interface UserSearchResult {
11
+ items: UserSearchItemRecord[];
12
+ pagination: PaginationMetadata;
13
+ }
14
+ export declare class UserSearchService {
15
+ private readonly repository;
16
+ private readonly accessRepository;
17
+ constructor(repository: UserSearchRepository, accessRepository: ProjectAccessRepository);
18
+ search(actorId: string, request: UserSearchQuery): UserSearchResult;
19
+ }
20
+ //# sourceMappingURL=user-search.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user-search.service.d.ts","sourceRoot":"","sources":["../../src/users/user-search.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkC,KAAK,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAGpG,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,0CAA0C,CAAC;AACxF,OAAO,KAAK,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAE9F,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,oBAAoB,EAAE,CAAC;IAC9B,UAAU,EAAE,kBAAkB,CAAC;CAChC;AAED,qBAAa,iBAAiB;IAE1B,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,gBAAgB;gBADhB,UAAU,EAAE,oBAAoB,EAChC,gBAAgB,EAAE,uBAAuB;IAGrD,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,eAAe,GAAG,gBAAgB;CAkC3E"}
@@ -0,0 +1,41 @@
1
+ import { pageMetadata, parsePageRequest } from '../community/contracts.js';
2
+ import { CommunityApiError } from '../community/errors.js';
3
+ import { requireUuid } from '../community/input.js';
4
+ export class UserSearchService {
5
+ repository;
6
+ accessRepository;
7
+ constructor(repository, accessRepository) {
8
+ this.repository = repository;
9
+ this.accessRepository = accessRepository;
10
+ }
11
+ search(actorId, request) {
12
+ const query = request.query?.trim() ?? '';
13
+ if (query.includes('\0')) {
14
+ throw new CommunityApiError(400, 'invalid-user-query', 'query에 NUL 문자를 포함할 수 없습니다.');
15
+ }
16
+ const { page, pageSize } = parsePageRequest(request.page, request.pageSize);
17
+ const offset = (page - 1) * pageSize;
18
+ if (!Number.isSafeInteger(offset)) {
19
+ throw new CommunityApiError(400, 'invalid-page', 'page는 1 이상이어야 합니다.');
20
+ }
21
+ const projectId = request.projectId === undefined
22
+ ? undefined
23
+ : requireUuid(request.projectId, 'projectId');
24
+ if (projectId !== undefined) {
25
+ this.accessRepository.requireAccess(projectId, actorId, false);
26
+ }
27
+ const result = this.repository.search(`%${escapeLike(query)}%`, pageSize, offset, projectId);
28
+ return {
29
+ items: result.items,
30
+ pagination: pageMetadata(page, pageSize, result.totalCount),
31
+ };
32
+ }
33
+ }
34
+ function escapeLike(value) {
35
+ return value
36
+ .replaceAll('~', '~~')
37
+ .replaceAll('%', '~%')
38
+ .replaceAll('_', '~_')
39
+ .replaceAll('[', '~[');
40
+ }
41
+ //# sourceMappingURL=user-search.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user-search.service.js","sourceRoot":"","sources":["../../src/users/user-search.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAA2B,MAAM,2BAA2B,CAAC;AACpG,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAgBpD,MAAM,OAAO,iBAAiB;IAET;IACA;IAFnB,YACmB,UAAgC,EAChC,gBAAyC;QADzC,eAAU,GAAV,UAAU,CAAsB;QAChC,qBAAgB,GAAhB,gBAAgB,CAAyB;IACzD,CAAC;IAEG,MAAM,CAAC,OAAe,EAAE,OAAwB;QACrD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;QAC1C,IAAI,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;YACzB,MAAM,IAAI,iBAAiB,CACzB,GAAG,EACH,oBAAoB,EACpB,4BAA4B,CAC7B,CAAC;QACJ,CAAC;QAED,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,gBAAgB,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;QAC5E,MAAM,MAAM,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,QAAQ,CAAC;QACrC,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,EAAE,CAAC;YAClC,MAAM,IAAI,iBAAiB,CAAC,GAAG,EAAE,cAAc,EAAE,oBAAoB,CAAC,CAAC;QACzE,CAAC;QAED,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,KAAK,SAAS;YAC/C,CAAC,CAAC,SAAS;YACX,CAAC,CAAC,WAAW,CAAC,OAAO,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;QAChD,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;YAC5B,IAAI,CAAC,gBAAgB,CAAC,aAAa,CAAC,SAAS,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;QACjE,CAAC;QAED,MAAM,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM,CACnC,IAAI,UAAU,CAAC,KAAK,CAAC,GAAG,EACxB,QAAQ,EACR,MAAM,EACN,SAAS,CACV,CAAC;QACF,OAAO;YACL,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,UAAU,EAAE,YAAY,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,UAAU,CAAC;SAC5D,CAAC;IACJ,CAAC;CACF;AAED,SAAS,UAAU,CAAC,KAAa;IAC/B,OAAO,KAAK;SACT,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC;SACrB,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC;SACrB,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC;SACrB,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;AAC3B,CAAC"}
@@ -0,0 +1,47 @@
1
+ export declare const userSchema: {
2
+ readonly type: "object";
3
+ readonly additionalProperties: false;
4
+ readonly required: readonly ["id", "userName", "profileImageUrl"];
5
+ readonly properties: {
6
+ readonly id: {
7
+ readonly type: "string";
8
+ readonly format: "uuid";
9
+ };
10
+ readonly userName: {
11
+ readonly type: "string";
12
+ };
13
+ readonly profileImageUrl: {
14
+ readonly anyOf: readonly [{
15
+ readonly type: "string";
16
+ }, {
17
+ readonly type: "null";
18
+ }];
19
+ };
20
+ };
21
+ };
22
+ export declare const nullableUserSchema: {
23
+ readonly anyOf: readonly [{
24
+ readonly type: "object";
25
+ readonly additionalProperties: false;
26
+ readonly required: readonly ["id", "userName", "profileImageUrl"];
27
+ readonly properties: {
28
+ readonly id: {
29
+ readonly type: "string";
30
+ readonly format: "uuid";
31
+ };
32
+ readonly userName: {
33
+ readonly type: "string";
34
+ };
35
+ readonly profileImageUrl: {
36
+ readonly anyOf: readonly [{
37
+ readonly type: "string";
38
+ }, {
39
+ readonly type: "null";
40
+ }];
41
+ };
42
+ };
43
+ }, {
44
+ readonly type: "null";
45
+ }];
46
+ };
47
+ //# sourceMappingURL=user.schemas.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user.schemas.d.ts","sourceRoot":"","sources":["../../src/users/user.schemas.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;CASb,CAAC;AAEX,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;CAAqD,CAAC"}
@@ -0,0 +1,13 @@
1
+ import { nullableStringSchema, uuidSchema } from '../community/schemas.js';
2
+ export const userSchema = {
3
+ type: 'object',
4
+ additionalProperties: false,
5
+ required: ['id', 'userName', 'profileImageUrl'],
6
+ properties: {
7
+ id: uuidSchema,
8
+ userName: { type: 'string' },
9
+ profileImageUrl: nullableStringSchema,
10
+ },
11
+ };
12
+ export const nullableUserSchema = { anyOf: [userSchema, { type: 'null' }] };
13
+ //# sourceMappingURL=user.schemas.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user.schemas.js","sourceRoot":"","sources":["../../src/users/user.schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAE3E,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,IAAI,EAAE,QAAQ;IACd,oBAAoB,EAAE,KAAK;IAC3B,QAAQ,EAAE,CAAC,IAAI,EAAE,UAAU,EAAE,iBAAiB,CAAC;IAC/C,UAAU,EAAE;QACV,EAAE,EAAE,UAAU;QACd,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;QAC5B,eAAe,EAAE,oBAAoB;KACtC;CACO,CAAC;AAEX,MAAM,CAAC,MAAM,kBAAkB,GAAG,EAAE,KAAK,EAAE,CAAC,UAAU,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAW,CAAC"}
@@ -0,0 +1,6 @@
1
+ export interface CommunityUserResponse {
2
+ id: string;
3
+ profileImageUrl: string | null;
4
+ userName: string;
5
+ }
6
+ //# sourceMappingURL=user.types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user.types.d.ts","sourceRoot":"","sources":["../../src/users/user.types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,qBAAqB;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,QAAQ,EAAE,MAAM,CAAC;CAClB"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=user.types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user.types.js","sourceRoot":"","sources":["../../src/users/user.types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,3 @@
1
+ export declare const PACKAGE_NAME: string;
2
+ export declare const PACKAGE_VERSION: string;
3
+ //# sourceMappingURL=version.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAWA,eAAO,MAAM,YAAY,QAAgB,CAAC;AAC1C,eAAO,MAAM,eAAe,QAAmB,CAAC"}
@@ -0,0 +1,5 @@
1
+ import { readFileSync } from 'node:fs';
2
+ const metadata = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'));
3
+ export const PACKAGE_NAME = metadata.name;
4
+ export const PACKAGE_VERSION = metadata.version;
5
+ //# sourceMappingURL=version.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAOvC,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CACzB,YAAY,CAAC,IAAI,GAAG,CAAC,iBAAiB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,CAC/C,CAAC;AAErB,MAAM,CAAC,MAAM,YAAY,GAAG,QAAQ,CAAC,IAAI,CAAC;AAC1C,MAAM,CAAC,MAAM,eAAe,GAAG,QAAQ,CAAC,OAAO,CAAC"}
@@ -0,0 +1,171 @@
1
+ # Community API 연동 빠른 시작
2
+
3
+ Community API의 업무 경로는 `/api/v1`입니다. 기존 .NET API의
4
+ `/api/v1/fivepixels` 접두사는 사용하지 않습니다.
5
+
6
+ ## 1. 사용자 등록
7
+
8
+ localhost에서는 회원가입이 기본 활성화됩니다. 외부 바인딩과 Docker에서는 최초
9
+ 사용자 등록 시에만 `REGISTRATION_ENABLED=true`를 명시하십시오.
10
+
11
+ ```bash
12
+ curl --request POST http://127.0.0.1:3000/api/v1/auth/register \
13
+ --header 'Content-Type: application/json' \
14
+ --data '{
15
+ "loginId": "frontend.dev",
16
+ "email": "frontend@example.com",
17
+ "password": "change-this-password",
18
+ "userName": "Frontend Developer"
19
+ }'
20
+ ```
21
+
22
+ 이 명령은 로컬 계정을 만들며 토큰은 발급하지 않습니다. `curl --request`는 사용할
23
+ HTTP Method를, `--header`는 JSON 본문임을, `--data`는 요청 본문을 지정합니다.
24
+
25
+ ## 2. 로그인과 프로젝트 생성
26
+
27
+ ```ts
28
+ const baseUrl = 'http://127.0.0.1:3000/api/v1';
29
+
30
+ const loginResponse = await fetch(`${baseUrl}/auth/login`, {
31
+ method: 'POST',
32
+ headers: { 'Content-Type': 'application/json' },
33
+ body: JSON.stringify({
34
+ loginId: 'frontend.dev',
35
+ password: 'change-this-password',
36
+ }),
37
+ });
38
+
39
+ const login = await loginResponse.json();
40
+ if (!loginResponse.ok || login.resultCode !== 'SUCCESS') {
41
+ throw new Error(login.resultMessage);
42
+ }
43
+
44
+ const accessToken = login.result.accessToken;
45
+ const refreshToken = login.result.refreshToken;
46
+
47
+ const projectResponse = await fetch(`${baseUrl}/projects`, {
48
+ method: 'POST',
49
+ headers: {
50
+ Authorization: `Bearer ${accessToken}`,
51
+ 'Content-Type': 'application/json',
52
+ },
53
+ body: JSON.stringify({ projectName: 'My Website' }),
54
+ });
55
+
56
+ const project = await projectResponse.json();
57
+ ```
58
+
59
+ Access Token은 React 상태나 별도 메모리 저장소에 두는 방식을 우선하십시오. XSS가
60
+ 발생하면 브라우저의 `localStorage`에 보관한 Access/Refresh Token이 탈취될 수
61
+ 있으므로 예제에서는 자동 영구 저장을 하지 않습니다.
62
+
63
+ ## 3. 사용자 검색과 프로젝트 멤버 관리
64
+
65
+ 프로젝트에 추가할 사용자는 먼저 Fivepixels에 가입된 활성 계정이어야 합니다. 사용자 검색은
66
+ selfdev와 동일하게 검색어 생략·공백·한 글자를 허용합니다. 생략하거나 공백이면 활성 사용자 전체를 페이지 단위로 조회하며 로그인 ID와 이메일은 반환하지 않습니다. 페이지 크기 제한과 분당 30회 요청 제한은 유지합니다.
67
+
68
+ ```ts
69
+ const projectId = project.result.id;
70
+ const usersResponse = await fetch(
71
+ `${baseUrl}/users?query=${encodeURIComponent('Designer')}&projectId=${projectId}`,
72
+ { headers: { Authorization: `Bearer ${accessToken}` } },
73
+ );
74
+ const users = await usersResponse.json();
75
+ const targetUserId = users.result[0].id;
76
+
77
+ const addMemberResponse = await fetch(`${baseUrl}/projects/${projectId}/members`, {
78
+ method: 'POST',
79
+ headers: {
80
+ Authorization: `Bearer ${accessToken}`,
81
+ 'Content-Type': 'application/json',
82
+ },
83
+ body: JSON.stringify({ userId: targetUserId, role: 'MEMBER' }),
84
+ });
85
+
86
+ if (!addMemberResponse.ok) {
87
+ const error = await addMemberResponse.json();
88
+ throw new Error(error.resultMessage);
89
+ }
90
+
91
+ const memberEtag = addMemberResponse.headers.get('ETag');
92
+ if (memberEtag === null) {
93
+ throw new Error('멤버 응답에 ETag가 없습니다.');
94
+ }
95
+ ```
96
+
97
+ 멤버 추가·역할 변경·제거는 OWNER만 실행할 수 있습니다. 역할 변경과 제거에는 가장 최근
98
+ 멤버 응답의 `ETag`를 `If-Match`로 전달해야 합니다. 멤버 목록을 다시 조회한 경우에는 대상
99
+ 항목의 `version`을 `"${member.version}"` 형태로 감싸 `If-Match` 값으로 사용합니다. OWNER 권한을 이전할 때는 새 OWNER를
100
+ 먼저 추가하거나 승격한 다음 기존 OWNER를 강등 또는 제거하십시오. 마지막 OWNER는 강등하거나
101
+ 제거할 수 없으며, Feedback 또는 Report Case 담당자는 먼저 담당 항목을 이관해야 합니다.
102
+
103
+ ```ts
104
+ const promoteResponse = await fetch(
105
+ `${baseUrl}/projects/${projectId}/members/${targetUserId}`,
106
+ {
107
+ method: 'PATCH',
108
+ headers: {
109
+ Authorization: `Bearer ${accessToken}`,
110
+ 'Content-Type': 'application/json',
111
+ 'If-Match': memberEtag,
112
+ },
113
+ body: JSON.stringify({ role: 'OWNER' }),
114
+ },
115
+ );
116
+
117
+ if (promoteResponse.status === 412) {
118
+ throw new Error('멤버 목록을 다시 조회한 뒤 최신 ETag로 재시도해야 합니다.');
119
+ }
120
+ ```
121
+
122
+ 일반 MEMBER가 직접 탈퇴하는 전용 API와 이메일 초대 기능은 이 버전에 포함되지 않습니다.
123
+
124
+ ## 4. 토큰 갱신과 로그아웃
125
+
126
+ ```ts
127
+ const refreshResponse = await fetch(`${baseUrl}/auth/refresh`, {
128
+ method: 'POST',
129
+ headers: { 'Content-Type': 'application/json' },
130
+ body: JSON.stringify({ refreshToken }),
131
+ });
132
+
133
+ const refreshed = await refreshResponse.json();
134
+ // 갱신 성공 시 이전 Refresh Token을 버리고 새 값을 보관합니다.
135
+ const nextAccessToken = refreshed.result.accessToken;
136
+ const nextRefreshToken = refreshed.result.refreshToken;
137
+
138
+ await fetch(`${baseUrl}/auth/logout`, {
139
+ method: 'POST',
140
+ headers: { 'Content-Type': 'application/json' },
141
+ body: JSON.stringify({ refreshToken: nextRefreshToken }),
142
+ });
143
+ ```
144
+
145
+ Refresh Token은 일회성으로 회전합니다. 같은 토큰을 동시에 여러 번 갱신에 사용하지
146
+ 말고, 성공 응답을 받은 즉시 기존 값을 폐기하십시오.
147
+
148
+ ## 5. 오류 처리
149
+
150
+ 업무 API의 오류도 성공 응답과 같은 봉투를 사용합니다.
151
+
152
+ ```json
153
+ {
154
+ "resultCode": "UNAUTHORIZED",
155
+ "resultMessage": "인증이 필요합니다.",
156
+ "result": null,
157
+ "pagination": {
158
+ "page": 0,
159
+ "pageSize": 0,
160
+ "totalCount": 0,
161
+ "totalPages": 0,
162
+ "hasNext": false,
163
+ "hasPrevious": false
164
+ }
165
+ }
166
+ ```
167
+
168
+ 클라이언트는 HTTP 상태코드를 먼저 확인하고 `resultCode`로 세부 원인을 분기합니다.
169
+ 401에서는 로그인 또는 갱신을 시도하고, 403은 권한이나 가입 설정을 확인하며, 409는
170
+ 현재 리소스 상태를 다시 조회하십시오. 503에는 짧은 지수 백오프를 적용할 수 있지만
171
+ 쓰기 요청을 무제한 자동 재시도해서는 안 됩니다.
@@ -0,0 +1,138 @@
1
+ # 기능별 코드 구조
2
+
3
+ ## 목적과 유지하는 계약
4
+
5
+ Lite API를 기능 단위로 탐색하고 수정할 수 있도록 구성합니다. selfdev 기준으로 정렬한 저장 규칙은 그대로 두고, 이번 변경은 내부 파일 배치와 업무 처리 책임만 정리합니다.
6
+
7
+ - 업무 API 34개와 시스템 API 2개의 URL, HTTP Method, 요청·응답 DTO, 성공·실패 상태코드, 인증, ETag, Location 계약을 유지합니다.
8
+ - Swagger의 업무 분류만 실제 기능에 맞춥니다. 범용 `lifecycle` 태그와 모듈은 제거합니다.
9
+ - DB 테이블·컬럼·인덱스·제약조건, migration 001~007, `sql/fivepixels-lite-init.sql`은 변경하지 않습니다.
10
+ - 이미지 첨부는 계속 제외합니다. 페이지 요소 태그/멘션은 이미지 첨부와 별개인 기존 기능입니다.
11
+ - SDK 소스, selfdev 소스, npm 패키지 버전, 배포 설정은 이 리팩터링의 변경 대상이 아닙니다.
12
+
13
+ 엔드포인트 목록은 [README](../../README.md#api와-swagger), 요청·응답 예시는 [빠른 시작](community-api-quickstart.md), 권한·ETag·상태 전이는 [비첨부 API 계약](non-attachment-api-contract.md)을 참고하십시오.
14
+
15
+ ## 디렉터리와 기능 소유권
16
+
17
+ ```text
18
+ src/
19
+ auth/ 회원가입·로그인·토큰 갱신/폐기·비밀번호
20
+ users/ 현재 사용자 조회·프로젝트 범위 사용자 검색
21
+ projects/ 프로젝트 생성·조회·이름/상태 변경·접근 권한
22
+ members/ 프로젝트 멤버 조회·추가·역할 변경·제거
23
+ feedbacks/ 피드백 생성·조회·마커·수정·상태/담당자·overview
24
+ report-cases/ Case 생성·조회·수정·상태/담당자·프로젝트 목록·timeline
25
+ replies/ 답글 생성·조회·수정·삭제·시스템 답글 저장
26
+ page-elements/ Case/답글의 HTML 요소 태그·멘션 검증과 저장
27
+ community/ 공통 응답·오류·ETag·입력 검증·변경 라우트 등록
28
+ plugins/ 인증·CORS·Swagger·업무 API 조립
29
+ db/ SQLite 연결·트랜잭션·마이그레이션·백업
30
+ migrations/ 변경하지 않은 001~007 SQL
31
+ storage/ selfdev 호환 날짜·경로 해시 등 저장 값 변환
32
+ routes/ 공개 시스템 API /health, /version만 유지
33
+ errors/ 시스템 API 오류 처리
34
+ config/ 설정·데이터 경로·JWT 키
35
+ cli/ CLI 명령
36
+ app.ts 애플리케이션 조립
37
+ server.ts 프로세스 시작·수신·종료
38
+ ```
39
+
40
+ 기능에 필요한 파일만 둡니다. 모든 폴더에 의미 없는 Service나 Repository를 만들지는 않습니다. 예를 들어 `users/me`는 인증된 사용자 정보를 반환하므로 별도 조회 Service가 없고, `page-elements`에는 독립 HTTP API가 없습니다.
41
+
42
+ | Swagger 태그 | 소유 폴더 | HTTP 작업 수 |
43
+ |---|---|---:|
44
+ | `auth` | `auth` | 4 |
45
+ | `users` | `users` | 2 |
46
+ | `projects` | `projects` | 5 |
47
+ | `project-members` | `projects/members` | 4 |
48
+ | `feedbacks` | `feedbacks` | 7 |
49
+ | `report-cases` | `report-cases` | 8 |
50
+ | `replies` | `replies` | 4 |
51
+ | `system` | `routes` | 2 |
52
+
53
+ 각 기능의 라우트는 `plugins/community-api.plugin.ts`에서 `/api/v1` 아래 등록합니다. 사용자 JWT 인증은 기존 `plugins/user-auth.plugin.ts`가 담당합니다.
54
+
55
+ ## 파일별 책임
56
+
57
+ | 파일 | 책임 |
58
+ |---|---|
59
+ | `*.routes.ts` | 경로·Method·인증·Schema 연결, 객체 조립, HTTP 응답·로그 처리 |
60
+ | `*.schemas.ts` | Fastify 요청·응답 검증 및 Swagger 계약 |
61
+ | `*.types.ts` | 해당 기능이 소유하는 내부 타입과 DTO |
62
+ | `*.input.ts` | 기능별 입력 정규화·검증 |
63
+ | `*.service.ts` | 생성·조회 등의 업무 규칙, Repository 호출 |
64
+ | `*-mutation.service.ts` | 해당 기능의 수정·삭제 규칙 및 트랜잭션 조정 |
65
+ | `*-workflow.service.ts` | 상태 전이·담당자·통합 조회 등 여러 저장소를 조정하는 처리 |
66
+ | `*.repository.ts` | 바인딩 SQL, 행 변환, 저장·조회 |
67
+
68
+ Route에는 SQL이나 권한 판단을 넣지 않습니다. Service에는 SQLite 객체나 SQL을 노출하지 않습니다. 기능 간 호출이 필요하면 소유 기능의 Repository를 사용하고, 관련 없는 SQL을 공통 저장소로 다시 모으지 않습니다.
69
+
70
+ 작은 범용 처리만 공통화합니다.
71
+
72
+ - `projects/project-access.policy.ts`: ID 정규화, 프로젝트 접근/활성 상태, OWNER, 담당자 멤버십 검사
73
+ - `community/etag.ts`: If-Match 파싱, 버전 비교, 응답 ETag 설정
74
+ - `community/mutation-route.ts`: 수정·삭제의 인증/검증 순서, 응답 봉투, ETag, 204 응답, 공통 변경 로그
75
+ - `db/transaction.ts`: 동기 쓰기/읽기 트랜잭션 실행
76
+
77
+ 기능별 권한 차이와 동일 값 재요청 시 버전 증가 여부는 각 Service의 기존 규칙을 유지합니다. 모든 변경을 같은 정책으로 단순화하지 않습니다.
78
+
79
+ ## 복합 처리와 트랜잭션
80
+
81
+ 기존 `LifecycleService`의 업무 처리를 다음 소유자로 분리했습니다.
82
+
83
+ | 처리 | 소유 Service |
84
+ |---|---|
85
+ | 프로젝트 이름·상태 변경 | `ProjectMutationService` |
86
+ | 피드백 수정·상태·담당자, overview | `FeedbackWorkflowService` |
87
+ | Case 수정·상태·담당자, 프로젝트 Case 목록·timeline | `ReportCaseWorkflowService` |
88
+ | 답글 수정·삭제 | `ReplyMutationService` |
89
+
90
+ `TransactionRunner.write()`는 기존과 같이 `BEGIN IMMEDIATE`를 사용합니다. 권한 확인, 현재 버전 조회, 데이터 변경, 응답 데이터 조회까지 하나의 동기 트랜잭션에 포함합니다.
91
+
92
+ 특히 Case 상태 변경에서는 다음 작업이 모두 성공하거나 모두 롤백되어야 합니다.
93
+
94
+ 1. Case 상태·버전·해결 시각 변경
95
+ 2. 조건에 맞는 시스템 답글 생성
96
+ 3. 피드백 상태·버전 갱신
97
+ 4. 변경 결과 조회
98
+
99
+ 담당자 변경과 시스템 답글, 본문 변경과 페이지 요소 교체도 같은 트랜잭션을 유지합니다. `overview`·`timeline`의 여러 조회는 `read()`의 동일 스냅샷에서 수행합니다. 기존 생성 처리의 Repository 내부 트랜잭션도 유지합니다.
100
+
101
+ **TransactionRunner와 참여 Repository에는 반드시 같은 SQLite 연결을 전달해야 합니다.** 트랜잭션 콜백은 동기 함수로 유지하고 `await`, HTTP 호출, 파일 I/O를 넣지 않습니다. 별도 연결이나 비동기 작업은 원자성을 깨뜨릴 수 있습니다.
102
+
103
+ ## 예외·성능·로그
104
+
105
+ - 인증 실패, 접근할 수 없는 리소스의 404, 권한 부족 403, 비활성 프로젝트 등 업무 충돌 409, 버전 불일치 412, If-Match 누락 428의 기존 처리 순서를 유지합니다.
106
+ - 입력 오류와 이미지 첨부 제외 정책의 상태코드/오류 코드는 기존 계약을 따릅니다.
107
+ - 답글 생성이나 마지막 피드백 UPDATE가 실패해도 Case·답글·피드백 중 일부만 저장되지 않아야 합니다.
108
+ - SQL, 조회 순서, 페이지 제한, 인덱스를 이 구조 변경으로 추가하거나 변경하지 않습니다. 새로운 DB 연결이나 worker도 만들지 않습니다. 성능 향상을 주장하는 변경이 아니며 실제 부하 성능은 별도 측정 대상입니다.
109
+ - 기존 Fastify 요청 로그와 기능별 생성/변경 로그를 유지합니다. 변경 로그는 actorId, 대상 ID, Method, 결과 version으로 추적합니다. 토큰, 비밀번호, 본문, SQL 내부 오류를 응답이나 로그에 추가하지 않습니다.
110
+
111
+ ## 새 기능 또는 변경을 넣는 순서
112
+
113
+ 1. 승인된 API 계약과 기능 소유자를 정합니다. 관련 기능 폴더에 Schema/DTO를 먼저 배치합니다.
114
+ 2. Repository에 SQL을 두고, Service에 권한·업무 규칙을 둡니다. 여러 테이블 변경은 소유 기능의 Workflow/Mutation Service에서 조정합니다.
115
+ 3. 해당 기능의 `*.routes.ts`에 등록하고 Swagger 태그를 맞춥니다. 신규 기능인 경우에만 업무 API plugin에 등록 함수를 추가합니다.
116
+ 4. 성공뿐 아니라 인증, 권한, 잘못된 입력, 리소스 누락, 오래된 ETag, 동일 값 재요청, 마지막 단계 실패 시 롤백을 검증합니다.
117
+ 5. 순환 런타임 import를 만들지 않습니다. 타입만 필요하면 `import type`을 사용하고, 공통 Schema가 필요하면 실제 소유 기능 또는 공통 모듈에서 가져옵니다.
118
+
119
+ ## 검증과 배포 주의
120
+
121
+ ```bash
122
+ npm run typecheck
123
+ npm run lint
124
+ npm test
125
+ npm run build
126
+ npm run test:storage-interop
127
+ npm pack --dry-run
128
+ ```
129
+
130
+ 순서대로 타입 검사, 정적 검사, 전체 테스트, 실행 파일 빌드, .NET과 저장 값/비밀번호 상호 호환 검사, npm 포함 파일 검사를 수행합니다. 상호 호환 검사는 로컬 .NET SDK가 필요하며 실제 SQL Server 연결이나 SDK 브라우저 E2E 검사는 아닙니다.
131
+
132
+ `test/feature-structure.test.ts`는 리팩터링 전 HTTP 작업 계약 해시(태그 제외), 기존 SQL 해시, 폐기한 lifecycle 모듈의 부재, 런타임 import 순환, 트랜잭션 오류 후 롤백을 확인합니다. 기준값은 `test/fixtures/feature-refactor-contract.json`에 있습니다. 실제 API/DB 변경이 승인된 경우에만 예상 차이를 검토하고 해당 기준값을 갱신하십시오. 테스트를 통과시키려고 일괄 덮어쓰면 안 됩니다. 이미 적용된 migration SQL은 수정하지 않습니다.
133
+
134
+ `test/lifecycle.test.ts`는 기능을 가로지르는 기존 업무 계약 회귀 테스트이므로 파일명을 유지했습니다. 이 테스트에는 시스템 답글 실패, 마지막 피드백 갱신 실패, 담당자 시스템 답글 실패의 원자성 검사가 포함됩니다.
135
+
136
+ 이 리팩터링 자체는 새 migration이 없으므로 DB 재생성·DDL 재실행이 필요하지 않습니다. 다만 기존 개발 브랜치의 미적용 migration 006·007이 있다면 배포 시 별도의 기존 마이그레이션 절차가 수행되므로 상태와 백업을 먼저 확인해야 합니다.
137
+
138
+ 배포할 때는 `npm run build`로 이전 경로의 dist 생성물을 정리한 완전한 빌드를 사용합니다. 소스만 복사하거나 이전 dist와 합치지 않습니다. 이번 구조 변경만 되돌리는 경우 이전 소스/빌드를 복원하고 재검증하며, 별도 DB migration을 되돌리는 작업과 혼동하지 않습니다. 실제 서비스 재시작·배포는 별도 승인이 필요합니다.
@@ -0,0 +1,73 @@
1
+ # 독립 CLI와 Lite 공개 런타임
2
+
3
+ ## 현재 구조
4
+
5
+ `@fivepixels-js/cli` 0.1.0은 별도 `fivepixels-cli` Git 프로젝트에서 관리합니다. Lite API를 모노레포로 옮기지 않습니다. CLI는 `@fivepixels-js/lite-api` 0.2.1을 정확한 npm 의존성으로 사용합니다.
6
+
7
+ 사용자는 새 `fivepixels` 명령을 사용할 수 있고, 기존 `fivepixels-lite-api` 명령과 Docker의 `node dist/cli/index.js start` 진입점도 유지합니다. 새 CLI의 현재 범위는 Lite의 init/start/status/backup/migrate와 도움말·버전뿐입니다.
8
+
9
+ CLI는 명령·사용자 안내·호환성 검사를 소유합니다. Lite는 설정 우선순위, 보안 검증, JWT 키, 서버 시작·종료, DB·마이그레이션·백업을 계속 소유합니다. 명령 처리 코드가 API에 남는 것은 하위 실행 도구와 기존 설치를 보존하기 위한 의도된 구조입니다.
10
+
11
+ ## 라이선스 경계
12
+
13
+ Lite 서버 패키지 `@fivepixels-js/lite-api`는 [Elastic License 2.0](../../LICENSE)인 source-available 소프트웨어입니다. 공개 runtime과 기존 `fivepixels-lite-api` 명령도 서버 패키지의 ELv2를 따릅니다.
14
+
15
+ 독립 `@fivepixels-js/cli` 자체 코드는 Apache-2.0입니다. 서버를 npm 의존성으로 설치하거나 같은 Node 프로세스에서 호출하더라도 서버의 라이선스는 바뀌지 않습니다. 제3자에게 서버의 상당한 기능을 호스팅·관리형 서비스로 제공하는 제한 등은 서버 라이선스 전문을 확인하십시오. SDK와 다른 API 저장소의 실제 라이선스 적용 여부는 각 저장소에서 별도로 확인합니다.
16
+
17
+ ## 공개 패키지 계약
18
+
19
+ - `@fivepixels-js/lite-api/runtime`: ESM 함수 `runRuntimeCommand(args: string[]): Promise<number>`
20
+ - `@fivepixels-js/lite-api/package.json`: 버전·이름 확인용 공개 메타데이터
21
+ - `fivepixels-lite-api`: 기존 bin 명령
22
+
23
+ 기존 공개 HTTP 계약은 변경하지 않습니다. 패키지 내부 src/dist 파일은 독립 CLI의 연동 계약이 아니며 export하지 않습니다.
24
+
25
+ ```ts
26
+ import { runRuntimeCommand } from '@fivepixels-js/lite-api/runtime';
27
+
28
+ process.exitCode = await runRuntimeCommand(process.argv.slice(2));
29
+ ```
30
+
31
+ 위 코드는 같은 Node 프로세스에서 명령을 수행하는 진입점 예시입니다. 모듈 import만으로는 설정 검증, DB 연결, JWT 파일 생성, 서버 시작이나 signal handler 등록이 발생하지 않습니다.
32
+
33
+ 함수를 호출하면 기존 Lite CLI로 위임하며 process.env, 현재 작업 디렉터리, 기존 표준 출력/오류와 종료 코드 0/1/2를 유지합니다. 이 API는 CLI 프로세스 진입점용이지 HTTP 요청마다 호출하는 서버 관리 API가 아닙니다.
34
+
35
+ `start`의 Promise는 서버가 수신을 시작한 뒤 반환됩니다. 이벤트 루프와 기존 SIGINT/SIGTERM 핸들러가 서버를 유지·종료하므로 반환 직후 `process.exit()`를 호출하지 마십시오. 한 프로세스에서 여러 서버를 중복 시작하지 않습니다. 독립 CLI는 별도 서버 자식 프로세스나 signal forwarding을 추가하지 않습니다.
36
+
37
+ ## 호환성과 검증
38
+
39
+ 독립 CLI는 명령 실행 전에 패키지 이름·정확한 Lite 버전을 확인한 뒤 공개 runtime 함수를 가져옵니다. 버전이 같아도 runtime export가 없는 과거 개발 tarball이면 거부합니다. 같은 개발 버전 0.2.1의 파일을 임의로 혼용하지 않습니다.
40
+
41
+ 현재 API 프로젝트의 테스트는 공개 export와 기존 bin을 확인하고, import만으로 데이터나 signal handler가 생기지 않는지 검증합니다. 독립 CLI의 패키지 테스트에서는 두 실제 tarball을 임시 위치에 설치하여 다음을 확인합니다.
42
+
43
+ - help/version/status의 불필요한 DB 미생성과 외부 바인딩 보안 거부
44
+ - init/start/health/status/온라인 백업/migrate
45
+ - 백업 integrity_check·foreign_key_check 및 미덮어쓰기
46
+ - 기존 Lite 명령과 새 CLI의 동일 DB 사용
47
+ - 실제 POSIX SIGINT/SIGTERM 종료 코드 0과 포트 해제
48
+
49
+ 이 검증은 실제 SDK·selfdev·SQL Server E2E나 운영 배포 검증을 의미하지 않습니다. Node 22·24 및 Linux/macOS/Windows CI 구성과 실제 실행된 환경도 구분합니다.
50
+
51
+ ## 패키징과 배포 경계
52
+
53
+ ```bash
54
+ npm ci
55
+ npm run typecheck
56
+ npm run lint
57
+ npm test
58
+ npm pack
59
+ ```
60
+
61
+ 각 명령은 의존성 설치, 타입 검사, 정적 검사, 테스트, 빌드된 로컬 tarball 생성을 수행합니다. npm publish는 실행하지 않습니다.
62
+
63
+ 소스 기반 개발에서는 생성한 Lite tarball을 독립 CLI의 `setup:local`로 공급합니다. 공개된 버전은 CLI의 `fetch:runtime`으로 registry에서 준비할 수 있습니다. CLI의 공개 manifest는 Lite 0.2.1 의존성을 유지하며, 개발 lockfile은 실제 tarball의 SHA-512를 고정합니다. API 소스나 포함 문서 변경 후 tarball 체크섬이 달라지면 CLI 쪽에서 변경을 검토하고 명시적으로 lock을 갱신한 뒤 다시 검증해야 합니다. 공개한 동일 이름·버전의 내용을 교체하지 않으며 공개 후 변경은 새 버전으로 배포합니다.
64
+
65
+ 정식 CLI 배포에는 이 공개 runtime이 포함된 Lite의 선행 공개가 필요합니다. 실제 scope/registry 권한과 registry를 통한 설치는 별도 승인·검증 대상입니다. API와 CLI의 비공개 GitHub 저장소는 각각 생성·연결되어 있지만, Git commit/push, npm 공개와 운영 배포는 별도로 승인된 단계에서 수행합니다.
66
+
67
+ DB 스키마·단일 DDL·migration SQL은 이 분리로 바뀌지 않습니다. 새 CLI를 쓰기 위해 DB를 재생성하지 않습니다. 다만 기존 pending migration은 실제 init/start/migrate 실행 시 기존 규칙대로 적용될 수 있습니다.
68
+
69
+ 문제가 생기면 기존 Lite 명령으로 운영할 수 있습니다. 이번 CLI 분리만 롤백할 때는 이전 소스/빌드 또는 기존 명령을 사용하며 DB 복원·DDL 재실행은 하지 않습니다.
70
+
71
+ ## 미래 계획과의 구분
72
+
73
+ `docs/plans/one-command-self-hosted-cli.md`의 Docker·PostgreSQL·Basic API·selfdev 확장 등은 현재 구현이 아닙니다. 해당 문서의 모노레포 구성은 이전 초안이며, 현재 독립 CLI 결정에 따라 자동 적용하지 않습니다. Docker 관리, stop/logs/restore/upgrade와 버전 선택을 추가하려면 별도 범위·계약이 필요합니다.