Skip to content

Instantly share code, notes, and snippets.

@zapplebee
Last active May 19, 2023 14:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zapplebee/5680983e9cb133819f03440428d6dc3b to your computer and use it in GitHub Desktop.
Save zapplebee/5680983e9cb133819f03440428d6dc3b to your computer and use it in GitHub Desktop.
an express app with react
import React from "react";
import ReactDOMServer from "react-dom/server";
import express from "express";
import type { RequestHandler } from "express";
import session from "express-session";
import { json, urlencoded } from "body-parser";
import passport from "passport";
import { Strategy as GitHubStrategy } from "passport-github2";
import { Octokit } from "@octokit/rest";
passport.serializeUser(function (user, done) {
// console.log(`---SERIALIZE---EOD`);
// console.log(JSON.stringify(user, null, 2));
// console.log(`EOD---SERIALIZE---`);
done(null, user);
});
passport.deserializeUser(function (obj: false | null | undefined, done) {
// console.log(`---DESERIALIZE---EOD`);
// console.log(JSON.stringify(obj, null, 2));
// console.log(`EOD---DESERIALIZE---`);
done(null, obj);
});
const GH_CLIENT_ID = process.env.GH_CLIENT_ID;
const GH_CLIENT_SECRET = process.env.GH_CLIENT_SECRET;
const accessTokens = new Map();
passport.use(
new GitHubStrategy(
{
clientID: GH_CLIENT_ID,
clientSecret: GH_CLIENT_SECRET,
callbackURL: "http://localhost:3400/auth/github/callback",
},
function (accessToken: any, _refreshToken: any, profile: any, done: any) {
accessTokens.set(profile.username, accessToken);
// asynchronous verification, for effect...
process.nextTick(function () {
// To keep the example simple, the user's GitHub profile is returned to
// represent the logged-in user. In a typical application, you would want
// to associate the GitHub account with a user record in your database,
// and return that user instead.
return done(null, profile);
});
}
)
);
const Page = function Page({
title,
children,
}: {
title: string;
children?: React.ReactElement<any>;
}): React.ReactElement {
return (
<html>
<head>
<title>{title}</title>
</head>
<body>{children}</body>
</html>
);
};
const r = (page: React.ReactElement): string => {
return "<!DOCTYPE html>" + ReactDOMServer.renderToStaticMarkup(page);
};
const app = express();
app.use(json());
app.use(urlencoded({ extended: true }));
export const withReact: RequestHandler = (_req, res, next) => {
res.react = (jsx: React.ReactElement) => res.send(r(jsx));
next();
};
declare global {
namespace Express {
export interface Request {
octokit: Octokit;
}
export interface Response {
react: (jsx: React.ReactElement) => void;
}
}
}
app.use(session({ secret: "foo", resave: false, saveUninitialized: false }));
app.use(passport.initialize());
app.use(passport.session());
app.use(withReact);
const ensureAuthenticated: RequestHandler = function ensureAuthenticated(
req,
res,
next
) {
if (req.isAuthenticated()) {
const octokit = new Octokit({
auth: accessTokens.get((req.user as any).username),
});
req.octokit = octokit;
return next();
}
res.redirect("/login");
};
app.get("/login", function (req, res) {
res.react(
<Page title="Login">
<a href="/auth/github">Login with GitHub</a>
</Page>
);
});
const Main: React.FunctionComponent<{ user?: any }> = function Main({ user }) {
if (user) {
return (
<>
<h2>welcome!</h2>
<hr />
<pre>{JSON.stringify(user, null, 2)}</pre>
</>
);
}
return (
<>
<h2>Welcome! Please log in.</h2>
<hr />
<a href="/login">login</a>
</>
);
};
app.get("/", function (req, res) {
res.react(
<Page title="welcome">
<Main user={req.user} />
</Page>
);
});
const Account: React.FunctionComponent<{ user?: any; data: any }> =
function Account({ user, data = null }) {
return (
<>
<h1>Account</h1> <pre>{JSON.stringify(user, null, 2)}</pre>
<pre>{JSON.stringify(data, null, 2)}</pre>
</>
);
};
app.get("/account", ensureAuthenticated, function (req, res) {
(async function () {
const repos = await req.octokit.paginate(
req.octokit.repos.listForUser,
{
username: (req.user as any).username,
per_page: 100,
},
(response) => response.data
);
const body = repos;
res.react(
<Page title="account">
<Account user={req.user} data={body} />
</Page>
);
})();
});
app.get(
"/auth/github",
passport.authenticate("github", { scope: ["user:email"] })
);
app.get(
"/auth/github/callback",
passport.authenticate("github", { failureRedirect: "/login" }),
function (req, res) {
res.redirect("/");
}
);
app.get("/logout", function (req, res) {
(req as any).logout();
res.redirect("/");
});
app.listen(3400, () => {
console.log("http://0.0.0.0:3400");
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment