;
14 |
--------------------------------------------------------------------------------
/admin-ui/src/user/EnumRoles.ts:
--------------------------------------------------------------------------------
1 | export enum EnumRoles {
2 | AirbnbUser = "airbnbUser",
3 | User = "user",
4 | }
5 |
--------------------------------------------------------------------------------
/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 { List, Datagrid, ListProps, DateField, TextField } from "react-admin";
3 | import Pagination from "../Components/Pagination";
4 |
5 | export const UserList = (props: ListProps): React.ReactElement => {
6 | return (
7 |
}
13 | >
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | );
25 | };
26 |
--------------------------------------------------------------------------------
/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 || String(record.id);
7 | };
8 |
--------------------------------------------------------------------------------
/admin-ui/src/user/roles.ts:
--------------------------------------------------------------------------------
1 | export const ROLES = [
2 | {
3 | name: "airbnbUser",
4 | displayName: "airbnb-user",
5 | },
6 | {
7 | name: "user",
8 | displayName: "User",
9 | },
10 | ];
11 |
--------------------------------------------------------------------------------
/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/src/wishlist/WishlistCreate.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import {
3 | Create,
4 | SimpleForm,
5 | CreateProps,
6 | ReferenceInput,
7 | SelectInput,
8 | } from "react-admin";
9 | import { ListingTitle } from "../listing/ListingTitle";
10 | import { UserTitle } from "../user/UserTitle";
11 |
12 | export const WishlistCreate = (props: CreateProps): React.ReactElement => {
13 | return (
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | );
25 | };
26 |
--------------------------------------------------------------------------------
/admin-ui/src/wishlist/WishlistEdit.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import {
3 | Edit,
4 | SimpleForm,
5 | EditProps,
6 | ReferenceInput,
7 | SelectInput,
8 | } from "react-admin";
9 | import { ListingTitle } from "../listing/ListingTitle";
10 | import { UserTitle } from "../user/UserTitle";
11 |
12 | export const WishlistEdit = (props: EditProps): React.ReactElement => {
13 | return (
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | );
25 | };
26 |
--------------------------------------------------------------------------------
/admin-ui/src/wishlist/WishlistList.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import {
3 | List,
4 | Datagrid,
5 | ListProps,
6 | DateField,
7 | TextField,
8 | ReferenceField,
9 | } from "react-admin";
10 | import Pagination from "../Components/Pagination";
11 | import { LISTING_TITLE_FIELD } from "../listing/ListingTitle";
12 | import { USER_TITLE_FIELD } from "../user/UserTitle";
13 |
14 | export const WishlistList = (props: ListProps): React.ReactElement => {
15 | return (
16 |
}
22 | >
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | );
36 | };
37 |
--------------------------------------------------------------------------------
/admin-ui/src/wishlist/WishlistShow.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import {
3 | Show,
4 | SimpleShowLayout,
5 | ShowProps,
6 | DateField,
7 | TextField,
8 | ReferenceField,
9 | } from "react-admin";
10 | import { LISTING_TITLE_FIELD } from "../listing/ListingTitle";
11 | import { USER_TITLE_FIELD } from "../user/UserTitle";
12 |
13 | export const WishlistShow = (props: ShowProps): React.ReactElement => {
14 | return (
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | );
29 | };
30 |
--------------------------------------------------------------------------------
/admin-ui/src/wishlist/WishlistTitle.ts:
--------------------------------------------------------------------------------
1 | import { Wishlist as TWishlist } from "../api/wishlist/Wishlist";
2 |
3 | export const WISHLIST_TITLE_FIELD = "id";
4 |
5 | export const WishlistTitle = (record: TWishlist): string => {
6 | return record.id || String(record.id);
7 | };
8 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "airbnb-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 public run dev",
9 | "start:admin": "npm --prefix admin-ui start",
10 | "start:backend": "npm --prefix server start",
11 | "postinstall": "npm i --prefix public && npm i --prefix admin-ui && 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-airbnb-clone.git"
20 | },
21 | "author": "",
22 | "license": "ISC",
23 | "bugs": {
24 | "url": "https://github.com/koolkishan/nextjs-airbnb-clone/issues"
25 | },
26 | "homepage": "https://github.com/koolkishan/nextjs-airbnb-clone#readme",
27 | "dependencies": {
28 | "qs": "^6.11.2"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/public/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["next/babel", "next/core-web-vitals"]
3 | }
4 |
--------------------------------------------------------------------------------
/public/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # next.js
12 | /.next/
13 | /out/
14 |
15 | # production
16 | /build
17 |
18 | # misc
19 | .DS_Store
20 | *.pem
21 |
22 | # debug
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 |
27 | # local env files
28 | .env*.local
29 |
30 | # vercel
31 | .vercel
32 |
33 | # typescript
34 | *.tsbuildinfo
35 | next-env.d.ts
36 |
--------------------------------------------------------------------------------
/public/README.md:
--------------------------------------------------------------------------------
1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
2 |
3 | ## Getting Started
4 |
5 | First, run the development server:
6 |
7 | ```bash
8 | npm run dev
9 | # or
10 | yarn dev
11 | # or
12 | pnpm dev
13 | ```
14 |
15 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
16 |
17 | You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
18 |
19 | This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
20 |
21 | ## Learn More
22 |
23 | To learn more about Next.js, take a look at the following resources:
24 |
25 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
26 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
27 |
28 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
29 |
30 | ## Deploy on Vercel
31 |
32 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
33 |
34 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
35 |
--------------------------------------------------------------------------------
/public/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "paths": {
4 | "airbnb/*": ["./src/*"]
5 | }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/public/next.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {
3 | reactStrictMode: false,
4 | env: {
5 | NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME: "dctahvizk",
6 | },
7 | images: {
8 | domains: ["res.cloudinary.com"],
9 | },
10 | };
11 |
12 | module.exports = nextConfig;
13 |
--------------------------------------------------------------------------------
/public/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "public",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "@mapbox/mapbox-gl-geocoder": "^5.0.1",
13 | "@types/mapbox__mapbox-gl-geocoder": "^4.7.3",
14 | "@types/node": "20.3.1",
15 | "@types/react": "18.2.12",
16 | "@types/react-date-range": "^1.4.4",
17 | "@types/react-dom": "18.2.5",
18 | "ag-grid-community": "^30.0.3",
19 | "ag-grid-react": "^30.0.4",
20 | "autoprefixer": "10.4.14",
21 | "axios": "^1.4.0",
22 | "eslint": "8.43.0",
23 | "eslint-config-next": "13.4.6",
24 | "mapbox-gl": "^2.15.0",
25 | "next": "13.4.6",
26 | "next-cloudinary": "^4.13.0",
27 | "postcss": "8.4.24",
28 | "react": "18.2.0",
29 | "react-confetti": "^6.1.0",
30 | "react-date-range": "^1.4.0",
31 | "react-datepicker": "^4.15.0",
32 | "react-dom": "18.2.0",
33 | "react-icons": "^4.10.1",
34 | "react-map-gl": "^7.1.0",
35 | "tailwindcss": "3.3.2",
36 | "typescript": "5.1.3",
37 | "zustand": "^4.3.8"
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/public/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/public/public/empty-profile.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/koolkishan/nextjs-airbnb-clone/e5dafbecc58651ed033dcaf5d6f8de060a03117c/public/public/empty-profile.png
--------------------------------------------------------------------------------
/public/public/home.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/koolkishan/nextjs-airbnb-clone/e5dafbecc58651ed033dcaf5d6f8de060a03117c/public/public/home.mp4
--------------------------------------------------------------------------------
/public/public/home2.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/koolkishan/nextjs-airbnb-clone/e5dafbecc58651ed033dcaf5d6f8de060a03117c/public/public/home2.mp4
--------------------------------------------------------------------------------
/public/public/home3.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/koolkishan/nextjs-airbnb-clone/e5dafbecc58651ed033dcaf5d6f8de060a03117c/public/public/home3.mp4
--------------------------------------------------------------------------------
/public/public/overview1.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/koolkishan/nextjs-airbnb-clone/e5dafbecc58651ed033dcaf5d6f8de060a03117c/public/public/overview1.webp
--------------------------------------------------------------------------------
/public/public/overview2.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/koolkishan/nextjs-airbnb-clone/e5dafbecc58651ed033dcaf5d6f8de060a03117c/public/public/overview2.webp
--------------------------------------------------------------------------------
/public/public/overview3.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/koolkishan/nextjs-airbnb-clone/e5dafbecc58651ed033dcaf5d6f8de060a03117c/public/public/overview3.webp
--------------------------------------------------------------------------------
/public/src/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/koolkishan/nextjs-airbnb-clone/e5dafbecc58651ed033dcaf5d6f8de060a03117c/public/src/app/favicon.ico
--------------------------------------------------------------------------------
/public/src/app/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | /* Hide scrollbar for Chrome, Safari and Opera */
6 | .no-scrollbar::-webkit-scrollbar {
7 | display: none;
8 | }
9 |
10 | /* Hide scrollbar for IE, Edge and Firefox */
11 | .no-scrollbar {
12 | -ms-overflow-style: none; /* IE and Edge */
13 | scrollbar-width: none; /* Firefox */
14 | }
15 |
16 | .control-panel {
17 | position: absolute;
18 | top: 0;
19 | right: 0;
20 | max-width: 320px;
21 | background: #fff;
22 | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
23 | padding: 12px 24px;
24 | margin: 20px;
25 | font-size: 13px;
26 | line-height: 2;
27 | color: #6b6b76;
28 | text-transform: uppercase;
29 | outline: none;
30 | }
31 |
32 | .mapboxgl-popup-content {
33 | width: 18rem !important;
34 | }
35 |
36 | .mapboxgl-popup-close-button {
37 | font-size: 1rem !important;
38 | margin-right: 0.2rem;
39 | }
40 |
41 | .mapboxgl-popup {
42 | width: 18rem !important;
43 | }
44 |
45 | div.mapboxgl-popup.mapboxgl-popup-anchor-top {
46 | width: 18rem !important;
47 | max-width: 18rem !important;
48 | font-size: 1rem;
49 | }
50 |
--------------------------------------------------------------------------------
/public/src/app/layout.jsx:
--------------------------------------------------------------------------------
1 | import NavigationEvents from "airbnb/components/common/NavigationEvents";
2 | import "./globals.css";
3 | import "mapbox-gl/dist/mapbox-gl.css";
4 |
5 | import { Inter } from "next/font/google";
6 | import { useRouter } from "next/navigation";
7 | import { Suspense } from "react";
8 | const inter = Inter({ subsets: ["latin"] });
9 |
10 | export const metadata = {
11 | title: "Airbnb Clone",
12 | description: "Created by Kishan Sheth",
13 | };
14 |
15 | export default function RootLayout({ children }) {
16 | return (
17 |
18 |
23 |
29 |
30 | {children}
31 |
32 |
33 |
34 |
35 |
36 | );
37 | }
38 |
--------------------------------------------------------------------------------
/public/src/app/listing/[listing]/components/ListingAmenties.jsx:
--------------------------------------------------------------------------------
1 | import { AmenetiesType } from "airbnb/data/Amenities";
2 | import { userAppStore } from "airbnb/store/store";
3 | import React from "react";
4 |
5 | export default function ListingAmenties() {
6 | const { currentListing } = userAppStore();
7 | function getSvgPathByName(name) {
8 | for (const amenity of AmenetiesType) {
9 | for (const data of amenity.data) {
10 | if (data.name === name) {
11 | return data.svgPath;
12 | }
13 | }
14 | }
15 | return null;
16 | }
17 |
18 | return (
19 |
20 |
Amenties
21 |
22 | {currentListing.placeAmeneites.map((amenity) => (
23 |
27 | {getSvgPathByName(amenity)}
28 | {amenity}
29 |
30 | ))}
31 |
32 |
33 | );
34 | }
35 |
--------------------------------------------------------------------------------
/public/src/app/listing/[listing]/components/ListingMap.jsx:
--------------------------------------------------------------------------------
1 | import Pin from "airbnb/components/common/Pin";
2 | import { userAppStore } from "airbnb/store/store";
3 | import React, { useMemo } from "react";
4 | import Map, { Marker } from "react-map-gl";
5 |
6 | const TOKEN =
7 | "pk.eyJ1Ijoia29vbGtpc2hhbiIsImEiOiJjazV3Zm41cG8wa3I1M2tydnVkcW53b2ZpIn0.mYrXogbdTrWSoJECNR1epg";
8 |
9 | export default function ListingMap() {
10 | const { currentListing } = userAppStore();
11 | const pins = useMemo(() => {
12 | return (
13 |
17 |
18 |
19 | );
20 | }, [currentListing]);
21 | return (
22 |
23 |
36 | {pins}
37 |
38 |
39 | );
40 | }
41 |
--------------------------------------------------------------------------------
/public/src/app/listing/[listing]/components/ListingPhotos.jsx:
--------------------------------------------------------------------------------
1 | import { userAppStore } from "airbnb/store/store";
2 | import Image from "next/image";
3 | import React, { useState } from "react";
4 |
5 | export default function ListingPhotos() {
6 | const { currentListing } = userAppStore();
7 | const [currentPhoto, setCurrentPhoto] = useState(0);
8 | return (
9 |
10 |
11 |
12 |
13 | {currentListing.photos.length > 1 && (
14 |
15 | {currentListing.photos.map((photo, index) => (
16 | setCurrentPhoto(index)}
20 | >
21 |
22 |
23 | ))}
24 |
25 | )}
26 |
27 | );
28 | }
29 |
--------------------------------------------------------------------------------
/public/src/app/listing/[listing]/loading.jsx:
--------------------------------------------------------------------------------
1 | import Spinner from "airbnb/components/common/Spinner";
2 | import React from "react";
3 |
4 | export default function loading() {
5 | return ;
6 | }
7 |
--------------------------------------------------------------------------------
/public/src/app/loading.jsx:
--------------------------------------------------------------------------------
1 | import Spinner from "airbnb/components/common/Spinner";
2 | import React from "react";
3 |
4 | export default function loading() {
5 | return ;
6 | }
7 |
--------------------------------------------------------------------------------
/public/src/app/my-listings/loading.jsx:
--------------------------------------------------------------------------------
1 | import Spinner from "airbnb/components/common/Spinner";
2 | import React from "react";
3 |
4 | export default function loading() {
5 | return ;
6 | }
7 |
--------------------------------------------------------------------------------
/public/src/app/new-listing/loading.jsx:
--------------------------------------------------------------------------------
1 | import Spinner from "airbnb/components/common/Spinner";
2 | import React from "react";
3 |
4 | export default function loading() {
5 | return ;
6 | }
7 |
--------------------------------------------------------------------------------
/public/src/app/search/loading.jsx:
--------------------------------------------------------------------------------
1 | import Spinner from "airbnb/components/common/Spinner";
2 | import React from "react";
3 |
4 | export default function loading() {
5 | return ;
6 | }
7 |
--------------------------------------------------------------------------------
/public/src/components/SearchScheduler/SearchDates.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | export default function SearchDates() {
4 | const [state, setState] = useState([
5 | {
6 | startDate: new Date(),
7 | endDate: addDays(new Date(), 7),
8 | key: "selection",
9 | },
10 | ]);
11 |
12 | function formatDate(dateString) {
13 | const options = { month: "long", day: "numeric" };
14 | const date = new Date(dateString);
15 | return date.toLocaleDateString("en-US", options);
16 | }
17 |
18 | return (
19 | <>
20 |
21 | Check in
22 |
23 | {formatDate(state[0]?.startDate)}
24 | {selected === "check-in" && (
25 |
26 |
27 |
28 | )}
29 | >
30 | );
31 | }
32 |
--------------------------------------------------------------------------------
/public/src/components/common/Calender.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import { addDays } from "date-fns";
3 | import { DateRangePicker } from "react-date-range";
4 | import "react-date-range/dist/styles.css"; // main css file
5 | import "react-date-range/dist/theme/default.css"; // theme css file
6 | export default function Calender({ state, setState }) {
7 | return (
8 |
9 | setState([item.selection])}
11 | showSelectionPreview={true}
12 | moveRangeOnFirstSelection={false}
13 | months={2}
14 | ranges={state}
15 | direction="horizontal"
16 | />
17 |
18 | );
19 | }
20 |
--------------------------------------------------------------------------------
/public/src/components/common/FormInput.jsx:
--------------------------------------------------------------------------------
1 | import React, { Dispatch, SetStateAction } from "react";
2 |
3 | export default function FormInput({
4 | name,
5 | type = "text",
6 | value,
7 | setValue,
8 | placeholder,
9 | isListing = false,
10 | }) {
11 | return (
12 |
17 | isListing ? setValue(name, e.target.value) : setValue(e.target.value)
18 | }
19 | placeholder={placeholder}
20 | className="border border-gray-300 px-2 py-4 rounded-md w-full"
21 | />
22 | );
23 | }
24 |
--------------------------------------------------------------------------------
/public/src/components/common/NavigationEvents.jsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import { me } from "airbnb/lib/auth";
3 | import { userAppStore } from "airbnb/store/store";
4 | import { usePathname } from "next/navigation";
5 | import { useEffect } from "react";
6 |
7 | export default function NavigationEvents() {
8 | const pathName = usePathname();
9 | const {
10 | setInitialView,
11 | setCurrentListing,
12 | setShowScheduleBar,
13 | showScheduleBar,
14 | setUserInfo,
15 | userInfo,
16 | } = userAppStore();
17 | useEffect(() => {
18 | setInitialView();
19 | setCurrentListing(undefined);
20 | if (!userInfo) {
21 | const getData = async () => {
22 | const data = await me();
23 | setUserInfo(data);
24 | };
25 | getData();
26 | }
27 | // if ( showScheduleBar) setShowScheduleBar();
28 | }, [
29 | pathName,
30 | setInitialView,
31 | showScheduleBar,
32 | setShowScheduleBar,
33 | setCurrentListing,
34 | setUserInfo,
35 | userInfo,
36 | ]);
37 | return null;
38 | }
39 |
--------------------------------------------------------------------------------
/public/src/components/common/Pin.jsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 |
3 | const ICON = `M20.2,15.7L20.2,15.7c1.1-1.6,1.8-3.6,1.8-5.7c0-5.6-4.5-10-10-10S2,4.5,2,10c0,2,0.6,3.9,1.6,5.4c0,0.1,0.1,0.2,0.2,0.3
4 | c0,0,0.1,0.1,0.1,0.2c0.2,0.3,0.4,0.6,0.7,0.9c2.6,3.1,7.4,7.6,7.4,7.6s4.8-4.5,7.4-7.5c0.2-0.3,0.5-0.6,0.7-0.9
5 | C20.1,15.8,20.2,15.8,20.2,15.7z`;
6 |
7 | const pinStyle = {
8 | cursor: "pointer",
9 | fill: "#d00",
10 | stroke: "none",
11 | };
12 |
13 | function Pin({ size = 20 }) {
14 | return (
15 |
16 |
17 |
18 | );
19 | }
20 |
21 | export default React.memo(Pin);
22 |
--------------------------------------------------------------------------------
/public/src/components/common/ScheduleBar.jsx:
--------------------------------------------------------------------------------
1 | import { userAppStore } from "airbnb/store/store";
2 | import React from "react";
3 | import { HiOutlineSearch } from "react-icons/hi";
4 | export default function ScheduleBar() {
5 | const { setShowScheduleBar } = userAppStore();
6 | return (
7 |
11 |
12 | Anywhere
13 | Any week
14 | Add guests
15 |
16 |
17 |
18 |
19 |
20 | );
21 | }
22 |
--------------------------------------------------------------------------------
/public/src/components/common/Spinner.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | export default function Spinner() {
4 | return (
5 |
6 |
13 |
17 |
21 |
22 |
Loading...
23 |
24 | );
25 | }
26 |
--------------------------------------------------------------------------------
/public/src/components/footer/CompactFooter.jsx:
--------------------------------------------------------------------------------
1 | import Link from "next/link";
2 | import React from "react";
3 | import { PiCaretUpBold } from "react-icons/pi";
4 | import { FiGlobe } from "react-icons/fi";
5 |
6 | export default function CompactFooter() {
7 | const links = [
8 | "privacy",
9 | "terms",
10 | "sitemap",
11 | "company details",
12 | "destinations",
13 | ];
14 | return (
15 |
16 |
17 | © {new Date().getFullYear()} Airbnb, Inc
18 | {links.map((link) => (
19 |
20 |
21 | {link.charAt(0).toUpperCase() + link.slice(1).toLowerCase()}
22 |
23 |
24 | ))}
25 |
26 |
27 |
28 | English (IN)
29 |
30 | $ USD
31 |
32 | Support & resources
33 |
34 |
35 |
36 | );
37 | }
38 |
--------------------------------------------------------------------------------
/public/src/components/footer/Footer.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | export default function Footer() {
4 | return Footer
;
5 | }
6 |
--------------------------------------------------------------------------------
/public/src/components/process/Description.jsx:
--------------------------------------------------------------------------------
1 | import { userAppStore } from "airbnb/store/store";
2 | import React from "react";
3 |
4 | export default function Description() {
5 | const { description, setDescription } = userAppStore();
6 | return (
7 |
8 |
9 |
10 |
Create your description
11 |
Share what makes your place special.
12 |
13 |
14 |
25 |
26 |
27 | );
28 | }
29 |
--------------------------------------------------------------------------------
/public/src/components/process/FinishSetup.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | export default function FinishSetup() {
4 | return FinishSetup
;
5 | }
6 |
--------------------------------------------------------------------------------
/public/src/components/process/ListingTypeSelector.jsx:
--------------------------------------------------------------------------------
1 | import { listingTypes } from "airbnb/data/listingTypes";
2 | import { userAppStore } from "airbnb/store/store";
3 | import Image from "next/image";
4 | import React, { useState } from "react";
5 |
6 | export default function ListingTypeSelector() {
7 | const { locationType, setLocationType } = userAppStore();
8 | const handleSelection = (type) => {
9 | setLocationType(type);
10 | };
11 |
12 | return (
13 |
14 |
15 |
16 | Which of these best describes your place?
17 |
18 |
19 | {listingTypes.map((type) => (
20 | handleSelection(type.name)}
26 | >
27 | {type.svgPath}
28 | {type.name}
29 |
30 | ))}
31 |
32 |
33 |
34 | );
35 | }
36 |
--------------------------------------------------------------------------------
/public/src/components/process/Photos.jsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import React, { useState } from "react";
3 | import ImageUpload from "../common/ImageUpload";
4 | import { userAppStore } from "airbnb/store/store";
5 | import { CldUploadButton } from "next-cloudinary";
6 | import Image from "next/image";
7 |
8 | export default function Photos() {
9 | const { photos, setPhotos } = userAppStore();
10 | const handleUpload = (data) => {
11 | setPhotos([...photos, data.info.secure_url]);
12 | };
13 | return (
14 |
15 |
Add some photos of your house
16 |
17 | You'll need 5 photos to get started. You can add more or make changes
18 | later.
19 |
20 |
25 |
26 | Upload
27 |
28 |
29 |
30 |
31 | {photos.map((photo) => (
32 |
33 |
34 |
35 | ))}
36 |
37 |
38 | );
39 | }
40 |
--------------------------------------------------------------------------------
/public/src/components/process/Price.jsx:
--------------------------------------------------------------------------------
1 | import { userAppStore } from "airbnb/store/store";
2 | import React from "react";
3 |
4 | export default function Price() {
5 | const { price, setPrice } = userAppStore();
6 | return (
7 |
8 |
9 |
10 |
Now, set your price
11 |
You can change it anytime.
12 |
13 |
14 |
15 | $
16 |
17 |
27 |
28 |
29 | );
30 | }
31 |
--------------------------------------------------------------------------------
/public/src/components/process/Review.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | export default function Review() {
4 | return Review
;
5 | }
6 |
--------------------------------------------------------------------------------
/public/src/components/process/StepOneStarter.jsx:
--------------------------------------------------------------------------------
1 | import Image from "next/image";
2 | import React from "react";
3 |
4 | export default function StepOneStarter() {
5 | return (
6 |
7 |
8 |
9 |
Step 1
10 |
11 | Tel us about your place
12 |
13 |
14 | In this step, we'll ask you which type of property you have and if
15 | guests will book the entire place or just a room. Then let us know
16 | the location and how many guests can stay.
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | );
25 | }
26 |
--------------------------------------------------------------------------------
/public/src/components/process/StepThreeStarter.jsx:
--------------------------------------------------------------------------------
1 | import Image from "next/image";
2 | import React from "react";
3 |
4 | export default function StepThreeStarter() {
5 | return (
6 |
7 |
8 |
9 |
Step 3
10 |
11 | Finish up and publish
12 |
13 |
14 | Finally, you’ll choose if you'd like to start with an experienced
15 | guest, then you'll set your nightly price. Answer a few quick
16 | questions and publish when you're ready.
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | );
25 | }
26 |
--------------------------------------------------------------------------------
/public/src/components/process/StepTwoStarter.jsx:
--------------------------------------------------------------------------------
1 | import Image from "next/image";
2 | import React from "react";
3 |
4 | export default function StepTwoStarter() {
5 | return (
6 |
7 |
8 |
9 |
Step 2
10 |
11 | Make your place stand out
12 |
13 |
14 | In this step, you’ll add some of the amenities your place offers,
15 | plus 5 or more photos. Then you’ll create a title and description.
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | );
24 | }
25 |
--------------------------------------------------------------------------------
/public/src/components/process/Title.jsx:
--------------------------------------------------------------------------------
1 | import { userAppStore } from "airbnb/store/store";
2 | import React from "react";
3 |
4 | export default function Title() {
5 | const { title, setTitle } = userAppStore();
6 | return (
7 |
8 |
9 |
10 | Now, let's give your house a title
11 |
12 |
13 | Short titles work best. Have fun with it – you can always change it
14 | later.
15 |
16 |
17 |
18 |
29 |
30 | );
31 | }
32 |
--------------------------------------------------------------------------------
/public/src/components/views/ListView.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ListingCard from "../listingCard";
3 | import { userAppStore } from "airbnb/store/store";
4 |
5 | export default function ListView() {
6 | const { listings } = userAppStore();
7 | return (
8 |
9 | {listings?.map((listing, index) => (
10 |
11 | ))}
12 |
13 | );
14 | }
15 |
--------------------------------------------------------------------------------
/public/src/components/views/ViewSwitchBadge.jsx:
--------------------------------------------------------------------------------
1 | "use client";
2 | import React from "react";
3 | import { BsFillMapFill } from "react-icons/bs";
4 | import { AiOutlineUnorderedList } from "react-icons/ai";
5 | import { userAppStore } from "airbnb/store/store";
6 |
7 | function ViewSwitchBadge() {
8 | const { isMapView, setMapView } = userAppStore();
9 |
10 | return (
11 |
12 |
13 |
setMapView()}
16 | >
17 | {!isMapView ? (
18 | <>
19 | Show Map
20 | >
21 | ) : (
22 | <>
23 | Show List
24 | >
25 | )}
26 |
27 |
28 |
29 | );
30 | }
31 |
32 | export default ViewSwitchBadge;
33 |
--------------------------------------------------------------------------------
/public/src/hooks/useClickOutside.jsx:
--------------------------------------------------------------------------------
1 | import { userAppStore } from "airbnb/store/store";
2 | import { useEffect, RefObject, useRef } from "react";
3 |
4 | function useClickOutside(isScheduleBar = false) {
5 | const { setSelectionType, setShowScheduleBar } = userAppStore();
6 | const containerRef = useRef(null);
7 | useEffect(() => {
8 | function handleClickOutside(event) {
9 | if (
10 | containerRef.current &&
11 | !containerRef.current.contains(event.target)
12 | ) {
13 | if (!isScheduleBar) {
14 | setSelectionType(undefined);
15 | } else {
16 | setShowScheduleBar();
17 | }
18 | }
19 | }
20 |
21 | document.addEventListener("mousedown", handleClickOutside);
22 |
23 | return () => {
24 | document.removeEventListener("mousedown", handleClickOutside);
25 | };
26 | }, [containerRef, setSelectionType]);
27 | return [containerRef];
28 | }
29 |
30 | export default useClickOutside;
31 |
--------------------------------------------------------------------------------
/public/src/lib/auth.js:
--------------------------------------------------------------------------------
1 | import { createUrl, get, isStoredJwt, post, setStoredJwt } from "./http";
2 |
3 | export const me = async () => {
4 | return isStoredJwt()
5 | ? (await get(createUrl("/api/me")).catch(() => null))?.data
6 | : null;
7 | };
8 |
9 | export const login = async (username, password) => {
10 | const result = (
11 | await post(createUrl("/api/login"), { username, password }).catch(
12 | () => null
13 | )
14 | )?.data;
15 |
16 | if (!result) {
17 | return alert("Could not login");
18 | }
19 | setStoredJwt(result.accessToken);
20 | return me();
21 | };
22 |
23 | export const signup = async (username, password, firstName, lastName) => {
24 | const result = (
25 | await post(createUrl("/api/signup"), {
26 | username,
27 | password,
28 | firstName,
29 | lastName,
30 | }).catch(() => null)
31 | )?.data;
32 |
33 | if (!result) {
34 | return alert("Could not sign up");
35 | }
36 | setStoredJwt(result.accessToken);
37 | return me();
38 | };
39 |
40 | export const checkUser = async (email) => {
41 | const result = (
42 | await post(createUrl("/api/check-user"), { email }).catch(() => null)
43 | )?.data;
44 | if (!result) return false;
45 | return true;
46 | };
47 |
--------------------------------------------------------------------------------
/public/src/lib/http.js:
--------------------------------------------------------------------------------
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);
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) => new URL(endpoint, apiUrl).href;
22 | export const isStoredJwt = () => Boolean(localStorage.getItem(jwtKey));
23 | export const setStoredJwt = (accessToken) =>
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 |
--------------------------------------------------------------------------------
/public/src/store/slices/AuthSlice.js:
--------------------------------------------------------------------------------
1 | export const createAuthSlice = (set, get) => ({
2 | isAuthModalOpen: false,
3 | isLoggedIn: false,
4 | userInfo: null,
5 | setAuthModal: () => {
6 | set({ isAuthModalOpen: !get().isAuthModalOpen });
7 | },
8 | setIsLoggedIn: (status) => {
9 | set({ isLoggedIn: status });
10 | },
11 | setUserInfo: (userInfo) => {
12 | set({ userInfo });
13 | },
14 | });
15 |
--------------------------------------------------------------------------------
/public/src/store/slices/ListingsSlice.js:
--------------------------------------------------------------------------------
1 | export const createLisitingsSlice = (set, get) => ({
2 | wishLists: [],
3 | setWishLists: (wishLists) => set({ wishLists }),
4 | wishListsPage: [],
5 | setWishListsPage: (wishListsPage) => set({ wishListsPage }),
6 | addToWishListSlice: (id) => {
7 | const lists = get().wishLists;
8 | lists.push(id);
9 | set({ wishLists: lists });
10 | },
11 | removeFromWishList: () => {},
12 | currentListing: undefined,
13 | setCurrentListing: (listing) => set({ currentListing: listing }),
14 | removeUserListing: (listing) => {
15 | const listings = get().userListings;
16 | const index = listings.findIndex((list) => list.id === listing);
17 | if (index !== -1) {
18 | listings.splice(index, 1);
19 | }
20 | set({ userListings: listings });
21 | },
22 | isMapView: false,
23 | listings: [],
24 | showScheduleBar: false,
25 | userListings: [],
26 | setUserListings: (userListings) => set({ userListings }),
27 | setMapView: () => {
28 | set({ isMapView: !get().isMapView });
29 | },
30 | setInitialView: () => {
31 | set({ isMapView: false });
32 | },
33 | setListings: (listings) => {
34 | set({ listings });
35 | },
36 | setShowScheduleBar: () => {
37 | set({ showScheduleBar: !get().showScheduleBar });
38 | },
39 | });
40 |
--------------------------------------------------------------------------------
/public/src/store/slices/ProcessSlice.js:
--------------------------------------------------------------------------------
1 | export const createProcessSlice = (set, get) => ({
2 | resetNewListingData: () =>
3 | set({
4 | locationType: undefined,
5 | placetype: undefined,
6 | mapData: undefined,
7 | locationData: undefined,
8 | placeSpace: { bathrooms: 1, beds: 1, guests: 4 },
9 | placeAmeneites: [],
10 | photos: [],
11 | title: "",
12 | description: "",
13 | price: 0,
14 | }),
15 | locationType: undefined,
16 | setLocationType: (locationType) => set({ locationType }),
17 | placetype: undefined,
18 | setPlaceType: (placetype) => set({ placetype }),
19 | mapData: undefined,
20 | setMapData: (mapData) => set({ mapData }),
21 | locationData: undefined,
22 | setLocationData: (locationData) => set({ locationData }),
23 | placeSpace: { bathrooms: 1, beds: 1, guests: 4 },
24 | setPlaceSpace: (placeSpace) => set({ placeSpace }),
25 | placeAmeneites: [],
26 | setPlaceAmenities: (placeAmeneites) => set({ placeAmeneites }),
27 | photos: [],
28 | setPhotos: (photos) => set({ photos }),
29 | title: "",
30 | setTitle: (title) => set({ title }),
31 | description: "",
32 | setDescription: (description) => set({ description }),
33 | price: 0,
34 | setPrice: (price) => set({ price }),
35 | });
36 |
--------------------------------------------------------------------------------
/public/src/store/slices/SearchSlice.js:
--------------------------------------------------------------------------------
1 | export const createSearchSlice = (set, get) => ({
2 | searchLocation: undefined,
3 | setSearchLocation: (location) => set({ searchLocation: location }),
4 | selectionType: undefined,
5 | searchPlaceSpace: {
6 | adults: 0,
7 | childrens: 0,
8 | infants: 0,
9 | },
10 | dates: undefined,
11 | searchListings: [],
12 | setSearchListings: (searchListings) => set({ searchListings }),
13 | setSearchPlaceSpace: (searchPlaceSpace) => set({ searchPlaceSpace }),
14 | setSelectionType: (type) => set({ selectionType: type }),
15 | setDates: (dates) => set({ dates }),
16 | });
17 |
--------------------------------------------------------------------------------
/public/src/store/store.js:
--------------------------------------------------------------------------------
1 | import { create } from "zustand";
2 | import { createLisitingsSlice } from "./slices/ListingsSlice";
3 | import { createAuthSlice } from "./slices/AuthSlice";
4 | import { createProcessSlice } from "./slices/ProcessSlice";
5 | import { createSearchSlice } from "./slices/SearchSlice";
6 |
7 | export const userAppStore = create()((...a) => ({
8 | ...createLisitingsSlice(...a),
9 | ...createAuthSlice(...a),
10 | ...createProcessSlice(...a),
11 | ...createSearchSlice(...a),
12 | }));
13 |
--------------------------------------------------------------------------------
/public/src/svg/ameneties/pool-table.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | export default function PoolTable() {
4 | return PoolTable
;
5 | }
6 |
--------------------------------------------------------------------------------
/public/src/svg/daimond.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | export default function Daimond() {
4 | return (
5 |
19 |
20 |
24 |
25 |
26 |
27 | );
28 | }
29 |
--------------------------------------------------------------------------------
/public/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | module.exports = {
3 | content: [
4 | "./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
5 | "./src/components/**/*.{js,ts,jsx,tsx,mdx}",
6 | "./src/app/**/*.{js,ts,jsx,tsx,mdx}",
7 | ],
8 | theme: {
9 | extend: {
10 | backgroundImage: {
11 | "airbnb-gradient":
12 | "linear-gradient(to right,#E61E4D 0%,#E31C5F 50%,#D70466 100%)",
13 | },
14 | colors: {
15 | "airbnb-theme-color": "#FF385C",
16 | "airbnb-light-black": "#222222",
17 | "airbnb-light-gray": "#717171",
18 | },
19 | gridTemplateRows: {
20 | "new-listing": "10vh 80vh 10vh",
21 | },
22 | },
23 | },
24 | plugins: [],
25 | };
26 |
--------------------------------------------------------------------------------
/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_cljejk0j20myvlu013m2efcap
3 | PORT=3000
4 | DB_URL=postgres://admin:admin@localhost:5432/my-db
5 | DB_USER=admin
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/docker-compose.db.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 |
55 | @InputType()
56 | export class CheckUserValues {
57 | @ApiProperty({
58 | required: true,
59 | type: String,
60 | })
61 | @IsString()
62 | @Field(() => String, { nullable: false })
63 | email!: string;
64 | }
65 |
--------------------------------------------------------------------------------
/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 { CheckUserValues, Credentials, SignupCredentials } from "./Credentials";
3 |
4 | @ArgsType()
5 | export class LoginArgs {
6 | @Field(() => Credentials, { nullable: false })
7 | credentials!: Credentials;
8 | }
9 |
10 | @ArgsType()
11 | export class SignupArgs {
12 | @Field(() => Credentials, { nullable: false })
13 | credentials!: SignupCredentials;
14 | }
15 |
16 | @ArgsType()
17 | export class CheckUserArgs {
18 | @Field(() => CheckUserValues, { nullable: false })
19 | CheckUserValues!: CheckUserValues;
20 | }
21 |
--------------------------------------------------------------------------------
/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 | const filteredData = permission.filter(data);
13 | return Object.keys(data).filter((key) => !(key in filteredData));
14 | }
15 |
--------------------------------------------------------------------------------
/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 { CheckUserValues, 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("check-user")
20 | async checkUser(@Body() body: CheckUserValues): Promise {
21 | return this.authService.checkUser(body.email);
22 | }
23 | @Post("login")
24 | async login(@Body() body: Credentials): Promise {
25 | return this.authService.login(body);
26 | }
27 | @Post("signup")
28 | async signup(@Body() body: SignupCredentials): Promise {
29 | return this.authService.signup(body);
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/server/src/auth/auth.resolver.ts:
--------------------------------------------------------------------------------
1 | import * as common from "@nestjs/common";
2 |
3 | import * as gqlACGuard from "../auth/gqlAC.guard";
4 | import { AuthService } from "./auth.service";
5 | import { GqlDefaultAuthGuard } from "./gqlDefaultAuth.guard";
6 | import { UserData } from "./userData.decorator";
7 | import { CheckUserArgs, LoginArgs, SignupArgs } from "./LoginArgs";
8 | import { UserInfo } from "./UserInfo";
9 | import { User } from "src/user/base/User";
10 | import { Args, Mutation, Query, Resolver, Context } from "@nestjs/graphql";
11 | import { Request } from "express";
12 |
13 | @Resolver(UserInfo)
14 | export class AuthResolver {
15 | constructor(private readonly authService: AuthService) {}
16 | @Mutation(() => UserInfo)
17 | async login(@Args() args: LoginArgs): Promise {
18 | return this.authService.login(args.credentials);
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 | @Query(() => User)
29 | async checkUser(@Args() args: CheckUserArgs): Promise {
30 | return this.authService.checkUser(args.CheckUserValues.email);
31 | }
32 |
33 | @Query(() => UserInfo)
34 | @common.UseGuards(GqlDefaultAuthGuard, gqlACGuard.GqlACGuard)
35 | async userInfo(@UserData() userInfo: UserInfo): Promise {
36 | return userInfo;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/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 { UserService } from "../../../user/user.service";
6 | import { UserInfo } from "../../UserInfo";
7 |
8 | export class JwtStrategyBase
9 | extends PassportStrategy(Strategy)
10 | implements IAuthStrategy
11 | {
12 | constructor(
13 | protected readonly userService: UserService,
14 | protected readonly secretOrKey: string
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 { UserService } from "../../user/user.service";
4 | import { JwtStrategyBase } from "./base/jwt.strategy.base";
5 |
6 | @Injectable()
7 | export class JwtStrategy extends JwtStrategyBase {
8 | constructor(
9 | protected readonly userService: UserService,
10 | @Inject(JWT_SECRET_KEY) secretOrKey: string
11 | ) {
12 | super(userService, secretOrKey);
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/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/auth/userData.decorator.ts:
--------------------------------------------------------------------------------
1 | import { createParamDecorator, ExecutionContext } from "@nestjs/common";
2 | import { GqlContextType, GqlExecutionContext } from "@nestjs/graphql";
3 | import { User } from "@prisma/client";
4 |
5 | /**
6 | * Access the user data from the request object i.e `req.user`.
7 | */
8 | function userFactory(ctx: ExecutionContext): User {
9 | const contextType = ctx.getType();
10 | if (contextType === "http") {
11 | // do something that is only important in the context of regular HTTP requests (REST)
12 | const { user } = ctx.switchToHttp().getRequest();
13 | return user;
14 | } else if (contextType === "rpc") {
15 | // do something that is only important in the context of Microservice requests
16 | throw new Error("Rpc context is not implemented yet");
17 | } else if (contextType === "ws") {
18 | // do something that is only important in the context of Websockets requests
19 | throw new Error("Websockets context is not implemented yet");
20 | } else if (ctx.getType() === "graphql") {
21 | // do something that is only important in the context of GraphQL requests
22 | const gqlExecutionContext = GqlExecutionContext.create(ctx);
23 | return gqlExecutionContext.getContext().req.user;
24 | }
25 | throw new Error("Invalid context");
26 | }
27 |
28 | export const UserData = createParamDecorator(
29 | (data, ctx: ExecutionContext) => userFactory(ctx)
30 | );
31 |
--------------------------------------------------------------------------------
/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/interceptors/aclFilterResponse.interceptor.ts:
--------------------------------------------------------------------------------
1 | import {
2 | CallHandler,
3 | ExecutionContext,
4 | Injectable,
5 | NestInterceptor,
6 | } from "@nestjs/common";
7 | import { Observable } from "rxjs";
8 | import { map } from "rxjs/operators";
9 | import { InjectRolesBuilder, RolesBuilder } from "nest-access-control";
10 | import { Reflector } from "@nestjs/core";
11 |
12 | @Injectable()
13 | export class AclFilterResponseInterceptor implements NestInterceptor {
14 | constructor(
15 | @InjectRolesBuilder() private readonly rolesBuilder: RolesBuilder,
16 | private readonly reflector: Reflector
17 | ) {}
18 |
19 | intercept(context: ExecutionContext, next: CallHandler): Observable {
20 | const [permissionsRoles]: any = this.reflector.getAllAndMerge(
21 | "roles",
22 | [context.getHandler(), context.getClass()]
23 | );
24 |
25 | const permission = this.rolesBuilder.permission({
26 | role: permissionsRoles.role,
27 | action: permissionsRoles.action,
28 | possession: permissionsRoles.possession,
29 | resource: permissionsRoles.resource,
30 | });
31 |
32 | return next.handle().pipe(
33 | map((data) => {
34 | if (Array.isArray(data)) {
35 | return data.map((results: any) => permission.filter(results));
36 | } else {
37 | return permission.filter(data);
38 | }
39 | })
40 | );
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/server/src/listing/base/CreateListingArgs.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 { ListingCreateInput } from "./ListingCreateInput";
15 | import { ValidateNested } from "class-validator";
16 | import { Type } from "class-transformer";
17 |
18 | @ArgsType()
19 | class CreateListingArgs {
20 | @ApiProperty({
21 | required: true,
22 | type: () => ListingCreateInput,
23 | })
24 | @ValidateNested()
25 | @Type(() => ListingCreateInput)
26 | @Field(() => ListingCreateInput, { nullable: false })
27 | data!: ListingCreateInput;
28 | }
29 |
30 | export { CreateListingArgs as CreateListingArgs };
31 |
--------------------------------------------------------------------------------
/server/src/listing/base/DeleteListingArgs.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 { ListingWhereUniqueInput } from "./ListingWhereUniqueInput";
15 | import { ValidateNested } from "class-validator";
16 | import { Type } from "class-transformer";
17 |
18 | @ArgsType()
19 | class DeleteListingArgs {
20 | @ApiProperty({
21 | required: true,
22 | type: () => ListingWhereUniqueInput,
23 | })
24 | @ValidateNested()
25 | @Type(() => ListingWhereUniqueInput)
26 | @Field(() => ListingWhereUniqueInput, { nullable: false })
27 | where!: ListingWhereUniqueInput;
28 | }
29 |
30 | export { DeleteListingArgs as DeleteListingArgs };
31 |
--------------------------------------------------------------------------------
/server/src/listing/base/ListingCountArgs.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 { ListingWhereInput } from "./ListingWhereInput";
15 | import { Type } from "class-transformer";
16 |
17 | @ArgsType()
18 | class ListingCountArgs {
19 | @ApiProperty({
20 | required: false,
21 | type: () => ListingWhereInput,
22 | })
23 | @Field(() => ListingWhereInput, { nullable: true })
24 | @Type(() => ListingWhereInput)
25 | where?: ListingWhereInput;
26 | }
27 |
28 | export { ListingCountArgs as ListingCountArgs };
29 |
--------------------------------------------------------------------------------
/server/src/listing/base/ListingFindUniqueArgs.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 { ListingWhereUniqueInput } from "./ListingWhereUniqueInput";
15 | import { ValidateNested } from "class-validator";
16 | import { Type } from "class-transformer";
17 |
18 | @ArgsType()
19 | class ListingFindUniqueArgs {
20 | @ApiProperty({
21 | required: true,
22 | type: () => ListingWhereUniqueInput,
23 | })
24 | @ValidateNested()
25 | @Type(() => ListingWhereUniqueInput)
26 | @Field(() => ListingWhereUniqueInput, { nullable: false })
27 | where!: ListingWhereUniqueInput;
28 | }
29 |
30 | export { ListingFindUniqueArgs as ListingFindUniqueArgs };
31 |
--------------------------------------------------------------------------------
/server/src/listing/base/ListingWhereUniqueInput.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 ListingWhereUniqueInput {
18 | @ApiProperty({
19 | required: true,
20 | type: String,
21 | })
22 | @IsString()
23 | @Field(() => String)
24 | id!: string;
25 | }
26 |
27 | export { ListingWhereUniqueInput as ListingWhereUniqueInput };
28 |
--------------------------------------------------------------------------------
/server/src/listing/base/TripCreateNestedManyWithoutListingsInput.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 { TripWhereUniqueInput } from "../../trip/base/TripWhereUniqueInput";
14 | import { ApiProperty } from "@nestjs/swagger";
15 |
16 | @InputType()
17 | class TripCreateNestedManyWithoutListingsInput {
18 | @Field(() => [TripWhereUniqueInput], {
19 | nullable: true,
20 | })
21 | @ApiProperty({
22 | required: false,
23 | type: () => [TripWhereUniqueInput],
24 | })
25 | connect?: Array;
26 | }
27 |
28 | export { TripCreateNestedManyWithoutListingsInput as TripCreateNestedManyWithoutListingsInput };
29 |
--------------------------------------------------------------------------------
/server/src/listing/base/TripUpdateManyWithoutListingsInput.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 { TripWhereUniqueInput } from "../../trip/base/TripWhereUniqueInput";
14 | import { ApiProperty } from "@nestjs/swagger";
15 |
16 | @InputType()
17 | class TripUpdateManyWithoutListingsInput {
18 | @Field(() => [TripWhereUniqueInput], {
19 | nullable: true,
20 | })
21 | @ApiProperty({
22 | required: false,
23 | type: () => [TripWhereUniqueInput],
24 | })
25 | connect?: Array;
26 |
27 | @Field(() => [TripWhereUniqueInput], {
28 | nullable: true,
29 | })
30 | @ApiProperty({
31 | required: false,
32 | type: () => [TripWhereUniqueInput],
33 | })
34 | disconnect?: Array;
35 |
36 | @Field(() => [TripWhereUniqueInput], {
37 | nullable: true,
38 | })
39 | @ApiProperty({
40 | required: false,
41 | type: () => [TripWhereUniqueInput],
42 | })
43 | set?: Array;
44 | }
45 |
46 | export { TripUpdateManyWithoutListingsInput as TripUpdateManyWithoutListingsInput };
47 |
--------------------------------------------------------------------------------
/server/src/listing/base/UpdateListingArgs.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 { ListingWhereUniqueInput } from "./ListingWhereUniqueInput";
15 | import { ValidateNested } from "class-validator";
16 | import { Type } from "class-transformer";
17 | import { ListingUpdateInput } from "./ListingUpdateInput";
18 |
19 | @ArgsType()
20 | class UpdateListingArgs {
21 | @ApiProperty({
22 | required: true,
23 | type: () => ListingWhereUniqueInput,
24 | })
25 | @ValidateNested()
26 | @Type(() => ListingWhereUniqueInput)
27 | @Field(() => ListingWhereUniqueInput, { nullable: false })
28 | where!: ListingWhereUniqueInput;
29 |
30 | @ApiProperty({
31 | required: true,
32 | type: () => ListingUpdateInput,
33 | })
34 | @ValidateNested()
35 | @Type(() => ListingUpdateInput)
36 | @Field(() => ListingUpdateInput, { nullable: false })
37 | data!: ListingUpdateInput;
38 | }
39 |
40 | export { UpdateListingArgs as UpdateListingArgs };
41 |
--------------------------------------------------------------------------------
/server/src/listing/base/WishlistCreateNestedManyWithoutListingsInput.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 { WishlistWhereUniqueInput } from "../../wishlist/base/WishlistWhereUniqueInput";
14 | import { ApiProperty } from "@nestjs/swagger";
15 |
16 | @InputType()
17 | class WishlistCreateNestedManyWithoutListingsInput {
18 | @Field(() => [WishlistWhereUniqueInput], {
19 | nullable: true,
20 | })
21 | @ApiProperty({
22 | required: false,
23 | type: () => [WishlistWhereUniqueInput],
24 | })
25 | connect?: Array;
26 | }
27 |
28 | export { WishlistCreateNestedManyWithoutListingsInput as WishlistCreateNestedManyWithoutListingsInput };
29 |
--------------------------------------------------------------------------------
/server/src/listing/base/WishlistUpdateManyWithoutListingsInput.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 { WishlistWhereUniqueInput } from "../../wishlist/base/WishlistWhereUniqueInput";
14 | import { ApiProperty } from "@nestjs/swagger";
15 |
16 | @InputType()
17 | class WishlistUpdateManyWithoutListingsInput {
18 | @Field(() => [WishlistWhereUniqueInput], {
19 | nullable: true,
20 | })
21 | @ApiProperty({
22 | required: false,
23 | type: () => [WishlistWhereUniqueInput],
24 | })
25 | connect?: Array;
26 |
27 | @Field(() => [WishlistWhereUniqueInput], {
28 | nullable: true,
29 | })
30 | @ApiProperty({
31 | required: false,
32 | type: () => [WishlistWhereUniqueInput],
33 | })
34 | disconnect?: Array;
35 |
36 | @Field(() => [WishlistWhereUniqueInput], {
37 | nullable: true,
38 | })
39 | @ApiProperty({
40 | required: false,
41 | type: () => [WishlistWhereUniqueInput],
42 | })
43 | set?: Array;
44 | }
45 |
46 | export { WishlistUpdateManyWithoutListingsInput as WishlistUpdateManyWithoutListingsInput };
47 |
--------------------------------------------------------------------------------
/server/src/listing/base/listing.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, forwardRef } from "@nestjs/common";
13 | import { MorganModule } from "nest-morgan";
14 | import { ACLModule } from "../../auth/acl.module";
15 | import { AuthModule } from "../../auth/auth.module";
16 | @Module({
17 | imports: [ACLModule, forwardRef(() => AuthModule), MorganModule],
18 | exports: [ACLModule, AuthModule, MorganModule],
19 | })
20 | export class ListingModuleBase {}
21 |
--------------------------------------------------------------------------------
/server/src/listing/listing.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 { ListingService } from "./listing.service";
5 | import { ListingControllerBase } from "./base/listing.controller.base";
6 |
7 | @swagger.ApiTags("listings")
8 | @common.Controller("listings")
9 | export class ListingController extends ListingControllerBase {
10 | constructor(
11 | protected readonly service: ListingService,
12 | @nestAccessControl.InjectRolesBuilder()
13 | protected readonly rolesBuilder: nestAccessControl.RolesBuilder
14 | ) {
15 | super(service, rolesBuilder);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/server/src/listing/listing.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from "@nestjs/common";
2 | import { ListingModuleBase } from "./base/listing.module.base";
3 | import { ListingService } from "./listing.service";
4 | import { ListingController } from "./listing.controller";
5 | import { ListingResolver } from "./listing.resolver";
6 |
7 | @Module({
8 | imports: [ListingModuleBase],
9 | controllers: [ListingController],
10 | providers: [ListingService, ListingResolver],
11 | exports: [ListingService],
12 | })
13 | export class ListingModule {}
14 |
--------------------------------------------------------------------------------
/server/src/listing/listing.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 { ListingResolverBase } from "./base/listing.resolver.base";
7 | import { Listing } from "./base/Listing";
8 | import { ListingService } from "./listing.service";
9 |
10 | @common.UseGuards(GqlDefaultAuthGuard, gqlACGuard.GqlACGuard)
11 | @graphql.Resolver(() => Listing)
12 | export class ListingResolver extends ListingResolverBase {
13 | constructor(
14 | protected readonly service: ListingService,
15 | @nestAccessControl.InjectRolesBuilder()
16 | protected readonly rolesBuilder: nestAccessControl.RolesBuilder
17 | ) {
18 | super(service, rolesBuilder);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/server/src/listing/listing.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from "@nestjs/common";
2 | import { PrismaService } from "../prisma/prisma.service";
3 | import { ListingServiceBase } from "./base/listing.service.base";
4 |
5 | @Injectable()
6 | export class ListingService extends ListingServiceBase {
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/providers/secrets/base/secretsManager.service.base.spec.ts:
--------------------------------------------------------------------------------
1 | import { ConfigService } from "@nestjs/config";
2 | import { mock } from "jest-mock-extended";
3 | import { SecretsManagerServiceBase } from "./secretsManager.service.base";
4 |
5 | describe("Testing the secrets manager base class", () => {
6 | const SECRET_KEY = "SECRET_KEY";
7 | const SECRET_VALUE = "SECRET_VALUE";
8 | const configService = mock();
9 | const secretsManagerServiceBase = new SecretsManagerServiceBase(
10 | configService
11 | );
12 | beforeEach(() => {
13 | configService.get.mockClear();
14 | });
15 | it("should return value from env", async () => {
16 | //ARRANGE
17 | configService.get.mockReturnValue(SECRET_VALUE);
18 | //ACT
19 | const result = await secretsManagerServiceBase.getSecret(SECRET_KEY);
20 | //ASSERT
21 | expect(result).toBe(SECRET_VALUE);
22 | });
23 | it("should return null for unknown keys", async () => {
24 | //ARRANGE
25 | configService.get.mockReturnValue(undefined);
26 | //ACT
27 | const result = await secretsManagerServiceBase.getSecret(SECRET_KEY);
28 | //ASSERT
29 | expect(result).toBeNull();
30 | });
31 | it("should throw error if dont get key", () => {
32 | return expect(secretsManagerServiceBase.getSecret("")).rejects.toThrow();
33 | });
34 | it("should throw an exeption if getting null key", () => {
35 | return expect(
36 | secretsManagerServiceBase.getSecret(null as unknown as string)
37 | ).rejects.toThrow();
38 | });
39 | });
40 |
--------------------------------------------------------------------------------
/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/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("airbnb-server")
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: "airbnb-server",
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 { UserService } from "../../../user/user.service";
5 | import { TEST_USER } from "../constants";
6 |
7 | describe("Testing the jwtStrategyBase.validate()", () => {
8 | const userService = mock();
9 | const jwtStrategy = new JwtStrategyBase(userService, "Secrete");
10 | beforeEach(() => {
11 | userService.findOne.mockClear();
12 | });
13 | it("should throw UnauthorizedException where there is no user", async () => {
14 | //ARRANGE
15 | userService.findOne
16 | .calledWith({ where: { username: TEST_USER.username } })
17 | .mockReturnValue(Promise.resolve(null));
18 | //ACT
19 | const result = jwtStrategy.validate({
20 | id: TEST_USER.id,
21 | username: TEST_USER.username,
22 | roles: TEST_USER.roles,
23 | });
24 | //ASSERT
25 | return expect(result).rejects.toThrowError(UnauthorizedException);
26 | });
27 | });
28 |
--------------------------------------------------------------------------------
/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/trip/base/CreateTripArgs.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 { TripCreateInput } from "./TripCreateInput";
15 | import { ValidateNested } from "class-validator";
16 | import { Type } from "class-transformer";
17 |
18 | @ArgsType()
19 | class CreateTripArgs {
20 | @ApiProperty({
21 | required: true,
22 | type: () => TripCreateInput,
23 | })
24 | @ValidateNested()
25 | @Type(() => TripCreateInput)
26 | @Field(() => TripCreateInput, { nullable: false })
27 | data!: TripCreateInput;
28 | }
29 |
30 | export { CreateTripArgs as CreateTripArgs };
31 |
--------------------------------------------------------------------------------
/server/src/trip/base/DeleteTripArgs.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 { TripWhereUniqueInput } from "./TripWhereUniqueInput";
15 | import { ValidateNested } from "class-validator";
16 | import { Type } from "class-transformer";
17 |
18 | @ArgsType()
19 | class DeleteTripArgs {
20 | @ApiProperty({
21 | required: true,
22 | type: () => TripWhereUniqueInput,
23 | })
24 | @ValidateNested()
25 | @Type(() => TripWhereUniqueInput)
26 | @Field(() => TripWhereUniqueInput, { nullable: false })
27 | where!: TripWhereUniqueInput;
28 | }
29 |
30 | export { DeleteTripArgs as DeleteTripArgs };
31 |
--------------------------------------------------------------------------------
/server/src/trip/base/TripCountArgs.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 { TripWhereInput } from "./TripWhereInput";
15 | import { Type } from "class-transformer";
16 |
17 | @ArgsType()
18 | class TripCountArgs {
19 | @ApiProperty({
20 | required: false,
21 | type: () => TripWhereInput,
22 | })
23 | @Field(() => TripWhereInput, { nullable: true })
24 | @Type(() => TripWhereInput)
25 | where?: TripWhereInput;
26 | }
27 |
28 | export { TripCountArgs as TripCountArgs };
29 |
--------------------------------------------------------------------------------
/server/src/trip/base/TripFindUniqueArgs.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 { TripWhereUniqueInput } from "./TripWhereUniqueInput";
15 | import { ValidateNested } from "class-validator";
16 | import { Type } from "class-transformer";
17 |
18 | @ArgsType()
19 | class TripFindUniqueArgs {
20 | @ApiProperty({
21 | required: true,
22 | type: () => TripWhereUniqueInput,
23 | })
24 | @ValidateNested()
25 | @Type(() => TripWhereUniqueInput)
26 | @Field(() => TripWhereUniqueInput, { nullable: false })
27 | where!: TripWhereUniqueInput;
28 | }
29 |
30 | export { TripFindUniqueArgs as TripFindUniqueArgs };
31 |
--------------------------------------------------------------------------------
/server/src/trip/base/TripWhereUniqueInput.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 TripWhereUniqueInput {
18 | @ApiProperty({
19 | required: true,
20 | type: String,
21 | })
22 | @IsString()
23 | @Field(() => String)
24 | id!: string;
25 | }
26 |
27 | export { TripWhereUniqueInput as TripWhereUniqueInput };
28 |
--------------------------------------------------------------------------------
/server/src/trip/base/UpdateTripArgs.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 { TripWhereUniqueInput } from "./TripWhereUniqueInput";
15 | import { ValidateNested } from "class-validator";
16 | import { Type } from "class-transformer";
17 | import { TripUpdateInput } from "./TripUpdateInput";
18 |
19 | @ArgsType()
20 | class UpdateTripArgs {
21 | @ApiProperty({
22 | required: true,
23 | type: () => TripWhereUniqueInput,
24 | })
25 | @ValidateNested()
26 | @Type(() => TripWhereUniqueInput)
27 | @Field(() => TripWhereUniqueInput, { nullable: false })
28 | where!: TripWhereUniqueInput;
29 |
30 | @ApiProperty({
31 | required: true,
32 | type: () => TripUpdateInput,
33 | })
34 | @ValidateNested()
35 | @Type(() => TripUpdateInput)
36 | @Field(() => TripUpdateInput, { nullable: false })
37 | data!: TripUpdateInput;
38 | }
39 |
40 | export { UpdateTripArgs as UpdateTripArgs };
41 |
--------------------------------------------------------------------------------
/server/src/trip/base/trip.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, forwardRef } from "@nestjs/common";
13 | import { MorganModule } from "nest-morgan";
14 | import { ACLModule } from "../../auth/acl.module";
15 | import { AuthModule } from "../../auth/auth.module";
16 | @Module({
17 | imports: [ACLModule, forwardRef(() => AuthModule), MorganModule],
18 | exports: [ACLModule, AuthModule, MorganModule],
19 | })
20 | export class TripModuleBase {}
21 |
--------------------------------------------------------------------------------
/server/src/trip/trip.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 { TripService } from "./trip.service";
5 | import { TripControllerBase } from "./base/trip.controller.base";
6 |
7 | @swagger.ApiTags("trips")
8 | @common.Controller("trips")
9 | export class TripController extends TripControllerBase {
10 | constructor(
11 | protected readonly service: TripService,
12 | @nestAccessControl.InjectRolesBuilder()
13 | protected readonly rolesBuilder: nestAccessControl.RolesBuilder
14 | ) {
15 | super(service, rolesBuilder);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/server/src/trip/trip.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from "@nestjs/common";
2 | import { TripModuleBase } from "./base/trip.module.base";
3 | import { TripService } from "./trip.service";
4 | import { TripController } from "./trip.controller";
5 | import { TripResolver } from "./trip.resolver";
6 |
7 | @Module({
8 | imports: [TripModuleBase],
9 | controllers: [TripController],
10 | providers: [TripService, TripResolver],
11 | exports: [TripService],
12 | })
13 | export class TripModule {}
14 |
--------------------------------------------------------------------------------
/server/src/trip/trip.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 { TripResolverBase } from "./base/trip.resolver.base";
7 | import { Trip } from "./base/Trip";
8 | import { TripService } from "./trip.service";
9 |
10 | @common.UseGuards(GqlDefaultAuthGuard, gqlACGuard.GqlACGuard)
11 | @graphql.Resolver(() => Trip)
12 | export class TripResolver extends TripResolverBase {
13 | constructor(
14 | protected readonly service: TripService,
15 | @nestAccessControl.InjectRolesBuilder()
16 | protected readonly rolesBuilder: nestAccessControl.RolesBuilder
17 | ) {
18 | super(service, rolesBuilder);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/server/src/trip/trip.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from "@nestjs/common";
2 | import { PrismaService } from "../prisma/prisma.service";
3 | import { TripServiceBase } from "./base/trip.service.base";
4 |
5 | @Injectable()
6 | export class TripService extends TripServiceBase {
7 | constructor(protected readonly prisma: PrismaService) {
8 | super(prisma);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/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/ListingCreateNestedManyWithoutUsersInput.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 { ListingWhereUniqueInput } from "../../listing/base/ListingWhereUniqueInput";
14 | import { ApiProperty } from "@nestjs/swagger";
15 |
16 | @InputType()
17 | class ListingCreateNestedManyWithoutUsersInput {
18 | @Field(() => [ListingWhereUniqueInput], {
19 | nullable: true,
20 | })
21 | @ApiProperty({
22 | required: false,
23 | type: () => [ListingWhereUniqueInput],
24 | })
25 | connect?: Array;
26 | }
27 |
28 | export { ListingCreateNestedManyWithoutUsersInput as ListingCreateNestedManyWithoutUsersInput };
29 |
--------------------------------------------------------------------------------
/server/src/user/base/ListingUpdateManyWithoutUsersInput.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 { ListingWhereUniqueInput } from "../../listing/base/ListingWhereUniqueInput";
14 | import { ApiProperty } from "@nestjs/swagger";
15 |
16 | @InputType()
17 | class ListingUpdateManyWithoutUsersInput {
18 | @Field(() => [ListingWhereUniqueInput], {
19 | nullable: true,
20 | })
21 | @ApiProperty({
22 | required: false,
23 | type: () => [ListingWhereUniqueInput],
24 | })
25 | connect?: Array;
26 |
27 | @Field(() => [ListingWhereUniqueInput], {
28 | nullable: true,
29 | })
30 | @ApiProperty({
31 | required: false,
32 | type: () => [ListingWhereUniqueInput],
33 | })
34 | disconnect?: Array;
35 |
36 | @Field(() => [ListingWhereUniqueInput], {
37 | nullable: true,
38 | })
39 | @ApiProperty({
40 | required: false,
41 | type: () => [ListingWhereUniqueInput],
42 | })
43 | set?: Array;
44 | }
45 |
46 | export { ListingUpdateManyWithoutUsersInput as ListingUpdateManyWithoutUsersInput };
47 |
--------------------------------------------------------------------------------
/server/src/user/base/TripCreateNestedManyWithoutUsersInput.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 { TripWhereUniqueInput } from "../../trip/base/TripWhereUniqueInput";
14 | import { ApiProperty } from "@nestjs/swagger";
15 |
16 | @InputType()
17 | class TripCreateNestedManyWithoutUsersInput {
18 | @Field(() => [TripWhereUniqueInput], {
19 | nullable: true,
20 | })
21 | @ApiProperty({
22 | required: false,
23 | type: () => [TripWhereUniqueInput],
24 | })
25 | connect?: Array;
26 | }
27 |
28 | export { TripCreateNestedManyWithoutUsersInput as TripCreateNestedManyWithoutUsersInput };
29 |
--------------------------------------------------------------------------------
/server/src/user/base/TripUpdateManyWithoutUsersInput.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 { TripWhereUniqueInput } from "../../trip/base/TripWhereUniqueInput";
14 | import { ApiProperty } from "@nestjs/swagger";
15 |
16 | @InputType()
17 | class TripUpdateManyWithoutUsersInput {
18 | @Field(() => [TripWhereUniqueInput], {
19 | nullable: true,
20 | })
21 | @ApiProperty({
22 | required: false,
23 | type: () => [TripWhereUniqueInput],
24 | })
25 | connect?: Array;
26 |
27 | @Field(() => [TripWhereUniqueInput], {
28 | nullable: true,
29 | })
30 | @ApiProperty({
31 | required: false,
32 | type: () => [TripWhereUniqueInput],
33 | })
34 | disconnect?: Array;
35 |
36 | @Field(() => [TripWhereUniqueInput], {
37 | nullable: true,
38 | })
39 | @ApiProperty({
40 | required: false,
41 | type: () => [TripWhereUniqueInput],
42 | })
43 | set?: Array;
44 | }
45 |
46 | export { TripUpdateManyWithoutUsersInput as TripUpdateManyWithoutUsersInput };
47 |
--------------------------------------------------------------------------------
/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/WishlistCreateNestedManyWithoutUsersInput.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 { WishlistWhereUniqueInput } from "../../wishlist/base/WishlistWhereUniqueInput";
14 | import { ApiProperty } from "@nestjs/swagger";
15 |
16 | @InputType()
17 | class WishlistCreateNestedManyWithoutUsersInput {
18 | @Field(() => [WishlistWhereUniqueInput], {
19 | nullable: true,
20 | })
21 | @ApiProperty({
22 | required: false,
23 | type: () => [WishlistWhereUniqueInput],
24 | })
25 | connect?: Array;
26 | }
27 |
28 | export { WishlistCreateNestedManyWithoutUsersInput as WishlistCreateNestedManyWithoutUsersInput };
29 |
--------------------------------------------------------------------------------
/server/src/user/base/WishlistUpdateManyWithoutUsersInput.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 { WishlistWhereUniqueInput } from "../../wishlist/base/WishlistWhereUniqueInput";
14 | import { ApiProperty } from "@nestjs/swagger";
15 |
16 | @InputType()
17 | class WishlistUpdateManyWithoutUsersInput {
18 | @Field(() => [WishlistWhereUniqueInput], {
19 | nullable: true,
20 | })
21 | @ApiProperty({
22 | required: false,
23 | type: () => [WishlistWhereUniqueInput],
24 | })
25 | connect?: Array;
26 |
27 | @Field(() => [WishlistWhereUniqueInput], {
28 | nullable: true,
29 | })
30 | @ApiProperty({
31 | required: false,
32 | type: () => [WishlistWhereUniqueInput],
33 | })
34 | disconnect?: Array;
35 |
36 | @Field(() => [WishlistWhereUniqueInput], {
37 | nullable: true,
38 | })
39 | @ApiProperty({
40 | required: false,
41 | type: () => [WishlistWhereUniqueInput],
42 | })
43 | set?: Array;
44 | }
45 |
46 | export { WishlistUpdateManyWithoutUsersInput as WishlistUpdateManyWithoutUsersInput };
47 |
--------------------------------------------------------------------------------
/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, forwardRef } from "@nestjs/common";
13 | import { MorganModule } from "nest-morgan";
14 | import { ACLModule } from "../../auth/acl.module";
15 | import { AuthModule } from "../../auth/auth.module";
16 | @Module({
17 | imports: [ACLModule, forwardRef(() => AuthModule), MorganModule],
18 | exports: [ACLModule, AuthModule, MorganModule],
19 | })
20 | export class UserModuleBase {}
21 |
--------------------------------------------------------------------------------
/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 } from "@nestjs/common";
2 | import { UserModuleBase } from "./base/user.module.base";
3 | import { UserService } from "./user.service";
4 | import { UserController } from "./user.controller";
5 | import { UserResolver } from "./user.resolver";
6 |
7 | @Module({
8 | imports: [UserModuleBase],
9 | controllers: [UserController],
10 | providers: [UserService, UserResolver],
11 | exports: [UserService],
12 | })
13 | export class UserModule {}
14 |
--------------------------------------------------------------------------------
/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: Object, 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/src/wishlist/base/CreateWishlistArgs.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 { WishlistCreateInput } from "./WishlistCreateInput";
15 | import { ValidateNested } from "class-validator";
16 | import { Type } from "class-transformer";
17 |
18 | @ArgsType()
19 | class CreateWishlistArgs {
20 | @ApiProperty({
21 | required: true,
22 | type: () => WishlistCreateInput,
23 | })
24 | @ValidateNested()
25 | @Type(() => WishlistCreateInput)
26 | @Field(() => WishlistCreateInput, { nullable: false })
27 | data!: WishlistCreateInput;
28 | }
29 |
30 | export { CreateWishlistArgs as CreateWishlistArgs };
31 |
--------------------------------------------------------------------------------
/server/src/wishlist/base/DeleteWishlistArgs.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 { WishlistWhereUniqueInput } from "./WishlistWhereUniqueInput";
15 | import { ValidateNested } from "class-validator";
16 | import { Type } from "class-transformer";
17 |
18 | @ArgsType()
19 | class DeleteWishlistArgs {
20 | @ApiProperty({
21 | required: true,
22 | type: () => WishlistWhereUniqueInput,
23 | })
24 | @ValidateNested()
25 | @Type(() => WishlistWhereUniqueInput)
26 | @Field(() => WishlistWhereUniqueInput, { nullable: false })
27 | where!: WishlistWhereUniqueInput;
28 | }
29 |
30 | export { DeleteWishlistArgs as DeleteWishlistArgs };
31 |
--------------------------------------------------------------------------------
/server/src/wishlist/base/UpdateWishlistArgs.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 { WishlistWhereUniqueInput } from "./WishlistWhereUniqueInput";
15 | import { ValidateNested } from "class-validator";
16 | import { Type } from "class-transformer";
17 | import { WishlistUpdateInput } from "./WishlistUpdateInput";
18 |
19 | @ArgsType()
20 | class UpdateWishlistArgs {
21 | @ApiProperty({
22 | required: true,
23 | type: () => WishlistWhereUniqueInput,
24 | })
25 | @ValidateNested()
26 | @Type(() => WishlistWhereUniqueInput)
27 | @Field(() => WishlistWhereUniqueInput, { nullable: false })
28 | where!: WishlistWhereUniqueInput;
29 |
30 | @ApiProperty({
31 | required: true,
32 | type: () => WishlistUpdateInput,
33 | })
34 | @ValidateNested()
35 | @Type(() => WishlistUpdateInput)
36 | @Field(() => WishlistUpdateInput, { nullable: false })
37 | data!: WishlistUpdateInput;
38 | }
39 |
40 | export { UpdateWishlistArgs as UpdateWishlistArgs };
41 |
--------------------------------------------------------------------------------
/server/src/wishlist/base/Wishlist.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 { ObjectType, Field } from "@nestjs/graphql";
13 | import { ApiProperty } from "@nestjs/swagger";
14 | import { IsDate, IsString, ValidateNested } from "class-validator";
15 | import { Type } from "class-transformer";
16 | import { Listing } from "../../listing/base/Listing";
17 | import { User } from "../../user/base/User";
18 |
19 | @ObjectType()
20 | class Wishlist {
21 | @ApiProperty({
22 | required: true,
23 | })
24 | @IsDate()
25 | @Type(() => Date)
26 | @Field(() => Date)
27 | createdAt!: Date;
28 |
29 | @ApiProperty({
30 | required: true,
31 | type: String,
32 | })
33 | @IsString()
34 | @Field(() => String)
35 | id!: string;
36 |
37 | @ApiProperty({
38 | required: true,
39 | type: () => Listing,
40 | })
41 | @ValidateNested()
42 | @Type(() => Listing)
43 | listing?: Listing;
44 |
45 | @ApiProperty({
46 | required: true,
47 | })
48 | @IsDate()
49 | @Type(() => Date)
50 | @Field(() => Date)
51 | updatedAt!: Date;
52 |
53 | @ApiProperty({
54 | required: true,
55 | type: () => User,
56 | })
57 | @ValidateNested()
58 | @Type(() => User)
59 | user?: User;
60 | }
61 |
62 | export { Wishlist as Wishlist };
63 |
--------------------------------------------------------------------------------
/server/src/wishlist/base/WishlistCountArgs.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 { WishlistWhereInput } from "./WishlistWhereInput";
15 | import { Type } from "class-transformer";
16 |
17 | @ArgsType()
18 | class WishlistCountArgs {
19 | @ApiProperty({
20 | required: false,
21 | type: () => WishlistWhereInput,
22 | })
23 | @Field(() => WishlistWhereInput, { nullable: true })
24 | @Type(() => WishlistWhereInput)
25 | where?: WishlistWhereInput;
26 | }
27 |
28 | export { WishlistCountArgs as WishlistCountArgs };
29 |
--------------------------------------------------------------------------------
/server/src/wishlist/base/WishlistCreateInput.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 { ListingWhereUniqueInput } from "../../listing/base/ListingWhereUniqueInput";
15 | import { ValidateNested } from "class-validator";
16 | import { Type } from "class-transformer";
17 | import { UserWhereUniqueInput } from "../../user/base/UserWhereUniqueInput";
18 |
19 | @InputType()
20 | class WishlistCreateInput {
21 | @ApiProperty({
22 | required: true,
23 | type: () => ListingWhereUniqueInput,
24 | })
25 | @ValidateNested()
26 | @Type(() => ListingWhereUniqueInput)
27 | @Field(() => ListingWhereUniqueInput)
28 | listing!: ListingWhereUniqueInput;
29 |
30 | @ApiProperty({
31 | required: true,
32 | type: () => UserWhereUniqueInput,
33 | })
34 | @ValidateNested()
35 | @Type(() => UserWhereUniqueInput)
36 | @Field(() => UserWhereUniqueInput)
37 | user!: UserWhereUniqueInput;
38 | }
39 |
40 | export { WishlistCreateInput as WishlistCreateInput };
41 |
--------------------------------------------------------------------------------
/server/src/wishlist/base/WishlistFindUniqueArgs.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 { WishlistWhereUniqueInput } from "./WishlistWhereUniqueInput";
15 | import { ValidateNested } from "class-validator";
16 | import { Type } from "class-transformer";
17 |
18 | @ArgsType()
19 | class WishlistFindUniqueArgs {
20 | @ApiProperty({
21 | required: true,
22 | type: () => WishlistWhereUniqueInput,
23 | })
24 | @ValidateNested()
25 | @Type(() => WishlistWhereUniqueInput)
26 | @Field(() => WishlistWhereUniqueInput, { nullable: false })
27 | where!: WishlistWhereUniqueInput;
28 | }
29 |
30 | export { WishlistFindUniqueArgs as WishlistFindUniqueArgs };
31 |
--------------------------------------------------------------------------------
/server/src/wishlist/base/WishlistUpdateInput.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 { ListingWhereUniqueInput } from "../../listing/base/ListingWhereUniqueInput";
15 | import { ValidateNested, IsOptional } from "class-validator";
16 | import { Type } from "class-transformer";
17 | import { UserWhereUniqueInput } from "../../user/base/UserWhereUniqueInput";
18 |
19 | @InputType()
20 | class WishlistUpdateInput {
21 | @ApiProperty({
22 | required: false,
23 | type: () => ListingWhereUniqueInput,
24 | })
25 | @ValidateNested()
26 | @Type(() => ListingWhereUniqueInput)
27 | @IsOptional()
28 | @Field(() => ListingWhereUniqueInput, {
29 | nullable: true,
30 | })
31 | listing?: ListingWhereUniqueInput;
32 |
33 | @ApiProperty({
34 | required: false,
35 | type: () => UserWhereUniqueInput,
36 | })
37 | @ValidateNested()
38 | @Type(() => UserWhereUniqueInput)
39 | @IsOptional()
40 | @Field(() => UserWhereUniqueInput, {
41 | nullable: true,
42 | })
43 | user?: UserWhereUniqueInput;
44 | }
45 |
46 | export { WishlistUpdateInput as WishlistUpdateInput };
47 |
--------------------------------------------------------------------------------
/server/src/wishlist/base/WishlistWhereUniqueInput.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 WishlistWhereUniqueInput {
18 | @ApiProperty({
19 | required: true,
20 | type: String,
21 | })
22 | @IsString()
23 | @Field(() => String)
24 | id!: string;
25 | }
26 |
27 | export { WishlistWhereUniqueInput as WishlistWhereUniqueInput };
28 |
--------------------------------------------------------------------------------
/server/src/wishlist/base/wishlist.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, forwardRef } from "@nestjs/common";
13 | import { MorganModule } from "nest-morgan";
14 | import { ACLModule } from "../../auth/acl.module";
15 | import { AuthModule } from "../../auth/auth.module";
16 | @Module({
17 | imports: [ACLModule, forwardRef(() => AuthModule), MorganModule],
18 | exports: [ACLModule, AuthModule, MorganModule],
19 | })
20 | export class WishlistModuleBase {}
21 |
--------------------------------------------------------------------------------
/server/src/wishlist/wishlist.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 { WishlistService } from "./wishlist.service";
5 | import { WishlistControllerBase } from "./base/wishlist.controller.base";
6 |
7 | @swagger.ApiTags("wishlists")
8 | @common.Controller("wishlists")
9 | export class WishlistController extends WishlistControllerBase {
10 | constructor(
11 | protected readonly service: WishlistService,
12 | @nestAccessControl.InjectRolesBuilder()
13 | protected readonly rolesBuilder: nestAccessControl.RolesBuilder
14 | ) {
15 | super(service, rolesBuilder);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/server/src/wishlist/wishlist.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from "@nestjs/common";
2 | import { WishlistModuleBase } from "./base/wishlist.module.base";
3 | import { WishlistService } from "./wishlist.service";
4 | import { WishlistController } from "./wishlist.controller";
5 | import { WishlistResolver } from "./wishlist.resolver";
6 |
7 | @Module({
8 | imports: [WishlistModuleBase],
9 | controllers: [WishlistController],
10 | providers: [WishlistService, WishlistResolver],
11 | exports: [WishlistService],
12 | })
13 | export class WishlistModule {}
14 |
--------------------------------------------------------------------------------
/server/src/wishlist/wishlist.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 { WishlistResolverBase } from "./base/wishlist.resolver.base";
7 | import { Wishlist } from "./base/Wishlist";
8 | import { WishlistService } from "./wishlist.service";
9 |
10 | @common.UseGuards(GqlDefaultAuthGuard, gqlACGuard.GqlACGuard)
11 | @graphql.Resolver(() => Wishlist)
12 | export class WishlistResolver extends WishlistResolverBase {
13 | constructor(
14 | protected readonly service: WishlistService,
15 | @nestAccessControl.InjectRolesBuilder()
16 | protected readonly rolesBuilder: nestAccessControl.RolesBuilder
17 | ) {
18 | super(service, rolesBuilder);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/server/src/wishlist/wishlist.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from "@nestjs/common";
2 | import { PrismaService } from "../prisma/prisma.service";
3 | import { WishlistServiceBase } from "./base/wishlist.service.base";
4 |
5 | @Injectable()
6 | export class WishlistService extends WishlistServiceBase {
7 | constructor(protected readonly prisma: PrismaService) {
8 | super(prisma);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------