;
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 |
}
20 | >
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
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 Page
;
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 |
12 |
13 |
14 |
24 |
25 |
26 |
27 |
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 |
20 |
21 |
22 |
23 |
24 |
25 |
31 |
32 |
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 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
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 |
7 |
8 |
9 | {title}
10 |
11 |
12 | {data}
13 |
14 |
15 |
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 Page
;
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 |
22 |
23 |
24 | {children}
25 |
26 |
27 |
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 |
11 |
12 | {children}
13 |
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 |
12 |
13 | {children}
14 |
15 |
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 |
8 |
9 |
10 |
11 |
12 |
{productData.title}
13 |
14 |
15 | Color:
16 | Titanium White
17 |
18 |
19 | Variant:
20 | {productData.variants[0]}
21 |
22 |
23 |
24 |
25 |
26 | ${productData.discountPrice}
27 |
28 |
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 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
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 AddReview
;
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 |
8 | {colors.map((color, index) => (
9 | - setSelected(index)}
16 | >
17 | ))}
18 |
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 |
8 |
9 | {images.map((image) => (
10 | - setSelected(image)}
14 | >
15 |
16 |
17 |
18 |
19 | ))}
20 |
21 |
22 |
27 |
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 ReviewBars
;
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 Reviews
;
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 |
29 | {images.map((image) => (
30 |
34 |
35 | {image.name}
36 |
37 | ))}
38 |
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 |
8 | {variants.map((variant, index) => (
9 | - setSelected(index)}
15 | >
16 | {variant}
17 |
18 | ))}
19 |
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 {children};
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 |
6 | {colors.map((color) => (
7 |
12 | ))}
13 |
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 |
7 |
5
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
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 |
26 |
27 | Payment successful. You are being redirected to the orders page.
28 |
29 | Please do not close the page.
30 |
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 |
8 |
gift cards
9 |
10 |
Let them choose the perfect gift
11 |
12 | Shop now
13 |
14 |
15 |
16 |
17 | {/*
*/}
18 |
19 |
26 |
27 | {/*
*/}
28 |
29 |
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 |
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 |
8 |
9 |
AMAZON EXCLUSIVE
10 |
FARZI
11 |
12 | Watch now
13 |
14 |
15 |
16 | Get Amazon Prime Video for just $20
17 |
18 |
19 |
20 |
21 |
22 |
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 = (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 = (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 = (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()((...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;
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;
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 {
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 {
17 | return this.authService.me(request.headers.authorization);
18 | }
19 | @Post("login")
20 | async login(@Body() body: Credentials): Promise {
21 | return this.authService.login(body);
22 | }
23 | @Post("signup")
24 | async signup(@Body() body: SignupCredentials): Promise {
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 {
17 | return this.authService.login(args.credentials);
18 | }
19 |
20 | @Mutation(() => UserInfo)
21 | async signup(@Args() args: SignupArgs): Promise {
22 | return this.authService.signup(args.credentials);
23 | }
24 | @Query(() => User)
25 | async me(@Context("req") request: Request): Promise {
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 {
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 {
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 | Observable {
16 | const isPublic = this.reflector.get(
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 extends ACGuard {
6 | async getUser(context: ExecutionContext): Promise {
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 {
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 => {
9 | const secret = await secretsService.getSecret(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;
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 {
9 | return response.status(HttpStatus.NO_CONTENT).send();
10 | }
11 | @Get("ready")
12 | async healthReady(@Res() response: Response): Promise> {
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 {
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;
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): Promise {
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;
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;
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;
5 | }
6 |
7 | export class SecretsManagerServiceBase implements ISecretsManager {
8 | constructor(protected readonly configService: ConfigService) {}
9 | async getSecret(key: string): Promise {
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�{_��&��)���gC�vI����N2`�����y�����yI��ڶ��ԟ��J<�y�S���
�)@�{�� ���/J��xMo�[���_.�}�_AGm���v�) �0�Z���J�:��� ���$�0V;�|��]) d�Jrx��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();
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();
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;
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;
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;
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, 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 |
--------------------------------------------------------------------------------