1
0
Fork 0
forked from OpenNeo/impress
impress/src/server/index.js

133 lines
3.2 KiB
JavaScript

const util = require("util");
const { beelinePlugin } = require("./lib/beeline-graphql");
const { gql, makeExecutableSchema } = require("apollo-server");
const jwtVerify = util.promisify(require("jsonwebtoken").verify);
const jwksClient = require("jwks-rsa");
const connectToDb = require("./db");
const buildLoaders = require("./loaders");
const { svgLoggingPlugin } = require("./types/AppearanceLayer");
const rootTypeDefs = gql`
directive @cacheControl(maxAge: Int!) on FIELD_DEFINITION | OBJECT
type Mutation
type Query
`;
function mergeTypeDefsAndResolvers(modules) {
const allTypeDefs = [];
const allResolvers = {};
for (const { typeDefs, resolvers } of modules) {
allTypeDefs.push(typeDefs);
for (const typeName of Object.keys(resolvers)) {
allResolvers[typeName] = {
...allResolvers[typeName],
...resolvers[typeName],
};
}
}
return { typeDefs: allTypeDefs, resolvers: allResolvers };
}
const schema = makeExecutableSchema(
mergeTypeDefsAndResolvers([
{ typeDefs: rootTypeDefs, resolvers: {} },
require("./types/AppearanceLayer"),
require("./types/Item"),
require("./types/MutationsForSupport"),
require("./types/Outfit"),
require("./types/Pet"),
require("./types/PetAppearance"),
require("./types/User"),
require("./types/Zone"),
])
);
const plugins = [svgLoggingPlugin];
if (process.env["NODE_ENV"] !== "test") {
plugins.push(beelinePlugin);
}
const jwks = jwksClient({
jwksUri: "https://openneo.us.auth0.com/.well-known/jwks.json",
});
async function getJwtKey(header, callback) {
jwks.getSigningKey(header.kid, (err, key) => {
if (err) {
return callback(null, signingKey);
}
const signingKey = key.publicKey || key.rsaPublicKey;
callback(null, signingKey);
});
}
async function getUserIdFromToken(token) {
if (!token) {
return null;
}
let payload;
try {
payload = await jwtVerify(token, getJwtKey, {
audience: "https://impress-2020.openneo.net/api",
issuer: "https://openneo.us.auth0.com/",
algorithms: ["RS256"],
});
} catch (e) {
console.error(`Invalid auth token: ${token}\n${e}`);
return null;
}
const subMatch = payload.sub.match(/auth0\|impress-([0-9]+)/);
if (!subMatch) {
console.log("Unexpected auth token sub format", payload.sub);
return null;
}
const userId = subMatch[1];
return userId;
}
const config = {
schema,
context: async ({ req }) => {
const db = await connectToDb();
const svgLogger = svgLoggingPlugin.buildSvgLogger();
const auth = (req && req.headers && req.headers.authorization) || "";
const authMatch = auth.match(/^Bearer (.+)$/);
const token = authMatch && authMatch[1];
const currentUserId = await getUserIdFromToken(token);
return {
db,
svgLogger,
currentUserId,
...buildLoaders(db),
};
},
plugins,
// Enable Playground in production :)
introspection: true,
playground: {
endpoint: "/api/graphql",
},
};
if (require.main === module) {
const { ApolloServer } = require("apollo-server");
const server = new ApolloServer(config);
server.listen().then(({ url }) => {
console.log(`🚀 Server ready at ${url}`);
});
}
module.exports = { config };