├── .gitignore ├── admin-ui ├── .dockerignore ├── .env ├── .gitignore ├── Dockerfile ├── README.md ├── configuration │ └── nginx.conf ├── package.json ├── public │ ├── favicon.ico │ ├── index.html │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── robots.txt ├── src │ ├── App.scss │ ├── App.tsx │ ├── Components │ │ └── Pagination.tsx │ ├── Login.tsx │ ├── api │ │ ├── category │ │ │ ├── Category.ts │ │ │ ├── CategoryCountArgs.ts │ │ │ ├── CategoryCreateInput.ts │ │ │ ├── CategoryFindManyArgs.ts │ │ │ ├── CategoryFindUniqueArgs.ts │ │ │ ├── CategoryListRelationFilter.ts │ │ │ ├── CategoryOrderByInput.ts │ │ │ ├── CategoryUpdateInput.ts │ │ │ ├── CategoryWhereInput.ts │ │ │ ├── CategoryWhereUniqueInput.ts │ │ │ ├── CreateCategoryArgs.ts │ │ │ ├── DeleteCategoryArgs.ts │ │ │ ├── ProductCreateNestedManyWithoutCategoriesInput.ts │ │ │ ├── ProductUpdateManyWithoutCategoriesInput.ts │ │ │ └── UpdateCategoryArgs.ts │ │ ├── order │ │ │ ├── CreateOrderArgs.ts │ │ │ ├── DeleteOrderArgs.ts │ │ │ ├── Order.ts │ │ │ ├── OrderCountArgs.ts │ │ │ ├── OrderCreateInput.ts │ │ │ ├── OrderFindManyArgs.ts │ │ │ ├── OrderFindUniqueArgs.ts │ │ │ ├── OrderListRelationFilter.ts │ │ │ ├── OrderOrderByInput.ts │ │ │ ├── OrderUpdateInput.ts │ │ │ ├── OrderWhereInput.ts │ │ │ ├── OrderWhereUniqueInput.ts │ │ │ ├── ProductCreateNestedManyWithoutOrdersInput.ts │ │ │ ├── ProductUpdateManyWithoutOrdersInput.ts │ │ │ └── UpdateOrderArgs.ts │ │ ├── product │ │ │ ├── CreateProductArgs.ts │ │ │ ├── DeleteProductArgs.ts │ │ │ ├── OrderCreateNestedManyWithoutProductsInput.ts │ │ │ ├── OrderUpdateManyWithoutProductsInput.ts │ │ │ ├── Product.ts │ │ │ ├── ProductCountArgs.ts │ │ │ ├── ProductCreateInput.ts │ │ │ ├── ProductFindManyArgs.ts │ │ │ ├── ProductFindUniqueArgs.ts │ │ │ ├── ProductListRelationFilter.ts │ │ │ ├── ProductOrderByInput.ts │ │ │ ├── ProductUpdateInput.ts │ │ │ ├── ProductWhereInput.ts │ │ │ ├── ProductWhereUniqueInput.ts │ │ │ ├── ReviewCreateNestedManyWithoutProductsInput.ts │ │ │ ├── ReviewUpdateManyWithoutProductsInput.ts │ │ │ └── UpdateProductArgs.ts │ │ ├── review │ │ │ ├── CreateReviewArgs.ts │ │ │ ├── DeleteReviewArgs.ts │ │ │ ├── Review.ts │ │ │ ├── ReviewCountArgs.ts │ │ │ ├── ReviewCreateInput.ts │ │ │ ├── ReviewFindManyArgs.ts │ │ │ ├── ReviewFindUniqueArgs.ts │ │ │ ├── ReviewListRelationFilter.ts │ │ │ ├── ReviewOrderByInput.ts │ │ │ ├── ReviewUpdateInput.ts │ │ │ ├── ReviewWhereInput.ts │ │ │ ├── ReviewWhereUniqueInput.ts │ │ │ └── UpdateReviewArgs.ts │ │ └── user │ │ │ ├── CreateUserArgs.ts │ │ │ ├── DeleteUserArgs.ts │ │ │ ├── OrderCreateNestedManyWithoutUsersInput.ts │ │ │ ├── OrderUpdateManyWithoutUsersInput.ts │ │ │ ├── ReviewCreateNestedManyWithoutUsersInput.ts │ │ │ ├── ReviewUpdateManyWithoutUsersInput.ts │ │ │ ├── UpdateUserArgs.ts │ │ │ ├── User.ts │ │ │ ├── UserCountArgs.ts │ │ │ ├── UserCreateInput.ts │ │ │ ├── UserFindManyArgs.ts │ │ │ ├── UserFindUniqueArgs.ts │ │ │ ├── UserListRelationFilter.ts │ │ │ ├── UserOrderByInput.ts │ │ │ ├── UserUpdateInput.ts │ │ │ ├── UserWhereInput.ts │ │ │ └── UserWhereUniqueInput.ts │ ├── auth-provider │ │ ├── ra-auth-http.ts │ │ └── ra-auth-jwt.ts │ ├── auth.ts │ ├── category │ │ ├── CategoryCreate.tsx │ │ ├── CategoryEdit.tsx │ │ ├── CategoryList.tsx │ │ ├── CategoryShow.tsx │ │ └── CategoryTitle.ts │ ├── constants.ts │ ├── data-provider │ │ └── graphqlDataProvider.ts │ ├── index.css │ ├── index.tsx │ ├── login.scss │ ├── order │ │ ├── OrderCreate.tsx │ │ ├── OrderEdit.tsx │ │ ├── OrderList.tsx │ │ ├── OrderShow.tsx │ │ └── OrderTitle.ts │ ├── pages │ │ └── Dashboard.tsx │ ├── product │ │ ├── ProductCreate.tsx │ │ ├── ProductEdit.tsx │ │ ├── ProductList.tsx │ │ ├── ProductShow.tsx │ │ └── ProductTitle.ts │ ├── reportWebVitals.ts │ ├── review │ │ ├── ReviewCreate.tsx │ │ ├── ReviewEdit.tsx │ │ ├── ReviewList.tsx │ │ ├── ReviewShow.tsx │ │ └── ReviewTitle.ts │ ├── setupTests.ts │ ├── theme │ │ └── theme.ts │ ├── types.ts │ ├── user │ │ ├── EnumRoles.ts │ │ ├── RolesOptions.ts │ │ ├── UserCreate.tsx │ │ ├── UserEdit.tsx │ │ ├── UserList.tsx │ │ ├── UserShow.tsx │ │ ├── UserTitle.ts │ │ └── roles.ts │ └── util │ │ ├── BooleanFilter.ts │ │ ├── BooleanNullableFilter.ts │ │ ├── DateTimeFilter.ts │ │ ├── DateTimeNullableFilter.ts │ │ ├── FloatFilter.ts │ │ ├── FloatNullableFilter.ts │ │ ├── IntFilter.ts │ │ ├── IntNullableFilter.ts │ │ ├── JsonFilter.ts │ │ ├── JsonNullableFilter.ts │ │ ├── MetaQueryPayload.ts │ │ ├── QueryMode.ts │ │ ├── SortOrder.ts │ │ ├── StringFilter.ts │ │ └── StringNullableFilter.ts └── tsconfig.json ├── client ├── .env ├── .eslintrc.json ├── .gitignore ├── README.md ├── components.json ├── dockerfile ├── next.config.js ├── package-lock.json ├── package.json ├── postcss.config.js ├── prisma │ └── schema.prisma ├── public │ ├── amazon-logo-white.png │ ├── amazon-logo.png │ ├── cart.png │ ├── home │ │ ├── farzi.jpg │ │ ├── gift-card.png │ │ ├── home.jpg │ │ ├── homepod.png │ │ └── watch.png │ ├── nav-sprite-global-2x-hm-dsk-reorg._CB405936311_.png │ ├── products │ │ ├── product1.png │ │ ├── product2.webp │ │ └── product3.webp │ └── trust │ │ ├── trust1.png │ │ ├── trust2.png │ │ ├── trust3.png │ │ ├── trust4.png │ │ └── trust5.png ├── src │ ├── app │ │ ├── admin │ │ │ ├── category │ │ │ │ ├── add-category │ │ │ │ │ └── page.tsx │ │ │ │ ├── all-category │ │ │ │ │ ├── data.ts │ │ │ │ │ └── page.tsx │ │ │ │ ├── edit-category │ │ │ │ │ └── [categoryID] │ │ │ │ │ │ └── page.tsx │ │ │ │ └── reports │ │ │ │ │ └── page.tsx │ │ │ ├── dashboard │ │ │ │ ├── api │ │ │ │ │ └── route.ts │ │ │ │ ├── components │ │ │ │ │ ├── charts │ │ │ │ │ │ ├── category-sales │ │ │ │ │ │ │ ├── category-sales.tsx │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ ├── daily-revenue │ │ │ │ │ │ │ ├── daily-revenue.tsx │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ └── monthly-sales │ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ │ └── monthly-sales.tsx │ │ │ │ │ ├── recent-orders │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── recent-orders.tsx │ │ │ │ │ └── stats │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── stats.tsx │ │ │ │ └── page.tsx │ │ │ ├── login │ │ │ │ └── page.tsx │ │ │ ├── logout │ │ │ │ └── page.tsx │ │ │ ├── orders │ │ │ │ ├── [orderId] │ │ │ │ │ ├── components │ │ │ │ │ │ ├── Product.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ └── page.tsx │ │ │ │ └── page.tsx │ │ │ ├── page.tsx │ │ │ └── products │ │ │ │ ├── add-product │ │ │ │ └── page.tsx │ │ │ │ ├── all-products │ │ │ │ ├── data.ts │ │ │ │ └── page.tsx │ │ │ │ ├── edit-product │ │ │ │ └── [productId] │ │ │ │ │ └── page.tsx │ │ │ │ └── reports │ │ │ │ └── page.tsx │ │ ├── cart │ │ │ ├── components │ │ │ │ └── product │ │ │ │ │ ├── index.ts │ │ │ │ │ └── product.tsx │ │ │ └── page.tsx │ │ ├── checkout │ │ │ ├── components │ │ │ │ └── stripe-form.tsx │ │ │ └── page.tsx │ │ ├── favicon.ico │ │ ├── globals.css │ │ ├── layout.tsx │ │ ├── layouts │ │ │ ├── admin-layout.tsx │ │ │ ├── client-store-layout.tsx │ │ │ ├── index.ts │ │ │ └── layouts.tsx │ │ ├── loading.tsx │ │ ├── login │ │ │ └── page.tsx │ │ ├── logout │ │ │ └── page.tsx │ │ ├── my-orders │ │ │ ├── [orderId] │ │ │ │ ├── components │ │ │ │ │ ├── Product.tsx │ │ │ │ │ └── index.ts │ │ │ │ └── page.tsx │ │ │ └── page.tsx │ │ ├── page.tsx │ │ ├── product │ │ │ └── [productId] │ │ │ │ ├── components │ │ │ │ ├── add-review │ │ │ │ │ ├── add-review.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── colors │ │ │ │ │ ├── colors.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── image-slider │ │ │ │ │ ├── image-slider.tsx │ │ │ │ │ └── index.ts │ │ │ │ ├── payment-info │ │ │ │ │ ├── index.ts │ │ │ │ │ └── payment-info.tsx │ │ │ │ ├── review-bars │ │ │ │ │ ├── index.ts │ │ │ │ │ └── review-bars.tsx │ │ │ │ ├── reviews │ │ │ │ │ ├── index.ts │ │ │ │ │ └── reviews.tsx │ │ │ │ ├── trust-slider │ │ │ │ │ ├── index.ts │ │ │ │ │ └── trust-slider.tsx │ │ │ │ └── variants │ │ │ │ │ ├── index.ts │ │ │ │ │ └── variants.tsx │ │ │ │ └── page.tsx │ │ ├── providers.tsx │ │ ├── search │ │ │ ├── components │ │ │ │ ├── filters │ │ │ │ │ ├── filters.tsx │ │ │ │ │ └── index.ts │ │ │ │ └── product │ │ │ │ │ ├── colors │ │ │ │ │ ├── colors.tsx │ │ │ │ │ └── index.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── product.tsx │ │ │ │ │ └── ratings │ │ │ │ │ ├── index.ts │ │ │ │ │ └── ratings.tsx │ │ │ └── page.tsx │ │ ├── signup │ │ │ └── page.tsx │ │ └── success │ │ │ └── page.tsx │ ├── components │ │ ├── admin │ │ │ └── sidebar │ │ │ │ ├── index.ts │ │ │ │ └── sidebar.tsx │ │ └── client │ │ │ ├── apple-cards │ │ │ ├── apple-cards.tsx │ │ │ └── index.ts │ │ │ ├── footer │ │ │ ├── footer.tsx │ │ │ └── index.ts │ │ │ ├── home-cards │ │ │ ├── home-cards.tsx │ │ │ └── index.ts │ │ │ ├── home-carousels │ │ │ ├── home-caroursels.tsx │ │ │ └── index.ts │ │ │ ├── navbar │ │ │ ├── index.ts │ │ │ └── navbar.tsx │ │ │ ├── prime-video │ │ │ ├── index.ts │ │ │ └── prime-video.tsx │ │ │ └── product-grid │ │ │ ├── index.ts │ │ │ └── product-grid.tsx │ ├── lib │ │ ├── api │ │ │ ├── api-client.ts │ │ │ ├── auth.ts │ │ │ ├── category.ts │ │ │ ├── orders.ts │ │ │ ├── products.ts │ │ │ └── search.ts │ │ └── utils.ts │ ├── store │ │ ├── slices │ │ │ ├── auth-slice.ts │ │ │ ├── cart-slice.ts │ │ │ ├── index.ts │ │ │ ├── orders-slice.ts │ │ │ ├── searchSlice.ts │ │ │ └── toasts-slice.ts │ │ └── store.ts │ └── utils │ │ └── types.ts ├── tailwind.config.ts ├── tsconfig.json └── yarn.lock ├── package.json └── server ├── .dockerignore ├── .env ├── .gitignore ├── .prettierignore ├── Dockerfile ├── README.md ├── docker-compose.dev.yml ├── docker-compose.yml ├── nest-cli.json ├── package.json ├── prisma └── schema.prisma ├── schema.graphql ├── scripts ├── clean.ts ├── customSeed.ts └── seed.ts ├── src ├── app.module.ts ├── auth │ ├── Credentials.ts │ ├── IAuthStrategy.ts │ ├── ITokenService.ts │ ├── LoginArgs.ts │ ├── UserInfo.ts │ ├── abac.util.ts │ ├── acl.module.ts │ ├── auth.controller.ts │ ├── auth.module.ts │ ├── auth.resolver.ts │ ├── auth.service.spec.ts │ ├── auth.service.ts │ ├── base │ │ └── token.service.base.ts │ ├── constants.ts │ ├── defaultAuth.guard.ts │ ├── gqlAC.guard.ts │ ├── gqlDefaultAuth.guard.ts │ ├── gqlUserRoles.decorator.ts │ ├── jwt │ │ ├── base │ │ │ └── jwt.strategy.base.ts │ │ ├── jwt.strategy.ts │ │ ├── jwtAuth.guard.ts │ │ └── jwtSecretFactory.ts │ ├── password.service.spec.ts │ ├── password.service.ts │ ├── tempfile │ ├── token.service.ts │ └── userData.decorator.ts ├── category │ ├── base │ │ ├── Category.ts │ │ ├── CategoryCountArgs.ts │ │ ├── CategoryCreateInput.ts │ │ ├── CategoryFindManyArgs.ts │ │ ├── CategoryFindUniqueArgs.ts │ │ ├── CategoryListRelationFilter.ts │ │ ├── CategoryOrderByInput.ts │ │ ├── CategoryUpdateInput.ts │ │ ├── CategoryWhereInput.ts │ │ ├── CategoryWhereUniqueInput.ts │ │ ├── CreateCategoryArgs.ts │ │ ├── DeleteCategoryArgs.ts │ │ ├── ProductCreateNestedManyWithoutCategoriesInput.ts │ │ ├── ProductUpdateManyWithoutCategoriesInput.ts │ │ ├── UpdateCategoryArgs.ts │ │ ├── category.controller.base.spec.ts │ │ ├── category.controller.base.ts │ │ ├── category.module.base.ts │ │ ├── category.resolver.base.ts │ │ └── category.service.base.ts │ ├── category.controller.ts │ ├── category.module.ts │ ├── category.resolver.ts │ └── category.service.ts ├── connectMicroservices.ts ├── constants.ts ├── decorators │ ├── api-nested-query.decorator.ts │ └── public.decorator.ts ├── errors.ts ├── filters │ └── HttpExceptions.filter.ts ├── grants.json ├── health │ ├── base │ │ ├── health.controller.base.ts │ │ └── health.service.base.ts │ ├── health.controller.ts │ ├── health.module.ts │ └── health.service.ts ├── interceptors │ ├── aclFilterResponse.interceptor.ts │ └── aclValidateRequest.interceptor.ts ├── main.ts ├── order │ ├── base │ │ ├── CreateOrderArgs.ts │ │ ├── DeleteOrderArgs.ts │ │ ├── Order.ts │ │ ├── OrderCountArgs.ts │ │ ├── OrderCreateInput.ts │ │ ├── OrderFindManyArgs.ts │ │ ├── OrderFindUniqueArgs.ts │ │ ├── OrderListRelationFilter.ts │ │ ├── OrderOrderByInput.ts │ │ ├── OrderUpdateInput.ts │ │ ├── OrderWhereInput.ts │ │ ├── OrderWhereUniqueInput.ts │ │ ├── ProductCreateNestedManyWithoutOrdersInput.ts │ │ ├── ProductUpdateManyWithoutOrdersInput.ts │ │ ├── UpdateOrderArgs.ts │ │ ├── order.controller.base.spec.ts │ │ ├── order.controller.base.ts │ │ ├── order.module.base.ts │ │ ├── order.resolver.base.ts │ │ └── order.service.base.ts │ ├── order.controller.ts │ ├── order.module.ts │ ├── order.resolver.ts │ └── order.service.ts ├── prisma.util.spec.ts ├── prisma.util.ts ├── prisma │ ├── prisma.module.ts │ └── prisma.service.ts ├── product │ ├── base │ │ ├── CreateProductArgs.ts │ │ ├── DeleteProductArgs.ts │ │ ├── OrderCreateNestedManyWithoutProductsInput.ts │ │ ├── OrderUpdateManyWithoutProductsInput.ts │ │ ├── Product.ts │ │ ├── ProductCountArgs.ts │ │ ├── ProductCreateInput.ts │ │ ├── ProductFindManyArgs.ts │ │ ├── ProductFindUniqueArgs.ts │ │ ├── ProductListRelationFilter.ts │ │ ├── ProductOrderByInput.ts │ │ ├── ProductUpdateInput.ts │ │ ├── ProductWhereInput.ts │ │ ├── ProductWhereUniqueInput.ts │ │ ├── ReviewCreateNestedManyWithoutProductsInput.ts │ │ ├── ReviewUpdateManyWithoutProductsInput.ts │ │ ├── UpdateProductArgs.ts │ │ ├── product.controller.base.spec.ts │ │ ├── product.controller.base.ts │ │ ├── product.module.base.ts │ │ ├── product.resolver.base.ts │ │ └── product.service.base.ts │ ├── product.controller.ts │ ├── product.module.ts │ ├── product.resolver.ts │ └── product.service.ts ├── providers │ └── secrets │ │ ├── base │ │ ├── secretsManager.service.base.spec.ts │ │ └── secretsManager.service.base.ts │ │ ├── secretsManager.module.ts │ │ └── secretsManager.service.ts ├── review │ ├── base │ │ ├── CreateReviewArgs.ts │ │ ├── DeleteReviewArgs.ts │ │ ├── Review.ts │ │ ├── ReviewCountArgs.ts │ │ ├── ReviewCreateInput.ts │ │ ├── ReviewFindManyArgs.ts │ │ ├── ReviewFindUniqueArgs.ts │ │ ├── ReviewListRelationFilter.ts │ │ ├── ReviewOrderByInput.ts │ │ ├── ReviewUpdateInput.ts │ │ ├── ReviewWhereInput.ts │ │ ├── ReviewWhereUniqueInput.ts │ │ ├── UpdateReviewArgs.ts │ │ ├── review.controller.base.spec.ts │ │ ├── review.controller.base.ts │ │ ├── review.module.base.ts │ │ ├── review.resolver.base.ts │ │ └── review.service.base.ts │ ├── review.controller.ts │ ├── review.module.ts │ ├── review.resolver.ts │ └── review.service.ts ├── serveStaticOptions.service.ts ├── swagger.ts ├── swagger │ ├── favicon.png │ ├── logo-amplication-white.svg │ └── swagger.css ├── tests │ ├── auth │ │ ├── constants.ts │ │ ├── jwt │ │ │ └── jwt.strategy.spec.ts │ │ └── token.service.spec.ts │ └── health │ │ └── health.service.spec.ts ├── types.ts ├── user │ ├── base │ │ ├── CreateUserArgs.ts │ │ ├── DeleteUserArgs.ts │ │ ├── OrderCreateNestedManyWithoutUsersInput.ts │ │ ├── OrderUpdateManyWithoutUsersInput.ts │ │ ├── ReviewCreateNestedManyWithoutUsersInput.ts │ │ ├── ReviewUpdateManyWithoutUsersInput.ts │ │ ├── UpdateUserArgs.ts │ │ ├── User.ts │ │ ├── UserCountArgs.ts │ │ ├── UserCreateInput.ts │ │ ├── UserFindManyArgs.ts │ │ ├── UserFindUniqueArgs.ts │ │ ├── UserListRelationFilter.ts │ │ ├── UserOrderByInput.ts │ │ ├── UserUpdateInput.ts │ │ ├── UserWhereInput.ts │ │ ├── UserWhereUniqueInput.ts │ │ ├── user.controller.base.spec.ts │ │ ├── user.controller.base.ts │ │ ├── user.module.base.ts │ │ ├── user.resolver.base.ts │ │ └── user.service.base.ts │ ├── user.controller.ts │ ├── user.module.ts │ ├── user.resolver.ts │ └── user.service.ts ├── util │ ├── BooleanFilter.ts │ ├── BooleanNullableFilter.ts │ ├── DateTimeFilter.ts │ ├── DateTimeNullableFilter.ts │ ├── FloatFilter.ts │ ├── FloatNullableFilter.ts │ ├── IntFilter.ts │ ├── IntNullableFilter.ts │ ├── JsonFilter.ts │ ├── JsonNullableFilter.ts │ ├── MetaQueryPayload.ts │ ├── QueryMode.ts │ ├── SortOrder.ts │ ├── StringFilter.ts │ └── StringNullableFilter.ts └── validators │ ├── index.ts │ ├── is-json-value-validator.spec.ts │ └── is-json-value-validator.ts ├── tsconfig.build.json └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules/ 5 | client/node_modules/ 6 | server/node_modules/ 7 | admin-ui/node_modules/ 8 | /.pnp 9 | .pnp.js 10 | 11 | # testing 12 | /coverage 13 | 14 | # next.js 15 | client/.next/ 16 | client/yarn.lock 17 | /out/ 18 | 19 | # production 20 | /build 21 | 22 | # misc 23 | .DS_Store 24 | *.pem 25 | 26 | # debug 27 | npm-debug.log* 28 | yarn-debug.log* 29 | yarn-error.log* 30 | 31 | # local env files 32 | .env*.local 33 | 34 | # vercel 35 | .vercel 36 | 37 | # typescript 38 | *.tsbuildinfo 39 | next-env.d.ts 40 | -------------------------------------------------------------------------------- /admin-ui/.dockerignore: -------------------------------------------------------------------------------- 1 | .dockerignore 2 | docker-compose.yml 3 | Dockerfile 4 | build/ 5 | node_modules 6 | .env 7 | .gitignore 8 | -------------------------------------------------------------------------------- /admin-ui/.env: -------------------------------------------------------------------------------- 1 | PORT=3001 2 | REACT_APP_SERVER_URL=http://localhost:3000 -------------------------------------------------------------------------------- /admin-ui/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /admin-ui/configuration/nginx.conf: -------------------------------------------------------------------------------- 1 | server_tokens off; 2 | 3 | server { 4 | listen 8080; 5 | server_name localhost; 6 | location / { 7 | root /usr/share/nginx/html; 8 | index index.html index.htm; 9 | try_files $uri /index.html; 10 | } 11 | } -------------------------------------------------------------------------------- /admin-ui/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "amazon-backend", 3 | "name": "amazon-backend", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } -------------------------------------------------------------------------------- /admin-ui/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /admin-ui/src/App.scss: -------------------------------------------------------------------------------- 1 | // .App { 2 | // .MuiAppBar-colorSecondary { 3 | // background-color: black; 4 | 5 | // .RaAppBar-menuButton-13 { 6 | // background-color: yellow; 7 | // } 8 | // } 9 | 10 | // .MuiDrawer-paper { 11 | // background-color: red; 12 | 13 | // .MuiListItemIcon-root { 14 | // color: white; 15 | // } 16 | // } 17 | 18 | // .MuiButton-textPrimary { 19 | // background-color: purple; 20 | // margin: 0 0.5rem; 21 | // color: white; 22 | // padding: 0.5rem 1rem; 23 | 24 | // &:hover { 25 | // background-color: blue; 26 | // } 27 | // } 28 | 29 | // .MuiTableRow-head { 30 | // .MuiTableCell-head { 31 | // background-color: black; 32 | // color: white; 33 | // } 34 | 35 | // .MuiTableSortLabel-root { 36 | // &:hover { 37 | // color: red; 38 | 39 | // .MuiTableSortLabel-icon { 40 | // color: red !important; 41 | // } 42 | // } 43 | // .MuiTableSortLabel-icon { 44 | // color: white !important; 45 | // } 46 | // } 47 | // .MuiTableSortLabel-active { 48 | // color: green; 49 | 50 | // .MuiTableSortLabel-icon { 51 | // color: green !important; 52 | // } 53 | // } 54 | // } 55 | 56 | // .MuiFormLabel-root { 57 | // color: magenta; 58 | // } 59 | // } 60 | -------------------------------------------------------------------------------- /admin-ui/src/Components/Pagination.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Pagination as RAPagination, PaginationProps } from "react-admin"; 3 | 4 | const PAGINATION_OPTIONS = [10, 25, 50, 100, 200]; 5 | 6 | const Pagination = (props: PaginationProps) => ( 7 | 8 | ); 9 | 10 | export default Pagination; 11 | -------------------------------------------------------------------------------- /admin-ui/src/api/category/Category.ts: -------------------------------------------------------------------------------- 1 | import { Product } from "../product/Product"; 2 | 3 | export type Category = { 4 | createdAt: Date; 5 | id: string; 6 | name: string; 7 | products?: Array; 8 | updatedAt: Date; 9 | }; 10 | -------------------------------------------------------------------------------- /admin-ui/src/api/category/CategoryCountArgs.ts: -------------------------------------------------------------------------------- 1 | import { CategoryWhereInput } from "./CategoryWhereInput"; 2 | 3 | export type CategoryCountArgs = { 4 | where?: CategoryWhereInput; 5 | }; 6 | -------------------------------------------------------------------------------- /admin-ui/src/api/category/CategoryCreateInput.ts: -------------------------------------------------------------------------------- 1 | import { ProductCreateNestedManyWithoutCategoriesInput } from "./ProductCreateNestedManyWithoutCategoriesInput"; 2 | 3 | export type CategoryCreateInput = { 4 | name: string; 5 | products?: ProductCreateNestedManyWithoutCategoriesInput; 6 | }; 7 | -------------------------------------------------------------------------------- /admin-ui/src/api/category/CategoryFindManyArgs.ts: -------------------------------------------------------------------------------- 1 | import { CategoryWhereInput } from "./CategoryWhereInput"; 2 | import { CategoryOrderByInput } from "./CategoryOrderByInput"; 3 | 4 | export type CategoryFindManyArgs = { 5 | where?: CategoryWhereInput; 6 | orderBy?: Array; 7 | skip?: number; 8 | take?: number; 9 | }; 10 | -------------------------------------------------------------------------------- /admin-ui/src/api/category/CategoryFindUniqueArgs.ts: -------------------------------------------------------------------------------- 1 | import { CategoryWhereUniqueInput } from "./CategoryWhereUniqueInput"; 2 | 3 | export type CategoryFindUniqueArgs = { 4 | where: CategoryWhereUniqueInput; 5 | }; 6 | -------------------------------------------------------------------------------- /admin-ui/src/api/category/CategoryListRelationFilter.ts: -------------------------------------------------------------------------------- 1 | import { CategoryWhereInput } from "./CategoryWhereInput"; 2 | 3 | export type CategoryListRelationFilter = { 4 | every?: CategoryWhereInput; 5 | some?: CategoryWhereInput; 6 | none?: CategoryWhereInput; 7 | }; 8 | -------------------------------------------------------------------------------- /admin-ui/src/api/category/CategoryOrderByInput.ts: -------------------------------------------------------------------------------- 1 | import { SortOrder } from "../../util/SortOrder"; 2 | 3 | export type CategoryOrderByInput = { 4 | createdAt?: SortOrder; 5 | id?: SortOrder; 6 | name?: SortOrder; 7 | updatedAt?: SortOrder; 8 | }; 9 | -------------------------------------------------------------------------------- /admin-ui/src/api/category/CategoryUpdateInput.ts: -------------------------------------------------------------------------------- 1 | import { ProductUpdateManyWithoutCategoriesInput } from "./ProductUpdateManyWithoutCategoriesInput"; 2 | 3 | export type CategoryUpdateInput = { 4 | name?: string; 5 | products?: ProductUpdateManyWithoutCategoriesInput; 6 | }; 7 | -------------------------------------------------------------------------------- /admin-ui/src/api/category/CategoryWhereInput.ts: -------------------------------------------------------------------------------- 1 | import { StringFilter } from "../../util/StringFilter"; 2 | import { ProductListRelationFilter } from "../product/ProductListRelationFilter"; 3 | 4 | export type CategoryWhereInput = { 5 | id?: StringFilter; 6 | name?: StringFilter; 7 | products?: ProductListRelationFilter; 8 | }; 9 | -------------------------------------------------------------------------------- /admin-ui/src/api/category/CategoryWhereUniqueInput.ts: -------------------------------------------------------------------------------- 1 | export type CategoryWhereUniqueInput = { 2 | id: string; 3 | }; 4 | -------------------------------------------------------------------------------- /admin-ui/src/api/category/CreateCategoryArgs.ts: -------------------------------------------------------------------------------- 1 | import { CategoryCreateInput } from "./CategoryCreateInput"; 2 | 3 | export type CreateCategoryArgs = { 4 | data: CategoryCreateInput; 5 | }; 6 | -------------------------------------------------------------------------------- /admin-ui/src/api/category/DeleteCategoryArgs.ts: -------------------------------------------------------------------------------- 1 | import { CategoryWhereUniqueInput } from "./CategoryWhereUniqueInput"; 2 | 3 | export type DeleteCategoryArgs = { 4 | where: CategoryWhereUniqueInput; 5 | }; 6 | -------------------------------------------------------------------------------- /admin-ui/src/api/category/ProductCreateNestedManyWithoutCategoriesInput.ts: -------------------------------------------------------------------------------- 1 | import { ProductWhereUniqueInput } from "../product/ProductWhereUniqueInput"; 2 | 3 | export type ProductCreateNestedManyWithoutCategoriesInput = { 4 | connect?: Array; 5 | }; 6 | -------------------------------------------------------------------------------- /admin-ui/src/api/category/ProductUpdateManyWithoutCategoriesInput.ts: -------------------------------------------------------------------------------- 1 | import { ProductWhereUniqueInput } from "../product/ProductWhereUniqueInput"; 2 | 3 | export type ProductUpdateManyWithoutCategoriesInput = { 4 | connect?: Array; 5 | disconnect?: Array; 6 | set?: Array; 7 | }; 8 | -------------------------------------------------------------------------------- /admin-ui/src/api/category/UpdateCategoryArgs.ts: -------------------------------------------------------------------------------- 1 | import { CategoryWhereUniqueInput } from "./CategoryWhereUniqueInput"; 2 | import { CategoryUpdateInput } from "./CategoryUpdateInput"; 3 | 4 | export type UpdateCategoryArgs = { 5 | where: CategoryWhereUniqueInput; 6 | data: CategoryUpdateInput; 7 | }; 8 | -------------------------------------------------------------------------------- /admin-ui/src/api/order/CreateOrderArgs.ts: -------------------------------------------------------------------------------- 1 | import { OrderCreateInput } from "./OrderCreateInput"; 2 | 3 | export type CreateOrderArgs = { 4 | data: OrderCreateInput; 5 | }; 6 | -------------------------------------------------------------------------------- /admin-ui/src/api/order/DeleteOrderArgs.ts: -------------------------------------------------------------------------------- 1 | import { OrderWhereUniqueInput } from "./OrderWhereUniqueInput"; 2 | 3 | export type DeleteOrderArgs = { 4 | where: OrderWhereUniqueInput; 5 | }; 6 | -------------------------------------------------------------------------------- /admin-ui/src/api/order/Order.ts: -------------------------------------------------------------------------------- 1 | import { Product } from "../product/Product"; 2 | import { JsonValue } from "type-fest"; 3 | import { User } from "../user/User"; 4 | 5 | export type Order = { 6 | createdAt: Date; 7 | id: string; 8 | paymentIntent: string; 9 | paymentStatus: boolean | null; 10 | price: number; 11 | products?: Array; 12 | status: JsonValue; 13 | updatedAt: Date; 14 | user?: User | null; 15 | }; 16 | -------------------------------------------------------------------------------- /admin-ui/src/api/order/OrderCountArgs.ts: -------------------------------------------------------------------------------- 1 | import { OrderWhereInput } from "./OrderWhereInput"; 2 | 3 | export type OrderCountArgs = { 4 | where?: OrderWhereInput; 5 | }; 6 | -------------------------------------------------------------------------------- /admin-ui/src/api/order/OrderCreateInput.ts: -------------------------------------------------------------------------------- 1 | import { ProductCreateNestedManyWithoutOrdersInput } from "./ProductCreateNestedManyWithoutOrdersInput"; 2 | import { InputJsonValue } from "../../types"; 3 | import { UserWhereUniqueInput } from "../user/UserWhereUniqueInput"; 4 | 5 | export type OrderCreateInput = { 6 | paymentIntent: string; 7 | paymentStatus?: boolean | null; 8 | price: number; 9 | products?: ProductCreateNestedManyWithoutOrdersInput; 10 | status: InputJsonValue; 11 | user?: UserWhereUniqueInput | null; 12 | }; 13 | -------------------------------------------------------------------------------- /admin-ui/src/api/order/OrderFindManyArgs.ts: -------------------------------------------------------------------------------- 1 | import { OrderWhereInput } from "./OrderWhereInput"; 2 | import { OrderOrderByInput } from "./OrderOrderByInput"; 3 | 4 | export type OrderFindManyArgs = { 5 | where?: OrderWhereInput; 6 | orderBy?: Array; 7 | skip?: number; 8 | take?: number; 9 | }; 10 | -------------------------------------------------------------------------------- /admin-ui/src/api/order/OrderFindUniqueArgs.ts: -------------------------------------------------------------------------------- 1 | import { OrderWhereUniqueInput } from "./OrderWhereUniqueInput"; 2 | 3 | export type OrderFindUniqueArgs = { 4 | where: OrderWhereUniqueInput; 5 | }; 6 | -------------------------------------------------------------------------------- /admin-ui/src/api/order/OrderListRelationFilter.ts: -------------------------------------------------------------------------------- 1 | import { OrderWhereInput } from "./OrderWhereInput"; 2 | 3 | export type OrderListRelationFilter = { 4 | every?: OrderWhereInput; 5 | some?: OrderWhereInput; 6 | none?: OrderWhereInput; 7 | }; 8 | -------------------------------------------------------------------------------- /admin-ui/src/api/order/OrderOrderByInput.ts: -------------------------------------------------------------------------------- 1 | import { SortOrder } from "../../util/SortOrder"; 2 | 3 | export type OrderOrderByInput = { 4 | createdAt?: SortOrder; 5 | id?: SortOrder; 6 | paymentIntent?: SortOrder; 7 | paymentStatus?: SortOrder; 8 | price?: SortOrder; 9 | status?: SortOrder; 10 | updatedAt?: SortOrder; 11 | userId?: SortOrder; 12 | }; 13 | -------------------------------------------------------------------------------- /admin-ui/src/api/order/OrderUpdateInput.ts: -------------------------------------------------------------------------------- 1 | import { ProductUpdateManyWithoutOrdersInput } from "./ProductUpdateManyWithoutOrdersInput"; 2 | import { InputJsonValue } from "../../types"; 3 | import { UserWhereUniqueInput } from "../user/UserWhereUniqueInput"; 4 | 5 | export type OrderUpdateInput = { 6 | paymentIntent?: string; 7 | paymentStatus?: boolean | null; 8 | price?: number; 9 | products?: ProductUpdateManyWithoutOrdersInput; 10 | status?: InputJsonValue; 11 | user?: UserWhereUniqueInput | null; 12 | }; 13 | -------------------------------------------------------------------------------- /admin-ui/src/api/order/OrderWhereInput.ts: -------------------------------------------------------------------------------- 1 | import { StringFilter } from "../../util/StringFilter"; 2 | import { BooleanNullableFilter } from "../../util/BooleanNullableFilter"; 3 | import { FloatFilter } from "../../util/FloatFilter"; 4 | import { ProductListRelationFilter } from "../product/ProductListRelationFilter"; 5 | import { JsonFilter } from "../../util/JsonFilter"; 6 | import { UserWhereUniqueInput } from "../user/UserWhereUniqueInput"; 7 | 8 | export type OrderWhereInput = { 9 | id?: StringFilter; 10 | paymentIntent?: StringFilter; 11 | paymentStatus?: BooleanNullableFilter; 12 | price?: FloatFilter; 13 | products?: ProductListRelationFilter; 14 | status?: JsonFilter; 15 | user?: UserWhereUniqueInput; 16 | }; 17 | -------------------------------------------------------------------------------- /admin-ui/src/api/order/OrderWhereUniqueInput.ts: -------------------------------------------------------------------------------- 1 | export type OrderWhereUniqueInput = { 2 | id: string; 3 | }; 4 | -------------------------------------------------------------------------------- /admin-ui/src/api/order/ProductCreateNestedManyWithoutOrdersInput.ts: -------------------------------------------------------------------------------- 1 | import { ProductWhereUniqueInput } from "../product/ProductWhereUniqueInput"; 2 | 3 | export type ProductCreateNestedManyWithoutOrdersInput = { 4 | connect?: Array; 5 | }; 6 | -------------------------------------------------------------------------------- /admin-ui/src/api/order/ProductUpdateManyWithoutOrdersInput.ts: -------------------------------------------------------------------------------- 1 | import { ProductWhereUniqueInput } from "../product/ProductWhereUniqueInput"; 2 | 3 | export type ProductUpdateManyWithoutOrdersInput = { 4 | connect?: Array; 5 | disconnect?: Array; 6 | set?: Array; 7 | }; 8 | -------------------------------------------------------------------------------- /admin-ui/src/api/order/UpdateOrderArgs.ts: -------------------------------------------------------------------------------- 1 | import { OrderWhereUniqueInput } from "./OrderWhereUniqueInput"; 2 | import { OrderUpdateInput } from "./OrderUpdateInput"; 3 | 4 | export type UpdateOrderArgs = { 5 | where: OrderWhereUniqueInput; 6 | data: OrderUpdateInput; 7 | }; 8 | -------------------------------------------------------------------------------- /admin-ui/src/api/product/CreateProductArgs.ts: -------------------------------------------------------------------------------- 1 | import { ProductCreateInput } from "./ProductCreateInput"; 2 | 3 | export type CreateProductArgs = { 4 | data: ProductCreateInput; 5 | }; 6 | -------------------------------------------------------------------------------- /admin-ui/src/api/product/DeleteProductArgs.ts: -------------------------------------------------------------------------------- 1 | import { ProductWhereUniqueInput } from "./ProductWhereUniqueInput"; 2 | 3 | export type DeleteProductArgs = { 4 | where: ProductWhereUniqueInput; 5 | }; 6 | -------------------------------------------------------------------------------- /admin-ui/src/api/product/OrderCreateNestedManyWithoutProductsInput.ts: -------------------------------------------------------------------------------- 1 | import { OrderWhereUniqueInput } from "../order/OrderWhereUniqueInput"; 2 | 3 | export type OrderCreateNestedManyWithoutProductsInput = { 4 | connect?: Array; 5 | }; 6 | -------------------------------------------------------------------------------- /admin-ui/src/api/product/OrderUpdateManyWithoutProductsInput.ts: -------------------------------------------------------------------------------- 1 | import { OrderWhereUniqueInput } from "../order/OrderWhereUniqueInput"; 2 | 3 | export type OrderUpdateManyWithoutProductsInput = { 4 | connect?: Array; 5 | disconnect?: Array; 6 | set?: Array; 7 | }; 8 | -------------------------------------------------------------------------------- /admin-ui/src/api/product/Product.ts: -------------------------------------------------------------------------------- 1 | import { Category } from "../category/Category"; 2 | import { JsonValue } from "type-fest"; 3 | import { Order } from "../order/Order"; 4 | import { Review } from "../review/Review"; 5 | 6 | export type Product = { 7 | category?: Category; 8 | colors: JsonValue; 9 | createdAt: Date; 10 | description: JsonValue; 11 | discountPrice: number; 12 | id: string; 13 | images: JsonValue; 14 | order?: Array; 15 | reviews?: Array; 16 | salePrice: number; 17 | title: string; 18 | updatedAt: Date; 19 | variants: JsonValue; 20 | }; 21 | -------------------------------------------------------------------------------- /admin-ui/src/api/product/ProductCountArgs.ts: -------------------------------------------------------------------------------- 1 | import { ProductWhereInput } from "./ProductWhereInput"; 2 | 3 | export type ProductCountArgs = { 4 | where?: ProductWhereInput; 5 | }; 6 | -------------------------------------------------------------------------------- /admin-ui/src/api/product/ProductCreateInput.ts: -------------------------------------------------------------------------------- 1 | import { CategoryWhereUniqueInput } from "../category/CategoryWhereUniqueInput"; 2 | import { InputJsonValue } from "../../types"; 3 | import { OrderCreateNestedManyWithoutProductsInput } from "./OrderCreateNestedManyWithoutProductsInput"; 4 | import { ReviewCreateNestedManyWithoutProductsInput } from "./ReviewCreateNestedManyWithoutProductsInput"; 5 | 6 | export type ProductCreateInput = { 7 | category: CategoryWhereUniqueInput; 8 | colors: InputJsonValue; 9 | description: InputJsonValue; 10 | discountPrice: number; 11 | images: InputJsonValue; 12 | order?: OrderCreateNestedManyWithoutProductsInput; 13 | reviews?: ReviewCreateNestedManyWithoutProductsInput; 14 | salePrice: number; 15 | title: string; 16 | variants: InputJsonValue; 17 | }; 18 | -------------------------------------------------------------------------------- /admin-ui/src/api/product/ProductFindManyArgs.ts: -------------------------------------------------------------------------------- 1 | import { ProductWhereInput } from "./ProductWhereInput"; 2 | import { ProductOrderByInput } from "./ProductOrderByInput"; 3 | 4 | export type ProductFindManyArgs = { 5 | where?: ProductWhereInput; 6 | orderBy?: Array; 7 | skip?: number; 8 | take?: number; 9 | }; 10 | -------------------------------------------------------------------------------- /admin-ui/src/api/product/ProductFindUniqueArgs.ts: -------------------------------------------------------------------------------- 1 | import { ProductWhereUniqueInput } from "./ProductWhereUniqueInput"; 2 | 3 | export type ProductFindUniqueArgs = { 4 | where: ProductWhereUniqueInput; 5 | }; 6 | -------------------------------------------------------------------------------- /admin-ui/src/api/product/ProductListRelationFilter.ts: -------------------------------------------------------------------------------- 1 | import { ProductWhereInput } from "./ProductWhereInput"; 2 | 3 | export type ProductListRelationFilter = { 4 | every?: ProductWhereInput; 5 | some?: ProductWhereInput; 6 | none?: ProductWhereInput; 7 | }; 8 | -------------------------------------------------------------------------------- /admin-ui/src/api/product/ProductOrderByInput.ts: -------------------------------------------------------------------------------- 1 | import { SortOrder } from "../../util/SortOrder"; 2 | 3 | export type ProductOrderByInput = { 4 | categoryId?: SortOrder; 5 | colors?: SortOrder; 6 | createdAt?: SortOrder; 7 | description?: SortOrder; 8 | discountPrice?: SortOrder; 9 | id?: SortOrder; 10 | images?: SortOrder; 11 | salePrice?: SortOrder; 12 | title?: SortOrder; 13 | updatedAt?: SortOrder; 14 | variants?: SortOrder; 15 | }; 16 | -------------------------------------------------------------------------------- /admin-ui/src/api/product/ProductUpdateInput.ts: -------------------------------------------------------------------------------- 1 | import { CategoryWhereUniqueInput } from "../category/CategoryWhereUniqueInput"; 2 | import { InputJsonValue } from "../../types"; 3 | import { OrderUpdateManyWithoutProductsInput } from "./OrderUpdateManyWithoutProductsInput"; 4 | import { ReviewUpdateManyWithoutProductsInput } from "./ReviewUpdateManyWithoutProductsInput"; 5 | 6 | export type ProductUpdateInput = { 7 | category?: CategoryWhereUniqueInput; 8 | colors?: InputJsonValue; 9 | description?: InputJsonValue; 10 | discountPrice?: number; 11 | images?: InputJsonValue; 12 | order?: OrderUpdateManyWithoutProductsInput; 13 | reviews?: ReviewUpdateManyWithoutProductsInput; 14 | salePrice?: number; 15 | title?: string; 16 | variants?: InputJsonValue; 17 | }; 18 | -------------------------------------------------------------------------------- /admin-ui/src/api/product/ProductWhereInput.ts: -------------------------------------------------------------------------------- 1 | import { CategoryWhereUniqueInput } from "../category/CategoryWhereUniqueInput"; 2 | import { JsonFilter } from "../../util/JsonFilter"; 3 | import { FloatFilter } from "../../util/FloatFilter"; 4 | import { StringFilter } from "../../util/StringFilter"; 5 | import { OrderListRelationFilter } from "../order/OrderListRelationFilter"; 6 | import { ReviewListRelationFilter } from "../review/ReviewListRelationFilter"; 7 | 8 | export type ProductWhereInput = { 9 | category?: CategoryWhereUniqueInput; 10 | colors?: JsonFilter; 11 | description?: JsonFilter; 12 | discountPrice?: FloatFilter; 13 | id?: StringFilter; 14 | images?: JsonFilter; 15 | order?: OrderListRelationFilter; 16 | reviews?: ReviewListRelationFilter; 17 | salePrice?: FloatFilter; 18 | title?: StringFilter; 19 | variants?: JsonFilter; 20 | }; 21 | -------------------------------------------------------------------------------- /admin-ui/src/api/product/ProductWhereUniqueInput.ts: -------------------------------------------------------------------------------- 1 | export type ProductWhereUniqueInput = { 2 | id: string; 3 | }; 4 | -------------------------------------------------------------------------------- /admin-ui/src/api/product/ReviewCreateNestedManyWithoutProductsInput.ts: -------------------------------------------------------------------------------- 1 | import { ReviewWhereUniqueInput } from "../review/ReviewWhereUniqueInput"; 2 | 3 | export type ReviewCreateNestedManyWithoutProductsInput = { 4 | connect?: Array; 5 | }; 6 | -------------------------------------------------------------------------------- /admin-ui/src/api/product/ReviewUpdateManyWithoutProductsInput.ts: -------------------------------------------------------------------------------- 1 | import { ReviewWhereUniqueInput } from "../review/ReviewWhereUniqueInput"; 2 | 3 | export type ReviewUpdateManyWithoutProductsInput = { 4 | connect?: Array; 5 | disconnect?: Array; 6 | set?: Array; 7 | }; 8 | -------------------------------------------------------------------------------- /admin-ui/src/api/product/UpdateProductArgs.ts: -------------------------------------------------------------------------------- 1 | import { ProductWhereUniqueInput } from "./ProductWhereUniqueInput"; 2 | import { ProductUpdateInput } from "./ProductUpdateInput"; 3 | 4 | export type UpdateProductArgs = { 5 | where: ProductWhereUniqueInput; 6 | data: ProductUpdateInput; 7 | }; 8 | -------------------------------------------------------------------------------- /admin-ui/src/api/review/CreateReviewArgs.ts: -------------------------------------------------------------------------------- 1 | import { ReviewCreateInput } from "./ReviewCreateInput"; 2 | 3 | export type CreateReviewArgs = { 4 | data: ReviewCreateInput; 5 | }; 6 | -------------------------------------------------------------------------------- /admin-ui/src/api/review/DeleteReviewArgs.ts: -------------------------------------------------------------------------------- 1 | import { ReviewWhereUniqueInput } from "./ReviewWhereUniqueInput"; 2 | 3 | export type DeleteReviewArgs = { 4 | where: ReviewWhereUniqueInput; 5 | }; 6 | -------------------------------------------------------------------------------- /admin-ui/src/api/review/Review.ts: -------------------------------------------------------------------------------- 1 | import { Product } from "../product/Product"; 2 | import { User } from "../user/User"; 3 | 4 | export type Review = { 5 | createdAt: Date; 6 | description: string; 7 | id: string; 8 | product?: Product | null; 9 | rating: number; 10 | updatedAt: Date; 11 | user?: User | null; 12 | }; 13 | -------------------------------------------------------------------------------- /admin-ui/src/api/review/ReviewCountArgs.ts: -------------------------------------------------------------------------------- 1 | import { ReviewWhereInput } from "./ReviewWhereInput"; 2 | 3 | export type ReviewCountArgs = { 4 | where?: ReviewWhereInput; 5 | }; 6 | -------------------------------------------------------------------------------- /admin-ui/src/api/review/ReviewCreateInput.ts: -------------------------------------------------------------------------------- 1 | import { ProductWhereUniqueInput } from "../product/ProductWhereUniqueInput"; 2 | import { UserWhereUniqueInput } from "../user/UserWhereUniqueInput"; 3 | 4 | export type ReviewCreateInput = { 5 | description: string; 6 | product?: ProductWhereUniqueInput | null; 7 | rating: number; 8 | user?: UserWhereUniqueInput | null; 9 | }; 10 | -------------------------------------------------------------------------------- /admin-ui/src/api/review/ReviewFindManyArgs.ts: -------------------------------------------------------------------------------- 1 | import { ReviewWhereInput } from "./ReviewWhereInput"; 2 | import { ReviewOrderByInput } from "./ReviewOrderByInput"; 3 | 4 | export type ReviewFindManyArgs = { 5 | where?: ReviewWhereInput; 6 | orderBy?: Array; 7 | skip?: number; 8 | take?: number; 9 | }; 10 | -------------------------------------------------------------------------------- /admin-ui/src/api/review/ReviewFindUniqueArgs.ts: -------------------------------------------------------------------------------- 1 | import { ReviewWhereUniqueInput } from "./ReviewWhereUniqueInput"; 2 | 3 | export type ReviewFindUniqueArgs = { 4 | where: ReviewWhereUniqueInput; 5 | }; 6 | -------------------------------------------------------------------------------- /admin-ui/src/api/review/ReviewListRelationFilter.ts: -------------------------------------------------------------------------------- 1 | import { ReviewWhereInput } from "./ReviewWhereInput"; 2 | 3 | export type ReviewListRelationFilter = { 4 | every?: ReviewWhereInput; 5 | some?: ReviewWhereInput; 6 | none?: ReviewWhereInput; 7 | }; 8 | -------------------------------------------------------------------------------- /admin-ui/src/api/review/ReviewOrderByInput.ts: -------------------------------------------------------------------------------- 1 | import { SortOrder } from "../../util/SortOrder"; 2 | 3 | export type ReviewOrderByInput = { 4 | createdAt?: SortOrder; 5 | description?: SortOrder; 6 | id?: SortOrder; 7 | productId?: SortOrder; 8 | rating?: SortOrder; 9 | updatedAt?: SortOrder; 10 | userId?: SortOrder; 11 | }; 12 | -------------------------------------------------------------------------------- /admin-ui/src/api/review/ReviewUpdateInput.ts: -------------------------------------------------------------------------------- 1 | import { ProductWhereUniqueInput } from "../product/ProductWhereUniqueInput"; 2 | import { UserWhereUniqueInput } from "../user/UserWhereUniqueInput"; 3 | 4 | export type ReviewUpdateInput = { 5 | description?: string; 6 | product?: ProductWhereUniqueInput | null; 7 | rating?: number; 8 | user?: UserWhereUniqueInput | null; 9 | }; 10 | -------------------------------------------------------------------------------- /admin-ui/src/api/review/ReviewWhereInput.ts: -------------------------------------------------------------------------------- 1 | import { StringFilter } from "../../util/StringFilter"; 2 | import { ProductWhereUniqueInput } from "../product/ProductWhereUniqueInput"; 3 | import { IntFilter } from "../../util/IntFilter"; 4 | import { UserWhereUniqueInput } from "../user/UserWhereUniqueInput"; 5 | 6 | export type ReviewWhereInput = { 7 | description?: StringFilter; 8 | id?: StringFilter; 9 | product?: ProductWhereUniqueInput; 10 | rating?: IntFilter; 11 | user?: UserWhereUniqueInput; 12 | }; 13 | -------------------------------------------------------------------------------- /admin-ui/src/api/review/ReviewWhereUniqueInput.ts: -------------------------------------------------------------------------------- 1 | export type ReviewWhereUniqueInput = { 2 | id: string; 3 | }; 4 | -------------------------------------------------------------------------------- /admin-ui/src/api/review/UpdateReviewArgs.ts: -------------------------------------------------------------------------------- 1 | import { ReviewWhereUniqueInput } from "./ReviewWhereUniqueInput"; 2 | import { ReviewUpdateInput } from "./ReviewUpdateInput"; 3 | 4 | export type UpdateReviewArgs = { 5 | where: ReviewWhereUniqueInput; 6 | data: ReviewUpdateInput; 7 | }; 8 | -------------------------------------------------------------------------------- /admin-ui/src/api/user/CreateUserArgs.ts: -------------------------------------------------------------------------------- 1 | import { UserCreateInput } from "./UserCreateInput"; 2 | 3 | export type CreateUserArgs = { 4 | data: UserCreateInput; 5 | }; 6 | -------------------------------------------------------------------------------- /admin-ui/src/api/user/DeleteUserArgs.ts: -------------------------------------------------------------------------------- 1 | import { UserWhereUniqueInput } from "./UserWhereUniqueInput"; 2 | 3 | export type DeleteUserArgs = { 4 | where: UserWhereUniqueInput; 5 | }; 6 | -------------------------------------------------------------------------------- /admin-ui/src/api/user/OrderCreateNestedManyWithoutUsersInput.ts: -------------------------------------------------------------------------------- 1 | import { OrderWhereUniqueInput } from "../order/OrderWhereUniqueInput"; 2 | 3 | export type OrderCreateNestedManyWithoutUsersInput = { 4 | connect?: Array; 5 | }; 6 | -------------------------------------------------------------------------------- /admin-ui/src/api/user/OrderUpdateManyWithoutUsersInput.ts: -------------------------------------------------------------------------------- 1 | import { OrderWhereUniqueInput } from "../order/OrderWhereUniqueInput"; 2 | 3 | export type OrderUpdateManyWithoutUsersInput = { 4 | connect?: Array; 5 | disconnect?: Array; 6 | set?: Array; 7 | }; 8 | -------------------------------------------------------------------------------- /admin-ui/src/api/user/ReviewCreateNestedManyWithoutUsersInput.ts: -------------------------------------------------------------------------------- 1 | import { ReviewWhereUniqueInput } from "../review/ReviewWhereUniqueInput"; 2 | 3 | export type ReviewCreateNestedManyWithoutUsersInput = { 4 | connect?: Array; 5 | }; 6 | -------------------------------------------------------------------------------- /admin-ui/src/api/user/ReviewUpdateManyWithoutUsersInput.ts: -------------------------------------------------------------------------------- 1 | import { ReviewWhereUniqueInput } from "../review/ReviewWhereUniqueInput"; 2 | 3 | export type ReviewUpdateManyWithoutUsersInput = { 4 | connect?: Array; 5 | disconnect?: Array; 6 | set?: Array; 7 | }; 8 | -------------------------------------------------------------------------------- /admin-ui/src/api/user/UpdateUserArgs.ts: -------------------------------------------------------------------------------- 1 | import { UserWhereUniqueInput } from "./UserWhereUniqueInput"; 2 | import { UserUpdateInput } from "./UserUpdateInput"; 3 | 4 | export type UpdateUserArgs = { 5 | where: UserWhereUniqueInput; 6 | data: UserUpdateInput; 7 | }; 8 | -------------------------------------------------------------------------------- /admin-ui/src/api/user/User.ts: -------------------------------------------------------------------------------- 1 | import { Order } from "../order/Order"; 2 | import { Review } from "../review/Review"; 3 | import { JsonValue } from "type-fest"; 4 | 5 | export type User = { 6 | createdAt: Date; 7 | firstName: string | null; 8 | id: string; 9 | isAdmin: boolean | null; 10 | lastName: string | null; 11 | orders?: Array; 12 | reviews?: Array; 13 | roles: JsonValue; 14 | updatedAt: Date; 15 | username: string; 16 | }; 17 | -------------------------------------------------------------------------------- /admin-ui/src/api/user/UserCountArgs.ts: -------------------------------------------------------------------------------- 1 | import { UserWhereInput } from "./UserWhereInput"; 2 | 3 | export type UserCountArgs = { 4 | where?: UserWhereInput; 5 | }; 6 | -------------------------------------------------------------------------------- /admin-ui/src/api/user/UserCreateInput.ts: -------------------------------------------------------------------------------- 1 | import { OrderCreateNestedManyWithoutUsersInput } from "./OrderCreateNestedManyWithoutUsersInput"; 2 | import { ReviewCreateNestedManyWithoutUsersInput } from "./ReviewCreateNestedManyWithoutUsersInput"; 3 | import { InputJsonValue } from "../../types"; 4 | 5 | export type UserCreateInput = { 6 | firstName?: string | null; 7 | isAdmin?: boolean | null; 8 | lastName?: string | null; 9 | orders?: OrderCreateNestedManyWithoutUsersInput; 10 | password: string; 11 | reviews?: ReviewCreateNestedManyWithoutUsersInput; 12 | roles: InputJsonValue; 13 | username: string; 14 | }; 15 | -------------------------------------------------------------------------------- /admin-ui/src/api/user/UserFindManyArgs.ts: -------------------------------------------------------------------------------- 1 | import { UserWhereInput } from "./UserWhereInput"; 2 | import { UserOrderByInput } from "./UserOrderByInput"; 3 | 4 | export type UserFindManyArgs = { 5 | where?: UserWhereInput; 6 | orderBy?: Array; 7 | skip?: number; 8 | take?: number; 9 | }; 10 | -------------------------------------------------------------------------------- /admin-ui/src/api/user/UserFindUniqueArgs.ts: -------------------------------------------------------------------------------- 1 | import { UserWhereUniqueInput } from "./UserWhereUniqueInput"; 2 | 3 | export type UserFindUniqueArgs = { 4 | where: UserWhereUniqueInput; 5 | }; 6 | -------------------------------------------------------------------------------- /admin-ui/src/api/user/UserListRelationFilter.ts: -------------------------------------------------------------------------------- 1 | import { UserWhereInput } from "./UserWhereInput"; 2 | 3 | export type UserListRelationFilter = { 4 | every?: UserWhereInput; 5 | some?: UserWhereInput; 6 | none?: UserWhereInput; 7 | }; 8 | -------------------------------------------------------------------------------- /admin-ui/src/api/user/UserOrderByInput.ts: -------------------------------------------------------------------------------- 1 | import { SortOrder } from "../../util/SortOrder"; 2 | 3 | export type UserOrderByInput = { 4 | createdAt?: SortOrder; 5 | firstName?: SortOrder; 6 | id?: SortOrder; 7 | isAdmin?: SortOrder; 8 | lastName?: SortOrder; 9 | password?: SortOrder; 10 | roles?: SortOrder; 11 | updatedAt?: SortOrder; 12 | username?: SortOrder; 13 | }; 14 | -------------------------------------------------------------------------------- /admin-ui/src/api/user/UserUpdateInput.ts: -------------------------------------------------------------------------------- 1 | import { OrderUpdateManyWithoutUsersInput } from "./OrderUpdateManyWithoutUsersInput"; 2 | import { ReviewUpdateManyWithoutUsersInput } from "./ReviewUpdateManyWithoutUsersInput"; 3 | import { InputJsonValue } from "../../types"; 4 | 5 | export type UserUpdateInput = { 6 | firstName?: string | null; 7 | isAdmin?: boolean | null; 8 | lastName?: string | null; 9 | orders?: OrderUpdateManyWithoutUsersInput; 10 | password?: string; 11 | reviews?: ReviewUpdateManyWithoutUsersInput; 12 | roles?: InputJsonValue; 13 | username?: string; 14 | }; 15 | -------------------------------------------------------------------------------- /admin-ui/src/api/user/UserWhereInput.ts: -------------------------------------------------------------------------------- 1 | import { StringNullableFilter } from "../../util/StringNullableFilter"; 2 | import { StringFilter } from "../../util/StringFilter"; 3 | import { BooleanNullableFilter } from "../../util/BooleanNullableFilter"; 4 | import { OrderListRelationFilter } from "../order/OrderListRelationFilter"; 5 | import { ReviewListRelationFilter } from "../review/ReviewListRelationFilter"; 6 | 7 | export type UserWhereInput = { 8 | firstName?: StringNullableFilter; 9 | id?: StringFilter; 10 | isAdmin?: BooleanNullableFilter; 11 | lastName?: StringNullableFilter; 12 | orders?: OrderListRelationFilter; 13 | reviews?: ReviewListRelationFilter; 14 | username?: StringFilter; 15 | }; 16 | -------------------------------------------------------------------------------- /admin-ui/src/api/user/UserWhereUniqueInput.ts: -------------------------------------------------------------------------------- 1 | export type UserWhereUniqueInput = { 2 | id: string; 3 | }; 4 | -------------------------------------------------------------------------------- /admin-ui/src/auth.ts: -------------------------------------------------------------------------------- 1 | import { EventEmitter } from "events"; 2 | import { CREDENTIALS_LOCAL_STORAGE_ITEM } from "./constants"; 3 | import { Credentials } from "./types"; 4 | 5 | const eventEmitter = new EventEmitter(); 6 | 7 | export function isAuthenticated(): boolean { 8 | return Boolean(getCredentials()); 9 | } 10 | 11 | export function listen(listener: (authenticated: boolean) => void): void { 12 | eventEmitter.on("change", () => { 13 | listener(isAuthenticated()); 14 | }); 15 | } 16 | 17 | export function setCredentials(credentials: Credentials) { 18 | localStorage.setItem( 19 | CREDENTIALS_LOCAL_STORAGE_ITEM, 20 | JSON.stringify(credentials) 21 | ); 22 | } 23 | 24 | export function getCredentials(): Credentials | null { 25 | const raw = localStorage.getItem(CREDENTIALS_LOCAL_STORAGE_ITEM); 26 | if (raw === null) { 27 | return null; 28 | } 29 | return JSON.parse(raw); 30 | } 31 | 32 | export function removeCredentials(): void { 33 | localStorage.removeItem(CREDENTIALS_LOCAL_STORAGE_ITEM); 34 | } 35 | -------------------------------------------------------------------------------- /admin-ui/src/category/CategoryCreate.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | import { 4 | Create, 5 | SimpleForm, 6 | CreateProps, 7 | TextInput, 8 | ReferenceArrayInput, 9 | SelectArrayInput, 10 | } from "react-admin"; 11 | 12 | import { ProductTitle } from "../product/ProductTitle"; 13 | 14 | export const CategoryCreate = (props: CreateProps): React.ReactElement => { 15 | return ( 16 | 17 | 18 | 19 | value && value.map((v: any) => ({ id: v }))} 23 | format={(value: any) => value && value.map((v: any) => v.id)} 24 | > 25 | 26 | 27 | 28 | 29 | ); 30 | }; 31 | -------------------------------------------------------------------------------- /admin-ui/src/category/CategoryEdit.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | import { 4 | Edit, 5 | SimpleForm, 6 | EditProps, 7 | TextInput, 8 | ReferenceArrayInput, 9 | SelectArrayInput, 10 | } from "react-admin"; 11 | 12 | import { ProductTitle } from "../product/ProductTitle"; 13 | 14 | export const CategoryEdit = (props: EditProps): React.ReactElement => { 15 | return ( 16 | 17 | 18 | 19 | value && value.map((v: any) => ({ id: v }))} 23 | format={(value: any) => value && value.map((v: any) => v.id)} 24 | > 25 | 26 | 27 | 28 | 29 | ); 30 | }; 31 | -------------------------------------------------------------------------------- /admin-ui/src/category/CategoryList.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { List, Datagrid, ListProps, DateField, TextField } from "react-admin"; 3 | import Pagination from "../Components/Pagination"; 4 | 5 | export const CategoryList = (props: ListProps): React.ReactElement => { 6 | return ( 7 | } 13 | > 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | ); 22 | }; 23 | -------------------------------------------------------------------------------- /admin-ui/src/category/CategoryTitle.ts: -------------------------------------------------------------------------------- 1 | import { Category as TCategory } from "../api/category/Category"; 2 | 3 | export const CATEGORY_TITLE_FIELD = "name"; 4 | 5 | export const CategoryTitle = (record: TCategory): string => { 6 | return record.name?.toString() || String(record.id); 7 | }; 8 | -------------------------------------------------------------------------------- /admin-ui/src/constants.ts: -------------------------------------------------------------------------------- 1 | export const CREDENTIALS_LOCAL_STORAGE_ITEM = "credentials"; 2 | export const USER_DATA_LOCAL_STORAGE_ITEM = "userData"; 3 | -------------------------------------------------------------------------------- /admin-ui/src/data-provider/graphqlDataProvider.ts: -------------------------------------------------------------------------------- 1 | import buildGraphQLProvider from "ra-data-graphql-amplication"; 2 | import { ApolloClient, InMemoryCache, createHttpLink } from "@apollo/client"; 3 | import { setContext } from "@apollo/client/link/context"; 4 | import { CREDENTIALS_LOCAL_STORAGE_ITEM } from "../constants"; 5 | 6 | const httpLink = createHttpLink({ 7 | uri: `${process.env.REACT_APP_SERVER_URL}/graphql`, 8 | }); 9 | 10 | // eslint-disable-next-line @typescript-eslint/naming-convention 11 | const authLink = setContext((_, { headers }) => { 12 | const token = localStorage.getItem(CREDENTIALS_LOCAL_STORAGE_ITEM); 13 | return { 14 | headers: { 15 | ...headers, 16 | authorization: token ? token : "", 17 | }, 18 | }; 19 | }); 20 | 21 | export const apolloClient = new ApolloClient({ 22 | cache: new InMemoryCache(), 23 | link: authLink.concat(httpLink), 24 | }); 25 | 26 | export default buildGraphQLProvider({ 27 | client: apolloClient, 28 | }); 29 | -------------------------------------------------------------------------------- /admin-ui/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", 4 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | #root { 11 | height: 100vh; 12 | } 13 | 14 | code { 15 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 16 | monospace; 17 | } 18 | 19 | .amp-breadcrumbs { 20 | padding: var(--default-spacing); 21 | } 22 | 23 | .entity-id { 24 | color: var(--primary); 25 | text-decoration: underline; 26 | } 27 | -------------------------------------------------------------------------------- /admin-ui/src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | import "./index.css"; 4 | // @ts-ignore 5 | // eslint-disable-next-line import/no-unresolved 6 | import App from "./App"; 7 | import reportWebVitals from "./reportWebVitals"; 8 | 9 | ReactDOM.render( 10 | 11 | 12 | , 13 | document.getElementById("root") 14 | ); 15 | 16 | // If you want to start measuring performance in your app, pass a function 17 | // to log results (for example: reportWebVitals(console.log)) 18 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 19 | reportWebVitals(); 20 | -------------------------------------------------------------------------------- /admin-ui/src/order/OrderCreate.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | import { 4 | Create, 5 | SimpleForm, 6 | CreateProps, 7 | TextInput, 8 | BooleanInput, 9 | NumberInput, 10 | ReferenceArrayInput, 11 | SelectArrayInput, 12 | ReferenceInput, 13 | SelectInput, 14 | } from "react-admin"; 15 | 16 | import { ProductTitle } from "../product/ProductTitle"; 17 | import { UserTitle } from "../user/UserTitle"; 18 | 19 | export const OrderCreate = (props: CreateProps): React.ReactElement => { 20 | return ( 21 | 22 | 23 | 24 | 25 | 26 | value && value.map((v: any) => ({ id: v }))} 30 | format={(value: any) => value && value.map((v: any) => v.id)} 31 | > 32 | 33 | 34 |
35 | 36 | 37 | 38 | 39 | 40 | ); 41 | }; 42 | -------------------------------------------------------------------------------- /admin-ui/src/order/OrderEdit.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | import { 4 | Edit, 5 | SimpleForm, 6 | EditProps, 7 | TextInput, 8 | BooleanInput, 9 | NumberInput, 10 | ReferenceArrayInput, 11 | SelectArrayInput, 12 | ReferenceInput, 13 | SelectInput, 14 | } from "react-admin"; 15 | 16 | import { ProductTitle } from "../product/ProductTitle"; 17 | import { UserTitle } from "../user/UserTitle"; 18 | 19 | export const OrderEdit = (props: EditProps): React.ReactElement => { 20 | return ( 21 | 22 | 23 | 24 | 25 | 26 | value && value.map((v: any) => ({ id: v }))} 30 | format={(value: any) => value && value.map((v: any) => v.id)} 31 | > 32 | 33 | 34 |
35 | 36 | 37 | 38 | 39 | 40 | ); 41 | }; 42 | -------------------------------------------------------------------------------- /admin-ui/src/order/OrderList.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | import { 4 | List, 5 | Datagrid, 6 | ListProps, 7 | DateField, 8 | TextField, 9 | BooleanField, 10 | ReferenceField, 11 | } from "react-admin"; 12 | 13 | import Pagination from "../Components/Pagination"; 14 | import { USER_TITLE_FIELD } from "../user/UserTitle"; 15 | 16 | export const OrderList = (props: ListProps): React.ReactElement => { 17 | return ( 18 | } 24 | > 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | ); 39 | }; 40 | -------------------------------------------------------------------------------- /admin-ui/src/order/OrderShow.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | import { 4 | Show, 5 | SimpleShowLayout, 6 | ShowProps, 7 | DateField, 8 | TextField, 9 | BooleanField, 10 | ReferenceField, 11 | } from "react-admin"; 12 | 13 | import { USER_TITLE_FIELD } from "../user/UserTitle"; 14 | 15 | export const OrderShow = (props: ShowProps): React.ReactElement => { 16 | return ( 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | ); 32 | }; 33 | -------------------------------------------------------------------------------- /admin-ui/src/order/OrderTitle.ts: -------------------------------------------------------------------------------- 1 | import { Order as TOrder } from "../api/order/Order"; 2 | 3 | export const ORDER_TITLE_FIELD = "paymentIntent"; 4 | 5 | export const OrderTitle = (record: TOrder): string => { 6 | return record.paymentIntent?.toString() || String(record.id); 7 | }; 8 | -------------------------------------------------------------------------------- /admin-ui/src/pages/Dashboard.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import Card from "@material-ui/core/Card"; 3 | import CardContent from "@material-ui/core/CardContent"; 4 | import { Title } from "react-admin"; 5 | const Dashboard = () => ( 6 | 7 | 8 | <CardContent>Welcome</CardContent> 9 | </Card> 10 | ); 11 | 12 | export default Dashboard; 13 | -------------------------------------------------------------------------------- /admin-ui/src/product/ProductTitle.ts: -------------------------------------------------------------------------------- 1 | import { Product as TProduct } from "../api/product/Product"; 2 | 3 | export const PRODUCT_TITLE_FIELD = "title"; 4 | 5 | export const ProductTitle = (record: TProduct): string => { 6 | return record.title?.toString() || String(record.id); 7 | }; 8 | -------------------------------------------------------------------------------- /admin-ui/src/reportWebVitals.ts: -------------------------------------------------------------------------------- 1 | import { ReportHandler } from "web-vitals"; 2 | 3 | const reportWebVitals = (onPerfEntry?: ReportHandler): void => { 4 | if (onPerfEntry && onPerfEntry instanceof Function) { 5 | void import("web-vitals").then( 6 | ({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 7 | getCLS(onPerfEntry); 8 | getFID(onPerfEntry); 9 | getFCP(onPerfEntry); 10 | getLCP(onPerfEntry); 11 | getTTFB(onPerfEntry); 12 | } 13 | ); 14 | } 15 | }; 16 | 17 | export default reportWebVitals; 18 | -------------------------------------------------------------------------------- /admin-ui/src/review/ReviewCreate.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | import { 4 | Create, 5 | SimpleForm, 6 | CreateProps, 7 | TextInput, 8 | ReferenceInput, 9 | SelectInput, 10 | NumberInput, 11 | } from "react-admin"; 12 | 13 | import { ProductTitle } from "../product/ProductTitle"; 14 | import { UserTitle } from "../user/UserTitle"; 15 | 16 | export const ReviewCreate = (props: CreateProps): React.ReactElement => { 17 | return ( 18 | <Create {...props}> 19 | <SimpleForm> 20 | <TextInput label="description" multiline source="description" /> 21 | <ReferenceInput 22 | source="product.id" 23 | reference="Product" 24 | label="products" 25 | > 26 | <SelectInput optionText={ProductTitle} /> 27 | </ReferenceInput> 28 | <NumberInput step={1} label="rating" source="rating" /> 29 | <ReferenceInput source="user.id" reference="User" label="user"> 30 | <SelectInput optionText={UserTitle} /> 31 | </ReferenceInput> 32 | </SimpleForm> 33 | </Create> 34 | ); 35 | }; 36 | -------------------------------------------------------------------------------- /admin-ui/src/review/ReviewEdit.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | import { 4 | Edit, 5 | SimpleForm, 6 | EditProps, 7 | TextInput, 8 | ReferenceInput, 9 | SelectInput, 10 | NumberInput, 11 | } from "react-admin"; 12 | 13 | import { ProductTitle } from "../product/ProductTitle"; 14 | import { UserTitle } from "../user/UserTitle"; 15 | 16 | export const ReviewEdit = (props: EditProps): React.ReactElement => { 17 | return ( 18 | <Edit {...props}> 19 | <SimpleForm> 20 | <TextInput label="description" multiline source="description" /> 21 | <ReferenceInput 22 | source="product.id" 23 | reference="Product" 24 | label="products" 25 | > 26 | <SelectInput optionText={ProductTitle} /> 27 | </ReferenceInput> 28 | <NumberInput step={1} label="rating" source="rating" /> 29 | <ReferenceInput source="user.id" reference="User" label="user"> 30 | <SelectInput optionText={UserTitle} /> 31 | </ReferenceInput> 32 | </SimpleForm> 33 | </Edit> 34 | ); 35 | }; 36 | -------------------------------------------------------------------------------- /admin-ui/src/review/ReviewShow.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { 3 | Show, 4 | SimpleShowLayout, 5 | ShowProps, 6 | DateField, 7 | TextField, 8 | ReferenceField, 9 | } from "react-admin"; 10 | import { PRODUCT_TITLE_FIELD } from "../product/ProductTitle"; 11 | import { USER_TITLE_FIELD } from "../user/UserTitle"; 12 | 13 | export const ReviewShow = (props: ShowProps): React.ReactElement => { 14 | return ( 15 | <Show {...props}> 16 | <SimpleShowLayout> 17 | <DateField source="createdAt" label="Created At" /> 18 | <TextField label="description" source="description" /> 19 | <TextField label="ID" source="id" /> 20 | <ReferenceField 21 | label="products" 22 | source="product.id" 23 | reference="Product" 24 | > 25 | <TextField source={PRODUCT_TITLE_FIELD} /> 26 | </ReferenceField> 27 | <TextField label="rating" source="rating" /> 28 | <DateField source="updatedAt" label="Updated At" /> 29 | <ReferenceField label="user" source="user.id" reference="User"> 30 | <TextField source={USER_TITLE_FIELD} /> 31 | </ReferenceField> 32 | </SimpleShowLayout> 33 | </Show> 34 | ); 35 | }; 36 | -------------------------------------------------------------------------------- /admin-ui/src/review/ReviewTitle.ts: -------------------------------------------------------------------------------- 1 | import { Review as TReview } from "../api/review/Review"; 2 | 3 | export const REVIEW_TITLE_FIELD = "id"; 4 | 5 | export const ReviewTitle = (record: TReview): string => { 6 | return record.id?.toString() || String(record.id); 7 | }; 8 | -------------------------------------------------------------------------------- /admin-ui/src/setupTests.ts: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import "@testing-library/jest-dom"; 6 | -------------------------------------------------------------------------------- /admin-ui/src/theme/theme.ts: -------------------------------------------------------------------------------- 1 | import { defaultTheme } from "react-admin"; 2 | import { createTheme, ThemeOptions } from "@material-ui/core/styles"; 3 | import { merge } from "lodash"; 4 | import createPalette from "@material-ui/core/styles/createPalette"; 5 | 6 | const palette = createPalette( 7 | merge({}, defaultTheme.palette, { 8 | primary: { 9 | main: "#20a4f3", 10 | }, 11 | secondary: { 12 | main: "#7950ed", 13 | }, 14 | error: { 15 | main: "#e93c51", 16 | }, 17 | warning: { 18 | main: "#f6aa50", 19 | }, 20 | info: { 21 | main: "#144bc1", 22 | }, 23 | success: { 24 | main: "#31c587", 25 | }, 26 | }) 27 | ); 28 | 29 | const themeOptions: ThemeOptions = { 30 | palette, 31 | }; 32 | 33 | export const theme = createTheme(merge({}, defaultTheme, themeOptions)); 34 | -------------------------------------------------------------------------------- /admin-ui/src/types.ts: -------------------------------------------------------------------------------- 1 | import { JsonValue } from "type-fest"; 2 | 3 | export type Credentials = { 4 | username: string; 5 | password: string; 6 | }; 7 | export type LoginMutateResult = { 8 | login: { 9 | username: string; 10 | accessToken: string; 11 | }; 12 | }; 13 | export type InputJsonValue = Omit<JsonValue, "null">; 14 | -------------------------------------------------------------------------------- /admin-ui/src/user/EnumRoles.ts: -------------------------------------------------------------------------------- 1 | export enum EnumRoles { 2 | User = "user", 3 | } 4 | -------------------------------------------------------------------------------- /admin-ui/src/user/RolesOptions.ts: -------------------------------------------------------------------------------- 1 | //@ts-ignore 2 | import { ROLES } from "./roles"; 3 | 4 | declare interface Role { 5 | name: string; 6 | displayName: string; 7 | } 8 | 9 | export const ROLES_OPTIONS = ROLES.map((role: Role) => ({ 10 | value: role.name, 11 | label: role.displayName, 12 | })); 13 | -------------------------------------------------------------------------------- /admin-ui/src/user/UserList.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { 3 | List, 4 | Datagrid, 5 | ListProps, 6 | DateField, 7 | TextField, 8 | BooleanField, 9 | } from "react-admin"; 10 | import Pagination from "../Components/Pagination"; 11 | 12 | export const UserList = (props: ListProps): React.ReactElement => { 13 | return ( 14 | <List 15 | {...props} 16 | bulkActionButtons={false} 17 | title={"Users"} 18 | perPage={50} 19 | pagination={<Pagination />} 20 | > 21 | <Datagrid rowClick="show"> 22 | <DateField source="createdAt" label="Created At" /> 23 | <TextField label="First Name" source="firstName" /> 24 | <TextField label="ID" source="id" /> 25 | <BooleanField label="isAdmin" source="isAdmin" /> 26 | <TextField label="Last Name" source="lastName" /> 27 | <TextField label="Roles" source="roles" /> 28 | <DateField source="updatedAt" label="Updated At" /> 29 | <TextField label="Username" source="username" /> 30 | </Datagrid> 31 | </List> 32 | ); 33 | }; 34 | -------------------------------------------------------------------------------- /admin-ui/src/user/UserTitle.ts: -------------------------------------------------------------------------------- 1 | import { User as TUser } from "../api/user/User"; 2 | 3 | export const USER_TITLE_FIELD = "firstName"; 4 | 5 | export const UserTitle = (record: TUser): string => { 6 | return record.firstName?.toString() || String(record.id); 7 | }; 8 | -------------------------------------------------------------------------------- /admin-ui/src/user/roles.ts: -------------------------------------------------------------------------------- 1 | export const ROLES = [ 2 | { 3 | name: "user", 4 | displayName: "User", 5 | }, 6 | ]; 7 | -------------------------------------------------------------------------------- /admin-ui/src/util/BooleanFilter.ts: -------------------------------------------------------------------------------- 1 | export class BooleanFilter { 2 | equals?: boolean; 3 | not?: boolean; 4 | } 5 | -------------------------------------------------------------------------------- /admin-ui/src/util/BooleanNullableFilter.ts: -------------------------------------------------------------------------------- 1 | export class BooleanNullableFilter { 2 | equals?: boolean | null; 3 | not?: boolean | null; 4 | } 5 | -------------------------------------------------------------------------------- /admin-ui/src/util/DateTimeFilter.ts: -------------------------------------------------------------------------------- 1 | export class DateTimeFilter { 2 | equals?: Date; 3 | not?: Date; 4 | in?: Date[]; 5 | notIn?: Date[]; 6 | lt?: Date; 7 | lte?: Date; 8 | gt?: Date; 9 | gte?: Date; 10 | } 11 | -------------------------------------------------------------------------------- /admin-ui/src/util/DateTimeNullableFilter.ts: -------------------------------------------------------------------------------- 1 | export class DateTimeNullableFilter { 2 | equals?: Date | null; 3 | in?: Date[] | null; 4 | notIn?: Date[] | null; 5 | lt?: Date; 6 | lte?: Date; 7 | gt?: Date; 8 | gte?: Date; 9 | not?: Date; 10 | } 11 | -------------------------------------------------------------------------------- /admin-ui/src/util/FloatFilter.ts: -------------------------------------------------------------------------------- 1 | export class FloatFilter { 2 | equals?: number; 3 | in?: number[]; 4 | notIn?: number[]; 5 | lt?: number; 6 | lte?: number; 7 | gt?: number; 8 | gte?: number; 9 | not?: number; 10 | } 11 | -------------------------------------------------------------------------------- /admin-ui/src/util/FloatNullableFilter.ts: -------------------------------------------------------------------------------- 1 | export class FloatNullableFilter { 2 | equals?: number | null; 3 | in?: number[] | null; 4 | notIn?: number[] | null; 5 | lt?: number; 6 | lte?: number; 7 | gt?: number; 8 | gte?: number; 9 | not?: number; 10 | } 11 | -------------------------------------------------------------------------------- /admin-ui/src/util/IntFilter.ts: -------------------------------------------------------------------------------- 1 | export class IntFilter { 2 | equals?: number; 3 | in?: number[]; 4 | notIn?: number[]; 5 | lt?: number; 6 | lte?: number; 7 | gt?: number; 8 | gte?: number; 9 | not?: number; 10 | } 11 | -------------------------------------------------------------------------------- /admin-ui/src/util/IntNullableFilter.ts: -------------------------------------------------------------------------------- 1 | export class IntNullableFilter { 2 | equals?: number | null; 3 | in?: number[] | null; 4 | notIn?: number[] | null; 5 | lt?: number; 6 | lte?: number; 7 | gt?: number; 8 | gte?: number; 9 | not?: number; 10 | } 11 | -------------------------------------------------------------------------------- /admin-ui/src/util/JsonFilter.ts: -------------------------------------------------------------------------------- 1 | import { InputJsonValue } from "../types"; 2 | export class JsonFilter { 3 | equals?: InputJsonValue; 4 | not?: InputJsonValue; 5 | } 6 | -------------------------------------------------------------------------------- /admin-ui/src/util/JsonNullableFilter.ts: -------------------------------------------------------------------------------- 1 | import { JsonValue } from "type-fest"; 2 | export class JsonNullableFilter { 3 | equals?: JsonValue | null; 4 | not?: JsonValue | null; 5 | } 6 | -------------------------------------------------------------------------------- /admin-ui/src/util/MetaQueryPayload.ts: -------------------------------------------------------------------------------- 1 | export class MetaQueryPayload { 2 | count!: number; 3 | } 4 | -------------------------------------------------------------------------------- /admin-ui/src/util/QueryMode.ts: -------------------------------------------------------------------------------- 1 | export enum QueryMode { 2 | Default = "default", 3 | Insensitive = "insensitive", 4 | } 5 | -------------------------------------------------------------------------------- /admin-ui/src/util/SortOrder.ts: -------------------------------------------------------------------------------- 1 | export enum SortOrder { 2 | Asc = "asc", 3 | Desc = "desc", 4 | } 5 | -------------------------------------------------------------------------------- /admin-ui/src/util/StringFilter.ts: -------------------------------------------------------------------------------- 1 | import { QueryMode } from "./QueryMode"; 2 | 3 | export class StringFilter { 4 | equals?: string; 5 | in?: string[]; 6 | notIn?: string[]; 7 | lt?: string; 8 | lte?: string; 9 | gt?: string; 10 | gte?: string; 11 | contains?: string; 12 | startsWith?: string; 13 | endsWith?: string; 14 | mode?: QueryMode; 15 | not?: string; 16 | } 17 | -------------------------------------------------------------------------------- /admin-ui/src/util/StringNullableFilter.ts: -------------------------------------------------------------------------------- 1 | import { QueryMode } from "./QueryMode"; 2 | export class StringNullableFilter { 3 | equals?: string | null; 4 | in?: string[] | null; 5 | notIn?: string[] | null; 6 | lt?: string; 7 | lte?: string; 8 | gt?: string; 9 | gte?: string; 10 | contains?: string; 11 | startsWith?: string; 12 | endsWith?: string; 13 | mode?: QueryMode; 14 | not?: string; 15 | } 16 | -------------------------------------------------------------------------------- /admin-ui/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "esModuleInterop": true, 8 | "allowSyntheticDefaultImports": true, 9 | "forceConsistentCasingInFileNames": true, 10 | "noFallthroughCasesInSwitch": true, 11 | "module": "esnext", 12 | "moduleResolution": "node", 13 | "resolveJsonModule": true, 14 | "isolatedModules": true, 15 | "noEmit": true, 16 | "jsx": "react-jsx", 17 | "strict": true 18 | }, 19 | "include": ["src"], 20 | "exclude": ["./node_modules"] 21 | } 22 | -------------------------------------------------------------------------------- /client/.env: -------------------------------------------------------------------------------- 1 | # Environment variables declared in this file are automatically made available to Prisma. 2 | # See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema 3 | 4 | # Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB. 5 | # See the documentation for all the connection string options: https://pris.ly/d/connection-strings 6 | 7 | DB_URL="postgres://postgres:admin@localhost:5432/amazon-clone" -------------------------------------------------------------------------------- /client/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /client/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .next/ -------------------------------------------------------------------------------- /client/components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "new-york", 4 | "rsc": true, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.js", 8 | "css": "src/app/globals.css", 9 | "baseColor": "slate", 10 | "cssVariables": true 11 | }, 12 | "aliases": { 13 | "components": "@/components", 14 | "utils": "@/lib/utils" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /client/dockerfile: -------------------------------------------------------------------------------- 1 | # Use an official Node.js runtime as the base image 2 | FROM node:18 3 | 4 | # Set the working directory inside the container 5 | WORKDIR /app 6 | 7 | # Copy package.json and package-lock.json (if available) 8 | COPY package*.json ./ 9 | 10 | # Install dependencies 11 | RUN npm install 12 | 13 | # Copy the rest of your app's source code 14 | COPY . . 15 | 16 | # Build the production version of the app 17 | RUN npm run build 18 | 19 | # Expose the port your Next.js app will run on 20 | EXPOSE 3000 21 | 22 | # Start your Next.js app 23 | CMD ["npm", "start"] 24 | -------------------------------------------------------------------------------- /client/next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | reactStrictMode: false, 4 | env: { 5 | NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME: "dctahvizk", 6 | NEXT_PUBLIC_API: "app-61711.on-aptible.com", 7 | }, 8 | images: { 9 | domains: ["res.cloudinary.com"], 10 | }, 11 | }; 12 | 13 | module.exports = nextConfig; 14 | -------------------------------------------------------------------------------- /client/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /client/public/amazon-logo-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koolkishan/nextjs-amazon-clone/30910cb8a8374347a1ed97c172be1fbbf43a8a39/client/public/amazon-logo-white.png -------------------------------------------------------------------------------- /client/public/amazon-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koolkishan/nextjs-amazon-clone/30910cb8a8374347a1ed97c172be1fbbf43a8a39/client/public/amazon-logo.png -------------------------------------------------------------------------------- /client/public/cart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koolkishan/nextjs-amazon-clone/30910cb8a8374347a1ed97c172be1fbbf43a8a39/client/public/cart.png -------------------------------------------------------------------------------- /client/public/home/farzi.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koolkishan/nextjs-amazon-clone/30910cb8a8374347a1ed97c172be1fbbf43a8a39/client/public/home/farzi.jpg -------------------------------------------------------------------------------- /client/public/home/gift-card.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koolkishan/nextjs-amazon-clone/30910cb8a8374347a1ed97c172be1fbbf43a8a39/client/public/home/gift-card.png -------------------------------------------------------------------------------- /client/public/home/home.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koolkishan/nextjs-amazon-clone/30910cb8a8374347a1ed97c172be1fbbf43a8a39/client/public/home/home.jpg -------------------------------------------------------------------------------- /client/public/home/homepod.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koolkishan/nextjs-amazon-clone/30910cb8a8374347a1ed97c172be1fbbf43a8a39/client/public/home/homepod.png -------------------------------------------------------------------------------- /client/public/home/watch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koolkishan/nextjs-amazon-clone/30910cb8a8374347a1ed97c172be1fbbf43a8a39/client/public/home/watch.png -------------------------------------------------------------------------------- /client/public/nav-sprite-global-2x-hm-dsk-reorg._CB405936311_.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koolkishan/nextjs-amazon-clone/30910cb8a8374347a1ed97c172be1fbbf43a8a39/client/public/nav-sprite-global-2x-hm-dsk-reorg._CB405936311_.png -------------------------------------------------------------------------------- /client/public/products/product1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koolkishan/nextjs-amazon-clone/30910cb8a8374347a1ed97c172be1fbbf43a8a39/client/public/products/product1.png -------------------------------------------------------------------------------- /client/public/products/product2.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koolkishan/nextjs-amazon-clone/30910cb8a8374347a1ed97c172be1fbbf43a8a39/client/public/products/product2.webp -------------------------------------------------------------------------------- /client/public/products/product3.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koolkishan/nextjs-amazon-clone/30910cb8a8374347a1ed97c172be1fbbf43a8a39/client/public/products/product3.webp -------------------------------------------------------------------------------- /client/public/trust/trust1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koolkishan/nextjs-amazon-clone/30910cb8a8374347a1ed97c172be1fbbf43a8a39/client/public/trust/trust1.png -------------------------------------------------------------------------------- /client/public/trust/trust2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koolkishan/nextjs-amazon-clone/30910cb8a8374347a1ed97c172be1fbbf43a8a39/client/public/trust/trust2.png -------------------------------------------------------------------------------- /client/public/trust/trust3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koolkishan/nextjs-amazon-clone/30910cb8a8374347a1ed97c172be1fbbf43a8a39/client/public/trust/trust3.png -------------------------------------------------------------------------------- /client/public/trust/trust4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koolkishan/nextjs-amazon-clone/30910cb8a8374347a1ed97c172be1fbbf43a8a39/client/public/trust/trust4.png -------------------------------------------------------------------------------- /client/public/trust/trust5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koolkishan/nextjs-amazon-clone/30910cb8a8374347a1ed97c172be1fbbf43a8a39/client/public/trust/trust5.png -------------------------------------------------------------------------------- /client/src/app/admin/category/all-category/data.ts: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const columns = [ 4 | { name: "ID", uid: "id", sortable: true }, 5 | { name: "NAME", uid: "name", sortable: true }, 6 | { name: "PRODUCTS", uid: "products", sortable: true }, 7 | { name: "ACTIONS", uid: "actions" }, 8 | ]; 9 | 10 | export { columns }; 11 | -------------------------------------------------------------------------------- /client/src/app/admin/category/reports/page.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const Page = () => { 4 | return <div>Page</div>; 5 | }; 6 | 7 | export default Page; 8 | -------------------------------------------------------------------------------- /client/src/app/admin/dashboard/components/charts/category-sales/category-sales.tsx: -------------------------------------------------------------------------------- 1 | import dynamic from "next/dynamic"; 2 | import React from "react"; 3 | import { PieChart, Pie, Legend, Tooltip, ResponsiveContainer } from "recharts"; 4 | 5 | const CategorySales = ({ 6 | data, 7 | }: { 8 | data: { id: string; name: string; revenue: number }[]; 9 | }) => { 10 | return ( 11 | <div style={{ width: "100%", height: "100%" }} className=""> 12 | <ResponsiveContainer height="100%" width="100%"> 13 | <PieChart> 14 | <Pie 15 | scale={4} 16 | data={data} 17 | dataKey="revenue" 18 | cx={300} 19 | cy={150} 20 | outerRadius={100} 21 | fill="#ffb700" 22 | label 23 | /> 24 | <Tooltip /> 25 | </PieChart> 26 | </ResponsiveContainer> 27 | </div> 28 | ); 29 | }; 30 | 31 | export default dynamic(() => Promise.resolve(CategorySales), { 32 | ssr: false, 33 | }); 34 | -------------------------------------------------------------------------------- /client/src/app/admin/dashboard/components/charts/category-sales/index.ts: -------------------------------------------------------------------------------- 1 | import CategorySales from "./category-sales"; 2 | 3 | export { CategorySales }; 4 | -------------------------------------------------------------------------------- /client/src/app/admin/dashboard/components/charts/daily-revenue/daily-revenue.tsx: -------------------------------------------------------------------------------- 1 | import dynamic from "next/dynamic"; 2 | import React from "react"; 3 | import { 4 | AreaChart, 5 | Area, 6 | XAxis, 7 | YAxis, 8 | Tooltip, 9 | Legend, 10 | ResponsiveContainer, 11 | } from "recharts"; 12 | 13 | const DailyRevenue = ({ 14 | data, 15 | }: { 16 | data: { date: string; revenue: number }[]; 17 | }) => { 18 | return ( 19 | <ResponsiveContainer height="100%" width="100%"> 20 | <AreaChart data={data}> 21 | <XAxis dataKey="date" /> 22 | <YAxis /> 23 | <Tooltip /> 24 | <Legend /> 25 | <Area 26 | type="monotone" 27 | dataKey="revenue" 28 | fill="#ffb700" 29 | stroke="#ff9900" 30 | /> 31 | </AreaChart> 32 | </ResponsiveContainer> 33 | ); 34 | }; 35 | 36 | export default dynamic(() => Promise.resolve(DailyRevenue), { 37 | ssr: false, 38 | }); 39 | -------------------------------------------------------------------------------- /client/src/app/admin/dashboard/components/charts/daily-revenue/index.ts: -------------------------------------------------------------------------------- 1 | import DailyRevenue from "./daily-revenue"; 2 | 3 | export { DailyRevenue }; 4 | -------------------------------------------------------------------------------- /client/src/app/admin/dashboard/components/charts/monthly-sales/index.ts: -------------------------------------------------------------------------------- 1 | import MonthlySales from "./monthly-sales"; 2 | 3 | export { MonthlySales }; 4 | -------------------------------------------------------------------------------- /client/src/app/admin/dashboard/components/charts/monthly-sales/monthly-sales.tsx: -------------------------------------------------------------------------------- 1 | import dynamic from "next/dynamic"; 2 | import React from "react"; 3 | import { 4 | BarChart, 5 | Bar, 6 | XAxis, 7 | YAxis, 8 | Tooltip, 9 | Legend, 10 | ResponsiveContainer, 11 | } from "recharts"; 12 | 13 | const MonthlySales = ({ 14 | data, 15 | }: { 16 | data: { month: string; sales: number }[]; 17 | }) => { 18 | return ( 19 | <ResponsiveContainer height="100%" width="100%"> 20 | <BarChart data={data}> 21 | <XAxis dataKey="month" /> 22 | <YAxis /> 23 | <Tooltip /> 24 | <Legend /> 25 | <Bar dataKey="sales" fill="#ffb700" stroke="#ff9900" /> 26 | </BarChart> 27 | </ResponsiveContainer> 28 | ); 29 | }; 30 | 31 | export default dynamic(() => Promise.resolve(MonthlySales), { 32 | ssr: false, 33 | }); 34 | -------------------------------------------------------------------------------- /client/src/app/admin/dashboard/components/recent-orders/index.ts: -------------------------------------------------------------------------------- 1 | import RecentOrders from "./recent-orders"; 2 | 3 | export { RecentOrders }; 4 | -------------------------------------------------------------------------------- /client/src/app/admin/dashboard/components/stats/index.ts: -------------------------------------------------------------------------------- 1 | import Stats from "./stats"; 2 | 3 | export { Stats }; 4 | -------------------------------------------------------------------------------- /client/src/app/admin/dashboard/components/stats/stats.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Card, CardHeader, CardBody } from "@nextui-org/react"; 3 | 4 | const Stats = ({ title, data }: { title: string; data: number }) => { 5 | return ( 6 | <div> 7 | <Card className="w-64 min-h-[100px]"> 8 | <CardHeader className="justify-between text-xl font-semibold"> 9 | {title} 10 | </CardHeader> 11 | <CardBody className="px-3 py-0 text-3xl font-bold text-amazon-primary "> 12 | {data} 13 | </CardBody> 14 | </Card> 15 | </div> 16 | ); 17 | }; 18 | 19 | export default Stats; 20 | -------------------------------------------------------------------------------- /client/src/app/admin/logout/page.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { useAppStore } from "@/store/store"; 3 | import { useRouter } from "next/navigation"; 4 | import React, { useEffect } from "react"; 5 | 6 | const Page = () => { 7 | const { setUserInfo } = useAppStore(); 8 | const router = useRouter(); 9 | useEffect(() => { 10 | setUserInfo(undefined); 11 | localStorage.clear(); 12 | router.push("/admin/login"); 13 | }, [setUserInfo, router]); 14 | 15 | return null; 16 | }; 17 | 18 | export default Page; 19 | -------------------------------------------------------------------------------- /client/src/app/admin/orders/[orderId]/components/index.ts: -------------------------------------------------------------------------------- 1 | import Product from "./Product"; 2 | 3 | export { Product }; 4 | -------------------------------------------------------------------------------- /client/src/app/admin/page.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { useRouter } from "next/navigation"; 3 | import React, { useEffect } from "react"; 4 | 5 | const Page = () => { 6 | const router = useRouter(); 7 | useEffect(() => { 8 | router.push("/admin/dashboard"); 9 | }, [router]); 10 | return null; 11 | }; 12 | 13 | export default Page; 14 | -------------------------------------------------------------------------------- /client/src/app/admin/products/all-products/data.ts: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const columns = [ 4 | { name: "ID", uid: "id", sortable: true }, 5 | { name: "NAME", uid: "title", sortable: true }, 6 | { name: "PRICE", uid: "discountPrice", sortable: true }, 7 | { name: "ORDERS", uid: "_count", sortable: true }, 8 | { name: "ACTIONS", uid: "actions" }, 9 | ]; 10 | 11 | export { columns }; 12 | -------------------------------------------------------------------------------- /client/src/app/admin/products/reports/page.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const Page = () => { 4 | return <div>Page</div>; 5 | }; 6 | 7 | export default Page; 8 | -------------------------------------------------------------------------------- /client/src/app/cart/components/product/index.ts: -------------------------------------------------------------------------------- 1 | import Product from "./product"; 2 | 3 | export { Product }; 4 | -------------------------------------------------------------------------------- /client/src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koolkishan/nextjs-amazon-clone/30910cb8a8374347a1ed97c172be1fbbf43a8a39/client/src/app/favicon.ico -------------------------------------------------------------------------------- /client/src/app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | .link-hover { 6 | cursor: pointer; 7 | width: max-content; 8 | } 9 | 10 | .link-hover:hover { 11 | color: #ff9900; 12 | } 13 | -------------------------------------------------------------------------------- /client/src/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import { Navbar } from "@/components/client/navbar"; 2 | import "./globals.css"; 3 | import type { Metadata } from "next"; 4 | import { Inter } from "next/font/google"; 5 | import Layouts from "./layouts/layouts"; 6 | import { Providers } from "./providers"; 7 | const inter = Inter({ subsets: ["latin"] }); 8 | 9 | export const metadata: Metadata = { 10 | title: "Amazon Clone", 11 | description: 12 | "Created by Kishan Sheth for Youtube. Just for learning purposes.", 13 | }; 14 | 15 | export default function RootLayout({ 16 | children, 17 | }: { 18 | children: React.ReactNode; 19 | }) { 20 | return ( 21 | <html lang="en" className="light"> 22 | <body className={inter.className} suppressHydrationWarning={true}> 23 | <Providers> 24 | <Layouts>{children}</Layouts> 25 | </Providers> 26 | </body> 27 | </html> 28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /client/src/app/layouts/admin-layout.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import React from "react"; 4 | import { usePathname } from "next/navigation"; 5 | import { Sidebar } from "@/components/admin/sidebar"; 6 | 7 | const AdminLayout = ({ children }: { children: React.ReactNode }) => { 8 | const pathname = usePathname(); 9 | return !pathname.includes("admin/login") ? ( 10 | <div className="flex min-h-[100vh] "> 11 | <Sidebar /> 12 | <main className="flex-1 min-h-[100vh]">{children}</main> 13 | </div> 14 | ) : ( 15 | children 16 | ); 17 | }; 18 | 19 | export default AdminLayout; 20 | -------------------------------------------------------------------------------- /client/src/app/layouts/client-store-layout.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import React from "react"; 4 | import { usePathname } from "next/navigation"; 5 | import { Navbar } from "@/components/client/navbar"; 6 | import { Footer } from "@/components/client/footer"; 7 | 8 | const ClientStoreLayout = ({ children }: { children: React.ReactNode }) => { 9 | const pathname = usePathname(); 10 | return !pathname.includes("login") && !pathname.includes("signup") ? ( 11 | <div className="flex flex-col min-h-[100vh]"> 12 | <Navbar /> 13 | <main className="flex-1 min-h-[100vh]">{children}</main> 14 | <Footer /> 15 | </div> 16 | ) : ( 17 | children 18 | ); 19 | }; 20 | 21 | export default ClientStoreLayout; 22 | -------------------------------------------------------------------------------- /client/src/app/layouts/index.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koolkishan/nextjs-amazon-clone/30910cb8a8374347a1ed97c172be1fbbf43a8a39/client/src/app/layouts/index.ts -------------------------------------------------------------------------------- /client/src/app/logout/page.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { useAppStore } from "@/store/store"; 3 | import { useRouter } from "next/navigation"; 4 | import React, { useEffect } from "react"; 5 | 6 | const Page = () => { 7 | const { setUserInfo } = useAppStore(); 8 | const router = useRouter(); 9 | useEffect(() => { 10 | setUserInfo(undefined); 11 | localStorage.clear(); 12 | router.push("/login"); 13 | }, [setUserInfo, router]); 14 | 15 | return null; 16 | }; 17 | 18 | export default Page; 19 | -------------------------------------------------------------------------------- /client/src/app/my-orders/[orderId]/components/Product.tsx: -------------------------------------------------------------------------------- 1 | import { ProductType } from "@/utils/types"; 2 | import Image from "next/image"; 3 | import React from "react"; 4 | 5 | const Product = ({ productData }: { productData: ProductType }) => { 6 | return ( 7 | <div className="flex gap-10 bg-gray-100 p-10 rounded-sm items-center "> 8 | <div className="relative h-24 w-24"> 9 | <Image src={productData.images[0]} fill alt="product" /> 10 | </div> 11 | <div className="flex-1"> 12 | <h3 className="font-bold text-lg">{productData.title}</h3> 13 | <div className="flex gap-5 text-sm mt-2"> 14 | <div className="flex gap-1"> 15 | <span>Color:</span> 16 | <span className="font-bold">Titanium White</span> 17 | </div> 18 | <div> 19 | <span>Variant:</span> 20 | <span className="font-bold">{productData.variants[0]}</span> 21 | </div> 22 | </div> 23 | </div> 24 | 25 | <div className="w-16 text-center"> 26 | <strong>${productData.discountPrice}</strong> 27 | </div> 28 | </div> 29 | ); 30 | }; 31 | 32 | export default Product; 33 | -------------------------------------------------------------------------------- /client/src/app/my-orders/[orderId]/components/index.ts: -------------------------------------------------------------------------------- 1 | import Product from "./Product"; 2 | 3 | export { Product }; 4 | -------------------------------------------------------------------------------- /client/src/app/page.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { AppleCards } from "@/components/client/apple-cards"; 3 | import { HomeCards } from "@/components/client/home-cards"; 4 | import { HomeCarousels } from "@/components/client/home-carousels"; 5 | import { PrimeVideo } from "@/components/client/prime-video"; 6 | import { ProductGrid } from "@/components/client/product-grid"; 7 | import { getAllProducts } from "@/lib/api/products"; 8 | import { useAppStore } from "@/store/store"; 9 | import { useEffect, useState } from "react"; 10 | 11 | export default function Home() { 12 | const [products, setProducts] = useState([]); 13 | const { setToast } = useAppStore(); 14 | useEffect(() => { 15 | const fetchProducts = async () => { 16 | const response = await getAllProducts(); 17 | if (response) { 18 | setProducts(response); 19 | } 20 | }; 21 | fetchProducts(); 22 | }, [setToast]); 23 | 24 | return ( 25 | <div className="flex gap-10 flex-col mb-10"> 26 | <HomeCarousels /> 27 | <ProductGrid products={products} title="Related to items you've viewed" /> 28 | <AppleCards /> 29 | <ProductGrid products={products} title="Inspired by shopping trends" /> 30 | <PrimeVideo /> 31 | <ProductGrid products={products} title="Today's Deals" /> 32 | <HomeCards /> 33 | <ProductGrid products={products} title="Your browsing history" /> 34 | </div> 35 | ); 36 | } 37 | -------------------------------------------------------------------------------- /client/src/app/product/[productId]/components/add-review/add-review.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const AddReview = () => { 4 | return <div>AddReview</div>; 5 | }; 6 | 7 | export default AddReview; 8 | -------------------------------------------------------------------------------- /client/src/app/product/[productId]/components/add-review/index.ts: -------------------------------------------------------------------------------- 1 | import AddReview from "./add-review"; 2 | 3 | export { AddReview }; 4 | -------------------------------------------------------------------------------- /client/src/app/product/[productId]/components/colors/colors.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | 3 | const Colors = ({ colors }: { colors: string[] }) => { 4 | const [selected, setSelected] = useState(0); 5 | 6 | return ( 7 | <ul className="flex gap-5"> 8 | {colors.map((color, index) => ( 9 | <li 10 | key={color} 11 | className={`h-10 w-10 rounded-full cursor-pointer border-4 ${ 12 | selected === index ? " border-amazon-primary " : "border-white" 13 | }`} 14 | style={{ backgroundColor: color }} 15 | onClick={() => setSelected(index)} 16 | ></li> 17 | ))} 18 | </ul> 19 | ); 20 | }; 21 | 22 | export default Colors; 23 | -------------------------------------------------------------------------------- /client/src/app/product/[productId]/components/colors/index.ts: -------------------------------------------------------------------------------- 1 | import Colors from "./colors"; 2 | 3 | export { Colors }; 4 | -------------------------------------------------------------------------------- /client/src/app/product/[productId]/components/image-slider/image-slider.tsx: -------------------------------------------------------------------------------- 1 | import Image from "next/image"; 2 | import React, { useState } from "react"; 3 | 4 | const ImageSlider = ({ images }: { images: string[] }) => { 5 | const [selected, setSelected] = useState(images[0]); 6 | return ( 7 | <div className="flex gap-5"> 8 | <ul className="flex flex-col gap-2"> 9 | {images.map((image) => ( 10 | <li 11 | className={`p-2 bg-gray-200 rounded-sm w-max cursor-pointer`} 12 | key={image} 13 | onClick={() => setSelected(image)} 14 | > 15 | <div className="relative h-10 w-16"> 16 | <Image src={image} alt="product" fill /> 17 | </div> 18 | </li> 19 | ))} 20 | </ul> 21 | 22 | <div className="flex items-center justify-center w-full"> 23 | <div className="h-[500px] w-full relative"> 24 | <Image src={selected} alt="product" fill /> 25 | </div> 26 | </div> 27 | </div> 28 | ); 29 | }; 30 | 31 | export default ImageSlider; 32 | -------------------------------------------------------------------------------- /client/src/app/product/[productId]/components/image-slider/index.ts: -------------------------------------------------------------------------------- 1 | import ImageSlider from "./image-slider"; 2 | 3 | export { ImageSlider }; 4 | -------------------------------------------------------------------------------- /client/src/app/product/[productId]/components/payment-info/index.ts: -------------------------------------------------------------------------------- 1 | import PaymentInfo from "./payment-info"; 2 | 3 | export { PaymentInfo }; 4 | -------------------------------------------------------------------------------- /client/src/app/product/[productId]/components/review-bars/index.ts: -------------------------------------------------------------------------------- 1 | import ReviewBars from "./review-bars"; 2 | 3 | export { ReviewBars }; 4 | -------------------------------------------------------------------------------- /client/src/app/product/[productId]/components/review-bars/review-bars.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const ReviewBars = () => { 4 | return <div>ReviewBars</div>; 5 | }; 6 | 7 | export default ReviewBars; 8 | -------------------------------------------------------------------------------- /client/src/app/product/[productId]/components/reviews/index.ts: -------------------------------------------------------------------------------- 1 | import Reviews from "./reviews"; 2 | 3 | export { Reviews }; 4 | -------------------------------------------------------------------------------- /client/src/app/product/[productId]/components/reviews/reviews.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const Reviews = () => { 4 | return <div>Reviews</div>; 5 | }; 6 | 7 | export default Reviews; 8 | -------------------------------------------------------------------------------- /client/src/app/product/[productId]/components/trust-slider/index.ts: -------------------------------------------------------------------------------- 1 | import TrustSlider from "./trust-slider"; 2 | 3 | export { TrustSlider }; 4 | -------------------------------------------------------------------------------- /client/src/app/product/[productId]/components/trust-slider/trust-slider.tsx: -------------------------------------------------------------------------------- 1 | import Image from "next/image"; 2 | import React from "react"; 3 | 4 | const TrustSlider = () => { 5 | const images = [ 6 | { 7 | name: "Amazon Delivered", 8 | image: "/trust/trust1.png", 9 | }, 10 | { 11 | name: "Top Brand", 12 | image: "/trust/trust2.png", 13 | }, 14 | { 15 | name: "Warranty Policy", 16 | image: "/trust/trust3.png", 17 | }, 18 | { 19 | name: "Free Delivery", 20 | image: "/trust/trust4.png", 21 | }, 22 | { 23 | name: "7 days Replacement", 24 | image: "/trust/trust5.png", 25 | }, 26 | ]; 27 | return ( 28 | <div className="flex gap-2 items-center justify-center my-4"> 29 | {images.map((image) => ( 30 | <div 31 | key={image.name} 32 | className="flex flex-col items-center justify-center" 33 | > 34 | <Image alt="trust" height={25} width={25} src={image.image} /> 35 | <span className=" text-xs text-center">{image.name}</span> 36 | </div> 37 | ))} 38 | </div> 39 | ); 40 | }; 41 | 42 | export default TrustSlider; 43 | -------------------------------------------------------------------------------- /client/src/app/product/[productId]/components/variants/index.ts: -------------------------------------------------------------------------------- 1 | import Variants from "./variants"; 2 | 3 | export { Variants }; 4 | -------------------------------------------------------------------------------- /client/src/app/product/[productId]/components/variants/variants.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | 3 | const Variants = ({ variants }: { variants: string[] }) => { 4 | const [selected, setSelected] = useState(0); 5 | 6 | return ( 7 | <ul className="flex gap-5 my-5"> 8 | {variants.map((variant, index) => ( 9 | <li 10 | key={variant} 11 | className={`cursor-pointer border-2 p-1 text-sm font-semibold ${ 12 | selected === index ? " border-amazon-primary " : "border-white" 13 | }`} 14 | onClick={() => setSelected(index)} 15 | > 16 | {variant} 17 | </li> 18 | ))} 19 | </ul> 20 | ); 21 | }; 22 | 23 | export default Variants; 24 | -------------------------------------------------------------------------------- /client/src/app/providers.tsx: -------------------------------------------------------------------------------- 1 | // app/providers.tsx 2 | "use client"; 3 | 4 | import { NextUIProvider } from "@nextui-org/react"; 5 | 6 | export function Providers({ children }: { children: React.ReactNode }) { 7 | return <NextUIProvider>{children}</NextUIProvider>; 8 | } 9 | -------------------------------------------------------------------------------- /client/src/app/search/components/filters/index.ts: -------------------------------------------------------------------------------- 1 | import Filters from "./filters"; 2 | 3 | export { Filters }; 4 | -------------------------------------------------------------------------------- /client/src/app/search/components/product/colors/colors.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const Colors = ({ colors }: { colors: string[] }) => { 4 | return ( 5 | <ul className="flex gap-1"> 6 | {colors.map((color) => ( 7 | <li 8 | key={color} 9 | className="h-5 w-5 rounded-full" 10 | style={{ backgroundColor: color }} 11 | ></li> 12 | ))} 13 | </ul> 14 | ); 15 | }; 16 | 17 | export default Colors; 18 | -------------------------------------------------------------------------------- /client/src/app/search/components/product/colors/index.ts: -------------------------------------------------------------------------------- 1 | import Colors from "./colors"; 2 | 3 | export { Colors }; 4 | -------------------------------------------------------------------------------- /client/src/app/search/components/product/index.ts: -------------------------------------------------------------------------------- 1 | import Product from "./product"; 2 | 3 | export { Product }; 4 | -------------------------------------------------------------------------------- /client/src/app/search/components/product/ratings/index.ts: -------------------------------------------------------------------------------- 1 | import Ratings from "./ratings"; 2 | 3 | export { Ratings }; 4 | -------------------------------------------------------------------------------- /client/src/app/search/components/product/ratings/ratings.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { FaStar } from "react-icons/fa"; 3 | 4 | const Ratings = () => { 5 | return ( 6 | <div className="flex gap-1 items-center "> 7 | <span className="font-medium">5</span> 8 | <div className="text-yellow-400 flex"> 9 | <FaStar /> 10 | <FaStar /> 11 | <FaStar /> 12 | <FaStar /> 13 | <FaStar /> 14 | </div> 15 | </div> 16 | ); 17 | }; 18 | 19 | export default Ratings; 20 | -------------------------------------------------------------------------------- /client/src/app/success/page.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { recordStripePayment } from "@/lib/api/orders"; 3 | import { useAppStore } from "@/store/store"; 4 | import { useRouter, useSearchParams } from "next/navigation"; 5 | import React, { useEffect } from "react"; 6 | 7 | const Page = () => { 8 | const { emptyCart } = useAppStore(); 9 | const router = useRouter(); 10 | const searchParams = useSearchParams(); 11 | const paymentIntent = searchParams.get("payment_intent"); 12 | 13 | useEffect(() => { 14 | const updateOrderInfo = async () => { 15 | const response = await recordStripePayment(paymentIntent as string); 16 | }; 17 | if (paymentIntent) { 18 | updateOrderInfo(); 19 | emptyCart(); 20 | setTimeout(() => router.push("/my-orders"), 3000); 21 | } 22 | }, [emptyCart, router, paymentIntent]); 23 | 24 | return ( 25 | <div className="h-[80vh] flex items-center px-20 pt-20 flex-col"> 26 | <h1 className="text-4xl text-center"> 27 | Payment successful. You are being redirected to the orders page. 28 | </h1> 29 | <h1 className="text-4xl text-center">Please do not close the page.</h1> 30 | </div> 31 | ); 32 | }; 33 | 34 | export default Page; 35 | -------------------------------------------------------------------------------- /client/src/components/admin/sidebar/index.ts: -------------------------------------------------------------------------------- 1 | import Sidebar from "./sidebar"; 2 | 3 | export { Sidebar }; 4 | -------------------------------------------------------------------------------- /client/src/components/client/apple-cards/index.ts: -------------------------------------------------------------------------------- 1 | import AppleCards from "./apple-cards"; 2 | 3 | export { AppleCards }; 4 | -------------------------------------------------------------------------------- /client/src/components/client/footer/index.ts: -------------------------------------------------------------------------------- 1 | import Footer from "./footer"; 2 | 3 | export { Footer }; 4 | -------------------------------------------------------------------------------- /client/src/components/client/home-cards/home-cards.tsx: -------------------------------------------------------------------------------- 1 | import Image from "next/image"; 2 | import React from "react"; 3 | import { FaCaretRight } from "react-icons/fa"; 4 | 5 | const HomeCards = () => { 6 | return ( 7 | <div className="h-[20vh] bg-orange-500 flex items-center justify-between px-40 text-white"> 8 | <div className="text-4xl capitalize ">gift cards</div> 9 | <div className=""> 10 | <h3 className="text-3xl">Let them choose the perfect gift</h3> 11 | <div className="flex items-center gap-2 cursor-pointer"> 12 | <span>Shop now</span> 13 | <FaCaretRight /> 14 | </div> 15 | </div> 16 | <div className="relative"> 17 | {/* <div className=" "> */} 18 | <div className=" absolute h-32 w-28 bg-white top-3 right-5 z-10"></div> 19 | <Image 20 | src="/home/gift-card.png" 21 | alt="gift card" 22 | height={300} 23 | width={300} 24 | className=" relative z-20" 25 | /> 26 | 27 | {/* </div> */} 28 | </div> 29 | </div> 30 | ); 31 | }; 32 | 33 | export default HomeCards; 34 | -------------------------------------------------------------------------------- /client/src/components/client/home-cards/index.ts: -------------------------------------------------------------------------------- 1 | import HomeCards from "./home-cards"; 2 | 3 | export { HomeCards }; 4 | -------------------------------------------------------------------------------- /client/src/components/client/home-carousels/home-caroursels.tsx: -------------------------------------------------------------------------------- 1 | import Image from "next/image"; 2 | import React from "react"; 3 | 4 | const HomeCarousels = () => { 5 | return ( 6 | <div className="h-[50vh] bg-red-100"> 7 | <div className="relative h-full w-full bg-cover"> 8 | <Image fill alt="home" src="/home/home.jpg" /> 9 | </div> 10 | </div> 11 | ); 12 | }; 13 | 14 | export default HomeCarousels; 15 | -------------------------------------------------------------------------------- /client/src/components/client/home-carousels/index.ts: -------------------------------------------------------------------------------- 1 | import HomeCarousels from "./home-caroursels"; 2 | 3 | export { HomeCarousels }; 4 | -------------------------------------------------------------------------------- /client/src/components/client/navbar/index.ts: -------------------------------------------------------------------------------- 1 | import Navbar from "./navbar"; 2 | 3 | export { Navbar }; 4 | -------------------------------------------------------------------------------- /client/src/components/client/prime-video/index.ts: -------------------------------------------------------------------------------- 1 | import PrimeVideo from "./prime-video"; 2 | 3 | export { PrimeVideo }; 4 | -------------------------------------------------------------------------------- /client/src/components/client/prime-video/prime-video.tsx: -------------------------------------------------------------------------------- 1 | import Image from "next/image"; 2 | import React from "react"; 3 | import { FaCaretRight } from "react-icons/fa"; 4 | 5 | const PrimeVideo = () => { 6 | return ( 7 | <div className="bg-amazon-dark h-[50vh] grid grid-cols-2 overflow-hidden"> 8 | <div className="flex items-center justify-center flex-col gap-3"> 9 | <span className="text-white tracking-[10px]">AMAZON EXCLUSIVE</span> 10 | <h1 className="text-6xl text-white letter tracking-widest">FARZI</h1> 11 | <div className="flex gap-2 text-amazon-blue font-semibold"> 12 | <span>Watch now</span> 13 | <FaCaretRight /> 14 | </div> 15 | <div className="text-amazon-blue font-semibold"> 16 | Get Amazon Prime Video for just $20 17 | </div> 18 | </div> 19 | <div className="relative h-full w-[800px]"> 20 | <Image src="/home/farzi.jpg" fill alt="prime" /> 21 | </div> 22 | </div> 23 | ); 24 | }; 25 | 26 | export default PrimeVideo; 27 | -------------------------------------------------------------------------------- /client/src/components/client/product-grid/index.ts: -------------------------------------------------------------------------------- 1 | import ProductGrid from "./product-grid"; 2 | 3 | export { ProductGrid }; 4 | -------------------------------------------------------------------------------- /client/src/lib/api/api-client.ts: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | 3 | const apiUrl = "http://localhost:3000"; 4 | const jwtKey = "accessToken"; 5 | 6 | axios.interceptors.request.use( 7 | (config) => { 8 | const { origin } = new URL(config.url as string); 9 | const allowedOrigins = [apiUrl]; 10 | const accessToken = localStorage.getItem(jwtKey); 11 | if (allowedOrigins.includes(origin)) { 12 | config.headers.authorization = `Bearer ${accessToken}`; 13 | } 14 | return config; 15 | }, 16 | (error) => { 17 | return Promise.reject(error); 18 | } 19 | ); 20 | 21 | export const createUrl = (endpoint: string) => new URL(endpoint, apiUrl).href; 22 | export const isStoredJwt = () => Boolean(localStorage.getItem(jwtKey)); 23 | export const setStoredJwt = (accessToken: string) => 24 | localStorage.setItem(jwtKey, accessToken); 25 | 26 | export const get = axios.get; 27 | export const patch = axios.patch; 28 | export const post = axios.post; 29 | export const axiosDelete = axios.delete; 30 | -------------------------------------------------------------------------------- /client/src/lib/api/auth.ts: -------------------------------------------------------------------------------- 1 | import { createUrl, isStoredJwt, post, setStoredJwt, get } from "./api-client"; 2 | 3 | export const me = async () => { 4 | try { 5 | return isStoredJwt() ? (await get(createUrl("/api/me")))?.data : null; 6 | } catch (err) { 7 | return err; 8 | } 9 | }; 10 | 11 | export const login = async (username: string, password: string) => { 12 | try { 13 | const result = await post(createUrl("/api/login"), { username, password }); 14 | setStoredJwt(result?.data?.accessToken); 15 | return me(); 16 | } catch (err) { 17 | return err; 18 | } 19 | }; 20 | 21 | export const signup = async (username: string, password: string) => { 22 | const result = ( 23 | await post(createUrl("/api/signup"), { 24 | username, 25 | password, 26 | firstName: "demo", 27 | lastName: "s", 28 | }).catch(() => null) 29 | )?.data; 30 | 31 | if (!result) { 32 | return alert("Could not sign up"); 33 | } 34 | setStoredJwt(result.accessToken); 35 | return me(); 36 | }; 37 | -------------------------------------------------------------------------------- /client/src/lib/api/search.ts: -------------------------------------------------------------------------------- 1 | import qs from "qs"; 2 | import { createUrl, get } from "./api-client"; 3 | 4 | export const getSearchResults = async ( 5 | searchTerm: string, 6 | category: string 7 | ) => { 8 | try { 9 | let query; 10 | if (searchTerm && searchTerm.length > 0) { 11 | query = qs.stringify({ 12 | where: { 13 | title: { contains: searchTerm }, 14 | }, 15 | }); 16 | } else if (category && category.length > 0) { 17 | query = qs.stringify({ 18 | where: { 19 | category: { id: category }, 20 | }, 21 | }); 22 | } 23 | 24 | const response = await get(createUrl(`/api/products?${query}`)); 25 | if (response.data) { 26 | return response.data; 27 | } 28 | } catch (error) { 29 | console.log(error); 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /client/src/lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { type ClassValue, clsx } from "clsx" 2 | import { twMerge } from "tailwind-merge" 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)) 6 | } 7 | -------------------------------------------------------------------------------- /client/src/store/slices/auth-slice.ts: -------------------------------------------------------------------------------- 1 | import { StateCreator } from "zustand"; 2 | 3 | export interface AuthSlice { 4 | userInfo: undefined | any; 5 | setUserInfo: (userInfo: any) => void; 6 | } 7 | 8 | export const createAuthSlice: StateCreator<AuthSlice> = (set, get) => ({ 9 | userInfo: undefined, 10 | setUserInfo: (userInfo: any) => set({ userInfo }), 11 | }); 12 | -------------------------------------------------------------------------------- /client/src/store/slices/index.ts: -------------------------------------------------------------------------------- 1 | import { AuthSlice, createAuthSlice } from "./auth-slice"; 2 | import { CartSlice, createCartSlice } from "./cart-slice"; 3 | import { OrdersSlice, createOrdersSlice } from "./orders-slice"; 4 | import { ToastsSlice, createToastsSlice } from "./toasts-slice"; 5 | export { 6 | createAuthSlice, 7 | createCartSlice, 8 | createOrdersSlice, 9 | createToastsSlice, 10 | }; 11 | export type { AuthSlice, CartSlice, OrdersSlice, ToastsSlice }; 12 | -------------------------------------------------------------------------------- /client/src/store/slices/orders-slice.ts: -------------------------------------------------------------------------------- 1 | import { StateCreator } from "zustand"; 2 | 3 | export interface OrdersSlice { 4 | ordersInfo: undefined | any; 5 | setOrdersInfo: (ordersInfo: any) => void; 6 | } 7 | 8 | export const createOrdersSlice: StateCreator<OrdersSlice> = (set, get) => ({ 9 | ordersInfo: undefined, 10 | setOrdersInfo: (ordersInfo) => { 11 | set({ ordersInfo }); 12 | }, 13 | }); 14 | -------------------------------------------------------------------------------- /client/src/store/slices/searchSlice.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koolkishan/nextjs-amazon-clone/30910cb8a8374347a1ed97c172be1fbbf43a8a39/client/src/store/slices/searchSlice.ts -------------------------------------------------------------------------------- /client/src/store/slices/toasts-slice.ts: -------------------------------------------------------------------------------- 1 | import { StateCreator } from "zustand"; 2 | 3 | export interface ToastsSlice { 4 | toasts: string[]; 5 | setToast: (message: string) => void; 6 | clearToast: () => void; 7 | } 8 | 9 | export const createToastsSlice: StateCreator<ToastsSlice> = (set, get) => ({ 10 | toasts: [], 11 | setToast: (message) => { 12 | const toasts = get().toasts; 13 | 14 | set({ toasts: [...toasts, message] }); 15 | }, 16 | clearToast: () => set({ toasts: [] }), 17 | }); 18 | -------------------------------------------------------------------------------- /client/src/store/store.ts: -------------------------------------------------------------------------------- 1 | import { create } from "zustand"; 2 | import { 3 | AuthSlice, 4 | createAuthSlice, 5 | CartSlice, 6 | createCartSlice, 7 | createOrdersSlice, 8 | OrdersSlice, 9 | ToastsSlice, 10 | createToastsSlice, 11 | } from "./slices"; 12 | 13 | type StoreState = AuthSlice & CartSlice & OrdersSlice & ToastsSlice; 14 | 15 | export const useAppStore = create<StoreState>()((...a) => ({ 16 | ...createAuthSlice(...a), 17 | ...createCartSlice(...a), 18 | ...createOrdersSlice(...a), 19 | ...createToastsSlice(...a), 20 | })); 21 | -------------------------------------------------------------------------------- /client/src/utils/types.ts: -------------------------------------------------------------------------------- 1 | export interface Category { 2 | id: string; 3 | } 4 | 5 | export interface Order { 6 | id: string; 7 | } 8 | 9 | export interface ProductType { 10 | category: Category; 11 | order: Order[]; 12 | colors: string[]; 13 | createdAt: string; 14 | description: string[]; 15 | discountPrice: number; 16 | id: string; 17 | images: string[]; 18 | salePrice: number; 19 | title: string; 20 | updatedAt: string; 21 | variants: string[]; 22 | } 23 | -------------------------------------------------------------------------------- /client/tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import { nextui } from "@nextui-org/react"; 2 | 3 | /** @type {import('tailwindcss').Config} */ 4 | module.exports = { 5 | content: [ 6 | "./pages/**/*.{ts,tsx}", 7 | "./components/**/*.{ts,tsx}", 8 | "./app/**/*.{ts,tsx}", 9 | "./src/**/*.{ts,tsx}", 10 | "./node_modules/@nextui-org/theme/dist/**/*.{js,ts,jsx,tsx}", 11 | ], 12 | theme: { 13 | extend: { 14 | colors: { 15 | primary: "#ff9900", 16 | "amazon-dark": "#141B24", 17 | "amazon-background": "#eef3f9", 18 | "amazon-primary": "#ff9900", 19 | "amazon-secondary": "#ffb700", 20 | "amazon-blue": "#00a8e1", 21 | }, 22 | }, 23 | }, 24 | darkMode: "class", 25 | plugins: [nextui()], 26 | }; 27 | -------------------------------------------------------------------------------- /client/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "noEmit": true, 9 | "esModuleInterop": true, 10 | "module": "esnext", 11 | "moduleResolution": "bundler", 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "jsx": "preserve", 15 | "incremental": true, 16 | "plugins": [ 17 | { 18 | "name": "next" 19 | } 20 | ], 21 | "paths": { 22 | "@/*": ["./src/*"] 23 | } 24 | }, 25 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 26 | "exclude": ["node_modules"] 27 | } 28 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "amazon-clone", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "npm-run-all -p start:frontend start:backend", 8 | "start:frontend": "cross-env PORT=5000 npm --prefix client run dev", 9 | "start:admin": "npm --prefix admin-ui start", 10 | "start:backend": "npm --prefix server start", 11 | "postinstall": "npm i --prefix client && npm i --prefix server" 12 | }, 13 | "devDependencies": { 14 | "cross-env": "^7.0.3", 15 | "npm-run-all": "^4.1.5" 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "git+https://github.com/koolkishan/nextjs-amazon-clone.git" 20 | }, 21 | "author": "", 22 | "license": "ISC", 23 | "bugs": { 24 | "url": "https://github.com/koolkishan/nextjs-amazon-clone/issues" 25 | }, 26 | "homepage": "https://github.com/koolkishan/nextjs-amazon-clone#readme", 27 | "dependencies": { 28 | "qs": "^6.11.2" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /server/.dockerignore: -------------------------------------------------------------------------------- 1 | .dockerignore 2 | docker-compose.yml 3 | Dockerfile 4 | dist/ 5 | node_modules 6 | .env 7 | .gitignore 8 | .prettierignore -------------------------------------------------------------------------------- /server/.env: -------------------------------------------------------------------------------- 1 | BCRYPT_SALT=10 2 | COMPOSE_PROJECT_NAME=amp_clmxto3lp04awm8016ggl589t 3 | PORT=3000 4 | DB_URL=postgres://postgres:admin@localhost:5432/amazon-clone 5 | DB_USER=postgres 6 | DB_PASSWORD=admin 7 | DB_PORT=5432 8 | DB_NAME=my-db 9 | JWT_SECRET_KEY=Change_ME!!! 10 | JWT_EXPIRATION=2d -------------------------------------------------------------------------------- /server/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | /node_modules 4 | /dist 5 | .DS_Store 6 | -------------------------------------------------------------------------------- /server/.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | dist/ 3 | prisma/migrations/ 4 | package-lock.json 5 | coverage/ -------------------------------------------------------------------------------- /server/Dockerfile: -------------------------------------------------------------------------------- 1 | # Development Dockerfile 2 | # This Dockerfile is intended for development use and includes development dependencies and tools. 3 | 4 | # Use a Node.js base image with development tools 5 | FROM node:16.0.0 AS development 6 | 7 | # Create a directory where the application will be built 8 | WORKDIR /app 9 | 10 | # Copy over the dependency manifests, both the package.json 11 | # and the package-lock.json are copied over 12 | COPY package*.json ./ 13 | 14 | # Install packages and their dependencies 15 | RUN npm install 16 | 17 | 18 | # Copy over the prisma schema 19 | COPY prisma/schema.prisma ./prisma/ 20 | 21 | # Generate the prisma client based on the schema 22 | RUN npm run prisma:generate 23 | 24 | # Copy over the code base 25 | COPY . . 26 | 27 | RUN npx prisma db push 28 | # Create the bundle of the application 29 | RUN npm run build 30 | 31 | # Expose a specific port on the Docker container for development purposes 32 | ENV PORT=3000 33 | EXPOSE ${PORT} 34 | 35 | # Start the development server using the previously built application 36 | CMD ["npm", "start"] 37 | -------------------------------------------------------------------------------- /server/docker-compose.dev.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | services: 3 | db: 4 | image: postgres:12 5 | ports: 6 | - ${DB_PORT}:5432 7 | environment: 8 | POSTGRES_USER: ${DB_USER} 9 | POSTGRES_PASSWORD: ${DB_PASSWORD} 10 | volumes: 11 | - postgres:/var/lib/postgresql/data 12 | volumes: 13 | postgres: ~ 14 | -------------------------------------------------------------------------------- /server/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | services: 3 | server: 4 | build: 5 | context: . 6 | args: 7 | NPM_LOG_LEVEL: notice 8 | ports: 9 | - ${PORT}:3000 10 | environment: 11 | BCRYPT_SALT: ${BCRYPT_SALT} 12 | JWT_SECRET_KEY: ${JWT_SECRET_KEY} 13 | JWT_EXPIRATION: ${JWT_EXPIRATION} 14 | DB_URL: postgres://${DB_USER}:${DB_PASSWORD}@db:5432/${DB_NAME} 15 | depends_on: 16 | - migrate 17 | migrate: 18 | build: 19 | context: . 20 | args: 21 | NPM_LOG_LEVEL: notice 22 | command: npm run db:init 23 | working_dir: /app/server 24 | environment: 25 | BCRYPT_SALT: ${BCRYPT_SALT} 26 | DB_URL: postgres://${DB_USER}:${DB_PASSWORD}@db:5432/${DB_NAME} 27 | depends_on: 28 | db: 29 | condition: service_healthy 30 | db: 31 | image: postgres:12 32 | ports: 33 | - ${DB_PORT}:5432 34 | environment: 35 | POSTGRES_USER: ${DB_USER} 36 | POSTGRES_PASSWORD: ${DB_PASSWORD} 37 | POSTGRES_DB: ${DB_NAME} 38 | volumes: 39 | - postgres:/var/lib/postgresql/data 40 | healthcheck: 41 | test: 42 | - CMD-SHELL 43 | - pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER} 44 | timeout: 45s 45 | interval: 10s 46 | retries: 10 47 | volumes: 48 | postgres: ~ 49 | -------------------------------------------------------------------------------- /server/nest-cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "sourceRoot": "src", 3 | "compilerOptions": { 4 | "assets": ["swagger"] 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /server/scripts/customSeed.ts: -------------------------------------------------------------------------------- 1 | import { PrismaClient } from "@prisma/client"; 2 | 3 | export async function customSeed() { 4 | const client = new PrismaClient(); 5 | const username = "admin"; 6 | 7 | //replace this sample code to populate your database 8 | //with data that is required for your service to start 9 | await client.user.update({ 10 | where: { username: username }, 11 | data: { 12 | username, 13 | }, 14 | }); 15 | 16 | client.$disconnect(); 17 | } 18 | -------------------------------------------------------------------------------- /server/scripts/seed.ts: -------------------------------------------------------------------------------- 1 | import * as dotenv from "dotenv"; 2 | import { PrismaClient } from "@prisma/client"; 3 | import { customSeed } from "./customSeed"; 4 | import { Salt, parseSalt } from "../src/auth/password.service"; 5 | import { hash } from "bcrypt"; 6 | 7 | if (require.main === module) { 8 | dotenv.config(); 9 | 10 | const { BCRYPT_SALT } = process.env; 11 | 12 | if (!BCRYPT_SALT) { 13 | throw new Error("BCRYPT_SALT environment variable must be defined"); 14 | } 15 | const salt = parseSalt(BCRYPT_SALT); 16 | 17 | seed(salt).catch((error) => { 18 | console.error(error); 19 | process.exit(1); 20 | }); 21 | } 22 | 23 | async function seed(bcryptSalt: Salt) { 24 | console.info("Seeding database..."); 25 | 26 | const client = new PrismaClient(); 27 | 28 | const data = { 29 | username: "admin", 30 | password: await hash("admin", bcryptSalt), 31 | roles: ["user"], 32 | }; 33 | 34 | await client.user.upsert({ 35 | where: { 36 | username: data.username, 37 | }, 38 | 39 | update: {}, 40 | create: data, 41 | }); 42 | 43 | void client.$disconnect(); 44 | 45 | console.info("Seeding database with custom seed..."); 46 | customSeed(); 47 | 48 | console.info("Seeded database successfully"); 49 | } 50 | -------------------------------------------------------------------------------- /server/src/auth/Credentials.ts: -------------------------------------------------------------------------------- 1 | import { ApiProperty } from "@nestjs/swagger"; 2 | import { InputType, Field } from "@nestjs/graphql"; 3 | import { IsString } from "class-validator"; 4 | 5 | @InputType() 6 | export class Credentials { 7 | @ApiProperty({ 8 | required: true, 9 | type: String, 10 | }) 11 | @IsString() 12 | @Field(() => String, { nullable: false }) 13 | username!: string; 14 | @ApiProperty({ 15 | required: true, 16 | type: String, 17 | }) 18 | @IsString() 19 | @Field(() => String, { nullable: false }) 20 | password!: string; 21 | } 22 | 23 | @InputType() 24 | export class SignupCredentials { 25 | @ApiProperty({ 26 | required: true, 27 | type: String, 28 | }) 29 | @IsString() 30 | @Field(() => String, { nullable: false }) 31 | username!: string; 32 | @ApiProperty({ 33 | required: true, 34 | type: String, 35 | }) 36 | @IsString() 37 | @Field(() => String, { nullable: false }) 38 | password!: string; 39 | @ApiProperty({ 40 | required: true, 41 | type: String, 42 | }) 43 | @IsString() 44 | @Field(() => String, { nullable: false }) 45 | firstName!: string; 46 | @ApiProperty({ 47 | required: true, 48 | type: String, 49 | }) 50 | @IsString() 51 | @Field(() => String, { nullable: false }) 52 | lastName!: string; 53 | } 54 | -------------------------------------------------------------------------------- /server/src/auth/IAuthStrategy.ts: -------------------------------------------------------------------------------- 1 | import { UserInfo } from "./UserInfo"; 2 | 3 | export interface IAuthStrategy { 4 | validate: (...any: any) => Promise<UserInfo>; 5 | } 6 | -------------------------------------------------------------------------------- /server/src/auth/ITokenService.ts: -------------------------------------------------------------------------------- 1 | export interface ITokenPayload { 2 | id: string; 3 | username: string; 4 | password: string; 5 | } 6 | 7 | export interface ITokenService { 8 | createToken: ({ id, username, password }: ITokenPayload) => Promise<string>; 9 | } 10 | -------------------------------------------------------------------------------- /server/src/auth/LoginArgs.ts: -------------------------------------------------------------------------------- 1 | import { ArgsType, Field } from "@nestjs/graphql"; 2 | import { ValidateNested } from "class-validator"; 3 | import { Type } from "class-transformer"; 4 | import { Credentials, SignupCredentials } from "./Credentials"; 5 | 6 | @ArgsType() 7 | export class LoginArgs { 8 | @Field(() => Credentials, { nullable: false }) 9 | @Type(() => Credentials) 10 | @ValidateNested() 11 | credentials!: Credentials; 12 | } 13 | 14 | @ArgsType() 15 | export class SignupArgs { 16 | @Field(() => Credentials, { nullable: false }) 17 | credentials!: SignupCredentials; 18 | } 19 | -------------------------------------------------------------------------------- /server/src/auth/UserInfo.ts: -------------------------------------------------------------------------------- 1 | import { Field, ObjectType } from "@nestjs/graphql"; 2 | import { User } from "../user/base/User"; 3 | 4 | @ObjectType() 5 | export class UserInfo implements Partial<User> { 6 | @Field(() => String) 7 | id!: string; 8 | @Field(() => String) 9 | username!: string; 10 | @Field(() => [String]) 11 | roles!: string[]; 12 | @Field(() => String, { nullable: true }) 13 | accessToken?: string; 14 | } 15 | -------------------------------------------------------------------------------- /server/src/auth/abac.util.ts: -------------------------------------------------------------------------------- 1 | import { Permission } from "accesscontrol"; 2 | 3 | /** 4 | * @returns attributes not allowed to appear on given data according to given 5 | * attributeMatchers 6 | */ 7 | export function getInvalidAttributes( 8 | permission: Permission, 9 | // eslint-disable-next-line @typescript-eslint/ban-types 10 | data: Object 11 | ): string[] { 12 | // The structuredClone call is necessary because the 13 | // `Permission.filter` function doesn't consider objects 14 | // with null prototypes. And in graphql requests, the 15 | // object passed here by the request interceptor is an object 16 | // with a null prototype. 17 | const filteredData = permission.filter(structuredClone(data)); 18 | return Object.keys(data).filter((key) => !(key in filteredData)); 19 | } 20 | -------------------------------------------------------------------------------- /server/src/auth/acl.module.ts: -------------------------------------------------------------------------------- 1 | import { AccessControlModule, RolesBuilder } from "nest-access-control"; 2 | // @ts-ignore 3 | // eslint-disable-next-line import/no-unresolved 4 | import grants from "../grants.json"; 5 | 6 | // eslint-disable-next-line @typescript-eslint/naming-convention 7 | export const ACLModule = AccessControlModule.forRoles(new RolesBuilder(grants)); 8 | -------------------------------------------------------------------------------- /server/src/auth/auth.controller.ts: -------------------------------------------------------------------------------- 1 | import { Body, Controller, Get, Post, Req } from "@nestjs/common"; 2 | import { ApiBearerAuth, ApiOkResponse, ApiTags } from "@nestjs/swagger"; 3 | import { AuthService } from "./auth.service"; 4 | import { Credentials, SignupCredentials } from "./Credentials"; 5 | import { UserInfo } from "./UserInfo"; 6 | import { User } from "src/user/base/User"; 7 | import { Request } from "express"; 8 | 9 | @ApiTags("auth") 10 | @Controller() 11 | export class AuthController { 12 | constructor(private readonly authService: AuthService) {} 13 | @ApiBearerAuth() 14 | @ApiOkResponse({ type: User }) 15 | @Get("me") 16 | async me(@Req() request: Request): Promise<User> { 17 | return this.authService.me(request.headers.authorization); 18 | } 19 | @Post("login") 20 | async login(@Body() body: Credentials): Promise<UserInfo> { 21 | return this.authService.login(body); 22 | } 23 | @Post("signup") 24 | async signup(@Body() body: SignupCredentials): Promise<UserInfo> { 25 | return this.authService.signup(body); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /server/src/auth/auth.resolver.ts: -------------------------------------------------------------------------------- 1 | import * as common from "@nestjs/common"; 2 | import * as gqlACGuard from "../auth/gqlAC.guard"; 3 | import { AuthService } from "./auth.service"; 4 | import { GqlDefaultAuthGuard } from "./gqlDefaultAuth.guard"; 5 | import { UserData } from "./userData.decorator"; 6 | import { LoginArgs, SignupArgs } from "./LoginArgs"; 7 | import { UserInfo } from "./UserInfo"; 8 | import { User } from "src/user/base/User"; 9 | import { Args, Mutation, Query, Resolver, Context } from "@nestjs/graphql"; 10 | import { Request } from "express"; 11 | 12 | @Resolver(UserInfo) 13 | export class AuthResolver { 14 | constructor(private readonly authService: AuthService) {} 15 | @Mutation(() => UserInfo) 16 | async login(@Args() args: LoginArgs): Promise<UserInfo> { 17 | return this.authService.login(args.credentials); 18 | } 19 | 20 | @Mutation(() => UserInfo) 21 | async signup(@Args() args: SignupArgs): Promise<UserInfo> { 22 | return this.authService.signup(args.credentials); 23 | } 24 | @Query(() => User) 25 | async me(@Context("req") request: Request): Promise<User> { 26 | return this.authService.me(request.headers.authorization); 27 | } 28 | 29 | @Query(() => UserInfo) 30 | @common.UseGuards(GqlDefaultAuthGuard, gqlACGuard.GqlACGuard) 31 | async userInfo(@UserData() entityInfo: UserInfo): Promise<UserInfo> { 32 | return entityInfo; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /server/src/auth/base/token.service.base.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-unresolved */ 2 | import { Injectable } from "@nestjs/common"; 3 | import { JwtService } from "@nestjs/jwt"; 4 | import { INVALID_PASSWORD_ERROR, INVALID_USERNAME_ERROR } from "../constants"; 5 | import { ITokenService, ITokenPayload } from "../ITokenService"; 6 | /** 7 | * TokenServiceBase is a jwt bearer implementation of ITokenService 8 | */ 9 | @Injectable() 10 | export class TokenServiceBase implements ITokenService { 11 | constructor(protected readonly jwtService: JwtService) {} 12 | /** 13 | * 14 | * @object { id: String, username: String, password: String} 15 | * @returns a jwt token sign with the username and user id 16 | */ 17 | createToken({ id, username, password }: ITokenPayload): Promise<string> { 18 | if (!username) return Promise.reject(INVALID_USERNAME_ERROR); 19 | if (!password) return Promise.reject(INVALID_PASSWORD_ERROR); 20 | return this.jwtService.signAsync({ 21 | sub: id, 22 | username, 23 | }); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /server/src/auth/constants.ts: -------------------------------------------------------------------------------- 1 | export const INVALID_USERNAME_ERROR = "Invalid username"; 2 | export const INVALID_PASSWORD_ERROR = "Invalid password"; 3 | -------------------------------------------------------------------------------- /server/src/auth/defaultAuth.guard.ts: -------------------------------------------------------------------------------- 1 | import { Observable } from "rxjs"; 2 | import { ExecutionContext, Injectable } from "@nestjs/common"; 3 | import { Reflector } from "@nestjs/core"; 4 | import { IS_PUBLIC_KEY } from "../decorators/public.decorator"; 5 | import { JwtAuthGuard } from "./jwt/jwtAuth.guard"; 6 | 7 | @Injectable() 8 | export class DefaultAuthGuard extends JwtAuthGuard { 9 | constructor(private readonly reflector: Reflector) { 10 | super(); 11 | } 12 | 13 | canActivate( 14 | context: ExecutionContext 15 | ): boolean | Promise<boolean> | Observable<any> { 16 | const isPublic = this.reflector.get<boolean>( 17 | IS_PUBLIC_KEY, 18 | context.getHandler() 19 | ); 20 | 21 | if (isPublic) { 22 | return true; 23 | } 24 | 25 | return super.canActivate(context); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /server/src/auth/gqlAC.guard.ts: -------------------------------------------------------------------------------- 1 | import { ExecutionContext } from "@nestjs/common"; 2 | import { GqlExecutionContext } from "@nestjs/graphql"; 3 | import { ACGuard } from "nest-access-control"; 4 | 5 | export class GqlACGuard<User extends any = any> extends ACGuard<User> { 6 | async getUser(context: ExecutionContext): Promise<User> { 7 | const ctx = GqlExecutionContext.create(context); 8 | const request = ctx.getContext<{ req: { user: User } }>().req; 9 | return request.user; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /server/src/auth/gqlDefaultAuth.guard.ts: -------------------------------------------------------------------------------- 1 | import { ExecutionContext } from "@nestjs/common"; 2 | import { GqlExecutionContext } from "@nestjs/graphql"; 3 | import type { Request } from "express"; 4 | // @ts-ignore 5 | // eslint-disable-next-line 6 | import { DefaultAuthGuard } from "./defaultAuth.guard"; 7 | 8 | export class GqlDefaultAuthGuard extends DefaultAuthGuard { 9 | // This method is required for the interface - do not delete it. 10 | getRequest(context: ExecutionContext): Request { 11 | const ctx = GqlExecutionContext.create(context); 12 | return ctx.getContext<{ req: Request }>().req; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /server/src/auth/gqlUserRoles.decorator.ts: -------------------------------------------------------------------------------- 1 | import { createParamDecorator, ExecutionContext } from "@nestjs/common"; 2 | import { GqlExecutionContext } from "@nestjs/graphql"; 3 | 4 | /** 5 | * Access the user roles from the request object i.e `req.user.roles`. 6 | * 7 | * You can pass an optional property key to the decorator to get it from the user object 8 | * e.g `@UserRoles('permissions')` will return the `req.user.permissions` instead. 9 | */ 10 | export const UserRoles = createParamDecorator( 11 | (data: string, context: ExecutionContext) => { 12 | const ctx = GqlExecutionContext.create(context); 13 | const request = ctx.getContext<{ req: { user: any } }>().req; 14 | if (!request.user) { 15 | return null; 16 | } 17 | return data ? request.user[data] : request.user.roles; 18 | } 19 | ); 20 | -------------------------------------------------------------------------------- /server/src/auth/jwt/base/jwt.strategy.base.ts: -------------------------------------------------------------------------------- 1 | import { UnauthorizedException } from "@nestjs/common"; 2 | import { PassportStrategy } from "@nestjs/passport"; 3 | import { ExtractJwt, Strategy } from "passport-jwt"; 4 | import { IAuthStrategy } from "../../IAuthStrategy"; 5 | import { UserInfo } from "../../UserInfo"; 6 | import { UserService } from "../../../user/user.service"; 7 | 8 | export class JwtStrategyBase 9 | extends PassportStrategy(Strategy) 10 | implements IAuthStrategy 11 | { 12 | constructor( 13 | protected readonly secretOrKey: string, 14 | protected readonly userService: UserService 15 | ) { 16 | super({ 17 | jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), 18 | ignoreExpiration: false, 19 | secretOrKey, 20 | }); 21 | } 22 | 23 | async validate(payload: UserInfo): Promise<UserInfo> { 24 | const { username } = payload; 25 | const user = await this.userService.findOne({ 26 | where: { username }, 27 | }); 28 | if (!user) { 29 | throw new UnauthorizedException(); 30 | } 31 | if ( 32 | !Array.isArray(user.roles) || 33 | typeof user.roles !== "object" || 34 | user.roles === null 35 | ) { 36 | throw new Error("User roles is not a valid value"); 37 | } 38 | return { ...user, roles: user.roles as string[] }; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /server/src/auth/jwt/jwt.strategy.ts: -------------------------------------------------------------------------------- 1 | import { Inject, Injectable } from "@nestjs/common"; 2 | import { JWT_SECRET_KEY } from "../../constants"; 3 | import { JwtStrategyBase } from "./base/jwt.strategy.base"; 4 | import { UserService } from "../../user/user.service"; 5 | 6 | @Injectable() 7 | export class JwtStrategy extends JwtStrategyBase { 8 | constructor( 9 | @Inject(JWT_SECRET_KEY) secretOrKey: string, 10 | protected readonly userService: UserService 11 | ) { 12 | super(secretOrKey, userService); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /server/src/auth/jwt/jwtAuth.guard.ts: -------------------------------------------------------------------------------- 1 | import { AuthGuard } from "@nestjs/passport"; 2 | 3 | export class JwtAuthGuard extends AuthGuard("jwt") {} 4 | -------------------------------------------------------------------------------- /server/src/auth/jwt/jwtSecretFactory.ts: -------------------------------------------------------------------------------- 1 | import { JWT_SECRET_KEY } from "../../constants"; 2 | import { SecretsManagerService } from "../../providers/secrets/secretsManager.service"; 3 | 4 | export const jwtSecretFactory = { 5 | provide: JWT_SECRET_KEY, 6 | useFactory: async ( 7 | secretsService: SecretsManagerService 8 | ): Promise<string> => { 9 | const secret = await secretsService.getSecret<string>(JWT_SECRET_KEY); 10 | if (secret) { 11 | return secret; 12 | } 13 | throw new Error("jwtSecretFactory missing secret"); 14 | }, 15 | inject: [SecretsManagerService], 16 | }; 17 | -------------------------------------------------------------------------------- /server/src/auth/tempfile: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koolkishan/nextjs-amazon-clone/30910cb8a8374347a1ed97c172be1fbbf43a8a39/server/src/auth/tempfile -------------------------------------------------------------------------------- /server/src/auth/token.service.ts: -------------------------------------------------------------------------------- 1 | //@ts-ignore 2 | import { ITokenService } from "./ITokenService"; 3 | // eslint-disable-next-line import/no-unresolved 4 | //@ts-ignore 5 | import { TokenServiceBase } from "./base/token.service.base"; 6 | 7 | export class TokenService extends TokenServiceBase implements ITokenService { 8 | /** 9 | * @param bearer 10 | * @returns the username from a jwt token 11 | */ 12 | decodeToken(bearer: string): string { 13 | return this.jwtService.verify(bearer).username; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /server/src/category/base/CategoryCountArgs.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { ArgsType, Field } from "@nestjs/graphql"; 13 | import { ApiProperty } from "@nestjs/swagger"; 14 | import { CategoryWhereInput } from "./CategoryWhereInput"; 15 | import { Type } from "class-transformer"; 16 | 17 | @ArgsType() 18 | class CategoryCountArgs { 19 | @ApiProperty({ 20 | required: false, 21 | type: () => CategoryWhereInput, 22 | }) 23 | @Field(() => CategoryWhereInput, { nullable: true }) 24 | @Type(() => CategoryWhereInput) 25 | where?: CategoryWhereInput; 26 | } 27 | 28 | export { CategoryCountArgs as CategoryCountArgs }; 29 | -------------------------------------------------------------------------------- /server/src/category/base/CategoryFindUniqueArgs.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { ArgsType, Field } from "@nestjs/graphql"; 13 | import { ApiProperty } from "@nestjs/swagger"; 14 | import { CategoryWhereUniqueInput } from "./CategoryWhereUniqueInput"; 15 | import { ValidateNested } from "class-validator"; 16 | import { Type } from "class-transformer"; 17 | 18 | @ArgsType() 19 | class CategoryFindUniqueArgs { 20 | @ApiProperty({ 21 | required: true, 22 | type: () => CategoryWhereUniqueInput, 23 | }) 24 | @ValidateNested() 25 | @Type(() => CategoryWhereUniqueInput) 26 | @Field(() => CategoryWhereUniqueInput, { nullable: false }) 27 | where!: CategoryWhereUniqueInput; 28 | } 29 | 30 | export { CategoryFindUniqueArgs as CategoryFindUniqueArgs }; 31 | -------------------------------------------------------------------------------- /server/src/category/base/CategoryWhereUniqueInput.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { InputType, Field } from "@nestjs/graphql"; 13 | import { ApiProperty } from "@nestjs/swagger"; 14 | import { IsString } from "class-validator"; 15 | 16 | @InputType() 17 | class CategoryWhereUniqueInput { 18 | @ApiProperty({ 19 | required: true, 20 | type: String, 21 | }) 22 | @IsString() 23 | @Field(() => String) 24 | id!: string; 25 | } 26 | 27 | export { CategoryWhereUniqueInput as CategoryWhereUniqueInput }; 28 | -------------------------------------------------------------------------------- /server/src/category/base/CreateCategoryArgs.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { ArgsType, Field } from "@nestjs/graphql"; 13 | import { ApiProperty } from "@nestjs/swagger"; 14 | import { CategoryCreateInput } from "./CategoryCreateInput"; 15 | import { ValidateNested } from "class-validator"; 16 | import { Type } from "class-transformer"; 17 | 18 | @ArgsType() 19 | class CreateCategoryArgs { 20 | @ApiProperty({ 21 | required: true, 22 | type: () => CategoryCreateInput, 23 | }) 24 | @ValidateNested() 25 | @Type(() => CategoryCreateInput) 26 | @Field(() => CategoryCreateInput, { nullable: false }) 27 | data!: CategoryCreateInput; 28 | } 29 | 30 | export { CreateCategoryArgs as CreateCategoryArgs }; 31 | -------------------------------------------------------------------------------- /server/src/category/base/DeleteCategoryArgs.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { ArgsType, Field } from "@nestjs/graphql"; 13 | import { ApiProperty } from "@nestjs/swagger"; 14 | import { CategoryWhereUniqueInput } from "./CategoryWhereUniqueInput"; 15 | import { ValidateNested } from "class-validator"; 16 | import { Type } from "class-transformer"; 17 | 18 | @ArgsType() 19 | class DeleteCategoryArgs { 20 | @ApiProperty({ 21 | required: true, 22 | type: () => CategoryWhereUniqueInput, 23 | }) 24 | @ValidateNested() 25 | @Type(() => CategoryWhereUniqueInput) 26 | @Field(() => CategoryWhereUniqueInput, { nullable: false }) 27 | where!: CategoryWhereUniqueInput; 28 | } 29 | 30 | export { DeleteCategoryArgs as DeleteCategoryArgs }; 31 | -------------------------------------------------------------------------------- /server/src/category/base/ProductCreateNestedManyWithoutCategoriesInput.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { InputType, Field } from "@nestjs/graphql"; 13 | import { ProductWhereUniqueInput } from "../../product/base/ProductWhereUniqueInput"; 14 | import { ApiProperty } from "@nestjs/swagger"; 15 | 16 | @InputType() 17 | class ProductCreateNestedManyWithoutCategoriesInput { 18 | @Field(() => [ProductWhereUniqueInput], { 19 | nullable: true, 20 | }) 21 | @ApiProperty({ 22 | required: false, 23 | type: () => [ProductWhereUniqueInput], 24 | }) 25 | connect?: Array<ProductWhereUniqueInput>; 26 | } 27 | 28 | export { ProductCreateNestedManyWithoutCategoriesInput as ProductCreateNestedManyWithoutCategoriesInput }; 29 | -------------------------------------------------------------------------------- /server/src/category/base/category.module.base.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { Module } from "@nestjs/common"; 13 | import { MorganModule } from "nest-morgan"; 14 | import { ACLModule } from "../../auth/acl.module"; 15 | @Module({ 16 | imports: [ACLModule, MorganModule], 17 | exports: [ACLModule, MorganModule], 18 | }) 19 | export class CategoryModuleBase {} 20 | -------------------------------------------------------------------------------- /server/src/category/category.controller.ts: -------------------------------------------------------------------------------- 1 | import * as common from "@nestjs/common"; 2 | import * as swagger from "@nestjs/swagger"; 3 | import * as nestAccessControl from "nest-access-control"; 4 | import { CategoryService } from "./category.service"; 5 | import { CategoryControllerBase } from "./base/category.controller.base"; 6 | 7 | @swagger.ApiTags("categories") 8 | @common.Controller("categories") 9 | export class CategoryController extends CategoryControllerBase { 10 | constructor( 11 | protected readonly service: CategoryService, 12 | @nestAccessControl.InjectRolesBuilder() 13 | protected readonly rolesBuilder: nestAccessControl.RolesBuilder 14 | ) { 15 | super(service, rolesBuilder); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /server/src/category/category.module.ts: -------------------------------------------------------------------------------- 1 | import { Module, forwardRef } from "@nestjs/common"; 2 | import { AuthModule } from "../auth/auth.module"; 3 | import { CategoryModuleBase } from "./base/category.module.base"; 4 | import { CategoryService } from "./category.service"; 5 | import { CategoryController } from "./category.controller"; 6 | import { CategoryResolver } from "./category.resolver"; 7 | 8 | @Module({ 9 | imports: [CategoryModuleBase, forwardRef(() => AuthModule)], 10 | controllers: [CategoryController], 11 | providers: [CategoryService, CategoryResolver], 12 | exports: [CategoryService], 13 | }) 14 | export class CategoryModule {} 15 | -------------------------------------------------------------------------------- /server/src/category/category.resolver.ts: -------------------------------------------------------------------------------- 1 | import * as graphql from "@nestjs/graphql"; 2 | import * as nestAccessControl from "nest-access-control"; 3 | import * as gqlACGuard from "../auth/gqlAC.guard"; 4 | import { GqlDefaultAuthGuard } from "../auth/gqlDefaultAuth.guard"; 5 | import * as common from "@nestjs/common"; 6 | import { CategoryResolverBase } from "./base/category.resolver.base"; 7 | import { Category } from "./base/Category"; 8 | import { CategoryService } from "./category.service"; 9 | 10 | @common.UseGuards(GqlDefaultAuthGuard, gqlACGuard.GqlACGuard) 11 | @graphql.Resolver(() => Category) 12 | export class CategoryResolver extends CategoryResolverBase { 13 | constructor( 14 | protected readonly service: CategoryService, 15 | @nestAccessControl.InjectRolesBuilder() 16 | protected readonly rolesBuilder: nestAccessControl.RolesBuilder 17 | ) { 18 | super(service, rolesBuilder); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /server/src/category/category.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from "@nestjs/common"; 2 | import { PrismaService } from "../prisma/prisma.service"; 3 | import { CategoryServiceBase } from "./base/category.service.base"; 4 | 5 | @Injectable() 6 | export class CategoryService extends CategoryServiceBase { 7 | constructor(protected readonly prisma: PrismaService) { 8 | super(prisma); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /server/src/connectMicroservices.ts: -------------------------------------------------------------------------------- 1 | import { INestApplication } from "@nestjs/common"; 2 | import { ConfigService } from "@nestjs/config"; 3 | 4 | export async function connectMicroservices(app: INestApplication) { 5 | const configService = app.get(ConfigService); 6 | } 7 | -------------------------------------------------------------------------------- /server/src/constants.ts: -------------------------------------------------------------------------------- 1 | export const JWT_SECRET_KEY = "JWT_SECRET_KEY"; 2 | export const JWT_EXPIRATION = "JWT_EXPIRATION"; 3 | -------------------------------------------------------------------------------- /server/src/decorators/public.decorator.ts: -------------------------------------------------------------------------------- 1 | import { applyDecorators, SetMetadata } from "@nestjs/common"; 2 | 3 | export const IS_PUBLIC_KEY = "isPublic"; 4 | 5 | const PublicAuthMiddleware = SetMetadata(IS_PUBLIC_KEY, true); 6 | const PublicAuthSwagger = SetMetadata("swagger/apiSecurity", ["isPublic"]); 7 | 8 | // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types 9 | export const Public = () => 10 | applyDecorators(PublicAuthMiddleware, PublicAuthSwagger); 11 | -------------------------------------------------------------------------------- /server/src/errors.ts: -------------------------------------------------------------------------------- 1 | import * as common from "@nestjs/common"; 2 | import { ApiProperty } from "@nestjs/swagger"; 3 | 4 | export class ForbiddenException extends common.ForbiddenException { 5 | @ApiProperty() 6 | statusCode!: number; 7 | @ApiProperty() 8 | message!: string; 9 | } 10 | 11 | export class NotFoundException extends common.NotFoundException { 12 | @ApiProperty() 13 | statusCode!: number; 14 | @ApiProperty() 15 | message!: string; 16 | } 17 | -------------------------------------------------------------------------------- /server/src/health/base/health.controller.base.ts: -------------------------------------------------------------------------------- 1 | import { Get, HttpStatus, Res } from "@nestjs/common"; 2 | import { Response } from "express"; 3 | import { HealthService } from "../health.service"; 4 | 5 | export class HealthControllerBase { 6 | constructor(protected readonly healthService: HealthService) {} 7 | @Get("live") 8 | healthLive(@Res() response: Response): Response<void> { 9 | return response.status(HttpStatus.NO_CONTENT).send(); 10 | } 11 | @Get("ready") 12 | async healthReady(@Res() response: Response): Promise<Response<void>> { 13 | const dbConnection = await this.healthService.isDbReady(); 14 | if (!dbConnection) { 15 | return response.status(HttpStatus.NOT_FOUND).send(); 16 | } 17 | return response.status(HttpStatus.NO_CONTENT).send(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /server/src/health/base/health.service.base.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from "@nestjs/common"; 2 | import { PrismaService } from "../../prisma/prisma.service"; 3 | 4 | @Injectable() 5 | export class HealthServiceBase { 6 | constructor(protected readonly prisma: PrismaService) {} 7 | async isDbReady(): Promise<boolean> { 8 | try { 9 | await this.prisma.$queryRaw`SELECT 1`; 10 | return true; 11 | } catch (error) { 12 | return false; 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /server/src/health/health.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller } from "@nestjs/common"; 2 | import { HealthControllerBase } from "./base/health.controller.base"; 3 | import { HealthService } from "./health.service"; 4 | 5 | @Controller("_health") 6 | export class HealthController extends HealthControllerBase { 7 | constructor(protected readonly healthService: HealthService) { 8 | super(healthService); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /server/src/health/health.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from "@nestjs/common"; 2 | import { HealthController } from "./health.controller"; 3 | import { HealthService } from "./health.service"; 4 | 5 | @Module({ 6 | controllers: [HealthController], 7 | providers: [HealthService], 8 | exports: [HealthService], 9 | }) 10 | export class HealthModule {} 11 | -------------------------------------------------------------------------------- /server/src/health/health.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from "@nestjs/common"; 2 | import { PrismaService } from "../prisma/prisma.service"; 3 | import { HealthServiceBase } from "./base/health.service.base"; 4 | 5 | @Injectable() 6 | export class HealthService extends HealthServiceBase { 7 | constructor(protected readonly prisma: PrismaService) { 8 | super(prisma); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /server/src/order/base/CreateOrderArgs.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { ArgsType, Field } from "@nestjs/graphql"; 13 | import { ApiProperty } from "@nestjs/swagger"; 14 | import { OrderCreateInput } from "./OrderCreateInput"; 15 | import { ValidateNested } from "class-validator"; 16 | import { Type } from "class-transformer"; 17 | 18 | @ArgsType() 19 | class CreateOrderArgs { 20 | @ApiProperty({ 21 | required: true, 22 | type: () => OrderCreateInput, 23 | }) 24 | @ValidateNested() 25 | @Type(() => OrderCreateInput) 26 | @Field(() => OrderCreateInput, { nullable: false }) 27 | data!: OrderCreateInput; 28 | } 29 | 30 | export { CreateOrderArgs as CreateOrderArgs }; 31 | -------------------------------------------------------------------------------- /server/src/order/base/DeleteOrderArgs.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { ArgsType, Field } from "@nestjs/graphql"; 13 | import { ApiProperty } from "@nestjs/swagger"; 14 | import { OrderWhereUniqueInput } from "./OrderWhereUniqueInput"; 15 | import { ValidateNested } from "class-validator"; 16 | import { Type } from "class-transformer"; 17 | 18 | @ArgsType() 19 | class DeleteOrderArgs { 20 | @ApiProperty({ 21 | required: true, 22 | type: () => OrderWhereUniqueInput, 23 | }) 24 | @ValidateNested() 25 | @Type(() => OrderWhereUniqueInput) 26 | @Field(() => OrderWhereUniqueInput, { nullable: false }) 27 | where!: OrderWhereUniqueInput; 28 | } 29 | 30 | export { DeleteOrderArgs as DeleteOrderArgs }; 31 | -------------------------------------------------------------------------------- /server/src/order/base/OrderCountArgs.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { ArgsType, Field } from "@nestjs/graphql"; 13 | import { ApiProperty } from "@nestjs/swagger"; 14 | import { OrderWhereInput } from "./OrderWhereInput"; 15 | import { Type } from "class-transformer"; 16 | 17 | @ArgsType() 18 | class OrderCountArgs { 19 | @ApiProperty({ 20 | required: false, 21 | type: () => OrderWhereInput, 22 | }) 23 | @Field(() => OrderWhereInput, { nullable: true }) 24 | @Type(() => OrderWhereInput) 25 | where?: OrderWhereInput; 26 | } 27 | 28 | export { OrderCountArgs as OrderCountArgs }; 29 | -------------------------------------------------------------------------------- /server/src/order/base/OrderFindUniqueArgs.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { ArgsType, Field } from "@nestjs/graphql"; 13 | import { ApiProperty } from "@nestjs/swagger"; 14 | import { OrderWhereUniqueInput } from "./OrderWhereUniqueInput"; 15 | import { ValidateNested } from "class-validator"; 16 | import { Type } from "class-transformer"; 17 | 18 | @ArgsType() 19 | class OrderFindUniqueArgs { 20 | @ApiProperty({ 21 | required: true, 22 | type: () => OrderWhereUniqueInput, 23 | }) 24 | @ValidateNested() 25 | @Type(() => OrderWhereUniqueInput) 26 | @Field(() => OrderWhereUniqueInput, { nullable: false }) 27 | where!: OrderWhereUniqueInput; 28 | } 29 | 30 | export { OrderFindUniqueArgs as OrderFindUniqueArgs }; 31 | -------------------------------------------------------------------------------- /server/src/order/base/OrderWhereUniqueInput.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { InputType, Field } from "@nestjs/graphql"; 13 | import { ApiProperty } from "@nestjs/swagger"; 14 | import { IsString } from "class-validator"; 15 | 16 | @InputType() 17 | class OrderWhereUniqueInput { 18 | @ApiProperty({ 19 | required: true, 20 | type: String, 21 | }) 22 | @IsString() 23 | @Field(() => String) 24 | id!: string; 25 | } 26 | 27 | export { OrderWhereUniqueInput as OrderWhereUniqueInput }; 28 | -------------------------------------------------------------------------------- /server/src/order/base/ProductCreateNestedManyWithoutOrdersInput.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { InputType, Field } from "@nestjs/graphql"; 13 | import { ProductWhereUniqueInput } from "../../product/base/ProductWhereUniqueInput"; 14 | import { ApiProperty } from "@nestjs/swagger"; 15 | 16 | @InputType() 17 | class ProductCreateNestedManyWithoutOrdersInput { 18 | @Field(() => [ProductWhereUniqueInput], { 19 | nullable: true, 20 | }) 21 | @ApiProperty({ 22 | required: false, 23 | type: () => [ProductWhereUniqueInput], 24 | }) 25 | connect?: Array<ProductWhereUniqueInput>; 26 | } 27 | 28 | export { ProductCreateNestedManyWithoutOrdersInput as ProductCreateNestedManyWithoutOrdersInput }; 29 | -------------------------------------------------------------------------------- /server/src/order/base/order.module.base.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { Module } from "@nestjs/common"; 13 | import { MorganModule } from "nest-morgan"; 14 | import { ACLModule } from "../../auth/acl.module"; 15 | @Module({ 16 | imports: [ACLModule, MorganModule], 17 | exports: [ACLModule, MorganModule], 18 | }) 19 | export class OrderModuleBase {} 20 | -------------------------------------------------------------------------------- /server/src/order/order.controller.ts: -------------------------------------------------------------------------------- 1 | import * as common from "@nestjs/common"; 2 | import * as swagger from "@nestjs/swagger"; 3 | import * as nestAccessControl from "nest-access-control"; 4 | import { OrderService } from "./order.service"; 5 | import { OrderControllerBase } from "./base/order.controller.base"; 6 | 7 | @swagger.ApiTags("orders") 8 | @common.Controller("orders") 9 | export class OrderController extends OrderControllerBase { 10 | constructor( 11 | protected readonly service: OrderService, 12 | @nestAccessControl.InjectRolesBuilder() 13 | protected readonly rolesBuilder: nestAccessControl.RolesBuilder 14 | ) { 15 | super(service, rolesBuilder); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /server/src/order/order.module.ts: -------------------------------------------------------------------------------- 1 | import { Module, forwardRef } from "@nestjs/common"; 2 | import { AuthModule } from "../auth/auth.module"; 3 | import { OrderModuleBase } from "./base/order.module.base"; 4 | import { OrderService } from "./order.service"; 5 | import { OrderController } from "./order.controller"; 6 | import { OrderResolver } from "./order.resolver"; 7 | 8 | @Module({ 9 | imports: [OrderModuleBase, forwardRef(() => AuthModule)], 10 | controllers: [OrderController], 11 | providers: [OrderService, OrderResolver], 12 | exports: [OrderService], 13 | }) 14 | export class OrderModule {} 15 | -------------------------------------------------------------------------------- /server/src/order/order.resolver.ts: -------------------------------------------------------------------------------- 1 | import * as graphql from "@nestjs/graphql"; 2 | import * as nestAccessControl from "nest-access-control"; 3 | import * as gqlACGuard from "../auth/gqlAC.guard"; 4 | import { GqlDefaultAuthGuard } from "../auth/gqlDefaultAuth.guard"; 5 | import * as common from "@nestjs/common"; 6 | import { OrderResolverBase } from "./base/order.resolver.base"; 7 | import { Order } from "./base/Order"; 8 | import { OrderService } from "./order.service"; 9 | 10 | @common.UseGuards(GqlDefaultAuthGuard, gqlACGuard.GqlACGuard) 11 | @graphql.Resolver(() => Order) 12 | export class OrderResolver extends OrderResolverBase { 13 | constructor( 14 | protected readonly service: OrderService, 15 | @nestAccessControl.InjectRolesBuilder() 16 | protected readonly rolesBuilder: nestAccessControl.RolesBuilder 17 | ) { 18 | super(service, rolesBuilder); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /server/src/order/order.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from "@nestjs/common"; 2 | import { PrismaService } from "../prisma/prisma.service"; 3 | import { OrderServiceBase } from "./base/order.service.base"; 4 | 5 | @Injectable() 6 | export class OrderService extends OrderServiceBase { 7 | constructor(protected readonly prisma: PrismaService) { 8 | super(prisma); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /server/src/prisma.util.spec.ts: -------------------------------------------------------------------------------- 1 | import { 2 | isRecordNotFoundError, 3 | PRISMA_QUERY_INTERPRETATION_ERROR, 4 | } from "./prisma.util"; 5 | 6 | describe("isRecordNotFoundError", () => { 7 | test("returns true for record not found error", () => { 8 | expect( 9 | isRecordNotFoundError( 10 | Object.assign( 11 | new Error(`Error occurred during query execution: 12 | InterpretationError("Error for binding '0': RecordNotFound("Record to update not found.")")`), 13 | { 14 | code: PRISMA_QUERY_INTERPRETATION_ERROR, 15 | } 16 | ) 17 | ) 18 | ).toBe(true); 19 | }); 20 | test("returns false for any other error", () => { 21 | expect(isRecordNotFoundError(new Error())).toBe(false); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /server/src/prisma.util.ts: -------------------------------------------------------------------------------- 1 | export const PRISMA_QUERY_INTERPRETATION_ERROR = "P2016"; 2 | export const PRISMA_RECORD_NOT_FOUND = "RecordNotFound"; 3 | 4 | export function isRecordNotFoundError( 5 | error: Error & { code?: string } 6 | ): boolean { 7 | return ( 8 | "code" in error && 9 | error.code === PRISMA_QUERY_INTERPRETATION_ERROR && 10 | error.message.includes(PRISMA_RECORD_NOT_FOUND) 11 | ); 12 | } 13 | 14 | export async function transformStringFieldUpdateInput< 15 | T extends undefined | string | { set?: string } 16 | >(input: T, transform: (input: string) => Promise<string>): Promise<T> { 17 | if (typeof input === "object" && typeof input?.set === "string") { 18 | return { set: await transform(input.set) } as T; 19 | } 20 | if (typeof input === "object") { 21 | if (typeof input.set === "string") { 22 | return { set: await transform(input.set) } as T; 23 | } 24 | return input; 25 | } 26 | if (typeof input === "string") { 27 | return (await transform(input)) as T; 28 | } 29 | return input; 30 | } 31 | -------------------------------------------------------------------------------- /server/src/prisma/prisma.module.ts: -------------------------------------------------------------------------------- 1 | import { Global, Module } from "@nestjs/common"; 2 | import { PrismaService } from "./prisma.service"; 3 | 4 | @Global() 5 | @Module({ 6 | providers: [PrismaService], 7 | exports: [PrismaService], 8 | }) 9 | export class PrismaModule {} 10 | -------------------------------------------------------------------------------- /server/src/prisma/prisma.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, OnModuleInit, INestApplication } from "@nestjs/common"; 2 | import { PrismaClient } from "@prisma/client"; 3 | 4 | @Injectable() 5 | export class PrismaService extends PrismaClient implements OnModuleInit { 6 | async onModuleInit() { 7 | await this.$connect(); 8 | } 9 | 10 | async enableShutdownHooks(app: INestApplication) { 11 | this.$on("beforeExit", async () => { 12 | await app.close(); 13 | }); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /server/src/product/base/CreateProductArgs.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { ArgsType, Field } from "@nestjs/graphql"; 13 | import { ApiProperty } from "@nestjs/swagger"; 14 | import { ProductCreateInput } from "./ProductCreateInput"; 15 | import { ValidateNested } from "class-validator"; 16 | import { Type } from "class-transformer"; 17 | 18 | @ArgsType() 19 | class CreateProductArgs { 20 | @ApiProperty({ 21 | required: true, 22 | type: () => ProductCreateInput, 23 | }) 24 | @ValidateNested() 25 | @Type(() => ProductCreateInput) 26 | @Field(() => ProductCreateInput, { nullable: false }) 27 | data!: ProductCreateInput; 28 | } 29 | 30 | export { CreateProductArgs as CreateProductArgs }; 31 | -------------------------------------------------------------------------------- /server/src/product/base/DeleteProductArgs.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { ArgsType, Field } from "@nestjs/graphql"; 13 | import { ApiProperty } from "@nestjs/swagger"; 14 | import { ProductWhereUniqueInput } from "./ProductWhereUniqueInput"; 15 | import { ValidateNested } from "class-validator"; 16 | import { Type } from "class-transformer"; 17 | 18 | @ArgsType() 19 | class DeleteProductArgs { 20 | @ApiProperty({ 21 | required: true, 22 | type: () => ProductWhereUniqueInput, 23 | }) 24 | @ValidateNested() 25 | @Type(() => ProductWhereUniqueInput) 26 | @Field(() => ProductWhereUniqueInput, { nullable: false }) 27 | where!: ProductWhereUniqueInput; 28 | } 29 | 30 | export { DeleteProductArgs as DeleteProductArgs }; 31 | -------------------------------------------------------------------------------- /server/src/product/base/OrderCreateNestedManyWithoutProductsInput.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { InputType, Field } from "@nestjs/graphql"; 13 | import { OrderWhereUniqueInput } from "../../order/base/OrderWhereUniqueInput"; 14 | import { ApiProperty } from "@nestjs/swagger"; 15 | 16 | @InputType() 17 | class OrderCreateNestedManyWithoutProductsInput { 18 | @Field(() => [OrderWhereUniqueInput], { 19 | nullable: true, 20 | }) 21 | @ApiProperty({ 22 | required: false, 23 | type: () => [OrderWhereUniqueInput], 24 | }) 25 | connect?: Array<OrderWhereUniqueInput>; 26 | } 27 | 28 | export { OrderCreateNestedManyWithoutProductsInput as OrderCreateNestedManyWithoutProductsInput }; 29 | -------------------------------------------------------------------------------- /server/src/product/base/ProductCountArgs.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { ArgsType, Field } from "@nestjs/graphql"; 13 | import { ApiProperty } from "@nestjs/swagger"; 14 | import { ProductWhereInput } from "./ProductWhereInput"; 15 | import { Type } from "class-transformer"; 16 | 17 | @ArgsType() 18 | class ProductCountArgs { 19 | @ApiProperty({ 20 | required: false, 21 | type: () => ProductWhereInput, 22 | }) 23 | @Field(() => ProductWhereInput, { nullable: true }) 24 | @Type(() => ProductWhereInput) 25 | where?: ProductWhereInput; 26 | } 27 | 28 | export { ProductCountArgs as ProductCountArgs }; 29 | -------------------------------------------------------------------------------- /server/src/product/base/ProductFindUniqueArgs.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { ArgsType, Field } from "@nestjs/graphql"; 13 | import { ApiProperty } from "@nestjs/swagger"; 14 | import { ProductWhereUniqueInput } from "./ProductWhereUniqueInput"; 15 | import { ValidateNested } from "class-validator"; 16 | import { Type } from "class-transformer"; 17 | 18 | @ArgsType() 19 | class ProductFindUniqueArgs { 20 | @ApiProperty({ 21 | required: true, 22 | type: () => ProductWhereUniqueInput, 23 | }) 24 | @ValidateNested() 25 | @Type(() => ProductWhereUniqueInput) 26 | @Field(() => ProductWhereUniqueInput, { nullable: false }) 27 | where!: ProductWhereUniqueInput; 28 | } 29 | 30 | export { ProductFindUniqueArgs as ProductFindUniqueArgs }; 31 | -------------------------------------------------------------------------------- /server/src/product/base/ProductWhereUniqueInput.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { InputType, Field } from "@nestjs/graphql"; 13 | import { ApiProperty } from "@nestjs/swagger"; 14 | import { IsString } from "class-validator"; 15 | 16 | @InputType() 17 | class ProductWhereUniqueInput { 18 | @ApiProperty({ 19 | required: true, 20 | type: String, 21 | }) 22 | @IsString() 23 | @Field(() => String) 24 | id!: string; 25 | } 26 | 27 | export { ProductWhereUniqueInput as ProductWhereUniqueInput }; 28 | -------------------------------------------------------------------------------- /server/src/product/base/ReviewCreateNestedManyWithoutProductsInput.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { InputType, Field } from "@nestjs/graphql"; 13 | import { ReviewWhereUniqueInput } from "../../review/base/ReviewWhereUniqueInput"; 14 | import { ApiProperty } from "@nestjs/swagger"; 15 | 16 | @InputType() 17 | class ReviewCreateNestedManyWithoutProductsInput { 18 | @Field(() => [ReviewWhereUniqueInput], { 19 | nullable: true, 20 | }) 21 | @ApiProperty({ 22 | required: false, 23 | type: () => [ReviewWhereUniqueInput], 24 | }) 25 | connect?: Array<ReviewWhereUniqueInput>; 26 | } 27 | 28 | export { ReviewCreateNestedManyWithoutProductsInput as ReviewCreateNestedManyWithoutProductsInput }; 29 | -------------------------------------------------------------------------------- /server/src/product/base/product.module.base.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { Module } from "@nestjs/common"; 13 | import { MorganModule } from "nest-morgan"; 14 | import { ACLModule } from "../../auth/acl.module"; 15 | @Module({ 16 | imports: [ACLModule, MorganModule], 17 | exports: [ACLModule, MorganModule], 18 | }) 19 | export class ProductModuleBase {} 20 | -------------------------------------------------------------------------------- /server/src/product/product.controller.ts: -------------------------------------------------------------------------------- 1 | import * as common from "@nestjs/common"; 2 | import * as swagger from "@nestjs/swagger"; 3 | import * as nestAccessControl from "nest-access-control"; 4 | import { ProductService } from "./product.service"; 5 | import { ProductControllerBase } from "./base/product.controller.base"; 6 | 7 | @swagger.ApiTags("products") 8 | @common.Controller("products") 9 | export class ProductController extends ProductControllerBase { 10 | constructor( 11 | protected readonly service: ProductService, 12 | @nestAccessControl.InjectRolesBuilder() 13 | protected readonly rolesBuilder: nestAccessControl.RolesBuilder 14 | ) { 15 | super(service, rolesBuilder); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /server/src/product/product.module.ts: -------------------------------------------------------------------------------- 1 | import { Module, forwardRef } from "@nestjs/common"; 2 | import { AuthModule } from "../auth/auth.module"; 3 | import { ProductModuleBase } from "./base/product.module.base"; 4 | import { ProductService } from "./product.service"; 5 | import { ProductController } from "./product.controller"; 6 | import { ProductResolver } from "./product.resolver"; 7 | 8 | @Module({ 9 | imports: [ProductModuleBase, forwardRef(() => AuthModule)], 10 | controllers: [ProductController], 11 | providers: [ProductService, ProductResolver], 12 | exports: [ProductService], 13 | }) 14 | export class ProductModule {} 15 | -------------------------------------------------------------------------------- /server/src/product/product.resolver.ts: -------------------------------------------------------------------------------- 1 | import * as graphql from "@nestjs/graphql"; 2 | import * as nestAccessControl from "nest-access-control"; 3 | import * as gqlACGuard from "../auth/gqlAC.guard"; 4 | import { GqlDefaultAuthGuard } from "../auth/gqlDefaultAuth.guard"; 5 | import * as common from "@nestjs/common"; 6 | import { ProductResolverBase } from "./base/product.resolver.base"; 7 | import { Product } from "./base/Product"; 8 | import { ProductService } from "./product.service"; 9 | 10 | @common.UseGuards(GqlDefaultAuthGuard, gqlACGuard.GqlACGuard) 11 | @graphql.Resolver(() => Product) 12 | export class ProductResolver extends ProductResolverBase { 13 | constructor( 14 | protected readonly service: ProductService, 15 | @nestAccessControl.InjectRolesBuilder() 16 | protected readonly rolesBuilder: nestAccessControl.RolesBuilder 17 | ) { 18 | super(service, rolesBuilder); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /server/src/product/product.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from "@nestjs/common"; 2 | import { PrismaService } from "../prisma/prisma.service"; 3 | import { ProductServiceBase } from "./base/product.service.base"; 4 | 5 | @Injectable() 6 | export class ProductService extends ProductServiceBase { 7 | constructor(protected readonly prisma: PrismaService) { 8 | super(prisma); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /server/src/providers/secrets/base/secretsManager.service.base.ts: -------------------------------------------------------------------------------- 1 | import { ConfigService } from "@nestjs/config"; 2 | 3 | export interface ISecretsManager { 4 | getSecret: (key: string) => Promise<any | null>; 5 | } 6 | 7 | export class SecretsManagerServiceBase implements ISecretsManager { 8 | constructor(protected readonly configService: ConfigService) {} 9 | async getSecret<T>(key: string): Promise<T | null> { 10 | if (!key) { 11 | throw new Error("Didn't got the key"); 12 | } 13 | const value = this.configService.get(key); 14 | if (value) { 15 | return value; 16 | } 17 | return null; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /server/src/providers/secrets/secretsManager.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from "@nestjs/common"; 2 | import { SecretsManagerService } from "./secretsManager.service"; 3 | 4 | @Module({ 5 | providers: [SecretsManagerService], 6 | exports: [SecretsManagerService], 7 | }) 8 | export class SecretsManagerModule {} 9 | -------------------------------------------------------------------------------- /server/src/providers/secrets/secretsManager.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from "@nestjs/common"; 2 | import { ConfigService } from "@nestjs/config"; 3 | import { SecretsManagerServiceBase } from "./base/secretsManager.service.base"; 4 | 5 | @Injectable() 6 | export class SecretsManagerService extends SecretsManagerServiceBase { 7 | constructor(protected readonly configService: ConfigService) { 8 | super(configService); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /server/src/review/base/CreateReviewArgs.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { ArgsType, Field } from "@nestjs/graphql"; 13 | import { ApiProperty } from "@nestjs/swagger"; 14 | import { ReviewCreateInput } from "./ReviewCreateInput"; 15 | import { ValidateNested } from "class-validator"; 16 | import { Type } from "class-transformer"; 17 | 18 | @ArgsType() 19 | class CreateReviewArgs { 20 | @ApiProperty({ 21 | required: true, 22 | type: () => ReviewCreateInput, 23 | }) 24 | @ValidateNested() 25 | @Type(() => ReviewCreateInput) 26 | @Field(() => ReviewCreateInput, { nullable: false }) 27 | data!: ReviewCreateInput; 28 | } 29 | 30 | export { CreateReviewArgs as CreateReviewArgs }; 31 | -------------------------------------------------------------------------------- /server/src/review/base/DeleteReviewArgs.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { ArgsType, Field } from "@nestjs/graphql"; 13 | import { ApiProperty } from "@nestjs/swagger"; 14 | import { ReviewWhereUniqueInput } from "./ReviewWhereUniqueInput"; 15 | import { ValidateNested } from "class-validator"; 16 | import { Type } from "class-transformer"; 17 | 18 | @ArgsType() 19 | class DeleteReviewArgs { 20 | @ApiProperty({ 21 | required: true, 22 | type: () => ReviewWhereUniqueInput, 23 | }) 24 | @ValidateNested() 25 | @Type(() => ReviewWhereUniqueInput) 26 | @Field(() => ReviewWhereUniqueInput, { nullable: false }) 27 | where!: ReviewWhereUniqueInput; 28 | } 29 | 30 | export { DeleteReviewArgs as DeleteReviewArgs }; 31 | -------------------------------------------------------------------------------- /server/src/review/base/ReviewCountArgs.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { ArgsType, Field } from "@nestjs/graphql"; 13 | import { ApiProperty } from "@nestjs/swagger"; 14 | import { ReviewWhereInput } from "./ReviewWhereInput"; 15 | import { Type } from "class-transformer"; 16 | 17 | @ArgsType() 18 | class ReviewCountArgs { 19 | @ApiProperty({ 20 | required: false, 21 | type: () => ReviewWhereInput, 22 | }) 23 | @Field(() => ReviewWhereInput, { nullable: true }) 24 | @Type(() => ReviewWhereInput) 25 | where?: ReviewWhereInput; 26 | } 27 | 28 | export { ReviewCountArgs as ReviewCountArgs }; 29 | -------------------------------------------------------------------------------- /server/src/review/base/ReviewFindUniqueArgs.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { ArgsType, Field } from "@nestjs/graphql"; 13 | import { ApiProperty } from "@nestjs/swagger"; 14 | import { ReviewWhereUniqueInput } from "./ReviewWhereUniqueInput"; 15 | import { ValidateNested } from "class-validator"; 16 | import { Type } from "class-transformer"; 17 | 18 | @ArgsType() 19 | class ReviewFindUniqueArgs { 20 | @ApiProperty({ 21 | required: true, 22 | type: () => ReviewWhereUniqueInput, 23 | }) 24 | @ValidateNested() 25 | @Type(() => ReviewWhereUniqueInput) 26 | @Field(() => ReviewWhereUniqueInput, { nullable: false }) 27 | where!: ReviewWhereUniqueInput; 28 | } 29 | 30 | export { ReviewFindUniqueArgs as ReviewFindUniqueArgs }; 31 | -------------------------------------------------------------------------------- /server/src/review/base/ReviewWhereUniqueInput.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { InputType, Field } from "@nestjs/graphql"; 13 | import { ApiProperty } from "@nestjs/swagger"; 14 | import { IsString } from "class-validator"; 15 | 16 | @InputType() 17 | class ReviewWhereUniqueInput { 18 | @ApiProperty({ 19 | required: true, 20 | type: String, 21 | }) 22 | @IsString() 23 | @Field(() => String) 24 | id!: string; 25 | } 26 | 27 | export { ReviewWhereUniqueInput as ReviewWhereUniqueInput }; 28 | -------------------------------------------------------------------------------- /server/src/review/base/review.module.base.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { Module } from "@nestjs/common"; 13 | import { MorganModule } from "nest-morgan"; 14 | import { ACLModule } from "../../auth/acl.module"; 15 | @Module({ 16 | imports: [ACLModule, MorganModule], 17 | exports: [ACLModule, MorganModule], 18 | }) 19 | export class ReviewModuleBase {} 20 | -------------------------------------------------------------------------------- /server/src/review/review.controller.ts: -------------------------------------------------------------------------------- 1 | import * as common from "@nestjs/common"; 2 | import * as swagger from "@nestjs/swagger"; 3 | import * as nestAccessControl from "nest-access-control"; 4 | import { ReviewService } from "./review.service"; 5 | import { ReviewControllerBase } from "./base/review.controller.base"; 6 | 7 | @swagger.ApiTags("reviews") 8 | @common.Controller("reviews") 9 | export class ReviewController extends ReviewControllerBase { 10 | constructor( 11 | protected readonly service: ReviewService, 12 | @nestAccessControl.InjectRolesBuilder() 13 | protected readonly rolesBuilder: nestAccessControl.RolesBuilder 14 | ) { 15 | super(service, rolesBuilder); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /server/src/review/review.module.ts: -------------------------------------------------------------------------------- 1 | import { Module, forwardRef } from "@nestjs/common"; 2 | import { AuthModule } from "../auth/auth.module"; 3 | import { ReviewModuleBase } from "./base/review.module.base"; 4 | import { ReviewService } from "./review.service"; 5 | import { ReviewController } from "./review.controller"; 6 | import { ReviewResolver } from "./review.resolver"; 7 | 8 | @Module({ 9 | imports: [ReviewModuleBase, forwardRef(() => AuthModule)], 10 | controllers: [ReviewController], 11 | providers: [ReviewService, ReviewResolver], 12 | exports: [ReviewService], 13 | }) 14 | export class ReviewModule {} 15 | -------------------------------------------------------------------------------- /server/src/review/review.resolver.ts: -------------------------------------------------------------------------------- 1 | import * as graphql from "@nestjs/graphql"; 2 | import * as nestAccessControl from "nest-access-control"; 3 | import * as gqlACGuard from "../auth/gqlAC.guard"; 4 | import { GqlDefaultAuthGuard } from "../auth/gqlDefaultAuth.guard"; 5 | import * as common from "@nestjs/common"; 6 | import { ReviewResolverBase } from "./base/review.resolver.base"; 7 | import { Review } from "./base/Review"; 8 | import { ReviewService } from "./review.service"; 9 | 10 | @common.UseGuards(GqlDefaultAuthGuard, gqlACGuard.GqlACGuard) 11 | @graphql.Resolver(() => Review) 12 | export class ReviewResolver extends ReviewResolverBase { 13 | constructor( 14 | protected readonly service: ReviewService, 15 | @nestAccessControl.InjectRolesBuilder() 16 | protected readonly rolesBuilder: nestAccessControl.RolesBuilder 17 | ) { 18 | super(service, rolesBuilder); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /server/src/review/review.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from "@nestjs/common"; 2 | import { PrismaService } from "../prisma/prisma.service"; 3 | import { ReviewServiceBase } from "./base/review.service.base"; 4 | 5 | @Injectable() 6 | export class ReviewService extends ReviewServiceBase { 7 | constructor(protected readonly prisma: PrismaService) { 8 | super(prisma); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /server/src/serveStaticOptions.service.ts: -------------------------------------------------------------------------------- 1 | import * as path from "path"; 2 | import { Injectable, Logger } from "@nestjs/common"; 3 | import { ConfigService } from "@nestjs/config"; 4 | import { 5 | ServeStaticModuleOptions, 6 | ServeStaticModuleOptionsFactory, 7 | } from "@nestjs/serve-static"; 8 | 9 | const SERVE_STATIC_ROOT_PATH_VAR = "SERVE_STATIC_ROOT_PATH"; 10 | const DEFAULT_STATIC_MODULE_OPTIONS_LIST: ServeStaticModuleOptions[] = [ 11 | { 12 | serveRoot: "/swagger", 13 | rootPath: path.join(__dirname, "swagger"), 14 | }, 15 | ]; 16 | 17 | @Injectable() 18 | export class ServeStaticOptionsService 19 | implements ServeStaticModuleOptionsFactory 20 | { 21 | private readonly logger = new Logger(ServeStaticOptionsService.name); 22 | 23 | constructor(private readonly configService: ConfigService) {} 24 | 25 | createLoggerOptions(): ServeStaticModuleOptions[] { 26 | const serveStaticRootPath = this.configService.get( 27 | SERVE_STATIC_ROOT_PATH_VAR 28 | ); 29 | if (serveStaticRootPath) { 30 | const resolvedPath = path.resolve(serveStaticRootPath); 31 | this.logger.log(`Serving static files from ${resolvedPath}`); 32 | return [ 33 | ...DEFAULT_STATIC_MODULE_OPTIONS_LIST, 34 | { rootPath: resolvedPath, exclude: ["/api*", "/graphql"] }, 35 | ]; 36 | } 37 | return DEFAULT_STATIC_MODULE_OPTIONS_LIST; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /server/src/swagger.ts: -------------------------------------------------------------------------------- 1 | import { DocumentBuilder, SwaggerCustomOptions } from "@nestjs/swagger"; 2 | 3 | export const swaggerPath = "api"; 4 | 5 | export const swaggerDocumentOptions = new DocumentBuilder() 6 | .setTitle("amazon-backend") 7 | .setDescription( 8 | '\n\n## Congratulations! Your service resource is ready.\n \nPlease note that all endpoints are secured with JWT Bearer authentication.\nBy default, your service resource comes with one user with the username "admin" and password "admin".\nLearn more in [our docs](https://docs.amplication.com)' 9 | ) 10 | .addBearerAuth() 11 | .build(); 12 | 13 | export const swaggerSetupOptions: SwaggerCustomOptions = { 14 | swaggerOptions: { 15 | persistAuthorization: true, 16 | }, 17 | customCssUrl: "../swagger/swagger.css", 18 | customfavIcon: "../swagger/favicon.png", 19 | customSiteTitle: "amazon-backend", 20 | }; 21 | -------------------------------------------------------------------------------- /server/src/swagger/favicon.png: -------------------------------------------------------------------------------- 1 | �PNG 2 |  3 | ��� IHDR��� ��� ���szz���4IDATXG��Kl]W�����}�L�����n�NR�IP%P)��U�� 2�$D�1c cF���PU��"!J�J�#����#%�[Ǿ���й��sI�(q�'G:���k�Oq�eM��P�����q ޏ�!%�$z��~��,-��A�/����L����/(�ɬ�|��o����zs��52r>�hn�KqGU�ݠ!)앴�ũ��%%~��Wk��}�H��@��<�M��� !^���������� TAi�� �##�/�|�ܶ��R� �tP�}�;Ph�n2~���� ����WE8(9`���1�J�M� 1��J6��~n�J=����,-td,��b؏�q�àA�����y ��B|Y�� ��&�͂��l� _6�M/�g�[��93q��L��cc�} W�nn�k�1R�0pDЇh5�/^�������?Ԕ�%;?_q>��ήkU�|��[K ])d�!�� ?d�/qP��~T�M��:g{�j�U��䮾�UZ�~�|Ǽl�{_��&��)���g C�vI����N2`����� y�����yI��ڶ��ԟ��J<�y�S��� �)@�{�� ���/J��xMo�[���_.�}�_AGm���v�)��0�Z���J�:��� ���$�0V;�|��])�d�J“rx��b��)x �SȃFMZ ����(��e�3�N��:{z��z%?Bv&�o#����K�Ǟ���쏁]2���-����ð^������G�wb J��!�o����^2� }�sg�~g�.��b�V��c������]�-���Vks��,[\������y'o�jyK��3s�Z�!k�yǖ+0>:����l#um=���J���z ��7/ 7���s�P��� ����*���'<���)��PP:�Ԯ��^���?�<���!= :*�ѱe�O=6�Qn��<=j��2-�%E��6�l�>�<���s}��� �L���o�6?������h��p����#�]��$b�B����5����:�/O&F\�m�����!���2�i٠ĽQ�1�����"Ձ�!��}@w�X�"2JE�N���� nC/Jkm��>O=�VS[�����R��Gz�M�~�ǐ;@%��~v���#��պٰ(���33m� ��Y5�P{���ń�.����R�X�q9Oա_�5l�L�7�XR��~�:�ԯ�'`��¶Ɩ���z��'H�sjޤp� 4 | q�j��8����F�� 5 | �ٰ\�W�a�%Qw� c8v�L77��lj��߯�Rm����;�R�)���D��F���:�kad \1, ������n�/U����IEND�B`� -------------------------------------------------------------------------------- /server/src/tests/auth/constants.ts: -------------------------------------------------------------------------------- 1 | import { Credentials } from "../../auth/Credentials"; 2 | import { UserInfo } from "../../auth/UserInfo"; 3 | 4 | export const VALID_ID = "1"; 5 | 6 | export const TEST_USER: UserInfo = { 7 | id: "cl7qmjh4h0000tothyjqapgj5", 8 | roles: ["User"], 9 | username: "ofek", 10 | }; 11 | export const SIGN_TOKEN = "SIGN_TOKEN"; 12 | export const VALID_CREDENTIALS: Credentials = { 13 | username: "Valid User", 14 | password: "Valid User Password", 15 | }; 16 | export const INVALID_CREDENTIALS: Credentials = { 17 | username: "Invalid User", 18 | password: "Invalid User Password", 19 | }; 20 | -------------------------------------------------------------------------------- /server/src/tests/auth/jwt/jwt.strategy.spec.ts: -------------------------------------------------------------------------------- 1 | import { UnauthorizedException } from "@nestjs/common"; 2 | import { mock } from "jest-mock-extended"; 3 | import { JwtStrategyBase } from "../../../auth/jwt/base/jwt.strategy.base"; 4 | import { TEST_USER } from "../constants"; 5 | import { UserService } from "../../../user/user.service"; 6 | describe("Testing the jwtStrategyBase.validate()", () => { 7 | const userService = mock<UserService>(); 8 | const jwtStrategy = new JwtStrategyBase(userService, "Secrete"); 9 | beforeEach(() => { 10 | userService.findOne.mockClear(); 11 | }); 12 | it("should throw UnauthorizedException where there is no user", async () => { 13 | //ARRANGE 14 | userService.findOne 15 | .calledWith({ where: { username: TEST_USER.username } }) 16 | .mockReturnValue(Promise.resolve(null)); 17 | //ACT 18 | const result = jwtStrategy.validate({ 19 | id: TEST_USER.id, 20 | username: TEST_USER.username, 21 | roles: TEST_USER.roles, 22 | }); 23 | //ASSERT 24 | return expect(result).rejects.toThrowError(UnauthorizedException); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /server/src/tests/health/health.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { mock } from "jest-mock-extended"; 2 | import { PrismaService } from "../../prisma/prisma.service"; 3 | import { HealthServiceBase } from "../../health/base/health.service.base"; 4 | 5 | describe("Testing the HealthServiceBase", () => { 6 | //ARRANGE 7 | let prismaService: PrismaService; 8 | let healthServiceBase: HealthServiceBase; 9 | 10 | describe("Testing the isDbReady function in HealthServiceBase class", () => { 11 | beforeEach(() => { 12 | prismaService = mock<PrismaService>(); 13 | healthServiceBase = new HealthServiceBase(prismaService); 14 | }); 15 | it("should return true if allow connection to db", async () => { 16 | //ARRANGE 17 | (prismaService.$queryRaw as jest.Mock).mockReturnValue( 18 | Promise.resolve(true) 19 | ); 20 | //ACT 21 | const response = await healthServiceBase.isDbReady(); 22 | //ASSERT 23 | expect(response).toBe(true); 24 | }); 25 | it("should return false if db is not available", async () => { 26 | //ARRANGE 27 | (prismaService.$queryRaw as jest.Mock).mockReturnValue( 28 | Promise.reject(false) 29 | ); 30 | //ACT 31 | const response = await healthServiceBase.isDbReady(); 32 | //ASSERT 33 | expect(response).toBe(false); 34 | }); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /server/src/types.ts: -------------------------------------------------------------------------------- 1 | import type { JsonValue } from "type-fest"; 2 | 3 | export type InputJsonValue = Omit<JsonValue, "null">; 4 | -------------------------------------------------------------------------------- /server/src/user/base/CreateUserArgs.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { ArgsType, Field } from "@nestjs/graphql"; 13 | import { ApiProperty } from "@nestjs/swagger"; 14 | import { UserCreateInput } from "./UserCreateInput"; 15 | import { ValidateNested } from "class-validator"; 16 | import { Type } from "class-transformer"; 17 | 18 | @ArgsType() 19 | class CreateUserArgs { 20 | @ApiProperty({ 21 | required: true, 22 | type: () => UserCreateInput, 23 | }) 24 | @ValidateNested() 25 | @Type(() => UserCreateInput) 26 | @Field(() => UserCreateInput, { nullable: false }) 27 | data!: UserCreateInput; 28 | } 29 | 30 | export { CreateUserArgs as CreateUserArgs }; 31 | -------------------------------------------------------------------------------- /server/src/user/base/DeleteUserArgs.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { ArgsType, Field } from "@nestjs/graphql"; 13 | import { ApiProperty } from "@nestjs/swagger"; 14 | import { UserWhereUniqueInput } from "./UserWhereUniqueInput"; 15 | import { ValidateNested } from "class-validator"; 16 | import { Type } from "class-transformer"; 17 | 18 | @ArgsType() 19 | class DeleteUserArgs { 20 | @ApiProperty({ 21 | required: true, 22 | type: () => UserWhereUniqueInput, 23 | }) 24 | @ValidateNested() 25 | @Type(() => UserWhereUniqueInput) 26 | @Field(() => UserWhereUniqueInput, { nullable: false }) 27 | where!: UserWhereUniqueInput; 28 | } 29 | 30 | export { DeleteUserArgs as DeleteUserArgs }; 31 | -------------------------------------------------------------------------------- /server/src/user/base/OrderCreateNestedManyWithoutUsersInput.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { InputType, Field } from "@nestjs/graphql"; 13 | import { OrderWhereUniqueInput } from "../../order/base/OrderWhereUniqueInput"; 14 | import { ApiProperty } from "@nestjs/swagger"; 15 | 16 | @InputType() 17 | class OrderCreateNestedManyWithoutUsersInput { 18 | @Field(() => [OrderWhereUniqueInput], { 19 | nullable: true, 20 | }) 21 | @ApiProperty({ 22 | required: false, 23 | type: () => [OrderWhereUniqueInput], 24 | }) 25 | connect?: Array<OrderWhereUniqueInput>; 26 | } 27 | 28 | export { OrderCreateNestedManyWithoutUsersInput as OrderCreateNestedManyWithoutUsersInput }; 29 | -------------------------------------------------------------------------------- /server/src/user/base/ReviewCreateNestedManyWithoutUsersInput.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { InputType, Field } from "@nestjs/graphql"; 13 | import { ReviewWhereUniqueInput } from "../../review/base/ReviewWhereUniqueInput"; 14 | import { ApiProperty } from "@nestjs/swagger"; 15 | 16 | @InputType() 17 | class ReviewCreateNestedManyWithoutUsersInput { 18 | @Field(() => [ReviewWhereUniqueInput], { 19 | nullable: true, 20 | }) 21 | @ApiProperty({ 22 | required: false, 23 | type: () => [ReviewWhereUniqueInput], 24 | }) 25 | connect?: Array<ReviewWhereUniqueInput>; 26 | } 27 | 28 | export { ReviewCreateNestedManyWithoutUsersInput as ReviewCreateNestedManyWithoutUsersInput }; 29 | -------------------------------------------------------------------------------- /server/src/user/base/UpdateUserArgs.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { ArgsType, Field } from "@nestjs/graphql"; 13 | import { ApiProperty } from "@nestjs/swagger"; 14 | import { UserWhereUniqueInput } from "./UserWhereUniqueInput"; 15 | import { ValidateNested } from "class-validator"; 16 | import { Type } from "class-transformer"; 17 | import { UserUpdateInput } from "./UserUpdateInput"; 18 | 19 | @ArgsType() 20 | class UpdateUserArgs { 21 | @ApiProperty({ 22 | required: true, 23 | type: () => UserWhereUniqueInput, 24 | }) 25 | @ValidateNested() 26 | @Type(() => UserWhereUniqueInput) 27 | @Field(() => UserWhereUniqueInput, { nullable: false }) 28 | where!: UserWhereUniqueInput; 29 | 30 | @ApiProperty({ 31 | required: true, 32 | type: () => UserUpdateInput, 33 | }) 34 | @ValidateNested() 35 | @Type(() => UserUpdateInput) 36 | @Field(() => UserUpdateInput, { nullable: false }) 37 | data!: UserUpdateInput; 38 | } 39 | 40 | export { UpdateUserArgs as UpdateUserArgs }; 41 | -------------------------------------------------------------------------------- /server/src/user/base/UserCountArgs.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { ArgsType, Field } from "@nestjs/graphql"; 13 | import { ApiProperty } from "@nestjs/swagger"; 14 | import { UserWhereInput } from "./UserWhereInput"; 15 | import { Type } from "class-transformer"; 16 | 17 | @ArgsType() 18 | class UserCountArgs { 19 | @ApiProperty({ 20 | required: false, 21 | type: () => UserWhereInput, 22 | }) 23 | @Field(() => UserWhereInput, { nullable: true }) 24 | @Type(() => UserWhereInput) 25 | where?: UserWhereInput; 26 | } 27 | 28 | export { UserCountArgs as UserCountArgs }; 29 | -------------------------------------------------------------------------------- /server/src/user/base/UserFindUniqueArgs.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { ArgsType, Field } from "@nestjs/graphql"; 13 | import { ApiProperty } from "@nestjs/swagger"; 14 | import { UserWhereUniqueInput } from "./UserWhereUniqueInput"; 15 | import { ValidateNested } from "class-validator"; 16 | import { Type } from "class-transformer"; 17 | 18 | @ArgsType() 19 | class UserFindUniqueArgs { 20 | @ApiProperty({ 21 | required: true, 22 | type: () => UserWhereUniqueInput, 23 | }) 24 | @ValidateNested() 25 | @Type(() => UserWhereUniqueInput) 26 | @Field(() => UserWhereUniqueInput, { nullable: false }) 27 | where!: UserWhereUniqueInput; 28 | } 29 | 30 | export { UserFindUniqueArgs as UserFindUniqueArgs }; 31 | -------------------------------------------------------------------------------- /server/src/user/base/UserWhereUniqueInput.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { InputType, Field } from "@nestjs/graphql"; 13 | import { ApiProperty } from "@nestjs/swagger"; 14 | import { IsString } from "class-validator"; 15 | 16 | @InputType() 17 | class UserWhereUniqueInput { 18 | @ApiProperty({ 19 | required: true, 20 | type: String, 21 | }) 22 | @IsString() 23 | @Field(() => String) 24 | id!: string; 25 | } 26 | 27 | export { UserWhereUniqueInput as UserWhereUniqueInput }; 28 | -------------------------------------------------------------------------------- /server/src/user/base/user.module.base.ts: -------------------------------------------------------------------------------- 1 | /* 2 | ------------------------------------------------------------------------------ 3 | This code was generated by Amplication. 4 | 5 | Changes to this file will be lost if the code is regenerated. 6 | 7 | There are other ways to to customize your code, see this doc to learn more 8 | https://docs.amplication.com/how-to/custom-code 9 | 10 | ------------------------------------------------------------------------------ 11 | */ 12 | import { Module } from "@nestjs/common"; 13 | import { MorganModule } from "nest-morgan"; 14 | import { ACLModule } from "../../auth/acl.module"; 15 | @Module({ 16 | imports: [ACLModule, MorganModule], 17 | exports: [ACLModule, MorganModule], 18 | }) 19 | export class UserModuleBase {} 20 | -------------------------------------------------------------------------------- /server/src/user/user.controller.ts: -------------------------------------------------------------------------------- 1 | import * as common from "@nestjs/common"; 2 | import * as swagger from "@nestjs/swagger"; 3 | import * as nestAccessControl from "nest-access-control"; 4 | import { UserService } from "./user.service"; 5 | import { UserControllerBase } from "./base/user.controller.base"; 6 | 7 | @swagger.ApiTags("users") 8 | @common.Controller("users") 9 | export class UserController extends UserControllerBase { 10 | constructor( 11 | protected readonly service: UserService, 12 | @nestAccessControl.InjectRolesBuilder() 13 | protected readonly rolesBuilder: nestAccessControl.RolesBuilder 14 | ) { 15 | super(service, rolesBuilder); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /server/src/user/user.module.ts: -------------------------------------------------------------------------------- 1 | import { Module, forwardRef } from "@nestjs/common"; 2 | import { AuthModule } from "../auth/auth.module"; 3 | import { UserModuleBase } from "./base/user.module.base"; 4 | import { UserService } from "./user.service"; 5 | import { UserController } from "./user.controller"; 6 | import { UserResolver } from "./user.resolver"; 7 | 8 | @Module({ 9 | imports: [UserModuleBase, forwardRef(() => AuthModule)], 10 | controllers: [UserController], 11 | providers: [UserService, UserResolver], 12 | exports: [UserService], 13 | }) 14 | export class UserModule {} 15 | -------------------------------------------------------------------------------- /server/src/user/user.resolver.ts: -------------------------------------------------------------------------------- 1 | import * as graphql from "@nestjs/graphql"; 2 | import * as nestAccessControl from "nest-access-control"; 3 | import * as gqlACGuard from "../auth/gqlAC.guard"; 4 | import { GqlDefaultAuthGuard } from "../auth/gqlDefaultAuth.guard"; 5 | import * as common from "@nestjs/common"; 6 | import { UserResolverBase } from "./base/user.resolver.base"; 7 | import { User } from "./base/User"; 8 | import { UserService } from "./user.service"; 9 | 10 | @common.UseGuards(GqlDefaultAuthGuard, gqlACGuard.GqlACGuard) 11 | @graphql.Resolver(() => User) 12 | export class UserResolver extends UserResolverBase { 13 | constructor( 14 | protected readonly service: UserService, 15 | @nestAccessControl.InjectRolesBuilder() 16 | protected readonly rolesBuilder: nestAccessControl.RolesBuilder 17 | ) { 18 | super(service, rolesBuilder); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /server/src/user/user.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from "@nestjs/common"; 2 | import { PrismaService } from "../prisma/prisma.service"; 3 | import { PasswordService } from "../auth/password.service"; 4 | import { UserServiceBase } from "./base/user.service.base"; 5 | 6 | @Injectable() 7 | export class UserService extends UserServiceBase { 8 | constructor( 9 | protected readonly prisma: PrismaService, 10 | protected readonly passwordService: PasswordService 11 | ) { 12 | super(prisma, passwordService); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /server/src/util/BooleanFilter.ts: -------------------------------------------------------------------------------- 1 | import { Field, InputType } from "@nestjs/graphql"; 2 | import { ApiProperty } from "@nestjs/swagger"; 3 | import { IsOptional } from "class-validator"; 4 | import { Type } from "class-transformer"; 5 | 6 | @InputType({ 7 | isAbstract: true, 8 | description: undefined, 9 | }) 10 | export class BooleanFilter { 11 | @ApiProperty({ 12 | required: false, 13 | type: Boolean, 14 | }) 15 | @IsOptional() 16 | @Field(() => Boolean, { 17 | nullable: true, 18 | }) 19 | @Type(() => Boolean) 20 | equals?: boolean; 21 | 22 | @ApiProperty({ 23 | required: false, 24 | type: Boolean, 25 | }) 26 | @IsOptional() 27 | @Field(() => Boolean, { 28 | nullable: true, 29 | }) 30 | @Type(() => Boolean) 31 | not?: boolean; 32 | } 33 | -------------------------------------------------------------------------------- /server/src/util/BooleanNullableFilter.ts: -------------------------------------------------------------------------------- 1 | import { Field, InputType } from "@nestjs/graphql"; 2 | import { ApiProperty } from "@nestjs/swagger"; 3 | import { IsOptional } from "class-validator"; 4 | import { Type } from "class-transformer"; 5 | @InputType({ 6 | isAbstract: true, 7 | description: undefined, 8 | }) 9 | export class BooleanNullableFilter { 10 | @ApiProperty({ 11 | required: false, 12 | type: Boolean, 13 | }) 14 | @IsOptional() 15 | @Field(() => Boolean, { 16 | nullable: true, 17 | }) 18 | @Type(() => Boolean) 19 | equals?: boolean | null; 20 | 21 | @ApiProperty({ 22 | required: false, 23 | type: Boolean, 24 | }) 25 | @IsOptional() 26 | @Field(() => Boolean, { 27 | nullable: true, 28 | }) 29 | @Type(() => Boolean) 30 | not?: boolean | null; 31 | } 32 | -------------------------------------------------------------------------------- /server/src/util/JsonFilter.ts: -------------------------------------------------------------------------------- 1 | import { Field, InputType } from "@nestjs/graphql"; 2 | import { ApiProperty } from "@nestjs/swagger"; 3 | import { IsOptional } from "class-validator"; 4 | import { GraphQLJSONObject } from "graphql-type-json"; 5 | import { InputJsonValue } from "../types"; 6 | 7 | @InputType({ 8 | isAbstract: true, 9 | description: undefined, 10 | }) 11 | export class JsonFilter { 12 | @ApiProperty({ 13 | required: false, 14 | type: GraphQLJSONObject, 15 | }) 16 | @IsOptional() 17 | @Field(() => GraphQLJSONObject, { 18 | nullable: true, 19 | }) 20 | equals?: InputJsonValue; 21 | 22 | @ApiProperty({ 23 | required: false, 24 | type: GraphQLJSONObject, 25 | }) 26 | @IsOptional() 27 | @Field(() => GraphQLJSONObject, { 28 | nullable: true, 29 | }) 30 | not?: InputJsonValue; 31 | } 32 | -------------------------------------------------------------------------------- /server/src/util/JsonNullableFilter.ts: -------------------------------------------------------------------------------- 1 | import type { JsonValue } from "type-fest"; 2 | import { Field, InputType } from "@nestjs/graphql"; 3 | import { ApiProperty } from "@nestjs/swagger"; 4 | import { IsOptional } from "class-validator"; 5 | import { GraphQLJSONObject } from "graphql-type-json"; 6 | 7 | @InputType({ 8 | isAbstract: true, 9 | description: undefined, 10 | }) 11 | export class JsonNullableFilter { 12 | @ApiProperty({ 13 | required: false, 14 | type: GraphQLJSONObject, 15 | }) 16 | @IsOptional() 17 | @Field(() => GraphQLJSONObject, { 18 | nullable: true, 19 | }) 20 | equals?: JsonValue; 21 | 22 | @ApiProperty({ 23 | required: false, 24 | type: GraphQLJSONObject, 25 | }) 26 | @IsOptional() 27 | @Field(() => GraphQLJSONObject, { 28 | nullable: true, 29 | }) 30 | not?: JsonValue; 31 | } 32 | -------------------------------------------------------------------------------- /server/src/util/MetaQueryPayload.ts: -------------------------------------------------------------------------------- 1 | import { ObjectType, Field } from "@nestjs/graphql"; 2 | import { ApiProperty } from "@nestjs/swagger"; 3 | 4 | @ObjectType() 5 | class MetaQueryPayload { 6 | @ApiProperty({ 7 | required: true, 8 | type: [Number], 9 | }) 10 | @Field(() => Number) 11 | count!: number; 12 | } 13 | export { MetaQueryPayload }; 14 | -------------------------------------------------------------------------------- /server/src/util/QueryMode.ts: -------------------------------------------------------------------------------- 1 | import { registerEnumType } from "@nestjs/graphql"; 2 | 3 | export enum QueryMode { 4 | Default = "default", 5 | Insensitive = "insensitive", 6 | } 7 | registerEnumType(QueryMode, { 8 | name: "QueryMode", 9 | description: undefined, 10 | }); 11 | -------------------------------------------------------------------------------- /server/src/util/SortOrder.ts: -------------------------------------------------------------------------------- 1 | import { registerEnumType } from "@nestjs/graphql"; 2 | 3 | export enum SortOrder { 4 | Asc = "asc", 5 | Desc = "desc", 6 | } 7 | registerEnumType(SortOrder, { 8 | name: "SortOrder", 9 | description: undefined, 10 | }); 11 | -------------------------------------------------------------------------------- /server/src/validators/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./is-json-value-validator"; 2 | -------------------------------------------------------------------------------- /server/src/validators/is-json-value-validator.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ValidationArguments, 3 | registerDecorator, 4 | ValidationOptions, 5 | } from "class-validator"; 6 | import isJSONValidator from "validator/lib/isJSON"; 7 | 8 | export function IsJSONValue(validationOptions?: ValidationOptions) { 9 | return function (object: Record<string, any>, propertyName: string) { 10 | registerDecorator({ 11 | name: "IsJSONValue", 12 | target: object.constructor, 13 | propertyName: propertyName, 14 | options: validationOptions, 15 | validator: { 16 | validate(value: any, args: ValidationArguments) { 17 | if (typeof value === "string") { 18 | return isJSONValidator(value); 19 | } 20 | 21 | return isJSONValidator(JSON.stringify(value)); 22 | }, 23 | defaultMessage(args: ValidationArguments): string { 24 | return `${args.property} must be a valid json`; 25 | }, 26 | }, 27 | }); 28 | }; 29 | } 30 | -------------------------------------------------------------------------------- /server/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "exclude": ["node_modules", "prisma", "test", "dist", "**/*spec.ts", "admin"] 4 | } 5 | -------------------------------------------------------------------------------- /server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": "./", 4 | "module": "commonjs", 5 | "declaration": false, 6 | "removeComments": true, 7 | "emitDecoratorMetadata": true, 8 | "experimentalDecorators": true, 9 | "target": "es2017", 10 | "lib": ["ES2020"], 11 | "sourceMap": true, 12 | "outDir": "./dist", 13 | "incremental": true, 14 | "esModuleInterop": true, 15 | "allowSyntheticDefaultImports": true, 16 | "resolveJsonModule": true, 17 | "skipLibCheck": true, 18 | "strict": true, 19 | "paths": { 20 | "@app/custom-validators": ["src/validators"] 21 | } 22 | }, 23 | "include": ["src"] 24 | } 25 | --------------------------------------------------------------------------------