summaryrefslogtreecommitdiffstats
path: root/backend/src
diff options
context:
space:
mode:
authorWes Bos <wesbos@gmail.com>2018-02-09 13:31:39 -0500
committerWes Bos <wesbos@gmail.com>2018-02-09 13:31:39 -0500
commite0d0baa75ec0bf617f91b6cd779305d4009cae12 (patch)
tree00eb968f49c6586cbc4367053887d0c5e68471b2 /backend/src
parent19c9683e2126c68cb9d231293162c756d69c51fb (diff)
🆒
Diffstat (limited to 'backend/src')
-rw-r--r--backend/src/generated/prisma.graphql160
-rw-r--r--backend/src/index.js5
-rw-r--r--backend/src/mail.js12
-rw-r--r--backend/src/resolvers/Mutation.js129
-rw-r--r--backend/src/schema.graphql7
5 files changed, 303 insertions, 10 deletions
diff --git a/backend/src/generated/prisma.graphql b/backend/src/generated/prisma.graphql
index 5e2d471..6132328 100644
--- a/backend/src/generated/prisma.graphql
+++ b/backend/src/generated/prisma.graphql
@@ -5,6 +5,12 @@
# Model Types
#
+type CartItem implements Node {
+ id: ID!
+ quantity: Int!
+ item(where: ItemWhereInput): Item!
+}
+
type Item implements Node {
id: ID!
title: String!
@@ -31,6 +37,8 @@ type User implements Node {
website: String
age: Int
resetToken: String
+ resetTokenExpiry: String
+ cart(where: CartItemWhereInput, orderBy: CartItemOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): [CartItem!]
}
@@ -38,6 +46,10 @@ type User implements Node {
# Other Types
#
+type AggregateCartItem {
+ count: Int!
+}
+
type AggregateItem {
count: Int!
}
@@ -54,6 +66,104 @@ type BatchPayload {
count: Long!
}
+type CartItemConnection {
+ pageInfo: PageInfo!
+ edges: [CartItemEdge]!
+ aggregate: AggregateCartItem!
+}
+
+input CartItemCreateInput {
+ quantity: Int
+ item: ItemCreateOneInput!
+}
+
+input CartItemCreateManyInput {
+ create: [CartItemCreateInput!]
+ connect: [CartItemWhereUniqueInput!]
+}
+
+type CartItemEdge {
+ node: CartItem!
+ cursor: String!
+}
+
+enum CartItemOrderByInput {
+ id_ASC
+ id_DESC
+ quantity_ASC
+ quantity_DESC
+ updatedAt_ASC
+ updatedAt_DESC
+ createdAt_ASC
+ createdAt_DESC
+}
+
+type CartItemPreviousValues {
+ id: ID!
+ quantity: Int!
+}
+
+type CartItemSubscriptionPayload {
+ mutation: MutationType!
+ node: CartItem
+ updatedFields: [String!]
+ previousValues: CartItemPreviousValues
+}
+
+input CartItemSubscriptionWhereInput {
+ AND: [CartItemSubscriptionWhereInput!]
+ OR: [CartItemSubscriptionWhereInput!]
+ mutation_in: [MutationType!]
+ updatedFields_contains: String
+ updatedFields_contains_every: [String!]
+ updatedFields_contains_some: [String!]
+ node: CartItemWhereInput
+}
+
+input CartItemUpdateInput {
+ quantity: Int
+ item: ItemUpdateOneInput
+}
+
+input CartItemUpdateManyInput {
+ create: [CartItemCreateInput!]
+ connect: [CartItemWhereUniqueInput!]
+ disconnect: [CartItemWhereUniqueInput!]
+ delete: [CartItemWhereUniqueInput!]
+}
+
+input CartItemWhereInput {
+ AND: [CartItemWhereInput!]
+ OR: [CartItemWhereInput!]
+ id: ID
+ id_not: ID
+ id_in: [ID!]
+ id_not_in: [ID!]
+ id_lt: ID
+ id_lte: ID
+ id_gt: ID
+ id_gte: ID
+ id_contains: ID
+ id_not_contains: ID
+ id_starts_with: ID
+ id_not_starts_with: ID
+ id_ends_with: ID
+ id_not_ends_with: ID
+ quantity: Int
+ quantity_not: Int
+ quantity_in: [Int!]
+ quantity_not_in: [Int!]
+ quantity_lt: Int
+ quantity_lte: Int
+ quantity_gt: Int
+ quantity_gte: Int
+ item: ItemWhereInput
+}
+
+input CartItemWhereUniqueInput {
+ id: ID
+}
+
scalar DateTime
type ItemConnection {
@@ -68,6 +178,11 @@ input ItemCreateInput {
price: Int!
}
+input ItemCreateOneInput {
+ create: ItemCreateInput
+ connect: ItemWhereUniqueInput
+}
+
type ItemEdge {
node: Item!
cursor: String!
@@ -118,6 +233,13 @@ input ItemUpdateInput {
price: Int
}
+input ItemUpdateOneInput {
+ create: ItemCreateInput
+ connect: ItemWhereUniqueInput
+ disconnect: ItemWhereUniqueInput
+ delete: ItemWhereUniqueInput
+}
+
input ItemWhereInput {
AND: [ItemWhereInput!]
OR: [ItemWhereInput!]
@@ -182,21 +304,27 @@ scalar Long
type Mutation {
createPost(data: PostCreateInput!): Post!
createUser(data: UserCreateInput!): User!
+ createCartItem(data: CartItemCreateInput!): CartItem!
createItem(data: ItemCreateInput!): Item!
updatePost(data: PostUpdateInput!, where: PostWhereUniqueInput!): Post
updateUser(data: UserUpdateInput!, where: UserWhereUniqueInput!): User
+ updateCartItem(data: CartItemUpdateInput!, where: CartItemWhereUniqueInput!): CartItem
updateItem(data: ItemUpdateInput!, where: ItemWhereUniqueInput!): Item
deletePost(where: PostWhereUniqueInput!): Post
deleteUser(where: UserWhereUniqueInput!): User
+ deleteCartItem(where: CartItemWhereUniqueInput!): CartItem
deleteItem(where: ItemWhereUniqueInput!): Item
upsertPost(where: PostWhereUniqueInput!, create: PostCreateInput!, update: PostUpdateInput!): Post!
upsertUser(where: UserWhereUniqueInput!, create: UserCreateInput!, update: UserUpdateInput!): User!
+ upsertCartItem(where: CartItemWhereUniqueInput!, create: CartItemCreateInput!, update: CartItemUpdateInput!): CartItem!
upsertItem(where: ItemWhereUniqueInput!, create: ItemCreateInput!, update: ItemUpdateInput!): Item!
updateManyPosts(data: PostUpdateInput!, where: PostWhereInput!): BatchPayload!
updateManyUsers(data: UserUpdateInput!, where: UserWhereInput!): BatchPayload!
+ updateManyCartItems(data: CartItemUpdateInput!, where: CartItemWhereInput!): BatchPayload!
updateManyItems(data: ItemUpdateInput!, where: ItemWhereInput!): BatchPayload!
deleteManyPosts(where: PostWhereInput!): BatchPayload!
deleteManyUsers(where: UserWhereInput!): BatchPayload!
+ deleteManyCartItems(where: CartItemWhereInput!): BatchPayload!
deleteManyItems(where: ItemWhereInput!): BatchPayload!
}
@@ -393,12 +521,15 @@ input PostWhereUniqueInput {
type Query {
posts(where: PostWhereInput, orderBy: PostOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): [Post]!
users(where: UserWhereInput, orderBy: UserOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): [User]!
+ cartItems(where: CartItemWhereInput, orderBy: CartItemOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): [CartItem]!
items(where: ItemWhereInput, orderBy: ItemOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): [Item]!
post(where: PostWhereUniqueInput!): Post
user(where: UserWhereUniqueInput!): User
+ cartItem(where: CartItemWhereUniqueInput!): CartItem
item(where: ItemWhereUniqueInput!): Item
postsConnection(where: PostWhereInput, orderBy: PostOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): PostConnection!
usersConnection(where: UserWhereInput, orderBy: UserOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): UserConnection!
+ cartItemsConnection(where: CartItemWhereInput, orderBy: CartItemOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): CartItemConnection!
itemsConnection(where: ItemWhereInput, orderBy: ItemOrderByInput, skip: Int, after: String, before: String, first: Int, last: Int): ItemConnection!
node(id: ID!): Node
}
@@ -406,6 +537,7 @@ type Query {
type Subscription {
post(where: PostSubscriptionWhereInput): PostSubscriptionPayload
user(where: UserSubscriptionWhereInput): UserSubscriptionPayload
+ cartItem(where: CartItemSubscriptionWhereInput): CartItemSubscriptionPayload
item(where: ItemSubscriptionWhereInput): ItemSubscriptionPayload
}
@@ -422,7 +554,9 @@ input UserCreateInput {
website: String
age: Int
resetToken: String
+ resetTokenExpiry: String
posts: PostCreateManyWithoutAuthorInput
+ cart: CartItemCreateManyInput
}
input UserCreateOneWithoutPostsInput {
@@ -437,6 +571,8 @@ input UserCreateWithoutPostsInput {
website: String
age: Int
resetToken: String
+ resetTokenExpiry: String
+ cart: CartItemCreateManyInput
}
type UserEdge {
@@ -459,6 +595,8 @@ enum UserOrderByInput {
age_DESC
resetToken_ASC
resetToken_DESC
+ resetTokenExpiry_ASC
+ resetTokenExpiry_DESC
updatedAt_ASC
updatedAt_DESC
createdAt_ASC
@@ -473,6 +611,7 @@ type UserPreviousValues {
website: String
age: Int
resetToken: String
+ resetTokenExpiry: String
}
type UserSubscriptionPayload {
@@ -499,7 +638,9 @@ input UserUpdateInput {
website: String
age: Int
resetToken: String
+ resetTokenExpiry: String
posts: PostUpdateManyWithoutAuthorInput
+ cart: CartItemUpdateManyInput
}
input UserUpdateOneWithoutPostsInput {
@@ -518,6 +659,8 @@ input UserUpdateWithoutPostsDataInput {
website: String
age: Int
resetToken: String
+ resetTokenExpiry: String
+ cart: CartItemUpdateManyInput
}
input UserUpdateWithoutPostsInput {
@@ -626,9 +769,26 @@ input UserWhereInput {
resetToken_not_starts_with: String
resetToken_ends_with: String
resetToken_not_ends_with: String
+ resetTokenExpiry: String
+ resetTokenExpiry_not: String
+ resetTokenExpiry_in: [String!]
+ resetTokenExpiry_not_in: [String!]
+ resetTokenExpiry_lt: String
+ resetTokenExpiry_lte: String
+ resetTokenExpiry_gt: String
+ resetTokenExpiry_gte: String
+ resetTokenExpiry_contains: String
+ resetTokenExpiry_not_contains: String
+ resetTokenExpiry_starts_with: String
+ resetTokenExpiry_not_starts_with: String
+ resetTokenExpiry_ends_with: String
+ resetTokenExpiry_not_ends_with: String
posts_every: PostWhereInput
posts_some: PostWhereInput
posts_none: PostWhereInput
+ cart_every: CartItemWhereInput
+ cart_some: CartItemWhereInput
+ cart_none: CartItemWhereInput
}
input UserWhereUniqueInput {
diff --git a/backend/src/index.js b/backend/src/index.js
index fefe598..359456f 100644
--- a/backend/src/index.js
+++ b/backend/src/index.js
@@ -31,3 +31,8 @@ const server = new GraphQLServer({
// });
server.start(() => console.log('Server is running on http://localhost:4000'));
+
+// overwrite console.log
+const chalk = require('chalk');
+
+global.console.l = (...butta) => console.log(chalk.bold.yellow(...butta));
diff --git a/backend/src/mail.js b/backend/src/mail.js
new file mode 100644
index 0000000..3159fe2
--- /dev/null
+++ b/backend/src/mail.js
@@ -0,0 +1,12 @@
+const nodemailer = require('nodemailer');
+
+const transport = nodemailer.createTransport({
+ host: 'smtp.mailtrap.io',
+ port: 2525,
+ auth: {
+ user: 'c41bab08214808',
+ pass: '04992d4af4bdcf',
+ },
+});
+
+module.exports = transport;
diff --git a/backend/src/resolvers/Mutation.js b/backend/src/resolvers/Mutation.js
index 95be561..003a563 100644
--- a/backend/src/resolvers/Mutation.js
+++ b/backend/src/resolvers/Mutation.js
@@ -4,6 +4,7 @@ const { forwardTo } = require('prisma-binding');
const { getUserId, Context } = require('../utils');
const { randomBytes } = require('crypto');
const { promisify } = require('util');
+const mail = require('../mail');
const mutations = {
// Signup Mutations
@@ -136,16 +137,128 @@ const mutations = {
}
console.log(user);
// 2. Set a reset token, and a reset date
- // const resetToken = await promisify(randomBytes)(20);
- // const resetTokenExpiry = Date.now() + 3600000; // 1 hour from now
- // console.log({ resetToken, resetTokenExpiry });
- // const res = await ctx.db.mutation.updateUser({
- // where: { email: args.email },
- // data: { resetToken, resetTokenExpiry },
- // });
+ const resetToken = (await promisify(randomBytes)(20)).toString('hex');
+ const resetTokenExpiry = Date.now() + 3600000; // 1 hour from now
+ console.log({ resetToken, resetTokenExpiry });
+ const res = await ctx.db.mutation.updateUser({
+ where: { email: args.email },
+ data: { resetToken, resetTokenExpiry },
+ });
- // console.log(res);
+ console.log(res);
// 3. Send them their token via email
+ const mailRes = await mail.sendMail({
+ from: 'wesbos@gmail.com',
+ to: 'wesbos@gmail.com',
+ subject: 'Your password reset token',
+ html: `Here is your reset link: http://localhost:3000/reset?resetToken=${resetToken}`,
+ });
+
+ return res.updateUser;
+ },
+
+ async resetPassword(parent, args, ctx, info) {
+ // 1. Check that the passwords match
+ if (args.password !== args.confirmPassword) {
+ throw new Error('Passwords do not match');
+ }
+
+ // 2. Check that this is a legit resetToken
+ // 3. Check that it's not expired
+ // Note: If we didn't need the user here, we could also use db.exists()
+ const [user] = await ctx.db.query.users({
+ where: {
+ resetToken: args.resetToken,
+ resetTokenExpiry_gte: Date.now() - 3600000, // within the last hour
+ },
+ });
+
+ if (!user) {
+ throw new Error('This token is either invalid or expired.');
+ }
+
+ // 4. Hash the password
+ const password = await bcrypt.hash(args.password, 10);
+
+ // 5. Update the users password
+ // clean up the resetToken fields at the same time
+ const updatedUser = await ctx.db.mutation.updateUser({
+ where: { email: user.email },
+ data: {
+ password,
+ resetToken: null,
+ resetTokenExpiry: null,
+ },
+ });
+
+ // 6. send back the Auth Payload for the GraphQL request on the client
+ return {
+ // TODO: This should use sub instead of userId
+ token: jwt.sign({ userId: updatedUser.id }, process.env.APP_SECRET),
+ user: updatedUser,
+ };
+ },
+ /*
+ Add to cart
+ */
+ async addToCart(parent, args, ctx, info) {
+ console.l('Add to cart calle');
+ const userId = getUserId(ctx);
+ // get the current user
+ const currentUser = await ctx.db.query.user(
+ {
+ where: { id: userId },
+ },
+ '{ cart { id, quantity item { id } }}'
+ );
+
+ // find out if the user currently has this item in their cart
+ const existingCartItemIndex = currentUser.cart.findIndex(cartItem => cartItem.item.id === args.id);
+
+ if (existingCartItemIndex >= 0) {
+ console.l('======This item already exists in the cart============');
+ const cartItem = currentUser.cart[existingCartItemIndex];
+ return ctx.db.mutation.updateCartItem({
+ where: { id: cartItem.id },
+ data: {
+ quantity: cartItem.quantity + 1,
+ },
+ });
+ }
+
+ const res = await ctx.db.mutation.updateUser({
+ where: { id: userId },
+ data: {
+ cart: {
+ create: [
+ {
+ item: {
+ connect: {
+ id: args.id,
+ },
+ },
+ quantity: 1,
+ },
+ ],
+ },
+ },
+ });
+ console.log(res);
+ // return res;
+ },
+ async removeFromCart(parent, args, ctx, info) {
+ // delete that cart item
+ return ctx.db.mutation.deleteCartItem({
+ where: { id: args.id },
+ });
+ // const userId = getUserId(ctx);
+ // return ctx.db.query.user({ where: { id: userId } }, '{ id, cart { id, quantity }}');
+ // const cartItem = await ctx.db.query.cartItem({
+ // where: { id: args.id },
+ // });
+
+ // console.log(cartItem);
+ // return cartItem;
},
};
diff --git a/backend/src/schema.graphql b/backend/src/schema.graphql
index 8a2fa23..b30b096 100644
--- a/backend/src/schema.graphql
+++ b/backend/src/schema.graphql
@@ -1,4 +1,4 @@
-# import ItemWhereInput, ItemOrderByInput, allItems, Post, Item, ItemCreateInput, ItemOrderByInput, ItemWhereInput from './generated/prisma.graphql'
+# import CartItem, ItemWhereInput, ItemOrderByInput, allItems, Post, Item, ItemCreateInput, ItemOrderByInput, ItemWhereInput from './generated/prisma.graphql'
type Query {
feed: [Post!]!
@@ -28,6 +28,7 @@ type Mutation {
signup(email: String!, password: String!, name: String!): AuthPayload!
signin(email: String!, password: String!): AuthPayload!
requestReset(email: String!): User
+ resetPassword(resetToken: String!, password: String!, confirmPassword: String!): AuthPayload!
createDraft(title: String!, text: String!): Post!
publish(id: ID!): Post!
deletePost(id: ID!): Post!
@@ -35,6 +36,8 @@ type Mutation {
createItem(title: String, description: String, price: Int): Item!
deleteItem(id: ID!): Item!
updateItem(id: ID!, title: String, description: String, price: Int): Item!
+ addToCart(id: ID!): CartItem
+ removeFromCart(id: ID!): CartItem
}
type AuthPayload {
@@ -49,5 +52,5 @@ type User {
name: String!
posts: [Post!]!
age: Int
- resetToken: String
+ cart: [CartItem!]!
}