Skip to content

Instantly share code, notes, and snippets.

@brenapp
Created December 17, 2018 23:00
Show Gist options
  • Save brenapp/506ca43b788cb0514a3372e1a4781e5e to your computer and use it in GitHub Desktop.
Save brenapp/506ca43b788cb0514a3372e1a4781e5e to your computer and use it in GitHub Desktop.
/**
* Invite manager:
* 1) Generates new invites
* 2) Applies them to users
* 3)
*/
import Koa from "koa";
import * as Router from "koa-router";
import * as admin from "firebase-admin";
import error from "../util/error";
async function authenticateUser(jwt: string) {
return admin
.auth()
.verifyIdToken(jwt, true)
.catch(function() {
throw error(
"invite.invalid.jwt",
"JWT isn't valid. Try signing out and back in again"
);
});
}
export default (router: Router, app: Koa) => {
const auth = admin.auth();
const database = admin.firestore();
// Use invite code
router.get("/invite/use", async (ctx, next) => {
let { code, jwt } = ctx.query;
if (!code) {
ctx.body = error("invite.no.code", "No invite code was supplied");
return;
}
if (!jwt) {
ctx.body = error(
"invite.no.jwt",
"No JWT was supplied. Try signing in again"
);
return;
}
// Decodes Base64 encoding
jwt = Buffer.from(jwt, "base64").toString();
let decodedToken = await authenticateUser(jwt).catch(
err => (ctx.body = err)
);
// If we errored
if (decodedToken.hasOwnProperty("error")) return;
decodedToken = decodedToken as admin.auth.DecodedIdToken;
// Get the correct team
const invite = await database
.collection("invite")
.where("code", "==", code)
.limit(1)
.get();
if (invite.empty) {
ctx.body = error(
"invite.invalid.code",
"Invalid invite code. Check your link"
);
return;
}
const team = invite.docs[0].id;
ctx.body = await addUserToTeam(decodedToken.uid, team, ctx);
});
};
async function addUserToTeam(
uid: string,
team: string,
ctx: Router.IRouterContext
) {
// First add them to the team directly
const teamRef = admin
.firestore()
.collection("teams")
.doc(team),
snapshot = await teamRef.get();
// See if the team is claimed
if (!snapshot.exists) {
return error(
"invite.invalid.team",
"That team hasn't been claimed yet, or is unavailable for other reasons"
);
}
// Check if the team already has that member
const data = snapshot.data() as FirebaseFirestore.DocumentData;
if (data.members.includes(uid)) {
return error(
"invite.invalid.user",
"User already part of this team. This is a no-op"
);
}
// Add the user to the approved invite lisrt
const invite = admin
.firestore()
.collection("invite")
.doc(team);
const inviteData = (await invite.get()).data() as FirebaseFirestore.DocumentData;
await invite.update({
approved: [...inviteData.approved, uid]
});
// Finally, push the user to the end of the team
return (
teamRef
.update({
members: [...data.members, uid]
})
// Create action summary
.then(() => ({
error: null,
action: {
"invite.member_add": uid,
"invite.team": team
}
}))
.catch(() => {
ctx.status = 500;
return error(
"invite.internal",
"Internal Server Error. Try again in a little bit"
);
})
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment