@stone-js/starters 0.8.3

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 (352) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +101 -0
  3. package/basic-react-declarative/LICENSE +21 -0
  4. package/basic-react-declarative/README.md +39 -0
  5. package/basic-react-declarative/app/Application.tsx +89 -0
  6. package/basic-react-declarative/assets/css/index.css +26 -0
  7. package/basic-react-declarative/package.json +39 -0
  8. package/basic-react-declarative/public/logo.png +0 -0
  9. package/basic-react-declarative/tests/Application.spec.ts +96 -0
  10. package/basic-react-declarative/tests/__snapshots__/Application.spec.ts.snap +3 -0
  11. package/basic-react-declarative/tsconfig.json +33 -0
  12. package/basic-react-declarative/vitest.config.ts +24 -0
  13. package/basic-react-imperative/LICENSE +21 -0
  14. package/basic-react-imperative/README.md +39 -0
  15. package/basic-react-imperative/app/Application.tsx +89 -0
  16. package/basic-react-imperative/assets/css/index.css +26 -0
  17. package/basic-react-imperative/package.json +39 -0
  18. package/basic-react-imperative/public/logo.png +0 -0
  19. package/basic-react-imperative/tests/Application.spec.ts +80 -0
  20. package/basic-react-imperative/tests/__snapshots__/Application.spec.ts.snap +3 -0
  21. package/basic-react-imperative/tsconfig.json +33 -0
  22. package/basic-react-imperative/vitest.config.ts +24 -0
  23. package/basic-service-declarative/LICENSE +21 -0
  24. package/basic-service-declarative/README.md +39 -0
  25. package/basic-service-declarative/app/Application.ts +56 -0
  26. package/basic-service-declarative/package.json +34 -0
  27. package/basic-service-declarative/tests/Application.spec.ts +60 -0
  28. package/basic-service-declarative/tsconfig.json +25 -0
  29. package/basic-service-declarative/vitest.config.ts +24 -0
  30. package/basic-service-imperative/LICENSE +21 -0
  31. package/basic-service-imperative/README.md +39 -0
  32. package/basic-service-imperative/app/Application.ts +60 -0
  33. package/basic-service-imperative/package.json +34 -0
  34. package/basic-service-imperative/tests/Application.spec.ts +52 -0
  35. package/basic-service-imperative/tsconfig.json +25 -0
  36. package/basic-service-imperative/vitest.config.ts +24 -0
  37. package/continuum-showcase/README.md +44 -0
  38. package/continuum-showcase/app/env.d.ts +34 -0
  39. package/continuum-showcase/app/layouts/MainLayout.tsx +20 -0
  40. package/continuum-showcase/app/pages/BlogPostPage.tsx +62 -0
  41. package/continuum-showcase/app/pages/HomePage.tsx +58 -0
  42. package/continuum-showcase/package.json +25 -0
  43. package/continuum-showcase/stone.config.mjs +20 -0
  44. package/full-react-declarative/.env.example +1 -0
  45. package/full-react-declarative/.env.public.example +1 -0
  46. package/full-react-declarative/LICENSE +21 -0
  47. package/full-react-declarative/README.md +39 -0
  48. package/full-react-declarative/app/Application.ts +85 -0
  49. package/full-react-declarative/app/clients/AxiosClient.ts +136 -0
  50. package/full-react-declarative/app/clients/CommentClient.ts +61 -0
  51. package/full-react-declarative/app/clients/PostClient.ts +90 -0
  52. package/full-react-declarative/app/clients/SecurityClient.ts +66 -0
  53. package/full-react-declarative/app/clients/UserClient.ts +97 -0
  54. package/full-react-declarative/app/components/ChangePasswordForm/ChangePasswordForm.tsx +50 -0
  55. package/full-react-declarative/app/components/CommentForm/CommentForm.tsx +37 -0
  56. package/full-react-declarative/app/components/CommentItem/CommentItem.tsx +42 -0
  57. package/full-react-declarative/app/components/CommentWidget/CommentWidget.tsx +95 -0
  58. package/full-react-declarative/app/components/LoginForm/LoginForm.css +0 -0
  59. package/full-react-declarative/app/components/LoginForm/LoginForm.tsx +71 -0
  60. package/full-react-declarative/app/components/PostDetails/PostDetails.tsx +44 -0
  61. package/full-react-declarative/app/components/PostForm/PostForm.css +0 -0
  62. package/full-react-declarative/app/components/PostForm/PostForm.tsx +47 -0
  63. package/full-react-declarative/app/components/PostItem/PostItem.css +0 -0
  64. package/full-react-declarative/app/components/PostItem/PostItem.tsx +34 -0
  65. package/full-react-declarative/app/components/RegisterForm/RegisterForm.tsx +110 -0
  66. package/full-react-declarative/app/components/UserForm/UserForm.tsx +47 -0
  67. package/full-react-declarative/app/configurations/AppConfiguration.ts +17 -0
  68. package/full-react-declarative/app/error-pages/DefaultErrorPage/DefaultErrorPage.css +0 -0
  69. package/full-react-declarative/app/error-pages/DefaultErrorPage/DefaultErrorPage.tsx +52 -0
  70. package/full-react-declarative/app/error-pages/ForbiddenErrorPage/ForbiddenErrorPage.css +0 -0
  71. package/full-react-declarative/app/error-pages/ForbiddenErrorPage/ForbiddenErrorPage.tsx +53 -0
  72. package/full-react-declarative/app/error-pages/NotFoundErrorPage/NotFoundErrorPage.css +0 -0
  73. package/full-react-declarative/app/error-pages/NotFoundErrorPage/NotFoundErrorPage.tsx +54 -0
  74. package/full-react-declarative/app/error-pages/UnauthorizedErrorPage/UnauthorizedErrorPage.css +0 -0
  75. package/full-react-declarative/app/error-pages/UnauthorizedErrorPage/UnauthorizedErrorPage.tsx +53 -0
  76. package/full-react-declarative/app/errors/ForbiddenError.ts +11 -0
  77. package/full-react-declarative/app/errors/PostNotFoundError.ts +11 -0
  78. package/full-react-declarative/app/errors/UnauthorizedError.ts +11 -0
  79. package/full-react-declarative/app/errors/UserNotFoundError.ts +11 -0
  80. package/full-react-declarative/app/events/UserEvent.ts +18 -0
  81. package/full-react-declarative/app/layout/AppLayout/AppLayout.css +20 -0
  82. package/full-react-declarative/app/layout/AppLayout/AppLayout.tsx +101 -0
  83. package/full-react-declarative/app/layout/ErrorLayout/ErrorLayout.css +0 -0
  84. package/full-react-declarative/app/layout/ErrorLayout/ErrorLayout.tsx +41 -0
  85. package/full-react-declarative/app/layout/SecurityLayout/SecurityLayout.css +0 -0
  86. package/full-react-declarative/app/layout/SecurityLayout/SecurityLayout.tsx +39 -0
  87. package/full-react-declarative/app/middleware/AuthMiddleware.ts +85 -0
  88. package/full-react-declarative/app/models/Comment.ts +31 -0
  89. package/full-react-declarative/app/models/Post.ts +22 -0
  90. package/full-react-declarative/app/models/User.ts +62 -0
  91. package/full-react-declarative/app/pages/home/HomePage.tsx +44 -0
  92. package/full-react-declarative/app/pages/login/LoginPage.css +0 -0
  93. package/full-react-declarative/app/pages/login/LoginPage.tsx +73 -0
  94. package/full-react-declarative/app/pages/posts/CreatePostPage/CreatePostPage.css +0 -0
  95. package/full-react-declarative/app/pages/posts/CreatePostPage/CreatePostPage.tsx +67 -0
  96. package/full-react-declarative/app/pages/posts/PostPage/PostPage.tsx +60 -0
  97. package/full-react-declarative/app/pages/posts/ShowPostPage/ShowPostPage.tsx +83 -0
  98. package/full-react-declarative/app/pages/posts/UpdatePostPage/UpdatePostPage.tsx +77 -0
  99. package/full-react-declarative/app/pages/register/RegisterPage.tsx +68 -0
  100. package/full-react-declarative/app/pages/users/CreateUserPage.tsx +48 -0
  101. package/full-react-declarative/app/pages/users/ShowUserPage.tsx +62 -0
  102. package/full-react-declarative/app/pages/users/UpdateUserPage.tsx +57 -0
  103. package/full-react-declarative/app/pages/users/UserPage.tsx +57 -0
  104. package/full-react-declarative/app/providers/AppServiceProvider.ts +43 -0
  105. package/full-react-declarative/app/services/CommentService.ts +59 -0
  106. package/full-react-declarative/app/services/PostService.ts +108 -0
  107. package/full-react-declarative/app/services/SecurityService.ts +80 -0
  108. package/full-react-declarative/app/services/TokenService.ts +116 -0
  109. package/full-react-declarative/app/services/UserService.ts +126 -0
  110. package/full-react-declarative/app/utils.ts +17 -0
  111. package/full-react-declarative/assets/css/index.css +301 -0
  112. package/full-react-declarative/assets/img/logo.png +0 -0
  113. package/full-react-declarative/package.json +46 -0
  114. package/full-react-declarative/public/logo.png +0 -0
  115. package/full-react-declarative/public/vite.svg +1 -0
  116. package/full-react-declarative/stone.config.mjs +12 -0
  117. package/full-react-declarative/tsconfig.json +29 -0
  118. package/full-react-declarative/vitest.config.ts +24 -0
  119. package/full-react-imperative/.env.example +1 -0
  120. package/full-react-imperative/.env.public.example +1 -0
  121. package/full-react-imperative/LICENSE +21 -0
  122. package/full-react-imperative/README.md +39 -0
  123. package/full-react-imperative/app/Application.ts +79 -0
  124. package/full-react-imperative/app/clients/AxiosClient.ts +124 -0
  125. package/full-react-imperative/app/clients/CommentClient.ts +57 -0
  126. package/full-react-imperative/app/clients/PostClient.ts +86 -0
  127. package/full-react-imperative/app/clients/SecurityClient.ts +60 -0
  128. package/full-react-imperative/app/clients/UserClient.ts +93 -0
  129. package/full-react-imperative/app/clients/contracts/IAxiosClient.ts +97 -0
  130. package/full-react-imperative/app/clients/contracts/ICommentClient.ts +31 -0
  131. package/full-react-imperative/app/clients/contracts/IPostClient.ts +55 -0
  132. package/full-react-imperative/app/clients/contracts/ISecurityClient.ts +33 -0
  133. package/full-react-imperative/app/clients/contracts/IUserClient.ts +61 -0
  134. package/full-react-imperative/app/components/ChangePasswordForm/ChangePasswordForm.tsx +50 -0
  135. package/full-react-imperative/app/components/CommentForm/CommentForm.css +35 -0
  136. package/full-react-imperative/app/components/CommentForm/CommentForm.tsx +45 -0
  137. package/full-react-imperative/app/components/CommentItem/CommentItem.css +74 -0
  138. package/full-react-imperative/app/components/CommentItem/CommentItem.tsx +62 -0
  139. package/full-react-imperative/app/components/CommentWidget/CommentWidget.css +25 -0
  140. package/full-react-imperative/app/components/CommentWidget/CommentWidget.tsx +119 -0
  141. package/full-react-imperative/app/components/Dropdown/Dropdown.css +52 -0
  142. package/full-react-imperative/app/components/Dropdown/Dropdown.tsx +40 -0
  143. package/full-react-imperative/app/components/LayoutHeader/LayoutHeader.css +62 -0
  144. package/full-react-imperative/app/components/LayoutHeader/LayoutHeader.tsx +61 -0
  145. package/full-react-imperative/app/components/LayoutLeftMenu/LayoutLeftMenu.css +25 -0
  146. package/full-react-imperative/app/components/LayoutLeftMenu/LayoutLeftMenu.tsx +32 -0
  147. package/full-react-imperative/app/components/LoginForm/LoginForm.css +0 -0
  148. package/full-react-imperative/app/components/LoginForm/LoginForm.tsx +71 -0
  149. package/full-react-imperative/app/components/NotificationBadge/NotificationBadge.css +41 -0
  150. package/full-react-imperative/app/components/NotificationBadge/NotificationBadge.tsx +63 -0
  151. package/full-react-imperative/app/components/PostCard/PostCard.css +129 -0
  152. package/full-react-imperative/app/components/PostCard/PostCard.tsx +65 -0
  153. package/full-react-imperative/app/components/PostForm/PostForm.css +92 -0
  154. package/full-react-imperative/app/components/PostForm/PostForm.tsx +76 -0
  155. package/full-react-imperative/app/components/PostSkeleton/PostSkeleton.css +65 -0
  156. package/full-react-imperative/app/components/PostSkeleton/PostSkeleton.tsx +25 -0
  157. package/full-react-imperative/app/components/PostWidget/PostWidget.tsx +95 -0
  158. package/full-react-imperative/app/components/ProfileForm/ProfileForm.css +84 -0
  159. package/full-react-imperative/app/components/ProfileForm/ProfileForm.tsx +79 -0
  160. package/full-react-imperative/app/components/RegisterForm/RegisterForm.tsx +110 -0
  161. package/full-react-imperative/app/components/RightAsidePanel/RightAsidePanel.css +32 -0
  162. package/full-react-imperative/app/components/RightAsidePanel/RightAsidePanel.tsx +62 -0
  163. package/full-react-imperative/app/components/SettingsForm/SettingsForm.css +126 -0
  164. package/full-react-imperative/app/components/SettingsForm/SettingsForm.tsx +86 -0
  165. package/full-react-imperative/app/components/UserAvatar/UserAvatar.css +18 -0
  166. package/full-react-imperative/app/components/UserAvatar/UserAvatar.tsx +60 -0
  167. package/full-react-imperative/app/components/UserBadge/UserBadge.css +33 -0
  168. package/full-react-imperative/app/components/UserBadge/UserBadge.tsx +49 -0
  169. package/full-react-imperative/app/components/UserCard/UserCard.css +70 -0
  170. package/full-react-imperative/app/components/UserCard/UserCard.tsx +43 -0
  171. package/full-react-imperative/app/configurations/AppConfiguration.ts +19 -0
  172. package/full-react-imperative/app/error-pages/DefaultErrorPage/DefaultErrorPage.css +0 -0
  173. package/full-react-imperative/app/error-pages/DefaultErrorPage/DefaultErrorPage.tsx +41 -0
  174. package/full-react-imperative/app/error-pages/ForbiddenErrorPage/ForbiddenErrorPage.css +0 -0
  175. package/full-react-imperative/app/error-pages/ForbiddenErrorPage/ForbiddenErrorPage.tsx +42 -0
  176. package/full-react-imperative/app/error-pages/NotFoundErrorPage/NotFoundErrorPage.css +0 -0
  177. package/full-react-imperative/app/error-pages/NotFoundErrorPage/NotFoundErrorPage.tsx +49 -0
  178. package/full-react-imperative/app/error-pages/UnauthorizedErrorPage/UnauthorizedErrorPage.css +0 -0
  179. package/full-react-imperative/app/error-pages/UnauthorizedErrorPage/UnauthorizedErrorPage.tsx +42 -0
  180. package/full-react-imperative/app/errors/ForbiddenError.ts +11 -0
  181. package/full-react-imperative/app/errors/PostNotFoundError.ts +11 -0
  182. package/full-react-imperative/app/errors/UnauthorizedError.ts +11 -0
  183. package/full-react-imperative/app/errors/UserNotFoundError.ts +11 -0
  184. package/full-react-imperative/app/events/UserEvent.ts +18 -0
  185. package/full-react-imperative/app/layout/AppLayout/AppLayout.css +106 -0
  186. package/full-react-imperative/app/layout/AppLayout/AppLayout.tsx +48 -0
  187. package/full-react-imperative/app/layout/ErrorLayout/ErrorLayout.css +0 -0
  188. package/full-react-imperative/app/layout/ErrorLayout/ErrorLayout.tsx +45 -0
  189. package/full-react-imperative/app/layout/SecurityLayout/SecurityLayout.css +0 -0
  190. package/full-react-imperative/app/layout/SecurityLayout/SecurityLayout.tsx +43 -0
  191. package/full-react-imperative/app/layout/SettingsLayout/SettingsLayout.css +99 -0
  192. package/full-react-imperative/app/layout/SettingsLayout/SettingsLayout.tsx +44 -0
  193. package/full-react-imperative/app/middleware/AuthMiddleware.ts +56 -0
  194. package/full-react-imperative/app/models/Comment.ts +35 -0
  195. package/full-react-imperative/app/models/Post.ts +27 -0
  196. package/full-react-imperative/app/models/User.ts +68 -0
  197. package/full-react-imperative/app/pages/Settings/SettingsPage.tsx +23 -0
  198. package/full-react-imperative/app/pages/home/HomePage.tsx +58 -0
  199. package/full-react-imperative/app/pages/login/LoginPage.css +0 -0
  200. package/full-react-imperative/app/pages/login/LoginPage.tsx +65 -0
  201. package/full-react-imperative/app/pages/posts/ShowPostPage/ShowPostPage.tsx +64 -0
  202. package/full-react-imperative/app/pages/posts/UpdatePostPage/UpdatePostPage.tsx +56 -0
  203. package/full-react-imperative/app/pages/register/RegisterPage.tsx +71 -0
  204. package/full-react-imperative/app/pages/users/ShowUserPage/ShowUserPage.css +64 -0
  205. package/full-react-imperative/app/pages/users/ShowUserPage/ShowUserPage.tsx +104 -0
  206. package/full-react-imperative/app/pages/users/UpdateUserPage/UpdateUserPage.css +0 -0
  207. package/full-react-imperative/app/pages/users/UpdateUserPage/UpdateUserPage.tsx +43 -0
  208. package/full-react-imperative/app/pages/users/UserPage/UserPage.css +31 -0
  209. package/full-react-imperative/app/pages/users/UserPage/UserPage.tsx +59 -0
  210. package/full-react-imperative/app/pages/utils.ts +67 -0
  211. package/full-react-imperative/app/providers/AppServiceProvider.ts +40 -0
  212. package/full-react-imperative/app/services/CommentService.ts +53 -0
  213. package/full-react-imperative/app/services/PostService.ts +102 -0
  214. package/full-react-imperative/app/services/SecurityService.ts +72 -0
  215. package/full-react-imperative/app/services/TokenService.ts +108 -0
  216. package/full-react-imperative/app/services/UserService.ts +115 -0
  217. package/full-react-imperative/app/services/contracts/ICommentService.ts +30 -0
  218. package/full-react-imperative/app/services/contracts/IPostService.ts +65 -0
  219. package/full-react-imperative/app/services/contracts/ISecurityService.ts +39 -0
  220. package/full-react-imperative/app/services/contracts/ITokenService.ts +44 -0
  221. package/full-react-imperative/app/services/contracts/IUserService.ts +70 -0
  222. package/full-react-imperative/app/utils.ts +31 -0
  223. package/full-react-imperative/assets/css/index.css +301 -0
  224. package/full-react-imperative/assets/img/logo.png +0 -0
  225. package/full-react-imperative/package.json +48 -0
  226. package/full-react-imperative/public/logo.png +0 -0
  227. package/full-react-imperative/public/vite.svg +1 -0
  228. package/full-react-imperative/stone.config.mjs +12 -0
  229. package/full-react-imperative/tsconfig.json +29 -0
  230. package/full-react-imperative/vitest.config.ts +24 -0
  231. package/full-service-declarative/.env.example +2 -0
  232. package/full-service-declarative/LICENSE +21 -0
  233. package/full-service-declarative/README.md +65 -0
  234. package/full-service-declarative/app/Application.ts +327 -0
  235. package/full-service-declarative/app/commands/ListSessionsCommand.ts +70 -0
  236. package/full-service-declarative/app/commands/ListUsersCommand.ts +56 -0
  237. package/full-service-declarative/app/configurations/AppConfiguration.ts +52 -0
  238. package/full-service-declarative/app/configurations/UserLiveConfiguration.ts +28 -0
  239. package/full-service-declarative/app/database/DatabaseClient.ts +43 -0
  240. package/full-service-declarative/app/database/schema.ts +43 -0
  241. package/full-service-declarative/app/error-handlers/SecurityErrorHandler.ts +61 -0
  242. package/full-service-declarative/app/errors/CredentialsError.ts +12 -0
  243. package/full-service-declarative/app/events/UserEvent.ts +17 -0
  244. package/full-service-declarative/app/handlers/CommentEventHandler.ts +103 -0
  245. package/full-service-declarative/app/handlers/PostEventHandler.ts +104 -0
  246. package/full-service-declarative/app/handlers/SecurityEventHandler.ts +108 -0
  247. package/full-service-declarative/app/handlers/UserEventHandler.ts +159 -0
  248. package/full-service-declarative/app/listeners/UserEventListener.ts +35 -0
  249. package/full-service-declarative/app/middleware/AuthMiddleware.ts +54 -0
  250. package/full-service-declarative/app/models/Comment.ts +38 -0
  251. package/full-service-declarative/app/models/Post.ts +50 -0
  252. package/full-service-declarative/app/models/Session.ts +14 -0
  253. package/full-service-declarative/app/models/User.ts +77 -0
  254. package/full-service-declarative/app/providers/DrizzleServiceProvider.ts +59 -0
  255. package/full-service-declarative/app/repositories/CommentRepository.ts +155 -0
  256. package/full-service-declarative/app/repositories/PostRepository.ts +138 -0
  257. package/full-service-declarative/app/repositories/SessionRepository.ts +123 -0
  258. package/full-service-declarative/app/repositories/UserRepository.ts +99 -0
  259. package/full-service-declarative/app/services/CommentService.ts +112 -0
  260. package/full-service-declarative/app/services/PostService.ts +100 -0
  261. package/full-service-declarative/app/services/SecurityService.ts +232 -0
  262. package/full-service-declarative/app/services/SessionService.ts +130 -0
  263. package/full-service-declarative/app/services/UserService.ts +110 -0
  264. package/full-service-declarative/app/subscribers/UserEventSubscriber.ts +38 -0
  265. package/full-service-declarative/drizzle.config.ts +13 -0
  266. package/full-service-declarative/package.json +48 -0
  267. package/full-service-declarative/stone.config.mjs +12 -0
  268. package/full-service-declarative/tsconfig.json +25 -0
  269. package/full-service-declarative/vitest.config.ts +24 -0
  270. package/full-service-imperative/.env.example +2 -0
  271. package/full-service-imperative/LICENSE +21 -0
  272. package/full-service-imperative/README.md +65 -0
  273. package/full-service-imperative/app/Application.ts +317 -0
  274. package/full-service-imperative/app/commands/ListSessionsCommand.ts +54 -0
  275. package/full-service-imperative/app/commands/ListUsersCommand.ts +42 -0
  276. package/full-service-imperative/app/configurations/AppConfiguration.ts +44 -0
  277. package/full-service-imperative/app/configurations/UserLiveConfiguration.ts +26 -0
  278. package/full-service-imperative/app/database/DatabaseClient.ts +39 -0
  279. package/full-service-imperative/app/database/schema.ts +43 -0
  280. package/full-service-imperative/app/error-handlers/SecurityErrorHandler.ts +58 -0
  281. package/full-service-imperative/app/errors/CredentialsError.ts +12 -0
  282. package/full-service-imperative/app/events/UserEvent.ts +17 -0
  283. package/full-service-imperative/app/handlers/CommentEventHandler.ts +99 -0
  284. package/full-service-imperative/app/handlers/PostEventHandler.ts +99 -0
  285. package/full-service-imperative/app/handlers/SecurityEventHandler.ts +83 -0
  286. package/full-service-imperative/app/handlers/UserEventHandler.ts +132 -0
  287. package/full-service-imperative/app/listeners/UserEventListener.ts +23 -0
  288. package/full-service-imperative/app/middleware/AuthMiddleware.ts +44 -0
  289. package/full-service-imperative/app/models/Comment.ts +38 -0
  290. package/full-service-imperative/app/models/Post.ts +50 -0
  291. package/full-service-imperative/app/models/Session.ts +14 -0
  292. package/full-service-imperative/app/models/User.ts +77 -0
  293. package/full-service-imperative/app/providers/DrizzleServiceProvider.ts +56 -0
  294. package/full-service-imperative/app/repositories/CommentRepository.ts +148 -0
  295. package/full-service-imperative/app/repositories/PostRepository.ts +130 -0
  296. package/full-service-imperative/app/repositories/SessionRepository.ts +115 -0
  297. package/full-service-imperative/app/repositories/UserRepository.ts +91 -0
  298. package/full-service-imperative/app/repositories/contracts/ICommentRepository.ts +72 -0
  299. package/full-service-imperative/app/repositories/contracts/IPostRepository.ts +63 -0
  300. package/full-service-imperative/app/repositories/contracts/ISessionRepository.ts +65 -0
  301. package/full-service-imperative/app/repositories/contracts/IUserRepository.ts +55 -0
  302. package/full-service-imperative/app/services/CommentService.ts +109 -0
  303. package/full-service-imperative/app/services/PostService.ts +97 -0
  304. package/full-service-imperative/app/services/SecurityService.ts +224 -0
  305. package/full-service-imperative/app/services/SessionService.ts +124 -0
  306. package/full-service-imperative/app/services/UserService.ts +105 -0
  307. package/full-service-imperative/app/services/contracts/ICommentService.ts +62 -0
  308. package/full-service-imperative/app/services/contracts/IPostService.ts +53 -0
  309. package/full-service-imperative/app/services/contracts/ISecurityService.ts +97 -0
  310. package/full-service-imperative/app/services/contracts/ISessionService.ts +73 -0
  311. package/full-service-imperative/app/services/contracts/IUserService.ts +57 -0
  312. package/full-service-imperative/app/subscribers/UserEventSubscriber.ts +28 -0
  313. package/full-service-imperative/drizzle.config.ts +13 -0
  314. package/full-service-imperative/package.json +48 -0
  315. package/full-service-imperative/stone.config.mjs +12 -0
  316. package/full-service-imperative/tsconfig.json +25 -0
  317. package/full-service-imperative/vitest.config.ts +24 -0
  318. package/package.json +225 -0
  319. package/standard-react-declarative/LICENSE +21 -0
  320. package/standard-react-declarative/README.md +39 -0
  321. package/standard-react-declarative/app/Application.ts +45 -0
  322. package/standard-react-declarative/app/pages/welcome/WelcomePage.tsx +57 -0
  323. package/standard-react-declarative/app/services/WelcomeService.ts +38 -0
  324. package/standard-react-declarative/assets/css/index.css +26 -0
  325. package/standard-react-declarative/package.json +42 -0
  326. package/standard-react-declarative/tsconfig.json +33 -0
  327. package/standard-react-declarative/vitest.config.ts +24 -0
  328. package/standard-react-imperative/LICENSE +21 -0
  329. package/standard-react-imperative/README.md +39 -0
  330. package/standard-react-imperative/app/Application.ts +41 -0
  331. package/standard-react-imperative/app/pages/welcome/WelcomePage.tsx +50 -0
  332. package/standard-react-imperative/app/services/WelcomeService.ts +38 -0
  333. package/standard-react-imperative/assets/css/index.css +26 -0
  334. package/standard-react-imperative/package.json +42 -0
  335. package/standard-react-imperative/tsconfig.json +33 -0
  336. package/standard-react-imperative/vitest.config.ts +24 -0
  337. package/standard-service-declarative/LICENSE +21 -0
  338. package/standard-service-declarative/README.md +39 -0
  339. package/standard-service-declarative/app/Application.ts +39 -0
  340. package/standard-service-declarative/app/handlers/WelcomeEventHandler.ts +45 -0
  341. package/standard-service-declarative/app/services/WelcomeService.ts +38 -0
  342. package/standard-service-declarative/package.json +36 -0
  343. package/standard-service-declarative/tsconfig.json +25 -0
  344. package/standard-service-declarative/vitest.config.ts +24 -0
  345. package/standard-service-imperative/LICENSE +21 -0
  346. package/standard-service-imperative/README.md +39 -0
  347. package/standard-service-imperative/app/Application.ts +11 -0
  348. package/standard-service-imperative/app/handlers/welcomeEventHandler.ts +31 -0
  349. package/standard-service-imperative/app/services/welcomeService.ts +38 -0
  350. package/standard-service-imperative/package.json +35 -0
  351. package/standard-service-imperative/tsconfig.json +25 -0
  352. package/standard-service-imperative/vitest.config.ts +24 -0
