plutonium 0.39.2 โ†’ 0.40.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.
Files changed (119) hide show
  1. checksums.yaml +4 -4
  2. data/.claude/skills/plutonium-connect-resource/SKILL.md +19 -1
  3. data/.claude/skills/plutonium-controller/SKILL.md +5 -9
  4. data/.claude/skills/plutonium-definition-query/SKILL.md +10 -2
  5. data/.claude/skills/plutonium-installation/SKILL.md +9 -7
  6. data/.claude/skills/plutonium-invites/SKILL.md +363 -0
  7. data/.claude/skills/plutonium-package/SKILL.md +2 -1
  8. data/.claude/skills/plutonium-portal/SKILL.md +30 -16
  9. data/.claude/skills/plutonium-rodauth/SKILL.md +111 -18
  10. data/CHANGELOG.md +43 -0
  11. data/app/assets/plutonium.css +1 -1
  12. data/config/initializers/sqlite_alias.rb +8 -8
  13. data/docs/.vitepress/config.ts +1 -0
  14. data/docs/getting-started/tutorial/07-author-portal.md +1 -0
  15. data/docs/getting-started/tutorial/08-customizing-ui.md +5 -2
  16. data/docs/guides/adding-resources.md +10 -0
  17. data/docs/guides/authentication.md +15 -8
  18. data/docs/guides/creating-packages.md +13 -8
  19. data/docs/guides/index.md +2 -0
  20. data/docs/guides/search-filtering.md +8 -3
  21. data/docs/guides/user-invites.md +497 -0
  22. data/docs/public/templates/base.rb +5 -1
  23. data/docs/public/templates/lite.rb +42 -0
  24. data/docs/public/templates/pluton8.rb +7 -2
  25. data/docs/reference/controller/index.md +12 -7
  26. data/docs/reference/definition/query.md +12 -3
  27. data/docs/reference/generators/index.md +70 -10
  28. data/docs/reference/portal/index.md +22 -11
  29. data/gemfiles/rails_7.gemfile.lock +1 -1
  30. data/gemfiles/rails_8.0.gemfile.lock +1 -1
  31. data/gemfiles/rails_8.1.gemfile.lock +1 -1
  32. data/lib/generators/pu/gem/active_shrine/active_shrine_generator.rb +31 -0
  33. data/lib/generators/pu/gem/active_shrine/templates/config/initializers/shrine.rb.tt +58 -0
  34. data/lib/generators/pu/gem/annotated/templates/lib/tasks/auto_annotate_models.rake +6 -1
  35. data/lib/generators/pu/gem/dotenv/templates/config/initializers/001_ensure_required_env.rb +3 -0
  36. data/lib/generators/pu/invites/USAGE +27 -0
  37. data/lib/generators/pu/invites/install_generator.rb +364 -0
  38. data/lib/generators/pu/invites/invitable/USAGE +31 -0
  39. data/lib/generators/pu/invites/invitable_generator.rb +143 -0
  40. data/lib/generators/pu/invites/templates/INSTRUCTIONS +22 -0
  41. data/lib/generators/pu/invites/templates/app/interactions/invite_user_interaction.rb.tt +24 -0
  42. data/lib/generators/pu/invites/templates/app/interactions/user_invite_user_interaction.rb.tt +26 -0
  43. data/lib/generators/pu/invites/templates/db/migrate/create_user_invites.rb.tt +47 -0
  44. data/lib/generators/pu/invites/templates/invitable/invitation.html.erb.tt +45 -0
  45. data/lib/generators/pu/invites/templates/invitable/invitation.text.erb.tt +15 -0
  46. data/lib/generators/pu/invites/templates/invitable/invite_user_interaction.rb.tt +33 -0
  47. data/lib/generators/pu/invites/templates/packages/invites/app/controllers/invites/user_invitations_controller.rb.tt +77 -0
  48. data/lib/generators/pu/invites/templates/packages/invites/app/controllers/invites/welcome_controller.rb.tt +68 -0
  49. data/lib/generators/pu/invites/templates/packages/invites/app/definitions/invites/user_invite_definition.rb.tt +23 -0
  50. data/lib/generators/pu/invites/templates/packages/invites/app/interactions/invites/cancel_invite_interaction.rb.tt +7 -0
  51. data/lib/generators/pu/invites/templates/packages/invites/app/interactions/invites/resend_invite_interaction.rb.tt +7 -0
  52. data/lib/generators/pu/invites/templates/packages/invites/app/mailers/invites/user_invite_mailer.rb.tt +34 -0
  53. data/lib/generators/pu/invites/templates/packages/invites/app/models/invites/user_invite.rb.tt +41 -0
  54. data/lib/generators/pu/invites/templates/packages/invites/app/policies/invites/user_invite_policy.rb.tt +33 -0
  55. data/lib/generators/pu/invites/templates/packages/invites/app/views/invites/user_invitations/error.html.erb.tt +24 -0
  56. data/lib/generators/pu/invites/templates/packages/invites/app/views/invites/user_invitations/landing.html.erb.tt +40 -0
  57. data/lib/generators/pu/invites/templates/packages/invites/app/views/invites/user_invitations/show.html.erb.tt +39 -0
  58. data/lib/generators/pu/invites/templates/packages/invites/app/views/invites/user_invitations/signup.html.erb.tt +49 -0
  59. data/lib/generators/pu/invites/templates/packages/invites/app/views/invites/user_invite_mailer/invitation.html.erb.tt +45 -0
  60. data/lib/generators/pu/invites/templates/packages/invites/app/views/invites/user_invite_mailer/invitation.text.erb.tt +15 -0
  61. data/lib/generators/pu/invites/templates/packages/invites/app/views/invites/welcome/pending_invitation.html.erb.tt +23 -0
  62. data/lib/generators/pu/invites/templates/packages/invites/app/views/layouts/invites/invitation.html.erb.tt +33 -0
  63. data/lib/generators/pu/lib/plutonium_generators/concerns/actions.rb +23 -2
  64. data/lib/generators/pu/lib/plutonium_generators/concerns/configures_sqlite.rb +130 -0
  65. data/lib/generators/pu/lib/plutonium_generators/concerns/mounts_engines.rb +72 -0
  66. data/lib/generators/pu/lib/plutonium_generators/concerns/package_selector.rb +4 -2
  67. data/lib/generators/pu/lib/plutonium_generators/model_generator_base.rb +7 -1
  68. data/lib/generators/pu/lite/litestream/litestream_generator.rb +105 -0
  69. data/lib/generators/pu/lite/rails_pulse/rails_pulse_generator.rb +88 -0
  70. data/lib/generators/pu/lite/rails_pulse/templates/config/initializers/rails_pulse.rb.tt +14 -0
  71. data/lib/generators/pu/lite/setup/setup_generator.rb +54 -0
  72. data/lib/generators/pu/lite/solid_cable/solid_cable_generator.rb +65 -0
  73. data/lib/generators/pu/lite/solid_cache/solid_cache_generator.rb +66 -0
  74. data/lib/generators/pu/lite/solid_errors/solid_errors_generator.rb +61 -0
  75. data/lib/generators/pu/lite/solid_queue/solid_queue_generator.rb +107 -0
  76. data/lib/generators/pu/pkg/portal/USAGE +8 -2
  77. data/lib/generators/pu/pkg/portal/portal_generator.rb +11 -1
  78. data/lib/generators/pu/pkg/portal/templates/app/controllers/concerns/controller.rb.tt +2 -0
  79. data/lib/generators/pu/pkg/portal/templates/app/controllers/plutonium_controller.rb.tt +1 -0
  80. data/lib/generators/pu/pkg/portal/templates/app/controllers/resource_controller.rb.tt +7 -0
  81. data/lib/generators/pu/pkg/portal/templates/lib/engine.rb.tt +3 -0
  82. data/lib/generators/pu/res/conn/USAGE +5 -0
  83. data/lib/generators/pu/res/conn/conn_generator.rb +30 -4
  84. data/lib/generators/pu/res/scaffold/scaffold_generator.rb +6 -3
  85. data/lib/generators/pu/res/scaffold/templates/policy.rb.tt +6 -6
  86. data/lib/generators/pu/rodauth/account_generator.rb +36 -11
  87. data/lib/generators/pu/rodauth/admin_generator.rb +55 -0
  88. data/lib/generators/pu/rodauth/install_generator.rb +1 -8
  89. data/lib/generators/pu/rodauth/templates/app/interactions/invite_admin_interaction.rb.tt +25 -0
  90. data/lib/generators/pu/rodauth/templates/app/models/account.rb.tt +6 -2
  91. data/lib/generators/pu/saas/USAGE +22 -0
  92. data/lib/generators/pu/saas/entity/USAGE +19 -0
  93. data/lib/generators/pu/saas/entity_generator.rb +55 -0
  94. data/lib/generators/pu/saas/membership/USAGE +25 -0
  95. data/lib/generators/pu/saas/membership_generator.rb +165 -0
  96. data/lib/generators/pu/saas/setup/USAGE +27 -0
  97. data/lib/generators/pu/saas/setup_generator.rb +98 -0
  98. data/lib/generators/pu/saas/user/USAGE +21 -0
  99. data/lib/generators/pu/saas/user_generator.rb +66 -0
  100. data/lib/plutonium/definition/base.rb +3 -1
  101. data/lib/plutonium/definition/scoping.rb +20 -0
  102. data/lib/plutonium/invites/concerns/cancel_invite.rb +44 -0
  103. data/lib/plutonium/invites/concerns/invitable.rb +98 -0
  104. data/lib/plutonium/invites/concerns/invite_token.rb +186 -0
  105. data/lib/plutonium/invites/concerns/invite_user.rb +147 -0
  106. data/lib/plutonium/invites/concerns/resend_invite.rb +66 -0
  107. data/lib/plutonium/invites/controller.rb +226 -0
  108. data/lib/plutonium/invites/pending_invite_check.rb +76 -0
  109. data/lib/plutonium/invites.rb +6 -0
  110. data/lib/plutonium/resource/controllers/queryable.rb +4 -0
  111. data/lib/plutonium/resource/query_object.rb +3 -5
  112. data/lib/plutonium/version.rb +1 -1
  113. data/package.json +1 -1
  114. metadata +64 -7
  115. data/lib/generators/pu/res/entity/entity_generator.rb +0 -158
  116. data/lib/generators/pu/rodauth/customer_generator.rb +0 -101
  117. data/public/plutonium-assets/plutonium-logo-original.png +0 -0
  118. data/public/plutonium-assets/plutonium-logo-white.png +0 -0
  119. data/public/plutonium-assets/plutonium-logo.png +0 -0
