[
  {
    "path": ".prettierrc.js",
    "content": "module.exports = {\n  trailingComma: 'es5',\n  tabWidth: 2,\n  singleQuote: true,\n  arrowParens: 'always',\n  useTabs: false,\n  semi: false,\n}\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2020 Brayden Werner\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE."
  },
  {
    "path": "README.md",
    "content": "<p align=\"center\">\n  <img src=\"https://i.imgur.com/Rz2joyj.jpg\" alt=\"phone-rig-image\" width=\"95%\"/>\n</p>\n\n### This repo is inspired by https://github.com/benawad/lireddit\n\n<br>\n\n## Features Include:\n\n- Server-side rendered data from postgres\n- Create user graphql mutation with password encryption\n- Get users/user graphql query\n- Light/dark theme support and switch component\n  <br>\n\n## Uses the following technologies:\n\n- React\n- Next.js\n- MaterialUI\n- Styled-Components\n- TypeGraphQL\n- URQL\n- ApolloServer(express)\n- TypeORM\n- PostgresSQL\n- Node.js\n- TypeScript\n  <br>\n\n## Running Locally:\n\n### <b>Client:</b>\n\n```\nnpm install\nnpm run dev\n```\n\n### <b>Server:</b> (You must have a postgreSQL database running and update the DATABASE_URL in server/.env if you wish to add database functionality)\n\n### A migration to create the users table can be found in server/src/migrations. To run it, uncomment the following line in server/src/index.ts\n\n<br>\n\n```\nawait conn.runMigrations()\n```\n\n```\nnpm install\nnpm run build\nnpm run dev2\n```\n\n<br>\n\n## Folder Structure\n\n```bash\n├── client\n│   └── src\n│       ├── components\n│       │   ├── elements\n│       │   └── modules\n│       ├── config\n│       ├── generated\n│       ├── graphql\n│       │   ├── fragments\n│       │   ├── mutations\n│       │   └── queries\n│       ├── hooks\n│       ├── pages\n│       │   └── api\n│       ├── providers\n│       ├── public\n│       │   ├── fonts\n│       │   └── images\n│       ├── styles\n│       └── util\n└── server\n    ├── dist\n    │   ├── entities\n    │   ├── middleware\n    │   ├── resolvers\n    │   └── utils\n    └── src\n        ├── entities\n        ├── middleware\n        ├── migrations\n        ├── resolvers\n        └── utils\n```\n\n## Hosting:\n\n### Client</b>: Hosted with vercel\n\n### Server: Hosted with Heroku using PostgreSQL plugin\n"
  },
  {
    "path": "client/.babelrc",
    "content": "{\n  \"presets\": [\"next/babel\"],\n  \"plugins\": [[\"styled-components\", { \"ssr\": true }]]\n}\n"
  },
  {
    "path": "client/.eslintignore",
    "content": "node_modules/"
  },
  {
    "path": "client/.eslintrc.js",
    "content": "module.exports = {\n  env: {\n    browser: true,\n    es2021: true,\n  },\n  extends: [\n    'eslint:recommended',\n    'plugin:react/recommended',\n    'plugin:@typescript-eslint/recommended',\n  ],\n  parser: '@typescript-eslint/parser',\n  parserOptions: {\n    ecmaFeatures: {\n      jsx: true,\n    },\n    ecmaVersion: 12,\n    sourceType: 'module',\n  },\n  plugins: ['react', '@typescript-eslint'],\n  rules: {\n    'comma-dangle': 'never',\n    'react/react-in-jsx-scope': 'off',\n    'react/prop-types': 'off',\n    '@typescript-eslint/explicit-module-boundary-types': 'off',\n    '@typescript-eslint/no-empty-interface': 'off',\n  },\n}\n"
  },
  {
    "path": "client/.gitignore",
    "content": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n/.pnp\n.pnp.js\n\n# testing\n/coverage\n\n# next.js\n/.next/\n/out/\n\n# production\n/build\n\n# misc\n.DS_Store\n*.pem\n\n# debug\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n\n# local env files\n.env.local\n.env.development.local\n.env.test.local\n.env.production.local\n\n# vercel\n.vercel\n"
  },
  {
    "path": "client/codegen.yml",
    "content": "overwrite: true\nschema: 'http://localhost:4000'\ndocuments: 'src/graphql/**/*.graphql'\ngenerates:\n  src/generated/graphql.tsx:\n    plugins:\n      - 'typescript'\n      - 'typescript-operations'\n      - 'typescript-urql'\n"
  },
  {
    "path": "client/next-env.d.ts",
    "content": "/// <reference types=\"next\" />\n/// <reference types=\"next/types/global\" />\n"
  },
  {
    "path": "client/package.json",
    "content": "{\n  \"name\": \"client\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"scripts\": {\n    \"dev\": \"next dev\",\n    \"build\": \"next build\",\n    \"start\": \"next start\",\n    \"gen\": \"graphql-codegen --config codegen.yml\"\n  },\n  \"dependencies\": {\n    \"@material-ui/core\": \"^4.11.4\",\n    \"graphql\": \"^15.5.0\",\n    \"next\": \"10.2.2\",\n    \"next-urql\": \"^3.1.0\",\n    \"react\": \"17.0.2\",\n    \"react-dom\": \"17.0.2\",\n    \"react-icons\": \"^4.2.0\",\n    \"react-switch\": \"^6.0.0\",\n    \"styled-components\": \"^5.3.0\",\n    \"urql\": \"^2.0.3\"\n  },\n  \"devDependencies\": {\n    \"@graphql-codegen/cli\": \"1.21.4\",\n    \"@graphql-codegen/typescript\": \"1.22.0\",\n    \"@graphql-codegen/typescript-operations\": \"^1.17.16\",\n    \"@graphql-codegen/typescript-urql\": \"^2.0.6\",\n    \"@types/styled-components\": \"^5.1.9\",\n    \"@typescript-eslint/eslint-plugin\": \"^4.24.0\",\n    \"@typescript-eslint/parser\": \"^4.24.0\",\n    \"babel-plugin-styled-components\": \"^1.12.0\",\n    \"eslint\": \"^7.27.0\",\n    \"eslint-config-standard\": \"^16.0.2\",\n    \"eslint-plugin-import\": \"^2.23.3\",\n    \"eslint-plugin-node\": \"^11.1.0\",\n    \"eslint-plugin-promise\": \"^4.3.1\",\n    \"eslint-plugin-react\": \"^7.23.2\",\n    \"typescript\": \"^4.2.4\"\n  }\n}\n"
  },
  {
    "path": "client/src/components/Components.txt",
    "content": "I like to break my components up into elements and modules. \n\nAn element is an invidual component that may be used in multiple places in the app. Ex: A button\nA module is a larger component generally consists of several elements. Ex: A navbar containing button components"
  },
  {
    "path": "client/src/components/elements/Nav/Nav.tsx",
    "content": "import { AppBar, Toolbar, Typography } from '@material-ui/core'\n\nexport const Nav: React.FC = () => {\n  return (\n    <AppBar position=\"static\">\n      <Toolbar>\n        <Typography variant=\"h6\">Wern Fullstack Template</Typography>\n      </Toolbar>\n    </AppBar>\n  )\n}\n"
  },
  {
    "path": "client/src/components/elements/ThemeToggle/ThemeToggle.styled.ts",
    "content": "import styled from 'styled-components'\n\nexport const SwitchContainer = styled.div`\n  position: fixed;\n  top: 1%;\n  right: 1%;\n  margin: 10px 0px 0px 0px;\n  z-index: 1;\n`\n"
  },
  {
    "path": "client/src/components/elements/ThemeToggle/ThemeToggle.tsx",
    "content": "import { useContext } from 'react'\nimport { FaMoon, FaSun } from 'react-icons/fa'\nimport Switch from 'react-switch'\n\nimport { ThemeContext } from '../../../providers/AppProvider'\nimport { SwitchContainer } from './ThemeToggle.styled'\n\nexport const ThemeToggle: React.FC = () => {\n  const { toggleTheme, themeMode } = useContext(ThemeContext)\n\n  const handleThemeChange = () => {\n    toggleTheme()\n  }\n\n  return (\n    <SwitchContainer>\n      <Switch\n        checked={themeMode === 'light' ? true : false}\n        height={25}\n        width={60}\n        handleDiameter={10}\n        onColor={'#FF5733'}\n        offColor={'#124EAA'}\n        checkedIcon={\n          <FaSun\n            style={{\n              display: 'flex',\n              justifyContent: 'center',\n              alignItems: 'center',\n              height: '100%',\n              fontSize: 18,\n              paddingLeft: 5,\n            }}\n            color={themeMode === 'light' ? 'white' : 'grey'}\n          />\n        }\n        uncheckedIcon={\n          <FaMoon\n            style={{\n              display: 'flex',\n              justifyContent: 'center',\n              alignItems: 'center',\n              height: '100%',\n              fontSize: 18,\n              paddingLeft: 14,\n            }}\n            color={themeMode === 'dark' ? 'blue' : 'blue'}\n          />\n        }\n        onChange={handleThemeChange}\n      />\n    </SwitchContainer>\n  )\n}\n"
  },
  {
    "path": "client/src/components/elements/index.ts",
    "content": "export { Nav } from './Nav/Nav'\nexport { ThemeToggle } from './ThemeToggle/ThemeToggle'\n"
  },
  {
    "path": "client/src/components/modules/index.ts",
    "content": "export {}\n"
  },
  {
    "path": "client/src/config/config.ts",
    "content": "const dev = process.env.NODE_ENV !== 'production'\n\nexport const URL = dev\n  ? 'http://localhost:3000/api'\n  : 'https://fullstack-wern-boilerplate.vercel.app/api'\n\nexport const serverURL = dev\n  ? 'http://localhost:4000'\n  : 'https://wern-fullstack-template.herokuapp.com/'\n"
  },
  {
    "path": "client/src/generated/graphql.tsx",
    "content": "import gql from 'graphql-tag';\nimport * as Urql from 'urql';\nexport type Maybe<T> = T | null;\nexport type Exact<T extends { [key: string]: unknown }> = { [K in keyof T]: T[K] };\nexport type MakeOptional<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]?: Maybe<T[SubKey]> };\nexport type MakeMaybe<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]: Maybe<T[SubKey]> };\nexport type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;\n/** All built-in and custom scalars, mapped to their actual values */\nexport type Scalars = {\n  ID: string;\n  String: string;\n  Boolean: boolean;\n  Int: number;\n  Float: number;\n};\n\nexport type FieldError = {\n  __typename?: 'FieldError';\n  field: Scalars['String'];\n  message: Scalars['String'];\n};\n\nexport type Mutation = {\n  __typename?: 'Mutation';\n  createUser: UserResponse;\n};\n\n\nexport type MutationCreateUserArgs = {\n  options: UserInput;\n};\n\nexport type Query = {\n  __typename?: 'Query';\n  user?: Maybe<UserAccount>;\n  users?: Maybe<Array<UserAccount>>;\n};\n\n\nexport type QueryUserArgs = {\n  id: Scalars['String'];\n};\n\nexport type UserAccount = {\n  __typename?: 'UserAccount';\n  id: Scalars['Float'];\n  username: Scalars['String'];\n  email: Scalars['String'];\n  createdAt: Scalars['String'];\n  updatedAt: Scalars['String'];\n};\n\nexport type UserInput = {\n  email: Scalars['String'];\n  username: Scalars['String'];\n  password: Scalars['String'];\n};\n\nexport type UserResponse = {\n  __typename?: 'UserResponse';\n  errors?: Maybe<Array<FieldError>>;\n  user?: Maybe<UserAccount>;\n};\n\nexport type CreateUserMutationVariables = Exact<{\n  username: Scalars['String'];\n  email: Scalars['String'];\n  password: Scalars['String'];\n}>;\n\n\nexport type CreateUserMutation = (\n  { __typename?: 'Mutation' }\n  & { createUser: (\n    { __typename?: 'UserResponse' }\n    & { errors?: Maybe<Array<(\n      { __typename?: 'FieldError' }\n      & Pick<FieldError, 'field' | 'message'>\n    )>>, user?: Maybe<(\n      { __typename?: 'UserAccount' }\n      & Pick<UserAccount, 'id' | 'username'>\n    )> }\n  ) }\n);\n\nexport type GetUserQueryVariables = Exact<{\n  id: Scalars['String'];\n}>;\n\n\nexport type GetUserQuery = (\n  { __typename?: 'Query' }\n  & { user?: Maybe<(\n    { __typename?: 'UserAccount' }\n    & Pick<UserAccount, 'id' | 'username' | 'email'>\n  )> }\n);\n\nexport type GetUsersQueryVariables = Exact<{ [key: string]: never; }>;\n\n\nexport type GetUsersQuery = (\n  { __typename?: 'Query' }\n  & { users?: Maybe<Array<(\n    { __typename?: 'UserAccount' }\n    & Pick<UserAccount, 'id' | 'username' | 'email'>\n  )>> }\n);\n\n\nexport const CreateUserDocument = gql`\n    mutation CreateUser($username: String!, $email: String!, $password: String!) {\n  createUser(options: {username: $username, email: $email, password: $password}) {\n    errors {\n      field\n      message\n    }\n    user {\n      id\n      username\n    }\n  }\n}\n    `;\n\nexport function useCreateUserMutation() {\n  return Urql.useMutation<CreateUserMutation, CreateUserMutationVariables>(CreateUserDocument);\n};\nexport const GetUserDocument = gql`\n    query getUser($id: String!) {\n  user(id: $id) {\n    id\n    username\n    email\n  }\n}\n    `;\n\nexport function useGetUserQuery(options: Omit<Urql.UseQueryArgs<GetUserQueryVariables>, 'query'> = {}) {\n  return Urql.useQuery<GetUserQuery>({ query: GetUserDocument, ...options });\n};\nexport const GetUsersDocument = gql`\n    query getUsers {\n  users {\n    id\n    username\n    email\n  }\n}\n    `;\n\nexport function useGetUsersQuery(options: Omit<Urql.UseQueryArgs<GetUsersQueryVariables>, 'query'> = {}) {\n  return Urql.useQuery<GetUsersQuery>({ query: GetUsersDocument, ...options });\n};"
  },
  {
    "path": "client/src/graphql/mutations/createUser.graphql",
    "content": "mutation CreateUser($username: String!, $email: String!, $password: String!) {\n  createUser(\n    options: { username: $username, email: $email, password: $password }\n  ) {\n    errors {\n      field\n      message\n    }\n    user {\n      id\n      username\n    }\n  }\n}\n"
  },
  {
    "path": "client/src/graphql/queries/getUser.graphql",
    "content": "query getUser($id: String!) {\n  user(id: $id) {\n    id\n    username\n    email\n  }\n}\n"
  },
  {
    "path": "client/src/graphql/queries/getUsers.graphql",
    "content": "query getUsers {\n  users {\n    id\n    username\n    email\n  }\n}\n"
  },
  {
    "path": "client/src/hooks/useMediaQuery.ts",
    "content": "import { useState, useEffect } from 'react'\n\nexport function useMediaQuery(query: string) {\n  const [matches, setMatches] = useState(false)\n\n  useEffect(() => {\n    const media = window.matchMedia(query)\n    if (media.matches !== matches) {\n      setMatches(media.matches)\n    }\n    const listener = () => setMatches(media.matches)\n\n    media.addEventListener('change', listener)\n    return () => media.removeEventListener('change', listener)\n  }, [matches, query])\n\n  return matches\n}\n"
  },
  {
    "path": "client/src/pages/404.tsx",
    "content": "const Error: React.FC = () => {\n  return <div>404 Error</div>\n}\nexport default Error\n"
  },
  {
    "path": "client/src/pages/_app.tsx",
    "content": "import Head from 'next/head'\nimport type { AppProps } from 'next/app'\n\nimport { AppProvider } from '../providers/AppProvider'\n\nfunction MyApp({ Component, pageProps }: AppProps) {\n  return (\n    <>\n      <Head>\n        <title>Wern Template</title>\n        <meta\n          name=\"viewport\"\n          content=\"minimum-scale=1, initial-scale=1, width=device-width\"\n        />\n      </Head>\n      <AppProvider>\n        <Component {...pageProps} />\n      </AppProvider>\n    </>\n  )\n}\n\nexport default MyApp\n"
  },
  {
    "path": "client/src/pages/_document.tsx",
    "content": "import React from 'react'\nimport Document, { Html, Head, Main, NextScript } from 'next/document'\nimport { ServerStyleSheets } from '@material-ui/core/styles'\n\nexport default class MyDocument extends Document {\n  render() {\n    return (\n      <Html lang=\"en\">\n        <Head>\n          <meta httpEquiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n          <meta\n            httpEquiv=\"Content-Security-Policy\"\n            content=\"upgrade-insecure-requests\"\n          />\n        </Head>\n        <body>\n          <Main />\n          <NextScript />\n        </body>\n      </Html>\n    )\n  }\n}\n\nMyDocument.getInitialProps = async (ctx) => {\n  const sheets = new ServerStyleSheets()\n  const originalRenderPage = ctx.renderPage\n\n  ctx.renderPage = () =>\n    originalRenderPage({\n      enhanceApp: (App) => (props) => sheets.collect(<App {...props} />),\n    })\n\n  const initialProps = await Document.getInitialProps(ctx)\n\n  return {\n    ...initialProps,\n    styles: [\n      ...React.Children.toArray(initialProps.styles),\n      sheets.getStyleElement(),\n    ],\n  }\n}\n"
  },
  {
    "path": "client/src/pages/api/hello.ts",
    "content": "export default (req: any, res: any) => {\n  console.log(req)\n  res.status(200).json({ name: 'John Doe' })\n}\n"
  },
  {
    "path": "client/src/pages/index.tsx",
    "content": "import { withUrqlClient } from 'next-urql'\nimport styled from 'styled-components'\n\nimport { /*useGetUserQuery*/ useGetUsersQuery } from '../generated/graphql'\nimport { Nav, ThemeToggle } from '../components/elements/index'\nimport { createUrqlClient } from '../util/createURQLClient'\nimport {\n  CenterContainer,\n  StyledGridContainer,\n  StyledHeaderText,\n  StyledSubText,\n} from '../styles/constantStyles'\nimport { useMediaQuery } from '../hooks/useMediaQuery'\n\nconst Home: React.FC = () => {\n  const [{ data }] = useGetUsersQuery()\n\n  //  example query with id parameter\n  // const [user] = useGetUserQuery({ variables: { id: '1' } })\n\n  const largerThan500px = useMediaQuery('(min-width: 775px)')\n\n  return (\n    <>\n      <ThemeToggle />\n      <Nav />\n      {data &&\n        data.users?.map((user, i: number) => {\n          return (\n            <CenterContainer key={i}>\n              <StyledGridContainer>\n                <StyledUserContainer largerThan500px={largerThan500px}>\n                  <StyledHeaderText>{user.username}</StyledHeaderText>\n                  <StyledSubText>Id: {user.id}</StyledSubText>\n                  <StyledSubText>{user.email}</StyledSubText>\n                </StyledUserContainer>\n              </StyledGridContainer>\n            </CenterContainer>\n          )\n        })}\n    </>\n  )\n}\n\ninterface StyledUserContainerProps {\n  largerThan500px: boolean\n}\n\nconst StyledUserContainer = styled.div<StyledUserContainerProps>`\n  width: ${(props) => (props.largerThan500px ? '700px' : '90%')};\n  height: 100%;\n  padding: 15px;\n  margin: 20px 0px 20px 0px;\n  border-radius: 10px;\n  background-color: ${(props) => props.theme.secondary};\n`\n\n//  creates client with server side rendering enabled\nexport default withUrqlClient(createUrqlClient, { ssr: true })(Home)\n"
  },
  {
    "path": "client/src/providers/AppProvider.tsx",
    "content": "import React, { useEffect, createContext, useState, useMemo } from 'react'\nimport { ThemeProvider } from 'styled-components'\n\nimport { GlobalStyles, theme } from '../styles/index'\n\nexport const ThemeContext = createContext({\n  themeMode: 'dark',\n  toggleTheme: () => {\n    return\n  },\n})\n\nexport const AppProvider: React.FC = ({ children }) => {\n  const [themeMode, setThemeMode] = useState<string>('dark')\n  const currentTheme = (theme as any)[themeMode]\n\n  useEffect(() => {\n    setThemeMode(localStorage.getItem('theme') || 'dark')\n  }, [])\n\n  useEffect(() => {\n    localStorage.setItem('theme', themeMode)\n  }, [themeMode])\n\n  const toggleTheme = () => {\n    setThemeMode((oldTheme) => {\n      if (oldTheme === 'light') return 'dark'\n      else return 'light'\n    })\n  }\n\n  //  combine into one object for global ThemeContext state\n  const value = useMemo(() => ({ themeMode, toggleTheme }), [themeMode])\n  return (\n    <ThemeContext.Provider value={value}>\n      <ThemeProvider theme={currentTheme}>\n        <GlobalStyles />\n        {children}\n      </ThemeProvider>\n    </ThemeContext.Provider>\n  )\n}\n"
  },
  {
    "path": "client/src/styles/constantStyles.ts",
    "content": "import styled from 'styled-components'\nimport { Grid } from '@material-ui/core'\n\nexport const CenterContainer = styled.div`\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  width: 100%;\n`\n\nexport const StyledGridContainer = styled(Grid)`\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  width: 100%;\n`\n\nexport const StyledHeaderText = styled.div`\n  font-size: 20px;\n`\n\nexport const StyledSubText = styled.div`\n  font-size: 15px;\n  color: ${(props) => props.theme.subText};\n`\n"
  },
  {
    "path": "client/src/styles/global.ts",
    "content": "import { createGlobalStyle, css } from 'styled-components'\n\nexport default createGlobalStyle`\n${({ theme }) => css`\n  html {\n    height: 100%;\n    body {\n      display: flex;\n      flex-direction: column;\n      height: 100%;\n      margin: 0;\n      background: ${theme.background};\n      color: ${theme.primaryText};\n      font-family: Newsreader;\n    }\n  }\n`}\n`\n"
  },
  {
    "path": "client/src/styles/index.ts",
    "content": "export { default as GlobalStyles } from './global'\nexport { default as theme } from './theme'\n"
  },
  {
    "path": "client/src/styles/styled.d.ts",
    "content": "import {} from 'styled-components'\nimport { ThemeType } from './theme'\ndeclare module 'styled-components' {\n  export interface DefaultTheme extends ThemeType {}\n}\n"
  },
  {
    "path": "client/src/styles/theme.ts",
    "content": "export type ThemeType = typeof theme['light']\n\nconst theme = {\n  light: {\n    background: '#ACDDDE',\n    secondary: '#CAF1DE',\n    secondaryDark: '#E1F8DC',\n    primaryText: 'black',\n    subText: 'gray',\n  },\n  dark: {\n    background: '#444444',\n    secondary: '#1E2328',\n    secondaryDark: '#878683',\n    primaryText: 'white',\n    subText: 'gray',\n  },\n}\n\nexport const commonColors = {\n  red: '#C6493A',\n}\nexport default theme\n"
  },
  {
    "path": "client/src/util/createURQLClient.ts",
    "content": "import { dedupExchange, cacheExchange, fetchExchange } from '@urql/core'\n\nimport { serverURL } from '../config/config'\n// import { isServer } from './isServer'\n\nexport const createUrqlClient = (ssrExchange: any, ctx: any) => {\n  //  uncomment if necessary to send cookie to the server. Useful for authentification\n  // let cookie = ''\n  // if (isServer()) {\n  //   cookie = ctx?.req?.headers?.cookie\n  // }\n\n  return {\n    url: serverURL,\n    fetchOptions: {\n      credentials: 'include' as const,\n      //  this will send a cookie to the server\n      // headers: cookie\n      //   ? {\n      //       cookie,\n      //     }\n      //   : undefined,\n    },\n    exchanges: [\n      dedupExchange,\n      cacheExchange,\n      ssrExchange, // Add `ssr` in front of the `fetchExchange`\n      fetchExchange,\n    ],\n  }\n}\n"
  },
  {
    "path": "client/src/util/isServer.ts",
    "content": "export const isServer = () => typeof window === 'undefined'\n"
  },
  {
    "path": "client/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"es5\",\n    \"lib\": [\"dom\", \"dom.iterable\", \"esnext\"],\n    \"allowJs\": true,\n    \"skipLibCheck\": true,\n    \"strict\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    \"noEmit\": true,\n    \"esModuleInterop\": true,\n    \"module\": \"esnext\",\n    \"moduleResolution\": \"node\",\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"jsx\": \"preserve\"\n  },\n  \"include\": [\"next-env.d.ts\", \"**/*.ts\", \"**/*.tsx\"],\n  \"exclude\": [\"node_modules\"]\n}\n"
  },
  {
    "path": "server/.dockerignore",
    "content": "  node_modules\nnpm-debug.log"
  },
  {
    "path": "server/.eslintignore",
    "content": "node_modules"
  },
  {
    "path": "server/.eslintrc.js",
    "content": "module.exports = {\n  env: {\n    browser: true,\n    commonjs: true,\n    es2021: true,\n  },\n  extends: ['standard'],\n  parser: '@typescript-eslint/parser',\n  parserOptions: {\n    ecmaVersion: 12,\n  },\n  plugins: ['@typescript-eslint'],\n  rules: {\n    'comma-dangle': 'never',\n  },\n}\n"
  },
  {
    "path": "server/.gitignore",
    "content": "#   add .env for real application\nnode_modules/"
  },
  {
    "path": "server/Dockerfile",
    "content": "FROM node:14\n\nWORKDIR /usr/src/app\n\nCOPY package*.json ./\n\nRUN npm install\n\nCOPY . .\nCOPY .env.production .env\n\nENV NODE_ENV production\n\nEXPOSE 8080\nCMD [ \"node\", \"dist/index.js\" ]"
  },
  {
    "path": "server/dist/config.js",
    "content": "\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.__prod__ = void 0;\nexports.__prod__ = process.env.NODE_ENV === 'production';\n//# sourceMappingURL=config.js.map"
  },
  {
    "path": "server/dist/entities/UserAccount.js",
    "content": "\"use strict\";\nvar __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {\n    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;\n    if (typeof Reflect === \"object\" && typeof Reflect.decorate === \"function\") r = Reflect.decorate(decorators, target, key, desc);\n    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;\n    return c > 3 && r && Object.defineProperty(target, key, r), r;\n};\nvar __metadata = (this && this.__metadata) || function (k, v) {\n    if (typeof Reflect === \"object\" && typeof Reflect.metadata === \"function\") return Reflect.metadata(k, v);\n};\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.UserAccount = void 0;\nconst type_graphql_1 = require(\"type-graphql\");\nconst typeorm_1 = require(\"typeorm\");\nlet UserAccount = class UserAccount extends typeorm_1.BaseEntity {\n};\n__decorate([\n    type_graphql_1.Field(),\n    typeorm_1.PrimaryGeneratedColumn(),\n    __metadata(\"design:type\", Number)\n], UserAccount.prototype, \"id\", void 0);\n__decorate([\n    type_graphql_1.Field(),\n    typeorm_1.Column({ unique: true }),\n    __metadata(\"design:type\", String)\n], UserAccount.prototype, \"username\", void 0);\n__decorate([\n    type_graphql_1.Field(),\n    typeorm_1.Column({ unique: true }),\n    __metadata(\"design:type\", String)\n], UserAccount.prototype, \"email\", void 0);\n__decorate([\n    typeorm_1.Column(),\n    __metadata(\"design:type\", String)\n], UserAccount.prototype, \"password\", void 0);\n__decorate([\n    type_graphql_1.Field(() => String),\n    typeorm_1.CreateDateColumn(),\n    __metadata(\"design:type\", Date)\n], UserAccount.prototype, \"createdAt\", void 0);\n__decorate([\n    type_graphql_1.Field(() => String),\n    typeorm_1.UpdateDateColumn(),\n    __metadata(\"design:type\", Date)\n], UserAccount.prototype, \"updatedAt\", void 0);\nUserAccount = __decorate([\n    type_graphql_1.ObjectType(),\n    typeorm_1.Entity()\n], UserAccount);\nexports.UserAccount = UserAccount;\n//# sourceMappingURL=UserAccount.js.map"
  },
  {
    "path": "server/dist/entities/index.js",
    "content": "\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar UserAccount_1 = require(\"./UserAccount\");\nObject.defineProperty(exports, \"UserAccount\", { enumerable: true, get: function () { return UserAccount_1.UserAccount; } });\n//# sourceMappingURL=index.js.map"
  },
  {
    "path": "server/dist/index.js",
    "content": "\"use strict\";\nvar __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {\n    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n    return new (P || (P = Promise))(function (resolve, reject) {\n        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n        function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n        step((generator = generator.apply(thisArg, _arguments || [])).next());\n    });\n};\nvar __importDefault = (this && this.__importDefault) || function (mod) {\n    return (mod && mod.__esModule) ? mod : { \"default\": mod };\n};\nObject.defineProperty(exports, \"__esModule\", { value: true });\nrequire(\"reflect-metadata\");\nrequire(\"dotenv-safe/config\");\nconst express_1 = __importDefault(require(\"express\"));\nconst path_1 = __importDefault(require(\"path\"));\nconst cors_1 = __importDefault(require(\"cors\"));\nconst apollo_server_express_1 = require(\"apollo-server-express\");\nconst type_graphql_1 = require(\"type-graphql\");\nconst typeorm_1 = require(\"typeorm\");\nconst index_1 = require(\"./entities/index\");\nconst index_2 = require(\"./resolvers/index\");\nconst main = () => __awaiter(void 0, void 0, void 0, function* () {\n    console.log(process.env.DATABASE_URL);\n    yield typeorm_1.createConnection({\n        type: 'postgres',\n        url: process.env.DATABASE_URL,\n        logging: true,\n        synchronize: true,\n        entities: [index_1.UserAccount],\n        migrations: [path_1.default.join(__dirname, './migrations/*')],\n        ssl: {\n            rejectUnauthorized: false,\n        },\n    });\n    const app = express_1.default();\n    app.set('trust proxy', 1);\n    app.use(cors_1.default({\n        origin: process.env.CORS_ORIGIN,\n        credentials: true,\n    }));\n    const apolloServer = new apollo_server_express_1.ApolloServer({\n        schema: yield type_graphql_1.buildSchema({\n            resolvers: [index_2.UserResolver],\n            validate: false,\n        }),\n        context: ({ req, res }) => ({\n            req,\n            res,\n        }),\n    });\n    apolloServer.applyMiddleware({\n        app,\n        cors: false,\n        path: '/',\n    });\n    app.listen(parseInt(process.env.PORT), () => {\n        console.log(`server started on localhost:${process.env.PORT}`);\n    });\n});\nmain().catch((err) => {\n    console.error(err);\n});\n//# sourceMappingURL=index.js.map"
  },
  {
    "path": "server/dist/middleware/isAuth.js",
    "content": "//# sourceMappingURL=isAuth.js.map"
  },
  {
    "path": "server/dist/migrations/1622333914717-initDB.js",
    "content": "\"use strict\";\nvar __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {\n    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n    return new (P || (P = Promise))(function (resolve, reject) {\n        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n        function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n        step((generator = generator.apply(thisArg, _arguments || [])).next());\n    });\n};\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.initDB1622333914717 = void 0;\nclass initDB1622333914717 {\n    constructor() {\n        this.name = 'initDB1622333914717';\n    }\n    up(queryRunner) {\n        return __awaiter(this, void 0, void 0, function* () {\n            yield queryRunner.query(`CREATE TABLE \"User_Account\" (\"id\" SERIAL NOT NULL, \"username\" character varying NOT NULL, \"email\" character varying NOT NULL, \"password\" character varying NOT NULL, \"createdAt\" TIMESTAMP NOT NULL DEFAULT now(), \"updatedAt\" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT \"UQ_78a916df40e02a9deb1c4b75edb\" UNIQUE (\"username\"), CONSTRAINT \"UQ_e12875dfb3b1d92d7d7c5377e22\" UNIQUE (\"email\"), CONSTRAINT \"PK_cace4a159ff9f2512dd42373760\" PRIMARY KEY (\"id\"))`);\n        });\n    }\n    down(queryRunner) {\n        return __awaiter(this, void 0, void 0, function* () {\n            yield queryRunner.query(`DROP TABLE \"user_account\"`);\n        });\n    }\n}\nexports.initDB1622333914717 = initDB1622333914717;\n//# sourceMappingURL=1622333914717-initDB.js.map"
  },
  {
    "path": "server/dist/migrations/1622348668437-MockUsers.js",
    "content": "\"use strict\";\nvar __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {\n    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n    return new (P || (P = Promise))(function (resolve, reject) {\n        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n        function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n        step((generator = generator.apply(thisArg, _arguments || [])).next());\n    });\n};\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.MockUsers1622348668437 = void 0;\nclass MockUsers1622348668437 {\n    up(queryRunner) {\n        return __awaiter(this, void 0, void 0, function* () {\n            yield queryRunner.query(`\n        insert into UserAccount (id, username, email, password, \"createdAt\", \"updatedAt\") values (1, 'sbauldry0', 'ctrewett0@unicef.org', 'jhZk91W5W', '4/18/2021', '7/22/2020');\ninsert into UserAccount (id, username, email, password, \"createdAt\", \"updatedAt\") values (2, 'gduiguid1', 'asallnow1@dell.com', 'U58IqmAs', '3/2/2021', '8/7/2020');\ninsert into UserAccount (id, username, email, password, \"createdAt\", \"updatedAt\") values (3, 'dwrey2', 'sportress2@google.com.au', 'MjoXnUBNnE', '1/23/2021', '10/5/2020');\ninsert into UserAccount (id, username, email, password, \"createdAt\", \"updatedAt\") values (4, 'hwebley3', 'whauxby3@live.com', 'aclMGjnKB4jC', '12/2/2020', '5/7/2021');\ninsert into UserAccount (id, username, email, password, \"createdAt\", \"updatedAt\") values (5, 'lscourgie4', 'kkerwick4@pen.io', '6slgst6', '6/28/2020', '3/13/2021');\ninsert into UserAccount (id, username, email, password, \"createdAt\", \"updatedAt\") values (6, 'hbrundall5', 'teaglestone5@tmall.com', 'hgmvqTD', '11/10/2020', '11/26/2020');\n        `);\n        });\n    }\n    down(_) {\n        return __awaiter(this, void 0, void 0, function* () { });\n    }\n}\nexports.MockUsers1622348668437 = MockUsers1622348668437;\n//# sourceMappingURL=1622348668437-MockUsers.js.map"
  },
  {
    "path": "server/dist/resolvers/UserAccount.js",
    "content": "\"use strict\";\nvar __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {\n    if (k2 === undefined) k2 = k;\n    Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });\n}) : (function(o, m, k, k2) {\n    if (k2 === undefined) k2 = k;\n    o[k2] = m[k];\n}));\nvar __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {\n    Object.defineProperty(o, \"default\", { enumerable: true, value: v });\n}) : function(o, v) {\n    o[\"default\"] = v;\n});\nvar __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {\n    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;\n    if (typeof Reflect === \"object\" && typeof Reflect.decorate === \"function\") r = Reflect.decorate(decorators, target, key, desc);\n    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;\n    return c > 3 && r && Object.defineProperty(target, key, r), r;\n};\nvar __importStar = (this && this.__importStar) || function (mod) {\n    if (mod && mod.__esModule) return mod;\n    var result = {};\n    if (mod != null) for (var k in mod) if (k !== \"default\" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);\n    __setModuleDefault(result, mod);\n    return result;\n};\nvar __metadata = (this && this.__metadata) || function (k, v) {\n    if (typeof Reflect === \"object\" && typeof Reflect.metadata === \"function\") return Reflect.metadata(k, v);\n};\nvar __param = (this && this.__param) || function (paramIndex, decorator) {\n    return function (target, key) { decorator(target, key, paramIndex); }\n};\nvar __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {\n    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n    return new (P || (P = Promise))(function (resolve, reject) {\n        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n        function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n        step((generator = generator.apply(thisArg, _arguments || [])).next());\n    });\n};\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.UserResolver = void 0;\nconst type_graphql_1 = require(\"type-graphql\");\nconst typeorm_1 = require(\"typeorm\");\nconst argon2 = __importStar(require(\"argon2\"));\nconst validateRegister_1 = require(\"../utils/validateRegister\");\nconst index_1 = require(\"../entities/index\");\nconst userInput_1 = require(\"./userInput\");\nlet FieldError = class FieldError {\n};\n__decorate([\n    type_graphql_1.Field(),\n    __metadata(\"design:type\", String)\n], FieldError.prototype, \"field\", void 0);\n__decorate([\n    type_graphql_1.Field(),\n    __metadata(\"design:type\", String)\n], FieldError.prototype, \"message\", void 0);\nFieldError = __decorate([\n    type_graphql_1.ObjectType()\n], FieldError);\nlet UserResponse = class UserResponse {\n};\n__decorate([\n    type_graphql_1.Field(() => [FieldError], { nullable: true }),\n    __metadata(\"design:type\", Array)\n], UserResponse.prototype, \"errors\", void 0);\n__decorate([\n    type_graphql_1.Field(() => index_1.UserAccount, { nullable: true }),\n    __metadata(\"design:type\", index_1.UserAccount)\n], UserResponse.prototype, \"user\", void 0);\nUserResponse = __decorate([\n    type_graphql_1.ObjectType()\n], UserResponse);\nlet UserResolver = class UserResolver {\n    user(id) {\n        const user = index_1.UserAccount.findOne({ where: { id } });\n        if (!user)\n            return null;\n        return user;\n    }\n    users() {\n        console.log('users query reached');\n        const users = index_1.UserAccount.find();\n        if (!users)\n            return null;\n        return users;\n    }\n    createUser(options) {\n        return __awaiter(this, void 0, void 0, function* () {\n            const errors = validateRegister_1.validateRegister(options);\n            if (errors) {\n                return { errors };\n            }\n            const hashedPassword = yield argon2.hash(options.password);\n            let user;\n            try {\n                const result = yield typeorm_1.getConnection()\n                    .createQueryBuilder()\n                    .insert()\n                    .into(index_1.UserAccount)\n                    .values({\n                    username: options.username,\n                    email: options.email,\n                    password: hashedPassword,\n                })\n                    .returning('*')\n                    .execute();\n                user = result.raw[0];\n            }\n            catch (err) {\n                if (err.code === '23505') {\n                    return {\n                        errors: [\n                            {\n                                field: 'username',\n                                message: 'username already taken',\n                            },\n                        ],\n                    };\n                }\n            }\n            return { user };\n        });\n    }\n};\n__decorate([\n    type_graphql_1.Query(() => index_1.UserAccount, { nullable: true }),\n    __param(0, type_graphql_1.Arg('id')),\n    __metadata(\"design:type\", Function),\n    __metadata(\"design:paramtypes\", [String]),\n    __metadata(\"design:returntype\", void 0)\n], UserResolver.prototype, \"user\", null);\n__decorate([\n    type_graphql_1.Query(() => [index_1.UserAccount], { nullable: true }),\n    __metadata(\"design:type\", Function),\n    __metadata(\"design:paramtypes\", []),\n    __metadata(\"design:returntype\", void 0)\n], UserResolver.prototype, \"users\", null);\n__decorate([\n    type_graphql_1.Mutation(() => UserResponse),\n    __param(0, type_graphql_1.Arg('options')),\n    __metadata(\"design:type\", Function),\n    __metadata(\"design:paramtypes\", [userInput_1.UserInput]),\n    __metadata(\"design:returntype\", Promise)\n], UserResolver.prototype, \"createUser\", null);\nUserResolver = __decorate([\n    type_graphql_1.Resolver(index_1.UserAccount)\n], UserResolver);\nexports.UserResolver = UserResolver;\n//# sourceMappingURL=UserAccount.js.map"
  },
  {
    "path": "server/dist/resolvers/index.js",
    "content": "\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nvar UserAccount_1 = require(\"./UserAccount\");\nObject.defineProperty(exports, \"UserResolver\", { enumerable: true, get: function () { return UserAccount_1.UserResolver; } });\n//# sourceMappingURL=index.js.map"
  },
  {
    "path": "server/dist/resolvers/userInput.js",
    "content": "\"use strict\";\nvar __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {\n    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;\n    if (typeof Reflect === \"object\" && typeof Reflect.decorate === \"function\") r = Reflect.decorate(decorators, target, key, desc);\n    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;\n    return c > 3 && r && Object.defineProperty(target, key, r), r;\n};\nvar __metadata = (this && this.__metadata) || function (k, v) {\n    if (typeof Reflect === \"object\" && typeof Reflect.metadata === \"function\") return Reflect.metadata(k, v);\n};\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.UserInput = void 0;\nconst type_graphql_1 = require(\"type-graphql\");\nlet UserInput = class UserInput {\n};\n__decorate([\n    type_graphql_1.Field(),\n    __metadata(\"design:type\", String)\n], UserInput.prototype, \"email\", void 0);\n__decorate([\n    type_graphql_1.Field(),\n    __metadata(\"design:type\", String)\n], UserInput.prototype, \"username\", void 0);\n__decorate([\n    type_graphql_1.Field(),\n    __metadata(\"design:type\", String)\n], UserInput.prototype, \"password\", void 0);\nUserInput = __decorate([\n    type_graphql_1.InputType()\n], UserInput);\nexports.UserInput = UserInput;\n//# sourceMappingURL=userInput.js.map"
  },
  {
    "path": "server/dist/types.js",
    "content": "\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\n//# sourceMappingURL=types.js.map"
  },
  {
    "path": "server/dist/utils/validateRegister.js",
    "content": "\"use strict\";\nObject.defineProperty(exports, \"__esModule\", { value: true });\nexports.validateRegister = void 0;\nexports.validateRegister = (options) => {\n    if (!options.email.includes('@')) {\n        return [\n            {\n                field: 'email',\n                message: 'invalid email',\n            },\n        ];\n    }\n    if (options.username.length <= 2) {\n        return [\n            {\n                field: 'username',\n                message: 'length must be greater than 2',\n            },\n        ];\n    }\n    if (options.username.includes('@')) {\n        return [\n            {\n                field: 'username',\n                message: 'cannot include an @',\n            },\n        ];\n    }\n    if (options.password.length <= 2) {\n        return [\n            {\n                field: 'password',\n                message: 'length must be greater than 2',\n            },\n        ];\n    }\n    return null;\n};\n//# sourceMappingURL=validateRegister.js.map"
  },
  {
    "path": "server/ormconfig.json",
    "content": "{\n  \"type\": \"postgres\",\n  \"host\": \"localhost\",\n  \"port\": \"5432\",\n  \"username\": \"postgres\",\n  \"password\": \"postgres\",\n  \"database\": \"thumbnailgame\",\n  \"entities\": [\"dist/entities/*.js\"]\n}\n"
  },
  {
    "path": "server/package.json",
    "content": "{\n  \"name\": \"wern-fullstack-template-server\",\n  \"version\": \"1.0.0\",\n  \"description\": \"\",\n  \"main\": \"index.js\",\n  \"scripts\": {\n    \"gen-env\": \"gen-env-types .env -o src/env.d.ts -e .\",\n    \"build\": \"tsc\",\n    \"watch\": \"tsc -w\",\n    \"dev\": \"nodemon dist/index.js\",\n    \"start\": \"node dist/index.js\",\n    \"start2\": \"ts-node src/index.ts\",\n    \"dev2\": \"nodemon --exec ts-node src/index.ts\"\n  },\n  \"keywords\": [],\n  \"author\": \"\",\n  \"license\": \"ISC\",\n  \"devDependencies\": {\n    \"@types/connect-redis\": \"^0.0.14\",\n    \"@types/cors\": \"^2.8.7\",\n    \"@types/express\": \"^4.17.7\",\n    \"@types/express-session\": \"^1.17.0\",\n    \"@types/ioredis\": \"^4.17.3\",\n    \"@types/node\": \"^14.0.27\",\n    \"@types/nodemailer\": \"^6.4.0\",\n    \"@types/redis\": \"^2.8.25\",\n    \"@types/uuid\": \"^8.0.1\",\n    \"gen-env-types\": \"^1.0.3\",\n    \"nodemon\": \"^2.0.4\",\n    \"ts-node\": \"^8.10.2\",\n    \"typescript\": \"^3.9.7\"\n  },\n  \"dependencies\": {\n    \"apollo-server-express\": \"^2.16.1\",\n    \"argon2\": \"^0.26.2\",\n    \"connect-redis\": \"^5.0.0\",\n    \"cors\": \"^2.8.5\",\n    \"dataloader\": \"^2.0.0\",\n    \"dotenv-safe\": \"^8.2.0\",\n    \"express\": \"^4.17.1\",\n    \"express-session\": \"^1.17.1\",\n    \"graphql\": \"^15.3.0\",\n    \"nodemailer\": \"^6.4.11\",\n    \"pg\": \"^8.3.0\",\n    \"reflect-metadata\": \"^0.1.13\",\n    \"type-graphql\": \"^1.0.0-rc.3\",\n    \"typeorm\": \"^0.2.25\",\n    \"uuid\": \"^8.3.0\"\n  },\n  \"mikro-orm\": {\n    \"useTsNode\": true,\n    \"configPaths\": [\n      \"./src/mikro-orm.config.ts\",\n      \"./dist/mikro-orm.config.js\"\n    ]\n  }\n}\n"
  },
  {
    "path": "server/src/config.ts",
    "content": "export const __prod__ = process.env.NODE_ENV === 'production'\n"
  },
  {
    "path": "server/src/entities/UserAccount.ts",
    "content": "import { ObjectType, Field } from 'type-graphql'\nimport {\n  Entity,\n  PrimaryGeneratedColumn,\n  CreateDateColumn,\n  UpdateDateColumn,\n  Column,\n  BaseEntity,\n} from 'typeorm'\n\n@ObjectType()\n@Entity()\nexport class UserAccount extends BaseEntity {\n  @Field()\n  @PrimaryGeneratedColumn()\n  id!: number\n\n  @Field()\n  @Column({ unique: true })\n  username!: string\n\n  @Field()\n  @Column({ unique: true })\n  email!: string\n\n  @Column()\n  password!: string\n\n  @Field(() => String)\n  @CreateDateColumn()\n  createdAt: Date\n\n  @Field(() => String)\n  @UpdateDateColumn()\n  updatedAt: Date\n}\n"
  },
  {
    "path": "server/src/entities/index.ts",
    "content": "export { UserAccount } from './UserAccount'\n"
  },
  {
    "path": "server/src/env.d.ts",
    "content": "declare namespace NodeJS {\n  interface ProcessEnv {\n    DATABASE_URL: string;\n    REDIS_URL: string;\n    PORT: string;\n    SESSION_SECRET: string;\n    CORS_ORIGIN: string;\n  }\n}"
  },
  {
    "path": "server/src/index.ts",
    "content": "import 'reflect-metadata'\nimport 'dotenv-safe/config'\nimport express from 'express'\nimport path from 'path'\nimport cors from 'cors'\nimport { ApolloServer } from 'apollo-server-express'\nimport { buildSchema } from 'type-graphql'\nimport { createConnection } from 'typeorm'\n\nimport { __prod__ } from './config'\nimport { UserAccount } from './entities/index'\nimport { UserResolver } from './resolvers/index'\n\nconst main = async () => {\n  console.log(process.env.DATABASE_URL)\n  /*const conn =*/ await createConnection({\n    type: 'postgres',\n    url: process.env.DATABASE_URL,\n    logging: true,\n    //  do not want synchronize true in production, possiblility of losing data\n    synchronize: false,\n    entities: [UserAccount],\n    migrations: [path.join(__dirname, './migrations/*')],\n    //  need this to use postgres heroku plugin\n    ssl: {\n      rejectUnauthorized: false,\n    },\n  })\n  // await conn.runMigrations()\n\n  const app = express()\n\n  app.set('trust proxy', 1)\n  app.use(\n    cors({\n      origin: process.env.CORS_ORIGIN,\n      credentials: true,\n    })\n  )\n\n  const apolloServer = new ApolloServer({\n    schema: await buildSchema({\n      resolvers: [UserResolver],\n      validate: false,\n    }),\n    context: ({ req, res }) => ({\n      req,\n      res,\n    }),\n  })\n\n  apolloServer.applyMiddleware({\n    app,\n    cors: false,\n    path: '/',\n  })\n\n  app.listen(parseInt(process.env.PORT!), () => {\n    console.log(`server started on localhost:${process.env.PORT!}`)\n  })\n}\n\nmain().catch((err) => {\n  console.error(err)\n})\n"
  },
  {
    "path": "server/src/middleware/isAuth.ts",
    "content": "// import { MiddlewareFn } from 'type-graphql'\n// import { MyContext } from '../types'\n\n// export const isAuth: MiddlewareFn<MyContext> = ({ context }, next) => {\n//   if (!context.req.session.userId) {\n//     throw new Error('not authenticated')\n//   }\n\n//   return next()\n// }\n"
  },
  {
    "path": "server/src/migrations/1622333914717-initDB.ts",
    "content": "import { MigrationInterface, QueryRunner } from 'typeorm'\n\nexport class initDB1622333914717 implements MigrationInterface {\n  name = 'initDB1622333914717'\n\n  public async up(queryRunner: QueryRunner): Promise<void> {\n    await queryRunner.query(\n      `CREATE TABLE \"User_Account\" (\"id\" SERIAL NOT NULL, \"username\" character varying NOT NULL, \"email\" character varying NOT NULL, \"password\" character varying NOT NULL, \"createdAt\" TIMESTAMP NOT NULL DEFAULT now(), \"updatedAt\" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT \"UQ_78a916df40e02a9deb1c4b75edb\" UNIQUE (\"username\"), CONSTRAINT \"UQ_e12875dfb3b1d92d7d7c5377e22\" UNIQUE (\"email\"), CONSTRAINT \"PK_cace4a159ff9f2512dd42373760\" PRIMARY KEY (\"id\"))`\n    )\n  }\n\n  public async down(queryRunner: QueryRunner): Promise<void> {\n    await queryRunner.query(`DROP TABLE \"user_account\"`)\n  }\n}\n"
  },
  {
    "path": "server/src/migrations/1622348668437-MockUsers.ts",
    "content": "import { MigrationInterface, QueryRunner } from 'typeorm'\n\nexport class MockUsers1622348668437 implements MigrationInterface {\n  public async up(queryRunner: QueryRunner): Promise<void> {\n    await queryRunner.query(`\n        insert into User_Account (id, username, email, password, \"createdAt\", \"updatedAt\") values (2, 'sbauldry0', 'ctrewett0@unicef.org', 'jhZk91W5W', '4/18/2021', '7/22/2020');\ninsert into User_Account (id, username, email, password, \"createdAt\", \"updatedAt\") values (3, 'gduiguid1', 'asallnow1@dell.com', 'U58IqmAs', '3/2/2021', '8/7/2020');\ninsert into User_Account (id, username, email, password, \"createdAt\", \"updatedAt\") values (4, 'dwrey2', 'sportress2@google.com.au', 'MjoXnUBNnE', '1/23/2021', '10/5/2020');\ninsert into User_Account (id, username, email, password, \"createdAt\", \"updatedAt\") values (5, 'hwebley3', 'whauxby3@live.com', 'aclMGjnKB4jC', '12/2/2020', '5/7/2021');\ninsert into User_Account (id, username, email, password, \"createdAt\", \"updatedAt\") values (6, 'lscourgie4', 'kkerwick4@pen.io', '6slgst6', '6/28/2020', '3/13/2021');\ninsert into User_Account (id, username, email, password, \"createdAt\", \"updatedAt\") values (7, 'hbrundall5', 'teaglestone5@tmall.com', 'hgmvqTD', '11/10/2020', '11/26/2020');\n        `)\n  }\n\n  public async down(_: QueryRunner): Promise<void> {}\n}\n"
  },
  {
    "path": "server/src/resolvers/UserAccount.ts",
    "content": "import {\n  Resolver,\n  Mutation,\n  Arg,\n  Field,\n  // Ctx,\n  ObjectType,\n  Query,\n  // FieldResolver,\n  // Root,\n} from 'type-graphql'\nimport { getConnection } from 'typeorm'\nimport * as argon2 from 'argon2'\n\n// import { MyContext } from '../types'\nimport { validateRegister } from '../utils/validateRegister'\nimport { UserAccount } from '../entities/index'\nimport { UserInput } from './userInput'\n\n@ObjectType()\nclass FieldError {\n  @Field()\n  field: string\n  @Field()\n  message: string\n}\n\n@ObjectType()\n//  return the errors associated and the user\nclass UserResponse {\n  @Field(() => [FieldError], { nullable: true })\n  errors?: FieldError[]\n\n  @Field(() => UserAccount, { nullable: true })\n  user?: UserAccount\n}\n\n@Resolver(UserAccount)\nexport class UserResolver {\n  @Query(() => UserAccount, { nullable: true })\n  user(@Arg('id') id: string) {\n    const user = UserAccount.findOne({ where: { id } })\n    if (!user) return null\n\n    return user\n  }\n\n  @Query(() => [UserAccount], { nullable: true })\n  users() {\n    console.log('users query reached')\n    const users = UserAccount.find()\n    if (!users) return null\n\n    return users\n  }\n\n  @Mutation(() => UserResponse)\n  async createUser(@Arg('options') options: UserInput): Promise<UserResponse> {\n    const errors = validateRegister(options)\n    if (errors) {\n      return { errors }\n    }\n\n    const hashedPassword = await argon2.hash(options.password)\n    let user\n    try {\n      const result = await getConnection()\n        .createQueryBuilder()\n        .insert()\n        .into(UserAccount)\n        .values({\n          username: options.username,\n          email: options.email,\n          password: hashedPassword,\n        })\n        .returning('*')\n        .execute()\n      user = result.raw[0]\n    } catch (err) {\n      if (err.code === '23505') {\n        return {\n          errors: [\n            {\n              field: 'username',\n              message: 'username already taken',\n            },\n          ],\n        }\n      }\n    }\n\n    return { user }\n  }\n}\n"
  },
  {
    "path": "server/src/resolvers/index.ts",
    "content": "export { UserResolver } from './UserAccount'\n"
  },
  {
    "path": "server/src/resolvers/userInput.ts",
    "content": "import { InputType, Field } from 'type-graphql'\n\n@InputType()\nexport class UserInput {\n  @Field()\n  email: string\n  @Field()\n  username: string\n  @Field()\n  password: string\n}\n"
  },
  {
    "path": "server/src/types.ts",
    "content": "import { Request, Response } from 'express'\n\nexport type MyContext = {\n  req: Request\n  res: Response\n}\n"
  },
  {
    "path": "server/src/utils/validateRegister.ts",
    "content": "import { UserInput } from '../resolvers/userInput'\n\nexport const validateRegister = (options: UserInput) => {\n  if (!options.email.includes('@')) {\n    return [\n      {\n        field: 'email',\n        message: 'invalid email',\n      },\n    ]\n  }\n\n  if (options.username.length <= 2) {\n    return [\n      {\n        field: 'username',\n        message: 'length must be greater than 2',\n      },\n    ]\n  }\n\n  if (options.username.includes('@')) {\n    return [\n      {\n        field: 'username',\n        message: 'cannot include an @',\n      },\n    ]\n  }\n\n  if (options.password.length <= 2) {\n    return [\n      {\n        field: 'password',\n        message: 'length must be greater than 2',\n      },\n    ]\n  }\n\n  return null\n}\n"
  },
  {
    "path": "server/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"es6\",\n    \"module\": \"commonjs\",\n    \"lib\": [\"dom\", \"es6\", \"es2017\", \"esnext.asynciterable\"],\n    \"skipLibCheck\": true,\n    \"sourceMap\": true,\n    \"outDir\": \"./dist\",\n    \"moduleResolution\": \"node\",\n    \"removeComments\": true,\n    \"noImplicitAny\": true,\n    \"strictNullChecks\": true,\n    \"strictFunctionTypes\": true,\n    \"noImplicitThis\": true,\n    \"noUnusedLocals\": true,\n    \"noUnusedParameters\": true,\n    \"noImplicitReturns\": true,\n    \"noFallthroughCasesInSwitch\": true,\n    \"allowSyntheticDefaultImports\": true,\n    \"esModuleInterop\": true,\n    \"emitDecoratorMetadata\": true,\n    \"experimentalDecorators\": true,\n    \"resolveJsonModule\": true,\n    \"baseUrl\": \".\"\n  },\n  \"exclude\": [\"node_modules\"],\n  \"include\": [\"./src/**/*.tsx\", \"./src/**/*.ts\", \"1622348668437-MockUsers.ts\"]\n}\n"
  }
]