Initial project structure with sails.js
This commit is contained in:
55
api/controllers/account/logout.js
Normal file
55
api/controllers/account/logout.js
Normal file
@ -0,0 +1,55 @@
|
||||
module.exports = {
|
||||
|
||||
|
||||
friendlyName: 'Logout',
|
||||
|
||||
|
||||
description: 'Log out of this app.',
|
||||
|
||||
|
||||
extendedDescription:
|
||||
`This action deletes the \`req.session.userId\` key from the session of the requesting user agent.
|
||||
Actual garbage collection of session data depends on this app's session store, and
|
||||
potentially also on the [TTL configuration](https://sailsjs.com/docs/reference/configuration/sails-config-session)
|
||||
you provided for it.
|
||||
|
||||
Note that this action does not check to see whether or not the requesting user was
|
||||
actually logged in. (If they weren't, then this action is just a no-op.)`,
|
||||
|
||||
|
||||
exits: {
|
||||
|
||||
success: {
|
||||
description: 'The requesting user agent has been successfully logged out.'
|
||||
},
|
||||
|
||||
redirect: {
|
||||
description: 'The requesting user agent looks to be a web browser.',
|
||||
extendedDescription: 'After logging out from a web browser, the user is redirected away.',
|
||||
responseType: 'redirect'
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
|
||||
fn: async function () {
|
||||
|
||||
// Clear the `userId` property from this session.
|
||||
delete this.req.session.userId;
|
||||
|
||||
// Broadcast a message that we can display in other open tabs.
|
||||
if (sails.hooks.sockets) {
|
||||
await sails.helpers.broadcastSessionChange(this.req);
|
||||
}
|
||||
|
||||
// Then finish up, sending an appropriate response.
|
||||
// > Under the covers, this persists the now-logged-out session back
|
||||
// > to the underlying session store.
|
||||
if (!this.req.wantsJSON) {
|
||||
throw {redirect: '/login'};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
};
|
79
api/controllers/account/update-billing-card.js
Normal file
79
api/controllers/account/update-billing-card.js
Normal file
@ -0,0 +1,79 @@
|
||||
module.exports = {
|
||||
|
||||
|
||||
friendlyName: 'Update billing card',
|
||||
|
||||
|
||||
description: 'Update the credit card for the logged-in user.',
|
||||
|
||||
|
||||
inputs: {
|
||||
|
||||
stripeToken: {
|
||||
type: 'string',
|
||||
example: 'tok_199k3qEXw14QdSnRwmsK99MH',
|
||||
description: 'The single-use Stripe Checkout token identifier representing the user\'s payment source (i.e. credit card.)',
|
||||
extendedDescription: 'Omit this (or use "") to remove this user\'s payment source.',
|
||||
whereToGet: {
|
||||
description: 'This Stripe.js token is provided to the front-end (client-side) code after completing a Stripe Checkout or Stripe Elements flow.'
|
||||
}
|
||||
},
|
||||
|
||||
billingCardLast4: {
|
||||
type: 'string',
|
||||
example: '4242',
|
||||
description: 'Omit if removing card info.',
|
||||
whereToGet: { description: 'Credit card info is provided by Stripe after completing the checkout flow.' }
|
||||
},
|
||||
|
||||
billingCardBrand: {
|
||||
type: 'string',
|
||||
example: 'visa',
|
||||
description: 'Omit if removing card info.',
|
||||
whereToGet: { description: 'Credit card info is provided by Stripe after completing the checkout flow.' }
|
||||
},
|
||||
|
||||
billingCardExpMonth: {
|
||||
type: 'string',
|
||||
example: '08',
|
||||
description: 'Omit if removing card info.',
|
||||
whereToGet: { description: 'Credit card info is provided by Stripe after completing the checkout flow.' }
|
||||
},
|
||||
|
||||
billingCardExpYear: {
|
||||
type: 'string',
|
||||
example: '2023',
|
||||
description: 'Omit if removing card info.',
|
||||
whereToGet: { description: 'Credit card info is provided by Stripe after completing the checkout flow.' }
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
|
||||
fn: async function ({stripeToken, billingCardLast4, billingCardBrand, billingCardExpMonth, billingCardExpYear}) {
|
||||
|
||||
// Add, update, or remove the default payment source for the logged-in user's
|
||||
// customer entry in Stripe.
|
||||
var stripeCustomerId = await sails.helpers.stripe.saveBillingInfo.with({
|
||||
stripeCustomerId: this.req.me.stripeCustomerId,
|
||||
token: stripeToken || '',
|
||||
}).timeout(5000).retry();
|
||||
|
||||
// Update (or clear) the card info we have stored for this user in our database.
|
||||
// > Remember, never store complete card numbers-- only the last 4 digits + expiration!
|
||||
// > Storing (or even receiving) complete, unencrypted card numbers would require PCI
|
||||
// > compliance in the U.S.
|
||||
await User.updateOne({ id: this.req.me.id })
|
||||
.set({
|
||||
stripeCustomerId,
|
||||
hasBillingCard: stripeToken ? true : false,
|
||||
billingCardBrand: stripeToken ? billingCardBrand : '',
|
||||
billingCardLast4: stripeToken ? billingCardLast4 : '',
|
||||
billingCardExpMonth: stripeToken ? billingCardExpMonth : '',
|
||||
billingCardExpYear: stripeToken ? billingCardExpYear : ''
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
};
|
35
api/controllers/account/update-password.js
Normal file
35
api/controllers/account/update-password.js
Normal file
@ -0,0 +1,35 @@
|
||||
module.exports = {
|
||||
|
||||
|
||||
friendlyName: 'Update password',
|
||||
|
||||
|
||||
description: 'Update the password for the logged-in user.',
|
||||
|
||||
|
||||
inputs: {
|
||||
|
||||
password: {
|
||||
description: 'The new, unencrypted password.',
|
||||
example: 'abc123v2',
|
||||
required: true
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
|
||||
fn: async function ({password}) {
|
||||
|
||||
// Hash the new password.
|
||||
var hashed = await sails.helpers.passwords.hashPassword(password);
|
||||
|
||||
// Update the record for the logged-in user.
|
||||
await User.updateOne({ id: this.req.me.id })
|
||||
.set({
|
||||
password: hashed
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
};
|
160
api/controllers/account/update-profile.js
Normal file
160
api/controllers/account/update-profile.js
Normal file
@ -0,0 +1,160 @@
|
||||
module.exports = {
|
||||
|
||||
|
||||
friendlyName: 'Update profile',
|
||||
|
||||
|
||||
description: 'Update the profile for the logged-in user.',
|
||||
|
||||
|
||||
inputs: {
|
||||
|
||||
fullName: {
|
||||
type: 'string'
|
||||
},
|
||||
|
||||
emailAddress: {
|
||||
type: 'string'
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
|
||||
exits: {
|
||||
|
||||
emailAlreadyInUse: {
|
||||
statusCode: 409,
|
||||
description: 'The provided email address is already in use.',
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
|
||||
fn: async function ({fullName, emailAddress}) {
|
||||
|
||||
var newEmailAddress = emailAddress;
|
||||
if (newEmailAddress !== undefined) {
|
||||
newEmailAddress = newEmailAddress.toLowerCase();
|
||||
}
|
||||
|
||||
// Determine if this request wants to change the current user's email address,
|
||||
// revert her pending email address change, modify her pending email address
|
||||
// change, or if the email address won't be affected at all.
|
||||
var desiredEmailEffect;// ('change-immediately', 'begin-change', 'cancel-pending-change', 'modify-pending-change', or '')
|
||||
if (
|
||||
newEmailAddress === undefined ||
|
||||
(this.req.me.emailStatus !== 'change-requested' && newEmailAddress === this.req.me.emailAddress) ||
|
||||
(this.req.me.emailStatus === 'change-requested' && newEmailAddress === this.req.me.emailChangeCandidate)
|
||||
) {
|
||||
desiredEmailEffect = '';
|
||||
} else if (this.req.me.emailStatus === 'change-requested' && newEmailAddress === this.req.me.emailAddress) {
|
||||
desiredEmailEffect = 'cancel-pending-change';
|
||||
} else if (this.req.me.emailStatus === 'change-requested' && newEmailAddress !== this.req.me.emailAddress) {
|
||||
desiredEmailEffect = 'modify-pending-change';
|
||||
} else if (!sails.config.custom.verifyEmailAddresses || this.req.me.emailStatus === 'unconfirmed') {
|
||||
desiredEmailEffect = 'change-immediately';
|
||||
} else {
|
||||
desiredEmailEffect = 'begin-change';
|
||||
}
|
||||
|
||||
|
||||
// If the email address is changing, make sure it is not already being used.
|
||||
if (_.contains(['begin-change', 'change-immediately', 'modify-pending-change'], desiredEmailEffect)) {
|
||||
let conflictingUser = await User.findOne({
|
||||
or: [
|
||||
{ emailAddress: newEmailAddress },
|
||||
{ emailChangeCandidate: newEmailAddress }
|
||||
]
|
||||
});
|
||||
if (conflictingUser) {
|
||||
throw 'emailAlreadyInUse';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Start building the values to set in the db.
|
||||
// (We always set the fullName if provided.)
|
||||
var valuesToSet = {
|
||||
fullName,
|
||||
};
|
||||
|
||||
switch (desiredEmailEffect) {
|
||||
|
||||
// Change now
|
||||
case 'change-immediately':
|
||||
_.extend(valuesToSet, {
|
||||
emailAddress: newEmailAddress,
|
||||
emailChangeCandidate: '',
|
||||
emailProofToken: '',
|
||||
emailProofTokenExpiresAt: 0,
|
||||
emailStatus: this.req.me.emailStatus === 'unconfirmed' ? 'unconfirmed' : 'confirmed'
|
||||
});
|
||||
break;
|
||||
|
||||
// Begin new email change, or modify a pending email change
|
||||
case 'begin-change':
|
||||
case 'modify-pending-change':
|
||||
_.extend(valuesToSet, {
|
||||
emailChangeCandidate: newEmailAddress,
|
||||
emailProofToken: await sails.helpers.strings.random('url-friendly'),
|
||||
emailProofTokenExpiresAt: Date.now() + sails.config.custom.emailProofTokenTTL,
|
||||
emailStatus: 'change-requested'
|
||||
});
|
||||
break;
|
||||
|
||||
// Cancel pending email change
|
||||
case 'cancel-pending-change':
|
||||
_.extend(valuesToSet, {
|
||||
emailChangeCandidate: '',
|
||||
emailProofToken: '',
|
||||
emailProofTokenExpiresAt: 0,
|
||||
emailStatus: 'confirmed'
|
||||
});
|
||||
break;
|
||||
|
||||
// Otherwise, do nothing re: email
|
||||
}
|
||||
|
||||
// Save to the db
|
||||
await User.updateOne({id: this.req.me.id })
|
||||
.set(valuesToSet);
|
||||
|
||||
// If this is an immediate change, and billing features are enabled,
|
||||
// then also update the billing email for this user's linked customer entry
|
||||
// in the Stripe API to make sure they receive email receipts.
|
||||
// > Note: If there was not already a Stripe customer entry for this user,
|
||||
// > then one will be set up implicitly, so we'll need to persist it to our
|
||||
// > database. (This could happen if Stripe credentials were not configured
|
||||
// > at the time this user was originally created.)
|
||||
if(desiredEmailEffect === 'change-immediately' && sails.config.custom.enableBillingFeatures) {
|
||||
let didNotAlreadyHaveCustomerId = (! this.req.me.stripeCustomerId);
|
||||
let stripeCustomerId = await sails.helpers.stripe.saveBillingInfo.with({
|
||||
stripeCustomerId: this.req.me.stripeCustomerId,
|
||||
emailAddress: newEmailAddress
|
||||
}).timeout(5000).retry();
|
||||
if (didNotAlreadyHaveCustomerId){
|
||||
await User.updateOne({ id: this.req.me.id })
|
||||
.set({
|
||||
stripeCustomerId
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// If an email address change was requested, and re-confirmation is required,
|
||||
// send the "confirm account" email.
|
||||
if (desiredEmailEffect === 'begin-change' || desiredEmailEffect === 'modify-pending-change') {
|
||||
await sails.helpers.sendTemplateEmail.with({
|
||||
to: newEmailAddress,
|
||||
subject: 'Your account has been updated',
|
||||
template: 'email-verify-new-email',
|
||||
templateData: {
|
||||
fullName: fullName||this.req.me.fullName,
|
||||
token: valuesToSet.emailProofToken
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
};
|
30
api/controllers/account/view-account-overview.js
Normal file
30
api/controllers/account/view-account-overview.js
Normal file
@ -0,0 +1,30 @@
|
||||
module.exports = {
|
||||
|
||||
|
||||
friendlyName: 'View account overview',
|
||||
|
||||
|
||||
description: 'Display "Account Overview" page.',
|
||||
|
||||
|
||||
exits: {
|
||||
|
||||
success: {
|
||||
viewTemplatePath: 'pages/account/account-overview',
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
|
||||
fn: async function () {
|
||||
|
||||
// If billing features are enabled, include our configured Stripe.js
|
||||
// public key in the view locals. Otherwise, leave it as undefined.
|
||||
return {
|
||||
stripePublishableKey: sails.config.custom.enableBillingFeatures? sails.config.custom.stripePublishableKey : undefined,
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
};
|
26
api/controllers/account/view-edit-password.js
Normal file
26
api/controllers/account/view-edit-password.js
Normal file
@ -0,0 +1,26 @@
|
||||
module.exports = {
|
||||
|
||||
|
||||
friendlyName: 'View edit password',
|
||||
|
||||
|
||||
description: 'Display "Edit password" page.',
|
||||
|
||||
|
||||
exits: {
|
||||
|
||||
success: {
|
||||
viewTemplatePath: 'pages/account/edit-password'
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
|
||||
fn: async function () {
|
||||
|
||||
return {};
|
||||
|
||||
}
|
||||
|
||||
|
||||
};
|
26
api/controllers/account/view-edit-profile.js
Normal file
26
api/controllers/account/view-edit-profile.js
Normal file
@ -0,0 +1,26 @@
|
||||
module.exports = {
|
||||
|
||||
|
||||
friendlyName: 'View edit profile',
|
||||
|
||||
|
||||
description: 'Display "Edit profile" page.',
|
||||
|
||||
|
||||
exits: {
|
||||
|
||||
success: {
|
||||
viewTemplatePath: 'pages/account/edit-profile',
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
|
||||
fn: async function () {
|
||||
|
||||
return {};
|
||||
|
||||
}
|
||||
|
||||
|
||||
};
|
Reference in New Issue
Block a user