@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.
- package/LICENSE +21 -0
- package/README.md +101 -0
- package/basic-react-declarative/LICENSE +21 -0
- package/basic-react-declarative/README.md +39 -0
- package/basic-react-declarative/app/Application.tsx +89 -0
- package/basic-react-declarative/assets/css/index.css +26 -0
- package/basic-react-declarative/package.json +39 -0
- package/basic-react-declarative/public/logo.png +0 -0
- package/basic-react-declarative/tests/Application.spec.ts +96 -0
- package/basic-react-declarative/tests/__snapshots__/Application.spec.ts.snap +3 -0
- package/basic-react-declarative/tsconfig.json +33 -0
- package/basic-react-declarative/vitest.config.ts +24 -0
- package/basic-react-imperative/LICENSE +21 -0
- package/basic-react-imperative/README.md +39 -0
- package/basic-react-imperative/app/Application.tsx +89 -0
- package/basic-react-imperative/assets/css/index.css +26 -0
- package/basic-react-imperative/package.json +39 -0
- package/basic-react-imperative/public/logo.png +0 -0
- package/basic-react-imperative/tests/Application.spec.ts +80 -0
- package/basic-react-imperative/tests/__snapshots__/Application.spec.ts.snap +3 -0
- package/basic-react-imperative/tsconfig.json +33 -0
- package/basic-react-imperative/vitest.config.ts +24 -0
- package/basic-service-declarative/LICENSE +21 -0
- package/basic-service-declarative/README.md +39 -0
- package/basic-service-declarative/app/Application.ts +56 -0
- package/basic-service-declarative/package.json +34 -0
- package/basic-service-declarative/tests/Application.spec.ts +60 -0
- package/basic-service-declarative/tsconfig.json +25 -0
- package/basic-service-declarative/vitest.config.ts +24 -0
- package/basic-service-imperative/LICENSE +21 -0
- package/basic-service-imperative/README.md +39 -0
- package/basic-service-imperative/app/Application.ts +60 -0
- package/basic-service-imperative/package.json +34 -0
- package/basic-service-imperative/tests/Application.spec.ts +52 -0
- package/basic-service-imperative/tsconfig.json +25 -0
- package/basic-service-imperative/vitest.config.ts +24 -0
- package/continuum-showcase/README.md +44 -0
- package/continuum-showcase/app/env.d.ts +34 -0
- package/continuum-showcase/app/layouts/MainLayout.tsx +20 -0
- package/continuum-showcase/app/pages/BlogPostPage.tsx +62 -0
- package/continuum-showcase/app/pages/HomePage.tsx +58 -0
- package/continuum-showcase/package.json +25 -0
- package/continuum-showcase/stone.config.mjs +20 -0
- package/full-react-declarative/.env.example +1 -0
- package/full-react-declarative/.env.public.example +1 -0
- package/full-react-declarative/LICENSE +21 -0
- package/full-react-declarative/README.md +39 -0
- package/full-react-declarative/app/Application.ts +85 -0
- package/full-react-declarative/app/clients/AxiosClient.ts +136 -0
- package/full-react-declarative/app/clients/CommentClient.ts +61 -0
- package/full-react-declarative/app/clients/PostClient.ts +90 -0
- package/full-react-declarative/app/clients/SecurityClient.ts +66 -0
- package/full-react-declarative/app/clients/UserClient.ts +97 -0
- package/full-react-declarative/app/components/ChangePasswordForm/ChangePasswordForm.tsx +50 -0
- package/full-react-declarative/app/components/CommentForm/CommentForm.tsx +37 -0
- package/full-react-declarative/app/components/CommentItem/CommentItem.tsx +42 -0
- package/full-react-declarative/app/components/CommentWidget/CommentWidget.tsx +95 -0
- package/full-react-declarative/app/components/LoginForm/LoginForm.css +0 -0
- package/full-react-declarative/app/components/LoginForm/LoginForm.tsx +71 -0
- package/full-react-declarative/app/components/PostDetails/PostDetails.tsx +44 -0
- package/full-react-declarative/app/components/PostForm/PostForm.css +0 -0
- package/full-react-declarative/app/components/PostForm/PostForm.tsx +47 -0
- package/full-react-declarative/app/components/PostItem/PostItem.css +0 -0
- package/full-react-declarative/app/components/PostItem/PostItem.tsx +34 -0
- package/full-react-declarative/app/components/RegisterForm/RegisterForm.tsx +110 -0
- package/full-react-declarative/app/components/UserForm/UserForm.tsx +47 -0
- package/full-react-declarative/app/configurations/AppConfiguration.ts +17 -0
- package/full-react-declarative/app/error-pages/DefaultErrorPage/DefaultErrorPage.css +0 -0
- package/full-react-declarative/app/error-pages/DefaultErrorPage/DefaultErrorPage.tsx +52 -0
- package/full-react-declarative/app/error-pages/ForbiddenErrorPage/ForbiddenErrorPage.css +0 -0
- package/full-react-declarative/app/error-pages/ForbiddenErrorPage/ForbiddenErrorPage.tsx +53 -0
- package/full-react-declarative/app/error-pages/NotFoundErrorPage/NotFoundErrorPage.css +0 -0
- package/full-react-declarative/app/error-pages/NotFoundErrorPage/NotFoundErrorPage.tsx +54 -0
- package/full-react-declarative/app/error-pages/UnauthorizedErrorPage/UnauthorizedErrorPage.css +0 -0
- package/full-react-declarative/app/error-pages/UnauthorizedErrorPage/UnauthorizedErrorPage.tsx +53 -0
- package/full-react-declarative/app/errors/ForbiddenError.ts +11 -0
- package/full-react-declarative/app/errors/PostNotFoundError.ts +11 -0
- package/full-react-declarative/app/errors/UnauthorizedError.ts +11 -0
- package/full-react-declarative/app/errors/UserNotFoundError.ts +11 -0
- package/full-react-declarative/app/events/UserEvent.ts +18 -0
- package/full-react-declarative/app/layout/AppLayout/AppLayout.css +20 -0
- package/full-react-declarative/app/layout/AppLayout/AppLayout.tsx +101 -0
- package/full-react-declarative/app/layout/ErrorLayout/ErrorLayout.css +0 -0
- package/full-react-declarative/app/layout/ErrorLayout/ErrorLayout.tsx +41 -0
- package/full-react-declarative/app/layout/SecurityLayout/SecurityLayout.css +0 -0
- package/full-react-declarative/app/layout/SecurityLayout/SecurityLayout.tsx +39 -0
- package/full-react-declarative/app/middleware/AuthMiddleware.ts +85 -0
- package/full-react-declarative/app/models/Comment.ts +31 -0
- package/full-react-declarative/app/models/Post.ts +22 -0
- package/full-react-declarative/app/models/User.ts +62 -0
- package/full-react-declarative/app/pages/home/HomePage.tsx +44 -0
- package/full-react-declarative/app/pages/login/LoginPage.css +0 -0
- package/full-react-declarative/app/pages/login/LoginPage.tsx +73 -0
- package/full-react-declarative/app/pages/posts/CreatePostPage/CreatePostPage.css +0 -0
- package/full-react-declarative/app/pages/posts/CreatePostPage/CreatePostPage.tsx +67 -0
- package/full-react-declarative/app/pages/posts/PostPage/PostPage.tsx +60 -0
- package/full-react-declarative/app/pages/posts/ShowPostPage/ShowPostPage.tsx +83 -0
- package/full-react-declarative/app/pages/posts/UpdatePostPage/UpdatePostPage.tsx +77 -0
- package/full-react-declarative/app/pages/register/RegisterPage.tsx +68 -0
- package/full-react-declarative/app/pages/users/CreateUserPage.tsx +48 -0
- package/full-react-declarative/app/pages/users/ShowUserPage.tsx +62 -0
- package/full-react-declarative/app/pages/users/UpdateUserPage.tsx +57 -0
- package/full-react-declarative/app/pages/users/UserPage.tsx +57 -0
- package/full-react-declarative/app/providers/AppServiceProvider.ts +43 -0
- package/full-react-declarative/app/services/CommentService.ts +59 -0
- package/full-react-declarative/app/services/PostService.ts +108 -0
- package/full-react-declarative/app/services/SecurityService.ts +80 -0
- package/full-react-declarative/app/services/TokenService.ts +116 -0
- package/full-react-declarative/app/services/UserService.ts +126 -0
- package/full-react-declarative/app/utils.ts +17 -0
- package/full-react-declarative/assets/css/index.css +301 -0
- package/full-react-declarative/assets/img/logo.png +0 -0
- package/full-react-declarative/package.json +46 -0
- package/full-react-declarative/public/logo.png +0 -0
- package/full-react-declarative/public/vite.svg +1 -0
- package/full-react-declarative/stone.config.mjs +12 -0
- package/full-react-declarative/tsconfig.json +29 -0
- package/full-react-declarative/vitest.config.ts +24 -0
- package/full-react-imperative/.env.example +1 -0
- package/full-react-imperative/.env.public.example +1 -0
- package/full-react-imperative/LICENSE +21 -0
- package/full-react-imperative/README.md +39 -0
- package/full-react-imperative/app/Application.ts +79 -0
- package/full-react-imperative/app/clients/AxiosClient.ts +124 -0
- package/full-react-imperative/app/clients/CommentClient.ts +57 -0
- package/full-react-imperative/app/clients/PostClient.ts +86 -0
- package/full-react-imperative/app/clients/SecurityClient.ts +60 -0
- package/full-react-imperative/app/clients/UserClient.ts +93 -0
- package/full-react-imperative/app/clients/contracts/IAxiosClient.ts +97 -0
- package/full-react-imperative/app/clients/contracts/ICommentClient.ts +31 -0
- package/full-react-imperative/app/clients/contracts/IPostClient.ts +55 -0
- package/full-react-imperative/app/clients/contracts/ISecurityClient.ts +33 -0
- package/full-react-imperative/app/clients/contracts/IUserClient.ts +61 -0
- package/full-react-imperative/app/components/ChangePasswordForm/ChangePasswordForm.tsx +50 -0
- package/full-react-imperative/app/components/CommentForm/CommentForm.css +35 -0
- package/full-react-imperative/app/components/CommentForm/CommentForm.tsx +45 -0
- package/full-react-imperative/app/components/CommentItem/CommentItem.css +74 -0
- package/full-react-imperative/app/components/CommentItem/CommentItem.tsx +62 -0
- package/full-react-imperative/app/components/CommentWidget/CommentWidget.css +25 -0
- package/full-react-imperative/app/components/CommentWidget/CommentWidget.tsx +119 -0
- package/full-react-imperative/app/components/Dropdown/Dropdown.css +52 -0
- package/full-react-imperative/app/components/Dropdown/Dropdown.tsx +40 -0
- package/full-react-imperative/app/components/LayoutHeader/LayoutHeader.css +62 -0
- package/full-react-imperative/app/components/LayoutHeader/LayoutHeader.tsx +61 -0
- package/full-react-imperative/app/components/LayoutLeftMenu/LayoutLeftMenu.css +25 -0
- package/full-react-imperative/app/components/LayoutLeftMenu/LayoutLeftMenu.tsx +32 -0
- package/full-react-imperative/app/components/LoginForm/LoginForm.css +0 -0
- package/full-react-imperative/app/components/LoginForm/LoginForm.tsx +71 -0
- package/full-react-imperative/app/components/NotificationBadge/NotificationBadge.css +41 -0
- package/full-react-imperative/app/components/NotificationBadge/NotificationBadge.tsx +63 -0
- package/full-react-imperative/app/components/PostCard/PostCard.css +129 -0
- package/full-react-imperative/app/components/PostCard/PostCard.tsx +65 -0
- package/full-react-imperative/app/components/PostForm/PostForm.css +92 -0
- package/full-react-imperative/app/components/PostForm/PostForm.tsx +76 -0
- package/full-react-imperative/app/components/PostSkeleton/PostSkeleton.css +65 -0
- package/full-react-imperative/app/components/PostSkeleton/PostSkeleton.tsx +25 -0
- package/full-react-imperative/app/components/PostWidget/PostWidget.tsx +95 -0
- package/full-react-imperative/app/components/ProfileForm/ProfileForm.css +84 -0
- package/full-react-imperative/app/components/ProfileForm/ProfileForm.tsx +79 -0
- package/full-react-imperative/app/components/RegisterForm/RegisterForm.tsx +110 -0
- package/full-react-imperative/app/components/RightAsidePanel/RightAsidePanel.css +32 -0
- package/full-react-imperative/app/components/RightAsidePanel/RightAsidePanel.tsx +62 -0
- package/full-react-imperative/app/components/SettingsForm/SettingsForm.css +126 -0
- package/full-react-imperative/app/components/SettingsForm/SettingsForm.tsx +86 -0
- package/full-react-imperative/app/components/UserAvatar/UserAvatar.css +18 -0
- package/full-react-imperative/app/components/UserAvatar/UserAvatar.tsx +60 -0
- package/full-react-imperative/app/components/UserBadge/UserBadge.css +33 -0
- package/full-react-imperative/app/components/UserBadge/UserBadge.tsx +49 -0
- package/full-react-imperative/app/components/UserCard/UserCard.css +70 -0
- package/full-react-imperative/app/components/UserCard/UserCard.tsx +43 -0
- package/full-react-imperative/app/configurations/AppConfiguration.ts +19 -0
- package/full-react-imperative/app/error-pages/DefaultErrorPage/DefaultErrorPage.css +0 -0
- package/full-react-imperative/app/error-pages/DefaultErrorPage/DefaultErrorPage.tsx +41 -0
- package/full-react-imperative/app/error-pages/ForbiddenErrorPage/ForbiddenErrorPage.css +0 -0
- package/full-react-imperative/app/error-pages/ForbiddenErrorPage/ForbiddenErrorPage.tsx +42 -0
- package/full-react-imperative/app/error-pages/NotFoundErrorPage/NotFoundErrorPage.css +0 -0
- package/full-react-imperative/app/error-pages/NotFoundErrorPage/NotFoundErrorPage.tsx +49 -0
- package/full-react-imperative/app/error-pages/UnauthorizedErrorPage/UnauthorizedErrorPage.css +0 -0
- package/full-react-imperative/app/error-pages/UnauthorizedErrorPage/UnauthorizedErrorPage.tsx +42 -0
- package/full-react-imperative/app/errors/ForbiddenError.ts +11 -0
- package/full-react-imperative/app/errors/PostNotFoundError.ts +11 -0
- package/full-react-imperative/app/errors/UnauthorizedError.ts +11 -0
- package/full-react-imperative/app/errors/UserNotFoundError.ts +11 -0
- package/full-react-imperative/app/events/UserEvent.ts +18 -0
- package/full-react-imperative/app/layout/AppLayout/AppLayout.css +106 -0
- package/full-react-imperative/app/layout/AppLayout/AppLayout.tsx +48 -0
- package/full-react-imperative/app/layout/ErrorLayout/ErrorLayout.css +0 -0
- package/full-react-imperative/app/layout/ErrorLayout/ErrorLayout.tsx +45 -0
- package/full-react-imperative/app/layout/SecurityLayout/SecurityLayout.css +0 -0
- package/full-react-imperative/app/layout/SecurityLayout/SecurityLayout.tsx +43 -0
- package/full-react-imperative/app/layout/SettingsLayout/SettingsLayout.css +99 -0
- package/full-react-imperative/app/layout/SettingsLayout/SettingsLayout.tsx +44 -0
- package/full-react-imperative/app/middleware/AuthMiddleware.ts +56 -0
- package/full-react-imperative/app/models/Comment.ts +35 -0
- package/full-react-imperative/app/models/Post.ts +27 -0
- package/full-react-imperative/app/models/User.ts +68 -0
- package/full-react-imperative/app/pages/Settings/SettingsPage.tsx +23 -0
- package/full-react-imperative/app/pages/home/HomePage.tsx +58 -0
- package/full-react-imperative/app/pages/login/LoginPage.css +0 -0
- package/full-react-imperative/app/pages/login/LoginPage.tsx +65 -0
- package/full-react-imperative/app/pages/posts/ShowPostPage/ShowPostPage.tsx +64 -0
- package/full-react-imperative/app/pages/posts/UpdatePostPage/UpdatePostPage.tsx +56 -0
- package/full-react-imperative/app/pages/register/RegisterPage.tsx +71 -0
- package/full-react-imperative/app/pages/users/ShowUserPage/ShowUserPage.css +64 -0
- package/full-react-imperative/app/pages/users/ShowUserPage/ShowUserPage.tsx +104 -0
- package/full-react-imperative/app/pages/users/UpdateUserPage/UpdateUserPage.css +0 -0
- package/full-react-imperative/app/pages/users/UpdateUserPage/UpdateUserPage.tsx +43 -0
- package/full-react-imperative/app/pages/users/UserPage/UserPage.css +31 -0
- package/full-react-imperative/app/pages/users/UserPage/UserPage.tsx +59 -0
- package/full-react-imperative/app/pages/utils.ts +67 -0
- package/full-react-imperative/app/providers/AppServiceProvider.ts +40 -0
- package/full-react-imperative/app/services/CommentService.ts +53 -0
- package/full-react-imperative/app/services/PostService.ts +102 -0
- package/full-react-imperative/app/services/SecurityService.ts +72 -0
- package/full-react-imperative/app/services/TokenService.ts +108 -0
- package/full-react-imperative/app/services/UserService.ts +115 -0
- package/full-react-imperative/app/services/contracts/ICommentService.ts +30 -0
- package/full-react-imperative/app/services/contracts/IPostService.ts +65 -0
- package/full-react-imperative/app/services/contracts/ISecurityService.ts +39 -0
- package/full-react-imperative/app/services/contracts/ITokenService.ts +44 -0
- package/full-react-imperative/app/services/contracts/IUserService.ts +70 -0
- package/full-react-imperative/app/utils.ts +31 -0
- package/full-react-imperative/assets/css/index.css +301 -0
- package/full-react-imperative/assets/img/logo.png +0 -0
- package/full-react-imperative/package.json +48 -0
- package/full-react-imperative/public/logo.png +0 -0
- package/full-react-imperative/public/vite.svg +1 -0
- package/full-react-imperative/stone.config.mjs +12 -0
- package/full-react-imperative/tsconfig.json +29 -0
- package/full-react-imperative/vitest.config.ts +24 -0
- package/full-service-declarative/.env.example +2 -0
- package/full-service-declarative/LICENSE +21 -0
- package/full-service-declarative/README.md +65 -0
- package/full-service-declarative/app/Application.ts +327 -0
- package/full-service-declarative/app/commands/ListSessionsCommand.ts +70 -0
- package/full-service-declarative/app/commands/ListUsersCommand.ts +56 -0
- package/full-service-declarative/app/configurations/AppConfiguration.ts +52 -0
- package/full-service-declarative/app/configurations/UserLiveConfiguration.ts +28 -0
- package/full-service-declarative/app/database/DatabaseClient.ts +43 -0
- package/full-service-declarative/app/database/schema.ts +43 -0
- package/full-service-declarative/app/error-handlers/SecurityErrorHandler.ts +61 -0
- package/full-service-declarative/app/errors/CredentialsError.ts +12 -0
- package/full-service-declarative/app/events/UserEvent.ts +17 -0
- package/full-service-declarative/app/handlers/CommentEventHandler.ts +103 -0
- package/full-service-declarative/app/handlers/PostEventHandler.ts +104 -0
- package/full-service-declarative/app/handlers/SecurityEventHandler.ts +108 -0
- package/full-service-declarative/app/handlers/UserEventHandler.ts +159 -0
- package/full-service-declarative/app/listeners/UserEventListener.ts +35 -0
- package/full-service-declarative/app/middleware/AuthMiddleware.ts +54 -0
- package/full-service-declarative/app/models/Comment.ts +38 -0
- package/full-service-declarative/app/models/Post.ts +50 -0
- package/full-service-declarative/app/models/Session.ts +14 -0
- package/full-service-declarative/app/models/User.ts +77 -0
- package/full-service-declarative/app/providers/DrizzleServiceProvider.ts +59 -0
- package/full-service-declarative/app/repositories/CommentRepository.ts +155 -0
- package/full-service-declarative/app/repositories/PostRepository.ts +138 -0
- package/full-service-declarative/app/repositories/SessionRepository.ts +123 -0
- package/full-service-declarative/app/repositories/UserRepository.ts +99 -0
- package/full-service-declarative/app/services/CommentService.ts +112 -0
- package/full-service-declarative/app/services/PostService.ts +100 -0
- package/full-service-declarative/app/services/SecurityService.ts +232 -0
- package/full-service-declarative/app/services/SessionService.ts +130 -0
- package/full-service-declarative/app/services/UserService.ts +110 -0
- package/full-service-declarative/app/subscribers/UserEventSubscriber.ts +38 -0
- package/full-service-declarative/drizzle.config.ts +13 -0
- package/full-service-declarative/package.json +48 -0
- package/full-service-declarative/stone.config.mjs +12 -0
- package/full-service-declarative/tsconfig.json +25 -0
- package/full-service-declarative/vitest.config.ts +24 -0
- package/full-service-imperative/.env.example +2 -0
- package/full-service-imperative/LICENSE +21 -0
- package/full-service-imperative/README.md +65 -0
- package/full-service-imperative/app/Application.ts +317 -0
- package/full-service-imperative/app/commands/ListSessionsCommand.ts +54 -0
- package/full-service-imperative/app/commands/ListUsersCommand.ts +42 -0
- package/full-service-imperative/app/configurations/AppConfiguration.ts +44 -0
- package/full-service-imperative/app/configurations/UserLiveConfiguration.ts +26 -0
- package/full-service-imperative/app/database/DatabaseClient.ts +39 -0
- package/full-service-imperative/app/database/schema.ts +43 -0
- package/full-service-imperative/app/error-handlers/SecurityErrorHandler.ts +58 -0
- package/full-service-imperative/app/errors/CredentialsError.ts +12 -0
- package/full-service-imperative/app/events/UserEvent.ts +17 -0
- package/full-service-imperative/app/handlers/CommentEventHandler.ts +99 -0
- package/full-service-imperative/app/handlers/PostEventHandler.ts +99 -0
- package/full-service-imperative/app/handlers/SecurityEventHandler.ts +83 -0
- package/full-service-imperative/app/handlers/UserEventHandler.ts +132 -0
- package/full-service-imperative/app/listeners/UserEventListener.ts +23 -0
- package/full-service-imperative/app/middleware/AuthMiddleware.ts +44 -0
- package/full-service-imperative/app/models/Comment.ts +38 -0
- package/full-service-imperative/app/models/Post.ts +50 -0
- package/full-service-imperative/app/models/Session.ts +14 -0
- package/full-service-imperative/app/models/User.ts +77 -0
- package/full-service-imperative/app/providers/DrizzleServiceProvider.ts +56 -0
- package/full-service-imperative/app/repositories/CommentRepository.ts +148 -0
- package/full-service-imperative/app/repositories/PostRepository.ts +130 -0
- package/full-service-imperative/app/repositories/SessionRepository.ts +115 -0
- package/full-service-imperative/app/repositories/UserRepository.ts +91 -0
- package/full-service-imperative/app/repositories/contracts/ICommentRepository.ts +72 -0
- package/full-service-imperative/app/repositories/contracts/IPostRepository.ts +63 -0
- package/full-service-imperative/app/repositories/contracts/ISessionRepository.ts +65 -0
- package/full-service-imperative/app/repositories/contracts/IUserRepository.ts +55 -0
- package/full-service-imperative/app/services/CommentService.ts +109 -0
- package/full-service-imperative/app/services/PostService.ts +97 -0
- package/full-service-imperative/app/services/SecurityService.ts +224 -0
- package/full-service-imperative/app/services/SessionService.ts +124 -0
- package/full-service-imperative/app/services/UserService.ts +105 -0
- package/full-service-imperative/app/services/contracts/ICommentService.ts +62 -0
- package/full-service-imperative/app/services/contracts/IPostService.ts +53 -0
- package/full-service-imperative/app/services/contracts/ISecurityService.ts +97 -0
- package/full-service-imperative/app/services/contracts/ISessionService.ts +73 -0
- package/full-service-imperative/app/services/contracts/IUserService.ts +57 -0
- package/full-service-imperative/app/subscribers/UserEventSubscriber.ts +28 -0
- package/full-service-imperative/drizzle.config.ts +13 -0
- package/full-service-imperative/package.json +48 -0
- package/full-service-imperative/stone.config.mjs +12 -0
- package/full-service-imperative/tsconfig.json +25 -0
- package/full-service-imperative/vitest.config.ts +24 -0
- package/package.json +225 -0
- package/standard-react-declarative/LICENSE +21 -0
- package/standard-react-declarative/README.md +39 -0
- package/standard-react-declarative/app/Application.ts +45 -0
- package/standard-react-declarative/app/pages/welcome/WelcomePage.tsx +57 -0
- package/standard-react-declarative/app/services/WelcomeService.ts +38 -0
- package/standard-react-declarative/assets/css/index.css +26 -0
- package/standard-react-declarative/package.json +42 -0
- package/standard-react-declarative/tsconfig.json +33 -0
- package/standard-react-declarative/vitest.config.ts +24 -0
- package/standard-react-imperative/LICENSE +21 -0
- package/standard-react-imperative/README.md +39 -0
- package/standard-react-imperative/app/Application.ts +41 -0
- package/standard-react-imperative/app/pages/welcome/WelcomePage.tsx +50 -0
- package/standard-react-imperative/app/services/WelcomeService.ts +38 -0
- package/standard-react-imperative/assets/css/index.css +26 -0
- package/standard-react-imperative/package.json +42 -0
- package/standard-react-imperative/tsconfig.json +33 -0
- package/standard-react-imperative/vitest.config.ts +24 -0
- package/standard-service-declarative/LICENSE +21 -0
- package/standard-service-declarative/README.md +39 -0
- package/standard-service-declarative/app/Application.ts +39 -0
- package/standard-service-declarative/app/handlers/WelcomeEventHandler.ts +45 -0
- package/standard-service-declarative/app/services/WelcomeService.ts +38 -0
- package/standard-service-declarative/package.json +36 -0
- package/standard-service-declarative/tsconfig.json +25 -0
- package/standard-service-declarative/vitest.config.ts +24 -0
- package/standard-service-imperative/LICENSE +21 -0
- package/standard-service-imperative/README.md +39 -0
- package/standard-service-imperative/app/Application.ts +11 -0
- package/standard-service-imperative/app/handlers/welcomeEventHandler.ts +31 -0
- package/standard-service-imperative/app/services/welcomeService.ts +38 -0
- package/standard-service-imperative/package.json +35 -0
- package/standard-service-imperative/tsconfig.json +25 -0
- 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 })
|