seams 0.1.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 (414) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +335 -0
  3. data/LICENSE +21 -0
  4. data/README.md +104 -0
  5. data/lib/generators/seams/accounts/accounts_generator.rb +272 -0
  6. data/lib/generators/seams/accounts/templates/README.md.tt +219 -0
  7. data/lib/generators/seams/accounts/templates/app/models/account.rb.tt +124 -0
  8. data/lib/generators/seams/accounts/templates/app/models/application_record.rb.tt +7 -0
  9. data/lib/generators/seams/accounts/templates/app/models/current.rb.tt +38 -0
  10. data/lib/generators/seams/accounts/templates/app/models/membership.rb.tt +114 -0
  11. data/lib/generators/seams/accounts/templates/config/routes.rb.tt +8 -0
  12. data/lib/generators/seams/accounts/templates/db/migrate/create_accounts.rb.tt +29 -0
  13. data/lib/generators/seams/accounts/templates/db/migrate/create_accounts_memberships.rb.tt +49 -0
  14. data/lib/generators/seams/accounts/templates/lib/accounts.rb.tt +21 -0
  15. data/lib/generators/seams/accounts/templates/lib/concerns/account_scoped.rb.tt +97 -0
  16. data/lib/generators/seams/accounts/templates/lib/concerns/authorization.rb.tt +105 -0
  17. data/lib/generators/seams/accounts/templates/lib/configuration.rb.tt +26 -0
  18. data/lib/generators/seams/accounts/templates/lib/engine.rb.tt +55 -0
  19. data/lib/generators/seams/accounts/templates/spec/factories/accounts.rb.tt +49 -0
  20. data/lib/generators/seams/accounts/templates/spec/models/accounts/account_spec.rb.tt +64 -0
  21. data/lib/generators/seams/accounts/templates/spec/models/accounts/membership_spec.rb.tt +99 -0
  22. data/lib/generators/seams/accounts/templates/spec/runtime/accounts_boot_spec.rb.tt +181 -0
  23. data/lib/generators/seams/admin/admin_generator.rb +852 -0
  24. data/lib/generators/seams/admin/templates/README.md.tt +266 -0
  25. data/lib/generators/seams/admin/templates/app/controllers/admin/accounts_controller.rb.tt +16 -0
  26. data/lib/generators/seams/admin/templates/app/controllers/admin/accounts_memberships_controller.rb.tt +16 -0
  27. data/lib/generators/seams/admin/templates/app/controllers/admin/application_controller.rb.tt +282 -0
  28. data/lib/generators/seams/admin/templates/app/controllers/admin/identities_controller.rb.tt +26 -0
  29. data/lib/generators/seams/admin/templates/app/controllers/admin/invitations_controller.rb.tt +14 -0
  30. data/lib/generators/seams/admin/templates/app/controllers/admin/invoices_controller.rb.tt +14 -0
  31. data/lib/generators/seams/admin/templates/app/controllers/admin/lifetime_passes_controller.rb.tt +14 -0
  32. data/lib/generators/seams/admin/templates/app/controllers/admin/notification_preferences_controller.rb.tt +15 -0
  33. data/lib/generators/seams/admin/templates/app/controllers/admin/notifications_controller.rb.tt +18 -0
  34. data/lib/generators/seams/admin/templates/app/controllers/admin/plans_controller.rb.tt +14 -0
  35. data/lib/generators/seams/admin/templates/app/controllers/admin/subscriptions_controller.rb.tt +14 -0
  36. data/lib/generators/seams/admin/templates/app/controllers/admin/teams_controller.rb.tt +14 -0
  37. data/lib/generators/seams/admin/templates/app/controllers/admin/teams_memberships_controller.rb.tt +16 -0
  38. data/lib/generators/seams/admin/templates/app/dashboards/admin/account_dashboard.rb.tt +50 -0
  39. data/lib/generators/seams/admin/templates/app/dashboards/admin/accounts_membership_dashboard.rb.tt +58 -0
  40. data/lib/generators/seams/admin/templates/app/dashboards/admin/identity_dashboard.rb.tt +48 -0
  41. data/lib/generators/seams/admin/templates/app/dashboards/admin/invitation_dashboard.rb.tt +51 -0
  42. data/lib/generators/seams/admin/templates/app/dashboards/admin/invoice_dashboard.rb.tt +67 -0
  43. data/lib/generators/seams/admin/templates/app/dashboards/admin/lifetime_pass_dashboard.rb.tt +65 -0
  44. data/lib/generators/seams/admin/templates/app/dashboards/admin/notification_dashboard.rb.tt +58 -0
  45. data/lib/generators/seams/admin/templates/app/dashboards/admin/notification_preference_dashboard.rb.tt +43 -0
  46. data/lib/generators/seams/admin/templates/app/dashboards/admin/plan_dashboard.rb.tt +72 -0
  47. data/lib/generators/seams/admin/templates/app/dashboards/admin/subscription_dashboard.rb.tt +59 -0
  48. data/lib/generators/seams/admin/templates/app/dashboards/admin/team_dashboard.rb.tt +39 -0
  49. data/lib/generators/seams/admin/templates/app/dashboards/admin/teams_membership_dashboard.rb.tt +43 -0
  50. data/lib/generators/seams/admin/templates/app/policies/admin/platform/account_policy.rb.tt +10 -0
  51. data/lib/generators/seams/admin/templates/app/policies/admin/platform/accounts_membership_policy.rb.tt +10 -0
  52. data/lib/generators/seams/admin/templates/app/policies/admin/platform/application_policy.rb.tt +85 -0
  53. data/lib/generators/seams/admin/templates/app/policies/admin/platform/identity_policy.rb.tt +18 -0
  54. data/lib/generators/seams/admin/templates/app/policies/admin/platform/invitation_policy.rb.tt +9 -0
  55. data/lib/generators/seams/admin/templates/app/policies/admin/platform/invoice_policy.rb.tt +9 -0
  56. data/lib/generators/seams/admin/templates/app/policies/admin/platform/lifetime_pass_policy.rb.tt +9 -0
  57. data/lib/generators/seams/admin/templates/app/policies/admin/platform/notification_policy.rb.tt +9 -0
  58. data/lib/generators/seams/admin/templates/app/policies/admin/platform/notification_preference_policy.rb.tt +9 -0
  59. data/lib/generators/seams/admin/templates/app/policies/admin/platform/plan_policy.rb.tt +11 -0
  60. data/lib/generators/seams/admin/templates/app/policies/admin/platform/subscription_policy.rb.tt +9 -0
  61. data/lib/generators/seams/admin/templates/app/policies/admin/platform/team_policy.rb.tt +9 -0
  62. data/lib/generators/seams/admin/templates/app/policies/admin/platform/teams_membership_policy.rb.tt +9 -0
  63. data/lib/generators/seams/admin/templates/app/policies/admin/tenant/account_policy.rb.tt +33 -0
  64. data/lib/generators/seams/admin/templates/app/policies/admin/tenant/accounts_membership_policy.rb.tt +24 -0
  65. data/lib/generators/seams/admin/templates/app/policies/admin/tenant/application_policy.rb.tt +169 -0
  66. data/lib/generators/seams/admin/templates/app/policies/admin/tenant/identity_policy.rb.tt +67 -0
  67. data/lib/generators/seams/admin/templates/app/policies/admin/tenant/invitation_policy.rb.tt +24 -0
  68. data/lib/generators/seams/admin/templates/app/policies/admin/tenant/invoice_policy.rb.tt +21 -0
  69. data/lib/generators/seams/admin/templates/app/policies/admin/tenant/lifetime_pass_policy.rb.tt +21 -0
  70. data/lib/generators/seams/admin/templates/app/policies/admin/tenant/notification_policy.rb.tt +25 -0
  71. data/lib/generators/seams/admin/templates/app/policies/admin/tenant/notification_preference_policy.rb.tt +23 -0
  72. data/lib/generators/seams/admin/templates/app/policies/admin/tenant/plan_policy.rb.tt +47 -0
  73. data/lib/generators/seams/admin/templates/app/policies/admin/tenant/subscription_policy.rb.tt +22 -0
  74. data/lib/generators/seams/admin/templates/app/policies/admin/tenant/team_policy.rb.tt +28 -0
  75. data/lib/generators/seams/admin/templates/app/policies/admin/tenant/teams_membership_policy.rb.tt +24 -0
  76. data/lib/generators/seams/admin/templates/config/routes.rb.tt +38 -0
  77. data/lib/generators/seams/admin/templates/lib/admin.rb.tt +36 -0
  78. data/lib/generators/seams/admin/templates/lib/concerns/authenticator.rb.tt +66 -0
  79. data/lib/generators/seams/admin/templates/lib/configuration.rb.tt +90 -0
  80. data/lib/generators/seams/admin/templates/lib/context.rb.tt +44 -0
  81. data/lib/generators/seams/admin/templates/lib/engine.rb.tt +68 -0
  82. data/lib/generators/seams/admin/templates/spec/factories/admin.rb.tt +10 -0
  83. data/lib/generators/seams/admin/templates/spec/runtime/admin_boot_spec.rb.tt +604 -0
  84. data/lib/generators/seams/auth/add_oauth_provider/add_oauth_provider_generator.rb +157 -0
  85. data/lib/generators/seams/auth/add_oauth_provider/templates/adapter.rb.tt +95 -0
  86. data/lib/generators/seams/auth/add_oauth_provider/templates/adapter_spec.rb.tt +58 -0
  87. data/lib/generators/seams/auth/auth_generator.rb +311 -0
  88. data/lib/generators/seams/auth/templates/README.md.tt +289 -0
  89. data/lib/generators/seams/auth/templates/app/controllers/oauth/callbacks_controller.rb.tt +80 -0
  90. data/lib/generators/seams/auth/templates/app/controllers/password_resets_controller.rb.tt +44 -0
  91. data/lib/generators/seams/auth/templates/app/controllers/registrations_controller.rb.tt +34 -0
  92. data/lib/generators/seams/auth/templates/app/controllers/sessions_controller.rb.tt +49 -0
  93. data/lib/generators/seams/auth/templates/app/jobs/application_job.rb.tt +7 -0
  94. data/lib/generators/seams/auth/templates/app/jobs/cleanup_expired_sessions_job.rb.tt +30 -0
  95. data/lib/generators/seams/auth/templates/app/mailers/passwords_mailer.rb.tt +15 -0
  96. data/lib/generators/seams/auth/templates/app/models/api_token.rb.tt +62 -0
  97. data/lib/generators/seams/auth/templates/app/models/application_record.rb.tt +7 -0
  98. data/lib/generators/seams/auth/templates/app/models/current.rb.tt +15 -0
  99. data/lib/generators/seams/auth/templates/app/models/identity.rb.tt +74 -0
  100. data/lib/generators/seams/auth/templates/app/models/oauth/provider.rb.tt +48 -0
  101. data/lib/generators/seams/auth/templates/app/models/session.rb.tt +28 -0
  102. data/lib/generators/seams/auth/templates/app/services/authenticate_identity.rb.tt +31 -0
  103. data/lib/generators/seams/auth/templates/app/services/generate_api_token.rb.tt +35 -0
  104. data/lib/generators/seams/auth/templates/app/services/oauth/authenticator.rb.tt +94 -0
  105. data/lib/generators/seams/auth/templates/app/services/register_identity.rb.tt +57 -0
  106. data/lib/generators/seams/auth/templates/app/services/reset_password.rb.tt +41 -0
  107. data/lib/generators/seams/auth/templates/app/services/revoke_api_token.rb.tt +38 -0
  108. data/lib/generators/seams/auth/templates/app/views/password_resets/edit.html.erb.tt +12 -0
  109. data/lib/generators/seams/auth/templates/app/views/password_resets/new.html.erb.tt +11 -0
  110. data/lib/generators/seams/auth/templates/app/views/passwords_mailer/reset_email.html.erb.tt +7 -0
  111. data/lib/generators/seams/auth/templates/app/views/registrations/new.html.erb.tt +26 -0
  112. data/lib/generators/seams/auth/templates/app/views/sessions/_oauth_buttons.html.erb.tt +18 -0
  113. data/lib/generators/seams/auth/templates/app/views/sessions/new.html.erb.tt +17 -0
  114. data/lib/generators/seams/auth/templates/config/routes.rb.tt +21 -0
  115. data/lib/generators/seams/auth/templates/db/migrate/create_auth_api_tokens.rb.tt +26 -0
  116. data/lib/generators/seams/auth/templates/db/migrate/create_auth_identities.rb.tt +29 -0
  117. data/lib/generators/seams/auth/templates/db/migrate/create_auth_oauth_providers.rb.tt +35 -0
  118. data/lib/generators/seams/auth/templates/db/migrate/create_auth_sessions.rb.tt +19 -0
  119. data/lib/generators/seams/auth/templates/lib/auth.rb.tt +39 -0
  120. data/lib/generators/seams/auth/templates/lib/concerns/api_authenticatable.rb.tt +58 -0
  121. data/lib/generators/seams/auth/templates/lib/concerns/authenticatable.rb.tt +32 -0
  122. data/lib/generators/seams/auth/templates/lib/concerns/authentication.rb.tt +60 -0
  123. data/lib/generators/seams/auth/templates/lib/configuration.rb.tt +45 -0
  124. data/lib/generators/seams/auth/templates/lib/engine.rb.tt +46 -0
  125. data/lib/generators/seams/auth/templates/lib/oauth/abstract.rb.tt +87 -0
  126. data/lib/generators/seams/auth/templates/lib/oauth/github.rb.tt +112 -0
  127. data/lib/generators/seams/auth/templates/lib/oauth/google.rb.tt +78 -0
  128. data/lib/generators/seams/auth/templates/lib/tasks/auth_pii.rake.tt +68 -0
  129. data/lib/generators/seams/auth/templates/spec/factories/auth.rb.tt +38 -0
  130. data/lib/generators/seams/auth/templates/spec/mailers/passwords_mailer_spec.rb.tt +37 -0
  131. data/lib/generators/seams/auth/templates/spec/models/api_token_spec.rb.tt +84 -0
  132. data/lib/generators/seams/auth/templates/spec/models/identity_spec.rb.tt +56 -0
  133. data/lib/generators/seams/auth/templates/spec/models/oauth/provider_spec.rb.tt +64 -0
  134. data/lib/generators/seams/auth/templates/spec/models/session_spec.rb.tt +34 -0
  135. data/lib/generators/seams/auth/templates/spec/runtime/boot_spec.rb.tt +30 -0
  136. data/lib/generators/seams/auth/templates/spec/runtime/event_payload_spec.rb.tt +29 -0
  137. data/lib/generators/seams/auth/templates/spec/runtime/login_flow_spec.rb.tt +45 -0
  138. data/lib/generators/seams/billing/billing_generator.rb +476 -0
  139. data/lib/generators/seams/billing/templates/README.md.tt +355 -0
  140. data/lib/generators/seams/billing/templates/app/controllers/admin/lifetime_passes_controller.rb.tt +84 -0
  141. data/lib/generators/seams/billing/templates/app/controllers/checkout_controller.rb.tt +92 -0
  142. data/lib/generators/seams/billing/templates/app/controllers/invoices_controller.rb.tt +63 -0
  143. data/lib/generators/seams/billing/templates/app/controllers/plans_controller.rb.tt +14 -0
  144. data/lib/generators/seams/billing/templates/app/controllers/portal_controller.rb.tt +45 -0
  145. data/lib/generators/seams/billing/templates/app/controllers/subscriptions_controller.rb.tt +119 -0
  146. data/lib/generators/seams/billing/templates/app/controllers/webhooks_controller.rb.tt +98 -0
  147. data/lib/generators/seams/billing/templates/app/helpers/currency_helper.rb.tt +44 -0
  148. data/lib/generators/seams/billing/templates/app/jobs/application_job.rb.tt +6 -0
  149. data/lib/generators/seams/billing/templates/app/jobs/cancel_subscription_job.rb.tt +39 -0
  150. data/lib/generators/seams/billing/templates/app/jobs/start_subscription_job.rb.tt +32 -0
  151. data/lib/generators/seams/billing/templates/app/jobs/webhooks/process_event_job.rb.tt +37 -0
  152. data/lib/generators/seams/billing/templates/app/models/application_record.rb.tt +7 -0
  153. data/lib/generators/seams/billing/templates/app/models/invoice.rb.tt +35 -0
  154. data/lib/generators/seams/billing/templates/app/models/lifetime_pass.rb.tt +60 -0
  155. data/lib/generators/seams/billing/templates/app/models/plan.rb.tt +95 -0
  156. data/lib/generators/seams/billing/templates/app/models/subscription.rb.tt +31 -0
  157. data/lib/generators/seams/billing/templates/app/models/webhook_event.rb.tt +13 -0
  158. data/lib/generators/seams/billing/templates/app/services/checkout_session_service.rb.tt +25 -0
  159. data/lib/generators/seams/billing/templates/app/services/customers/find_or_create_service.rb.tt +73 -0
  160. data/lib/generators/seams/billing/templates/app/services/invoices/sync_service.rb.tt +50 -0
  161. data/lib/generators/seams/billing/templates/app/services/lifetime/create_lifetime_session_service.rb.tt +82 -0
  162. data/lib/generators/seams/billing/templates/app/services/lifetime/create_pass_from_checkout_service.rb.tt +88 -0
  163. data/lib/generators/seams/billing/templates/app/services/lifetime/grant_pass_service.rb.tt +80 -0
  164. data/lib/generators/seams/billing/templates/app/services/lifetime/revoke_pass_service.rb.tt +59 -0
  165. data/lib/generators/seams/billing/templates/app/services/portal_session_service.rb.tt +23 -0
  166. data/lib/generators/seams/billing/templates/app/services/service_result.rb.tt +38 -0
  167. data/lib/generators/seams/billing/templates/app/services/stripe_service.rb.tt +67 -0
  168. data/lib/generators/seams/billing/templates/app/services/subscriptions/cancel_service.rb.tt +42 -0
  169. data/lib/generators/seams/billing/templates/app/services/subscriptions/change_plan_service.rb.tt +48 -0
  170. data/lib/generators/seams/billing/templates/app/services/subscriptions/reactivate_service.rb.tt +28 -0
  171. data/lib/generators/seams/billing/templates/app/services/webhooks/event_router.rb.tt +54 -0
  172. data/lib/generators/seams/billing/templates/app/services/webhooks/handler.rb.tt +93 -0
  173. data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/charge_refunded_handler.rb.tt +18 -0
  174. data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/checkout_session_completed_handler.rb.tt +58 -0
  175. data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/invoice_created_handler.rb.tt +16 -0
  176. data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/invoice_finalized_handler.rb.tt +14 -0
  177. data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/invoice_handler_base.rb.tt +80 -0
  178. data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/invoice_paid_handler.rb.tt +12 -0
  179. data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/invoice_payment_failed_handler.rb.tt +12 -0
  180. data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/invoice_voided_handler.rb.tt +12 -0
  181. data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/payment_failed_handler.rb.tt +15 -0
  182. data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/payment_succeeded_handler.rb.tt +19 -0
  183. data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/subscription_created_handler.rb.tt +11 -0
  184. data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/subscription_deleted_handler.rb.tt +15 -0
  185. data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/subscription_handler_base.rb.tt +92 -0
  186. data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/subscription_trial_will_end_handler.rb.tt +15 -0
  187. data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/subscription_updated_handler.rb.tt +11 -0
  188. data/lib/generators/seams/billing/templates/app/views/admin/lifetime_passes/index.html.erb.tt +36 -0
  189. data/lib/generators/seams/billing/templates/app/views/admin/lifetime_passes/new.html.erb.tt +37 -0
  190. data/lib/generators/seams/billing/templates/app/views/checkout/success.html.erb.tt +5 -0
  191. data/lib/generators/seams/billing/templates/app/views/invoices/index.html.erb.tt +22 -0
  192. data/lib/generators/seams/billing/templates/app/views/invoices/show.html.erb.tt +14 -0
  193. data/lib/generators/seams/billing/templates/app/views/plans/index.html.erb.tt +51 -0
  194. data/lib/generators/seams/billing/templates/app/views/subscriptions/index.html.erb.tt +16 -0
  195. data/lib/generators/seams/billing/templates/app/views/subscriptions/show.html.erb.tt +25 -0
  196. data/lib/generators/seams/billing/templates/config/routes.rb.tt +39 -0
  197. data/lib/generators/seams/billing/templates/db/migrate/create_billing_invoices.rb.tt +32 -0
  198. data/lib/generators/seams/billing/templates/db/migrate/create_billing_lifetime_passes.rb.tt +43 -0
  199. data/lib/generators/seams/billing/templates/db/migrate/create_billing_plans.rb.tt +31 -0
  200. data/lib/generators/seams/billing/templates/db/migrate/create_billing_subscriptions.rb.tt +33 -0
  201. data/lib/generators/seams/billing/templates/db/migrate/create_billing_webhook_events.rb.tt +24 -0
  202. data/lib/generators/seams/billing/templates/lib/billing.rb.tt +34 -0
  203. data/lib/generators/seams/billing/templates/lib/concerns/billable.rb.tt +100 -0
  204. data/lib/generators/seams/billing/templates/lib/configuration.rb.tt +52 -0
  205. data/lib/generators/seams/billing/templates/lib/engine.rb.tt +72 -0
  206. data/lib/generators/seams/billing/templates/lib/gateways/abstract.rb.tt +65 -0
  207. data/lib/generators/seams/billing/templates/lib/gateways/adyen.rb.tt +16 -0
  208. data/lib/generators/seams/billing/templates/lib/gateways/paddle.rb.tt +22 -0
  209. data/lib/generators/seams/billing/templates/lib/gateways/stripe.rb.tt +155 -0
  210. data/lib/generators/seams/billing/templates/lib/stripe/client.rb.tt +101 -0
  211. data/lib/generators/seams/billing/templates/lib/stripe/webhook_signature.rb.tt +43 -0
  212. data/lib/generators/seams/billing/templates/lib/tasks/billing_check.rake.tt +34 -0
  213. data/lib/generators/seams/billing/templates/spec/factories/billing.rb.tt +65 -0
  214. data/lib/generators/seams/billing/templates/spec/fixtures/stripe/charge_refunded.json.tt +19 -0
  215. data/lib/generators/seams/billing/templates/spec/fixtures/stripe/checkout_session_completed.json.tt +17 -0
  216. data/lib/generators/seams/billing/templates/spec/fixtures/stripe/customer_subscription_created.json.tt +25 -0
  217. data/lib/generators/seams/billing/templates/spec/fixtures/stripe/customer_subscription_deleted.json.tt +17 -0
  218. data/lib/generators/seams/billing/templates/spec/fixtures/stripe/customer_subscription_trial_will_end.json.tt +17 -0
  219. data/lib/generators/seams/billing/templates/spec/fixtures/stripe/customer_subscription_updated.json.tt +28 -0
  220. data/lib/generators/seams/billing/templates/spec/fixtures/stripe/invoice_created.json.tt +18 -0
  221. data/lib/generators/seams/billing/templates/spec/fixtures/stripe/invoice_finalized.json.tt +18 -0
  222. data/lib/generators/seams/billing/templates/spec/fixtures/stripe/invoice_paid.json.tt +19 -0
  223. data/lib/generators/seams/billing/templates/spec/fixtures/stripe/invoice_payment_failed.json.tt +20 -0
  224. data/lib/generators/seams/billing/templates/spec/fixtures/stripe/invoice_voided.json.tt +18 -0
  225. data/lib/generators/seams/billing/templates/spec/fixtures/stripe/payment_intent_payment_failed.json.tt +21 -0
  226. data/lib/generators/seams/billing/templates/spec/fixtures/stripe/payment_intent_succeeded.json.tt +17 -0
  227. data/lib/generators/seams/billing/templates/spec/gateways/contract_spec.rb.tt +11 -0
  228. data/lib/generators/seams/billing/templates/spec/gateways/stripe_spec.rb.tt +53 -0
  229. data/lib/generators/seams/billing/templates/spec/models/plan_spec.rb.tt +81 -0
  230. data/lib/generators/seams/billing/templates/spec/models/subscription_spec.rb.tt +43 -0
  231. data/lib/generators/seams/billing/templates/spec/runtime/boot_spec.rb.tt +38 -0
  232. data/lib/generators/seams/billing/templates/spec/runtime/webhook_handlers_spec.rb.tt +382 -0
  233. data/lib/generators/seams/billing/templates/spec/support/shared_examples/a_billing_gateway.rb.tt +100 -0
  234. data/lib/generators/seams/billing/templates/spec/support/stripe_helpers.rb.tt +59 -0
  235. data/lib/generators/seams/core/core_generator.rb +191 -0
  236. data/lib/generators/seams/core/templates/README.md.tt +45 -0
  237. data/lib/generators/seams/core/templates/app/controllers/concerns/has_current_attributes.rb.tt +71 -0
  238. data/lib/generators/seams/core/templates/app/models/application_record.rb.tt +7 -0
  239. data/lib/generators/seams/core/templates/app/models/audit_log.rb.tt +19 -0
  240. data/lib/generators/seams/core/templates/app/models/concerns/auditable.rb.tt +64 -0
  241. data/lib/generators/seams/core/templates/app/models/concerns/sluggable.rb.tt +53 -0
  242. data/lib/generators/seams/core/templates/app/models/concerns/soft_deletable.rb.tt +37 -0
  243. data/lib/generators/seams/core/templates/app/models/concerns/tenant_scoped.rb.tt +39 -0
  244. data/lib/generators/seams/core/templates/app/models/current.rb.tt +16 -0
  245. data/lib/generators/seams/core/templates/app/services/event_publisher.rb.tt +23 -0
  246. data/lib/generators/seams/core/templates/app/validators/email_format_validator.rb.tt +21 -0
  247. data/lib/generators/seams/core/templates/db/migrate/create_core_audit_logs.rb.tt +29 -0
  248. data/lib/generators/seams/core/templates/lib/core.rb.tt +8 -0
  249. data/lib/generators/seams/core/templates/lib/engine.rb.tt +28 -0
  250. data/lib/generators/seams/core/templates/spec/concerns/auditable_spec.rb.tt +39 -0
  251. data/lib/generators/seams/core/templates/spec/concerns/sluggable_spec.rb.tt +29 -0
  252. data/lib/generators/seams/core/templates/spec/models/audit_log_spec.rb.tt +22 -0
  253. data/lib/generators/seams/core/templates/spec/runtime/boot_spec.rb.tt +25 -0
  254. data/lib/generators/seams/core/templates/spec/validators/email_format_validator_spec.rb.tt +29 -0
  255. data/lib/generators/seams/engine/engine_generator.rb +165 -0
  256. data/lib/generators/seams/engine/templates/Gemfile.tt +19 -0
  257. data/lib/generators/seams/engine/templates/LICENSE.tt +21 -0
  258. data/lib/generators/seams/engine/templates/README.md.tt +40 -0
  259. data/lib/generators/seams/engine/templates/Rakefile.tt +14 -0
  260. data/lib/generators/seams/engine/templates/app/application_controller.rb.tt +6 -0
  261. data/lib/generators/seams/engine/templates/app/application_record.rb.tt +16 -0
  262. data/lib/generators/seams/engine/templates/config/locales/en.yml.tt +14 -0
  263. data/lib/generators/seams/engine/templates/config/routes.rb.tt +4 -0
  264. data/lib/generators/seams/engine/templates/gemspec.tt +20 -0
  265. data/lib/generators/seams/engine/templates/host_initializer.rb.tt +13 -0
  266. data/lib/generators/seams/engine/templates/lib/engine.rb.tt +27 -0
  267. data/lib/generators/seams/engine/templates/lib/root.rb.tt +7 -0
  268. data/lib/generators/seams/engine/templates/lib/version.rb.tt +5 -0
  269. data/lib/generators/seams/engine/templates/rubocop.yml.tt +55 -0
  270. data/lib/generators/seams/engine/templates/spec/example_spec.rb.tt +16 -0
  271. data/lib/generators/seams/engine/templates/spec/spec_helper.rb.tt +23 -0
  272. data/lib/generators/seams/install/install_generator.rb +211 -0
  273. data/lib/generators/seams/install/templates/Dockerfile.tt +52 -0
  274. data/lib/generators/seams/install/templates/Procfile.tt +14 -0
  275. data/lib/generators/seams/install/templates/bin_seams.tt +107 -0
  276. data/lib/generators/seams/install/templates/ci.yml.tt +123 -0
  277. data/lib/generators/seams/install/templates/deploy.yml.tt +63 -0
  278. data/lib/generators/seams/install/templates/doc/ARCHITECTURE.md.tt +86 -0
  279. data/lib/generators/seams/install/templates/docker-entrypoint.tt +27 -0
  280. data/lib/generators/seams/install/templates/rubocop.yml.tt +33 -0
  281. data/lib/generators/seams/install/templates/ruby-version.tt +1 -0
  282. data/lib/generators/seams/install/templates/script/collate_coverage.rb.tt +33 -0
  283. data/lib/generators/seams/install/templates/script/run_affected_tests.sh.tt +64 -0
  284. data/lib/generators/seams/install/templates/seams.rake.tt +65 -0
  285. data/lib/generators/seams/install/templates/seams.rb.tt +9 -0
  286. data/lib/generators/seams/install/templates/seams_engines.rb.tt +15 -0
  287. data/lib/generators/seams/notifications/notifications_generator.rb +395 -0
  288. data/lib/generators/seams/notifications/templates/README.md.tt +269 -0
  289. data/lib/generators/seams/notifications/templates/app/channels/notification_channel.rb.tt +36 -0
  290. data/lib/generators/seams/notifications/templates/app/controllers/notifications_controller.rb.tt +58 -0
  291. data/lib/generators/seams/notifications/templates/app/controllers/preferences_controller.rb.tt +54 -0
  292. data/lib/generators/seams/notifications/templates/app/javascript/controllers/notification_bell_controller.js.tt +34 -0
  293. data/lib/generators/seams/notifications/templates/app/jobs/application_job.rb.tt +6 -0
  294. data/lib/generators/seams/notifications/templates/app/jobs/create_notification_job.rb.tt +31 -0
  295. data/lib/generators/seams/notifications/templates/app/jobs/send_due_notifications_job.rb.tt +22 -0
  296. data/lib/generators/seams/notifications/templates/app/jobs/send_notification_job.rb.tt +13 -0
  297. data/lib/generators/seams/notifications/templates/app/mailers/application_mailer.rb.tt +12 -0
  298. data/lib/generators/seams/notifications/templates/app/mailers/notification_mailer.rb.tt +23 -0
  299. data/lib/generators/seams/notifications/templates/app/models/application_record.rb.tt +7 -0
  300. data/lib/generators/seams/notifications/templates/app/models/delivery.rb.tt +13 -0
  301. data/lib/generators/seams/notifications/templates/app/models/notification.rb.tt +218 -0
  302. data/lib/generators/seams/notifications/templates/app/models/notification_preference.rb.tt +29 -0
  303. data/lib/generators/seams/notifications/templates/app/models/strategies/email.rb.tt +38 -0
  304. data/lib/generators/seams/notifications/templates/app/models/strategies/in_app.rb.tt +26 -0
  305. data/lib/generators/seams/notifications/templates/app/models/strategies/sms.rb.tt +33 -0
  306. data/lib/generators/seams/notifications/templates/app/subscribers/auth_subscriber.rb.tt +71 -0
  307. data/lib/generators/seams/notifications/templates/app/subscribers/billing_subscriber.rb.tt +127 -0
  308. data/lib/generators/seams/notifications/templates/app/views/layouts/notifications/mailer.html.erb.tt +22 -0
  309. data/lib/generators/seams/notifications/templates/app/views/layouts/notifications/mailer.text.erb.tt +4 -0
  310. data/lib/generators/seams/notifications/templates/app/views/notifications/_bell.html.erb.tt +15 -0
  311. data/lib/generators/seams/notifications/templates/app/views/notifications/index.html.erb.tt +15 -0
  312. data/lib/generators/seams/notifications/templates/app/views/templates/billing/invoice_failed.html.erb.tt +4 -0
  313. data/lib/generators/seams/notifications/templates/app/views/templates/billing/invoice_failed.text.erb.tt +4 -0
  314. data/lib/generators/seams/notifications/templates/app/views/templates/billing/invoice_paid.html.erb.tt +3 -0
  315. data/lib/generators/seams/notifications/templates/app/views/templates/billing/invoice_paid.text.erb.tt +3 -0
  316. data/lib/generators/seams/notifications/templates/app/views/templates/billing/lifetime_granted.html.erb.tt +5 -0
  317. data/lib/generators/seams/notifications/templates/app/views/templates/billing/lifetime_granted.text.erb.tt +5 -0
  318. data/lib/generators/seams/notifications/templates/app/views/templates/billing/lifetime_purchased.html.erb.tt +5 -0
  319. data/lib/generators/seams/notifications/templates/app/views/templates/billing/lifetime_purchased.text.erb.tt +5 -0
  320. data/lib/generators/seams/notifications/templates/app/views/templates/billing/subscription_canceled.html.erb.tt +4 -0
  321. data/lib/generators/seams/notifications/templates/app/views/templates/billing/subscription_canceled.text.erb.tt +4 -0
  322. data/lib/generators/seams/notifications/templates/app/views/templates/billing/subscription_started.html.erb.tt +4 -0
  323. data/lib/generators/seams/notifications/templates/app/views/templates/billing/subscription_started.text.erb.tt +5 -0
  324. data/lib/generators/seams/notifications/templates/app/views/templates/billing/subscription_updated.html.erb.tt +3 -0
  325. data/lib/generators/seams/notifications/templates/app/views/templates/billing/subscription_updated.text.erb.tt +3 -0
  326. data/lib/generators/seams/notifications/templates/app/views/templates/default.html.erb.tt +10 -0
  327. data/lib/generators/seams/notifications/templates/app/views/templates/default.text.erb.tt +11 -0
  328. data/lib/generators/seams/notifications/templates/app/views/templates/welcome.html.erb.tt +6 -0
  329. data/lib/generators/seams/notifications/templates/app/views/templates/welcome.text.erb.tt +6 -0
  330. data/lib/generators/seams/notifications/templates/config/initializers/notifications.rb.tt +58 -0
  331. data/lib/generators/seams/notifications/templates/config/routes.rb.tt +17 -0
  332. data/lib/generators/seams/notifications/templates/db/migrate/create_notification_deliveries.rb.tt +16 -0
  333. data/lib/generators/seams/notifications/templates/db/migrate/create_notification_preferences.rb.tt +25 -0
  334. data/lib/generators/seams/notifications/templates/db/migrate/create_notifications.rb.tt +35 -0
  335. data/lib/generators/seams/notifications/templates/lib/adapters/abstract.rb.tt +20 -0
  336. data/lib/generators/seams/notifications/templates/lib/adapters/action_mailer.rb.tt +17 -0
  337. data/lib/generators/seams/notifications/templates/lib/adapters/null_sms.rb.tt +23 -0
  338. data/lib/generators/seams/notifications/templates/lib/concerns/notifiable.rb.tt +135 -0
  339. data/lib/generators/seams/notifications/templates/lib/configuration.rb.tt +24 -0
  340. data/lib/generators/seams/notifications/templates/lib/engine.rb.tt +35 -0
  341. data/lib/generators/seams/notifications/templates/lib/notifications.rb.tt +75 -0
  342. data/lib/generators/seams/notifications/templates/lib/type_registry.rb.tt +74 -0
  343. data/lib/generators/seams/notifications/templates/spec/factories/notifications.rb.tt +53 -0
  344. data/lib/generators/seams/notifications/templates/spec/models/delivery_spec.rb.tt +28 -0
  345. data/lib/generators/seams/notifications/templates/spec/models/notification_preference_spec.rb.tt +46 -0
  346. data/lib/generators/seams/notifications/templates/spec/models/notification_spec.rb.tt +60 -0
  347. data/lib/generators/seams/notifications/templates/spec/runtime/bell_broadcast_spec.rb.tt +59 -0
  348. data/lib/generators/seams/notifications/templates/spec/runtime/billing_subscriber_skip_spec.rb.tt +87 -0
  349. data/lib/generators/seams/notifications/templates/spec/runtime/boot_spec.rb.tt +39 -0
  350. data/lib/generators/seams/notifications/templates/spec/runtime/schedule_round_trip_spec.rb.tt +55 -0
  351. data/lib/generators/seams/remove/remove_generator.rb +259 -0
  352. data/lib/generators/seams/teams/teams_generator.rb +298 -0
  353. data/lib/generators/seams/teams/templates/README.md.tt +88 -0
  354. data/lib/generators/seams/teams/templates/app/controllers/invitations_controller.rb.tt +102 -0
  355. data/lib/generators/seams/teams/templates/app/controllers/memberships_controller.rb.tt +54 -0
  356. data/lib/generators/seams/teams/templates/app/controllers/teams_controller.rb.tt +68 -0
  357. data/lib/generators/seams/teams/templates/app/jobs/application_job.rb.tt +6 -0
  358. data/lib/generators/seams/teams/templates/app/mailers/invitation_mailer.rb.tt +34 -0
  359. data/lib/generators/seams/teams/templates/app/models/application_record.rb.tt +7 -0
  360. data/lib/generators/seams/teams/templates/app/models/current.rb.tt +30 -0
  361. data/lib/generators/seams/teams/templates/app/models/invitation.rb.tt +36 -0
  362. data/lib/generators/seams/teams/templates/app/models/membership.rb.tt +36 -0
  363. data/lib/generators/seams/teams/templates/app/models/team.rb.tt +32 -0
  364. data/lib/generators/seams/teams/templates/app/subscribers/invitation_subscriber.rb.tt +36 -0
  365. data/lib/generators/seams/teams/templates/app/views/invitation_mailer/invite.text.erb.tt +8 -0
  366. data/lib/generators/seams/teams/templates/app/views/invitations/index.html.erb.tt +44 -0
  367. data/lib/generators/seams/teams/templates/app/views/memberships/index.html.erb.tt +32 -0
  368. data/lib/generators/seams/teams/templates/app/views/teams/edit.html.erb.tt +28 -0
  369. data/lib/generators/seams/teams/templates/app/views/teams/index.html.erb.tt +15 -0
  370. data/lib/generators/seams/teams/templates/app/views/teams/new.html.erb.tt +24 -0
  371. data/lib/generators/seams/teams/templates/app/views/teams/show.html.erb.tt +17 -0
  372. data/lib/generators/seams/teams/templates/config/routes.rb.tt +19 -0
  373. data/lib/generators/seams/teams/templates/db/migrate/create_team_invitations.rb.tt +24 -0
  374. data/lib/generators/seams/teams/templates/db/migrate/create_team_memberships.rb.tt +25 -0
  375. data/lib/generators/seams/teams/templates/db/migrate/create_teams.rb.tt +18 -0
  376. data/lib/generators/seams/teams/templates/lib/concerns/account_scoped.rb.tt +79 -0
  377. data/lib/generators/seams/teams/templates/lib/concerns/authorization.rb.tt +55 -0
  378. data/lib/generators/seams/teams/templates/lib/configuration.rb.tt +45 -0
  379. data/lib/generators/seams/teams/templates/lib/engine.rb.tt +51 -0
  380. data/lib/generators/seams/teams/templates/lib/teams.rb.tt +22 -0
  381. data/lib/generators/seams/teams/templates/spec/factories/teams.rb.tt +47 -0
  382. data/lib/generators/seams/teams/templates/spec/models/invitation_spec.rb.tt +25 -0
  383. data/lib/generators/seams/teams/templates/spec/models/membership_spec.rb.tt +29 -0
  384. data/lib/generators/seams/teams/templates/spec/models/team_spec.rb.tt +23 -0
  385. data/lib/generators/seams/teams/templates/spec/runtime/boot_spec.rb.tt +32 -0
  386. data/lib/seams/cli/list.rb +111 -0
  387. data/lib/seams/cli/quality.rb +99 -0
  388. data/lib/seams/cli/resolve.rb +276 -0
  389. data/lib/seams/cli/test_changed.rb +116 -0
  390. data/lib/seams/cli.rb +48 -0
  391. data/lib/seams/configuration.rb +19 -0
  392. data/lib/seams/cops/known_queue_names.rb +42 -0
  393. data/lib/seams/cops/migration_comments.rb +68 -0
  394. data/lib/seams/cops/no_cross_engine_dependency.rb +58 -0
  395. data/lib/seams/cops/no_cross_engine_model_access.rb +153 -0
  396. data/lib/seams/cops.rb +18 -0
  397. data/lib/seams/event_registry.rb +49 -0
  398. data/lib/seams/events/adapter.rb +24 -0
  399. data/lib/seams/events/adapters/active_support.rb +31 -0
  400. data/lib/seams/events/publisher.rb +178 -0
  401. data/lib/seams/events.rb +39 -0
  402. data/lib/seams/generators/dummy_app_writer.rb +424 -0
  403. data/lib/seams/generators/eject_aware.rb +102 -0
  404. data/lib/seams/generators/follow_up_generator.rb +148 -0
  405. data/lib/seams/generators/host_injector.rb +124 -0
  406. data/lib/seams/generators/sibling_rubocop_writer.rb +77 -0
  407. data/lib/seams/generators/splicer.rb +217 -0
  408. data/lib/seams/observability/adapter.rb +33 -0
  409. data/lib/seams/observability/adapters/rails_logger.rb +59 -0
  410. data/lib/seams/observability.rb +34 -0
  411. data/lib/seams/runtime.rb +23 -0
  412. data/lib/seams/version.rb +5 -0
  413. data/lib/seams.rb +23 -0
  414. metadata +493 -0
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Admin
4
+ module Tenant
5
+ # Tenant-mode policy for Notifications::Notification. Filters by
6
+ # `account_id`. The canonical seams `notifications` table is
7
+ # polymorphically owned (`owner_type`/`owner_id`) and doesn't
8
+ # carry `account_id`. The default falls back to `scope.none` if
9
+ # the column is absent — fail-closed. Hosts whose Notification
10
+ # model uses the polymorphic shape (or a custom direct
11
+ # `account_id` column) eject this policy and rewrite `resolve` to
12
+ # join through the owning model.
13
+ class NotificationPolicy < ApplicationPolicy
14
+ class Scope < ApplicationPolicy::Scope
15
+ def resolve
16
+ return scope.all if staff?
17
+ return scope.none if account_id.nil?
18
+ return scope.none unless account_id_column_present?
19
+
20
+ scope.where(account_id: account_id)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Admin
4
+ module Tenant
5
+ # Tenant-mode policy for Notifications::NotificationPreference.
6
+ # Filters by `account_id`. NotificationPreference rows are tied to
7
+ # an Identity, but Identities can span Accounts. Hosts that carry
8
+ # `account_id` directly on the preference row pick up this policy
9
+ # as-is; hosts whose schema lacks the column fall through to
10
+ # `scope.none` (fail-closed) and are expected to eject + rewrite.
11
+ class NotificationPreferencePolicy < ApplicationPolicy
12
+ class Scope < ApplicationPolicy::Scope
13
+ def resolve
14
+ return scope.all if staff?
15
+ return scope.none if account_id.nil?
16
+ return scope.none unless account_id_column_present?
17
+
18
+ scope.where(account_id: account_id)
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Admin
4
+ module Tenant
5
+ # Tenant-mode policy for Billing::Plan.
6
+ #
7
+ # Plans are global product rows, not account-scoped — the
8
+ # `billing_plans` table carries no `account_id` column. Tenant
9
+ # admins typically need to **see** the catalogue of available plans
10
+ # (so they can switch their Subscription) but should not create or
11
+ # edit them. The default here keeps the catalogue readable to any
12
+ # account-admin and falls back to the staff? gate for writes.
13
+ #
14
+ # `Scope#resolve` returns `scope.all` because the catalogue is
15
+ # global — there is no `account_id` to filter by. Hosts that mark
16
+ # plans private to specific accounts eject and add the filter.
17
+ class PlanPolicy < ApplicationPolicy
18
+ def create?
19
+ user&.identity&.staff?
20
+ end
21
+
22
+ def update?
23
+ user&.identity&.staff?
24
+ end
25
+
26
+ def destroy?
27
+ user&.identity&.staff?
28
+ end
29
+
30
+ class Scope < ApplicationPolicy::Scope
31
+ # Plans are global; tenant admins see the catalogue.
32
+ def resolve
33
+ scope.all
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ # Plans are global; per-record tenant guard always passes for
40
+ # the read actions, and the write actions short-circuit on the
41
+ # staff? predicates above.
42
+ def record_in_tenant_scope?
43
+ true
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Admin
4
+ module Tenant
5
+ # Tenant-mode policy for Billing::Subscription. Filters by
6
+ # `account_id`. The `billing_subscriptions` table carries the
7
+ # column directly in the canonical seams schema. The
8
+ # column-presence guard is defensive for hosts whose schema
9
+ # differs.
10
+ class SubscriptionPolicy < ApplicationPolicy
11
+ class Scope < ApplicationPolicy::Scope
12
+ def resolve
13
+ return scope.all if staff?
14
+ return scope.none if account_id.nil?
15
+ return scope.none unless account_id_column_present?
16
+
17
+ scope.where(account_id: account_id)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Admin
4
+ module Tenant
5
+ # Tenant-mode policy for Teams::Team. Filters by `account_id`.
6
+ #
7
+ # Hosts whose Team model is NOT account-scoped (e.g. teams that
8
+ # span accounts, or whose schema has not been migrated to add
9
+ # `account_id` to `teams`) eject this policy and replace `resolve`
10
+ # with a join through Teams::Membership or whatever the host's
11
+ # tenant boundary is.
12
+ #
13
+ # Without an `account_id` column the default `Scope#resolve`
14
+ # returns `scope.none` rather than crashing on
15
+ # `PG::UndefinedColumn` — fail closed.
16
+ class TeamPolicy < ApplicationPolicy
17
+ class Scope < ApplicationPolicy::Scope
18
+ def resolve
19
+ return scope.all if staff?
20
+ return scope.none if account_id.nil?
21
+ return scope.none unless account_id_column_present?
22
+
23
+ scope.where(account_id: account_id)
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Admin
4
+ module Tenant
5
+ # Tenant-mode policy for Teams::Membership. Filters by `account_id`.
6
+ #
7
+ # The canonical seams `team_memberships` table doesn't carry
8
+ # `account_id` directly (memberships are scoped through their
9
+ # Team). The default falls back to `scope.none` if the column is
10
+ # absent — fail-closed. Hosts whose schema differs eject this
11
+ # policy and replace `resolve` with a join through Teams::Team.
12
+ class TeamsMembershipPolicy < ApplicationPolicy
13
+ class Scope < ApplicationPolicy::Scope
14
+ def resolve
15
+ return scope.all if staff?
16
+ return scope.none if account_id.nil?
17
+ return scope.none unless account_id_column_present?
18
+
19
+ scope.where(account_id: account_id)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Routes for the seams admin engine. Phase 2 ships full CRUD route
4
+ # surfaces for the twelve canonical seams models. Each `resources`
5
+ # line points at a controller under `app/controllers/admin/<plural>`
6
+ # (single-namespace `Admin::*` controllers, matching Administrate's
7
+ # convention) — the controllers themselves subclass
8
+ # Seams::Admin::ApplicationController so the gate, the pundit_user
9
+ # hook, and Phase 3's audit-log auto-write apply uniformly.
10
+ Seams::Admin::Engine.routes.draw do
11
+ # Follow-up generators that add admin sections (impersonation, bulk-action endpoints, etc.) splice their routes here, ahead of the dashboard resources Phase 2 ships.
12
+ # seams:insertion-point admin.routes.before_resources
13
+
14
+ # Phase 2 dashboards. Per Administrate convention, each resource
15
+ # gets a full CRUD route surface (index, show, new, create, edit,
16
+ # update, destroy). The `controller:` option points at the
17
+ # engine-prefixed controllers under app/controllers/admin/.
18
+ resources :identities, controller: "admin/identities"
19
+ resources :accounts, controller: "admin/accounts"
20
+ resources :accounts_memberships, controller: "admin/accounts_memberships"
21
+ resources :teams, controller: "admin/teams"
22
+ resources :teams_memberships, controller: "admin/teams_memberships"
23
+ resources :invitations, controller: "admin/invitations"
24
+ resources :notifications, controller: "admin/notifications"
25
+ resources :notification_preferences, controller: "admin/notification_preferences"
26
+ resources :plans, controller: "admin/plans"
27
+ resources :subscriptions, controller: "admin/subscriptions"
28
+ resources :invoices, controller: "admin/invoices"
29
+ resources :lifetime_passes, controller: "admin/lifetime_passes"
30
+
31
+ # Follow-up generators that add NEW route surfaces (custom collection routes, JSON-only endpoints) splice their resources here.
32
+ # seams:insertion-point admin.routes.after_resources
33
+
34
+ # Default landing page so visiting `/admin` lands somewhere
35
+ # meaningful. Identities is the natural index — the operator
36
+ # console use case starts with "find this user".
37
+ root to: "admin/identities#index"
38
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "admin/configuration"
4
+ require "admin/context"
5
+ require "admin/engine"
6
+ require "admin/concerns/authenticator"
7
+
8
+ # Engine root for the seams admin engine. Namespaces under
9
+ # `Seams::Admin` (rather than top-level `Admin`) so the host's own
10
+ # `app/admin/...` code does not collide with the engine's controllers,
11
+ # dashboards, or policies. The path layout under engines/admin/lib/
12
+ # uses the short `admin/...` form for grep-friendliness; constants
13
+ # live under `Seams::Admin::*`.
14
+ module Seams
15
+ module Admin
16
+ class Error < StandardError; end
17
+
18
+ class << self
19
+ def configuration
20
+ @configuration ||= Configuration.new
21
+ end
22
+
23
+ def configure
24
+ yield configuration
25
+ end
26
+
27
+ # Convenience alias matching how the host reads it in
28
+ # initializers and controller before-actions:
29
+ #
30
+ # Seams::Admin.config.authenticator.call(self)
31
+ def config
32
+ configuration
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/concern"
4
+
5
+ module Seams
6
+ module Admin
7
+ # Controller concern mixed into the admin engine's
8
+ # `Seams::Admin::ApplicationController`. Gates every admin request
9
+ # on `Seams::Admin.config.authenticator` and runs the optional
10
+ # `before_admin_action` hook (2FA, IP allow-list, etc.) before the
11
+ # action itself.
12
+ #
13
+ # This is the public seam Phase 3 builds Pundit policies on top of.
14
+ # Phase 1 ships the gate only; Phase 3 fills in the
15
+ # `pundit_user` / `policy_namespace` cooperation so resource-level
16
+ # authorisation kicks in after the gate.
17
+ module Authenticator
18
+ extend ActiveSupport::Concern
19
+
20
+ included do
21
+ before_action :authenticate_admin!
22
+ before_action :run_before_admin_action_hook
23
+ end
24
+
25
+ private
26
+
27
+ def authenticate_admin!
28
+ return if admin_authenticator_call
29
+
30
+ respond_with_admin_forbidden(<<~MSG.strip)
31
+ Access denied. The seams admin surface is gated on
32
+ `Seams::Admin.config.authenticator`; the configured callable
33
+ returned a falsey value for this request. Default gate:
34
+ `current_identity&.staff?`. Override the authenticator in
35
+ config/initializers/seams_admin.rb if your host uses a
36
+ different signal.
37
+ MSG
38
+ end
39
+
40
+ def run_before_admin_action_hook
41
+ hook = ::Seams::Admin.config.before_admin_action
42
+ return if hook.nil?
43
+
44
+ hook.call(self)
45
+ end
46
+
47
+ def admin_authenticator_call
48
+ gate = ::Seams::Admin.config.authenticator
49
+ # Defensive: a misconfigured host that nils the gate locks
50
+ # admin closed rather than wide-open. "Fail-closed" is the
51
+ # only sane default for an admin surface.
52
+ return false unless gate.respond_to?(:call)
53
+
54
+ gate.call(self)
55
+ end
56
+
57
+ def respond_with_admin_forbidden(message)
58
+ if respond_to?(:render)
59
+ render plain: message, status: :forbidden
60
+ else
61
+ head :forbidden
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Seams
4
+ module Admin
5
+ # Engine-scoped configuration for the admin surface. Override in
6
+ # config/initializers/seams_admin.rb of the host application:
7
+ #
8
+ # Seams::Admin.configure do |c|
9
+ # c.authenticator = ->(ctrl) { ctrl.current_identity&.platform_admin? }
10
+ # c.tenancy_scope = :tenant # default :platform
11
+ # c.theme_css_path = "admin/seams_brand.css"
12
+ # c.before_admin_action = ->(ctrl) { ctrl.require_2fa! }
13
+ # end
14
+ #
15
+ # Four knobs, deliberately small:
16
+ #
17
+ # - `authenticator` — callable taking the controller, returning
18
+ # truthy when the request is allowed into the admin engine.
19
+ # Default: `staff?` on the current Identity. Hosts that don't
20
+ # carry `staff?` on Identity (custom flags, role tables, etc.)
21
+ # override this.
22
+ #
23
+ # - `tenancy_scope` — `:platform` (Wave 11A default — see all
24
+ # accounts) or `:tenant` (scope to `current_membership.account_id`,
25
+ # wired in Phase 3 via Pundit policy_namespace). Phase 1 only
26
+ # stores the value; Phase 3 reads it.
27
+ #
28
+ # - `theme_css_path` — host-supplied admin restyle. Loaded on top
29
+ # of Administrate's stock CSS so the admin UI carries the host's
30
+ # brand. nil means "use Administrate's defaults".
31
+ #
32
+ # - `before_admin_action` — callable that runs before every admin
33
+ # action. The recommended hook for 2FA enforcement, IP allow-list
34
+ # checks, audit-trail entry-point logging, etc. Phase 1 calls it
35
+ # if set; Phase 1 ships no default behaviour beyond the gate.
36
+ #
37
+ # - `current_membership_resolver` — callable taking the controller,
38
+ # returning the active `Accounts::Membership` for tenant-mode
39
+ # policy decisions. Default reads `Accounts::Current.membership`
40
+ # (Wave 9's CurrentAttributes object). Hosts that resolve the
41
+ # active membership differently (custom membership classes, a
42
+ # Devise/Doorkeeper-style "current_account" pattern, multiple
43
+ # memberships per request, etc.) override this in the host
44
+ # initializer rather than ejecting the application controller.
45
+ class Configuration
46
+ attr_accessor :authenticator,
47
+ :tenancy_scope,
48
+ :theme_css_path,
49
+ :before_admin_action,
50
+ :current_membership_resolver
51
+ # Follow-up generators that add knobs (impersonation_audit_logger,
52
+ # session_timeout, etc.) declare their attr_accessor here.
53
+ # seams:insertion-point admin.configuration.attributes
54
+
55
+ def initialize
56
+ # Default authenticator: the current Identity must carry the
57
+ # `staff` flag. Hosts that need a finer-grained gate (per-
58
+ # account admin role, IP allow-list, etc.) override this.
59
+ @authenticator = ->(controller) { controller.current_identity&.staff? }
60
+
61
+ # :platform → admins see every Account's data. :tenant → admins
62
+ # are scoped to the Account they're a member of. Phase 3 reads
63
+ # this; Phase 1 just stores it.
64
+ @tenancy_scope = :platform
65
+
66
+ # nil → Administrate's stock CSS only.
67
+ @theme_css_path = nil
68
+
69
+ # nil → no extra before-action; only the authenticator gate
70
+ # runs. Hosts wire 2FA / IP allow-list here.
71
+ @before_admin_action = nil
72
+
73
+ # Default current-membership resolver: read
74
+ # `Accounts::Current.membership` (Wave 9). Returns nil when
75
+ # the constant isn't loaded so platform-only hosts (no accounts
76
+ # engine) don't NameError. Hosts that store the active
77
+ # membership elsewhere assign their own callable here.
78
+ @current_membership_resolver = lambda do |_controller|
79
+ next nil unless defined?(::Accounts::Current)
80
+
81
+ ::Accounts::Current.membership
82
+ rescue NoMethodError
83
+ nil
84
+ end
85
+ # Follow-up generators that add defaults for new attributes (matching admin.configuration.attributes) splice them here.
86
+ # seams:insertion-point admin.configuration.defaults
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Seams
4
+ module Admin
5
+ # The value `Seams::Admin::ApplicationController#pundit_user`
6
+ # returns. Wraps the current Auth::Identity together with the
7
+ # current Accounts::Membership so a Pundit policy can read both
8
+ # signals (staff? on the Identity, role/account_id on the
9
+ # Membership) without the policy having to know how to fish either
10
+ # out of the controller.
11
+ #
12
+ # Why a Struct, not the raw Identity? In tenant mode the policies
13
+ # need to filter by `account_id`, which lives on the Membership,
14
+ # not the Identity. Pundit's `pundit_user` is a single value, so
15
+ # we wrap. Hosts that don't carry an Accounts::Membership concept
16
+ # (single-tenant apps, platform-only admins) leave `membership` nil
17
+ # and the Identity's `staff?` carries the gate alone.
18
+ #
19
+ # Both fields are nil-safe in every policy — the platform policies
20
+ # only read `identity&.staff?`, the tenant policies fall through to
21
+ # `scope.none` when membership is nil.
22
+ Context = Struct.new(:identity, :membership) do
23
+ # Convenience: a policy that wants to call `user.staff?` rather
24
+ # than `user.identity&.staff?` can. Mirrors the bare-Identity
25
+ # surface so policies stay readable when only the Identity
26
+ # matters.
27
+ def staff?
28
+ identity&.staff?
29
+ end
30
+
31
+ # Convenience: same idea on the Membership side. A policy that
32
+ # only cares about the role can call `user.role` instead of
33
+ # `user.membership&.role`.
34
+ def role
35
+ membership&.role
36
+ end
37
+
38
+ # Convenience: same idea on the Membership side for account_id.
39
+ def account_id
40
+ membership&.account_id
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Seams
4
+ module Admin
5
+ class Engine < ::Rails::Engine
6
+ isolate_namespace Seams::Admin
7
+
8
+ # Engine is rooted at engines/admin/. Rails picks up app/, config/,
9
+ # db/migrate/ etc. from the engine root by convention. lib/ is on
10
+ # $LOAD_PATH but not Zeitwerk-managed — sub-files under lib/admin/
11
+ # are explicit `require`s from lib/admin.rb.
12
+ config.generators do |g|
13
+ g.test_framework :rspec
14
+ end
15
+
16
+ initializer "admin.register_events" do
17
+ # Phase 1 ships no admin-specific events. Phase 3 adds an
18
+ # `admin.action.taken.admin` event when the audit-log auto-write
19
+ # lands. Follow-up generators (e.g. seams:admin:add_dashboard)
20
+ # may register events here.
21
+ # Follow-up generators that emit new admin events register them here.
22
+ # seams:insertion-point admin.engine.events
23
+ end
24
+
25
+ initializer "admin.append_migrations" do |app|
26
+ unless app.root == root
27
+ config.paths["db/migrate"].expanded.each do |expanded_path|
28
+ app.config.paths["db/migrate"] << expanded_path
29
+ end
30
+ end
31
+ end
32
+
33
+ # Boot-time dependency assertion. The admin engine's default
34
+ # authenticator reads `current_identity&.staff?`, and Phase 2
35
+ # dashboards target Auth::Identity / Accounts::Account /
36
+ # Accounts::Membership. Without auth, the engine cannot gate; we
37
+ # fail loud at boot rather than at first request so the operator
38
+ # gets a clear "install seams:auth" error instead of a deep
39
+ # NameError mid-request.
40
+ #
41
+ # Administrate is checked too: the engine's ApplicationController
42
+ # subclasses Administrate::ApplicationController — without the
43
+ # gem in the host's Gemfile the constant doesn't exist.
44
+ config.after_initialize do
45
+ missing = []
46
+ missing << "Auth::Identity (run: bin/rails generate seams:auth)" unless defined?(::Auth::Identity)
47
+ missing << "Administrate (add `gem \"administrate\"` to Gemfile)" unless defined?(::Administrate)
48
+ missing << "Pundit (add `gem \"pundit\"` to Gemfile)" unless defined?(::Pundit)
49
+
50
+ if missing.any?
51
+ raise <<~MSG
52
+ [seams admin] missing required dependency:
53
+ #{missing.join("\n ")}
54
+
55
+ The admin engine builds dashboards on top of Administrate,
56
+ gates them on Auth::Identity#staff?, and authorises
57
+ individual records via Pundit policies under
58
+ Admin::Platform::* and Admin::Tenant::*. It cannot run
59
+ without all three. Generate the auth engine and ensure
60
+ both administrate + pundit are in the host Gemfile, or
61
+ remove admin with
62
+ `bin/rails generate seams:remove admin --force`.
63
+ MSG
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Factories for the seams admin engine. Phase 1 ships no factories —
4
+ # the boot spec uses Auth::Identity directly via `create!`. Phase 2
5
+ # adds factories per dashboard if specs need fixture variants
6
+ # (an Identity with staff: true, a Membership with role: admin, etc.)
7
+ # beyond what the auth + accounts engines already ship.
8
+ FactoryBot.define do
9
+ # Phase 2: factories go here.
10
+ end