-
Notifications
You must be signed in to change notification settings - Fork 30
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
6e7e639
commit 04c7964
Showing
11 changed files
with
804 additions
and
6 deletions.
There are no files selected for viewing
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
/** | ||
* Context creation, for graphql but also REST endpoints | ||
*/ | ||
import { Connector, VulcanGraphqlModel } from "@vulcanjs/graphql"; | ||
|
||
import { createMongooseConnector } from "@vulcanjs/mongo"; | ||
import { User, UserConnector, UserType } from "~/models/user"; | ||
import { NextApiRequest } from "next"; | ||
import { getSession } from "./passport/iron"; | ||
import { Request } from "express"; | ||
import debug from "debug"; | ||
import models from "~/models"; | ||
const debugGraphqlContext = debug("vn:graphql:context"); | ||
|
||
/** | ||
const models = [Tweek, Twaik]; | ||
* Expected shape of the context | ||
* { | ||
* "Foo": { | ||
* model: Foo, | ||
* connector: FooConnector | ||
* } | ||
* } | ||
*/ | ||
interface ModelContext { | ||
[typeName: string]: { model: VulcanGraphqlModel; connector: Connector }; | ||
} | ||
/** | ||
* Build a default graphql context for a list of models | ||
* @param models | ||
*/ | ||
const createContextForModels = ( | ||
models: Array<VulcanGraphqlModel> | ||
): ModelContext => { | ||
return models.reduce( | ||
(context, model: VulcanGraphqlModel) => ({ | ||
...context, | ||
[model.name]: { | ||
model, | ||
connector: createMongooseConnector(model), | ||
}, | ||
}), | ||
{} | ||
); | ||
}; | ||
|
||
// TODO: isolate context creation code like we do in Vulcan + initialize the currentUser too | ||
export const contextBase = { | ||
...createContextForModels(models), | ||
// add some custom context here | ||
[User.graphql.typeName]: { | ||
model: User, | ||
connector: UserConnector, // we use the premade connector | ||
}, | ||
}; | ||
|
||
interface UserContext { | ||
userId?: string; | ||
currentUser?: UserType; | ||
} | ||
const userContextFromReq = async ( | ||
req: Request | NextApiRequest | ||
): Promise<UserContext> => { | ||
const session = await getSession(req); | ||
if (!session) return {}; | ||
// Refetch the user from db in order to get the freshest data | ||
const user = await UserConnector.findOneById(session._id); | ||
if (user) { | ||
return { userId: user._id, currentUser: user }; | ||
} | ||
return {}; | ||
}; | ||
export const contextFromReq = async (req: Request) => { | ||
// TODO | ||
const userContext = await userContextFromReq(req); | ||
const context = { | ||
...contextBase, | ||
...userContext, | ||
}; | ||
debugGraphqlContext("Graphql context for current request:", context); | ||
return context; | ||
}; |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import { serialize, parse } from "cookie"; | ||
|
||
export const TOKEN_NAME = "token"; | ||
const MAX_AGE = 60 * 60 * 8; // 8 hours | ||
|
||
export function setTokenCookie(res, token) { | ||
const cookie = serialize(TOKEN_NAME, token, { | ||
maxAge: MAX_AGE, | ||
expires: new Date(Date.now() + MAX_AGE * 1000), | ||
httpOnly: true, | ||
secure: process.env.NODE_ENV === "production", | ||
path: "/", | ||
sameSite: "lax", | ||
}); | ||
|
||
res.setHeader("Set-Cookie", cookie); | ||
} | ||
|
||
export function removeTokenCookie(res) { | ||
const cookie = serialize(TOKEN_NAME, "", { | ||
maxAge: -1, | ||
path: "/", | ||
}); | ||
|
||
res.setHeader("Set-Cookie", cookie); | ||
} | ||
|
||
export function parseCookies(req) { | ||
// For API Routes we don't need to parse the cookies. | ||
if (req.cookies) return req.cookies; | ||
|
||
// For pages we do need to parse the cookies. | ||
const cookie = req.headers?.cookie; | ||
return parse(cookie || ""); | ||
} | ||
|
||
export function getTokenCookie(req) { | ||
const cookies = parseCookies(req); | ||
return cookies[TOKEN_NAME]; | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
// For the token | ||
// https://hapi.dev/module/iron/ | ||
import Iron from "@hapi/iron"; | ||
import { Request } from "express"; | ||
import { NextApiRequest } from "next"; | ||
import { UserType } from "~/models/user"; | ||
import { getTokenCookie } from "./auth-cookies"; | ||
|
||
// Use an environment variable here instead of a hardcoded value for production | ||
const TOKEN_SECRET = "this-is-a-secret-value-with-at-least-32-characters"; | ||
|
||
export function encryptSession(session: UserType) { | ||
return Iron.seal(session, TOKEN_SECRET, Iron.defaults); | ||
} | ||
|
||
/** | ||
* Returns the user data from the token | ||
* @param req | ||
*/ | ||
export async function getSession( | ||
req: NextApiRequest | Request | ||
): Promise<UserType> { | ||
const token = getTokenCookie(req); | ||
return token && Iron.unseal(token, TOKEN_SECRET, Iron.defaults); | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
//@see http://www.passportjs.org/packages/passport-local/ | ||
import Local from "passport-local"; | ||
import { findUserByCredentials } from "~/models/user"; | ||
|
||
export const localStrategy = new Local.Strategy(function ( | ||
email, | ||
password, | ||
done | ||
) { | ||
findUserByCredentials({ email, password }) | ||
.then((user) => { | ||
if (!user) { | ||
done(new Error("Email/password not matching")); | ||
} else { | ||
done(null, user); | ||
} | ||
}) | ||
.catch((error) => { | ||
done(error); | ||
}); | ||
}); |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import { createMutator, getModelConnector } from "@vulcanjs/graphql"; | ||
import { User } from "~/models/user"; | ||
|
||
const seed = (context) => { | ||
// Add your seed functions here based on the example of users | ||
const UserConnector = getModelConnector(context, User); | ||
|
||
const seedAdminUser = async () => { | ||
const count = await UserConnector.count({ isAdmin: true }); | ||
if (count === 0) { | ||
console.log("No admin user found, seeding admin"); | ||
const admin = { | ||
email: process.env.ADMIN_EMAIL, | ||
password: process.env.ADMIN_INITIAL_PASSWORD, | ||
isAdmin: true, | ||
}; | ||
try { | ||
await createMutator({ | ||
model: User, | ||
data: admin, | ||
context, | ||
asAdmin: true, | ||
validate: false, | ||
}); | ||
} catch (error) { | ||
console.error("Could not seed admin user", error); | ||
} | ||
} else { | ||
console.log(`Found ${count} Admin(s) in the database, no need to seed.`); | ||
} | ||
}; | ||
|
||
// Run the seed functions | ||
seedAdminUser(); | ||
}; | ||
|
||
export default seed; |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
// Export all your models here | ||
// Please do not remove the User model, which is necessary for auth | ||
import { User } from "./user"; | ||
export default [User]; |
Oops, something went wrong.