gemstack-cli 0.2.5 → 0.3.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 (118) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +3 -25
  3. data/README.md +6 -17
  4. data/exe/gemstack +8 -1
  5. metadata +6 -159
  6. data/lib/gemstack/cli/add_generator.rb +0 -239
  7. data/lib/gemstack/cli/app_generator.rb +0 -142
  8. data/lib/gemstack/cli/commands/db.rb +0 -135
  9. data/lib/gemstack/cli/commands/jobs.rb +0 -96
  10. data/lib/gemstack/cli/controller_generator.rb +0 -73
  11. data/lib/gemstack/cli/deploy_generator.rb +0 -102
  12. data/lib/gemstack/cli/doctor.rb +0 -308
  13. data/lib/gemstack/cli/generator.rb +0 -144
  14. data/lib/gemstack/cli/job_generator.rb +0 -48
  15. data/lib/gemstack/cli/migration_generator.rb +0 -52
  16. data/lib/gemstack/cli/policy_generator.rb +0 -35
  17. data/lib/gemstack/cli/project.rb +0 -53
  18. data/lib/gemstack/cli/resource_generator.rb +0 -170
  19. data/lib/gemstack/cli/resource_spec.rb +0 -95
  20. data/lib/gemstack/cli.rb +0 -304
  21. data/templates/app/Gemfile.tt +0 -44
  22. data/templates/app/README.md.tt +0 -29
  23. data/templates/app/app/controllers/application_controller.rb +0 -6
  24. data/templates/app/app/jobs/application_job.rb +0 -9
  25. data/templates/app/app/mailers/application_mailer.rb +0 -8
  26. data/templates/app/app/mailers/templates/dot_keep +0 -0
  27. data/templates/app/app/models/application_model.rb +0 -19
  28. data/templates/app/app/serializers/application_serializer.rb +0 -6
  29. data/templates/app/bin/gemstack +0 -7
  30. data/templates/app/config/app.rb.tt +0 -54
  31. data/templates/app/config/database.yml.tt +0 -61
  32. data/templates/app/config/environments/development.rb.tt +0 -35
  33. data/templates/app/config/environments/production.rb.tt +0 -40
  34. data/templates/app/config/environments/test.rb.tt +0 -21
  35. data/templates/app/config/puma.rb +0 -21
  36. data/templates/app/config/routes.rb +0 -10
  37. data/templates/app/config.ru +0 -6
  38. data/templates/app/db/migrations/dot_keep +0 -0
  39. data/templates/app/db/seeds.rb +0 -5
  40. data/templates/app/dot_env.example.tt +0 -19
  41. data/templates/app/dot_gitignore +0 -16
  42. data/templates/app/dot_node-version.tt +0 -1
  43. data/templates/app/dot_nvmrc.tt +0 -1
  44. data/templates/app/dot_ruby-version.tt +0 -1
  45. data/templates/app/dot_tool-versions.tt +0 -4
  46. data/templates/app/test/health_test.rb +0 -18
  47. data/templates/app/test/test_helper.rb.tt +0 -25
  48. data/templates/auth/app/controllers/api_tokens_controller.rb +0 -35
  49. data/templates/auth/app/controllers/email_verifications_controller.rb +0 -31
  50. data/templates/auth/app/controllers/password_resets_controller.rb +0 -40
  51. data/templates/auth/app/controllers/registrations_controller.rb +0 -19
  52. data/templates/auth/app/controllers/sessions_controller.rb +0 -29
  53. data/templates/auth/app/mailers/auth_mailer.rb +0 -20
  54. data/templates/auth/app/mailers/templates/auth_mailer/email_verification.html.erb +0 -2
  55. data/templates/auth/app/mailers/templates/auth_mailer/email_verification.text.erb +0 -2
  56. data/templates/auth/app/mailers/templates/auth_mailer/password_reset.html.erb +0 -3
  57. data/templates/auth/app/mailers/templates/auth_mailer/password_reset.text.erb +0 -6
  58. data/templates/auth/app/models/auth_token.rb +0 -15
  59. data/templates/auth/app/models/user.rb +0 -12
  60. data/templates/auth/app/policies/application_policy.rb +0 -6
  61. data/templates/auth/app/serializers/api_token_serializer.rb +0 -6
  62. data/templates/auth/app/serializers/new_api_token_serializer.rb +0 -9
  63. data/templates/auth/app/serializers/user_serializer.rb +0 -6
  64. data/templates/auth/db/migrations/%timestamp%_create_auth_tables.rb +0 -41
  65. data/templates/auth/frontend/app/account/page.tsx +0 -116
  66. data/templates/auth/frontend/app/forgot-password/page.tsx +0 -48
  67. data/templates/auth/frontend/app/login/page.tsx +0 -26
  68. data/templates/auth/frontend/app/reset-password/page.tsx +0 -11
  69. data/templates/auth/frontend/app/signup/page.tsx +0 -27
  70. data/templates/auth/frontend/app/verify-email/page.tsx +0 -11
  71. data/templates/auth/frontend/components/auth/CredentialsForm.tsx +0 -58
  72. data/templates/auth/frontend/components/auth/ResetPasswordForm.tsx +0 -43
  73. data/templates/auth/frontend/components/auth/VerifyEmail.tsx +0 -28
  74. data/templates/auth/frontend/lib/auth.ts +0 -113
  75. data/templates/auth/test/controllers/auth_test.rb +0 -108
  76. data/templates/controller/app/controllers/%file_name%_controller.rb.tt +0 -10
  77. data/templates/controller/test/controllers/%file_name%_controller_test.rb.tt +0 -14
  78. data/templates/deploy/Caddyfile.tt +0 -19
  79. data/templates/deploy/Dockerfile.tt +0 -63
  80. data/templates/deploy/Procfile.tt +0 -6
  81. data/templates/deploy/compose.yaml.tt +0 -129
  82. data/templates/deploy/dot_dockerignore +0 -14
  83. data/templates/frontend/app/globals.css +0 -171
  84. data/templates/frontend/app/layout.tsx.tt +0 -19
  85. data/templates/frontend/app/page.module.css +0 -315
  86. data/templates/frontend/app/page.tsx.tt +0 -183
  87. data/templates/frontend/app/providers.tsx +0 -24
  88. data/templates/frontend/lib/gemstack/client.ts +0 -125
  89. data/templates/frontend/next-env.d.ts +0 -5
  90. data/templates/frontend/next.config.ts +0 -17
  91. data/templates/frontend/package.json.tt +0 -23
  92. data/templates/frontend/tsconfig.json +0 -21
  93. data/templates/job/app/jobs/%file_name%.rb.tt +0 -15
  94. data/templates/job/test/jobs/%file_name%_test.rb.tt +0 -15
  95. data/templates/migration/db/migrations/%timestamp%_%file_name%.rb.tt +0 -17
  96. data/templates/policy/app/policies/%file_name%_policy.rb.tt +0 -21
  97. data/templates/policy/test/policies/%file_name%_policy_test.rb.tt +0 -9
  98. data/templates/realtime/config/channels.rb +0 -14
  99. data/templates/realtime/frontend/lib/gemstack/realtime.ts +0 -120
  100. data/templates/resource/controller/app/controllers/%plural%_controller.rb.tt +0 -51
  101. data/templates/resource/controller/test/controllers/%plural%_controller_test.rb.tt +0 -74
  102. data/templates/resource/frontend/frontend/app/%url_segment%/[id]/edit/page.tsx.tt +0 -45
  103. data/templates/resource/frontend/frontend/app/%url_segment%/[id]/page.tsx.tt +0 -49
  104. data/templates/resource/frontend/frontend/app/%url_segment%/new/page.tsx.tt +0 -27
  105. data/templates/resource/frontend/frontend/app/%url_segment%/page.tsx.tt +0 -51
  106. data/templates/resource/frontend/frontend/components/%url_segment%/%class_name%Card.tsx.tt +0 -15
  107. data/templates/resource/frontend/frontend/components/%url_segment%/%class_name%Form.tsx.tt +0 -98
  108. data/templates/resource/frontend/frontend/components/%url_segment%/%class_name%Table.tsx.tt +0 -36
  109. data/templates/resource/frontend/frontend/lib/format.ts +0 -12
  110. data/templates/resource/frontend/frontend/lib/queries/%url_segment%.ts.tt +0 -64
  111. data/templates/resource/migration/db/migrations/%timestamp%_create_%table%.rb.tt +0 -18
  112. data/templates/resource/model/app/models/%file_name%.rb.tt +0 -15
  113. data/templates/resource/model/test/models/%file_name%_test.rb.tt +0 -29
  114. data/templates/resource/serializer/app/serializers/%file_name%_serializer.rb.tt +0 -7
  115. data/templates/storage/app/controllers/uploads_controller.rb.tt +0 -33
  116. data/templates/storage/app/serializers/upload_serializer.rb +0 -11
  117. data/templates/storage/frontend/lib/upload.ts +0 -52
  118. data/templates/storage/test/controllers/uploads_test.rb.tt +0 -30