@@ -35,8 +35,8 @@ rails generate pu:rodauth:account user
35
35
  # Admin with 2FA and security features
36
36
  rails generate pu:rodauth:admin admin
37
37
 
38
- # Customer with entity association
39
- rails generate pu:rodauth:customer customer
38
+ # SaaS user with entity/organization (multi-tenant)
39
+ rails generate pu:saas:setup --user Customer --entity Organization
40
40
  ```
41
41
 
42
42
  ## Account Generators
@@ -94,40 +94,132 @@ Creates a secure admin account with:
94
94
  - Account lockout
95
95
  - Active sessions tracking
96
96
  - Audit logging
97
- - No public signup (accounts created via rake task)
97
+ - Role-based access control
98
+ - Invite interaction for adding new admins
99
+ - No public signup (accounts created via rake task or invite)
98
100
 
99
101
  ```bash
100
102
  rails generate pu:rodauth:admin admin
103
+ rails generate pu:rodauth:admin admin --roles=super_admin,admin,viewer
104
+ rails generate pu:rodauth:admin admin --extra-attributes=name:string,department:string
105
+ ```
106
+
107
+ **Options:**
108
+
109
+ | Option | Default | Description |
110
+ |--------|---------|-------------|
111
+ | `--roles` | super_admin,admin | Comma-separated roles for admin accounts |
112
+ | `--extra_attributes` | | Additional model attributes (e.g., name:string) |
113
+
114
+ **Generated role enum:**
115
+ ```ruby
116
+ # app/models/admin.rb
117
+ enum :role, super_admin: 0, admin: 1
118
+ ```
119
+
120
+ **Generated invite interaction:**
121
+ ```ruby
122
+ # app/interactions/admin/invite_interaction.rb
123
+ class Admin::InviteInteraction < Plutonium::Interaction::Base
124
+ attribute :email, :string
125
+ attribute :role, default: :admin # Second role is default
126
+
127
+ def execute
128
+ # Creates admin via internal request and sends invite email
129
+ end
130
+ end
101
131
  ```
102
132
 
103
133
  **Creates rake task:**
104
134
  ```bash
