kaze 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +42 -0
- data/bin/kaze +11 -0
- data/lib/kaze/commands/install_command.rb +63 -0
- data/lib/kaze/commands/install_inertia_stacks.rb +151 -0
- data/lib/kaze/commands.rb +2 -0
- data/lib/kaze/version.rb +3 -0
- data/lib/kaze.rb +9 -0
- data/stubs/default/Procfile.dev +3 -0
- data/stubs/default/app/assets/stylesheets/application.css +1 -0
- data/stubs/default/app/assets/stylesheets/application.tailwind.css +3 -0
- data/stubs/default/app/controllers/application_controller.rb +5 -0
- data/stubs/default/app/controllers/auth/authenticated_session_controller.rb +28 -0
- data/stubs/default/app/controllers/auth/new_password_controller.rb +18 -0
- data/stubs/default/app/controllers/auth/password_reset_link_controller.rb +17 -0
- data/stubs/default/app/controllers/auth/registered_user_controller.rb +19 -0
- data/stubs/default/app/controllers/concerns/authenticate.rb +34 -0
- data/stubs/default/app/controllers/concerns/handle_inertia_requests.rb +9 -0
- data/stubs/default/app/controllers/concerns/verify_csrf_token.rb +24 -0
- data/stubs/default/app/controllers/dashboard_controller.rb +5 -0
- data/stubs/default/app/controllers/password_controller.rb +11 -0
- data/stubs/default/app/controllers/profile_controller.rb +31 -0
- data/stubs/default/app/controllers/welcome_controller.rb +10 -0
- data/stubs/default/app/forms/application_form.rb +9 -0
- data/stubs/default/app/forms/auth/login_form.rb +18 -0
- data/stubs/default/app/forms/auth/new_password_form.rb +21 -0
- data/stubs/default/app/forms/auth/register_form.rb +7 -0
- data/stubs/default/app/forms/auth/send_password_reset_link_form.rb +22 -0
- data/stubs/default/app/forms/delete_user_form.rb +5 -0
- data/stubs/default/app/forms/update_password_form.rb +6 -0
- data/stubs/default/app/forms/update_profile_information_form.rb +6 -0
- data/stubs/default/app/mailers/application_mailer.rb +11 -0
- data/stubs/default/app/mailers/user_mailer.rb +8 -0
- data/stubs/default/app/models/application_record.rb +3 -0
- data/stubs/default/app/models/concerns/can_reset_password.rb +5 -0
- data/stubs/default/app/models/current.rb +3 -0
- data/stubs/default/app/models/user.rb +11 -0
- data/stubs/default/app/validators/current_password_validator.rb +5 -0
- data/stubs/default/app/validators/email_validator.rb +7 -0
- data/stubs/default/app/validators/lowercase_validator.rb +5 -0
- data/stubs/default/app/validators/uniqueness_validator.rb +24 -0
- data/stubs/default/app/views/layouts/mailer.html.erb +374 -0
- data/stubs/default/app/views/layouts/mailer.text.erb +11 -0
- data/stubs/default/app/views/user_mailer/reset_password.html.erb +39 -0
- data/stubs/default/bin/dev +16 -0
- data/stubs/default/bin/vite +27 -0
- data/stubs/default/config/routes.rb +27 -0
- data/stubs/default/config/vite.json +16 -0
- data/stubs/default/db/migrate/20240101000000_create_users.rb +14 -0
- data/stubs/default/db/migrate/20240101000001_create_delayed_jobs.rb +22 -0
- data/stubs/inertia-react-ts/app/javascript/Components/ApplicationLogo.tsx +12 -0
- data/stubs/inertia-react-ts/app/javascript/Components/Checkbox.tsx +14 -0
- data/stubs/inertia-react-ts/app/javascript/Components/DangerButton.tsx +17 -0
- data/stubs/inertia-react-ts/app/javascript/Components/Dropdown.tsx +99 -0
- data/stubs/inertia-react-ts/app/javascript/Components/InputError.tsx +9 -0
- data/stubs/inertia-react-ts/app/javascript/Components/InputLabel.tsx +9 -0
- data/stubs/inertia-react-ts/app/javascript/Components/Modal.tsx +68 -0
- data/stubs/inertia-react-ts/app/javascript/Components/NavLink.tsx +18 -0
- data/stubs/inertia-react-ts/app/javascript/Components/PrimaryButton.tsx +17 -0
- data/stubs/inertia-react-ts/app/javascript/Components/ResponsiveNavLink.tsx +16 -0
- data/stubs/inertia-react-ts/app/javascript/Components/SecondaryButton.tsx +18 -0
- data/stubs/inertia-react-ts/app/javascript/Components/TextInput.tsx +30 -0
- data/stubs/inertia-react-ts/app/javascript/Layouts/AuthenticatedLayout.tsx +131 -0
- data/stubs/inertia-react-ts/app/javascript/Layouts/GuestLayout.tsx +19 -0
- data/stubs/inertia-react-ts/app/javascript/Pages/Auth/ForgotPassword.tsx +52 -0
- data/stubs/inertia-react-ts/app/javascript/Pages/Auth/Login.tsx +98 -0
- data/stubs/inertia-react-ts/app/javascript/Pages/Auth/Register.tsx +118 -0
- data/stubs/inertia-react-ts/app/javascript/Pages/Auth/ResetPassword.tsx +74 -0
- data/stubs/inertia-react-ts/app/javascript/Pages/Dashboard.tsx +22 -0
- data/stubs/inertia-react-ts/app/javascript/Pages/Profile/Edit.tsx +33 -0
- data/stubs/inertia-react-ts/app/javascript/Pages/Profile/Partials/DeleteUserForm.tsx +100 -0
- data/stubs/inertia-react-ts/app/javascript/Pages/Profile/Partials/UpdatePasswordForm.tsx +114 -0
- data/stubs/inertia-react-ts/app/javascript/Pages/Profile/Partials/UpdateProfileInformationForm.tsx +84 -0
- data/stubs/inertia-react-ts/app/javascript/Pages/Welcome.tsx +66 -0
- data/stubs/inertia-react-ts/app/javascript/entrypoints/application.tsx +34 -0
- data/stubs/inertia-react-ts/app/javascript/entrypoints/bootstrap.ts +4 -0
- data/stubs/inertia-react-ts/app/javascript/types/global.d.ts +7 -0
- data/stubs/inertia-react-ts/app/javascript/types/index.d.ts +12 -0
- data/stubs/inertia-react-ts/app/javascript/types/vite-env.d.ts +1 -0
- data/stubs/inertia-react-ts/app/views/layouts/application.html.erb +26 -0
- data/stubs/inertia-react-ts/config/tailwind.config.js +22 -0
- data/stubs/inertia-react-ts/package.json +26 -0
- data/stubs/inertia-react-ts/tsconfig.json +19 -0
- data/stubs/inertia-react-ts/vite.config.ts +13 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ApplicationLogo.vue +8 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/Checkbox.vue +29 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/DangerButton.vue +7 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/Dropdown.vue +75 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/DropdownLink.vue +16 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/InputError.vue +13 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/InputLabel.vue +12 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/Modal.vue +96 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/NavLink.vue +21 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/PrimaryButton.vue +7 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/ResponsiveNavLink.vue +21 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/SecondaryButton.vue +19 -0
- data/stubs/inertia-vue-ts/app/javascript/Components/TextInput.vue +23 -0
- data/stubs/inertia-vue-ts/app/javascript/Layouts/AuthenticatedLayout.vue +155 -0
- data/stubs/inertia-vue-ts/app/javascript/Layouts/GuestLayout.vue +20 -0
- data/stubs/inertia-vue-ts/app/javascript/Pages/Auth/ForgotPassword.vue +60 -0
- data/stubs/inertia-vue-ts/app/javascript/Pages/Auth/Login.vue +93 -0
- data/stubs/inertia-vue-ts/app/javascript/Pages/Auth/Register.vue +106 -0
- data/stubs/inertia-vue-ts/app/javascript/Pages/Auth/ResetPassword.vue +89 -0
- data/stubs/inertia-vue-ts/app/javascript/Pages/Dashboard.vue +22 -0
- data/stubs/inertia-vue-ts/app/javascript/Pages/Profile/Edit.vue +42 -0
- data/stubs/inertia-vue-ts/app/javascript/Pages/Profile/Partials/DeleteUserForm.vue +98 -0
- data/stubs/inertia-vue-ts/app/javascript/Pages/Profile/Partials/UpdatePasswordForm.vue +108 -0
- data/stubs/inertia-vue-ts/app/javascript/Pages/Profile/Partials/UpdateProfileInformationForm.vue +78 -0
- data/stubs/inertia-vue-ts/app/javascript/Pages/Welcome.vue +56 -0
- data/stubs/inertia-vue-ts/app/javascript/entrypoints/application.ts +34 -0
- data/stubs/inertia-vue-ts/app/javascript/entrypoints/bootstrap.ts +4 -0
- data/stubs/inertia-vue-ts/app/javascript/types/global.d.ts +13 -0
- data/stubs/inertia-vue-ts/app/javascript/types/index.d.ts +12 -0
- data/stubs/inertia-vue-ts/app/javascript/types/vite-env.d.ts +1 -0
- data/stubs/inertia-vue-ts/app/views/layouts/application.html.erb +25 -0
- data/stubs/inertia-vue-ts/config/tailwind.config.js +22 -0
- data/stubs/inertia-vue-ts/package.json +24 -0
- data/stubs/inertia-vue-ts/tsconfig.json +19 -0
- data/stubs/inertia-vue-ts/vite.config.ts +13 -0
- metadata +205 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: fa07032d843778439756cd1f5440d066f46e368ca699aadd2c96559721e9456d
|
4
|
+
data.tar.gz: e9a408babe88ac76f5d524b8ed4e35213eb2407a0135c422dfd1c0ee29ae5438
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5e6ca9927646f4aaca149938b413af65d8257f65a0883fe5dff23512f10829e77c865ba08295edca37c8293273443aaf75e60c2cab375fb1fdf5b766c5dcc105
|
7
|
+
data.tar.gz: 8824870ef8846a2c370cf64c06def95cb657032482dcd56521e63483c895175fa921c9658efb49dbde2f23770cbd8b58be3fa34fc279c924dc0bc2d57e0aad8b
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2024 Cuong Giang
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
# Kaze
|
2
|
+
|
3
|
+
Heavily inspired by [Laravel Breeze](https://github.com/laravel/breeze), this gem offers authentication and application starter kits to give you a head start building your new Rails application. These kits automatically scaffold your application with the routes, controllers, and views you need to register and authenticate your application's users.
|
4
|
+
|
5
|
+
[Kaze](https://github.com/gtkvn/kaze) is a minimal, simple implementation of all of Rails's authentication features, including login, registration, password reset. In addition, Kaze includes a simple "profile" page where the user may update their name, email address, and password.
|
6
|
+
|
7
|
+
Kaze provides scaffolding options based on Inertia, with the choice of using Vue or React for the Inertia-based scaffolding.
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
First, you should create a new Rails application, configure your database, and run your database migrations.
|
12
|
+
|
13
|
+
You may install Kaze globally with:
|
14
|
+
|
15
|
+
```
|
16
|
+
gem install kaze
|
17
|
+
```
|
18
|
+
|
19
|
+
Once Kaze is installed, you may scaffold your application using one of the Kaze "stacks" discussed in the documentation below.
|
20
|
+
|
21
|
+
## Kaze & React / Vue
|
22
|
+
|
23
|
+
Kaze offers React and Vue scaffolding via an Inertia frontend implementation. Inertia allows you to build modern, single-page React and Vue applications using classic server-side routing and controllers.
|
24
|
+
|
25
|
+
Inertia lets you enjoy the frontend power of React and Vue combined with the incredible backend productivity of Rails and lightning-fast Vite compilation. To use an Inertia stack, specify vue or react as your desired stack when executing the install command inside your app directory:
|
26
|
+
|
27
|
+
```
|
28
|
+
kaze install react
|
29
|
+
|
30
|
+
# Or...
|
31
|
+
|
32
|
+
kaze install vue
|
33
|
+
```
|
34
|
+
|
35
|
+
After Kaze's scaffolding is installed, you may start your application:
|
36
|
+
|
37
|
+
```
|
38
|
+
bin/setup
|
39
|
+
bin/dev
|
40
|
+
```
|
41
|
+
|
42
|
+
Next, you may navigate to your application's `/login` or `/register` URLs in your web browser.
|
data/bin/kaze
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
require "bundler"
|
2
|
+
require "fileutils"
|
3
|
+
require "open3"
|
4
|
+
require "thor"
|
5
|
+
|
6
|
+
class Kaze::Commands::InstallCommand < Thor
|
7
|
+
include Kaze::Commands::InstallInertiaStacks
|
8
|
+
|
9
|
+
desc "install [STACK]", "Install the Kaze controllers and resources. Supported stacks: react, vue."
|
10
|
+
def install(stack = "hotwire")
|
11
|
+
if stack == "react"
|
12
|
+
return install_inertia_react_stack
|
13
|
+
end
|
14
|
+
|
15
|
+
if stack == "vue"
|
16
|
+
return install_inertia_vue_stack
|
17
|
+
end
|
18
|
+
|
19
|
+
say "Invalid stack. Supported stacks are [react], [vue].", :red
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def require_gems(gems = [])
|
25
|
+
installed_gems = Bundler::Definition.build("#{Dir.pwd}/Gemfile", nil, {}).dependencies.map(&:name)
|
26
|
+
|
27
|
+
installing_gems = gems.map { |gem| gem unless installed_gems.include?(gem) }.compact
|
28
|
+
|
29
|
+
return true if installing_gems.empty?
|
30
|
+
|
31
|
+
status = run_command("bundle add #{installing_gems.join(" ")}")
|
32
|
+
|
33
|
+
status.success?
|
34
|
+
end
|
35
|
+
|
36
|
+
def install_migrations
|
37
|
+
ensure_directory_exists("#{Dir.pwd}/db/migrate")
|
38
|
+
FileUtils.copy_entry("#{File.dirname(__FILE__)}/../../../stubs/default/db/migrate", "#{Dir.pwd}/db/migrate")
|
39
|
+
stdin, _ = Open3.capture3("rails version")
|
40
|
+
versions = stdin.gsub!("Rails ", "").split(".")
|
41
|
+
Open3.capture3('grep -rl ActiveRecord::Migration$ db | xargs sed -i "" "s/ActiveRecord::Migration/ActiveRecord::Migration[' + [ versions[0], versions[1] ].join(".") + ']/g"')
|
42
|
+
end
|
43
|
+
|
44
|
+
def ensure_directory_exists(path)
|
45
|
+
FileUtils.mkdir_p(path) unless File.directory?(path)
|
46
|
+
end
|
47
|
+
|
48
|
+
def run_command(command)
|
49
|
+
Open3.popen3(command) do |stdin, stdout, stderr, wait_thr|
|
50
|
+
Thread.new do
|
51
|
+
stdout.each { |line| say line }
|
52
|
+
end
|
53
|
+
Thread.new do
|
54
|
+
stderr.each { |line| say line, :red }
|
55
|
+
end
|
56
|
+
wait_thr.value
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def run_commands(commands)
|
61
|
+
commands.each { |command| run_command(command) }
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,151 @@
|
|
1
|
+
module Kaze::Commands::InstallInertiaStacks
|
2
|
+
private
|
3
|
+
|
4
|
+
def install_inertia_react_stack
|
5
|
+
# Install Inertia...
|
6
|
+
return unless require_gems([ "propshaft", "tailwindcss-rails", "inertia_rails", "vite_rails", "dotenv", "bcrypt", "js-routes" ])
|
7
|
+
|
8
|
+
# NPM Packages...
|
9
|
+
FileUtils.copy_file("#{File.dirname(__FILE__)}/../../../stubs/inertia-react-ts/package.json", "#{Dir.pwd}/package.json")
|
10
|
+
|
11
|
+
# Controllers...
|
12
|
+
FileUtils.copy_entry("#{File.dirname(__FILE__)}/../../../stubs/default/app/controllers", "#{Dir.pwd}/app/controllers")
|
13
|
+
|
14
|
+
# Models...
|
15
|
+
FileUtils.copy_entry("#{File.dirname(__FILE__)}/../../../stubs/default/app/models", "#{Dir.pwd}/app/models")
|
16
|
+
|
17
|
+
# Forms...
|
18
|
+
ensure_directory_exists("#{Dir.pwd}/app/forms")
|
19
|
+
FileUtils.copy_entry("#{File.dirname(__FILE__)}/../../../stubs/default/app/forms", "#{Dir.pwd}/app/forms")
|
20
|
+
|
21
|
+
# Validators...
|
22
|
+
ensure_directory_exists("#{Dir.pwd}/app/validators")
|
23
|
+
FileUtils.copy_entry("#{File.dirname(__FILE__)}/../../../stubs/default/app/validators", "#{Dir.pwd}/app/validators")
|
24
|
+
|
25
|
+
# Views...
|
26
|
+
ensure_directory_exists("#{Dir.pwd}/app/views/layouts")
|
27
|
+
ensure_directory_exists("#{Dir.pwd}/app/views/user_mailer")
|
28
|
+
FileUtils.copy_file("#{File.dirname(__FILE__)}/../../../stubs/inertia-react-ts/app/views/layouts/application.html.erb", "#{Dir.pwd}/app/views/layouts/application.html.erb")
|
29
|
+
FileUtils.copy_file("#{File.dirname(__FILE__)}/../../../stubs/default/app/views/layouts/mailer.html.erb", "#{Dir.pwd}/app/views/layouts/mailer.html.erb")
|
30
|
+
FileUtils.copy_file("#{File.dirname(__FILE__)}/../../../stubs/default/app/views/layouts/mailer.text.erb", "#{Dir.pwd}/app/views/layouts/mailer.text.erb")
|
31
|
+
FileUtils.copy_file("#{File.dirname(__FILE__)}/../../../stubs/default/app/views/user_mailer/reset_password.html.erb", "#{Dir.pwd}/app/views/user_mailer/reset_password.html.erb")
|
32
|
+
|
33
|
+
# Mailers...
|
34
|
+
ensure_directory_exists("#{Dir.pwd}/app/mailers")
|
35
|
+
FileUtils.copy_entry("#{File.dirname(__FILE__)}/../../../stubs/default/app/mailers", "#{Dir.pwd}/app/mailers")
|
36
|
+
|
37
|
+
# Components + Pages...
|
38
|
+
ensure_directory_exists("#{Dir.pwd}/app/javascript")
|
39
|
+
FileUtils.copy_entry("#{File.dirname(__FILE__)}/../../../stubs/inertia-react-ts/app/javascript", "#{Dir.pwd}/app/javascript")
|
40
|
+
|
41
|
+
# Tests...
|
42
|
+
|
43
|
+
# Routes...
|
44
|
+
FileUtils.copy_file("#{File.dirname(__FILE__)}/../../../stubs/default/config/routes.rb", "#{Dir.pwd}/config/routes.rb")
|
45
|
+
|
46
|
+
# Migrations...
|
47
|
+
install_migrations
|
48
|
+
|
49
|
+
# Tailwind / Vite...
|
50
|
+
FileUtils.copy_file("#{File.dirname(__FILE__)}/../../../stubs/default/app/assets/stylesheets/application.css", "#{Dir.pwd}/app/assets/stylesheets/application.css")
|
51
|
+
FileUtils.copy_file("#{File.dirname(__FILE__)}/../../../stubs/default/app/assets/stylesheets/application.tailwind.css", "#{Dir.pwd}/app/assets/stylesheets/application.tailwind.css")
|
52
|
+
FileUtils.copy_file("#{File.dirname(__FILE__)}/../../../stubs/inertia-react-ts/config/tailwind.config.js", "#{Dir.pwd}/config/tailwind.config.js")
|
53
|
+
FileUtils.copy_file("#{File.dirname(__FILE__)}/../../../stubs/default/config/vite.json", "#{Dir.pwd}/config/vite.json")
|
54
|
+
FileUtils.copy_file("#{File.dirname(__FILE__)}/../../../stubs/inertia-react-ts/tsconfig.json", "#{Dir.pwd}/tsconfig.json")
|
55
|
+
FileUtils.copy_file("#{File.dirname(__FILE__)}/../../../stubs/inertia-react-ts/vite.config.ts", "#{Dir.pwd}/vite.config.ts")
|
56
|
+
FileUtils.copy_file("#{File.dirname(__FILE__)}/../../../stubs/default/bin/dev", "#{Dir.pwd}/bin/dev")
|
57
|
+
FileUtils.copy_file("#{File.dirname(__FILE__)}/../../../stubs/default/bin/vite", "#{Dir.pwd}/bin/vite")
|
58
|
+
FileUtils.copy_file("#{File.dirname(__FILE__)}/../../../stubs/default/Procfile.dev", "#{Dir.pwd}/Procfile.dev")
|
59
|
+
run_command("rails generate js_routes:middleware")
|
60
|
+
|
61
|
+
say ""
|
62
|
+
say "Installing and building Node dependencies.", :magenta
|
63
|
+
|
64
|
+
if File.exist?("#{Dir.pwd}/pnpm-lock.yaml")
|
65
|
+
run_commands([ "pnpm install", "pnpm run build" ])
|
66
|
+
elsif File.exist?("#{Dir.pwd}/yarn.lock")
|
67
|
+
run_commands([ "yarn install", "yarn build" ])
|
68
|
+
elsif File.exist?("#{Dir.pwd}/bun.lockb")
|
69
|
+
run_commands([ "bun install", "bun run build" ])
|
70
|
+
else
|
71
|
+
run_commands([ "npm install", "npm run build" ])
|
72
|
+
end
|
73
|
+
|
74
|
+
say ""
|
75
|
+
say "Kaze scaffolding installed successfully.", :green
|
76
|
+
end
|
77
|
+
|
78
|
+
def install_inertia_vue_stack
|
79
|
+
# Install Inertia...
|
80
|
+
return unless require_gems([ "propshaft", "tailwindcss-rails", "inertia_rails", "vite_rails", "dotenv", "bcrypt", "js-routes" ])
|
81
|
+
|
82
|
+
# NPM Packages...
|
83
|
+
FileUtils.copy_file("#{File.dirname(__FILE__)}/../../../stubs/inertia-vue-ts/package.json", "#{Dir.pwd}/package.json")
|
84
|
+
|
85
|
+
# Controllers...
|
86
|
+
FileUtils.copy_entry("#{File.dirname(__FILE__)}/../../../stubs/default/app/controllers", "#{Dir.pwd}/app/controllers")
|
87
|
+
|
88
|
+
# Models...
|
89
|
+
FileUtils.copy_entry("#{File.dirname(__FILE__)}/../../../stubs/default/app/models", "#{Dir.pwd}/app/models")
|
90
|
+
|
91
|
+
# Forms...
|
92
|
+
ensure_directory_exists("#{Dir.pwd}/app/forms")
|
93
|
+
FileUtils.copy_entry("#{File.dirname(__FILE__)}/../../../stubs/default/app/forms", "#{Dir.pwd}/app/forms")
|
94
|
+
|
95
|
+
# Validators...
|
96
|
+
ensure_directory_exists("#{Dir.pwd}/app/validators")
|
97
|
+
FileUtils.copy_entry("#{File.dirname(__FILE__)}/../../../stubs/default/app/validators", "#{Dir.pwd}/app/validators")
|
98
|
+
|
99
|
+
# Views...
|
100
|
+
ensure_directory_exists("#{Dir.pwd}/app/views/layouts")
|
101
|
+
ensure_directory_exists("#{Dir.pwd}/app/views/user_mailer")
|
102
|
+
FileUtils.copy_file("#{File.dirname(__FILE__)}/../../../stubs/inertia-vue-ts/app/views/layouts/application.html.erb", "#{Dir.pwd}/app/views/layouts/application.html.erb")
|
103
|
+
FileUtils.copy_file("#{File.dirname(__FILE__)}/../../../stubs/default/app/views/layouts/mailer.html.erb", "#{Dir.pwd}/app/views/layouts/mailer.html.erb")
|
104
|
+
FileUtils.copy_file("#{File.dirname(__FILE__)}/../../../stubs/default/app/views/layouts/mailer.text.erb", "#{Dir.pwd}/app/views/layouts/mailer.text.erb")
|
105
|
+
FileUtils.copy_file("#{File.dirname(__FILE__)}/../../../stubs/default/app/views/user_mailer/reset_password.html.erb", "#{Dir.pwd}/app/views/user_mailer/reset_password.html.erb")
|
106
|
+
|
107
|
+
# Mailers...
|
108
|
+
ensure_directory_exists("#{Dir.pwd}/app/mailers")
|
109
|
+
FileUtils.copy_entry("#{File.dirname(__FILE__)}/../../../stubs/default/app/mailers", "#{Dir.pwd}/app/mailers")
|
110
|
+
|
111
|
+
# Components + Pages...
|
112
|
+
ensure_directory_exists("#{Dir.pwd}/app/javascript")
|
113
|
+
FileUtils.copy_entry("#{File.dirname(__FILE__)}/../../../stubs/inertia-vue-ts/app/javascript", "#{Dir.pwd}/app/javascript")
|
114
|
+
|
115
|
+
# Tests...
|
116
|
+
|
117
|
+
# Routes...
|
118
|
+
FileUtils.copy_file("#{File.dirname(__FILE__)}/../../../stubs/default/config/routes.rb", "#{Dir.pwd}/config/routes.rb")
|
119
|
+
|
120
|
+
# Migrations...
|
121
|
+
install_migrations
|
122
|
+
|
123
|
+
# Tailwind / Vite...
|
124
|
+
FileUtils.copy_file("#{File.dirname(__FILE__)}/../../../stubs/default/app/assets/stylesheets/application.css", "#{Dir.pwd}/app/assets/stylesheets/application.css")
|
125
|
+
FileUtils.copy_file("#{File.dirname(__FILE__)}/../../../stubs/default/app/assets/stylesheets/application.tailwind.css", "#{Dir.pwd}/app/assets/stylesheets/application.tailwind.css")
|
126
|
+
FileUtils.copy_file("#{File.dirname(__FILE__)}/../../../stubs/inertia-vue-ts/config/tailwind.config.js", "#{Dir.pwd}/config/tailwind.config.js")
|
127
|
+
FileUtils.copy_file("#{File.dirname(__FILE__)}/../../../stubs/default/config/vite.json", "#{Dir.pwd}/config/vite.json")
|
128
|
+
FileUtils.copy_file("#{File.dirname(__FILE__)}/../../../stubs/inertia-vue-ts/tsconfig.json", "#{Dir.pwd}/tsconfig.json")
|
129
|
+
FileUtils.copy_file("#{File.dirname(__FILE__)}/../../../stubs/inertia-vue-ts/vite.config.ts", "#{Dir.pwd}/vite.config.ts")
|
130
|
+
FileUtils.copy_file("#{File.dirname(__FILE__)}/../../../stubs/default/bin/dev", "#{Dir.pwd}/bin/dev")
|
131
|
+
FileUtils.copy_file("#{File.dirname(__FILE__)}/../../../stubs/default/bin/vite", "#{Dir.pwd}/bin/vite")
|
132
|
+
FileUtils.copy_file("#{File.dirname(__FILE__)}/../../../stubs/default/Procfile.dev", "#{Dir.pwd}/Procfile.dev")
|
133
|
+
run_command("rails generate js_routes:middleware")
|
134
|
+
|
135
|
+
say ""
|
136
|
+
say "Installing and building Node dependencies.", :magenta
|
137
|
+
|
138
|
+
if File.exist?("#{Dir.pwd}/pnpm-lock.yaml")
|
139
|
+
run_commands([ "pnpm install", "pnpm run build" ])
|
140
|
+
elsif File.exist?("#{Dir.pwd}/yarn.lock")
|
141
|
+
run_commands([ "yarn install", "yarn build" ])
|
142
|
+
elsif File.exist?("#{Dir.pwd}/bun.lockb")
|
143
|
+
run_commands([ "bun install", "bun run build" ])
|
144
|
+
else
|
145
|
+
run_commands([ "npm install", "npm run build" ])
|
146
|
+
end
|
147
|
+
|
148
|
+
say ""
|
149
|
+
say "Kaze scaffolding installed successfully.", :green
|
150
|
+
end
|
151
|
+
end
|
data/lib/kaze/version.rb
ADDED
data/lib/kaze.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
/* Application styles */
|
@@ -0,0 +1,28 @@
|
|
1
|
+
class Auth::AuthenticatedSessionController < ApplicationController
|
2
|
+
skip_authentication only: %i[new create]
|
3
|
+
|
4
|
+
def new
|
5
|
+
render inertia: "Auth/Login", props: {
|
6
|
+
canResetPassword: true,
|
7
|
+
status: flash[:status]
|
8
|
+
}
|
9
|
+
end
|
10
|
+
|
11
|
+
def create
|
12
|
+
form = Auth::LoginForm.new params.permit(:email, :password)
|
13
|
+
|
14
|
+
user = form.authenticate
|
15
|
+
|
16
|
+
return redirect_back_or_to login_path, inertia: { errors: form.error_messages } if user.nil?
|
17
|
+
|
18
|
+
login user
|
19
|
+
|
20
|
+
redirect_to dashboard_path
|
21
|
+
end
|
22
|
+
|
23
|
+
def destroy
|
24
|
+
logout
|
25
|
+
|
26
|
+
redirect_to login_path
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class Auth::NewPasswordController < ApplicationController
|
2
|
+
skip_authentication
|
3
|
+
|
4
|
+
def new
|
5
|
+
render inertia: "Auth/ResetPassword", props: {
|
6
|
+
token: params[:token]
|
7
|
+
}
|
8
|
+
end
|
9
|
+
|
10
|
+
def create
|
11
|
+
form = Auth::NewPasswordForm.new params.permit(:token, :password, :password_confirmation)
|
12
|
+
|
13
|
+
return redirect_to login_path, flash: { status: "Your password has been reset." } if form.reset?
|
14
|
+
|
15
|
+
redirect_back_or_to password_reset_path(token: form.token),
|
16
|
+
inertia: { errors: form.error_messages }
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class Auth::PasswordResetLinkController < ApplicationController
|
2
|
+
skip_authentication
|
3
|
+
|
4
|
+
def new
|
5
|
+
render inertia: "Auth/ForgotPassword", props: {
|
6
|
+
status: flash[:status]
|
7
|
+
}
|
8
|
+
end
|
9
|
+
|
10
|
+
def create
|
11
|
+
form = Auth::SendPasswordResetLinkForm.new params.permit(:email)
|
12
|
+
|
13
|
+
return redirect_back_or_to password_request_path, inertia: { errors: form.error_messages } unless form.send_reset_link?
|
14
|
+
|
15
|
+
redirect_back_or_to password_request_path, flash: { status: "We have emailed your password reset link." }
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class Auth::RegisteredUserController < ApplicationController
|
2
|
+
skip_authentication
|
3
|
+
|
4
|
+
def new
|
5
|
+
render inertia: "Auth/Register"
|
6
|
+
end
|
7
|
+
|
8
|
+
def create
|
9
|
+
form = Auth::RegisterForm.new params.permit(:name, :email, :password, :password_confirmation)
|
10
|
+
|
11
|
+
return redirect_to register_path, inertia: { errors: form.error_messages } if form.invalid?
|
12
|
+
|
13
|
+
user = User.create(name: form.name, email: form.email, password: form.password)
|
14
|
+
|
15
|
+
login user
|
16
|
+
|
17
|
+
redirect_to dashboard_path
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Authenticate
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
included do
|
5
|
+
before_action :authenticate_user!
|
6
|
+
end
|
7
|
+
|
8
|
+
class_methods do
|
9
|
+
def skip_authentication(**options)
|
10
|
+
skip_before_action :authenticate_user!, **options
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def authenticate_user!
|
17
|
+
if user = User.find_by(id: session[:user_id])
|
18
|
+
Current.user = user
|
19
|
+
else
|
20
|
+
redirect_to login_path
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def login(user)
|
25
|
+
Current.user = user
|
26
|
+
reset_session
|
27
|
+
session[:user_id] = user.id
|
28
|
+
end
|
29
|
+
|
30
|
+
def logout
|
31
|
+
Current.user = nil
|
32
|
+
reset_session
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module VerifyCsrfToken
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
included do
|
5
|
+
before_action :set_csrf_cookie
|
6
|
+
|
7
|
+
rescue_from ActionController::InvalidAuthenticityToken do
|
8
|
+
redirect_back fallback_location: "/", notice: "The page expired, please try again."
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def request_authenticity_tokens
|
13
|
+
super << request.headers["HTTP_X_XSRF_TOKEN"]
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def set_csrf_cookie
|
19
|
+
cookies["XSRF-TOKEN"] = {
|
20
|
+
value: form_authenticity_token,
|
21
|
+
same_site: "Strict"
|
22
|
+
}
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
class PasswordController < ApplicationController
|
2
|
+
def update
|
3
|
+
form = UpdatePasswordForm.new params.permit(:current_password, :password, :password_confirmation)
|
4
|
+
|
5
|
+
return redirect_to profile_edit_path, inertia: { errors: form.error_messages } if form.invalid?
|
6
|
+
|
7
|
+
Current.user.update(password: form.password)
|
8
|
+
|
9
|
+
redirect_back_or_to profile_edit_path
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
class ProfileController < ApplicationController
|
2
|
+
def edit
|
3
|
+
render inertia: "Profile/Edit", props: {
|
4
|
+
status: session[:status]
|
5
|
+
}
|
6
|
+
end
|
7
|
+
|
8
|
+
def update
|
9
|
+
form = UpdateProfileInformationForm.new params.permit(:name, :email)
|
10
|
+
|
11
|
+
return redirect_to profile_edit_path, inertia: { errors: form.error_messages } if form.invalid?
|
12
|
+
|
13
|
+
Current.user.update(name: form.name, email: form.email)
|
14
|
+
|
15
|
+
redirect_to profile_edit_path
|
16
|
+
end
|
17
|
+
|
18
|
+
def destroy
|
19
|
+
form = DeleteUserForm.new params.permit(:password)
|
20
|
+
|
21
|
+
return redirect_back_or_to profile_edit_path, inertia: { errors: form.error_messages } if form.invalid?
|
22
|
+
|
23
|
+
user = Current.user
|
24
|
+
|
25
|
+
logout
|
26
|
+
|
27
|
+
user.delete
|
28
|
+
|
29
|
+
redirect_to "/"
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class Auth::LoginForm < ApplicationForm
|
2
|
+
attr_accessor :email, :password
|
3
|
+
|
4
|
+
validates :email, presence: true, email: true
|
5
|
+
validates :password, presence: true
|
6
|
+
|
7
|
+
def authenticate
|
8
|
+
return nil if invalid?
|
9
|
+
|
10
|
+
user = User.authenticate_by(email: email, password: password)
|
11
|
+
|
12
|
+
return user if user.present?
|
13
|
+
|
14
|
+
errors.add(:email, message: "These credentials do not match our records.")
|
15
|
+
|
16
|
+
nil
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class Auth::NewPasswordForm < ApplicationForm
|
2
|
+
attr_accessor :token, :password, :password_confirmation
|
3
|
+
|
4
|
+
validates :token, presence: true
|
5
|
+
validates :password, presence: true, confirmation: true, length: { minimum: 8 }
|
6
|
+
|
7
|
+
def reset?
|
8
|
+
return false if invalid?
|
9
|
+
|
10
|
+
user = User.find_by_token_for(:password_reset, token)
|
11
|
+
|
12
|
+
if user.nil?
|
13
|
+
errors.add(:password, message: "This password reset token is invalid.")
|
14
|
+
return false
|
15
|
+
end
|
16
|
+
|
17
|
+
user.update(password: password)
|
18
|
+
|
19
|
+
true
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,7 @@
|
|
1
|
+
class Auth::RegisterForm < ApplicationForm
|
2
|
+
attr_accessor :name, :email, :password, :password_confirmation
|
3
|
+
|
4
|
+
validates :name, presence: true
|
5
|
+
validates :email, presence: true, lowercase: true, email: true, uniqueness: { model: User, attribute: :email }
|
6
|
+
validates :password, presence: true, confirmation: true, length: { minimum: 8 }
|
7
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class Auth::SendPasswordResetLinkForm < ApplicationForm
|
2
|
+
attr_accessor :email
|
3
|
+
|
4
|
+
validates :email, presence: true, email: true
|
5
|
+
|
6
|
+
def send_reset_link?
|
7
|
+
return false if invalid?
|
8
|
+
|
9
|
+
user = User.find_by(email: email)
|
10
|
+
|
11
|
+
if user.nil?
|
12
|
+
errors.add(:email, message: "We can't find a user with that email address.")
|
13
|
+
return false
|
14
|
+
end
|
15
|
+
|
16
|
+
token = user.generate_token_for(:password_reset)
|
17
|
+
|
18
|
+
user.send_password_reset_notification(token)
|
19
|
+
|
20
|
+
true
|
21
|
+
end
|
22
|
+
end
|