Created
December 17, 2018 23:00
-
-
Save brenapp/506ca43b788cb0514a3372e1a4781e5e to your computer and use it in GitHub Desktop.
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
/** | |
* 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