105
- # Create admin account
135
+ # Create admin account directly
106
136
  rails rodauth_admin:create[admin@example.com,password123]
107
137
  ```
108
138
 
109
- ### Customer Account (`pu:rodauth:customer`)
139
+ ### SaaS Setup (`pu:saas:setup`)
110
140
 
111
- Creates a customer account with an associated entity (organization/company):
141
+ Creates a complete multi-tenant SaaS setup with user account, entity, and membership:
112
142
 
113
143
  ```bash
114
- rails generate pu:rodauth:customer customer
115
- rails generate pu:rodauth:customer customer --entity=Organization
116
- rails generate pu:rodauth:customer customer --no-allow_signup
144
+ rails generate pu:saas:setup --user Customer --entity Organization
145
+ rails generate pu:saas:setup --user Customer --entity Organization --roles=member,admin,owner
146
+ rails generate pu:saas:setup --user Customer --entity Organization --no-allow-signup
147
+ rails generate pu:saas:setup --user Customer --entity Organization --user-attributes=name:string --entity-attributes=slug:string
117
148
  ```
118
149
 
119
150
  **Options:**
120
151
 
121
- | Option | Description |
122
- |--------|-------------|
123
- | `--entity=NAME` | Entity model name (default: "Entity") |
124
- | `--no-allow_signup` | Disable public registration |
152
+ | Option | Default | Description |
153
+ |--------|---------|-------------|
154
+ | `--user=NAME` | (required) | User account model name (e.g., Customer) |
155
+ | `--entity=NAME` | (required) | Entity model name (e.g., Organization) |
156
+ | `--allow-signup` | true | Allow public registration |
157
+ | `--roles` | member,owner | Comma-separated membership roles |
158
+ | `--skip-entity` | false | Skip entity model generation |
159
+ | `--skip-membership` | false | Skip membership model generation |
160
+ | `--user-attributes` | | Additional user model attributes |
161
+ | `--entity-attributes` | | Additional entity model attributes (name is always included) |
162
+ | `--membership-attributes` | | Additional membership model attributes |
163
+
164
+ **Individual Generators:**
165
+
166
+ You can also run each component separately:
167
+
168
+ ```bash
169
+ # Just the user account
170
+ rails g pu:saas:user Customer
171
+
172
+ # Just the entity model
173
+ rails g pu:saas:entity Organization
174
+
175
+ # Just the membership (requires user and entity to exist)
176
+ rails g pu:saas:membership --user Customer --entity Organization
177
+ ```
178
+
179
+ **Generated Models:**
180
+
181
+ 1. **User account** - The user model with Rodauth authentication
182
+ 2. **Entity model** - The organization/company with unique name
183
+ 3. **Membership model** - Join table `{entity}_{user}` (e.g., `OrganizationCustomer`)
125
184
 
126
- This creates:
127
- - Customer account model
128
- - Entity model (Organization, Company, etc.)
129
- - Membership join model
130
- - Has-many-through associations
185
+ ```ruby
186
+ # app/models/customer.rb
187
+ class Customer < ApplicationRecord
188
+ include Rodauth::Rails.model(:customer)
189
+
190
+ has_many :organization_customers, dependent: :destroy
191
+ has_many :organizations, through: :organization_customers
192
+ end
193
+
194
+ # app/models/organization.rb
195
+ class Organization < ApplicationRecord
196
+ has_many :organization_customers, dependent: :destroy
197
+ has_many :customers, through: :organization_customers
198
+ end
199
+
200
+ # app/models/organization_customer.rb
201
+ class OrganizationCustomer < ApplicationRecord
202
+ belongs_to :organization
203
+ belongs_to :customer
204
+
205
+ enum :role, member: 0, owner: 1
206
+
207
+ validates :customer, uniqueness: {
208
+ scope: :organization_id,
209
+ message: "is already a member of this organization"
210
+ }
211
+ end
212
+ ```
213
+
214
+ **Membership Roles:**
215
+
216
+ The membership model includes a role enum for access control within the entity:
217
+
218
+ ```ruby
219
+ membership = OrganizationCustomer.find_by(organization: org, customer: current_user)
220
+ membership.member? # Default role
221
+ membership.owner? # Admin role for the entity
222
+ ```
131
223
 
132
224
  ## Connecting Auth to Controllers
133
225
 
@@ -450,3 +542,4 @@ User.create!(
450
542
  - `plutonium-installation` - Initial Plutonium setup
451
543
  - `plutonium-portal` - Portal configuration
452
544
  - `plutonium-policy` - Authorization after authentication
545
+ - `plutonium-invites` - User invitation system for multi-tenant apps
data/CHANGELOG.md CHANGED
@@ -1,3 +1,46 @@
1
+ ## [0.40.0] - 2026-02-04
2
+
3
+ ### ๐Ÿš€ Features
4
+
5
+ - *(invites)* Add user invitation system for multi-tenant apps
6
+ - *(generators)* Add roles and extra_attributes options to rodauth generators
7
+ - *(generator)* Add --scope flag to portal generator for entity scoping
8
+ - *(generator)* Add --singular flag to pu:res:conn for singular resources
9
+ - *(generators)* Add pu:lite namespace for SQLite-based services
10
+ - *(generators)* Add pu:saas namespace for multi-tenant SaaS setup
11
+ - *(definition)* Add default_scope method for setting default query scope
12
+
13
+ ### ๐Ÿ› Bug Fixes
14
+
15
+ - *(rodauth)* Scope load_memory to account-specific paths
16
+ - *(generators)* Resolve Thor invoke caching in entity generator
17
+ - *(test)* Use dynamic migration versioning for Rails compatibility
18
+ - *(config)* Wrap SQLite alias in defined? check for compatibility
19
+ - *(test)* Add variants to product policy permitted attributes
20
+ - *(test)* Improve has_one url assertion in resource_url_for test
21
+ - *(generators)* Normalize CamelCase package names and validate resource records
22
+ - *(generators)* Dedupe namespace and read model attrs with --no-model
23
+ - *(templates)* Use local gem path when LOCAL=1
24
+ - *(generators)* Guard against envs gems during template execution
25
+ - *(generators)* Run db:prepare after config in lite generators
26
+ - *(generators)* Restore ResourceController to portal generator
27
+
28
+ ### ๐Ÿšœ Refactor
29
+
30
+ - *(generators)* Comment out default policy methods
31
+
32
+ ### ๐Ÿ“š Documentation
33
+
34
+ - *(skills)* Add invites skill and update rodauth skill
35
+ - *(guides)* Add user invites guide
36
+
37
+ ### ๐Ÿงช Testing
38
+
39
+ - *(generators)* Add tests for rodauth and invites generators
40
+
41
+ ### โš™๏ธ Miscellaneous Tasks
42
+
43
+ - Remove old assets
1
44
  ## [0.39.2] - 2026-01-27
2
45
 
3
46
  ### ๐Ÿ› Bug Fixes