kybernus 3.0.1 → 3.2.0
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/README.md +1 -1
- package/dist/cli/commands/ecommerce.d.ts +3 -0
- package/dist/cli/commands/ecommerce.d.ts.map +1 -0
- package/dist/cli/commands/ecommerce.js +164 -0
- package/dist/cli/commands/ecommerce.js.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/templates/ecommerce/.env.example +10 -0
- package/templates/ecommerce/.github/workflows/ci.yml +102 -0
- package/templates/ecommerce/.github/workflows/deploy.yml +31 -0
- package/templates/ecommerce/.prettierrc +9 -0
- package/templates/ecommerce/Dockerfile +54 -0
- package/templates/ecommerce/README.md +295 -0
- package/templates/ecommerce/apps/api/.env.example +59 -0
- package/templates/ecommerce/apps/api/jest.config.ts +50 -0
- package/templates/ecommerce/apps/api/jest.integration.config.ts +45 -0
- package/templates/ecommerce/apps/api/package.json +59 -0
- package/templates/ecommerce/apps/api/prisma/migrations/20260306000137_init/migration.sql +184 -0
- package/templates/ecommerce/apps/api/prisma/migrations/migration_lock.toml +3 -0
- package/templates/ecommerce/apps/api/prisma/schema.prisma +181 -0
- package/templates/ecommerce/apps/api/prisma/seed.ts +159 -0
- package/templates/ecommerce/apps/api/src/__tests__/app.test.ts +39 -0
- package/templates/ecommerce/apps/api/src/__tests__/globalSetup.ts +34 -0
- package/templates/ecommerce/apps/api/src/__tests__/globalTeardown.ts +16 -0
- package/templates/ecommerce/apps/api/src/__tests__/setup.db.ts +18 -0
- package/templates/ecommerce/apps/api/src/__tests__/setup.env.ts +14 -0
- package/templates/ecommerce/apps/api/src/app.ts +133 -0
- package/templates/ecommerce/apps/api/src/application/admin/admin-user.service.ts +24 -0
- package/templates/ecommerce/apps/api/src/application/admin/dashboard.service.ts +102 -0
- package/templates/ecommerce/apps/api/src/application/auth/auth.service.ts +185 -0
- package/templates/ecommerce/apps/api/src/application/cart/cart.service.ts +151 -0
- package/templates/ecommerce/apps/api/src/application/cart/coupon.service.ts +51 -0
- package/templates/ecommerce/apps/api/src/application/catalog/catalog.service.ts +168 -0
- package/templates/ecommerce/apps/api/src/application/checkout/checkout.service.ts +114 -0
- package/templates/ecommerce/apps/api/src/application/orders/order.service.ts +93 -0
- package/templates/ecommerce/apps/api/src/application/ports/email.port.ts +3 -0
- package/templates/ecommerce/apps/api/src/application/ports/payment.port.ts +24 -0
- package/templates/ecommerce/apps/api/src/application/ports/shipping.port.ts +9 -0
- package/templates/ecommerce/apps/api/src/application/ports/storage.port.ts +3 -0
- package/templates/ecommerce/apps/api/src/application/ports/token-blacklist.port.ts +4 -0
- package/templates/ecommerce/apps/api/src/application/ports/token.port.ts +18 -0
- package/templates/ecommerce/apps/api/src/application/profile/profile.service.ts +76 -0
- package/templates/ecommerce/apps/api/src/domain/auth/user.entity.ts +109 -0
- package/templates/ecommerce/apps/api/src/domain/auth/user.repository.ts +11 -0
- package/templates/ecommerce/apps/api/src/domain/cart/cart.entity.ts +136 -0
- package/templates/ecommerce/apps/api/src/domain/cart/cart.repository.ts +8 -0
- package/templates/ecommerce/apps/api/src/domain/cart/coupon.entity.ts +58 -0
- package/templates/ecommerce/apps/api/src/domain/cart/coupon.repository.ts +10 -0
- package/templates/ecommerce/apps/api/src/domain/catalog/category.entity.ts +51 -0
- package/templates/ecommerce/apps/api/src/domain/catalog/category.repository.ts +10 -0
- package/templates/ecommerce/apps/api/src/domain/catalog/product.entity.ts +130 -0
- package/templates/ecommerce/apps/api/src/domain/catalog/product.repository.ts +28 -0
- package/templates/ecommerce/apps/api/src/domain/checkout/order.entity.ts +121 -0
- package/templates/ecommerce/apps/api/src/domain/checkout/order.repository.ts +11 -0
- package/templates/ecommerce/apps/api/src/domain/shared/AppError.ts +12 -0
- package/templates/ecommerce/apps/api/src/infrastructure/cache/redis.ts +16 -0
- package/templates/ecommerce/apps/api/src/infrastructure/config/registry/admin.registry.ts +13 -0
- package/templates/ecommerce/apps/api/src/infrastructure/config/registry/auth.registry.ts +34 -0
- package/templates/ecommerce/apps/api/src/infrastructure/config/registry/cart.registry.ts +49 -0
- package/templates/ecommerce/apps/api/src/infrastructure/config/registry/catalog.registry.ts +24 -0
- package/templates/ecommerce/apps/api/src/infrastructure/config/registry/checkout.registry.ts +47 -0
- package/templates/ecommerce/apps/api/src/infrastructure/config/registry/orders.registry.ts +6 -0
- package/templates/ecommerce/apps/api/src/infrastructure/config/registry/profile.registry.ts +4 -0
- package/templates/ecommerce/apps/api/src/infrastructure/persistence/in-memory/cart.memory.repository.ts +33 -0
- package/templates/ecommerce/apps/api/src/infrastructure/persistence/in-memory/category.memory.repository.ts +41 -0
- package/templates/ecommerce/apps/api/src/infrastructure/persistence/in-memory/coupon.memory.repository.ts +55 -0
- package/templates/ecommerce/apps/api/src/infrastructure/persistence/in-memory/order.memory.repository.ts +75 -0
- package/templates/ecommerce/apps/api/src/infrastructure/persistence/in-memory/product.memory.repository.ts +100 -0
- package/templates/ecommerce/apps/api/src/infrastructure/persistence/in-memory/user.memory.repository.ts +54 -0
- package/templates/ecommerce/apps/api/src/infrastructure/persistence/prisma/auth/user.prisma.repository.ts +83 -0
- package/templates/ecommerce/apps/api/src/infrastructure/persistence/prisma/catalog/category.prisma.repository.ts +69 -0
- package/templates/ecommerce/apps/api/src/infrastructure/persistence/prisma/catalog/product.prisma.repository.ts +185 -0
- package/templates/ecommerce/apps/api/src/infrastructure/persistence/prisma/checkout/order.prisma.repository.ts +149 -0
- package/templates/ecommerce/apps/api/src/infrastructure/persistence/prisma-client.ts +17 -0
- package/templates/ecommerce/apps/api/src/infrastructure/services/email/email.registry.ts +18 -0
- package/templates/ecommerce/apps/api/src/infrastructure/services/email/ethereal.email.service.ts +38 -0
- package/templates/ecommerce/apps/api/src/infrastructure/services/email/noop.email.service.ts +12 -0
- package/templates/ecommerce/apps/api/src/infrastructure/services/email/smtp.email.service.ts +36 -0
- package/templates/ecommerce/apps/api/src/infrastructure/services/payment/stripe-webhook.handler.ts +83 -0
- package/templates/ecommerce/apps/api/src/infrastructure/services/payment/stripe.adapter.ts +39 -0
- package/templates/ecommerce/apps/api/src/infrastructure/services/shipping/mock.shipping.service.ts +17 -0
- package/templates/ecommerce/apps/api/src/infrastructure/services/storage/in-memory.storage.service.ts +11 -0
- package/templates/ecommerce/apps/api/src/infrastructure/services/storage/local-disk.storage.service.ts +27 -0
- package/templates/ecommerce/apps/api/src/infrastructure/services/storage/s3.storage.service.ts +52 -0
- package/templates/ecommerce/apps/api/src/infrastructure/services/storage/storage.registry.ts +19 -0
- package/templates/ecommerce/apps/api/src/infrastructure/services/token/redis.token.blacklist.ts +23 -0
- package/templates/ecommerce/apps/api/src/infrastructure/services/token/token.blacklist.ts +30 -0
- package/templates/ecommerce/apps/api/src/infrastructure/services/token/token.service.ts +136 -0
- package/templates/ecommerce/apps/api/src/modules/admin/__tests__/admin.routes.integration.test.ts +250 -0
- package/templates/ecommerce/apps/api/src/modules/admin/admin.controller.ts +116 -0
- package/templates/ecommerce/apps/api/src/modules/admin/admin.registry.ts +1 -0
- package/templates/ecommerce/apps/api/src/modules/admin/admin.routes.ts +21 -0
- package/templates/ecommerce/apps/api/src/modules/admin/admin.service.ts +1 -0
- package/templates/ecommerce/apps/api/src/modules/admin/admin.user.service.ts +1 -0
- package/templates/ecommerce/apps/api/src/modules/auth/__tests__/auth.logout.redis.test.ts +104 -0
- package/templates/ecommerce/apps/api/src/modules/auth/__tests__/auth.routes.integration.test.ts +211 -0
- package/templates/ecommerce/apps/api/src/modules/auth/__tests__/auth.service.unit.test.ts +260 -0
- package/templates/ecommerce/apps/api/src/modules/auth/__tests__/email.service.unit.test.ts +94 -0
- package/templates/ecommerce/apps/api/src/modules/auth/__tests__/token.blacklist.redis.test.ts +65 -0
- package/templates/ecommerce/apps/api/src/modules/auth/__tests__/user.entity.unit.test.ts +79 -0
- package/templates/ecommerce/apps/api/src/modules/auth/__tests__/user.prisma.repository.test.ts +138 -0
- package/templates/ecommerce/apps/api/src/modules/auth/auth.controller.ts +148 -0
- package/templates/ecommerce/apps/api/src/modules/auth/auth.registry.ts +1 -0
- package/templates/ecommerce/apps/api/src/modules/auth/auth.routes.ts +17 -0
- package/templates/ecommerce/apps/api/src/modules/auth/auth.service.ts +1 -0
- package/templates/ecommerce/apps/api/src/modules/auth/redis.token.blacklist.ts +1 -0
- package/templates/ecommerce/apps/api/src/modules/auth/token.blacklist.ts +2 -0
- package/templates/ecommerce/apps/api/src/modules/auth/token.service.ts +2 -0
- package/templates/ecommerce/apps/api/src/modules/auth/user.entity.ts +1 -0
- package/templates/ecommerce/apps/api/src/modules/auth/user.prisma.repository.ts +1 -0
- package/templates/ecommerce/apps/api/src/modules/auth/user.repository.ts +2 -0
- package/templates/ecommerce/apps/api/src/modules/cart/__tests__/cart.entity.unit.test.ts +144 -0
- package/templates/ecommerce/apps/api/src/modules/cart/__tests__/cart.routes.integration.test.ts +242 -0
- package/templates/ecommerce/apps/api/src/modules/cart/__tests__/cart.service.unit.test.ts +151 -0
- package/templates/ecommerce/apps/api/src/modules/cart/__tests__/coupon.admin.integration.test.ts +136 -0
- package/templates/ecommerce/apps/api/src/modules/cart/cart.controller.ts +94 -0
- package/templates/ecommerce/apps/api/src/modules/cart/cart.entity.ts +1 -0
- package/templates/ecommerce/apps/api/src/modules/cart/cart.registry.ts +1 -0
- package/templates/ecommerce/apps/api/src/modules/cart/cart.repository.ts +2 -0
- package/templates/ecommerce/apps/api/src/modules/cart/cart.routes.ts +17 -0
- package/templates/ecommerce/apps/api/src/modules/cart/cart.service.ts +1 -0
- package/templates/ecommerce/apps/api/src/modules/cart/coupon.entity.ts +1 -0
- package/templates/ecommerce/apps/api/src/modules/cart/coupon.repository.ts +2 -0
- package/templates/ecommerce/apps/api/src/modules/cart/coupon.service.ts +1 -0
- package/templates/ecommerce/apps/api/src/modules/cart/shipping.service.ts +2 -0
- package/templates/ecommerce/apps/api/src/modules/catalog/__tests__/catalog.routes.integration.test.ts +275 -0
- package/templates/ecommerce/apps/api/src/modules/catalog/__tests__/catalog.service.unit.test.ts +223 -0
- package/templates/ecommerce/apps/api/src/modules/catalog/__tests__/product.image.integration.test.ts +130 -0
- package/templates/ecommerce/apps/api/src/modules/catalog/__tests__/product.prisma.repository.test.ts +174 -0
- package/templates/ecommerce/apps/api/src/modules/catalog/catalog.controller.ts +176 -0
- package/templates/ecommerce/apps/api/src/modules/catalog/catalog.registry.ts +1 -0
- package/templates/ecommerce/apps/api/src/modules/catalog/catalog.routes.ts +38 -0
- package/templates/ecommerce/apps/api/src/modules/catalog/catalog.service.ts +1 -0
- package/templates/ecommerce/apps/api/src/modules/catalog/category.entity.ts +1 -0
- package/templates/ecommerce/apps/api/src/modules/catalog/category.prisma.repository.ts +1 -0
- package/templates/ecommerce/apps/api/src/modules/catalog/category.repository.ts +2 -0
- package/templates/ecommerce/apps/api/src/modules/catalog/product.entity.ts +1 -0
- package/templates/ecommerce/apps/api/src/modules/catalog/product.prisma.repository.ts +1 -0
- package/templates/ecommerce/apps/api/src/modules/catalog/product.repository.ts +2 -0
- package/templates/ecommerce/apps/api/src/modules/checkout/__tests__/checkout.routes.integration.test.ts +163 -0
- package/templates/ecommerce/apps/api/src/modules/checkout/__tests__/checkout.service.unit.test.ts +191 -0
- package/templates/ecommerce/apps/api/src/modules/checkout/__tests__/order.prisma.repository.test.ts +150 -0
- package/templates/ecommerce/apps/api/src/modules/checkout/checkout.controller.ts +59 -0
- package/templates/ecommerce/apps/api/src/modules/checkout/checkout.registry.ts +1 -0
- package/templates/ecommerce/apps/api/src/modules/checkout/checkout.routes.ts +18 -0
- package/templates/ecommerce/apps/api/src/modules/checkout/checkout.service.ts +1 -0
- package/templates/ecommerce/apps/api/src/modules/checkout/order.entity.ts +1 -0
- package/templates/ecommerce/apps/api/src/modules/checkout/order.prisma.repository.ts +1 -0
- package/templates/ecommerce/apps/api/src/modules/checkout/order.repository.ts +2 -0
- package/templates/ecommerce/apps/api/src/modules/checkout/tax.service.ts +9 -0
- package/templates/ecommerce/apps/api/src/modules/orders/__tests__/order.entity.unit.test.ts +68 -0
- package/templates/ecommerce/apps/api/src/modules/orders/__tests__/order.routes.integration.test.ts +254 -0
- package/templates/ecommerce/apps/api/src/modules/orders/__tests__/order.service.email.unit.test.ts +142 -0
- package/templates/ecommerce/apps/api/src/modules/orders/order.controller.ts +96 -0
- package/templates/ecommerce/apps/api/src/modules/orders/order.registry.ts +1 -0
- package/templates/ecommerce/apps/api/src/modules/orders/order.routes.ts +17 -0
- package/templates/ecommerce/apps/api/src/modules/orders/order.service.ts +1 -0
- package/templates/ecommerce/apps/api/src/modules/payment/__tests__/stripe-webhook.unit.test.ts +330 -0
- package/templates/ecommerce/apps/api/src/modules/payment/__tests__/stripe.adapter.unit.test.ts +84 -0
- package/templates/ecommerce/apps/api/src/modules/payment/adapters/stripe.adapter.ts +1 -0
- package/templates/ecommerce/apps/api/src/modules/payment/payment.port.ts +1 -0
- package/templates/ecommerce/apps/api/src/modules/payment/stripe-webhook.handler.ts +1 -0
- package/templates/ecommerce/apps/api/src/modules/profile/__tests__/profile.routes.integration.test.ts +180 -0
- package/templates/ecommerce/apps/api/src/modules/profile/__tests__/profile.service.unit.test.ts +187 -0
- package/templates/ecommerce/apps/api/src/modules/profile/profile.controller.ts +92 -0
- package/templates/ecommerce/apps/api/src/modules/profile/profile.registry.ts +1 -0
- package/templates/ecommerce/apps/api/src/modules/profile/profile.routes.ts +14 -0
- package/templates/ecommerce/apps/api/src/modules/profile/profile.service.ts +1 -0
- package/templates/ecommerce/apps/api/src/presentation/middlewares/authenticate.ts +37 -0
- package/templates/ecommerce/apps/api/src/presentation/middlewares/authorize.ts +23 -0
- package/templates/ecommerce/apps/api/src/presentation/middlewares/errorHandler.ts +48 -0
- package/templates/ecommerce/apps/api/src/presentation/modules/admin/admin.controller.ts +116 -0
- package/templates/ecommerce/apps/api/src/presentation/modules/admin/admin.routes.ts +21 -0
- package/templates/ecommerce/apps/api/src/presentation/modules/auth/auth.controller.ts +147 -0
- package/templates/ecommerce/apps/api/src/presentation/modules/auth/auth.routes.ts +17 -0
- package/templates/ecommerce/apps/api/src/presentation/modules/cart/cart.controller.ts +94 -0
- package/templates/ecommerce/apps/api/src/presentation/modules/cart/cart.routes.ts +17 -0
- package/templates/ecommerce/apps/api/src/presentation/modules/catalog/catalog.controller.ts +176 -0
- package/templates/ecommerce/apps/api/src/presentation/modules/catalog/catalog.routes.ts +38 -0
- package/templates/ecommerce/apps/api/src/presentation/modules/checkout/checkout.controller.ts +59 -0
- package/templates/ecommerce/apps/api/src/presentation/modules/checkout/checkout.routes.ts +18 -0
- package/templates/ecommerce/apps/api/src/presentation/modules/orders/order.controller.ts +96 -0
- package/templates/ecommerce/apps/api/src/presentation/modules/orders/order.routes.ts +17 -0
- package/templates/ecommerce/apps/api/src/presentation/modules/profile/profile.controller.ts +92 -0
- package/templates/ecommerce/apps/api/src/presentation/modules/profile/profile.routes.ts +14 -0
- package/templates/ecommerce/apps/api/src/presentation/validators/uuidParam.ts +20 -0
- package/templates/ecommerce/apps/api/src/server.ts +47 -0
- package/templates/ecommerce/apps/api/src/shared/__tests__/uuid.validation.test.ts +111 -0
- package/templates/ecommerce/apps/api/src/shared/errors/AppError.ts +1 -0
- package/templates/ecommerce/apps/api/src/shared/infra/email/EtherealEmailService.ts +1 -0
- package/templates/ecommerce/apps/api/src/shared/infra/email/IEmailService.ts +1 -0
- package/templates/ecommerce/apps/api/src/shared/infra/email/NoopEmailService.ts +1 -0
- package/templates/ecommerce/apps/api/src/shared/infra/email/SmtpEmailService.ts +1 -0
- package/templates/ecommerce/apps/api/src/shared/infra/email/__tests__/ethereal.email.integration.test.ts +32 -0
- package/templates/ecommerce/apps/api/src/shared/infra/email/email.registry.ts +1 -0
- package/templates/ecommerce/apps/api/src/shared/infra/prisma.ts +1 -0
- package/templates/ecommerce/apps/api/src/shared/infra/redis.ts +1 -0
- package/templates/ecommerce/apps/api/src/shared/infra/storage/IStorageService.ts +1 -0
- package/templates/ecommerce/apps/api/src/shared/infra/storage/InMemoryStorageService.ts +1 -0
- package/templates/ecommerce/apps/api/src/shared/infra/storage/LocalDiskStorageService.ts +1 -0
- package/templates/ecommerce/apps/api/src/shared/infra/storage/S3StorageService.ts +1 -0
- package/templates/ecommerce/apps/api/src/shared/infra/storage/__tests__/s3.storage.unit.test.ts +73 -0
- package/templates/ecommerce/apps/api/src/shared/infra/storage/storage.registry.ts +1 -0
- package/templates/ecommerce/apps/api/src/shared/middlewares/authenticate.ts +1 -0
- package/templates/ecommerce/apps/api/src/shared/middlewares/authorize.ts +1 -0
- package/templates/ecommerce/apps/api/src/shared/middlewares/errorHandler.ts +1 -0
- package/templates/ecommerce/apps/api/src/shared/validators/uuidParam.ts +1 -0
- package/templates/ecommerce/apps/api/tsconfig.json +15 -0
- package/templates/ecommerce/apps/web/.env.example +8 -0
- package/templates/ecommerce/apps/web/index.html +19 -0
- package/templates/ecommerce/apps/web/jest.config.ts +45 -0
- package/templates/ecommerce/apps/web/package.json +38 -0
- package/templates/ecommerce/apps/web/src/App.tsx +133 -0
- package/templates/ecommerce/apps/web/src/__mocks__/fileMock.ts +1 -0
- package/templates/ecommerce/apps/web/src/__mocks__/styleMock.ts +1 -0
- package/templates/ecommerce/apps/web/src/index.css +159 -0
- package/templates/ecommerce/apps/web/src/main.tsx +13 -0
- package/templates/ecommerce/apps/web/src/modules/admin/__tests__/CouponsAdminPage.test.tsx +134 -0
- package/templates/ecommerce/apps/web/src/modules/admin/__tests__/DashboardPage.test.tsx +65 -0
- package/templates/ecommerce/apps/web/src/modules/admin/__tests__/OrdersAdminPage.test.tsx +79 -0
- package/templates/ecommerce/apps/web/src/modules/admin/__tests__/ProductsAdminPage.test.tsx +84 -0
- package/templates/ecommerce/apps/web/src/modules/admin/__tests__/UsersAdminPage.test.tsx +85 -0
- package/templates/ecommerce/apps/web/src/modules/admin/pages/CouponsAdminPage.tsx +179 -0
- package/templates/ecommerce/apps/web/src/modules/admin/pages/DashboardPage.tsx +58 -0
- package/templates/ecommerce/apps/web/src/modules/admin/pages/OrdersAdminPage.tsx +178 -0
- package/templates/ecommerce/apps/web/src/modules/admin/pages/ProductsAdminPage.tsx +444 -0
- package/templates/ecommerce/apps/web/src/modules/admin/pages/UsersAdminPage.tsx +87 -0
- package/templates/ecommerce/apps/web/src/modules/auth/LoginForm.tsx +91 -0
- package/templates/ecommerce/apps/web/src/modules/auth/RegisterForm.tsx +109 -0
- package/templates/ecommerce/apps/web/src/modules/auth/__tests__/ForgotPasswordPage.test.tsx +42 -0
- package/templates/ecommerce/apps/web/src/modules/auth/__tests__/LoginForm.test.tsx +76 -0
- package/templates/ecommerce/apps/web/src/modules/auth/__tests__/RegisterForm.test.tsx +62 -0
- package/templates/ecommerce/apps/web/src/modules/auth/__tests__/ResetPasswordPage.test.tsx +66 -0
- package/templates/ecommerce/apps/web/src/modules/auth/pages/ForgotPasswordPage.tsx +100 -0
- package/templates/ecommerce/apps/web/src/modules/auth/pages/LoginPage.tsx +39 -0
- package/templates/ecommerce/apps/web/src/modules/auth/pages/RegisterPage.tsx +39 -0
- package/templates/ecommerce/apps/web/src/modules/auth/pages/ResetPasswordPage.tsx +110 -0
- package/templates/ecommerce/apps/web/src/modules/auth/useAuthStore.ts +141 -0
- package/templates/ecommerce/apps/web/src/modules/cart/__tests__/CartPage.test.tsx +111 -0
- package/templates/ecommerce/apps/web/src/modules/cart/pages/CartPage.tsx +313 -0
- package/templates/ecommerce/apps/web/src/modules/catalog/__tests__/ProductCard.test.tsx +59 -0
- package/templates/ecommerce/apps/web/src/modules/catalog/__tests__/ProductFilters.test.tsx +56 -0
- package/templates/ecommerce/apps/web/src/modules/catalog/components/ProductCard.tsx +78 -0
- package/templates/ecommerce/apps/web/src/modules/catalog/components/ProductFilters.tsx +104 -0
- package/templates/ecommerce/apps/web/src/modules/catalog/pages/ProductDetailPage.tsx +179 -0
- package/templates/ecommerce/apps/web/src/modules/catalog/pages/ProductListPage.tsx +100 -0
- package/templates/ecommerce/apps/web/src/modules/checkout/__tests__/CheckoutPage.test.tsx +159 -0
- package/templates/ecommerce/apps/web/src/modules/checkout/__tests__/StripePaymentForm.test.tsx +79 -0
- package/templates/ecommerce/apps/web/src/modules/checkout/components/StripePaymentForm.tsx +55 -0
- package/templates/ecommerce/apps/web/src/modules/checkout/hooks/useCheckout.ts +56 -0
- package/templates/ecommerce/apps/web/src/modules/checkout/pages/CheckoutPage.tsx +344 -0
- package/templates/ecommerce/apps/web/src/modules/checkout/pages/CheckoutSuccessPage.tsx +12 -0
- package/templates/ecommerce/apps/web/src/modules/legal/pages/PrivacyPolicyPage.tsx +207 -0
- package/templates/ecommerce/apps/web/src/modules/legal/pages/TermsOfServicePage.tsx +175 -0
- package/templates/ecommerce/apps/web/src/modules/orders/__tests__/OrderDetailPage.test.tsx +75 -0
- package/templates/ecommerce/apps/web/src/modules/orders/__tests__/OrderHistoryPage.test.tsx +87 -0
- package/templates/ecommerce/apps/web/src/modules/orders/pages/OrderDetailPage.tsx +73 -0
- package/templates/ecommerce/apps/web/src/modules/orders/pages/OrderHistoryPage.tsx +97 -0
- package/templates/ecommerce/apps/web/src/modules/profile/__tests__/ProfilePage.test.tsx +150 -0
- package/templates/ecommerce/apps/web/src/modules/profile/pages/ProfilePage.tsx +275 -0
- package/templates/ecommerce/apps/web/src/setupTests.ts +10 -0
- package/templates/ecommerce/apps/web/src/shared/components/CookieConsent.tsx +108 -0
- package/templates/ecommerce/apps/web/src/shared/components/ErrorBoundary.tsx +112 -0
- package/templates/ecommerce/apps/web/src/shared/components/Layout.tsx +143 -0
- package/templates/ecommerce/apps/web/src/shared/components/ProtectedRoute.tsx +21 -0
- package/templates/ecommerce/apps/web/src/shared/config/siteConfig.ts +57 -0
- package/templates/ecommerce/apps/web/src/shared/hooks/usePageTitle.ts +16 -0
- package/templates/ecommerce/apps/web/src/shared/lib/apiFetch.ts +16 -0
- package/templates/ecommerce/apps/web/src/shared/pages/NotFoundPage.tsx +42 -0
- package/templates/ecommerce/apps/web/src/shared/theme/ThemeProvider.tsx +45 -0
- package/templates/ecommerce/apps/web/src/shared/theme/__tests__/ThemeProvider.test.tsx +78 -0
- package/templates/ecommerce/apps/web/src/shared/theme/createTheme.ts +58 -0
- package/templates/ecommerce/apps/web/src/shared/theme/tokens.ts +81 -0
- package/templates/ecommerce/apps/web/src/vite-env.d.ts +1 -0
- package/templates/ecommerce/apps/web/tsconfig.jest.json +12 -0
- package/templates/ecommerce/apps/web/tsconfig.json +25 -0
- package/templates/ecommerce/apps/web/tsconfig.node.json +11 -0
- package/templates/ecommerce/apps/web/vite.config.ts +30 -0
- package/templates/ecommerce/docker-compose.yml +85 -0
- package/templates/ecommerce/package-lock.json +11255 -0
- package/templates/ecommerce/package.json +27 -0
- package/templates/ecommerce/packages/shared-types/package.json +13 -0
- package/templates/ecommerce/packages/shared-types/src/index.ts +3 -0
- package/templates/ecommerce/packages/shared-types/src/theme.ts +44 -0
- package/templates/ecommerce/packages/shared-types/tsconfig.json +11 -0
- package/templates/ecommerce/scripts/customize.sh +201 -0
- package/templates/ecommerce/tsconfig.json +14 -0
- package/templates/java-spring/clean/.gitignore.hbs +72 -0
- package/templates/java-spring/clean/docker-compose.yml.hbs +6 -3
- package/templates/java-spring/clean/src/main/java/{{packagePath}}/application/usecase/PaymentUseCase.java.hbs +21 -17
- package/templates/java-spring/clean/src/main/java/{{packagePath}}/infrastructure/persistence/entity/UserEntity.java.hbs +52 -0
- package/templates/java-spring/clean/src/main/java/{{packagePath}}/infrastructure/persistence/repository/JpaUserRepository.java.hbs +12 -0
- package/templates/java-spring/clean/src/main/java/{{packagePath}}/infrastructure/security/JwtAuthenticationFilter.java.hbs +64 -0
- package/templates/java-spring/clean/src/main/java/{{packagePath}}/infrastructure/security/SecurityConfig.java.hbs +36 -0
- package/templates/java-spring/clean/src/main/java/{{packagePath}}/infrastructure/stripe/StripeGateway.java.hbs +63 -0
- package/templates/java-spring/clean/src/main/resources/application.properties.hbs +6 -7
- package/templates/java-spring/hexagonal/.gitignore.hbs +72 -0
- package/templates/java-spring/hexagonal/docker-compose.yml.hbs +6 -3
- package/templates/java-spring/hexagonal/src/main/java/{{packagePath}}/adapters/outbound/security/JwtFilter.java.hbs +71 -0
- package/templates/java-spring/hexagonal/src/main/java/{{packagePath}}/adapters/outbound/security/SecurityConfig.java.hbs +35 -0
- package/templates/java-spring/hexagonal/src/main/java/{{packagePath}}/core/service/PaymentService.java.hbs +3 -3
- package/templates/java-spring/hexagonal/src/main/resources/application.properties.hbs +4 -4
- package/templates/java-spring/mvc/.gitignore.hbs +72 -0
- package/templates/java-spring/mvc/docker-compose.yml.hbs +6 -3
- package/templates/java-spring/mvc/src/main/java/{{packagePath}}/config/SecurityConfig.java.hbs +13 -12
- package/templates/java-spring/mvc/src/main/java/{{packagePath}}/controller/AuthController.java.hbs +9 -8
- package/templates/java-spring/mvc/src/main/java/{{packagePath}}/controller/PaymentsController.java.hbs +5 -6
- package/templates/java-spring/mvc/src/main/java/{{packagePath}}/service/StripeService.java.hbs +3 -3
- package/templates/java-spring/mvc/src/main/resources/application.yml.hbs +29 -26
- package/templates/nestjs/clean/.gitignore.hbs +42 -0
- package/templates/nestjs/clean/Dockerfile.hbs +6 -3
- package/templates/nestjs/clean/docker-compose.yml.hbs +1 -11
- package/templates/nestjs/clean/src/app.module.ts.hbs +2 -1
- package/templates/nestjs/clean/src/application/payment.service.ts.hbs +72 -72
- package/templates/nestjs/clean/src/domain/entities/user.entity.ts.hbs +2 -2
- package/templates/nestjs/clean/src/domain/repositories/user.repository.ts.hbs +2 -2
- package/templates/nestjs/clean/src/infrastructure/database/repositories/prisma.user.repository.ts.hbs +18 -18
- package/templates/nestjs/clean/src/infrastructure/http/health.controller.ts.hbs +9 -0
- package/templates/nestjs/clean/src/main.ts.hbs +1 -4
- package/templates/nestjs/clean/src/payment.module.ts.hbs +12 -12
- package/templates/nestjs/hexagonal/.gitignore.hbs +42 -0
- package/templates/nestjs/hexagonal/Dockerfile.hbs +6 -3
- package/templates/nestjs/hexagonal/docker-compose.yml.hbs +1 -11
- package/templates/nestjs/hexagonal/src/adapters/inbound/health.controller.ts.hbs +9 -0
- package/templates/nestjs/hexagonal/src/app.module.ts.hbs +2 -1
- package/templates/nestjs/hexagonal/src/core/domain/user.entity.ts.hbs +6 -6
- package/templates/nestjs/hexagonal/src/core/ports/ports.ts.hbs +4 -4
- package/templates/nestjs/hexagonal/src/main.ts.hbs +1 -4
- package/templates/nestjs/mvc/.gitignore.hbs +42 -0
- package/templates/nestjs/mvc/Dockerfile.hbs +6 -3
- package/templates/nestjs/mvc/docker-compose.yml.hbs +1 -11
- package/templates/nestjs/mvc/src/auth/auth.controller.ts.hbs +11 -1
- package/templates/nestjs/mvc/src/auth/auth.service.ts.hbs +3 -1
- package/templates/nestjs/mvc/src/controllers/health.controller.ts.hbs +6 -6
- package/templates/nestjs/mvc/src/main.ts.hbs +1 -4
- package/templates/nestjs/mvc/src/models/create-item.dto.ts.hbs +5 -2
- package/templates/nestjs/mvc/src/prisma/prisma.service.ts.hbs +1 -0
- package/templates/nextjs/mvc/.gitignore.hbs +42 -0
- package/templates/nextjs/mvc/Dockerfile.hbs +23 -8
- package/templates/nextjs/mvc/docker-compose.yml.hbs +1 -1
- package/templates/nodejs-express/clean/.gitignore.hbs +42 -0
- package/templates/nodejs-express/clean/Dockerfile.hbs +6 -1
- package/templates/nodejs-express/clean/docker-compose.yml.hbs +2 -2
- package/templates/nodejs-express/clean/package.json.hbs +69 -69
- package/templates/nodejs-express/clean/src/config.ts.hbs +11 -0
- package/templates/nodejs-express/clean/src/domain/entities/User.ts.hbs +46 -8
- package/templates/nodejs-express/hexagonal/.gitignore.hbs +42 -0
- package/templates/nodejs-express/hexagonal/Dockerfile.hbs +1 -1
- package/templates/nodejs-express/hexagonal/docker-compose.yml.hbs +2 -2
- package/templates/nodejs-express/hexagonal/package.json.hbs +69 -69
- package/templates/nodejs-express/hexagonal/src/adapters/inbound/http/PaymentController.ts.hbs +21 -38
- package/templates/nodejs-express/hexagonal/src/adapters/outbound/persistence/prisma.ts.hbs +2 -0
- package/templates/nodejs-express/hexagonal/src/config.ts.hbs +9 -0
- package/templates/nodejs-express/hexagonal/src/core/AuthService.ts.hbs +5 -5
- package/templates/nodejs-express/hexagonal/src/core/PaymentService.ts.hbs +7 -22
- package/templates/nodejs-express/hexagonal/src/core/domain/entities/User.ts.hbs +24 -4
- package/templates/nodejs-express/mvc/.gitignore.hbs +42 -0
- package/templates/nodejs-express/mvc/package.json.hbs +67 -67
- package/templates/python-fastapi/clean/.gitignore.hbs +76 -0
- package/templates/python-fastapi/clean/app/application/services/payment_service.py.hbs +3 -3
- package/templates/python-fastapi/clean/app/config.py.hbs +6 -7
- package/templates/python-fastapi/clean/app/domain/usecases/login_user.py.hbs +15 -0
- package/templates/python-fastapi/clean/app/infrastructure/http/auth_controller.py.hbs +40 -6
- package/templates/python-fastapi/clean/app/infrastructure/http/payment_controller.py.hbs +5 -4
- package/templates/python-fastapi/clean/app/infrastructure/security/jwt.py.hbs +23 -0
- package/templates/python-fastapi/clean/app/main.py.hbs +3 -0
- package/templates/python-fastapi/clean/docker-compose.yml.hbs +5 -12
- package/templates/python-fastapi/clean/requirements.txt.hbs +3 -0
- package/templates/python-fastapi/hexagonal/.gitignore.hbs +76 -0
- package/templates/python-fastapi/hexagonal/app/adapters/inbound/http_adapter.py.hbs +6 -9
- package/templates/python-fastapi/hexagonal/app/adapters/inbound/payment_http_adapter.py.hbs +4 -3
- package/templates/python-fastapi/hexagonal/app/adapters/outbound/stripe_adapter.py.hbs +30 -19
- package/templates/python-fastapi/hexagonal/app/config.py.hbs +14 -4
- package/templates/python-fastapi/hexagonal/app/core/domain/user.py.hbs +3 -1
- package/templates/python-fastapi/hexagonal/app/core/payment_service.py.hbs +28 -18
- package/templates/python-fastapi/hexagonal/app/core/ports/__init__.py.hbs +3 -0
- package/templates/python-fastapi/hexagonal/app/core/ports/user_repository.py.hbs +15 -0
- package/templates/python-fastapi/hexagonal/app/infrastructure/database/session.py.hbs +7 -0
- package/templates/python-fastapi/hexagonal/app/infrastructure/database/user_repository.py.hbs +53 -0
- package/templates/python-fastapi/hexagonal/app/infrastructure/security/__init__.py.hbs +0 -0
- package/templates/python-fastapi/hexagonal/app/infrastructure/security/adapters.py.hbs +23 -0
- package/templates/python-fastapi/hexagonal/app/infrastructure/security/jwt.py.hbs +23 -0
- package/templates/python-fastapi/hexagonal/docker-compose.yml.hbs +5 -12
- package/templates/python-fastapi/hexagonal/requirements.txt.hbs +4 -0
- package/templates/python-fastapi/mvc/.gitignore.hbs +76 -0
- package/templates/python-fastapi/mvc/app/controllers/payments.py.hbs +3 -17
- package/templates/python-fastapi/mvc/app/middleware/security.py.hbs +24 -3
- package/templates/python-fastapi/mvc/app/schemas/item.py.hbs +3 -1
- package/templates/python-fastapi/mvc/docker-compose.yml.hbs +5 -12
- package/templates/python-fastapi/mvc/requirements.txt.hbs +3 -1
- package/templates/nodejs-express/hexagonal/src/adapters/outbound/persistence/prisma.ts +0 -5
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
package {{packageName}}.infrastructure.stripe;
|
|
2
|
+
|
|
3
|
+
import com.stripe.model.Customer;
|
|
4
|
+
import com.stripe.model.Event;
|
|
5
|
+
import com.stripe.model.checkout.Session;
|
|
6
|
+
import com.stripe.param.CustomerCreateParams;
|
|
7
|
+
import com.stripe.param.checkout.SessionCreateParams;
|
|
8
|
+
import org.springframework.beans.factory.annotation.Value;
|
|
9
|
+
import org.springframework.stereotype.Service;
|
|
10
|
+
|
|
11
|
+
@Service
|
|
12
|
+
public class StripeGateway {
|
|
13
|
+
|
|
14
|
+
@Value("${stripe.secret-key:sk_test_dummy}")
|
|
15
|
+
private String secretKey;
|
|
16
|
+
|
|
17
|
+
@Value("${stripe.webhook-secret:whsec_dummy}")
|
|
18
|
+
private String webhookSecret;
|
|
19
|
+
|
|
20
|
+
@Value("${frontend.url:http://localhost:3000}")
|
|
21
|
+
private String frontendUrl;
|
|
22
|
+
|
|
23
|
+
@jakarta.annotation.PostConstruct
|
|
24
|
+
public void init() {
|
|
25
|
+
com.stripe.Stripe.apiKey = secretKey;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
public Customer createCustomer(String email, String userId) throws Exception {
|
|
29
|
+
CustomerCreateParams params = CustomerCreateParams.builder()
|
|
30
|
+
.setEmail(email)
|
|
31
|
+
.putMetadata("userId", userId)
|
|
32
|
+
.build();
|
|
33
|
+
return Customer.create(params);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
public Session createCheckoutSession(String customerId, String priceId, String userId) throws Exception {
|
|
37
|
+
SessionCreateParams params = SessionCreateParams.builder()
|
|
38
|
+
.setCustomer(customerId)
|
|
39
|
+
.setSuccessUrl(frontendUrl + "/success?session_id={CHECKOUT_SESSION_ID}")
|
|
40
|
+
.setCancelUrl(frontendUrl + "/cancel")
|
|
41
|
+
.addLineItem(SessionCreateParams.LineItem.builder()
|
|
42
|
+
.setPrice(priceId)
|
|
43
|
+
.setQuantity(1L)
|
|
44
|
+
.build())
|
|
45
|
+
.setMode(SessionCreateParams.Mode.SUBSCRIPTION)
|
|
46
|
+
.setClientReferenceId(userId)
|
|
47
|
+
.build();
|
|
48
|
+
return Session.create(params);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
public com.stripe.model.billingportal.Session createPortalSession(String customerId) throws Exception {
|
|
52
|
+
com.stripe.param.billingportal.SessionCreateParams params =
|
|
53
|
+
com.stripe.param.billingportal.SessionCreateParams.builder()
|
|
54
|
+
.setCustomer(customerId)
|
|
55
|
+
.setReturnUrl(frontendUrl + "/account")
|
|
56
|
+
.build();
|
|
57
|
+
return com.stripe.model.billingportal.Session.create(params);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
public Event constructWebhookEvent(String payload, String sigHeader) throws Exception {
|
|
61
|
+
return com.stripe.net.Webhook.constructEvent(payload, sigHeader, webhookSecret);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
spring.application.name={{projectNameKebabCase}}
|
|
2
2
|
|
|
3
3
|
# Database Configuration
|
|
4
|
-
spring.datasource.url=jdbc:postgresql
|
|
4
|
+
spring.datasource.url=jdbc:postgresql://${DB_HOST:localhost}:5432/{{projectNameKebabCase}}
|
|
5
5
|
spring.datasource.username=postgres
|
|
6
6
|
spring.datasource.password=postgres
|
|
7
7
|
spring.datasource.driver-class-name=org.postgresql.Driver
|
|
@@ -12,14 +12,13 @@ spring.jpa.hibernate.ddl-auto=update
|
|
|
12
12
|
spring.jpa.show-sql=true
|
|
13
13
|
spring.jpa.properties.hibernate.format_sql=true
|
|
14
14
|
|
|
15
|
-
# JWT
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
application.security.jwt.refresh-token.expiration=604800000
|
|
15
|
+
# JWT (property name matches SecurityAdapters.java @Value)
|
|
16
|
+
jwt.secret=${JWT_SECRET:{{jwtSecretKey}}}
|
|
17
|
+
jwt.expiration=86400000
|
|
19
18
|
|
|
20
19
|
# Stripe
|
|
21
|
-
stripe.secret-key=${STRIPE_SECRET_KEY}
|
|
22
|
-
stripe.webhook-secret=${STRIPE_WEBHOOK_SECRET}
|
|
20
|
+
stripe.secret-key=${STRIPE_SECRET_KEY:sk_test_dummy}
|
|
21
|
+
stripe.webhook-secret=${STRIPE_WEBHOOK_SECRET:whsec_dummy}
|
|
23
22
|
|
|
24
23
|
# Frontend URL (for Stripe redirect URLs)
|
|
25
24
|
frontend.url=${FRONTEND_URL:http://localhost:3000}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Compiled class files
|
|
2
|
+
*.class
|
|
3
|
+
|
|
4
|
+
# Log files
|
|
5
|
+
*.log
|
|
6
|
+
|
|
7
|
+
# Package files
|
|
8
|
+
*.jar
|
|
9
|
+
*.war
|
|
10
|
+
*.nar
|
|
11
|
+
*.ear
|
|
12
|
+
*.zip
|
|
13
|
+
*.tar.gz
|
|
14
|
+
*.rar
|
|
15
|
+
|
|
16
|
+
# Virtual machine crash logs
|
|
17
|
+
hs_err_pid*
|
|
18
|
+
replay_pid*
|
|
19
|
+
|
|
20
|
+
# Maven
|
|
21
|
+
target/
|
|
22
|
+
.mvn/wrapper/maven-wrapper.jar
|
|
23
|
+
!**/src/main/**/target/
|
|
24
|
+
!**/src/test/**/target/
|
|
25
|
+
|
|
26
|
+
# Gradle
|
|
27
|
+
.gradle
|
|
28
|
+
build/
|
|
29
|
+
!gradle/wrapper/gradle-wrapper.jar
|
|
30
|
+
!**/src/main/**/build/
|
|
31
|
+
!**/src/test/**/build/
|
|
32
|
+
|
|
33
|
+
# Spring Boot
|
|
34
|
+
spring-shell.log
|
|
35
|
+
|
|
36
|
+
# STS / Eclipse
|
|
37
|
+
.apt_generated
|
|
38
|
+
.classpath
|
|
39
|
+
.factorypath
|
|
40
|
+
.project
|
|
41
|
+
.settings
|
|
42
|
+
.springBeans
|
|
43
|
+
.sts4-cache
|
|
44
|
+
|
|
45
|
+
# IntelliJ IDEA
|
|
46
|
+
.idea/
|
|
47
|
+
*.iws
|
|
48
|
+
*.iml
|
|
49
|
+
*.ipr
|
|
50
|
+
|
|
51
|
+
# VS Code
|
|
52
|
+
.vscode/
|
|
53
|
+
|
|
54
|
+
# NetBeans
|
|
55
|
+
/nbproject/private/
|
|
56
|
+
/nbbuild/
|
|
57
|
+
/nbdist/
|
|
58
|
+
/.nb-gradle/
|
|
59
|
+
|
|
60
|
+
# Environment
|
|
61
|
+
.env
|
|
62
|
+
.env.local
|
|
63
|
+
.env.*.local
|
|
64
|
+
!.env.example
|
|
65
|
+
application-local.yml
|
|
66
|
+
application-local.properties
|
|
67
|
+
application-dev.yml
|
|
68
|
+
application-dev.properties
|
|
69
|
+
|
|
70
|
+
# OS
|
|
71
|
+
.DS_Store
|
|
72
|
+
Thumbs.db
|
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
version: '3.8'
|
|
2
|
-
|
|
3
1
|
services:
|
|
4
|
-
|
|
2
|
+
db:
|
|
5
3
|
image: postgres:15-alpine
|
|
6
4
|
container_name: {{projectNameKebabCase}}-db
|
|
7
5
|
environment:
|
|
@@ -13,6 +11,11 @@ services:
|
|
|
13
11
|
volumes:
|
|
14
12
|
- postgres_data:/var/lib/postgresql/data
|
|
15
13
|
restart: unless-stopped
|
|
14
|
+
healthcheck:
|
|
15
|
+
test: ["CMD-SHELL", "pg_isready -U postgres"]
|
|
16
|
+
interval: 10s
|
|
17
|
+
timeout: 5s
|
|
18
|
+
retries: 5
|
|
16
19
|
|
|
17
20
|
volumes:
|
|
18
21
|
postgres_data:
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
package {{packageName}}.adapters.outbound.security;
|
|
2
|
+
|
|
3
|
+
import io.jsonwebtoken.Claims;
|
|
4
|
+
import io.jsonwebtoken.Jwts;
|
|
5
|
+
import io.jsonwebtoken.security.Keys;
|
|
6
|
+
import jakarta.servlet.FilterChain;
|
|
7
|
+
import jakarta.servlet.ServletException;
|
|
8
|
+
import jakarta.servlet.http.HttpServletRequest;
|
|
9
|
+
import jakarta.servlet.http.HttpServletResponse;
|
|
10
|
+
import org.springframework.beans.factory.annotation.Value;
|
|
11
|
+
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
|
12
|
+
import org.springframework.security.core.context.SecurityContextHolder;
|
|
13
|
+
import org.springframework.security.core.userdetails.User;
|
|
14
|
+
import org.springframework.security.core.userdetails.UserDetails;
|
|
15
|
+
import org.springframework.stereotype.Component;
|
|
16
|
+
import org.springframework.web.filter.OncePerRequestFilter;
|
|
17
|
+
|
|
18
|
+
import javax.crypto.SecretKey;
|
|
19
|
+
import java.io.IOException;
|
|
20
|
+
import java.nio.charset.StandardCharsets;
|
|
21
|
+
import java.util.Collections;
|
|
22
|
+
|
|
23
|
+
@Component
|
|
24
|
+
public class JwtFilter extends OncePerRequestFilter {
|
|
25
|
+
|
|
26
|
+
@Value("${application.security.jwt.secret-key}")
|
|
27
|
+
private String jwtSecret;
|
|
28
|
+
|
|
29
|
+
private SecretKey getSigningKey() {
|
|
30
|
+
return Keys.hmacShaKeyFor(jwtSecret.getBytes(StandardCharsets.UTF_8));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
@Override
|
|
34
|
+
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
|
|
35
|
+
throws ServletException, IOException {
|
|
36
|
+
|
|
37
|
+
final String authHeader = request.getHeader("Authorization");
|
|
38
|
+
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
|
|
39
|
+
filterChain.doFilter(request, response);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
final String jwt = authHeader.substring(7);
|
|
44
|
+
try {
|
|
45
|
+
Claims claims = Jwts.parser()
|
|
46
|
+
.verifyWith(getSigningKey())
|
|
47
|
+
.build()
|
|
48
|
+
.parseSignedClaims(jwt)
|
|
49
|
+
.getPayload();
|
|
50
|
+
|
|
51
|
+
String userId = claims.getSubject();
|
|
52
|
+
|
|
53
|
+
if (userId != null && SecurityContextHolder.getContext().getAuthentication() == null) {
|
|
54
|
+
UserDetails userDetails = User.builder()
|
|
55
|
+
.username(userId)
|
|
56
|
+
.password("")
|
|
57
|
+
.authorities(Collections.emptyList())
|
|
58
|
+
.build();
|
|
59
|
+
|
|
60
|
+
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
|
|
61
|
+
userDetails, null, userDetails.getAuthorities());
|
|
62
|
+
|
|
63
|
+
SecurityContextHolder.getContext().setAuthentication(authToken);
|
|
64
|
+
}
|
|
65
|
+
} catch (Exception e) {
|
|
66
|
+
// Token invalid or expired — continue without setting authentication
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
filterChain.doFilter(request, response);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
package {{packageName}}.adapters.outbound.security;
|
|
2
|
+
|
|
3
|
+
import org.springframework.context.annotation.Bean;
|
|
4
|
+
import org.springframework.context.annotation.Configuration;
|
|
5
|
+
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
|
6
|
+
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
|
7
|
+
import org.springframework.security.config.http.SessionCreationPolicy;
|
|
8
|
+
import org.springframework.security.web.SecurityFilterChain;
|
|
9
|
+
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
|
10
|
+
|
|
11
|
+
@Configuration
|
|
12
|
+
@EnableWebSecurity
|
|
13
|
+
public class SecurityConfig {
|
|
14
|
+
|
|
15
|
+
private final JwtFilter jwtFilter;
|
|
16
|
+
|
|
17
|
+
public SecurityConfig(JwtFilter jwtFilter) {
|
|
18
|
+
this.jwtFilter = jwtFilter;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
@Bean
|
|
22
|
+
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
|
23
|
+
return http
|
|
24
|
+
.csrf(csrf -> csrf.disable())
|
|
25
|
+
.authorizeHttpRequests(auth -> auth
|
|
26
|
+
.requestMatchers("/api/auth/**").permitAll()
|
|
27
|
+
.requestMatchers("/api/payments/webhook").permitAll()
|
|
28
|
+
.requestMatchers("/actuator/**").permitAll()
|
|
29
|
+
.anyRequest().authenticated()
|
|
30
|
+
)
|
|
31
|
+
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
|
|
32
|
+
.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class)
|
|
33
|
+
.build();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -23,7 +23,7 @@ public class PaymentService {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
public String createCheckoutSession(String userId, String priceId) throws Exception {
|
|
26
|
-
User user = userRepository.findById(
|
|
26
|
+
User user = userRepository.findById(userId)
|
|
27
27
|
.orElseThrow(() -> new RuntimeException("User not found"));
|
|
28
28
|
|
|
29
29
|
String customerId = user.getStripeCustomerId();
|
|
@@ -39,7 +39,7 @@ public class PaymentService {
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
public String createPortalSession(String userId) throws Exception {
|
|
42
|
-
User user = userRepository.findById(
|
|
42
|
+
User user = userRepository.findById(userId)
|
|
43
43
|
.orElseThrow(() -> new RuntimeException("User not found"));
|
|
44
44
|
|
|
45
45
|
if (user.getStripeCustomerId() == null) {
|
|
@@ -57,7 +57,7 @@ public class PaymentService {
|
|
|
57
57
|
Session session = (Session) event.getDataObjectDeserializer().getObject().orElse(null);
|
|
58
58
|
if (session != null && session.getClientReferenceId() != null) {
|
|
59
59
|
String userId = session.getClientReferenceId();
|
|
60
|
-
userRepository.findById(
|
|
60
|
+
userRepository.findById(userId).ifPresent(user -> {
|
|
61
61
|
user.setStripeCustomerId(session.getCustomer());
|
|
62
62
|
userRepository.save(user);
|
|
63
63
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
spring.application.name={{projectNameKebabCase}}
|
|
2
2
|
|
|
3
3
|
# Database Configuration
|
|
4
|
-
spring.datasource.url=jdbc:postgresql
|
|
4
|
+
spring.datasource.url=jdbc:postgresql://${DB_HOST:localhost}:5432/{{projectNameKebabCase}}
|
|
5
5
|
spring.datasource.username=postgres
|
|
6
6
|
spring.datasource.password=postgres
|
|
7
7
|
spring.datasource.driver-class-name=org.postgresql.Driver
|
|
@@ -13,13 +13,13 @@ spring.jpa.show-sql=true
|
|
|
13
13
|
spring.jpa.properties.hibernate.format_sql=true
|
|
14
14
|
|
|
15
15
|
# JWT
|
|
16
|
-
application.security.jwt.secret-key
|
|
16
|
+
application.security.jwt.secret-key=${JWT_SECRET:{{jwtSecretKey}}}
|
|
17
17
|
application.security.jwt.expiration=86400000
|
|
18
18
|
application.security.jwt.refresh-token.expiration=604800000
|
|
19
19
|
|
|
20
20
|
# Stripe
|
|
21
|
-
stripe.secret-key=${STRIPE_SECRET_KEY}
|
|
22
|
-
stripe.webhook-secret=${STRIPE_WEBHOOK_SECRET}
|
|
21
|
+
stripe.secret-key=${STRIPE_SECRET_KEY:sk_test_dummy}
|
|
22
|
+
stripe.webhook-secret=${STRIPE_WEBHOOK_SECRET:whsec_dummy}
|
|
23
23
|
|
|
24
24
|
# Frontend URL (for Stripe redirect URLs)
|
|
25
25
|
frontend.url=${FRONTEND_URL:http://localhost:3000}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Compiled class files
|
|
2
|
+
*.class
|
|
3
|
+
|
|
4
|
+
# Log files
|
|
5
|
+
*.log
|
|
6
|
+
|
|
7
|
+
# Package files
|
|
8
|
+
*.jar
|
|
9
|
+
*.war
|
|
10
|
+
*.nar
|
|
11
|
+
*.ear
|
|
12
|
+
*.zip
|
|
13
|
+
*.tar.gz
|
|
14
|
+
*.rar
|
|
15
|
+
|
|
16
|
+
# Virtual machine crash logs
|
|
17
|
+
hs_err_pid*
|
|
18
|
+
replay_pid*
|
|
19
|
+
|
|
20
|
+
# Maven
|
|
21
|
+
target/
|
|
22
|
+
.mvn/wrapper/maven-wrapper.jar
|
|
23
|
+
!**/src/main/**/target/
|
|
24
|
+
!**/src/test/**/target/
|
|
25
|
+
|
|
26
|
+
# Gradle
|
|
27
|
+
.gradle
|
|
28
|
+
build/
|
|
29
|
+
!gradle/wrapper/gradle-wrapper.jar
|
|
30
|
+
!**/src/main/**/build/
|
|
31
|
+
!**/src/test/**/build/
|
|
32
|
+
|
|
33
|
+
# Spring Boot
|
|
34
|
+
spring-shell.log
|
|
35
|
+
|
|
36
|
+
# STS / Eclipse
|
|
37
|
+
.apt_generated
|
|
38
|
+
.classpath
|
|
39
|
+
.factorypath
|
|
40
|
+
.project
|
|
41
|
+
.settings
|
|
42
|
+
.springBeans
|
|
43
|
+
.sts4-cache
|
|
44
|
+
|
|
45
|
+
# IntelliJ IDEA
|
|
46
|
+
.idea/
|
|
47
|
+
*.iws
|
|
48
|
+
*.iml
|
|
49
|
+
*.ipr
|
|
50
|
+
|
|
51
|
+
# VS Code
|
|
52
|
+
.vscode/
|
|
53
|
+
|
|
54
|
+
# NetBeans
|
|
55
|
+
/nbproject/private/
|
|
56
|
+
/nbbuild/
|
|
57
|
+
/nbdist/
|
|
58
|
+
/.nb-gradle/
|
|
59
|
+
|
|
60
|
+
# Environment
|
|
61
|
+
.env
|
|
62
|
+
.env.local
|
|
63
|
+
.env.*.local
|
|
64
|
+
!.env.example
|
|
65
|
+
application-local.yml
|
|
66
|
+
application-local.properties
|
|
67
|
+
application-dev.yml
|
|
68
|
+
application-dev.properties
|
|
69
|
+
|
|
70
|
+
# OS
|
|
71
|
+
.DS_Store
|
|
72
|
+
Thumbs.db
|
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
version: '3.8'
|
|
2
|
-
|
|
3
1
|
services:
|
|
4
|
-
|
|
2
|
+
db:
|
|
5
3
|
image: postgres:15-alpine
|
|
6
4
|
container_name: {{projectNameKebabCase}}-db
|
|
7
5
|
environment:
|
|
@@ -13,6 +11,11 @@ services:
|
|
|
13
11
|
volumes:
|
|
14
12
|
- postgres_data:/var/lib/postgresql/data
|
|
15
13
|
restart: unless-stopped
|
|
14
|
+
healthcheck:
|
|
15
|
+
test: ["CMD-SHELL", "pg_isready -U postgres"]
|
|
16
|
+
interval: 10s
|
|
17
|
+
timeout: 5s
|
|
18
|
+
retries: 5
|
|
16
19
|
|
|
17
20
|
volumes:
|
|
18
21
|
postgres_data:
|
package/templates/java-spring/mvc/src/main/java/{{packagePath}}/config/SecurityConfig.java.hbs
CHANGED
|
@@ -27,18 +27,19 @@ public class SecurityConfig {
|
|
|
27
27
|
.csrf(csrf -> csrf.disable())
|
|
28
28
|
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
|
|
29
29
|
.authorizeHttpRequests(auth -> auth
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
30
|
+
.requestMatchers("/api/auth/**").permitAll()
|
|
31
|
+
.requestMatchers("/api/payments/webhook").permitAll()
|
|
32
|
+
.requestMatchers("/api/health").permitAll()
|
|
33
|
+
.requestMatchers("/actuator/health").permitAll()
|
|
34
|
+
.anyRequest().authenticated()
|
|
35
|
+
)
|
|
36
|
+
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
|
|
36
37
|
|
|
37
|
-
|
|
38
|
-
}
|
|
38
|
+
return http.build();
|
|
39
|
+
}
|
|
39
40
|
|
|
40
|
-
@Bean
|
|
41
|
-
public PasswordEncoder passwordEncoder() {
|
|
42
|
-
|
|
41
|
+
@Bean
|
|
42
|
+
public PasswordEncoder passwordEncoder() {
|
|
43
|
+
return new BCryptPasswordEncoder();
|
|
44
|
+
}
|
|
43
45
|
}
|
|
44
|
-
}
|
package/templates/java-spring/mvc/src/main/java/{{packagePath}}/controller/AuthController.java.hbs
CHANGED
|
@@ -10,6 +10,7 @@ import org.springframework.web.bind.annotation.*;
|
|
|
10
10
|
import org.springframework.security.core.context.SecurityContextHolder;
|
|
11
11
|
import org.springframework.security.core.Authentication;
|
|
12
12
|
|
|
13
|
+
import java.util.HashMap;
|
|
13
14
|
import java.util.Map;
|
|
14
15
|
|
|
15
16
|
@RestController
|
|
@@ -48,17 +49,17 @@ public class AuthController {
|
|
|
48
49
|
if (authentication == null || !authentication.isAuthenticated()) {
|
|
49
50
|
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
|
|
50
51
|
}
|
|
51
|
-
|
|
52
|
+
|
|
52
53
|
try {
|
|
53
|
-
String userId = (String) authentication.getPrincipal();
|
|
54
|
+
String userId = (String) authentication.getPrincipal();
|
|
54
55
|
User user = authService.getMe(userId);
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
));
|
|
56
|
+
// Use HashMap instead of Map.of() — tolerates null values (e.g. name may be null)
|
|
57
|
+
Map<String, Object> result = new HashMap<>();
|
|
58
|
+
result.put("id", user.getId());
|
|
59
|
+
result.put("email", user.getEmail());
|
|
60
|
+
result.put("name", user.getName());
|
|
61
|
+
return ResponseEntity.ok(result);
|
|
60
62
|
} catch (Exception e) {
|
|
61
|
-
// Keep original behavior if principal isn't id
|
|
62
63
|
return ResponseEntity.ok(Map.of("message", "Authenticated"));
|
|
63
64
|
}
|
|
64
65
|
}
|
|
@@ -6,7 +6,6 @@ import org.slf4j.LoggerFactory;
|
|
|
6
6
|
import org.springframework.http.HttpStatus;
|
|
7
7
|
import org.springframework.http.ResponseEntity;
|
|
8
8
|
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
|
9
|
-
import org.springframework.security.core.userdetails.UserDetails;
|
|
10
9
|
import org.springframework.web.bind.annotation.*;
|
|
11
10
|
|
|
12
11
|
import java.util.Map;
|
|
@@ -29,16 +28,16 @@ public class PaymentsController {
|
|
|
29
28
|
*/
|
|
30
29
|
@PostMapping("/checkout")
|
|
31
30
|
public ResponseEntity<?> createCheckout(
|
|
32
|
-
@AuthenticationPrincipal
|
|
31
|
+
@AuthenticationPrincipal String userId,
|
|
33
32
|
@RequestBody Map<String, String> body) {
|
|
34
33
|
try {
|
|
35
34
|
String priceId = body.get("priceId");
|
|
36
35
|
if (priceId == null || priceId.isEmpty()) {
|
|
37
36
|
return ResponseEntity.badRequest().body(Map.of("error", "priceId is required"));
|
|
38
37
|
}
|
|
39
|
-
//
|
|
38
|
+
// userId is set directly in SecurityContextHolder by JwtAuthenticationFilter
|
|
40
39
|
com.stripe.model.checkout.Session session =
|
|
41
|
-
stripeService.createCheckoutSession(
|
|
40
|
+
stripeService.createCheckoutSession(userId, priceId);
|
|
42
41
|
return ResponseEntity.ok(Map.of("url", session.getUrl()));
|
|
43
42
|
} catch (Exception e) {
|
|
44
43
|
logger.error("Error creating checkout session", e);
|
|
@@ -51,10 +50,10 @@ public class PaymentsController {
|
|
|
51
50
|
* Requires authentication. Opens Stripe Billing Portal.
|
|
52
51
|
*/
|
|
53
52
|
@PostMapping("/portal")
|
|
54
|
-
public ResponseEntity<?> createPortal(@AuthenticationPrincipal
|
|
53
|
+
public ResponseEntity<?> createPortal(@AuthenticationPrincipal String userId) {
|
|
55
54
|
try {
|
|
56
55
|
com.stripe.model.billingportal.Session session =
|
|
57
|
-
stripeService.createPortalSession(
|
|
56
|
+
stripeService.createPortalSession(userId);
|
|
58
57
|
return ResponseEntity.ok(Map.of("url", session.getUrl()));
|
|
59
58
|
} catch (Exception e) {
|
|
60
59
|
logger.error("Error creating portal session", e);
|
package/templates/java-spring/mvc/src/main/java/{{packagePath}}/service/StripeService.java.hbs
CHANGED
|
@@ -45,7 +45,7 @@ public class StripeService {
|
|
|
45
45
|
* Retrieve or create a Stripe customer for the given user, then open a checkout session.
|
|
46
46
|
*/
|
|
47
47
|
public Session createCheckoutSession(String userId, String priceId) throws StripeException {
|
|
48
|
-
User user = userRepository.findById(
|
|
48
|
+
User user = userRepository.findById(userId)
|
|
49
49
|
.orElseThrow(() -> new RuntimeException("User not found"));
|
|
50
50
|
|
|
51
51
|
String customerId = user.getStripeCustomerId();
|
|
@@ -81,7 +81,7 @@ public class StripeService {
|
|
|
81
81
|
* Open the Stripe Billing Portal for the given customer.
|
|
82
82
|
*/
|
|
83
83
|
public com.stripe.model.billingportal.Session createPortalSession(String userId) throws StripeException {
|
|
84
|
-
User user = userRepository.findById(
|
|
84
|
+
User user = userRepository.findById(userId)
|
|
85
85
|
.orElseThrow(() -> new RuntimeException("User not found"));
|
|
86
86
|
|
|
87
87
|
if (user.getStripeCustomerId() == null) {
|
|
@@ -113,7 +113,7 @@ public class StripeService {
|
|
|
113
113
|
Session session = (Session) event.getDataObjectDeserializer().getObject().orElse(null);
|
|
114
114
|
if (session != null && session.getClientReferenceId() != null) {
|
|
115
115
|
String userId = session.getClientReferenceId();
|
|
116
|
-
userRepository.findById(
|
|
116
|
+
userRepository.findById(userId).ifPresent(user -> {
|
|
117
117
|
user.setStripeCustomerId(session.getCustomer());
|
|
118
118
|
userRepository.save(user);
|
|
119
119
|
});
|
|
@@ -1,32 +1,35 @@
|
|
|
1
1
|
spring:
|
|
2
2
|
application:
|
|
3
3
|
name: {{projectNameKebabCase}}
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
format_sql: true
|
|
18
|
-
dialect: org.hibernate.dialect.PostgreSQLDialect
|
|
4
|
+
datasource:
|
|
5
|
+
url: jdbc:postgresql://localhost:5432/{{projectNameSnakeCase}}
|
|
6
|
+
username: postgres
|
|
7
|
+
password: postgres
|
|
8
|
+
driver-class-name: org.postgresql.Driver
|
|
9
|
+
jpa:
|
|
10
|
+
hibernate:
|
|
11
|
+
ddl-auto: update
|
|
12
|
+
show-sql: true
|
|
13
|
+
properties:
|
|
14
|
+
hibernate:
|
|
15
|
+
format_sql: true
|
|
16
|
+
dialect: org.hibernate.dialect.PostgreSQLDialect
|
|
19
17
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
console:
|
|
23
|
-
enabled: true
|
|
24
|
-
path: /h2-console
|
|
18
|
+
server:
|
|
19
|
+
port: 8080
|
|
25
20
|
|
|
26
|
-
|
|
27
|
-
|
|
21
|
+
logging:
|
|
22
|
+
level:
|
|
23
|
+
root: INFO
|
|
24
|
+
{{packageName}}: DEBUG
|
|
28
25
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
26
|
+
jwt:
|
|
27
|
+
secret: ${JWT_SECRET:{{jwtSecretKey}}}
|
|
28
|
+
expiration: 604800000
|
|
29
|
+
|
|
30
|
+
stripe:
|
|
31
|
+
secret-key: ${STRIPE_SECRET_KEY:sk_test_dummy}
|
|
32
|
+
webhook-secret: ${STRIPE_WEBHOOK_SECRET:whsec_dummy}
|
|
33
|
+
|
|
34
|
+
frontend:
|
|
35
|
+
url: ${FRONTEND_URL:http://localhost:3000}
|