@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,112 @@
|
|
|
1
|
+
import { NotFoundError } from '@stone-js/http-core'
|
|
2
|
+
import { Comment, CommentModel } from '../models/Comment'
|
|
3
|
+
import { IContainer, isNotEmpty, Service } from '@stone-js/core'
|
|
4
|
+
import { CommentRepository } from '../repositories/CommentRepository'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Comment Service Options
|
|
8
|
+
*/
|
|
9
|
+
export interface CommentServiceOptions {
|
|
10
|
+
commentRepository: CommentRepository
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Comment Service
|
|
15
|
+
*
|
|
16
|
+
* Manages comment-related business logic.
|
|
17
|
+
*/
|
|
18
|
+
@Service({ alias: 'commentService' })
|
|
19
|
+
export class CommentService {
|
|
20
|
+
private readonly commentRepository: CommentRepository
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Resolve route binding.
|
|
24
|
+
*
|
|
25
|
+
* @param _key - The key of the binding.
|
|
26
|
+
* @param value - The value of the binding.
|
|
27
|
+
* @param container - The container instance.
|
|
28
|
+
*/
|
|
29
|
+
static async resolveRouteBinding (_key: string, value: any, container: IContainer): Promise<Comment | undefined> {
|
|
30
|
+
const commentService = container.resolve<CommentService>('commentService')
|
|
31
|
+
return await commentService.findById(Number(value))
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Create a new Comment Service.
|
|
36
|
+
*/
|
|
37
|
+
constructor ({ commentRepository }: CommentServiceOptions) {
|
|
38
|
+
this.commentRepository = commentRepository
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* List comments.
|
|
43
|
+
*
|
|
44
|
+
* @param limit - The limit of comments to list.
|
|
45
|
+
*/
|
|
46
|
+
async list (limit: number = 10): Promise<Comment[]> {
|
|
47
|
+
return await this.commentRepository.list({ limit })
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Retrieve comments for a specific post.
|
|
52
|
+
*
|
|
53
|
+
* @param postId - The post ID.
|
|
54
|
+
* @param limit - The number of comments to retrieve.
|
|
55
|
+
*/
|
|
56
|
+
async listByPost (postId: number, limit: number = 10): Promise<Comment[]> {
|
|
57
|
+
return await this.commentRepository.list({ postId, limit })
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Retrieve comments by a specific user.
|
|
62
|
+
*
|
|
63
|
+
* @param authorId - The author ID.
|
|
64
|
+
* @param limit - The number of comments to retrieve.
|
|
65
|
+
*/
|
|
66
|
+
async listByAuthor (authorId: number, limit: number = 10): Promise<Comment[]> {
|
|
67
|
+
return await this.commentRepository.list({ authorId, limit })
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Find a comment by ID.
|
|
72
|
+
*
|
|
73
|
+
* @param id - The ID of the comment.
|
|
74
|
+
* @returns The found comment.
|
|
75
|
+
*/
|
|
76
|
+
async findById (id: number): Promise<Comment> {
|
|
77
|
+
const comment = await this.commentRepository.findById(id)
|
|
78
|
+
if (isNotEmpty<CommentModel>(comment)) return comment
|
|
79
|
+
throw new NotFoundError(`Comment with ID ${id} not found`)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Create a new comment.
|
|
84
|
+
*
|
|
85
|
+
* @param payload - The comment data to create.
|
|
86
|
+
*/
|
|
87
|
+
async create (payload: Omit<CommentModel, 'id'>): Promise<bigint | undefined> {
|
|
88
|
+
return await this.commentRepository.create({ ...payload, createdAt: Date.now() })
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Update an existing comment.
|
|
93
|
+
*
|
|
94
|
+
* @param id - The ID of the comment.
|
|
95
|
+
* @param payload - The comment data to update.
|
|
96
|
+
* @returns The updated comment.
|
|
97
|
+
*/
|
|
98
|
+
async update (id: number, payload: Partial<CommentModel>): Promise<Comment> {
|
|
99
|
+
const comment = await this.commentRepository.update(id, payload)
|
|
100
|
+
if (isNotEmpty<CommentModel>(comment)) return comment
|
|
101
|
+
throw new NotFoundError(`Comment with ID ${id} not found`)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Delete a comment.
|
|
106
|
+
*
|
|
107
|
+
* @param id - The ID of the comment.
|
|
108
|
+
*/
|
|
109
|
+
async delete (id: number): Promise<boolean> {
|
|
110
|
+
return await this.commentRepository.delete(id)
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { NotFoundError } from '@stone-js/http-core'
|
|
2
|
+
import { PostRepository } from '../repositories/PostRepository'
|
|
3
|
+
import { IContainer, isNotEmpty, Service } from '@stone-js/core'
|
|
4
|
+
import { Post, PostCreateModel, PostModel } from '../models/Post'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Post Service Options
|
|
8
|
+
*/
|
|
9
|
+
export interface PostServiceOptions {
|
|
10
|
+
postRepository: PostRepository
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Post Service
|
|
15
|
+
*
|
|
16
|
+
* Manages post-related business logic.
|
|
17
|
+
*/
|
|
18
|
+
@Service({ alias: 'postService' })
|
|
19
|
+
export class PostService {
|
|
20
|
+
private readonly postRepository: PostRepository
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Resolve route binding.
|
|
24
|
+
*
|
|
25
|
+
* @param _key - The key of the binding.
|
|
26
|
+
* @param value - The value of the binding.
|
|
27
|
+
* @param container - The container instance.
|
|
28
|
+
*/
|
|
29
|
+
static async resolveRouteBinding (_key: string, value: any, container: IContainer): Promise<Post | undefined> {
|
|
30
|
+
const postService = container.resolve<PostService>('postService')
|
|
31
|
+
return await postService.findById(Number(value))
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Create a new Post Service.
|
|
36
|
+
*/
|
|
37
|
+
constructor ({ postRepository }: PostServiceOptions) {
|
|
38
|
+
this.postRepository = postRepository
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* List posts.
|
|
43
|
+
*
|
|
44
|
+
* @param limit - The limit of posts to list.
|
|
45
|
+
*/
|
|
46
|
+
async list (limit: number = 10): Promise<Post[]> {
|
|
47
|
+
return await this.postRepository.list({ limit })
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Retrieve posts by a specific user.
|
|
52
|
+
*
|
|
53
|
+
* @param authorId - The author ID.
|
|
54
|
+
* @param limit - The number of posts to retrieve.
|
|
55
|
+
*/
|
|
56
|
+
async listByAuthor (authorId: number, limit: number = 10): Promise<Post[]> {
|
|
57
|
+
return await this.postRepository.listByAuthor(authorId, limit)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Find a post by ID.
|
|
62
|
+
*
|
|
63
|
+
* @param id - The ID of the post.
|
|
64
|
+
* @returns The found post.
|
|
65
|
+
*/
|
|
66
|
+
async findById (id: number): Promise<Post> {
|
|
67
|
+
const post = await this.postRepository.findById(id)
|
|
68
|
+
if (isNotEmpty<PostModel>(post)) return post
|
|
69
|
+
throw new NotFoundError(`Post with ID ${id} not found`)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Create a new post.
|
|
74
|
+
*
|
|
75
|
+
* @param payload - The post data to create.
|
|
76
|
+
*/
|
|
77
|
+
async create (payload: Omit<PostCreateModel, 'id'>): Promise<bigint | undefined> {
|
|
78
|
+
return await this.postRepository.create({ ...payload, createdAt: Date.now(), updatedAt: Date.now() })
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Update an existing post.
|
|
83
|
+
*
|
|
84
|
+
* @param id - The ID of the post.
|
|
85
|
+
* @param payload - The post data to update.
|
|
86
|
+
* @returns The updated post.
|
|
87
|
+
*/
|
|
88
|
+
async update (id: number, payload: Partial<PostModel>): Promise<boolean> {
|
|
89
|
+
return await this.postRepository.update(id, payload)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Delete a post.
|
|
94
|
+
*
|
|
95
|
+
* @param id - The ID of the post.
|
|
96
|
+
*/
|
|
97
|
+
async delete (id: number): Promise<boolean> {
|
|
98
|
+
return await this.postRepository.delete(id)
|
|
99
|
+
}
|
|
100
|
+
}
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import bcrypt from 'bcryptjs'
|
|
2
|
+
import jwt from 'jsonwebtoken'
|
|
3
|
+
import { SessionService } from './SessionService'
|
|
4
|
+
import { UserRepository } from '../repositories/UserRepository'
|
|
5
|
+
import { BadCredentialsError } from '../errors/CredentialsError'
|
|
6
|
+
import { IBlueprint, isEmpty, isNotEmpty, Service } from '@stone-js/core'
|
|
7
|
+
import { BadRequestError, IncomingHttpEvent, UnauthorizedError } from '@stone-js/http-core'
|
|
8
|
+
import { UserLogin, UserToken, UserRegister, UserChangePassword, UserModel, UserTokenPayload } from '../models/User'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Security Service Options
|
|
12
|
+
*/
|
|
13
|
+
export interface SecurityServiceOptions {
|
|
14
|
+
blueprint: IBlueprint
|
|
15
|
+
userRepository: UserRepository
|
|
16
|
+
sessionService: SessionService
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Security Service
|
|
21
|
+
*
|
|
22
|
+
* @Service() decorator is used to define a new service
|
|
23
|
+
* @Service() is an alias of @Stone() decorator.
|
|
24
|
+
* The alias is required to get benefits of desctructuring Dependency Injection.
|
|
25
|
+
* And because the front-end class will be minified, we need to use alias to keep the class name.
|
|
26
|
+
*/
|
|
27
|
+
@Service({ alias: 'securityService' })
|
|
28
|
+
export class SecurityService {
|
|
29
|
+
private readonly blueprint: IBlueprint
|
|
30
|
+
private readonly userRepository: UserRepository
|
|
31
|
+
private readonly sessionService: SessionService
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Create a new Security Service
|
|
35
|
+
*/
|
|
36
|
+
constructor ({ blueprint, userRepository, sessionService }: SecurityServiceOptions) {
|
|
37
|
+
this.blueprint = blueprint
|
|
38
|
+
this.userRepository = userRepository
|
|
39
|
+
this.sessionService = sessionService
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Login a user
|
|
44
|
+
*
|
|
45
|
+
* @param credentials - The user to login
|
|
46
|
+
* @returns The user token
|
|
47
|
+
*/
|
|
48
|
+
async login (event: IncomingHttpEvent, credentials: UserLogin): Promise<UserToken> {
|
|
49
|
+
const user = await this.userRepository.findBy(credentials)
|
|
50
|
+
|
|
51
|
+
if (isEmpty(user)) {
|
|
52
|
+
throw new BadCredentialsError(`The user with email ${credentials.email} does not exist`)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (!(await this.comparePassword(credentials.password, user.password))) {
|
|
56
|
+
throw new BadCredentialsError('Invalid user password')
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
tokenType: 'bearer',
|
|
61
|
+
expiresIn: this.blueprint.get<number>('security.jwt.expiresIn', 3600),
|
|
62
|
+
accessToken: await this.generateToken(user, event.ip, event.userAgent)
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Refresh a token
|
|
68
|
+
*
|
|
69
|
+
* @param token - The token to refresh
|
|
70
|
+
*/
|
|
71
|
+
async refresh (token: string): Promise<UserToken> {
|
|
72
|
+
const payload = this.verifyToken(token)
|
|
73
|
+
const user = await this.userRepository.findById(payload.user.id)
|
|
74
|
+
|
|
75
|
+
if (isEmpty(user)) {
|
|
76
|
+
throw new UnauthorizedError('User not found')
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
tokenType: 'bearer',
|
|
81
|
+
expiresIn: this.blueprint.get<number>('security.jwt.expiresIn', 3600),
|
|
82
|
+
accessToken: await this.generateToken(user, payload.session.ip, payload.session.userAgent ?? undefined)
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Logout a user
|
|
88
|
+
*
|
|
89
|
+
* @param token - The token to logout
|
|
90
|
+
*/
|
|
91
|
+
async logout (token: string): Promise<void> {
|
|
92
|
+
const payload = this.verifyToken(token)
|
|
93
|
+
await this.sessionService.close(payload.session)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Authenticate a user
|
|
98
|
+
*
|
|
99
|
+
* @param token - The token to authenticate
|
|
100
|
+
* @returns The authenticated user
|
|
101
|
+
*/
|
|
102
|
+
async authenticate (token: string, ip: string, userAgent?: string): Promise<UserModel> {
|
|
103
|
+
const payload = this.verifyToken(token)
|
|
104
|
+
const user = await this.userRepository.findById(payload.user.id)
|
|
105
|
+
|
|
106
|
+
if (payload.session.ip !== ip) {
|
|
107
|
+
throw new UnauthorizedError('Invalid IP address')
|
|
108
|
+
} else if (payload.session.userAgent !== userAgent) {
|
|
109
|
+
throw new UnauthorizedError('Invalid user agent')
|
|
110
|
+
} else if (isEmpty(user)) {
|
|
111
|
+
throw new UnauthorizedError('User not found')
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
await this.sessionService.updateLastActivity(payload.session)
|
|
115
|
+
|
|
116
|
+
return user
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Hashes a password before storing it in the database.
|
|
121
|
+
*
|
|
122
|
+
* @param password - The plaintext password.
|
|
123
|
+
* @returns The hashed password.
|
|
124
|
+
*/
|
|
125
|
+
async hashPassword (password: string): Promise<string> {
|
|
126
|
+
return await bcrypt.hash(password, this.blueprint.get<number>('security.bcrypt.saltRounds', 10))
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Compares a plaintext password with a hashed password.
|
|
131
|
+
*
|
|
132
|
+
* @param password - The plaintext password.
|
|
133
|
+
* @param hashedPassword - The stored hashed password.
|
|
134
|
+
* @returns `true` if the password matches, otherwise `false`.
|
|
135
|
+
*/
|
|
136
|
+
async comparePassword (password: string, hashedPassword?: string | null): Promise<boolean> {
|
|
137
|
+
return await bcrypt.compare(password, hashedPassword ?? '')
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Generates a JWT token for a user.
|
|
142
|
+
*
|
|
143
|
+
* @param user - The user information to include in the token.
|
|
144
|
+
* @returns The generated JWT token.
|
|
145
|
+
*/
|
|
146
|
+
async generateToken (user: UserModel, ip: string, userAgent?: string): Promise<string> {
|
|
147
|
+
let session = await this.sessionService.getLatest(user)
|
|
148
|
+
|
|
149
|
+
if (
|
|
150
|
+
isEmpty(session) ||
|
|
151
|
+
session?.ip !== ip ||
|
|
152
|
+
isNotEmpty(session.closedAt) ||
|
|
153
|
+
session?.userAgent !== userAgent
|
|
154
|
+
) {
|
|
155
|
+
session = await this.sessionService.createForUser(user, ip, userAgent)
|
|
156
|
+
} else {
|
|
157
|
+
session = await this.sessionService.extend(
|
|
158
|
+
session,
|
|
159
|
+
this.blueprint.get<number>('security.jwt.expiresIn', 3600)
|
|
160
|
+
)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return jwt.sign(
|
|
164
|
+
{
|
|
165
|
+
user: {
|
|
166
|
+
...user,
|
|
167
|
+
password: undefined
|
|
168
|
+
},
|
|
169
|
+
session
|
|
170
|
+
},
|
|
171
|
+
this.blueprint.get<string>('security.secret', 'secret'),
|
|
172
|
+
this.blueprint.get<jwt.SignOptions>('security.jwt')
|
|
173
|
+
)
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Verifies a JWT token and returns the decoded payload.
|
|
178
|
+
*
|
|
179
|
+
* @param token - The JWT token to verify.
|
|
180
|
+
* @returns The decoded user payload or null if invalid.
|
|
181
|
+
*/
|
|
182
|
+
verifyToken = (token: string): UserTokenPayload => {
|
|
183
|
+
try {
|
|
184
|
+
return jwt.verify(
|
|
185
|
+
token,
|
|
186
|
+
this.blueprint.get<string>('security.secret', 'secret')
|
|
187
|
+
) as UserTokenPayload
|
|
188
|
+
} catch (error: any) {
|
|
189
|
+
throw new UnauthorizedError('Invalid token', { cause: error })
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Register a user
|
|
195
|
+
*
|
|
196
|
+
* @param user - The user to register
|
|
197
|
+
*/
|
|
198
|
+
async register (payload: UserRegister): Promise<void> {
|
|
199
|
+
const user = await this.userRepository.findBy({ email: payload.email })
|
|
200
|
+
|
|
201
|
+
if (isNotEmpty(user)) {
|
|
202
|
+
throw new BadRequestError(`The user with email ${payload.email} already exists`)
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const password = await this.hashPassword(payload.password)
|
|
206
|
+
|
|
207
|
+
await this.userRepository.create({
|
|
208
|
+
...payload,
|
|
209
|
+
password,
|
|
210
|
+
updatedAt: Date.now(),
|
|
211
|
+
createdAt: Date.now()
|
|
212
|
+
})
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Change the user password
|
|
217
|
+
*
|
|
218
|
+
* @param user - The user to change the password
|
|
219
|
+
* @param password - The password to change
|
|
220
|
+
*/
|
|
221
|
+
async changePassword (user?: UserModel, password?: UserChangePassword): Promise<void> {
|
|
222
|
+
if (isEmpty(user)) {
|
|
223
|
+
throw new UnauthorizedError('User not found')
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (isEmpty(password) || !(await this.comparePassword(password.password, user.password))) {
|
|
227
|
+
throw new UnauthorizedError('Invalid user password')
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
await this.userRepository.update(user.id, { password: password.newPassword })
|
|
231
|
+
}
|
|
232
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { Service } from '@stone-js/core'
|
|
2
|
+
import { randomUUID } from 'node:crypto'
|
|
3
|
+
import { UserModel } from '../models/User'
|
|
4
|
+
import { Session } from '../models/Session'
|
|
5
|
+
import { SessionRepository } from '../repositories/SessionRepository'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Session Service Options
|
|
9
|
+
*/
|
|
10
|
+
export interface SessionServiceOptions {
|
|
11
|
+
sessionRepository: SessionRepository
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Session Service
|
|
16
|
+
*
|
|
17
|
+
* @Service() decorator is used to define a new service
|
|
18
|
+
* @Service() is an alias of @Stone() decorator.
|
|
19
|
+
* The alias is required to get benefits of desctructuring Dependency Injection.
|
|
20
|
+
* And because the front-end class will be minified, we need to use alias to keep the class name.
|
|
21
|
+
*/
|
|
22
|
+
@Service({ alias: 'sessionService' })
|
|
23
|
+
export class SessionService {
|
|
24
|
+
private readonly sessionRepository: SessionRepository
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Create a new Session Service
|
|
28
|
+
*
|
|
29
|
+
* @param options - The options to create the service
|
|
30
|
+
*/
|
|
31
|
+
constructor ({ sessionRepository }: SessionServiceOptions) {
|
|
32
|
+
this.sessionRepository = sessionRepository
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Finds all sessions.
|
|
37
|
+
*
|
|
38
|
+
* @param limit - The limit of sessions to list
|
|
39
|
+
* @returns The list of sessions
|
|
40
|
+
*/
|
|
41
|
+
async list (limit: number): Promise<Session[]> {
|
|
42
|
+
return await this.sessionRepository.list(limit)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Finds a sessions by user.
|
|
47
|
+
*
|
|
48
|
+
* @param user - The user to look up
|
|
49
|
+
* @param limit - The limit of sessions to list
|
|
50
|
+
* @returns The list of sessions
|
|
51
|
+
*/
|
|
52
|
+
async getByUser (user: UserModel, limit: number): Promise<Session[]> {
|
|
53
|
+
return await this.sessionRepository.listByUser(user, limit)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Finds a session by ID.
|
|
58
|
+
*
|
|
59
|
+
* @param sessionId - The session ID to look up
|
|
60
|
+
* @returns The session data or `undefined` if not found
|
|
61
|
+
*/
|
|
62
|
+
async getById (id: number): Promise<Session | undefined> {
|
|
63
|
+
return await this.sessionRepository.findById(id)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Finds the latest session of a user.
|
|
68
|
+
*
|
|
69
|
+
* @param user - The user to look up
|
|
70
|
+
* @returns The latest session or `undefined` if not found
|
|
71
|
+
*/
|
|
72
|
+
async getLatest (user: UserModel): Promise<Session | undefined> {
|
|
73
|
+
return await this.sessionRepository.getLatest(user)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Creates a new session for a user.
|
|
78
|
+
*
|
|
79
|
+
* @param user - The user to create the session for
|
|
80
|
+
* @param ip - The IP address of the user
|
|
81
|
+
* @param userAgent - The user agent of the user
|
|
82
|
+
* @returns The created session
|
|
83
|
+
*/
|
|
84
|
+
async createForUser (user: UserModel, ip: string, userAgent?: string): Promise<Session> {
|
|
85
|
+
const session = {
|
|
86
|
+
ip,
|
|
87
|
+
userAgent,
|
|
88
|
+
userId: user.id,
|
|
89
|
+
createdAt: Date.now(),
|
|
90
|
+
uuid: randomUUID(),
|
|
91
|
+
lastActivityAt: Date.now(),
|
|
92
|
+
expiresAt: Date.now() + 3600
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return await this.sessionRepository.create(session)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Updates the last activity timestamp of a session.
|
|
100
|
+
*
|
|
101
|
+
* @param sessionId - The session ID to update
|
|
102
|
+
* @returns `true` if the session was updated, otherwise `false`
|
|
103
|
+
*/
|
|
104
|
+
async updateLastActivity (session: Session): Promise<void> {
|
|
105
|
+
await this.sessionRepository.update(session.id, { lastActivityAt: Date.now() })
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Extends a session expiration time.
|
|
110
|
+
*
|
|
111
|
+
* @param sessionId - The session ID to extend
|
|
112
|
+
* @param additionalTime - Additional time in milliseconds
|
|
113
|
+
* @returns `true` if the session was extended, otherwise `false`
|
|
114
|
+
*/
|
|
115
|
+
async extend (session: Session, additionalTime: number): Promise<Session> {
|
|
116
|
+
session.expiresAt = Date.now() + additionalTime
|
|
117
|
+
await this.sessionRepository.update(session.id, session)
|
|
118
|
+
return session
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Closes a session.
|
|
123
|
+
*
|
|
124
|
+
* @param sessionId - The session ID to close
|
|
125
|
+
*/
|
|
126
|
+
async close (session: Session): Promise<void> {
|
|
127
|
+
session.closedAt = Date.now()
|
|
128
|
+
await this.sessionRepository.update(session.id, session)
|
|
129
|
+
}
|
|
130
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { UserModel } from '../models/User'
|
|
2
|
+
import { UserEvent } from '../events/UserEvent'
|
|
3
|
+
import { NotFoundError } from '@stone-js/http-core'
|
|
4
|
+
import { UserRepository } from '../repositories/UserRepository'
|
|
5
|
+
import { EventEmitter, IContainer, isNotEmpty, Service } from '@stone-js/core'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* User Service Options
|
|
9
|
+
*/
|
|
10
|
+
export interface UserServiceOptions {
|
|
11
|
+
eventEmitter: EventEmitter
|
|
12
|
+
userRepository: UserRepository
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* User Service
|
|
17
|
+
*
|
|
18
|
+
* @Service() decorator is used to define a new service
|
|
19
|
+
* @Service() is an alias of @Stone() decorator.
|
|
20
|
+
* The alias is required to get benefits of desctructuring Dependency Injection.
|
|
21
|
+
* And because the front-end class will be minified, we need to use alias to keep the class name.
|
|
22
|
+
*/
|
|
23
|
+
@Service({ alias: 'userService' })
|
|
24
|
+
export class UserService {
|
|
25
|
+
private readonly eventEmitter: EventEmitter
|
|
26
|
+
private readonly userRepository: UserRepository
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Resolve route binding
|
|
30
|
+
*
|
|
31
|
+
* @param key - The key of the binding
|
|
32
|
+
* @param value - The value of the binding
|
|
33
|
+
* @param container - The container
|
|
34
|
+
*/
|
|
35
|
+
static async resolveRouteBinding (key: string, value: any, container: IContainer): Promise<UserModel | undefined> {
|
|
36
|
+
const userService = container.resolve<UserService>('userService')
|
|
37
|
+
return await userService.findBy({ [key]: value })
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Create a new User Service
|
|
42
|
+
*/
|
|
43
|
+
constructor ({ userRepository, eventEmitter }: UserServiceOptions) {
|
|
44
|
+
this.eventEmitter = eventEmitter
|
|
45
|
+
this.userRepository = userRepository
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* List users
|
|
50
|
+
*
|
|
51
|
+
* @param limit - The limit of users to list
|
|
52
|
+
*/
|
|
53
|
+
async list (limit: number = 10): Promise<UserModel[]> {
|
|
54
|
+
return await this.userRepository.list(limit)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Find a user
|
|
59
|
+
*
|
|
60
|
+
* @param conditions - The conditions to find the user
|
|
61
|
+
* @returns The found user
|
|
62
|
+
*/
|
|
63
|
+
async findBy (conditions: Record<string, any>): Promise<UserModel> {
|
|
64
|
+
const user = await this.userRepository.findById(conditions.id)
|
|
65
|
+
if (isNotEmpty<UserModel>(user)) return user
|
|
66
|
+
throw new NotFoundError(`User with ID ${String(conditions.id)} not found`)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Find a user
|
|
71
|
+
*
|
|
72
|
+
* @param conditions - The conditions to find the user
|
|
73
|
+
* @returns The found user
|
|
74
|
+
*/
|
|
75
|
+
async findByEmail (conditions: Record<string, any>): Promise<UserModel | undefined> {
|
|
76
|
+
return await this.userRepository.findBy(conditions)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Create a user
|
|
81
|
+
*
|
|
82
|
+
* @param payload - The user data to create
|
|
83
|
+
*/
|
|
84
|
+
async create (payload: Omit<UserModel, 'id'>): Promise<bigint | undefined> {
|
|
85
|
+
await this.eventEmitter.emit(new UserEvent(payload))
|
|
86
|
+
return await this.userRepository.create({ ...payload, createdAt: Date.now(), updatedAt: Date.now() })
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Update a user
|
|
91
|
+
*
|
|
92
|
+
* @param id - The id of the user to update
|
|
93
|
+
* @param payload - The user data to update
|
|
94
|
+
* @returns The updated user
|
|
95
|
+
*/
|
|
96
|
+
async update (id: number, payload: UserModel): Promise<UserModel> {
|
|
97
|
+
const user = await this.userRepository.update(id, payload)
|
|
98
|
+
if (isNotEmpty<UserModel>(user)) return user
|
|
99
|
+
throw new NotFoundError(`User with ID ${id} not found`)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Delete a user
|
|
104
|
+
*
|
|
105
|
+
* @param id - The id of the user to delete
|
|
106
|
+
*/
|
|
107
|
+
async delete (id: number): Promise<boolean> {
|
|
108
|
+
return await this.userRepository.delete(id)
|
|
109
|
+
}
|
|
110
|
+
}
|