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