Last active
May 19, 2023 14:38
-
-
Save zapplebee/5680983e9cb133819f03440428d6dc3b to your computer and use it in GitHub Desktop.
an express app with react
This file contains hidden or 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
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