diff --git a/client/package.json b/client/package.json
index c2645c0..117db32 100644
--- a/client/package.json
+++ b/client/package.json
@@ -31,7 +31,7 @@
"test": "react-scripts test",
"eject": "react-scripts eject"
},
- "proxy": "http://localhost:3000",
+ "proxy": "http://localhost:4000",
"eslintConfig": {
"extends": "react-app"
},
diff --git a/client/src/App.js b/client/src/App.js
index dd6f6f9..840ceeb 100644
--- a/client/src/App.js
+++ b/client/src/App.js
@@ -1,7 +1,7 @@
import React, { Component } from "react";
import { BrowserRouter as Router, Route } from "react-router-dom";
-import { Home } from "./pages";
+import { Home, GithubCallback } from "./pages";
import Request from "./components/Request";
@@ -11,14 +11,18 @@ class App extends Component {
};
renderError = err => {
- return
{err}
;
+ return {err.toString()}
;
+ };
+
+ renderGithubCallback = () => {
+ return ;
};
renderHome = () => {
return (
@@ -29,11 +33,16 @@ class App extends Component {
render() {
return (
-
-
+
+
+
-
-
+
+
);
}
}
diff --git a/client/src/components/EditableInput.js b/client/src/components/EditableInput.js
new file mode 100644
index 0000000..504c335
--- /dev/null
+++ b/client/src/components/EditableInput.js
@@ -0,0 +1,10 @@
+import React from "react";
+
+export default ({ value, onClick, onSubmit, editable }) =>
+ editable ? (
+
+
+
+ ) : (
+ {value}
+ );
diff --git a/client/src/components/Request.js b/client/src/components/Request.js
index 4258eb3..568eef3 100644
--- a/client/src/components/Request.js
+++ b/client/src/components/Request.js
@@ -11,7 +11,11 @@ export default class Request extends Component {
};
componentDidMount() {
- axios[this.props.method](this.props.url).then(
+ axios[this.props.method](this.props.url, {
+ headers: {
+ Authorization: `Bearer ${window.localStorage.getItem("jwt")}`
+ }
+ }).then(
response => {
this.setState({
payload: response.data,
@@ -33,6 +37,8 @@ export default class Request extends Component {
props: { renderPending, renderError, children }
} = this;
+ console.log("rendering", requestStatus);
+
if (requestStatus === PENDING) return renderPending();
if (requestStatus === ERROR) return renderError(error);
diff --git a/client/src/pages/GithubCallback.js b/client/src/pages/GithubCallback.js
new file mode 100644
index 0000000..e3ebe0a
--- /dev/null
+++ b/client/src/pages/GithubCallback.js
@@ -0,0 +1,23 @@
+import React from "react";
+import axios from "axios";
+import { withRouter } from "react-router-dom";
+
+class GithubCallback extends React.Component {
+ componentDidMount() {
+ // polyfill required?
+ const params = new URL(window.location).searchParams;
+ const code = params.get("code");
+
+ axios.get(`/api/github/callback?code=${code}`).then(jwt => {
+ window.localStorage.setItem("jwt", jwt.data.token);
+
+ this.props.history.push("/");
+ });
+ }
+
+ render() {
+ return github callback ;
+ }
+}
+
+export default withRouter(GithubCallback);
diff --git a/client/src/pages/Home.js b/client/src/pages/Home.js
index 49fe650..734749d 100644
--- a/client/src/pages/Home.js
+++ b/client/src/pages/Home.js
@@ -10,6 +10,20 @@ import profilePhotoPlaceholder from "../assets/profilePhotoPlaceholder.png";
import githubIcon from "../assets/githubLogo.svg";
import linkedinIcon from "../assets/linkedinLogo.svg";
import * as r from "ramda";
+const qs = require("querystring");
+
+const loginUrl = `https://github.com/login/oauth/authorize?${qs.stringify({
+ client_id: process.env.REACT_APP_GITHUB_CLIENT_ID,
+ redirect_uri: process.env.REACT_APP_GITHUB_CALLBACK_URL
+})}`;
+
+console.log(loginUrl);
+
+const LoginLink = () => (
+
+ Developer Login
+
+);
const HomeContainer = styled.section.attrs({
className: "black w-90 db center"
@@ -109,6 +123,7 @@ const Home = ({ listings }) => {
return (
+
Gaza Talent
{r.map(listing => (
diff --git a/client/src/pages/index.js b/client/src/pages/index.js
index 6b59262..8045388 100644
--- a/client/src/pages/index.js
+++ b/client/src/pages/index.js
@@ -1,2 +1,3 @@
import Home from "./Home";
-export { Home };
+import GithubCallback from "./GithubCallback";
+export { Home, GithubCallback };
diff --git a/controllers/github.js b/controllers/github.js
new file mode 100644
index 0000000..9219438
--- /dev/null
+++ b/controllers/github.js
@@ -0,0 +1,28 @@
+const wrapAsync = require("../lib/wrap_async.js");
+const jwt = require("jsonwebtoken");
+
+const { accessToken, currentUser } = require("../lib/github.js");
+const airtable = require("../lib/airtable.js");
+
+const callback = wrapAsync(async (req, res) => {
+ const userAccessToken = (await accessToken(req.query.code)).access_token;
+ const userProfile = await currentUser(userAccessToken);
+ const userExists = airtable.userExists(userProfile.login);
+
+ if (!userExists) {
+ return res.status(404).json({ error: "user does not exist" });
+ }
+
+ res.json({
+ token: jwt.sign(
+ {
+ githubUsername: userProfile.login
+ },
+ process.env.JWT_SECRET
+ )
+ });
+});
+
+module.exports = {
+ callback
+};
diff --git a/controllers/getTalent.js b/controllers/profile.js
similarity index 79%
rename from controllers/getTalent.js
rename to controllers/profile.js
index 4f47233..a9c5b07 100644
--- a/controllers/getTalent.js
+++ b/controllers/profile.js
@@ -17,7 +17,8 @@ const binAndMerge = (getKey, records) => {
return out;
};
-module.exports = (req, res) => {
+const getAll = (req, res) => {
+ console.log("Getting profiles", { session: req.session });
Promise.all([getAllPeople(), getAllProfiles()])
.then(([personList, profileList]) => {
return {
@@ -30,7 +31,10 @@ module.exports = (req, res) => {
})
.then(({ people, profiles }) => {
Object.keys(profiles).forEach(name => {
- Object.assign(people[name].fields, profiles[name].fields);
+ people[name].fields;
+ if (people[name]) {
+ Object.assign(people[name].fields, profiles[name].fields);
+ }
});
return Object.keys(people).map(name => people[name]);
@@ -44,3 +48,7 @@ module.exports = (req, res) => {
res.status(500).send("something is not working. Sorry :(");
});
};
+
+module.exports = {
+ getAll
+};
diff --git a/index.js b/index.js
index f9a9fd7..9a2114d 100644
--- a/index.js
+++ b/index.js
@@ -1,13 +1,20 @@
const express = require("express");
const morgan = require("morgan");
-// INIT
require("env2")(".env"); // configure enviroment (in ./.env)
+const sessionMiddleware = require("./middleware/session.js");
+
+const githubController = require("./controllers/github.js");
+const profileController = require("./controllers/profile.js");
+
+// INIT
+
const app = express();
// MIDDLEWARE
app.use(morgan("tiny"));
+app.use(sessionMiddleware);
// CLIENT
app.use(express.static("client/build"));
@@ -15,11 +22,20 @@ app.use(express.static("client/build"));
// API
const apiRouter = new express.Router();
-apiRouter.get("/talent", require("./controllers/getTalent.js"));
+apiRouter.get("/profile", profileController.getAll);
+apiRouter.get("/github/callback", githubController.callback);
app.use("/api", apiRouter);
+// Error MIDDLEWARE
+app.use(function(req, res, next) {
+ res.status(404);
+
+ // default to plain-text. send()
+ res.type("txt").send("Not found");
+});
+
// START
-const port = process.env.PORT || 3000;
+const port = process.env.PORT || 4000;
app.listen(port, () => console.log(`Server is listening on port ${port}`));
diff --git a/lib/airtable.js b/lib/airtable.js
index 8f1a08a..afd390c 100644
--- a/lib/airtable.js
+++ b/lib/airtable.js
@@ -5,6 +5,26 @@ const profilesTableName = "Talent Profiles";
const base = airtable.base(process.env.AIRTABLE_BASE_ID);
+const userExists = (ghUsername, cb) => {
+ let count = 0;
+ return new Promise((resolve, reject) => {
+ base(profilesTableName)
+ .select({
+ filterByFormula: "{Github Username}=ghUsername"
+ })
+ .eachPage(
+ (pageRecords, getNextPage) => {
+ count += pageRecords.length;
+ },
+ err => {
+ if (err) return reject(err);
+
+ resolve(count >= 0);
+ }
+ );
+ });
+};
+
const getAll = (tableName, cb) => {
const records = [];
@@ -38,5 +58,6 @@ module.exports = {
base,
getAll,
getAllPeople,
- getAllProfiles
+ getAllProfiles,
+ userExists
};
diff --git a/lib/github.js b/lib/github.js
new file mode 100644
index 0000000..8e95ce8
--- /dev/null
+++ b/lib/github.js
@@ -0,0 +1,29 @@
+const request = require("request-promise-native");
+
+const accessToken = async code => {
+ console.log({ code });
+ return request.post({
+ url: "https://github.com/login/oauth/access_token",
+ json: true,
+ body: {
+ client_id: process.env.GITHUB_CLIENT_ID,
+ client_secret: process.env.GITHUB_CLIENT_SECRET,
+ code: code
+ }
+ });
+};
+
+const currentUser = async accessToken =>
+ await request.get({
+ url: "https://api.github.com/user",
+ json: true,
+ headers: {
+ Authorization: `token ${accessToken}`,
+ "User-Agent": "Gaza Sky Geeks Talent"
+ }
+ });
+
+module.exports = {
+ accessToken,
+ currentUser
+};
diff --git a/lib/wrap_async.js b/lib/wrap_async.js
new file mode 100644
index 0000000..f6bcfb5
--- /dev/null
+++ b/lib/wrap_async.js
@@ -0,0 +1,3 @@
+module.exports = fn => (req, res, next) => {
+ Promise.resolve(fn(req, res, next)).catch(next);
+};
diff --git a/middleware/session.js b/middleware/session.js
new file mode 100644
index 0000000..7cb6462
--- /dev/null
+++ b/middleware/session.js
@@ -0,0 +1,25 @@
+const wrapAsync = require("../lib/wrap_async.js");
+const jwt = require("jsonwebtoken");
+
+const sessionMiddleware = wrapAsync(async (req, res, next) => {
+ const authHeader = req.get("Authorization");
+
+ console.log(req.headers);
+ console.log("session middleware", { authHeader });
+ if (authHeader) {
+ const token = authHeader.split(" ")[1];
+ let tokenPayload;
+ try {
+ tokenPayload = jwt.verify(token, process.env.JWT_SECRET);
+ } catch (e) {
+ console.error(e);
+ }
+
+ console.log({ tokenPayload });
+ req.session = tokenPayload;
+ }
+
+ next();
+});
+
+module.exports = sessionMiddleware;
diff --git a/package.json b/package.json
index 7ec080d..aada70c 100644
--- a/package.json
+++ b/package.json
@@ -24,7 +24,9 @@
"airtable": "^0.5.8",
"env2": "^2.2.2",
"express": "^4.16.4",
- "morgan": "^1.9.1"
+ "jsonwebtoken": "^8.4.0",
+ "morgan": "^1.9.1",
+ "request-promise-native": "^1.0.5"
},
"devDependencies": {
"nodemon": "^1.18.7"