diff --git a/packages/poolbase-app/.gitignore b/packages/poolbase-app/.gitignore index f626852..2bf435d 100644 --- a/packages/poolbase-app/.gitignore +++ b/packages/poolbase-app/.gitignore @@ -61,5 +61,9 @@ node_modules/ # Yarn Integrity file .yarn-integrity -# dotenv environment variables file -.env +# public env vars +!.env +# Local dotenv files. We're following the file structure used in +# create-react-app and documented in the Ruby dotenv: +# https://github.com/bkeepers/dotenv#what-other-env-files-can-i-use +.env.*local diff --git a/packages/poolbase-app/package.json b/packages/poolbase-app/package.json index 723b348..49417e7 100644 --- a/packages/poolbase-app/package.json +++ b/packages/poolbase-app/package.json @@ -4,16 +4,18 @@ "description": "", "main": "index.js", "scripts": { - "dev": "next src/app", + "dev": "NODE_ENV=development next dev \"src/app\"", "clean": "rimraf \"../../dist/app\"", - "build-app": "next build \"src/app\"", + "build-app": "NODE_ENV=production next build \"src/app\"", "build-functions": "tsc --project src/functions", "build-public": "cpx \"src/public/**/*.*\" \"../../dist/app/public\" -C", "prebuild-install-deps": "cpx \"*{package.json,package-lock.json,yarn.lock}\" \"../../dist/app/functions\" -C", "build-install-deps": "cd \"../../dist/app/functions\" && yarn install", "prebuild": "yarn clean", "build": "yarn build-app && yarn build-functions && yarn build-public && yarn prebuild-install-deps", - "typecheck-app": "tsc --project src/app" + "typecheck-app": "tsc --project src/app", + "typecheck-functions": "tsc --project src/functions", + "type-check": "yarn typecheck-app && yarn typecheck-functions" }, "keywords": [], "author": "Andreas Sahle (https://pixelmord.de/)", @@ -22,11 +24,17 @@ "node": "10" }, "dependencies": { + "cookie-session": "1.4.0", + "dotenv": "8.2.0", + "firebase": "^7.7.0", "firebase-admin": "^8.9.2", "firebase-functions": "^3.3.0", + "isomorphic-unfetch": "^3.0.0", + "lodash": "^4.17.15", "next": "^9.2.1", "react": "^16.12.0", "react-dom": "^16.12.0", + "react-firebaseui": "^4.1.0", "theme-ui": "^0.3.1" }, "devDependencies": { diff --git a/packages/poolbase-app/src/app/.env b/packages/poolbase-app/src/app/.env new file mode 100755 index 0000000..97e4793 --- /dev/null +++ b/packages/poolbase-app/src/app/.env @@ -0,0 +1,21 @@ + +# For variables you need accessible at build time, add the variable to +# next.config.js. For secret values in local development, add the variable +# to .env.local, outside of source control. + +# Update these with your Firebase app's values. +FIREBASE_AUTH_DOMAIN=poolbase-123.firebaseapp.com +FIREBASE_CLIENT_EMAIL=firebase-adminsdk-5mj0u@poolbase-123.iam.gserviceaccount.com +FIREBASE_PROJECT_ID=poolbase-123 +FIREBASE_PUBLIC_API_KEY=AIzaSyAnbedWWD7dfug3DkkIahChaE2SjDxiaEk + +# Create another file in this directory named ".env.local", which you +# should not include in source control. In .env.local, set these secret +# environment variables: + +# Your Firebase private key. +# FIREBASE_PRIVATE_KEY=some-key-here + +# Secrets used by cookie-session. +# SESSION_SECRET_CURRENT=someSecretValue +# SESSION_SECRET_PREVIOUS=anotherSecretValue diff --git a/packages/poolbase-app/src/app/components/FirebaseAuth.tsx b/packages/poolbase-app/src/app/components/FirebaseAuth.tsx new file mode 100755 index 0000000..a87acd6 --- /dev/null +++ b/packages/poolbase-app/src/app/components/FirebaseAuth.tsx @@ -0,0 +1,46 @@ +/* globals window */ +import React, { useEffect, useState } from 'react' +import StyledFirebaseAuth from 'react-firebaseui/StyledFirebaseAuth' +import firebase from 'firebase/app' +import 'firebase/auth' +import initFirebase from '../utils/auth/initFirebase' + +// Init the Firebase app. +initFirebase() + +const firebaseAuthConfig = { + signInFlow: 'popup', + // Auth providers + // https://github.com/firebase/firebaseui-web#configure-oauth-providers + signInOptions: [ + { + provider: firebase.auth.EmailAuthProvider.PROVIDER_ID, + requireDisplayName: false, + }, + ], + signInSuccessUrl: '/', + credentialHelper: 'none', +} + +const FirebaseAuth = () => { + // Do not SSR FirebaseUI, because it is not supported. + // https://github.com/firebase/firebaseui-web/issues/213 + const [renderAuth, setRenderAuth] = useState(false) + useEffect(() => { + if (typeof window !== 'undefined') { + setRenderAuth(true) + } + }, []) + return ( +
+ {renderAuth ? ( + + ) : null} +
+ ) +} + +export default FirebaseAuth diff --git a/packages/poolbase-app/src/app/env.js b/packages/poolbase-app/src/app/env.js new file mode 100755 index 0000000..d739900 --- /dev/null +++ b/packages/poolbase-app/src/app/env.js @@ -0,0 +1,39 @@ +// Responsible for setting environment variables. +// Note: this isn't strictly required for this example – you can +// inline your Firebase config or set environment variables howevever +// else you wish – but it's a convenient way to make sure the private +// key doesn't end up in source control. + +const fs = require('fs'); +const path = require('path'); + +const { NODE_ENV } = process.env +if (!NODE_ENV) { + throw new Error( + 'The NODE_ENV environment variable is required but was not specified.' + ) +} + +// Set env vars from appropiate `.env` files. We're following the +// file structure used in create-react-app and documented in the +// Ruby dotenv. See: +// https://github.com/bkeepers/dotenv#what-other-env-files-can-i-use +const dotEnvPath = path.resolve(__dirname, '.env'); + +const dotEnvFiles = [ + `${dotEnvPath}.${NODE_ENV}.local`, + `${dotEnvPath}.${NODE_ENV}`, + // Don't include `.env.local` for the test environment. + NODE_ENV !== 'test' && `${dotEnvPath}.local`, + dotEnvPath, +].filter(Boolean) + + +dotEnvFiles.forEach(dotenvFile => { + if (fs.existsSync(dotenvFile)) { + // eslint-disable-next-line global-require + require('dotenv').config({ + path: dotenvFile, + }) + } +}) diff --git a/packages/poolbase-app/src/app/interfaces/index.ts b/packages/poolbase-app/src/app/interfaces/index.ts new file mode 100644 index 0000000..3fd5afa --- /dev/null +++ b/packages/poolbase-app/src/app/interfaces/index.ts @@ -0,0 +1,10 @@ +export type PropsWithAuthUserInfo = { + AuthUserInfo: { + AuthUser: { + id: string; + email: string; + emailVerified: boolean; + } + token: string; + } +}; diff --git a/packages/poolbase-app/src/app/next.config.js b/packages/poolbase-app/src/app/next.config.js index ed4ce44..9e514ff 100644 --- a/packages/poolbase-app/src/app/next.config.js +++ b/packages/poolbase-app/src/app/next.config.js @@ -1,5 +1,15 @@ 'use strict'; +require('./env.js'); +console.log(process.env.FIREBASE_AUTH_DOMAIN); + module.exports = { distDir: '../../../../dist/app/functions/next', + // Public, build-time env vars. + // https://nextjs.org/docs#build-time-configuration + env: { + FIREBASE_AUTH_DOMAIN: process.env.FIREBASE_AUTH_DOMAIN, + FIREBASE_PROJECT_ID: process.env.FIREBASE_PROJECT_ID, + FIREBASE_PUBLIC_API_KEY: process.env.FIREBASE_PUBLIC_API_KEY, + }, }; diff --git a/packages/poolbase-app/src/app/pages/_app.tsx b/packages/poolbase-app/src/app/pages/_app.tsx index 164ba21..0b01c68 100644 --- a/packages/poolbase-app/src/app/pages/_app.tsx +++ b/packages/poolbase-app/src/app/pages/_app.tsx @@ -1,20 +1,19 @@ import React from 'react'; import App from 'next/app'; -import { ThemeProvider, Styled } from 'theme-ui'; +import { ThemeProvider } from 'theme-ui'; import Header from '../components/Header'; import theme from '../theme'; class MyApp extends App { - render() { const { Component, pageProps } = this.props; return (
- +
- +
); } diff --git a/packages/poolbase-app/src/app/pages/_document.tsx b/packages/poolbase-app/src/app/pages/_document.tsx new file mode 100755 index 0000000..3df3bec --- /dev/null +++ b/packages/poolbase-app/src/app/pages/_document.tsx @@ -0,0 +1,52 @@ +/* eslint react/no-danger: 0 */ +import React from 'react' +import PropTypes from 'prop-types' +import { get } from 'lodash/object' +import Document, { DocumentContext, Html, Head, Main, NextScript } from 'next/document'; + +type CustomDocumentProps = { + AuthUserInfo: { + AuthUser: { + id: string; + email: string; + emailVerified: boolean; + } + token: string; + } +}; + +export default class CustomDocument extends Document { + static async getInitialProps(ctx: DocumentContext) { + // Get the AuthUserInfo object. This is set if the server-rendered page + // is wrapped in the `withAuthUser` higher-order component. + const AuthUserInfo = get(ctx, 'myCustomData.AuthUserInfo', null); + + const initialProps = await Document.getInitialProps(ctx); + return { ...initialProps, AuthUserInfo }; + } + render() { + // Store initial props from request data that we need to use again on + // the client. See: + // https://github.com/zeit/next.js/issues/3043#issuecomment-334521241 + // https://github.com/zeit/next.js/issues/2252#issuecomment-353992669 + // Alternatively, you could use a store, like Redux. + const { AuthUserInfo } = this.props; + return ( + + +