-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Update ecommerce example to match Wes' course
- Loading branch information
Showing
22 changed files
with
332 additions
and
552 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
'@keystone-next/example-ecommerce': major | ||
--- | ||
|
||
Updated all code to match the released version of Wes' course. |
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,5 @@ | ||
--- | ||
'@keystone-next/example-ecommerce': patch | ||
--- | ||
|
||
Added missing `returnFields` in `addToCart` resolver. |
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 |
---|---|---|
@@ -1,92 +1,82 @@ | ||
import { permissionsList } from './schemas/fields'; | ||
import { ListAccessArgs } from './types'; | ||
// At it's simplest, the access control returns a yes or no value depending on the users session | ||
|
||
/* | ||
The basic level of access to the system is being signed in as a valid user. This gives you access | ||
to the Admin UI, access to your own User and Todo items, and read access to roles. | ||
*/ | ||
export const isSignedIn = ({ session }: ListAccessArgs) => { | ||
export function isSignedIn({ session }: ListAccessArgs) { | ||
return !!session; | ||
}; | ||
} | ||
|
||
/* | ||
Permissions are shorthand functions for checking that the current user's role has the specified | ||
permission boolean set to true | ||
*/ | ||
const generatedPermissions = Object.fromEntries( | ||
permissionsList.map(permission => [ | ||
permission, | ||
function ({ session }: ListAccessArgs) { | ||
// Do they have that Permission? Yes or no | ||
return !!session?.data.role?.[permission]; | ||
}, | ||
]) | ||
); | ||
|
||
// Permissions check if someone meets a criteria - yes or no. | ||
export const permissions = { | ||
// We create a permission for each can* field on the Role type | ||
...generatedPermissions, | ||
// we can also add additional permissions as we need them | ||
isAwesome({ session }: ListAccessArgs) { | ||
if (session?.data.name?.includes('wes') || session?.data.name?.includes('jed')) { | ||
// they are awesome, let them have access | ||
return true; | ||
} | ||
return false; // not awesome, no access | ||
isAwesome({ session }: ListAccessArgs): boolean { | ||
return !!session?.data.name.includes('wes'); | ||
}, | ||
}; | ||
|
||
/* | ||
Rules are logical functions that can be used for list access, and may return a boolean (meaning | ||
all or no items are available) or a set of filters that limit the available items | ||
*/ | ||
// Rule based function | ||
// Rules can return a boolean - yes or no - or a filter which limits which products they can CRUD. | ||
export const rules = { | ||
canOrder: ({ session }: ListAccessArgs) => { | ||
if (!session) return false; // not signed in | ||
if (permissions.canManageCart(session)) return true; // if they have the permission | ||
// otherwise we only show them cart items that they own | ||
return { | ||
user: { id: session.itemId }, | ||
}; | ||
canManageProducts({ session }: ListAccessArgs) { | ||
if (!isSignedIn({ session })) { | ||
return false; | ||
} | ||
// 1. Do they have the permission of canManageProducts | ||
if (permissions.canManageProducts({ session })) { | ||
return true; | ||
} | ||
// 2. If not, do they own this item? | ||
return { user: { id: session?.itemId } }; | ||
}, | ||
canReadUsers: ({ session }: ListAccessArgs) => { | ||
if (!session) { | ||
// No session? No people. | ||
canOrder({ session }: ListAccessArgs) { | ||
if (!isSignedIn({ session })) { | ||
return false; | ||
} else if (session.data.role?.canSeeOtherUsers) { | ||
// Can see everyone | ||
} | ||
// 1. Do they have the permission of canManageProducts | ||
if (permissions.canManageCart({ session })) { | ||
return true; | ||
} else { | ||
// Can only see yourself | ||
return { id: session.itemId }; | ||
} | ||
// 2. If not, do they own this item? | ||
return { user: { id: session?.itemId } }; | ||
}, | ||
canUpdateUsers: ({ session }: ListAccessArgs) => { | ||
if (!session) { | ||
// No session? No people. | ||
canManageOrderItems({ session }: ListAccessArgs) { | ||
if (!isSignedIn({ session })) { | ||
return false; | ||
} else if (session.data.role?.canManageUsers) { | ||
// Can update everyone | ||
} | ||
// 1. Do they have the permission of canManageProducts | ||
if (permissions.canManageCart({ session })) { | ||
return true; | ||
} else { | ||
// Can update yourself | ||
return { id: session.itemId }; | ||
} | ||
// 2. If not, do they own this item? | ||
return { order: { user: { id: session?.itemId } } }; | ||
}, | ||
canUpdateProducts({ session }: ListAccessArgs) { | ||
// Do they have access? | ||
canReadProducts({ session }: ListAccessArgs) { | ||
if (!isSignedIn({ session })) { | ||
return false; | ||
} | ||
if (permissions.canManageProducts({ session })) { | ||
// They have the permission | ||
return true; | ||
return true; // They can read everything! | ||
} | ||
// Otherwise, only allow them to manage their own products | ||
return { user: { id: session?.itemId } }; | ||
// They should only see available products (based on the status field) | ||
return { status: 'AVAILABLE' }; | ||
}, | ||
canReadProducts: ({ session }: ListAccessArgs) => { | ||
if (session?.data.role?.canManageProducts) { | ||
canManageUsers({ session }: ListAccessArgs) { | ||
if (!isSignedIn({ session })) { | ||
return false; | ||
} | ||
if (permissions.canManageUsers({ session })) { | ||
return true; | ||
} else { | ||
return { status: 'AVAILABLE' }; | ||
} | ||
// Otherwise they may only update themselves! | ||
return { id: session?.itemId }; | ||
}, | ||
}; |
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 |
---|---|---|
@@ -1,14 +1,14 @@ | ||
import nodemailer from 'nodemailer'; | ||
import SMTPTransport from 'nodemailer/lib/smtp-transport'; | ||
import { createTransport, getTestMessageUrl } from 'nodemailer'; | ||
|
||
const transport = nodemailer.createTransport({ | ||
const transport = createTransport({ | ||
// @ts-ignore | ||
host: process.env.MAIL_HOST, | ||
port: process.env.MAIL_PORT, | ||
auth: { | ||
user: process.env.MAIL_USER, | ||
pass: process.env.MAIL_PASS, | ||
}, | ||
} as SMTPTransport.Options); | ||
}); | ||
|
||
function makeANiceEmail(text: string) { | ||
return ` | ||
|
@@ -27,4 +27,33 @@ function makeANiceEmail(text: string) { | |
`; | ||
} | ||
|
||
export { makeANiceEmail, transport }; | ||
export interface MailResponse { | ||
accepted?: string[] | null; | ||
rejected?: null[] | null; | ||
envelopeTime: number; | ||
messageTime: number; | ||
messageSize: number; | ||
response: string; | ||
envelope: Envelope; | ||
messageId: string; | ||
} | ||
export interface Envelope { | ||
from: string; | ||
to?: string[] | null; | ||
} | ||
|
||
export async function sendPasswordResetEmail(resetToken: string, to: string): Promise<void> { | ||
// email the user a token | ||
const info = (await transport.sendMail({ | ||
to, | ||
from: '[email protected]', | ||
subject: 'Your password reset token!', | ||
html: makeANiceEmail(`Your Password Reset Token is here! | ||
<a href="${process.env.FRONTEND_URL}/reset?token=${resetToken}">Click Here to reset</a> | ||
`), | ||
})) as MailResponse; | ||
if (process.env.MAIL_USER?.includes('ethereal.email')) { | ||
// @ts-ignore | ||
console.log(`� Message Sent! Preview it at ${getTestMessageUrl(info)}`); | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.