@@ -0,0 +1,44 @@
1
+ import { Config } from '@libsql/client'
2
+ import { getString } from '@stone-js/env'
3
+ import { CORSHeadersMiddleware } from '@stone-js/http-core'
4
+ import { defineBlueprintMiddleware, defineConfig, IBlueprint, LogLevel, Promiseable } from '@stone-js/core'
5
+
6
+ /**
7
+ * User Implicit Configuration
8
+ *
9
+ * Explicit configuration takes precedence over implicit configuration.
10
+ * Implicit configuration are defined at decorators level.
11
+ */
12
+ export const AppConfiguration = defineConfig((blueprint: IBlueprint): Promiseable<void> => {
13
+ blueprint
14
+ .set('database', databaseConfig())
15
+ .set('security', securityConfig())
16
+ .set('stone.logger.level', LogLevel.INFO)
17
+ .set('stone.http.cors.preflightStop', true)
18
+ .set('stone.http.cors.allowedHeaders', ['*'])
19
+ .set(defineBlueprintMiddleware(CORSHeadersMiddleware))
20
+ })
21
+
22
+ /**
23
+ * Get the database configuration
24
+ */
25
+ function databaseConfig (): Config {
26
+ return {
27
+ url: getString('DATABASE_URL', 'file:local.db')
28
+ }
29
+ }
30
+
31
+ /**
32
+ * Get the security configuration
33
+ */
34
+ function securityConfig (): Record<string, any> {
35
+ return {
36
+ jwt: {
37
+ expiresIn: 3600
38
+ },
39
+ secret: getString('SECURITY_JWT_SECRET', 'non_prod_secret'),
40
+ bcrypt: {
41
+ saltRounds: 10
42
+ }
43
+ }
44
+ }
@@ -0,0 +1,26 @@
1
+ import { defineConfig, IBlueprint } from '@stone-js/core'
2
+
3
+ /**
4
+ * Configure the application
5
+ *
6
+ * @param blueprint - The blueprint to configure
7
+ */
8
+ export async function liveConfig (blueprint: IBlueprint): Promise<void> {
9
+ blueprint.set('stone.name', await fetchConfigurationRemotely())
10
+ console.log('I am live cause i am loaded at each request...')
11
+ }
12
+
13
+ /**
14
+ * Fetch the configuration remotely
15
+ */
16
+ async function fetchConfigurationRemotely (): Promise<string> {
17
+ console.log('Fetching configuration...')
18
+ return await Promise.resolve('My Fetched app name')
19
+ }
20
+
21
+ /**
22
+ * User Live Configuration
23
+ */
24
+ export const UserLiveConfiguration = defineConfig((blueprint: IBlueprint): void => {
25
+ blueprint.add('stone.liveConfigurations', [liveConfig])
26
+ })
@@ -0,0 +1,39 @@
1
+ import { Client, Config, createClient as baseCreateClient } from '@libsql/client'
2
+
3
+ /**
4
+ * DatabaseClient
5
+ */
6
+ let dbClient: Client | undefined
7
+
8
+ /**
9
+ * The client instance
10
+ */
11
+ export function getClient (): Client {
12
+ if (dbClient === undefined) {
13
+ throw new Error('Database client is not initialized.')
14
+ }
15
+ return dbClient
16
+ }
17
+
18
+ /**
19
+ * Create a new client instance
20
+ *
21
+ * @param config - The configuration object for the client
22
+ * @returns The client instance
23
+ */
24
+ export function createClient (config: Config): Client {
25
+ if (dbClient === undefined) {
26
+ dbClient = baseCreateClient(config)
27
+ }
28
+ return dbClient
29
+ }
30
+
31
+ /**
32
+ * Close the client instance
33
+ */
34
+ export function closeClient (): void {
35
+ if (dbClient !== undefined) {
36
+ dbClient.close()
37
+ dbClient = undefined
38
+ }
39
+ }
@@ -0,0 +1,43 @@
1
+ import { sqliteTable, integer, text } from 'drizzle-orm/sqlite-core'
2
+
3
+ // User Table
4
+ export const users = sqliteTable('users', {
5
+ id: integer('id').primaryKey(),
6
+ name: text('name').notNull(),
7
+ password: text('password'),
8
+ avatar: text('avatar'),
9
+ email: text('email').unique().notNull(),
10
+ updatedAt: integer('updated_at').notNull(),
11
+ createdAt: integer('created_at').notNull() // Store timestamps as integers (Unix timestamp)
12
+ })
13
+
14
+ export const sessions = sqliteTable('sessions', {
15
+ id: integer('id').primaryKey(),
16
+ ip: text('ip').notNull(),
17
+ userAgent: text('user_agent'),
18
+ closedAt: integer('closed_at'),
19
+ uuid: text('uuid').unique().notNull(),
20
+ createdAt: integer('created_at').notNull(),
21
+ expiresAt: integer('expires_at').notNull(),
22
+ lastActivityAt: integer('last_activity_at').notNull(),
23
+ userId: integer('user_id').notNull().references(() => users.id, { onDelete: 'cascade' })
24
+ })
25
+
26
+ // Post Table
27
+ export const posts = sqliteTable('posts', {
28
+ id: integer('id').primaryKey(),
29
+ title: text('title').notNull(),
30
+ content: text('content').notNull(),
31
+ createdAt: integer('created_at').notNull(),
32
+ updatedAt: integer('updated_at').notNull(),
33
+ authorId: integer('author_id').notNull().references(() => users.id, { onDelete: 'cascade' })
34
+ })
35
+
36
+ // Comment Table
37
+ export const comments = sqliteTable('comments', {
38
+ id: integer('id').primaryKey(),
39
+ content: text('content').notNull(),
40
+ createdAt: integer('created_at').notNull(),
41
+ authorId: integer('author_id').notNull().references(() => users.id, { onDelete: 'cascade' }),
42
+ postId: integer('post_id').notNull().references(() => posts.id, { onDelete: 'cascade' })
43
+ })
@@ -0,0 +1,58 @@
1
+ import { BadCredentialsError } from '../errors/CredentialsError'
2
+ import { defineErrorHandler, FunctionalErrorHandler, ILogger, Promiseable } from '@stone-js/core'
3
+ import { BadRequestError, HTTP_BAD_REQUEST, HTTP_INTERNAL_SERVER_ERROR, HTTP_NOT_FOUND, HTTP_UNAUTHORIZED, IncomingHttpEvent, NotFoundError, UnauthorizedError } from '@stone-js/http-core'
4
+
5
+ /**
6
+ * User Error Handler Options
7
+ */
8
+ export interface SecurityErrorHandlerOptions {
9
+ logger: ILogger
10
+ }
11
+
12
+ /**
13
+ * Factory User Error Handler
14
+ */
15
+ export function factorySecurityErrorHandler ({ logger }: SecurityErrorHandlerOptions): FunctionalErrorHandler<IncomingHttpEvent> {
16
+ /**
17
+ * Handle CredentialsError and UnauthorizedError
18
+ *
19
+ * @param error - The error to handle
20
+ * @param _event - The incoming event
21
+ * @returns The response
22
+ */
23
+ return function (error: BadCredentialsError | UnauthorizedError | NotFoundError | BadRequestError, _event: IncomingHttpEvent): Promiseable<unknown> {
24
+ logger.error(error.message)
25
+
26
+ let message: string = 'An error occurred'
27
+ let statusCode: number = HTTP_INTERNAL_SERVER_ERROR
28
+
29
+ if (error instanceof NotFoundError) {
30
+ message = error.message
31
+ statusCode = HTTP_NOT_FOUND
32
+ } else if (error instanceof BadCredentialsError) {
33
+ message = 'Invalid credentials'
34
+ statusCode = HTTP_UNAUTHORIZED
35
+ } else if (error instanceof UnauthorizedError) {
36
+ statusCode = HTTP_UNAUTHORIZED
37
+ message = 'You are not authorized to perform this action'
38
+ } else if (error instanceof BadRequestError) {
39
+ statusCode = HTTP_BAD_REQUEST
40
+ message = error.message
41
+ }
42
+
43
+ return {
44
+ statusCode,
45
+ content: {
46
+ errors: { message }
47
+ }
48
+ }
49
+ }
50
+ }
51
+
52
+ /**
53
+ * User Error Handler
54
+ */
55
+ export const SecurityErrorHandler = defineErrorHandler(
56
+ factorySecurityErrorHandler,
57
+ { isFactory: true, error: ['BadCredentialsError', 'UnauthorizedError', 'NotFoundError', 'BadRequestError'] }
58
+ )
@@ -0,0 +1,12 @@
1
+ import { ErrorOptions } from '@stone-js/core'
2
+ import { UnauthorizedError } from '@stone-js/http-core'
3
+
4
+ /**
5
+ * Bad Credentials Error
6
+ */
7
+ export class BadCredentialsError extends UnauthorizedError {
8
+ constructor (message: string, options?: ErrorOptions) {
9
+ super(message, options)
10
+ this.name = 'BadCredentialsError'
11
+ }
12
+ }
@@ -0,0 +1,17 @@
1
+ import { Event } from '@stone-js/core'
2
+
3
+ /**
4
+ * User Event
5
+ */
6
+ export class UserEvent extends Event {
7
+ /**
8
+ * USER_CREATED Event name, fires after a user have been created.
9
+ *
10
+ * @event UserEvent#USER_CREATED
11
+ */
12
+ static USER_CREATED: string = 'user.created'
13
+
14
+ constructor (public readonly user: any) {
15
+ super({ type: UserEvent.USER_CREATED })
16
+ }
17
+ }
@@ -0,0 +1,99 @@
1
+ import { User } from '../models/User'
2
+ import { Post as BlogPost } from '../models/Post'
3
+ import { IncomingHttpEvent } from '@stone-js/http-core'
4
+ import { FunctionalEventHandler, ILogger } from '@stone-js/core'
5
+ import { postResolveRouteBinding } from '../services/PostService'
6
+ import { CommentModel, CommentResponse } from '../models/Comment'
7
+ import { defineRoutes, DELETE, GET, POST } from '@stone-js/router'
8
+ import { ICommentService } from '../services/contracts/ICommentService'
9
+ import { commentResolveRouteBinding } from '../services/CommentService'
10
+
11
+ /**
12
+ * Comment Event Handler Options
13
+ */
14
+ export interface CommentEventHandlerOptions {
15
+ logger: ILogger
16
+ commentService: ICommentService
17
+ }
18
+
19
+ /**
20
+ * List all comments
21
+ */
22
+ export function factoryCommentListEventHandler ({ commentService }: CommentEventHandlerOptions): FunctionalEventHandler<IncomingHttpEvent> {
23
+ return async function (event: IncomingHttpEvent): Promise<CommentResponse[]> {
24
+ return await commentService.list(event.get<number>('limit', 10))
25
+ }
26
+ }
27
+
28
+ /**
29
+ * List comments by post
30
+ */
31
+ export function factoryCommentListByPostEventHandler ({ commentService }: CommentEventHandlerOptions): FunctionalEventHandler<IncomingHttpEvent> {
32
+ return async function (event: IncomingHttpEvent): Promise<CommentResponse[]> {
33
+ return await commentService.listByPost(
34
+ event.get<number>('postId', 0),
35
+ event.get<number>('limit', 10)
36
+ )
37
+ }
38
+ }
39
+
40
+ /**
41
+ * List comments by author
42
+ */
43
+ export function factoryCommentListByAuthorEventHandler ({ commentService }: CommentEventHandlerOptions): FunctionalEventHandler<IncomingHttpEvent> {
44
+ return async function (event: IncomingHttpEvent): Promise<CommentResponse[]> {
45
+ return await commentService.listByAuthor(
46
+ event.get<number>('authorId', 0),
47
+ event.get<number>('limit', 10)
48
+ )
49
+ }
50
+ }
51
+
52
+ /**
53
+ * Show a comment by ID
54
+ */
55
+ export function factoryCommentShowEventHandler ({ commentService }: CommentEventHandlerOptions): FunctionalEventHandler<IncomingHttpEvent> {
56
+ return function (event: IncomingHttpEvent): CommentResponse {
57
+ return event.get<CommentResponse>('comment', {} as unknown as CommentResponse)
58
+ }
59
+ }
60
+
61
+ /**
62
+ * Create a comment
63
+ */
64
+ export function factoryCommentCreateEventHandler ({ commentService }: CommentEventHandlerOptions): FunctionalEventHandler<IncomingHttpEvent> {
65
+ return async function (event: IncomingHttpEvent): Promise<{ id?: number }> {
66
+ const id = await commentService.create({
67
+ ...event.getBody<CommentModel>({} as any),
68
+ authorId: event.getUser<User>()?.id ?? 0,
69
+ postId: event.get<BlogPost>('post', { id: 0 } as any).id
70
+ })
71
+
72
+ return { id: Number(id) }
73
+ }
74
+ }
75
+
76
+ /**
77
+ * Delete a comment
78
+ */
79
+ export function factoryCommentDeleteEventHandler ({ commentService, logger }: CommentEventHandlerOptions): FunctionalEventHandler<IncomingHttpEvent> {
80
+ return async function (event: IncomingHttpEvent): Promise<{ statusCode: number }> {
81
+ await commentService.delete(event.get<number>('id', 0))
82
+ logger.info(`Comment deleted: ${String(event.get<number>('id'))}, by user: ${String(event.getUser<User>()?.id)}`)
83
+ return { statusCode: 204 }
84
+ }
85
+ }
86
+
87
+ /**
88
+ * Comment Event Handler
89
+ */
90
+ export const CommentEventHandler = defineRoutes(
91
+ [
92
+ [factoryCommentListEventHandler, { isFactory: true, path: '/comments/', method: GET, name: 'comments.list' }],
93
+ [factoryCommentDeleteEventHandler, { isFactory: true, path: '/comments/:id', method: DELETE, name: 'comments.delete', rules: { id: /\d+/ } }],
94
+ [factoryCommentListByPostEventHandler, { isFactory: true, path: '/comments/posts/:postId(\\d+)', method: GET, name: 'comments.list-by-post' }],
95
+ [factoryCommentListByAuthorEventHandler, { isFactory: true, path: '/comments/authors/:authorId(\\d+)', method: GET, name: 'comments.list-by-author' }],
96
+ [factoryCommentShowEventHandler, { isFactory: true, path: '/comments/:comment@id(\\d+)', method: GET, name: 'comments.show', bindings: { user: commentResolveRouteBinding } }],
97
+ [factoryCommentCreateEventHandler, { isFactory: true, path: '/comments/posts/:post@id(\\d+)', method: POST, name: 'comments.create', bindings: { post: postResolveRouteBinding } }]
98
+ ]
99
+ )
@@ -0,0 +1,99 @@
1
+ import { User } from '../models/User'
2
+ import { PostModel, PostResponse } from '../models/Post'
3
+ import { FunctionalEventHandler, ILogger } from '@stone-js/core'
4
+ import { IPostService } from '../services/contracts/IPostService'
5
+ import { postResolveRouteBinding } from '../services/PostService'
6
+ import { defineRoutes, DELETE, GET, PATCH, POST } from '@stone-js/router'
7
+ import { IncomingHttpEvent, InternalServerError } from '@stone-js/http-core'
8
+
9
+ /**
10
+ * Post Event Handler Options
11
+ */
12
+ export interface PostEventHandlerOptions {
13
+ logger: ILogger
14
+ postService: IPostService
15
+ }
16
+
17
+ /**
18
+ * List all posts
19
+ */
20
+ export function factoryPostListEventHandler ({ postService }: PostEventHandlerOptions): FunctionalEventHandler<IncomingHttpEvent> {
21
+ return async function (event: IncomingHttpEvent): Promise<PostResponse[]> {
22
+ return await postService.list(event.get<number>('limit', 10))
23
+ }
24
+ }
25
+
26
+ /**
27
+ * List all posts by a specific author
28
+ */
29
+ export function factoryPostListByAuthorEventHandler ({ postService }: PostEventHandlerOptions): FunctionalEventHandler<IncomingHttpEvent> {
30
+ return async function (event: IncomingHttpEvent): Promise<PostResponse[]> {
31
+ return await postService.listByAuthor(
32
+ event.get<number>('id', 0),
33
+ event.get<number>('limit', 10)
34
+ )
35
+ }
36
+ }
37
+
38
+ /**
39
+ * Show a post by ID
40
+ */
41
+ export function factoryPostShowEventHandler (): FunctionalEventHandler<IncomingHttpEvent> {
42
+ return function (event: IncomingHttpEvent): PostResponse {
43
+ return event.get<PostResponse>('post', {} as unknown as PostResponse)
44
+ }
45
+ }
46
+
47
+ /**
48
+ * Create a post
49
+ */
50
+ export function factoryPostCreateEventHandler ({ postService }: PostEventHandlerOptions): FunctionalEventHandler<IncomingHttpEvent> {
51
+ return async function (event: IncomingHttpEvent): Promise<{ id?: number }> {
52
+ const id = await postService.create({
53
+ ...event.getBody<PostModel>({} as any),
54
+ authorId: event.getUser<User>()?.id ?? 0
55
+ })
56
+
57
+ return { id: Number(id) }
58
+ }
59
+ }
60
+
61
+ /**
62
+ * Update a post
63
+ */
64
+ export function factoryPostUpdateEventHandler ({ postService }: PostEventHandlerOptions): FunctionalEventHandler<IncomingHttpEvent> {
65
+ return async function (event: IncomingHttpEvent): Promise<void> {
66
+ const updated = await postService.update(event.get<number>('id', 0), event.getBody<PostModel>({} as any))
67
+ if (!updated) {
68
+ throw new InternalServerError(`Failed to update post with ID ${String(event.get<number>('id'))}`)
69
+ }
70
+ }
71
+ }
72
+
73
+ /**
74
+ * Delete a post
75
+ */
76
+ export function factoryPostDeleteEventHandler ({ postService, logger }: PostEventHandlerOptions): FunctionalEventHandler<IncomingHttpEvent> {
77
+ return async function (event: IncomingHttpEvent): Promise<{ statusCode: number }> {
78
+ const deleted = await postService.delete(event.get<number>('id', 0))
79
+ if (!deleted) {
80
+ throw new InternalServerError(`Failed to update post with ID ${String(event.get<number>('id'))}`)
81
+ }
82
+ logger.info(`Post deleted: ${String(event.get<number>('id'))}, by user: ${String(event.getUser<User>()?.id)}`)
83
+ return { statusCode: 204 }
84
+ }
85
+ }
86
+
87
+ /**
88
+ * Post Event Handler
89
+ */
90
+ export const PostEventHandler = defineRoutes(
91
+ [
92
+ [factoryPostListEventHandler, { isFactory: true, path: '/posts/', method: GET, name: 'posts.list' }],
93
+ [factoryPostCreateEventHandler, { isFactory: true, path: '/posts/', method: POST, name: 'posts.create' }],
94
+ [factoryPostUpdateEventHandler, { isFactory: true, path: '/posts/:id', method: PATCH, name: 'posts.update', rules: { id: /\d+/ } }],
95
+ [factoryPostDeleteEventHandler, { isFactory: true, path: '/posts/:id', method: DELETE, name: 'posts.delete', rules: { id: /\d+/ } }],
96
+ [factoryPostListByAuthorEventHandler, { isFactory: true, path: '/posts/authors/:authorId(\\d+)', method: GET, name: 'posts.list-by-author' }],
97
+ [factoryPostShowEventHandler, { isFactory: true, path: '/posts/:post@id(\\d+)', method: GET, name: 'posts.show', bindings: { user: postResolveRouteBinding } }]
98
+ ]
99
+ )
@@ -0,0 +1,83 @@
1
+ import { defineRoutes, POST } from '@stone-js/router'
2
+ import { IncomingHttpEvent } from '@stone-js/http-core'
3
+ import { FunctionalEventHandler, ILogger } from '@stone-js/core'
4
+ import { ISecurityService } from '../services/contracts/ISecurityService'
5
+ import { UserChangePassword, UserLogin, UserRegister, UserToken } from '../models/User'
6
+
7
+ /**
8
+ * Security Event Handler Options
9
+ */
10
+ export interface SecurityEventHandlerOptions {
11
+ logger: ILogger
12
+ securityService: ISecurityService
13
+ }
14
+
15
+ /**
16
+ * Login a user
17
+ */
18
+ export function factoryLoginEventHandler ({ securityService }: SecurityEventHandlerOptions): FunctionalEventHandler<IncomingHttpEvent> {
19
+ return async function (event: IncomingHttpEvent): Promise<UserToken> {
20
+ return await securityService.login(
21
+ event,
22
+ event.getBody<UserLogin>({ email: '', password: '' })
23
+ )
24
+ }
25
+ }
26
+
27
+ /**
28
+ * Logout a user
29
+ */
30
+ export function factoryLogoutEventHandler ({ securityService }: SecurityEventHandlerOptions): FunctionalEventHandler<IncomingHttpEvent> {
31
+ return async function (event: IncomingHttpEvent): Promise<void> {
32
+ await securityService.logout(
33
+ event.get<string>('Authorization', '').replace('Bearer ', '')
34
+ )
35
+ }
36
+ }
37
+
38
+ /**
39
+ * Refresh a token
40
+ */
41
+ export function factoryRefreshEventHandler ({ securityService }: SecurityEventHandlerOptions): FunctionalEventHandler<IncomingHttpEvent> {
42
+ return async function (event: IncomingHttpEvent): Promise<UserToken> {
43
+ return await securityService.refresh(
44
+ event.get<string>('Authorization', '').replace('Bearer ', '')
45
+ )
46
+ }
47
+ }
48
+
49
+ /**
50
+ * Register a user
51
+ */
52
+ export function factoryRegisterEventHandler ({ securityService }: SecurityEventHandlerOptions): FunctionalEventHandler<IncomingHttpEvent> {
53
+ return async function (event: IncomingHttpEvent): Promise<void> {
54
+ await securityService.register(
55
+ event.getBody<UserRegister>({} as any)
56
+ )
57
+ }
58
+ }
59
+
60
+ /**
61
+ * Change password
62
+ */
63
+ export function factoryChangePasswordEventHandler ({ securityService }: SecurityEventHandlerOptions): FunctionalEventHandler<IncomingHttpEvent> {
64
+ return async function (event: IncomingHttpEvent): Promise<void> {
65
+ await securityService.changePassword(
66
+ event.getUser(),
67
+ event.getBody<UserChangePassword>()
68
+ )
69
+ }
70
+ }
71
+
72
+ /**
73
+ * Security Event Handler
74
+ */
75
+ export const SecurityEventHandler = defineRoutes(
76
+ [
77
+ [factoryLoginEventHandler, { isFactory: true, path: '/login', method: POST, name: 'login' }],
78
+ [factoryLogoutEventHandler, { isFactory: true, path: '/logout', method: POST, name: 'logout' }],
79
+ [factoryRefreshEventHandler, { isFactory: true, path: '/refresh', method: POST, name: 'refresh' }],
80
+ [factoryRegisterEventHandler, { isFactory: true, path: '/register', method: POST, name: 'register' }],
81
+ [factoryChangePasswordEventHandler, { isFactory: true, path: '/change-password', method: POST, name: 'change-password' }]
82
+ ]
83
+ )
@@ -0,0 +1,132 @@
1
+ import { File } from '@stone-js/filesystem'
2
+ import { IncomingHttpEvent } from '@stone-js/http-core'
3
+ import { User, UserModel, UserResponse } from '../models/User'
4
+ import { FunctionalEventHandler, ILogger } from '@stone-js/core'
5
+ import { IUserService } from '../services/contracts/IUserService'
6
+ import { userResolveRouteBinding } from '../services/UserService'
7
+ import { defineRoutes, DELETE, GET, PATCH, POST } from '@stone-js/router'
8
+
9
+ /**
10
+ * User Event Handler Options
11
+ */
12
+ export interface UserEventHandlerOptions {
13
+ logger: ILogger
14
+ userService: IUserService
15
+ }
16
+
17
+ /**
18
+ * List all users
19
+ */
20
+ export function factoryUserListEventHandler ({ userService }: UserEventHandlerOptions): FunctionalEventHandler<IncomingHttpEvent> {
21
+ return async function (event: IncomingHttpEvent): Promise<UserResponse[]> {
22
+ return await userService.list(event.get<number>('limit', 10))
23
+ }
24
+ }
25
+
26
+ /**
27
+ * Show current user
28
+ */
29
+ export function factoryUserShowCurrentEventHandler (): FunctionalEventHandler<IncomingHttpEvent> {
30
+ return function (event: IncomingHttpEvent): UserResponse {
31
+ const user = { ...event.getUser<UserModel>(), password: undefined }
32
+ return user as UserResponse
33
+ }
34
+ }
35
+
36
+ /**
37
+ * Show a user
38
+ */
39
+ export function factoryUserShowEventHandler (): FunctionalEventHandler<IncomingHttpEvent> {
40
+ return function (event: IncomingHttpEvent): UserResponse {
41
+ return event.get<UserResponse>('user', {} as unknown as UserResponse)
42
+ }
43
+ }
44
+
45
+ /**
46
+ * Show a user avatar
47
+ */
48
+ export function factoryUserAvatarEventHandler (): FunctionalEventHandler<IncomingHttpEvent> {
49
+ return function (event: IncomingHttpEvent): File {
50
+ const user = event.get<UserResponse>('user', {} as unknown as UserResponse)
51
+ return File.create(`public/uploads/${String(user.avatar)}`)
52
+ }
53
+ }
54
+
55
+ /**
56
+ * Create a user
57
+ */
58
+ export function factoryUserCreateEventHandler ({ userService }: UserEventHandlerOptions): FunctionalEventHandler<IncomingHttpEvent> {
59
+ return async function (event: IncomingHttpEvent): Promise<{ id?: number }> {
60
+ const id = await userService.create(handleFileUpload(event))
61
+ return { id: Number(id) }
62
+ }
63
+ }
64
+
65
+ /**
66
+ * Update the current user
67
+ */
68
+ export function factoryUserUpdateCurrentEventHandler ({ userService }: UserEventHandlerOptions): FunctionalEventHandler<IncomingHttpEvent> {
69
+ return async function (event: IncomingHttpEvent): Promise<UserResponse> {
70
+ return await userService.update(
71
+ event.getUser<UserModel>()?.id ?? 0,
72
+ handleFileUpload(event)
73
+ )
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Update a user
79
+ */
80
+ export function factoryUserUpdateEventHandler ({ userService }: UserEventHandlerOptions): FunctionalEventHandler<IncomingHttpEvent> {
81
+ return async function (event: IncomingHttpEvent): Promise<UserResponse> {
82
+ return await userService.update(
83
+ event.get<number>('id', 0),
84
+ handleFileUpload(event)
85
+ )
86
+ }
87
+ }
88
+
89
+ /**
90
+ * Delete a user
91
+ */
92
+ export function factoryUserDeleteEventHandler ({ logger, userService }: UserEventHandlerOptions): FunctionalEventHandler<IncomingHttpEvent> {
93
+ return async function (event: IncomingHttpEvent): Promise<{ statusCode: number }> {
94
+ await userService.delete(event.get<number>('id', 0))
95
+ logger.info(`User deleted: ${String(event.get<number>('id'))}, by user: ${String(event.getUser<User>()?.id)}`)
96
+ return { statusCode: 204 }
97
+ }
98
+ }
99
+
100
+ /**
101
+ * Handle file upload
102
+ *
103
+ * @param event - IncomingHttpEvent
104
+ * @param payload - UserModel
105
+ */
106
+ function handleFileUpload (event: IncomingHttpEvent): UserModel {
107
+ const payload = event.getBody<UserModel>({} as any)
108
+
109
+ if (event.hasFile('avatar')) {
110
+ const file = event.getFile('avatar')?.[0]
111
+ payload.avatar = `${crypto.randomUUID()}-${String(file?.getClientOriginalName())}`
112
+ file?.move('public/uploads', payload.avatar)
113
+ }
114
+
115
+ return payload
116
+ }
117
+
118
+ /**
119
+ * User Event Handler
120
+ */
121
+ export const UserEventHandler = defineRoutes(
122
+ [
123
+ [factoryUserListEventHandler, { isFactory: true, path: '/users/', method: GET, name: 'users.list' }],
124
+ [factoryUserCreateEventHandler, { isFactory: true, path: '/users/', method: POST, name: 'users.create' }],
125
+ [factoryUserDeleteEventHandler, { isFactory: true, path: '/users/:id', method: DELETE, name: 'users.delete' }],
126
+ [factoryUserShowCurrentEventHandler, { isFactory: true, path: '/users/me', method: GET, name: 'users.show-current' }],
127
+ [factoryUserUpdateCurrentEventHandler, { isFactory: true, path: '/users/me', method: PATCH, name: 'users.update-current' }],
128
+ [factoryUserUpdateEventHandler, { isFactory: true, path: '/users/:id', method: PATCH, name: 'users.update', rules: { id: /\d+/ } }],
129
+ [factoryUserShowEventHandler, { isFactory: true, path: '/users/:user@id(\\d+)', method: GET, name: 'users.show', bindings: { user: userResolveRouteBinding } }],
130
+ [factoryUserAvatarEventHandler, { isFactory: true, path: '/users/:user@id(\\d+)/avatar', method: GET, name: 'users.avatar', bindings: { user: userResolveRouteBinding } }]
131
+ ]
132
+ )
@@ -0,0 +1,23 @@
1
+ import { UserEvent } from '../events/UserEvent'
2
+ import { defineEventListener, FunctionalEventListener, ILogger } from '@stone-js/core'
3
+
4
+ /**
5
+ * User Event Listener Options
6
+ */
7
+ export interface UserEventListenerOptions {
8
+ logger: ILogger
9
+ }
10
+
11
+ /**
12
+ * Factory User event listener
13
+ */
14
+ export function fatoryUserEventListener ({ logger }: UserEventListenerOptions): FunctionalEventListener<UserEvent> {
15
+ return function (event: UserEvent): void {
16
+ logger.info('User event listener', event)
17
+ }
18
+ }
19
+
20
+ /**
21
+ * User event listener Type
22
+ */
23
+ export const UserEventListener = defineEventListener(fatoryUserEventListener, { isFactory: true, event: UserEvent.USER_CREATED })