-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
94 changed files
with
12,793 additions
and
8,857 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,24 @@ | ||
/node_modules | ||
/.idea | ||
/.vs | ||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. | ||
|
||
# dependencies | ||
/node_modules | ||
/.pnp | ||
.pnp.js | ||
|
||
# testing | ||
/coverage | ||
|
||
# production | ||
/build | ||
|
||
# misc | ||
.DS_Store | ||
.env | ||
.env.local | ||
.env.development.local | ||
.env.test.local | ||
.env.production.local | ||
|
||
npm-debug.log* | ||
yarn-debug.log* | ||
yarn-error.log* |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,199 @@ | ||
# Installation | ||
1. Check out repo | ||
2. npm install | ||
3. npm start => http://localhost:3000 | ||
|
||
test push | ||
## 👉 Get Started | ||
|
||
Install dependencies | ||
|
||
``` | ||
npm install | ||
``` | ||
|
||
Update your `.env` file with values for each environment variable | ||
|
||
``` | ||
API_KEY=AIzaSyBkkFF0XhNZeWuDmOfEhsgdfX1VBG7WTas | ||
etc ... | ||
``` | ||
|
||
Install the Vercel CLI | ||
|
||
``` | ||
npm install -g vercel | ||
``` | ||
|
||
Link your codebase to a Vercel project | ||
|
||
``` | ||
vercel | ||
``` | ||
|
||
Run the development server | ||
|
||
``` | ||
vercel dev | ||
``` | ||
|
||
When the above command completes you'll be able to view your website at `http://localhost:3000`. | ||
|
||
_Note: You can run just the front-end with `npm run start`, but `vercel dev` also handles running your API endpoints (located in the `/api` directory)._ | ||
|
||
## 🥞 Stack | ||
|
||
This project uses the following libraries and services: | ||
|
||
- Framework - [Create React App](https://create-react-app.dev) with React Router | ||
- UI Kit - [Bootstrap](https://react-bootstrap.github.io) | ||
- Authentication - [Firebase Auth](https://firebase.google.com/products/auth) | ||
- Database - [Cloud Firestore](https://firebase.google.com/products/firestore) | ||
- Payments - [Stripe](https://stripe.com) | ||
- Newsletter - [Mailchimp](https://mailchimp.com) | ||
- Contact Form - [Formspree](https://formspree.io) | ||
- Analytics - [Google Analytics](https://googleanalytics.com) | ||
- Hosting - [Vercel](https://vercel.com) | ||
|
||
## 📚 Guide | ||
|
||
<details> | ||
<summary><b>Styles</b></summary> | ||
<p> | ||
You can edit Bootstrap SASS variables in the global stylesheet located at <code><a href="src/styles/global.scss">src/styles/global.scss</a></code>. Variables allow you to control global styles (like colors and fonts), as well as element specific styles (like button padding). Before overriding Bootstrap elements with custom style check the <a href="https://getbootstrap.com/docs/4.3/getting-started/introduction/">Bootstrap docs</a> to see if you can do what need by tweaking a SASS variable. | ||
</p> | ||
<p> | ||
Custom styles are located in their related component's directory. For example, if any custom style is applied to the Navbar component you'll find it in <code>src/components/Navbar.scss</code>. We ensure custom styles are scoped to their component by prepending the classname with the component name (such as <code>.Navbar__brand</code>). This ensures styles never affect elements in other components. If styles need to be re-used in multiple components consider creating a new component that encapsulates that style and structure and using that component in multiple places. | ||
</p> | ||
</details> | ||
|
||
<details> | ||
<summary><b>Routing</b></summary> | ||
<p> | ||
This project uses <a target="_blank" href="https://reacttraining.com/react-router/web/guides/quick-start">React Router</a> and includes a convenient <code>useRouter</code> hook (located in <code><a href="src/util/router.js">src/util/router.js</a></code>) that wraps React Router and gives all the route methods and data you need. | ||
|
||
```jsx | ||
import { Link, useRouter } from './../util/router.js'; | ||
|
||
function MyComponent(){ | ||
// Get the router object | ||
const router = useRouter(); | ||
|
||
// Get value from query string (?postId=123) or route param (/:postId) | ||
console.log(router.query.postId); | ||
|
||
// Get current pathname | ||
console.log(router.pathname) | ||
|
||
// Navigate with the <Link> component or with router.push() | ||
return ( | ||
<div> | ||
<Link to="/about">About</Link> | ||
<button onClick={(e) => router.push('/about')}>About</button> | ||
</div> | ||
); | ||
} | ||
``` | ||
</p> | ||
|
||
</details> | ||
|
||
<details> | ||
<summary><b>Authentication</b></summary> | ||
<p> | ||
This project uses <a href="https://firebase.google.com">Firebase Auth</a> and includes a convenient <code>useAuth</code> hook (located in <code><a href="src/util/auth.js">src/util/auth.js</a></code>) that wraps Firebase and gives you common authentication methods. Depending on your needs you may want to edit this file and expose more Firebase functionality. | ||
|
||
```js | ||
import { useAuth } from './../util/auth.js'; | ||
|
||
function MyComponent(){ | ||
// Get the auth object in any component | ||
const auth = useAuth(); | ||
|
||
// Depending on auth state show signin or signout button | ||
// auth.user will either be an object, null when loading, or false if signed out | ||
return ( | ||
<div> | ||
{auth.user ? ( | ||
<button onClick={(e) => auth.signout()}>Signout</button> | ||
) : ( | ||
<button onClick={(e) => auth.signin('[email protected]', 'yolo')}>Signin</button> | ||
)} | ||
</div> | ||
); | ||
} | ||
``` | ||
|
||
</p> | ||
</details> | ||
|
||
<details> | ||
<summary><b>Database</b></summary> | ||
|
||
<p> | ||
This project uses <a href="https://firebase.google.com/products/firestore">Cloud Firestore</a> and includes some data fetching hooks to get you started (located in <code><a href="src/util/db.js">src/util/db.js</a></code>). You'll want to edit that file and add any additional query hooks you need for your project. | ||
|
||
```js | ||
import { useAuth } from './../util/auth.js'; | ||
import { useItemsByOwner } from './../util/db.js'; | ||
import ItemsList from './ItemsList.js'; | ||
|
||
function ItemsPage(){ | ||
const auth = useAuth(); | ||
|
||
// Fetch items by owner | ||
// It's okay if uid is undefined while auth is still loading | ||
// The hook will return a "loading" status until it has a uid | ||
const uid = auth.user ? auth.user.uid : undefined; | ||
const { data: items, status } = useItemsByOwner(uid); | ||
|
||
// Once we items data then render ItemsList component | ||
return ( | ||
<div> | ||
{status === "loading" ? ( | ||
<span>One moment please</span> | ||
) : ( | ||
<ItemsList data={items}> | ||
)} | ||
</div> | ||
); | ||
} | ||
``` | ||
|
||
</p> | ||
</details> | ||
|
||
<details> | ||
<summary><b>Deployment</b></summary> | ||
<p> | ||
Install the Vercel CLI | ||
|
||
``` | ||
npm install -g vercel | ||
``` | ||
|
||
Add each variable from `.env` to your Vercel project with the following command. You'll be prompted to enter its value and then choose one or more environments (development, preview, or production). | ||
<a target="_blank" href="https://vercel.com/docs/v2/build-step#environment-variables">Learn more here</a>. | ||
|
||
``` | ||
vercel env add VARIABLE_NAME | ||
``` | ||
|
||
Run this command to deploy a preview (for testing a live deployment) | ||
|
||
``` | ||
vercel | ||
``` | ||
|
||
Run this command to deploy to production | ||
|
||
``` | ||
vercel --prod | ||
``` | ||
|
||
See the <a target="_blank" href="https://vercel.com/docs/v2/platform/deployments">Vercel docs</a> for more details. | ||
</p> | ||
|
||
</details> | ||
|
||
<details> | ||
<summary><b>Other</b></summary> | ||
<p> | ||
The <a href="https://create-react-app.dev">Create React App documention</a> covers many other topics. | ||
This project was initially created using <a href="https://divjoy.com?ref=readme_other">Divjoy</a>, a React codebase generator. Feel free to ask questions in the <a href="https://spectrum.chat/divjoy">Divjoy forum</a> and we'll do our best to help you out. | ||
</p> | ||
</details> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
const firebaseAdmin = require("./_firebase"); | ||
|
||
const firestore = firebaseAdmin.firestore(); | ||
|
||
// Update an existing user | ||
function updateUser(uid, data) { | ||
return firestore.collection("users").doc(uid).update(data); | ||
} | ||
|
||
// Get user by uid | ||
function getUser(uid) { | ||
return firestore.collection("users").doc(uid).get().then(format); | ||
} | ||
|
||
// Get user by stripeCustomerId | ||
function getUserByCustomerId(customerId) { | ||
return firestore | ||
.collection("users") | ||
.where("stripeCustomerId", "==", customerId) | ||
.get() | ||
.then(format) | ||
.then((docs) => (docs ? docs[0] : null)); // Get first result | ||
} | ||
|
||
// Update a user by their stripeCustomerId | ||
function updateUserByCustomerId(customerId, data) { | ||
return getUserByCustomerId(customerId).then((user) => { | ||
return updateUser(user.id, data); | ||
}); | ||
} | ||
|
||
/**** HELPERS ****/ | ||
|
||
// Format Firestore response (handles a collection or single doc) | ||
function format(response) { | ||
if (response.docs) { | ||
return response.docs.map(getDoc); | ||
} else { | ||
return getDoc(response); | ||
} | ||
} | ||
|
||
// Get doc data and merge in doc.id | ||
function getDoc(doc) { | ||
return doc.exists ? { id: doc.id, ...doc.data() } : null; | ||
} | ||
|
||
module.exports = { | ||
updateUser, | ||
getUser, | ||
getUserByCustomerId, | ||
updateUserByCustomerId, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
const firebaseAdmin = require("firebase-admin"); | ||
|
||
if (!firebaseAdmin.apps.length) { | ||
firebaseAdmin.initializeApp({ | ||
credential: firebaseAdmin.credential.cert({ | ||
projectId: process.env.FIREBASE_PROJECT_ID, | ||
clientEmail: process.env.FIREBASE_CLIENT_EMAIL, | ||
privateKey: process.env.FIREBASE_PRIVATE_KEY.replace(/\\n/g, "\n"), | ||
}), | ||
databaseURL: `https://${process.env.FIREBASE_PROJECT_ID}.firebaseio.com`, | ||
}); | ||
} | ||
|
||
module.exports = firebaseAdmin; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
const firebaseAdmin = require("./_firebase.js"); | ||
|
||
// Middleware for requiring authentication and getting user | ||
const requireAuth = (fn) => async (req, res) => { | ||
// Respond with error if no authorization header | ||
if (!req.headers.authorization) { | ||
return res.status(401).send({ | ||
status: "error", | ||
message: "You must be signed in to call this endpoint", | ||
}); | ||
} | ||
|
||
// Get access token from authorization header ("Bearer: xxxxxxx") | ||
const accessToken = req.headers.authorization.split(" ")[1]; | ||
|
||
try { | ||
// Get user from token and add to req object | ||
req.user = await firebaseAdmin.auth().verifyIdToken(accessToken); | ||
|
||
// Call route function passed into this middleware | ||
return fn(req, res); | ||
} catch (error) { | ||
// If there's an error assume token is expired and return | ||
// auth/invalid-user-token error (handled by apiRequest in util.js) | ||
res.status(401).send({ | ||
status: "error", | ||
code: "auth/invalid-user-token", | ||
message: "Your login has expired. Please login again.", | ||
}); | ||
} | ||
}; | ||
|
||
module.exports = requireAuth; |
Oops, something went wrong.