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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +335 -0
- data/LICENSE +21 -0
- data/README.md +104 -0
- data/lib/generators/seams/accounts/accounts_generator.rb +272 -0
- data/lib/generators/seams/accounts/templates/README.md.tt +219 -0
- data/lib/generators/seams/accounts/templates/app/models/account.rb.tt +124 -0
- data/lib/generators/seams/accounts/templates/app/models/application_record.rb.tt +7 -0
- data/lib/generators/seams/accounts/templates/app/models/current.rb.tt +38 -0
- data/lib/generators/seams/accounts/templates/app/models/membership.rb.tt +114 -0
- data/lib/generators/seams/accounts/templates/config/routes.rb.tt +8 -0
- data/lib/generators/seams/accounts/templates/db/migrate/create_accounts.rb.tt +29 -0
- data/lib/generators/seams/accounts/templates/db/migrate/create_accounts_memberships.rb.tt +49 -0
- data/lib/generators/seams/accounts/templates/lib/accounts.rb.tt +21 -0
- data/lib/generators/seams/accounts/templates/lib/concerns/account_scoped.rb.tt +97 -0
- data/lib/generators/seams/accounts/templates/lib/concerns/authorization.rb.tt +105 -0
- data/lib/generators/seams/accounts/templates/lib/configuration.rb.tt +26 -0
- data/lib/generators/seams/accounts/templates/lib/engine.rb.tt +55 -0
- data/lib/generators/seams/accounts/templates/spec/factories/accounts.rb.tt +49 -0
- data/lib/generators/seams/accounts/templates/spec/models/accounts/account_spec.rb.tt +64 -0
- data/lib/generators/seams/accounts/templates/spec/models/accounts/membership_spec.rb.tt +99 -0
- data/lib/generators/seams/accounts/templates/spec/runtime/accounts_boot_spec.rb.tt +181 -0
- data/lib/generators/seams/admin/admin_generator.rb +852 -0
- data/lib/generators/seams/admin/templates/README.md.tt +266 -0
- data/lib/generators/seams/admin/templates/app/controllers/admin/accounts_controller.rb.tt +16 -0
- data/lib/generators/seams/admin/templates/app/controllers/admin/accounts_memberships_controller.rb.tt +16 -0
- data/lib/generators/seams/admin/templates/app/controllers/admin/application_controller.rb.tt +282 -0
- data/lib/generators/seams/admin/templates/app/controllers/admin/identities_controller.rb.tt +26 -0
- data/lib/generators/seams/admin/templates/app/controllers/admin/invitations_controller.rb.tt +14 -0
- data/lib/generators/seams/admin/templates/app/controllers/admin/invoices_controller.rb.tt +14 -0
- data/lib/generators/seams/admin/templates/app/controllers/admin/lifetime_passes_controller.rb.tt +14 -0
- data/lib/generators/seams/admin/templates/app/controllers/admin/notification_preferences_controller.rb.tt +15 -0
- data/lib/generators/seams/admin/templates/app/controllers/admin/notifications_controller.rb.tt +18 -0
- data/lib/generators/seams/admin/templates/app/controllers/admin/plans_controller.rb.tt +14 -0
- data/lib/generators/seams/admin/templates/app/controllers/admin/subscriptions_controller.rb.tt +14 -0
- data/lib/generators/seams/admin/templates/app/controllers/admin/teams_controller.rb.tt +14 -0
- data/lib/generators/seams/admin/templates/app/controllers/admin/teams_memberships_controller.rb.tt +16 -0
- data/lib/generators/seams/admin/templates/app/dashboards/admin/account_dashboard.rb.tt +50 -0
- data/lib/generators/seams/admin/templates/app/dashboards/admin/accounts_membership_dashboard.rb.tt +58 -0
- data/lib/generators/seams/admin/templates/app/dashboards/admin/identity_dashboard.rb.tt +48 -0
- data/lib/generators/seams/admin/templates/app/dashboards/admin/invitation_dashboard.rb.tt +51 -0
- data/lib/generators/seams/admin/templates/app/dashboards/admin/invoice_dashboard.rb.tt +67 -0
- data/lib/generators/seams/admin/templates/app/dashboards/admin/lifetime_pass_dashboard.rb.tt +65 -0
- data/lib/generators/seams/admin/templates/app/dashboards/admin/notification_dashboard.rb.tt +58 -0
- data/lib/generators/seams/admin/templates/app/dashboards/admin/notification_preference_dashboard.rb.tt +43 -0
- data/lib/generators/seams/admin/templates/app/dashboards/admin/plan_dashboard.rb.tt +72 -0
- data/lib/generators/seams/admin/templates/app/dashboards/admin/subscription_dashboard.rb.tt +59 -0
- data/lib/generators/seams/admin/templates/app/dashboards/admin/team_dashboard.rb.tt +39 -0
- data/lib/generators/seams/admin/templates/app/dashboards/admin/teams_membership_dashboard.rb.tt +43 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/platform/account_policy.rb.tt +10 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/platform/accounts_membership_policy.rb.tt +10 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/platform/application_policy.rb.tt +85 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/platform/identity_policy.rb.tt +18 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/platform/invitation_policy.rb.tt +9 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/platform/invoice_policy.rb.tt +9 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/platform/lifetime_pass_policy.rb.tt +9 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/platform/notification_policy.rb.tt +9 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/platform/notification_preference_policy.rb.tt +9 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/platform/plan_policy.rb.tt +11 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/platform/subscription_policy.rb.tt +9 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/platform/team_policy.rb.tt +9 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/platform/teams_membership_policy.rb.tt +9 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/account_policy.rb.tt +33 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/accounts_membership_policy.rb.tt +24 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/application_policy.rb.tt +169 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/identity_policy.rb.tt +67 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/invitation_policy.rb.tt +24 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/invoice_policy.rb.tt +21 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/lifetime_pass_policy.rb.tt +21 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/notification_policy.rb.tt +25 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/notification_preference_policy.rb.tt +23 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/plan_policy.rb.tt +47 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/subscription_policy.rb.tt +22 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/team_policy.rb.tt +28 -0
- data/lib/generators/seams/admin/templates/app/policies/admin/tenant/teams_membership_policy.rb.tt +24 -0
- data/lib/generators/seams/admin/templates/config/routes.rb.tt +38 -0
- data/lib/generators/seams/admin/templates/lib/admin.rb.tt +36 -0
- data/lib/generators/seams/admin/templates/lib/concerns/authenticator.rb.tt +66 -0
- data/lib/generators/seams/admin/templates/lib/configuration.rb.tt +90 -0
- data/lib/generators/seams/admin/templates/lib/context.rb.tt +44 -0
- data/lib/generators/seams/admin/templates/lib/engine.rb.tt +68 -0
- data/lib/generators/seams/admin/templates/spec/factories/admin.rb.tt +10 -0
- data/lib/generators/seams/admin/templates/spec/runtime/admin_boot_spec.rb.tt +604 -0
- data/lib/generators/seams/auth/add_oauth_provider/add_oauth_provider_generator.rb +157 -0
- data/lib/generators/seams/auth/add_oauth_provider/templates/adapter.rb.tt +95 -0
- data/lib/generators/seams/auth/add_oauth_provider/templates/adapter_spec.rb.tt +58 -0
- data/lib/generators/seams/auth/auth_generator.rb +311 -0
- data/lib/generators/seams/auth/templates/README.md.tt +289 -0
- data/lib/generators/seams/auth/templates/app/controllers/oauth/callbacks_controller.rb.tt +80 -0
- data/lib/generators/seams/auth/templates/app/controllers/password_resets_controller.rb.tt +44 -0
- data/lib/generators/seams/auth/templates/app/controllers/registrations_controller.rb.tt +34 -0
- data/lib/generators/seams/auth/templates/app/controllers/sessions_controller.rb.tt +49 -0
- data/lib/generators/seams/auth/templates/app/jobs/application_job.rb.tt +7 -0
- data/lib/generators/seams/auth/templates/app/jobs/cleanup_expired_sessions_job.rb.tt +30 -0
- data/lib/generators/seams/auth/templates/app/mailers/passwords_mailer.rb.tt +15 -0
- data/lib/generators/seams/auth/templates/app/models/api_token.rb.tt +62 -0
- data/lib/generators/seams/auth/templates/app/models/application_record.rb.tt +7 -0
- data/lib/generators/seams/auth/templates/app/models/current.rb.tt +15 -0
- data/lib/generators/seams/auth/templates/app/models/identity.rb.tt +74 -0
- data/lib/generators/seams/auth/templates/app/models/oauth/provider.rb.tt +48 -0
- data/lib/generators/seams/auth/templates/app/models/session.rb.tt +28 -0
- data/lib/generators/seams/auth/templates/app/services/authenticate_identity.rb.tt +31 -0
- data/lib/generators/seams/auth/templates/app/services/generate_api_token.rb.tt +35 -0
- data/lib/generators/seams/auth/templates/app/services/oauth/authenticator.rb.tt +94 -0
- data/lib/generators/seams/auth/templates/app/services/register_identity.rb.tt +57 -0
- data/lib/generators/seams/auth/templates/app/services/reset_password.rb.tt +41 -0
- data/lib/generators/seams/auth/templates/app/services/revoke_api_token.rb.tt +38 -0
- data/lib/generators/seams/auth/templates/app/views/password_resets/edit.html.erb.tt +12 -0
- data/lib/generators/seams/auth/templates/app/views/password_resets/new.html.erb.tt +11 -0
- data/lib/generators/seams/auth/templates/app/views/passwords_mailer/reset_email.html.erb.tt +7 -0
- data/lib/generators/seams/auth/templates/app/views/registrations/new.html.erb.tt +26 -0
- data/lib/generators/seams/auth/templates/app/views/sessions/_oauth_buttons.html.erb.tt +18 -0
- data/lib/generators/seams/auth/templates/app/views/sessions/new.html.erb.tt +17 -0
- data/lib/generators/seams/auth/templates/config/routes.rb.tt +21 -0
- data/lib/generators/seams/auth/templates/db/migrate/create_auth_api_tokens.rb.tt +26 -0
- data/lib/generators/seams/auth/templates/db/migrate/create_auth_identities.rb.tt +29 -0
- data/lib/generators/seams/auth/templates/db/migrate/create_auth_oauth_providers.rb.tt +35 -0
- data/lib/generators/seams/auth/templates/db/migrate/create_auth_sessions.rb.tt +19 -0
- data/lib/generators/seams/auth/templates/lib/auth.rb.tt +39 -0
- data/lib/generators/seams/auth/templates/lib/concerns/api_authenticatable.rb.tt +58 -0
- data/lib/generators/seams/auth/templates/lib/concerns/authenticatable.rb.tt +32 -0
- data/lib/generators/seams/auth/templates/lib/concerns/authentication.rb.tt +60 -0
- data/lib/generators/seams/auth/templates/lib/configuration.rb.tt +45 -0
- data/lib/generators/seams/auth/templates/lib/engine.rb.tt +46 -0
- data/lib/generators/seams/auth/templates/lib/oauth/abstract.rb.tt +87 -0
- data/lib/generators/seams/auth/templates/lib/oauth/github.rb.tt +112 -0
- data/lib/generators/seams/auth/templates/lib/oauth/google.rb.tt +78 -0
- data/lib/generators/seams/auth/templates/lib/tasks/auth_pii.rake.tt +68 -0
- data/lib/generators/seams/auth/templates/spec/factories/auth.rb.tt +38 -0
- data/lib/generators/seams/auth/templates/spec/mailers/passwords_mailer_spec.rb.tt +37 -0
- data/lib/generators/seams/auth/templates/spec/models/api_token_spec.rb.tt +84 -0
- data/lib/generators/seams/auth/templates/spec/models/identity_spec.rb.tt +56 -0
- data/lib/generators/seams/auth/templates/spec/models/oauth/provider_spec.rb.tt +64 -0
- data/lib/generators/seams/auth/templates/spec/models/session_spec.rb.tt +34 -0
- data/lib/generators/seams/auth/templates/spec/runtime/boot_spec.rb.tt +30 -0
- data/lib/generators/seams/auth/templates/spec/runtime/event_payload_spec.rb.tt +29 -0
- data/lib/generators/seams/auth/templates/spec/runtime/login_flow_spec.rb.tt +45 -0
- data/lib/generators/seams/billing/billing_generator.rb +476 -0
- data/lib/generators/seams/billing/templates/README.md.tt +355 -0
- data/lib/generators/seams/billing/templates/app/controllers/admin/lifetime_passes_controller.rb.tt +84 -0
- data/lib/generators/seams/billing/templates/app/controllers/checkout_controller.rb.tt +92 -0
- data/lib/generators/seams/billing/templates/app/controllers/invoices_controller.rb.tt +63 -0
- data/lib/generators/seams/billing/templates/app/controllers/plans_controller.rb.tt +14 -0
- data/lib/generators/seams/billing/templates/app/controllers/portal_controller.rb.tt +45 -0
- data/lib/generators/seams/billing/templates/app/controllers/subscriptions_controller.rb.tt +119 -0
- data/lib/generators/seams/billing/templates/app/controllers/webhooks_controller.rb.tt +98 -0
- data/lib/generators/seams/billing/templates/app/helpers/currency_helper.rb.tt +44 -0
- data/lib/generators/seams/billing/templates/app/jobs/application_job.rb.tt +6 -0
- data/lib/generators/seams/billing/templates/app/jobs/cancel_subscription_job.rb.tt +39 -0
- data/lib/generators/seams/billing/templates/app/jobs/start_subscription_job.rb.tt +32 -0
- data/lib/generators/seams/billing/templates/app/jobs/webhooks/process_event_job.rb.tt +37 -0
- data/lib/generators/seams/billing/templates/app/models/application_record.rb.tt +7 -0
- data/lib/generators/seams/billing/templates/app/models/invoice.rb.tt +35 -0
- data/lib/generators/seams/billing/templates/app/models/lifetime_pass.rb.tt +60 -0
- data/lib/generators/seams/billing/templates/app/models/plan.rb.tt +95 -0
- data/lib/generators/seams/billing/templates/app/models/subscription.rb.tt +31 -0
- data/lib/generators/seams/billing/templates/app/models/webhook_event.rb.tt +13 -0
- data/lib/generators/seams/billing/templates/app/services/checkout_session_service.rb.tt +25 -0
- data/lib/generators/seams/billing/templates/app/services/customers/find_or_create_service.rb.tt +73 -0
- data/lib/generators/seams/billing/templates/app/services/invoices/sync_service.rb.tt +50 -0
- data/lib/generators/seams/billing/templates/app/services/lifetime/create_lifetime_session_service.rb.tt +82 -0
- data/lib/generators/seams/billing/templates/app/services/lifetime/create_pass_from_checkout_service.rb.tt +88 -0
- data/lib/generators/seams/billing/templates/app/services/lifetime/grant_pass_service.rb.tt +80 -0
- data/lib/generators/seams/billing/templates/app/services/lifetime/revoke_pass_service.rb.tt +59 -0
- data/lib/generators/seams/billing/templates/app/services/portal_session_service.rb.tt +23 -0
- data/lib/generators/seams/billing/templates/app/services/service_result.rb.tt +38 -0
- data/lib/generators/seams/billing/templates/app/services/stripe_service.rb.tt +67 -0
- data/lib/generators/seams/billing/templates/app/services/subscriptions/cancel_service.rb.tt +42 -0
- data/lib/generators/seams/billing/templates/app/services/subscriptions/change_plan_service.rb.tt +48 -0
- data/lib/generators/seams/billing/templates/app/services/subscriptions/reactivate_service.rb.tt +28 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/event_router.rb.tt +54 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handler.rb.tt +93 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/charge_refunded_handler.rb.tt +18 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/checkout_session_completed_handler.rb.tt +58 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/invoice_created_handler.rb.tt +16 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/invoice_finalized_handler.rb.tt +14 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/invoice_handler_base.rb.tt +80 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/invoice_paid_handler.rb.tt +12 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/invoice_payment_failed_handler.rb.tt +12 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/invoice_voided_handler.rb.tt +12 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/payment_failed_handler.rb.tt +15 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/payment_succeeded_handler.rb.tt +19 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/subscription_created_handler.rb.tt +11 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/subscription_deleted_handler.rb.tt +15 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/subscription_handler_base.rb.tt +92 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/subscription_trial_will_end_handler.rb.tt +15 -0
- data/lib/generators/seams/billing/templates/app/services/webhooks/handlers/subscription_updated_handler.rb.tt +11 -0
- data/lib/generators/seams/billing/templates/app/views/admin/lifetime_passes/index.html.erb.tt +36 -0
- data/lib/generators/seams/billing/templates/app/views/admin/lifetime_passes/new.html.erb.tt +37 -0
- data/lib/generators/seams/billing/templates/app/views/checkout/success.html.erb.tt +5 -0
- data/lib/generators/seams/billing/templates/app/views/invoices/index.html.erb.tt +22 -0
- data/lib/generators/seams/billing/templates/app/views/invoices/show.html.erb.tt +14 -0
- data/lib/generators/seams/billing/templates/app/views/plans/index.html.erb.tt +51 -0
- data/lib/generators/seams/billing/templates/app/views/subscriptions/index.html.erb.tt +16 -0
- data/lib/generators/seams/billing/templates/app/views/subscriptions/show.html.erb.tt +25 -0
- data/lib/generators/seams/billing/templates/config/routes.rb.tt +39 -0
- data/lib/generators/seams/billing/templates/db/migrate/create_billing_invoices.rb.tt +32 -0
- data/lib/generators/seams/billing/templates/db/migrate/create_billing_lifetime_passes.rb.tt +43 -0
- data/lib/generators/seams/billing/templates/db/migrate/create_billing_plans.rb.tt +31 -0
- data/lib/generators/seams/billing/templates/db/migrate/create_billing_subscriptions.rb.tt +33 -0
- data/lib/generators/seams/billing/templates/db/migrate/create_billing_webhook_events.rb.tt +24 -0
- data/lib/generators/seams/billing/templates/lib/billing.rb.tt +34 -0
- data/lib/generators/seams/billing/templates/lib/concerns/billable.rb.tt +100 -0
- data/lib/generators/seams/billing/templates/lib/configuration.rb.tt +52 -0
- data/lib/generators/seams/billing/templates/lib/engine.rb.tt +72 -0
- data/lib/generators/seams/billing/templates/lib/gateways/abstract.rb.tt +65 -0
- data/lib/generators/seams/billing/templates/lib/gateways/adyen.rb.tt +16 -0
- data/lib/generators/seams/billing/templates/lib/gateways/paddle.rb.tt +22 -0
- data/lib/generators/seams/billing/templates/lib/gateways/stripe.rb.tt +155 -0
- data/lib/generators/seams/billing/templates/lib/stripe/client.rb.tt +101 -0
- data/lib/generators/seams/billing/templates/lib/stripe/webhook_signature.rb.tt +43 -0
- data/lib/generators/seams/billing/templates/lib/tasks/billing_check.rake.tt +34 -0
- data/lib/generators/seams/billing/templates/spec/factories/billing.rb.tt +65 -0
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/charge_refunded.json.tt +19 -0
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/checkout_session_completed.json.tt +17 -0
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/customer_subscription_created.json.tt +25 -0
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/customer_subscription_deleted.json.tt +17 -0
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/customer_subscription_trial_will_end.json.tt +17 -0
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/customer_subscription_updated.json.tt +28 -0
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/invoice_created.json.tt +18 -0
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/invoice_finalized.json.tt +18 -0
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/invoice_paid.json.tt +19 -0
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/invoice_payment_failed.json.tt +20 -0
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/invoice_voided.json.tt +18 -0
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/payment_intent_payment_failed.json.tt +21 -0
- data/lib/generators/seams/billing/templates/spec/fixtures/stripe/payment_intent_succeeded.json.tt +17 -0
- data/lib/generators/seams/billing/templates/spec/gateways/contract_spec.rb.tt +11 -0
- data/lib/generators/seams/billing/templates/spec/gateways/stripe_spec.rb.tt +53 -0
- data/lib/generators/seams/billing/templates/spec/models/plan_spec.rb.tt +81 -0
- data/lib/generators/seams/billing/templates/spec/models/subscription_spec.rb.tt +43 -0
- data/lib/generators/seams/billing/templates/spec/runtime/boot_spec.rb.tt +38 -0
- data/lib/generators/seams/billing/templates/spec/runtime/webhook_handlers_spec.rb.tt +382 -0
- data/lib/generators/seams/billing/templates/spec/support/shared_examples/a_billing_gateway.rb.tt +100 -0
- data/lib/generators/seams/billing/templates/spec/support/stripe_helpers.rb.tt +59 -0
- data/lib/generators/seams/core/core_generator.rb +191 -0
- data/lib/generators/seams/core/templates/README.md.tt +45 -0
- data/lib/generators/seams/core/templates/app/controllers/concerns/has_current_attributes.rb.tt +71 -0
- data/lib/generators/seams/core/templates/app/models/application_record.rb.tt +7 -0
- data/lib/generators/seams/core/templates/app/models/audit_log.rb.tt +19 -0
- data/lib/generators/seams/core/templates/app/models/concerns/auditable.rb.tt +64 -0
- data/lib/generators/seams/core/templates/app/models/concerns/sluggable.rb.tt +53 -0
- data/lib/generators/seams/core/templates/app/models/concerns/soft_deletable.rb.tt +37 -0
- data/lib/generators/seams/core/templates/app/models/concerns/tenant_scoped.rb.tt +39 -0
- data/lib/generators/seams/core/templates/app/models/current.rb.tt +16 -0
- data/lib/generators/seams/core/templates/app/services/event_publisher.rb.tt +23 -0
- data/lib/generators/seams/core/templates/app/validators/email_format_validator.rb.tt +21 -0
- data/lib/generators/seams/core/templates/db/migrate/create_core_audit_logs.rb.tt +29 -0
- data/lib/generators/seams/core/templates/lib/core.rb.tt +8 -0
- data/lib/generators/seams/core/templates/lib/engine.rb.tt +28 -0
- data/lib/generators/seams/core/templates/spec/concerns/auditable_spec.rb.tt +39 -0
- data/lib/generators/seams/core/templates/spec/concerns/sluggable_spec.rb.tt +29 -0
- data/lib/generators/seams/core/templates/spec/models/audit_log_spec.rb.tt +22 -0
- data/lib/generators/seams/core/templates/spec/runtime/boot_spec.rb.tt +25 -0
- data/lib/generators/seams/core/templates/spec/validators/email_format_validator_spec.rb.tt +29 -0
- data/lib/generators/seams/engine/engine_generator.rb +165 -0
- data/lib/generators/seams/engine/templates/Gemfile.tt +19 -0
- data/lib/generators/seams/engine/templates/LICENSE.tt +21 -0
- data/lib/generators/seams/engine/templates/README.md.tt +40 -0
- data/lib/generators/seams/engine/templates/Rakefile.tt +14 -0
- data/lib/generators/seams/engine/templates/app/application_controller.rb.tt +6 -0
- data/lib/generators/seams/engine/templates/app/application_record.rb.tt +16 -0
- data/lib/generators/seams/engine/templates/config/locales/en.yml.tt +14 -0
- data/lib/generators/seams/engine/templates/config/routes.rb.tt +4 -0
- data/lib/generators/seams/engine/templates/gemspec.tt +20 -0
- data/lib/generators/seams/engine/templates/host_initializer.rb.tt +13 -0
- data/lib/generators/seams/engine/templates/lib/engine.rb.tt +27 -0
- data/lib/generators/seams/engine/templates/lib/root.rb.tt +7 -0
- data/lib/generators/seams/engine/templates/lib/version.rb.tt +5 -0
- data/lib/generators/seams/engine/templates/rubocop.yml.tt +55 -0
- data/lib/generators/seams/engine/templates/spec/example_spec.rb.tt +16 -0
- data/lib/generators/seams/engine/templates/spec/spec_helper.rb.tt +23 -0
- data/lib/generators/seams/install/install_generator.rb +211 -0
- data/lib/generators/seams/install/templates/Dockerfile.tt +52 -0
- data/lib/generators/seams/install/templates/Procfile.tt +14 -0
- data/lib/generators/seams/install/templates/bin_seams.tt +107 -0
- data/lib/generators/seams/install/templates/ci.yml.tt +123 -0
- data/lib/generators/seams/install/templates/deploy.yml.tt +63 -0
- data/lib/generators/seams/install/templates/doc/ARCHITECTURE.md.tt +86 -0
- data/lib/generators/seams/install/templates/docker-entrypoint.tt +27 -0
- data/lib/generators/seams/install/templates/rubocop.yml.tt +33 -0
- data/lib/generators/seams/install/templates/ruby-version.tt +1 -0
- data/lib/generators/seams/install/templates/script/collate_coverage.rb.tt +33 -0
- data/lib/generators/seams/install/templates/script/run_affected_tests.sh.tt +64 -0
- data/lib/generators/seams/install/templates/seams.rake.tt +65 -0
- data/lib/generators/seams/install/templates/seams.rb.tt +9 -0
- data/lib/generators/seams/install/templates/seams_engines.rb.tt +15 -0
- data/lib/generators/seams/notifications/notifications_generator.rb +395 -0
- data/lib/generators/seams/notifications/templates/README.md.tt +269 -0
- data/lib/generators/seams/notifications/templates/app/channels/notification_channel.rb.tt +36 -0
- data/lib/generators/seams/notifications/templates/app/controllers/notifications_controller.rb.tt +58 -0
- data/lib/generators/seams/notifications/templates/app/controllers/preferences_controller.rb.tt +54 -0
- data/lib/generators/seams/notifications/templates/app/javascript/controllers/notification_bell_controller.js.tt +34 -0
- data/lib/generators/seams/notifications/templates/app/jobs/application_job.rb.tt +6 -0
- data/lib/generators/seams/notifications/templates/app/jobs/create_notification_job.rb.tt +31 -0
- data/lib/generators/seams/notifications/templates/app/jobs/send_due_notifications_job.rb.tt +22 -0
- data/lib/generators/seams/notifications/templates/app/jobs/send_notification_job.rb.tt +13 -0
- data/lib/generators/seams/notifications/templates/app/mailers/application_mailer.rb.tt +12 -0
- data/lib/generators/seams/notifications/templates/app/mailers/notification_mailer.rb.tt +23 -0
- data/lib/generators/seams/notifications/templates/app/models/application_record.rb.tt +7 -0
- data/lib/generators/seams/notifications/templates/app/models/delivery.rb.tt +13 -0
- data/lib/generators/seams/notifications/templates/app/models/notification.rb.tt +218 -0
- data/lib/generators/seams/notifications/templates/app/models/notification_preference.rb.tt +29 -0
- data/lib/generators/seams/notifications/templates/app/models/strategies/email.rb.tt +38 -0
- data/lib/generators/seams/notifications/templates/app/models/strategies/in_app.rb.tt +26 -0
- data/lib/generators/seams/notifications/templates/app/models/strategies/sms.rb.tt +33 -0
- data/lib/generators/seams/notifications/templates/app/subscribers/auth_subscriber.rb.tt +71 -0
- data/lib/generators/seams/notifications/templates/app/subscribers/billing_subscriber.rb.tt +127 -0
- data/lib/generators/seams/notifications/templates/app/views/layouts/notifications/mailer.html.erb.tt +22 -0
- data/lib/generators/seams/notifications/templates/app/views/layouts/notifications/mailer.text.erb.tt +4 -0
- data/lib/generators/seams/notifications/templates/app/views/notifications/_bell.html.erb.tt +15 -0
- data/lib/generators/seams/notifications/templates/app/views/notifications/index.html.erb.tt +15 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/invoice_failed.html.erb.tt +4 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/invoice_failed.text.erb.tt +4 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/invoice_paid.html.erb.tt +3 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/invoice_paid.text.erb.tt +3 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/lifetime_granted.html.erb.tt +5 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/lifetime_granted.text.erb.tt +5 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/lifetime_purchased.html.erb.tt +5 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/lifetime_purchased.text.erb.tt +5 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/subscription_canceled.html.erb.tt +4 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/subscription_canceled.text.erb.tt +4 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/subscription_started.html.erb.tt +4 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/subscription_started.text.erb.tt +5 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/subscription_updated.html.erb.tt +3 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/billing/subscription_updated.text.erb.tt +3 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/default.html.erb.tt +10 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/default.text.erb.tt +11 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/welcome.html.erb.tt +6 -0
- data/lib/generators/seams/notifications/templates/app/views/templates/welcome.text.erb.tt +6 -0
- data/lib/generators/seams/notifications/templates/config/initializers/notifications.rb.tt +58 -0
- data/lib/generators/seams/notifications/templates/config/routes.rb.tt +17 -0
- data/lib/generators/seams/notifications/templates/db/migrate/create_notification_deliveries.rb.tt +16 -0
- data/lib/generators/seams/notifications/templates/db/migrate/create_notification_preferences.rb.tt +25 -0
- data/lib/generators/seams/notifications/templates/db/migrate/create_notifications.rb.tt +35 -0
- data/lib/generators/seams/notifications/templates/lib/adapters/abstract.rb.tt +20 -0
- data/lib/generators/seams/notifications/templates/lib/adapters/action_mailer.rb.tt +17 -0
- data/lib/generators/seams/notifications/templates/lib/adapters/null_sms.rb.tt +23 -0
- data/lib/generators/seams/notifications/templates/lib/concerns/notifiable.rb.tt +135 -0
- data/lib/generators/seams/notifications/templates/lib/configuration.rb.tt +24 -0
- data/lib/generators/seams/notifications/templates/lib/engine.rb.tt +35 -0
- data/lib/generators/seams/notifications/templates/lib/notifications.rb.tt +75 -0
- data/lib/generators/seams/notifications/templates/lib/type_registry.rb.tt +74 -0
- data/lib/generators/seams/notifications/templates/spec/factories/notifications.rb.tt +53 -0
- data/lib/generators/seams/notifications/templates/spec/models/delivery_spec.rb.tt +28 -0
- data/lib/generators/seams/notifications/templates/spec/models/notification_preference_spec.rb.tt +46 -0
- data/lib/generators/seams/notifications/templates/spec/models/notification_spec.rb.tt +60 -0
- data/lib/generators/seams/notifications/templates/spec/runtime/bell_broadcast_spec.rb.tt +59 -0
- data/lib/generators/seams/notifications/templates/spec/runtime/billing_subscriber_skip_spec.rb.tt +87 -0
- data/lib/generators/seams/notifications/templates/spec/runtime/boot_spec.rb.tt +39 -0
- data/lib/generators/seams/notifications/templates/spec/runtime/schedule_round_trip_spec.rb.tt +55 -0
- data/lib/generators/seams/remove/remove_generator.rb +259 -0
- data/lib/generators/seams/teams/teams_generator.rb +298 -0
- data/lib/generators/seams/teams/templates/README.md.tt +88 -0
- data/lib/generators/seams/teams/templates/app/controllers/invitations_controller.rb.tt +102 -0
- data/lib/generators/seams/teams/templates/app/controllers/memberships_controller.rb.tt +54 -0
- data/lib/generators/seams/teams/templates/app/controllers/teams_controller.rb.tt +68 -0
- data/lib/generators/seams/teams/templates/app/jobs/application_job.rb.tt +6 -0
- data/lib/generators/seams/teams/templates/app/mailers/invitation_mailer.rb.tt +34 -0
- data/lib/generators/seams/teams/templates/app/models/application_record.rb.tt +7 -0
- data/lib/generators/seams/teams/templates/app/models/current.rb.tt +30 -0
- data/lib/generators/seams/teams/templates/app/models/invitation.rb.tt +36 -0
- data/lib/generators/seams/teams/templates/app/models/membership.rb.tt +36 -0
- data/lib/generators/seams/teams/templates/app/models/team.rb.tt +32 -0
- data/lib/generators/seams/teams/templates/app/subscribers/invitation_subscriber.rb.tt +36 -0
- data/lib/generators/seams/teams/templates/app/views/invitation_mailer/invite.text.erb.tt +8 -0
- data/lib/generators/seams/teams/templates/app/views/invitations/index.html.erb.tt +44 -0
- data/lib/generators/seams/teams/templates/app/views/memberships/index.html.erb.tt +32 -0
- data/lib/generators/seams/teams/templates/app/views/teams/edit.html.erb.tt +28 -0
- data/lib/generators/seams/teams/templates/app/views/teams/index.html.erb.tt +15 -0
- data/lib/generators/seams/teams/templates/app/views/teams/new.html.erb.tt +24 -0
- data/lib/generators/seams/teams/templates/app/views/teams/show.html.erb.tt +17 -0
- data/lib/generators/seams/teams/templates/config/routes.rb.tt +19 -0
- data/lib/generators/seams/teams/templates/db/migrate/create_team_invitations.rb.tt +24 -0
- data/lib/generators/seams/teams/templates/db/migrate/create_team_memberships.rb.tt +25 -0
- data/lib/generators/seams/teams/templates/db/migrate/create_teams.rb.tt +18 -0
- data/lib/generators/seams/teams/templates/lib/concerns/account_scoped.rb.tt +79 -0
- data/lib/generators/seams/teams/templates/lib/concerns/authorization.rb.tt +55 -0
- data/lib/generators/seams/teams/templates/lib/configuration.rb.tt +45 -0
- data/lib/generators/seams/teams/templates/lib/engine.rb.tt +51 -0
- data/lib/generators/seams/teams/templates/lib/teams.rb.tt +22 -0
- data/lib/generators/seams/teams/templates/spec/factories/teams.rb.tt +47 -0
- data/lib/generators/seams/teams/templates/spec/models/invitation_spec.rb.tt +25 -0
- data/lib/generators/seams/teams/templates/spec/models/membership_spec.rb.tt +29 -0
- data/lib/generators/seams/teams/templates/spec/models/team_spec.rb.tt +23 -0
- data/lib/generators/seams/teams/templates/spec/runtime/boot_spec.rb.tt +32 -0
- data/lib/seams/cli/list.rb +111 -0
- data/lib/seams/cli/quality.rb +99 -0
- data/lib/seams/cli/resolve.rb +276 -0
- data/lib/seams/cli/test_changed.rb +116 -0
- data/lib/seams/cli.rb +48 -0
- data/lib/seams/configuration.rb +19 -0
- data/lib/seams/cops/known_queue_names.rb +42 -0
- data/lib/seams/cops/migration_comments.rb +68 -0
- data/lib/seams/cops/no_cross_engine_dependency.rb +58 -0
- data/lib/seams/cops/no_cross_engine_model_access.rb +153 -0
- data/lib/seams/cops.rb +18 -0
- data/lib/seams/event_registry.rb +49 -0
- data/lib/seams/events/adapter.rb +24 -0
- data/lib/seams/events/adapters/active_support.rb +31 -0
- data/lib/seams/events/publisher.rb +178 -0
- data/lib/seams/events.rb +39 -0
- data/lib/seams/generators/dummy_app_writer.rb +424 -0
- data/lib/seams/generators/eject_aware.rb +102 -0
- data/lib/seams/generators/follow_up_generator.rb +148 -0
- data/lib/seams/generators/host_injector.rb +124 -0
- data/lib/seams/generators/sibling_rubocop_writer.rb +77 -0
- data/lib/seams/generators/splicer.rb +217 -0
- data/lib/seams/observability/adapter.rb +33 -0
- data/lib/seams/observability/adapters/rails_logger.rb +59 -0
- data/lib/seams/observability.rb +34 -0
- data/lib/seams/runtime.rb +23 -0
- data/lib/seams/version.rb +5 -0
- data/lib/seams.rb +23 -0
- 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
|
data/lib/generators/seams/admin/templates/app/policies/admin/tenant/teams_membership_policy.rb.tt
ADDED
|
@@ -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
|