Skip to content

Commit

Permalink
finish mellow vue (#113)
Browse files Browse the repository at this point in the history
* chore(mellow-vue): add license

* chore(mellow-vue): update README

* chore(mellow-vue): update introduction

* feat(mellow-vue): redirect to dashboard

* feat(mellow-vue): redirect to dashboard

* fix(mellow-vue): rename to view-home

* feat(mellow-vue): clean up route

* feat: update how the hero section looks

* feat(mellow-vue): update cards for home page

* feat: abstract layout for the app

* feat: adjust page to use Layout

* fix: adjust margin

* fix: typo fix

* feat: update title

* feat: update page titles

* feat: update footer

* fix: remove flex grow

* feat: add navigation to dashboard

* feat: add policy for dashboard

* feat: rename Layout to AppLayout

* fix: update use of Layout

* feat: use an auth folder to organize auth pages

* feat: remove type file

* feat: update page structure

* chore: make routes more readable

* feat: change migration strategy to alter

* feat: simplify logic for password recovery

* feat: style dashboard

* feat(mellow-vue): add page title

* feat(mellow-vue): add profile edit forms

* feat(mellow-vue): update app layout

* feat(mellow-vue): remove block

* feat(mellow-vue): add reuseable components

* chore(mellow-vue): update shipwright

* feat(mellow-vue): refactor to use input components

* chore(mellow-vue): remove custom defined types

* feat(mellow-vue): use GoogleButton

* feat(mellow-vue): implement GoogleButton

* feat(mellow-vue): change text to name

* feat(mellow-vue): use input components

* fix(mellow-vue): change name to fullName

* feat(mellow-vue): use input components

* fix(mellow-vue): remove unnecessary props

* feat(mellow-vue): add handling of back string in inertiaRedirect

* feat(mellow-vue): add route to delete profile

* feat(mellow-vue): add call out to install extension

* feat(mellow-vue): add template for new email confirmation

* fix(mellow-vue): remove await

* chore(mellow-vue): prettier

* fix(mellow-vue): resolve forgot password logic

* feat(mellow-vue): update logic to verify email

* feat(mellow-vue): add action to delete profile

* fix(mellow-vue): wrong link in email-verify-new-email

* fix(mellow-vue): make loading spinner stay in the center

* fix(mellow-vue): resolve issue with suffix-icon not showing

* feat(mellow-vue): add complete svg for suffix icon

* feat(mellow-vue): add action to update profile

* feat(mellow-vue): add onError handler

* feat(mellow-vue): fix logout issues

* feat(mellow-vue): make error message full width

* feat(mellow-vue): add router for logout

* fix(mellow-vue): wrong syntax for disabling button

* feat(mellow-vue): add action to delete account

* feat(mellow-vue): remove helper and use Sails Mail instead

* feat(mellow-vue): add form handler to delete account

* feat(mellow-vue): redirect to dashboard

* chore(mellow-vue): remove type entry

* feat(mellow-vue): remove Japa test runner

* feat(mellow-vue): setup test with native Node runner

* chore(mellow-vue): update all dependencies
  • Loading branch information
DominusKelvin authored Sep 18, 2024
1 parent 3863b01 commit 97dd53d
Show file tree
Hide file tree
Showing 55 changed files with 2,521 additions and 5,283 deletions.
21 changes: 21 additions & 0 deletions templates/mellow-vue/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2024 The Sailscasts Company

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
49 changes: 43 additions & 6 deletions templates/mellow-vue/README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,46 @@
# Vue
<div align="center">
<a href="https://sailscasts.com/boring"><img src="https://github.com/sailscastshq/boring-stack/blob/main/.github/banner.png" alt="The Boring JavaScript Stack"></a>
</div>

This template should help get you started developing with a modern Sails fullstack application with Sails and Vue 3.
# Mellow

This scaffold contains:
## Introduction

- Sails
- Vue
- Tailwind
Mellow provides a minimal and simple starting point for building fullstack Sails applications with authentication. Styled with Tailwind, Mellow ships with authentication Sails actions and Vue pages/components that can be easily customized based on your application's needs.

Mellow is powered by Tailwind and Vue and made possible by Inertia.js

Key features of Mellow include:

1. **Sails.js Backend**: Leveraging the power and simplicity of Sails.js for robust server-side operations.
2. **Vue.js Frontend**: Utilizing Vue.js for building dynamic and responsive user interfaces.
3. **Tailwind CSS Styling**: Employing Tailwind CSS for rapid and flexible UI development with utility-first classes.
4. **Inertia.js Integration**: Bridging the gap between the Sails.js backend and Vue.js frontend, allowing for SPA-like experiences without the complexity of building an API.
5. **Authentication Out-of-the-Box**: Providing pre-built authentication actions and pages that can be easily customized to fit your application's needs.

## Why "Mellow"?

The name "Mellow" reflects this template's philosophy:

1. **Ease of Use**: A smooth, hassle-free development experience.
2. **Balanced Approach**: Combining powerful tools without overwhelming complexity.
3. **Flexibility**: Adapting to your needs without rigid constraints.
4. **Stability**: Providing a calm, reliable foundation for your projects.

## Official Documentation

Documentation for Mellow can be found on [The Boring JavaScript Stack docs](https://docs.sailscasts.com/boring-stack/templates#mellow).

## Community and Support

Join our community of developers using The Boring JavaScript Stack:

- **Discord**: Real-time chat with other developers on our [Discord server](https://sailscasts.com/chat)
- **GitHub**: Report issues and contribute to the project on [GitHub](https://github.com/sailscasts/boring-stack/issues)
- **YouTube**: Watch tutorials and updates on our [YouTube channel](https://youtube.com/@sailscasts)

## License

Mellow is open-sourced software licensed under the MIT license. This means you're free to use, modify, and distribute the software, subject to the conditions of the MIT license. We encourage contributions from the community to help improve and evolve Mellow for everyone's benefit.

For full license details, please see the [LICENSE](LICENSE.md) file in the project repository.
2 changes: 1 addition & 1 deletion templates/mellow-vue/api/controllers/auth/callback.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ module.exports = {
}

req.session.userId = user.id
const urlToRedirectTo = '/'
const urlToRedirectTo = '/dashboard'
return exits.success(urlToRedirectTo)
})
}
Expand Down
9 changes: 5 additions & 4 deletions templates/mellow-vue/api/controllers/auth/forgot-password.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,14 @@ module.exports = {
},

fn: async function ({ email }) {
const user = await User.findOne({ email })
if (!user) {
return
const userExists = await User.count({ email: this.req.session.userEmail })
if (!userExists) {
return '/check-email'
}

const token = await sails.helpers.strings.random('url-friendly')

await User.update({ id: user.id }).set({
const user = await User.updateOne({ email }).set({
passwordResetToken: token,
passwordResetTokenExpiresAt:
Date.now() + sails.config.custom.passwordResetTokenTTL
Expand All @@ -46,6 +46,7 @@ module.exports = {
token
}
})

this.req.session.userEmail = user.email
return '/check-email'
}
Expand Down
4 changes: 2 additions & 2 deletions templates/mellow-vue/api/controllers/auth/login.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ and exposed as a shared data via loggedInUser prop.)`,
},

fn: async function ({ email, password, rememberMe }) {
var user = await User.findOne({
const user = await User.findOne({
email: email.toLowerCase()
})

Expand Down Expand Up @@ -76,6 +76,6 @@ and exposed as a shared data via loggedInUser prop.)`,
}

this.req.session.userId = user.id
return '/'
return '/dashboard'
}
}
19 changes: 12 additions & 7 deletions templates/mellow-vue/api/controllers/auth/resend-link.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,18 @@ module.exports = {
},

fn: async function () {
const unverifiedUser = await User.updateOne(this.req.session.userId).set({
emailStatus: 'unverified',
emailProofToken: sails.helpers.strings.random('url-friendly'),
emailProofTokenExpiresAt:
Date.now() + sails.config.custom.emailProofTokenTTL
})
if (!unverifiedUser) throw 'userNotFound'
const userExists = await User.count({ email: this.req.session.userEmail })
if (!userExists) {
return '/check-email'
}
const unverifiedUser = await User.updateOne(this.req.session.userEmail).set(
{
emailStatus: 'unverified',
emailProofToken: sails.helpers.strings.random('url-friendly'),
emailProofTokenExpiresAt:
Date.now() + sails.config.custom.emailProofTokenTTL
}
)

this.req.session.userId = unverifiedUser.id

Expand Down
7 changes: 3 additions & 4 deletions templates/mellow-vue/api/controllers/auth/signup.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,14 @@ module.exports = {

fn: async function ({ fullName, email: userEmail, password }) {
const email = userEmail.toLowerCase()

const emailProofToken = await sails.helpers.strings.random('url-friendly')
try {
unverifiedUser = await User.create({
email,
password,
fullName,
tosAcceptedByIp: this.req.ip,
emailProofToken: sails.helpers.strings.random('url-friendly'),
emailProofToken,
emailProofTokenExpiresAt:
Date.now() + sails.config.custom.emailProofTokenTTL
}).fetch()
Expand Down Expand Up @@ -73,8 +73,6 @@ module.exports = {
}
}

this.req.session.userEmail = unverifiedUser.email

await sails.helpers.mail.send.with({
subject: 'Verify your email',
template: 'email-verify-account',
Expand All @@ -84,6 +82,7 @@ module.exports = {
fullName: unverifiedUser.fullName
}
})
this.req.session.userEmail = unverifiedUser.email
return '/check-email'
}
}
4 changes: 3 additions & 1 deletion templates/mellow-vue/api/controllers/auth/verify-email.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,9 @@ module.exports = {
await User.updateOne({ id: user.id }).set({
emailStatus: 'confirmed',
emailProofToken: '',
emailProofTokenExpiresAt: 0
emailProofTokenExpiresAt: 0,
email: user.emailChangeCandidate,
emailChangeCandidate: ''
})
this.req.session.userId = user.id
return '/'
Expand Down
9 changes: 2 additions & 7 deletions templates/mellow-vue/api/controllers/auth/view-check-email.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,9 @@ module.exports = {
},

fn: async function () {
let message = null
if (this.req.get('referrer').includes('forgot-password')) {
message = `We sent a password reset link to ${this.req.session.userEmail}`
} else {
message = `We sent an email verification link to ${this.req.session.userEmail}`
}
let message = `We sent a link to the email address you provided. Please check your inbox and follow the instructions.`
return {
page: 'check-email',
page: 'auth/check-email',
props: {
message
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ module.exports = {
},

fn: async function () {
return { page: 'forgot-password' }
return { page: 'auth/forgot-password' }
}
}
2 changes: 1 addition & 1 deletion templates/mellow-vue/api/controllers/auth/view-login.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ module.exports = {
},

fn: async function () {
return { page: 'login' }
return { page: 'auth/login' }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,6 @@ module.exports = {
if (!user || user.passwordResetTokenExpiresAt <= Date.now()) {
throw 'invalidOrExpiredToken'
}
return { page: 'reset-password', props: { token } }
return { page: 'auth/reset-password', props: { token } }
}
}
2 changes: 1 addition & 1 deletion templates/mellow-vue/api/controllers/auth/view-signup.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ module.exports = {
},

fn: async function () {
return { page: 'signup' }
return { page: 'auth/signup' }
}
}
2 changes: 1 addition & 1 deletion templates/mellow-vue/api/controllers/auth/view-success.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ module.exports = {
pageHeading = 'Password reset successful'
}
return {
page: 'success',
page: 'auth/success',
props: {
pageTitle,
pageHeading,
Expand Down
15 changes: 15 additions & 0 deletions templates/mellow-vue/api/controllers/dashboard/view-dashboard.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module.exports = {
friendlyName: 'View dashboard',

description: 'Display "Dashboard" page.',

exits: {
success: {
responseType: 'inertia'
}
},

fn: async function () {
return { page: 'dashboard/index' }
}
}
52 changes: 52 additions & 0 deletions templates/mellow-vue/api/controllers/user/delete-profile.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
module.exports = {
friendlyName: 'Delete profile',

description:
"Delete the logged-in user's account after verifying the password.",

inputs: {
password: {
type: 'string',
required: true,
description: 'The current password of the user to verify before deletion.'
}
},

exits: {
success: {
responseType: 'inertiaRedirect',
description: 'User account deleted successfully.'
},
unauthorized: {
responseType: 'inertiaRedirect',
description: 'User is not logged in.'
}
},

fn: async function ({ password }) {
const userId = this.req.session.userId
const user = await User.findOne({ id: userId }).intercept(
'notFound',
() => {
delete this.req.session.userId
return { unauthorized: '/login' }
}
)

const passwordMatch = await sails.helpers.passwords
.checkPassword(password, user.password)
.intercept('incorrect', () => {
delete this.req.session.userId
return { unauthorized: '/login' }
})

await User.destroy({ id: userId }).intercept('error', (err) => {
sails.log.error('Error deleting account:', err)
throw 'error'
})

delete this.req.session.userId

return '/login'
}
}
2 changes: 1 addition & 1 deletion templates/mellow-vue/api/controllers/user/logout.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,6 @@ module.exports = {
fn: async function () {
sails.inertia.flushShared('loggedInUser')
delete this.req.session.userId
return '/'
return '/login'
}
}
Loading

0 comments on commit 97dd53d

Please sign in to comment.