kaze 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|