@@ -1,19 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # The base class for this app's models — add plugins, validations and helpers
4
- # every model should share here. `class Product < ApplicationModel` maps to the
5
- # products table (docs/models.md).
6
- #
7
- # Created with Class.new on purpose: `class ApplicationModel < GemStack::Model`
8
- # would make Sequel look for an "application_models" table.
9
- ApplicationModel = Class.new(GemStack::Model) # rubocop:disable Style/EmptyClassDefinition
10
-
11
- class ApplicationModel
12
- # Sequel plugins for every model, e.g.:
13
- # plugin :dirty # previous values after an update (column_changes)
14
-
15
- # Shared query helpers:
16
- # dataset_module do
17
- # def recent = order(Sequel.desc(:created_at))
18
- # end
19
- end
@@ -1,6 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # The base class for this app's serializers (docs/serialization.md): attributes
4
- # and helpers defined here are shared by every serializer.
5
- class ApplicationSerializer < GemStack::Serializer
6
- end
@@ -1,7 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- # Runs the GemStack CLI from this application's bundle.
5
- ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
6
- require "bundler/setup"
7
- load Gem.bin_path("gemstack-cli", "gemstack")
@@ -1,54 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "bundler/setup"
4
- require "gemstack"
5
-
6
- # Sets the application root and loads .env files (development/test).
7
- GemStack.setup(root: File.expand_path("..", __dir__))
8
-
9
- # Require the gems listed in the Gemfile for the current environment.
10
- Bundler.require(:default, GemStack.env.to_sym)
11
-
12
- GemStack.configure do |config|
13
- config.name = "<%= name %>"
14
-
15
- # Settings for every environment. Everything below is optional: each line
16
- # shows the default — uncomment one to change it. Per-environment settings go
17
- # in config/environments/*.rb. All settings: docs/configuration.md.
18
-
19
- # ── HTTP ────────────────────────────────────────────────────────────────
20
- # config.http.api_path = "/api" # where the API lives (routes are relative to it)
21
- # config.http.max_body_size = 10 * 1024 * 1024 # bytes; larger requests get 413
22
- # config.http.json = :json # or :oj (add gem "oj"), or your own codec
23
- # config.http.cors.origins = [] # only for frontends on another domain
24
- # config.http.middleware.use MyMiddleware # add / insert_before / swap / delete
25
-
26
- # ── Pagination: paginate(...) in controllers, ?page=2&per_page=50 (docs/pagination.md) ──
27
- # config.http.pagination.per_page = 25 # records per page when the client doesn't say
28
- # config.http.pagination.max_per_page = 100 # the most a client may ask for with ?per_page=
29
-
30
- # ── Responses ───────────────────────────────────────────────────────────
31
- # config.http.compression.enabled = true # gzip/Brotli responses over 1 KB (add gem "brotli" for br)
32
- # config.http.etags = true # ETag + 304 Not Modified for GET responses
33
-
34
- # ── Logging ─────────────────────────────────────────────────────────────
35
- # config.logger.level = :debug # :info in production (or GEMSTACK_LOG_LEVEL)
36
- # config.filter_parameters += %w[iban] # masked in logs, next to password, token, secret…
37
- <% if database? -%>
38
-
39
- # ── Database — the connection is in config/database.yml (DATABASE_URL overrides it) ──
40
- # config.db.pool_size = 5 # default: GEMSTACK_MAX_THREADS (one per Puma thread)
41
- # config.db.statement_timeout = 5_000 # ms
42
- <% end -%>
43
-
44
- # ── Cache (GemStack.cache) ──────────────────────────────────────────────
45
- # config.cache.store = :memory # :redis to share it between processes (gem "redis-client")
46
- # config.cache.default_expires_in = nil # seconds; nil = until evicted
47
-
48
- # ── Background jobs ─────────────────────────────────────────────────────
49
- # config.jobs.default_queue = "default"
50
- # config.jobs.default_max_attempts = 10 # then the job is kept as failed
51
-
52
- # ── Mail ────────────────────────────────────────────────────────────────
53
- # config.mail.default_from = "<%= name %> <no-reply@localhost>" # or MAIL_FROM
54
- end
@@ -1,61 +0,0 @@
1
- # Database connection (docs/database.md) — the same format as Rails' database.yml.
2
- # DATABASE_URL (TEST_DATABASE_URL in tests) overrides this file when set, which
3
- # is what most hosting platforms provide. Adapters: sqlite3, postgresql, mysql2,
4
- # trilogy (add the matching gem: sqlite3, pg, mysql2, trilogy).
5
- default: &default
6
- <% if database_adapter == "sqlite3" -%>
7
- adapter: sqlite3
8
- pool: <%%= ENV.fetch("GEMSTACK_MAX_THREADS", 5) %>
9
-
10
- development:
11
- <<: *default
12
- database: db/development.sqlite3
13
-
14
- test:
15
- <<: *default
16
- database: db/test.sqlite3
17
-
18
- # Keep the file on persistent storage (a volume); or set DATABASE_URL=sqlite3:/data/app.sqlite3.
19
- production:
20
- <<: *default
21
- database: db/production.sqlite3
22
- <% elsif database_adapter == "postgresql" -%>
23
- adapter: postgresql
24
- pool: <%%= ENV.fetch("GEMSTACK_MAX_THREADS", 5) %>
25
- # Without host/username, the local server is used as your OS user.
26
- # host: localhost
27
- # username: <%= database_name %>
28
- # password: <%%= ENV["DATABASE_PASSWORD"] %>
29
-
30
- development:
31
- <<: *default
32
- database: <%= database_name %>_development
33
-
34
- test:
35
- <<: *default
36
- database: <%= database_name %>_test
37
-
38
- # In production, DATABASE_URL (postgres://user:password@host:5432/name) usually replaces this.
39
- production:
40
- <<: *default
41
- database: <%= database_name %>_production
42
- <% else -%>
43
- adapter: <%= database_adapter %>
44
- pool: <%%= ENV.fetch("GEMSTACK_MAX_THREADS", 5) %>
45
- host: <%%= ENV.fetch("DATABASE_HOST", "127.0.0.1") %>
46
- username: root
47
- password: <%%= ENV["DATABASE_PASSWORD"] %>
48
-
49
- development:
50
- <<: *default
51
- database: <%= database_name %>_development
52
-
53
- test:
54
- <<: *default
55
- database: <%= database_name %>_test
56
-
57
- # In production, DATABASE_URL (<%= database_adapter %>://user:password@host:3306/name) usually replaces this.
58
- production:
59
- <<: *default
60
- database: <%= database_name %>_production
61
- <% end -%>
@@ -1,35 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Development settings: applied after config/app.rb when GEMSTACK_ENV=development
4
- # (the default). Each line shows the default value — uncomment one to change it.
5
- # All settings: docs/configuration.md.
6
- GemStack.configure do |config|
7
- # ── Code ────────────────────────────────────────────────────────────────
8
- # config.reload_code = true # reload app/ and config/routes.rb when they change
9
- # config.eager_load = false # load app/ lazily, for a faster boot
10
- # config.jit = nil # :yjit or :zjit to try Ruby's JIT locally
11
-
12
- # ── Logging ─────────────────────────────────────────────────────────────
13
- # config.logger.level = :debug # :debug, :info, :warn, :error (or GEMSTACK_LOG_LEVEL)
14
- # config.logger.format = :pretty # readable lines; :json to see what production logs
15
-
16
- # ── HTTP ────────────────────────────────────────────────────────────────
17
- # config.http.show_exceptions = true # 500s carry the exception; browsers get an error page
18
- # config.contract.docs = true # interactive API docs at /api/docs
19
-
20
- # ── gemstack dev ────────────────────────────────────────────────────────
21
- # config.dev.port = 3000 # the one public port (or PORT)
22
- # config.dev.jobs_command = %w[bundle exec gemstack jobs] # nil: don't start a jobs worker
23
- <% if database? -%>
24
-
25
- # ── Database (connection: config/database.yml) ──────────────────────────
26
- # config.db.log_queries = true # every SQL statement at DEBUG
27
- # config.db.slow_query_ms = 500 # slower queries are logged at WARN
28
- <% end -%>
29
-
30
- # ── Cache ───────────────────────────────────────────────────────────────
31
- # config.cache.store = :memory # :null to switch caching off, :redis to share it
32
-
33
- # ── Mail ────────────────────────────────────────────────────────────────
34
- # config.mail.delivery = :log # saved to tmp/mail (.eml + .html); :smtp really sends
35
- end
@@ -1,40 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Production settings: applied after config/app.rb when GEMSTACK_ENV=production.
4
- # Each line shows the default value — uncomment one to change it.
5
- #
6
- # Secrets and connections come from environment variables, never from files:
7
- # SECRET_KEY_BASE, <% if database? %>DATABASE_URL, <% end %>SMTP_URL, MAIL_FROM, APP_URL, REDIS_URL…
8
- # `gemstack doctor --production` checks what's missing (docs/deployment.md).
9
- GemStack.configure do |config|
10
- # ── Code ────────────────────────────────────────────────────────────────
11
- # config.eager_load = true # load all code at boot (copy-on-write friendly with Puma workers)
12
- # config.jit = :yjit # Ruby's JIT (or GEMSTACK_JIT=yjit|zjit|off)
13
-
14
- # ── Logging ─────────────────────────────────────────────────────────────
15
- # config.logger.level = :info
16
- # config.logger.format = :json # one JSON object per line, with the request id
17
-
18
- # ── HTTP ────────────────────────────────────────────────────────────────
19
- # config.http.show_exceptions = false # never send exception details to clients
20
- # config.http.hsts = "max-age=63072000; includeSubDomains" # on HTTPS requests; nil if your proxy sets it
21
- # config.http.cors.origins = ["https://app.example.com"] # only if the frontend is on another domain
22
- # config.http.max_body_size = 10 * 1024 * 1024 # larger requests get 413
23
- <% if database? -%>
24
-
25
- # ── Database ────────────────────────────────────────────────────────────
26
- # config.db.pool_size = 5 # connections per process (GEMSTACK_MAX_THREADS)
27
- # config.db.statement_timeout = 5_000 # ms; stop runaway queries (PostgreSQL, MySQL)
28
- # config.db.slow_query_ms = 500 # slower queries are logged at WARN
29
- <% end -%>
30
-
31
- # ── Cache: :memory is per process — use Redis with several processes or servers ──
32
- # config.cache.store = :redis # add gem "redis-client"; REDIS_URL
33
-
34
- # ── Background jobs (`gemstack jobs`) ───────────────────────────────────
35
- # config.jobs.concurrency = 5 # threads per worker (or GEMSTACK_JOB_CONCURRENCY)
36
-
37
- # ── Mail ────────────────────────────────────────────────────────────────
38
- # config.mail.delivery = :smtp # SMTP_URL=smtp://user:password@smtp.example.com:587
39
- # config.mail.default_from = "App <hello@example.com>" # or MAIL_FROM
40
- end
@@ -1,21 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Test settings: applied after config/app.rb when tests run (`gemstack test`).
4
- # Each line shows the default value — uncomment one to change it.
5
- GemStack.configure do |config|
6
- # ── Logging ─────────────────────────────────────────────────────────────
7
- # config.logger.level = :debug # logs are discarded in tests unless this (or GEMSTACK_LOG_LEVEL) is set
8
-
9
- # ── HTTP ────────────────────────────────────────────────────────────────
10
- # config.http.show_exceptions = true # a failing request's JSON includes the exception
11
- <% if database? -%>
12
-
13
- # ── Database (connection: the test section of config/database.yml, or TEST_DATABASE_URL) ──
14
- # config.db.log_queries = false
15
- <% end -%>
16
-
17
- # ── Background work is recorded, not performed ──────────────────────────
18
- # config.jobs.adapter = :test # assert_enqueued / perform_enqueued_jobs; :inline runs jobs at once
19
- # config.mail.delivery = :test # assert_emails / last_email
20
- # config.cache.store = :null # no caching, so tests can't leak into each other; :memory to test it
21
- end
@@ -1,21 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Puma configuration. Every value can be set through the environment.
4
- # `gemstack dev` sets GEMSTACK_API_HOST/PORT to a private loopback port
5
- # behind the development gateway.
6
-
7
- max_threads = Integer(ENV.fetch("GEMSTACK_MAX_THREADS", 5))
8
- min_threads = Integer(ENV.fetch("GEMSTACK_MIN_THREADS", max_threads))
9
- threads min_threads, max_threads
10
-
11
- bind "tcp://#{ENV.fetch("GEMSTACK_API_HOST", "0.0.0.0")}:#{ENV.fetch("GEMSTACK_API_PORT", 4000)}"
12
- environment ENV.fetch("GEMSTACK_ENV", "development")
13
-
14
- # Cluster mode for multi-core production servers: WEB_CONCURRENCY=4
15
- worker_count = Integer(ENV.fetch("WEB_CONCURRENCY", 0))
16
- if worker_count.positive?
17
- workers worker_count
18
- preload_app!
19
- # Connections must not be shared across forked workers; each reconnects lazily.
20
- before_fork { GemStack::DB.disconnect if defined?(GemStack::DB) }
21
- end
@@ -1,10 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # API routes, relative to config.http.api_path ("/api"):
4
- #
5
- # get "/status", to: "status#show" # GET /api/status → StatusController#show
6
- # resources :products # /api/products: index, create, show, update, destroy
7
- #
8
- # List them with `gemstack routes`. In development this file reloads on save.
9
- GemStack.routes do
10
- end
@@ -1,6 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Rack entry point. Works with Puma (config/puma.rb) or any Rack server.
4
- require_relative "config/app"
5
-
6
- run GemStack.boot!
File without changes
@@ -1,5 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Development data, loaded by `gemstack db:seed` (and db:setup). Make it safe to run twice:
4
- #
5
- # Product.find_by(name: "Desk lamp") || Product.create(name: "Desk lamp", price: "39.00")
@@ -1,19 +0,0 @@
1
- # Copy to .env for local development overrides. Never commit real secrets.
2
- # Variables already set in your shell always take precedence.
3
-
4
- # PORT=3000 # public development port (gemstack dev)
5
- # GEMSTACK_LOG_LEVEL=debug
6
-
7
- <% if database? -%>
8
- # Database: config/database.yml. A URL here overrides it (hosting platforms set DATABASE_URL).
9
- <% if database_adapter == "sqlite3" -%>
10
- # DATABASE_URL=sqlite3:db/development.sqlite3
11
- <% elsif database_adapter == "postgresql" -%>
12
- # DATABASE_URL=postgres://user:password@localhost:5432/<%= database_name %>_development
13
- # TEST_DATABASE_URL=postgres://user:password@localhost:5432/<%= database_name %>_test
14
- <% else -%>
15
- # DATABASE_PASSWORD=
16
- # DATABASE_URL=<%= database_adapter %>://root:password@127.0.0.1:3306/<%= database_name %>_development
17
- # TEST_DATABASE_URL=<%= database_adapter %>://root:password@127.0.0.1:3306/<%= database_name %>_test
18
- <% end -%>
19
- <% end -%>
@@ -1,16 +0,0 @@
1
- /.bundle/
2
- /log/
3
- /tmp/
4
- /db/*.sqlite3
5
- /db/*.sqlite3-*
6
- .env
7
- .env.local
8
- .env.*.local
9
-
10
- node_modules/
11
- /frontend/.next/
12
- /frontend/out/
13
- /frontend/next-env.d.ts
14
- *.tsbuildinfo
15
-
16
- .DS_Store
@@ -1 +0,0 @@
1
- <%= node_version %>
@@ -1 +0,0 @@
1
- <%= node_version %>
@@ -1 +0,0 @@
1
- <%= ruby_version %>
@@ -1,4 +0,0 @@
1
- ruby <%= ruby_version %>
2
- <% if frontend? -%>
3
- nodejs <%= node_version %>
4
- <% end -%>
@@ -1,18 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "test_helper"
4
-
5
- class HealthTest < GemStack::TestCase
6
- def test_api_boots_and_answers_health_checks
7
- get_json "/api/health"
8
-
9
- assert_status 200
10
- assert_equal({ "status" => "ok" }, json_body)
11
- end
12
-
13
- def test_unknown_routes_return_json_errors
14
- get_json "/api/does-not-exist"
15
-
16
- assert_error 404, "route_not_found"
17
- end
18
- end
@@ -1,25 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- ENV["GEMSTACK_ENV"] = "test"
4
-
5
- require_relative "../config/app"
6
- require "gemstack/testing"
7
- require "gemstack/jobs/testing"
8
- <% if database? -%>
9
- require "gemstack/db/testing"
10
- <% end -%>
11
- require "minitest/autorun"
12
-
13
- <% if database? -%>
14
- # Creates the test database (TEST_DATABASE_URL) if needed and applies migrations.
15
- GemStack::DB::Testing.prepare!
16
- <% end -%>
17
- GemStack.boot!
18
- <% if database? -%>
19
-
20
- # Every test runs in a transaction that is rolled back afterwards.
21
- GemStack::TestCase.include GemStack::DB::Testing::Transactions
22
- <% end -%>
23
-
24
- # Jobs are recorded, not run: assert_enqueued / perform_enqueued_jobs.
25
- GemStack::TestCase.include GemStack::Jobs::Testing
@@ -1,35 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Personal API tokens: send `Authorization: Bearer <token>` instead of a cookie.
4
- # GET /api/auth/tokens · POST /api/auth/tokens · DELETE /api/auth/tokens/:id
5
- class ApiTokensController < ApplicationController
6
- before :require_login
7
- before :require_session, only: %i[create destroy] # a leaked token can't mint or revoke tokens
8
-
9
- accepts :create do
10
- required :name, :string, max_length: 100
11
- end
12
- returns :index, [ApiTokenSerializer]
13
- returns :create, NewApiTokenSerializer
14
-
15
- def index = render(current_user.auth_tokens_dataset.api.order(Sequel.desc(:created_at)), serializer: ApiTokenSerializer)
16
-
17
- def create
18
- token, id = GemStack::Auth::Tokens.issue(current_user.id, purpose: "api", name: input[:name])
19
- render NewApiTokenSerializer::Result.new(token: token, api_token: AuthToken[id]),
20
- status: :created, serializer: NewApiTokenSerializer
21
- end
22
-
23
- def destroy
24
- revoked = GemStack::Auth::Tokens.revoke(params[:id], user_id: current_user.id)
25
- raise GemStack::NotFound, "API token not found" unless revoked
26
- end
27
-
28
- private
29
-
30
- def require_session
31
- return if authenticated_by == :session
32
-
33
- raise GemStack::Forbidden.new("Manage API tokens from the app.", code: "session_required")
34
- end
35
- end
@@ -1,31 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # POST /api/auth/email/resend · POST /api/auth/email/verify
4
- class EmailVerificationsController < ApplicationController
5
- before :require_login, only: :create
6
- rate_limit to: 5, within: 3600, only: :create
7
- rate_limit to: 20, within: 3600, only: :update
8
-
9
- accepts :update do
10
- required :token, :string, max_length: 200
11
- end
12
- returns :create, nil
13
- returns :update, nil
14
-
15
- def create
16
- AuthMailer.email_verification(current_user.id).deliver_later unless current_user.email_verified?
17
- head :accepted
18
- end
19
-
20
- # Works signed in or out: the link may be opened on another device.
21
- def update
22
- row = GemStack::Auth::Tokens.consume(input[:token], purpose: "email_verification")
23
- user = row && User[row[:user_id]]
24
- # Only valid for the address it was sent to.
25
- unless user && user.email == row[:email]
26
- raise GemStack::BadRequest.new("This verification link is invalid or has expired.", code: "invalid_token")
27
- end
28
-
29
- user.update(email_verified_at: Time.now) unless user.email_verified?
30
- end
31
- end
@@ -1,40 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # POST /api/auth/password/forgot · POST /api/auth/password/reset
4
- class PasswordResetsController < ApplicationController
5
- rate_limit to: 10, within: 3600, only: :create
6
- rate_limit to: 3, within: 3600, only: :create, name: "reset-email", by: -> { params[:email].to_s.downcase }
7
- rate_limit to: 10, within: 3600, only: :update
8
-
9
- accepts :create do
10
- required :email, :string, max_length: 254
11
- end
12
- accepts :update do
13
- required :token, :string, max_length: 200
14
- required :password, :string, max_length: 1024
15
- end
16
- returns :create, nil
17
- returns :update, UserSerializer
18
-
19
- # Always 202: whether an account exists is nobody else's business.
20
- def create
21
- user = User.first(email: User.normalize_email(input[:email]))
22
- AuthMailer.password_reset(user.id).deliver_later if user
23
- head :accepted
24
- end
25
-
26
- def update
27
- errors = GemStack::Auth::Password.errors(input[:password])
28
- raise GemStack::ValidationError.new(errors: { password: errors }) unless errors.empty?
29
-
30
- row = GemStack::Auth::Tokens.consume(input[:token], purpose: "password_reset")
31
- raise GemStack::BadRequest.new("This reset link is invalid or has expired.", code: "invalid_token") unless row
32
-
33
- user = User[row[:user_id]]
34
- user.update(password: input[:password])
35
- # A reset usually means the old password leaked: end every session.
36
- GemStack::Auth::Sessions.revoke_all(user.id)
37
- sign_in(user)
38
- render user
39
- end
40
- end
@@ -1,19 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # POST /api/auth/signup
4
- class RegistrationsController < ApplicationController
5
- rate_limit to: 10, within: 3600, only: :create
6
-
7
- accepts :create do
8
- required :email, :string, max_length: 254
9
- required :password, :string, max_length: 1024
10
- end
11
- returns :create, UserSerializer
12
-
13
- def create
14
- user = User.create(email: input[:email], password: input[:password]) # invalid: 422 with field errors
15
- AuthMailer.email_verification(user.id).deliver_later
16
- sign_in(user)
17
- render user, status: :created
18
- end
19
- end
@@ -1,29 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # POST /api/auth/login · GET /api/auth/me · DELETE /api/auth/logout
4
- class SessionsController < ApplicationController
5
- before :require_login, only: :show
6
- # Per IP, and per account so one address can't be guessed at from many IPs.
7
- rate_limit to: 20, within: 300, only: :create
8
- rate_limit to: 10, within: 300, only: :create, name: "login-email", by: -> { params[:email].to_s.downcase }
9
-
10
- accepts :create do
11
- required :email, :string, max_length: 254
12
- required :password, :string, max_length: 1024
13
- end
14
- returns :create, UserSerializer
15
- returns :show, UserSerializer
16
-
17
- def create
18
- user = User.authenticate_by(email: input[:email], password: input[:password])
19
- # The same answer for "no such account" and "wrong password".
20
- raise GemStack::Unauthorized.new("Invalid email or password.", code: "invalid_credentials") unless user
21
-
22
- sign_in(user)
23
- render user
24
- end
25
-
26
- def show = render(current_user)
27
-
28
- def destroy = sign_out
29
- end
@@ -1,20 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Account emails. Templates: app/mailers/templates/auth_mailer/*.erb.
4
- # Links point at the Next.js pages (config.auth.app_url, APP_URL in production).
5
- class AuthMailer < ApplicationMailer
6
- def password_reset(user_id)
7
- @user = User[user_id] or return
8
- token, = GemStack::Auth::Tokens.issue(@user.id, purpose: "password_reset")
9
- @url = GemStack::Auth.url("/reset-password", token: token)
10
- @minutes = GemStack.config.auth.password_reset_ttl / 60
11
- mail to: @user.email, subject: "Reset your password"
12
- end
13
-
14
- def email_verification(user_id)
15
- @user = User[user_id] or return
16
- token, = GemStack::Auth::Tokens.issue(@user.id, purpose: "email_verification", email: @user.email)
17
- @url = GemStack::Auth.url("/verify-email", token: token)
18
- mail to: @user.email, subject: "Confirm your email address"
19
- end
20
- end
@@ -1,2 +0,0 @@
1
- <p>Welcome! Please confirm that <%= @user.email %> is your email address.</p>
2
- <p><a href="<%= @url %>">Confirm my email address</a></p>
@@ -1,2 +0,0 @@
1
- Welcome! Please confirm that <%= @user.email %> is your email address:
2
- <%= @url %>
@@ -1,3 +0,0 @@
1
- <p>Someone (hopefully you) asked to reset the password for <%= @user.email %>.</p>
2
- <p><a href="<%= @url %>">Choose a new password</a> — the link works once, for <%= @minutes %> minutes.</p>
3
- <p>If it wasn't you, ignore this email: your password stays the same.</p>
@@ -1,6 +0,0 @@
1
- Someone (hopefully you) asked to reset the password for <%= @user.email %>.
2
-
3
- Choose a new password here (the link works once, for <%= @minutes %> minutes):
4
- <%= @url %>
5
-
6
- If it wasn't you, ignore this email — your password stays the same.
@@ -1,15 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # API tokens (and single-use reset/verification tokens) — see GemStack::Auth::Tokens.
4
- class AuthToken < ApplicationModel
5
- field :purpose, :string, null: false
6
- field :name, :string, size: 100
7
- field :last_used_at, :datetime
8
- field :expires_at, :datetime
9
-
10
- belongs_to :user
11
-
12
- dataset_module do
13
- def api = where(purpose: "api")
14
- end
15
- end
@@ -1,12 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Accounts. GemStack::Auth::User adds password hashing (Argon2id), email
4
- # normalization, validations and User.authenticate_by(email:, password:).
5
- class User < ApplicationModel
6
- include GemStack::Auth::User
7
-
8
- field :email, :string, null: false, size: 254
9
- field :email_verified_at, :datetime
10
-
11
- has_many :auth_tokens
12
- end
@@ -1,6 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Base class for policies (`gemstack generate policy Order`). Everything is
4
- # denied unless a policy allows it; see docs/authorization.md.
5
- class ApplicationPolicy < GemStack::Policy
6
- end