better_app_gen 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/.rubocop.yml +134 -0
  4. data/CHANGELOG.md +29 -0
  5. data/CLAUDE.md +84 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +174 -0
  8. data/RELEASE.md +25 -0
  9. data/Rakefile +10 -0
  10. data/exe/better_app_gen +6 -0
  11. data/lib/better_app_gen/app_generator.rb +75 -0
  12. data/lib/better_app_gen/cli.rb +136 -0
  13. data/lib/better_app_gen/configuration.rb +90 -0
  14. data/lib/better_app_gen/dependency_checker.rb +129 -0
  15. data/lib/better_app_gen/errors.rb +56 -0
  16. data/lib/better_app_gen/generators/base.rb +219 -0
  17. data/lib/better_app_gen/generators/database.rb +64 -0
  18. data/lib/better_app_gen/generators/docker.rb +73 -0
  19. data/lib/better_app_gen/generators/gemfile.rb +26 -0
  20. data/lib/better_app_gen/generators/home_controller.rb +34 -0
  21. data/lib/better_app_gen/generators/locale.rb +24 -0
  22. data/lib/better_app_gen/generators/rails_app.rb +60 -0
  23. data/lib/better_app_gen/generators/simple_form.rb +33 -0
  24. data/lib/better_app_gen/generators/solid_stack.rb +18 -0
  25. data/lib/better_app_gen/generators/vite.rb +122 -0
  26. data/lib/better_app_gen/templates/app/controllers/home_controller.rb.erb +4 -0
  27. data/lib/better_app_gen/templates/app/helpers/home_helper.rb.erb +2 -0
  28. data/lib/better_app_gen/templates/app/views/home/index.html.erb.erb +2 -0
  29. data/lib/better_app_gen/templates/app/views/layouts/application.html.erb.erb +33 -0
  30. data/lib/better_app_gen/templates/bin/dev.erb +11 -0
  31. data/lib/better_app_gen/templates/bin/docker-entrypoint.erb +7 -0
  32. data/lib/better_app_gen/templates/bin/docker-entrypoint.prod.erb +12 -0
  33. data/lib/better_app_gen/templates/config/application.rb.erb +80 -0
  34. data/lib/better_app_gen/templates/config/database.yml.erb +65 -0
  35. data/lib/better_app_gen/templates/config/initializers/active_record_schema_settings.rb.erb +3 -0
  36. data/lib/better_app_gen/templates/config/initializers/better_vite_helper.rb.erb +5 -0
  37. data/lib/better_app_gen/templates/config/initializers/simple_form.rb.erb +21 -0
  38. data/lib/better_app_gen/templates/config/locales/it.yml.erb +68 -0
  39. data/lib/better_app_gen/templates/config/locales/simple_form.it.yml.erb +49 -0
  40. data/lib/better_app_gen/templates/config/routes.rb.erb +15 -0
  41. data/lib/better_app_gen/templates/db/cable_migrate/create_solid_cable_schema.rb.erb +15 -0
  42. data/lib/better_app_gen/templates/db/cache_migrate/create_solid_cache_schema.rb.erb +16 -0
  43. data/lib/better_app_gen/templates/db/migrate/create_shared_schema.rb.erb +31 -0
  44. data/lib/better_app_gen/templates/db/migrate/enable_uuid_extension.rb.erb +7 -0
  45. data/lib/better_app_gen/templates/db/queue_migrate/create_solid_queue_schema.rb.erb +133 -0
  46. data/lib/better_app_gen/templates/docker/DEPLOY.md.erb +129 -0
  47. data/lib/better_app_gen/templates/docker/Dockerfile.dev.erb +58 -0
  48. data/lib/better_app_gen/templates/docker/Dockerfile.prod.erb +172 -0
  49. data/lib/better_app_gen/templates/docker/compose.runner.yml.erb +6 -0
  50. data/lib/better_app_gen/templates/docker/compose.yml.erb +86 -0
  51. data/lib/better_app_gen/templates/docker/env.docker.erb +28 -0
  52. data/lib/better_app_gen/templates/lib/tasks/db.rake.erb +28 -0
  53. data/lib/better_app_gen/templates/public/robots.txt.erb +1 -0
  54. data/lib/better_app_gen/templates/root/Procfile.dev.erb +2 -0
  55. data/lib/better_app_gen/templates/root/env.example.erb +27 -0
  56. data/lib/better_app_gen/templates/root/gitignore.erb +404 -0
  57. data/lib/better_app_gen/templates/root/yarnrc.yml.erb +6 -0
  58. data/lib/better_app_gen/templates/script/dc-attach.erb +13 -0
  59. data/lib/better_app_gen/templates/script/dc-build.erb +8 -0
  60. data/lib/better_app_gen/templates/script/dc-down.erb +8 -0
  61. data/lib/better_app_gen/templates/script/dc-logs-tail.erb +16 -0
  62. data/lib/better_app_gen/templates/script/dc-logs.erb +17 -0
  63. data/lib/better_app_gen/templates/script/dc-rails.erb +8 -0
  64. data/lib/better_app_gen/templates/script/dc-restart.erb +11 -0
  65. data/lib/better_app_gen/templates/script/dc-shell.erb +12 -0
  66. data/lib/better_app_gen/templates/script/dc-up.erb +11 -0
  67. data/lib/better_app_gen/templates/vite/application.css.erb +12 -0
  68. data/lib/better_app_gen/templates/vite/application.js.erb +6 -0
  69. data/lib/better_app_gen/templates/vite/controllers/application.js.erb +9 -0
  70. data/lib/better_app_gen/templates/vite/controllers/hello_controller.js.erb +7 -0
  71. data/lib/better_app_gen/templates/vite/controllers/index.js.erb +8 -0
  72. data/lib/better_app_gen/templates/vite/postcss.config.js.erb +5 -0
  73. data/lib/better_app_gen/templates/vite/vite.config.js.erb +48 -0
  74. data/lib/better_app_gen/version.rb +5 -0
  75. data/lib/better_app_gen.rb +23 -0
  76. metadata +182 -0
@@ -0,0 +1,68 @@
1
+ it:
2
+ hello: "Ciao mondo"
3
+
4
+ # Common translations
5
+ common:
6
+ save: "Salva"
7
+ cancel: "Annulla"
8
+ edit: "Modifica"
9
+ delete: "Elimina"
10
+ create: "Crea"
11
+ update: "Aggiorna"
12
+ back: "Indietro"
13
+ show: "Mostra"
14
+ confirm: "Sei sicuro?"
15
+ yes: "Si"
16
+ no: "No"
17
+
18
+ # Date and time
19
+ date:
20
+ formats:
21
+ default: "%d/%m/%Y"
22
+ short: "%d %b"
23
+ long: "%d %B %Y"
24
+
25
+ time:
26
+ formats:
27
+ default: "%d/%m/%Y %H:%M"
28
+ short: "%d %b %H:%M"
29
+ long: "%d %B %Y %H:%M:%S"
30
+
31
+ # ActiveRecord
32
+ activerecord:
33
+ errors:
34
+ messages:
35
+ blank: "non puo essere vuoto"
36
+ too_short: "e troppo corto (minimo %{count} caratteri)"
37
+ too_long: "e troppo lungo (massimo %{count} caratteri)"
38
+ invalid: "non e valido"
39
+ confirmation: "non corrisponde"
40
+ accepted: "deve essere accettato"
41
+ empty: "non puo essere vuoto"
42
+ present: "deve essere vuoto"
43
+ required: "deve esistere"
44
+ taken: "e gia stato preso"
45
+ wrong_length: "ha la lunghezza sbagliata (dovrebbe essere di %{count} caratteri)"
46
+ not_a_number: "non e un numero"
47
+ not_an_integer: "deve essere un numero intero"
48
+ greater_than: "deve essere maggiore di %{count}"
49
+ greater_than_or_equal_to: "deve essere maggiore o uguale a %{count}"
50
+ equal_to: "deve essere uguale a %{count}"
51
+ less_than: "deve essere minore di %{count}"
52
+ less_than_or_equal_to: "deve essere minore o uguale a %{count}"
53
+ other_than: "deve essere diverso da %{count}"
54
+ odd: "deve essere dispari"
55
+ even: "deve essere pari"
56
+
57
+ # Form helpers
58
+ helpers:
59
+ submit:
60
+ create: "Crea %{model}"
61
+ update: "Aggiorna %{model}"
62
+ submit: "Salva %{model}"
63
+
64
+ select:
65
+ prompt: "Seleziona..."
66
+
67
+ label:
68
+ required: " *"
@@ -0,0 +1,49 @@
1
+ it:
2
+ simple_form:
3
+ "yes": 'Si'
4
+ "no": 'No'
5
+ required:
6
+ text: 'obbligatorio'
7
+ mark: '*'
8
+ error_notification:
9
+ default_message: "Si sono verificati alcuni errori:"
10
+ labels:
11
+ defaults:
12
+ name: 'Nome'
13
+ email: 'Email'
14
+ password: 'Password'
15
+ password_confirmation: 'Conferma password'
16
+ phone: 'Telefono'
17
+ address: 'Indirizzo'
18
+ city: 'Citta'
19
+ country: 'Paese'
20
+ description: 'Descrizione'
21
+ title: 'Titolo'
22
+ content: 'Contenuto'
23
+ message: 'Messaggio'
24
+ subject: 'Oggetto'
25
+ first_name: 'Nome'
26
+ last_name: 'Cognome'
27
+ company: 'Azienda'
28
+ website: 'Sito web'
29
+ created_at: 'Creato il'
30
+ updated_at: 'Aggiornato il'
31
+ hints:
32
+ defaults:
33
+ email: 'Inserisci un indirizzo email valido'
34
+ password: 'Minimo 8 caratteri'
35
+ phone: 'Formato: +39 123 456 7890'
36
+ placeholders:
37
+ defaults:
38
+ name: 'Inserisci il nome...'
39
+ email: 'nome@esempio.com'
40
+ password: 'Inserisci la password...'
41
+ phone: '+39 123 456 7890'
42
+ address: 'Via, numero civico...'
43
+ city: 'Nome della citta...'
44
+ description: 'Inserisci una descrizione...'
45
+ title: 'Inserisci il titolo...'
46
+ content: 'Inserisci il contenuto...'
47
+ message: 'Scrivi il tuo messaggio...'
48
+ subject: 'Oggetto del messaggio...'
49
+ search: 'Cerca...'
@@ -0,0 +1,15 @@
1
+ Rails.application.routes.draw do
2
+ root "home#index"
3
+ # Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html
4
+
5
+ # Reveal health status on /up that returns 200 if the app boots with no exceptions, otherwise 500.
6
+ # Can be used by load balancers and uptime monitors to verify that the app is live.
7
+ get "up" => "rails/health#show", as: :rails_health_check
8
+
9
+ # Render dynamic PWA files from app/views/pwa/* (remember to link manifest in application.html.erb)
10
+ # get "manifest" => "rails/pwa#manifest", as: :pwa_manifest
11
+ # get "service-worker" => "rails/pwa#service_worker", as: :pwa_service_worker
12
+
13
+ # Defines the root path route ("/")
14
+ # root "posts#index"
15
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateSolidCableSchema < ActiveRecord::Migration[8.1]
4
+ def change
5
+ create_table :solid_cable_messages, force: :cascade do |t|
6
+ t.binary :channel, limit: 1024, null: false
7
+ t.binary :payload, limit: 536870912, null: false
8
+ t.datetime :created_at, null: false
9
+ t.integer :channel_hash, limit: 8, null: false
10
+ t.index :channel, name: "index_solid_cable_messages_on_channel"
11
+ t.index :channel_hash, name: "index_solid_cable_messages_on_channel_hash"
12
+ t.index :created_at, name: "index_solid_cable_messages_on_created_at"
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateSolidCacheSchema < ActiveRecord::Migration[8.1]
4
+ def change
5
+ create_table :solid_cache_entries, force: :cascade do |t|
6
+ t.binary :key, limit: 1024, null: false
7
+ t.binary :value, limit: 536870912, null: false
8
+ t.datetime :created_at, null: false
9
+ t.integer :key_hash, limit: 8, null: false
10
+ t.integer :byte_size, limit: 4, null: false
11
+ t.index :byte_size, name: "index_solid_cache_entries_on_byte_size"
12
+ t.index [ :key_hash, :byte_size ], name: "index_solid_cache_entries_on_key_hash_and_byte_size"
13
+ t.index :key_hash, name: "index_solid_cache_entries_on_key_hash", unique: true
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,31 @@
1
+ # This migration creates a 'shared' schema in PostgreSQL
2
+ # Use this schema for tables that need to be accessed across different contexts
3
+ # Examples: lookup tables, configurations, multi-tenant shared data
4
+ class CreateSharedSchema < ActiveRecord::Migration[8.0]
5
+ def up
6
+ # Create the shared schema if it doesn't exist
7
+ execute "CREATE SCHEMA IF NOT EXISTS shared"
8
+
9
+ # Grant usage permissions to the current database user
10
+ # This ensures the application can access tables in the shared schema
11
+ execute "GRANT USAGE ON SCHEMA shared TO CURRENT_USER"
12
+ execute "GRANT CREATE ON SCHEMA shared TO CURRENT_USER"
13
+
14
+ # Optional: Add shared schema to search_path for easier access
15
+ # Uncomment if you want shared schema tables accessible without schema prefix
16
+ # execute <<-SQL
17
+ # ALTER DATABASE #{connection.current_database}
18
+ # SET search_path TO "$user", public, shared;
19
+ # SQL
20
+
21
+ Rails.logger.info "Created 'shared' schema successfully"
22
+ end
23
+
24
+ def down
25
+ # Drop the shared schema and all its contents
26
+ # CASCADE will drop all tables, views, and other objects in the schema
27
+ execute "DROP SCHEMA IF EXISTS shared CASCADE"
28
+
29
+ Rails.logger.info "Dropped 'shared' schema"
30
+ end
31
+ end
@@ -0,0 +1,7 @@
1
+ # This migration enables UUID support in PostgreSQL
2
+ # It should be the first migration run in any new Rails application
3
+ class EnableUuidExtension < ActiveRecord::Migration[8.0]
4
+ def change
5
+ enable_extension 'pgcrypto' unless extension_enabled?('pgcrypto')
6
+ end
7
+ end
@@ -0,0 +1,133 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateSolidQueueSchema < ActiveRecord::Migration[8.1]
4
+ def change
5
+ create_table :solid_queue_blocked_executions, force: :cascade do |t|
6
+ t.bigint :job_id, null: false
7
+ t.string :queue_name, null: false
8
+ t.integer :priority, default: 0, null: false
9
+ t.string :concurrency_key, null: false
10
+ t.datetime :expires_at, null: false
11
+ t.datetime :created_at, null: false
12
+ t.index [ :concurrency_key, :priority, :job_id ], name: "index_solid_queue_blocked_executions_for_release"
13
+ t.index [ :expires_at, :concurrency_key ], name: "index_solid_queue_blocked_executions_for_maintenance"
14
+ t.index [ :job_id ], name: "index_solid_queue_blocked_executions_on_job_id", unique: true
15
+ end
16
+
17
+ create_table :solid_queue_claimed_executions, force: :cascade do |t|
18
+ t.bigint :job_id, null: false
19
+ t.bigint :process_id
20
+ t.datetime :created_at, null: false
21
+ t.index [ :job_id ], name: "index_solid_queue_claimed_executions_on_job_id", unique: true
22
+ t.index [ :process_id, :job_id ], name: "index_solid_queue_claimed_executions_on_process_id_and_job_id"
23
+ end
24
+
25
+ create_table :solid_queue_failed_executions, force: :cascade do |t|
26
+ t.bigint :job_id, null: false
27
+ t.text :error
28
+ t.datetime :created_at, null: false
29
+ t.index [ :job_id ], name: "index_solid_queue_failed_executions_on_job_id", unique: true
30
+ end
31
+
32
+ create_table :solid_queue_jobs, force: :cascade do |t|
33
+ t.string :queue_name, null: false
34
+ t.string :class_name, null: false
35
+ t.text :arguments
36
+ t.integer :priority, default: 0, null: false
37
+ t.string :active_job_id
38
+ t.datetime :scheduled_at
39
+ t.datetime :finished_at
40
+ t.string :concurrency_key
41
+ t.datetime :created_at, null: false
42
+ t.datetime :updated_at, null: false
43
+ t.index [ :active_job_id ], name: "index_solid_queue_jobs_on_active_job_id"
44
+ t.index [ :class_name ], name: "index_solid_queue_jobs_on_class_name"
45
+ t.index [ :finished_at ], name: "index_solid_queue_jobs_on_finished_at"
46
+ t.index [ :queue_name, :finished_at ], name: "index_solid_queue_jobs_for_filtering"
47
+ t.index [ :scheduled_at, :finished_at ], name: "index_solid_queue_jobs_for_alerting"
48
+ end
49
+
50
+ create_table :solid_queue_pauses, force: :cascade do |t|
51
+ t.string :queue_name, null: false
52
+ t.datetime :created_at, null: false
53
+ t.index [ :queue_name ], name: "index_solid_queue_pauses_on_queue_name", unique: true
54
+ end
55
+
56
+ create_table :solid_queue_processes, force: :cascade do |t|
57
+ t.string :kind, null: false
58
+ t.datetime :last_heartbeat_at, null: false
59
+ t.bigint :supervisor_id
60
+ t.integer :pid, null: false
61
+ t.string :hostname
62
+ t.text :metadata
63
+ t.datetime :created_at, null: false
64
+ t.string :name, null: false
65
+ t.index [ :last_heartbeat_at ], name: "index_solid_queue_processes_on_last_heartbeat_at"
66
+ t.index [ :name, :supervisor_id ], name: "index_solid_queue_processes_on_name_and_supervisor_id", unique: true
67
+ t.index [ :supervisor_id ], name: "index_solid_queue_processes_on_supervisor_id"
68
+ end
69
+
70
+ create_table :solid_queue_ready_executions, force: :cascade do |t|
71
+ t.bigint :job_id, null: false
72
+ t.string :queue_name, null: false
73
+ t.integer :priority, default: 0, null: false
74
+ t.datetime :created_at, null: false
75
+ t.index [ :job_id ], name: "index_solid_queue_ready_executions_on_job_id", unique: true
76
+ t.index [ :priority, :job_id ], name: "index_solid_queue_poll_all"
77
+ t.index [ :queue_name, :priority, :job_id ], name: "index_solid_queue_poll_by_queue"
78
+ end
79
+
80
+ create_table :solid_queue_recurring_executions, force: :cascade do |t|
81
+ t.bigint :job_id, null: false
82
+ t.string :task_key, null: false
83
+ t.datetime :run_at, null: false
84
+ t.datetime :created_at, null: false
85
+ t.index [ :job_id ], name: "index_solid_queue_recurring_executions_on_job_id", unique: true
86
+ t.index [ :task_key, :run_at ], name: "index_solid_queue_recurring_executions_on_task_key_and_run_at", unique: true
87
+ end
88
+
89
+ create_table :solid_queue_recurring_tasks, force: :cascade do |t|
90
+ t.string :key, null: false
91
+ t.string :schedule, null: false
92
+ t.string :command, limit: 2048
93
+ t.string :class_name
94
+ t.text :arguments
95
+ t.string :queue_name
96
+ t.integer :priority, default: 0
97
+ t.boolean :static, default: true, null: false
98
+ t.text :description
99
+ t.datetime :created_at, null: false
100
+ t.datetime :updated_at, null: false
101
+ t.index [ :key ], name: "index_solid_queue_recurring_tasks_on_key", unique: true
102
+ t.index [ :static ], name: "index_solid_queue_recurring_tasks_on_static"
103
+ end
104
+
105
+ create_table :solid_queue_scheduled_executions, force: :cascade do |t|
106
+ t.bigint :job_id, null: false
107
+ t.string :queue_name, null: false
108
+ t.integer :priority, default: 0, null: false
109
+ t.datetime :scheduled_at, null: false
110
+ t.datetime :created_at, null: false
111
+ t.index [ :job_id ], name: "index_solid_queue_scheduled_executions_on_job_id", unique: true
112
+ t.index [ :scheduled_at, :priority, :job_id ], name: "index_solid_queue_dispatch_all"
113
+ end
114
+
115
+ create_table :solid_queue_semaphores, force: :cascade do |t|
116
+ t.string :key, null: false
117
+ t.integer :value, default: 1, null: false
118
+ t.datetime :expires_at, null: false
119
+ t.datetime :created_at, null: false
120
+ t.datetime :updated_at, null: false
121
+ t.index [ :expires_at ], name: "index_solid_queue_semaphores_on_expires_at"
122
+ t.index [ :key, :value ], name: "index_solid_queue_semaphores_on_key_and_value"
123
+ t.index [ :key ], name: "index_solid_queue_semaphores_on_key", unique: true
124
+ end
125
+
126
+ add_foreign_key :solid_queue_blocked_executions, :solid_queue_jobs, column: :job_id, on_delete: :cascade
127
+ add_foreign_key :solid_queue_claimed_executions, :solid_queue_jobs, column: :job_id, on_delete: :cascade
128
+ add_foreign_key :solid_queue_failed_executions, :solid_queue_jobs, column: :job_id, on_delete: :cascade
129
+ add_foreign_key :solid_queue_ready_executions, :solid_queue_jobs, column: :job_id, on_delete: :cascade
130
+ add_foreign_key :solid_queue_recurring_executions, :solid_queue_jobs, column: :job_id, on_delete: :cascade
131
+ add_foreign_key :solid_queue_scheduled_executions, :solid_queue_jobs, column: :job_id, on_delete: :cascade
132
+ end
133
+ end
@@ -0,0 +1,129 @@
1
+ # Deploy su Dokploy
2
+
3
+ Guida per configurare il deploy con zero-downtime su Dokploy.
4
+
5
+ ## Prerequisiti
6
+
7
+ - Dokploy installato e configurato
8
+ - Repository Git accessibile da Dokploy
9
+ - Database PostgreSQL 17+ raggiungibile dalla rete Dokploy
10
+
11
+ ## Creare il Progetto
12
+
13
+ 1. In Dokploy, clicca **Create Project**
14
+ 2. Seleziona **Docker** (non Compose)
15
+ 3. Collega il repository Git
16
+
17
+ ## Configurazione Build
18
+
19
+ | Campo | Valore |
20
+ | --------------- | ------------------------- |
21
+ | Dockerfile Path | `.docker/Dockerfile.prod` |
22
+ | Build Context | `.` |
23
+
24
+ ## Variabili d'Ambiente
25
+
26
+ Configura le seguenti variabili in **Environment**:
27
+
28
+ ```bash
29
+ # Database (tutte puntano allo stesso PostgreSQL, database diversi)
30
+ DATABASE_URL=postgresql://user:password@host:5432/<%= @config.app_name_snake %>_production
31
+ CACHE_DATABASE_URL=postgresql://user:password@host:5432/<%= @config.app_name_snake %>_production_cache
32
+ QUEUE_DATABASE_URL=postgresql://user:password@host:5432/<%= @config.app_name_snake %>_production_queue
33
+ CABLE_DATABASE_URL=postgresql://user:password@host:5432/<%= @config.app_name_snake %>_production_cable
34
+
35
+ # Rails
36
+ RAILS_ENV=production
37
+ SECRET_KEY_BASE=<genera con: rails secret>
38
+
39
+ # Opzionali
40
+ RAILS_MAX_THREADS=3
41
+ RAILS_LOG_TO_STDOUT=true
42
+ ```
43
+
44
+ ## Zero-Downtime Deployment
45
+
46
+ Per abilitare il deploy senza downtime:
47
+
48
+ 1. Vai in **Advanced** → **Swarm Settings**
49
+ 2. Nella sezione **Health Check**, incolla:
50
+
51
+ ```json
52
+ {
53
+ "Test": ["CMD", "curl", "-f", "http://localhost:3000/up"],
54
+ "Interval": 30000000000,
55
+ "Timeout": 10000000000,
56
+ "StartPeriod": 60000000000,
57
+ "Retries": 3
58
+ }
59
+ ```
60
+
61
+ ### Spiegazione parametri (in nanosecondi)
62
+
63
+ | Parametro | Valore | Descrizione |
64
+ | ----------- | ------ | ------------------------------------------ |
65
+ | Interval | 30s | Frequenza del check |
66
+ | Timeout | 10s | Timeout per ogni check |
67
+ | StartPeriod | 60s | Tempo di grazia all'avvio (per migrazioni) |
68
+ | Retries | 3 | Tentativi prima di considerare unhealthy |
69
+
70
+ ## Rete
71
+
72
+ Assicurati che il container possa raggiungere PostgreSQL. Se usi una rete esterna:
73
+
74
+ 1. Vai in **Advanced** → **Network**
75
+ 2. Aggiungi la rete dove si trova PostgreSQL (es. `dokploy-network`)
76
+
77
+ ## Primo Deploy
78
+
79
+ Al primo deploy:
80
+
81
+ 1. Il container eseguirà automaticamente `rails db:prepare`
82
+ 2. Questo crea i database e esegue le migrazioni
83
+ 3. Verifica i log per eventuali errori
84
+
85
+ ## Deploy Successivi
86
+
87
+ 1. Push sul branch configurato
88
+ 2. Dokploy builda la nuova immagine
89
+ 3. Avvia il nuovo container
90
+ 4. Aspetta che l'health check passi
91
+ 5. Rimuove il vecchio container (zero-downtime)
92
+
93
+ ## Architettura
94
+
95
+ ```
96
+ ┌─────────────────────────────────────────┐
97
+ │ Container Puma │
98
+ │ │
99
+ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
100
+ │ │ Rails │ │ Solid │ │ Solid │ │
101
+ │ │ Web │ │ Queue │ │ Cable │ │
102
+ │ └────┬────┘ └────┬────┘ └────┬────┘ │
103
+ │ │ │ │ │
104
+ └───────┼────────────┼────────────┼───────┘
105
+ │ │ │
106
+ ▼ ▼ ▼
107
+ ┌─────────────────────────────────────────┐
108
+ │ PostgreSQL 17+ │
109
+ │ ┌─────────┐ ┌───────┐ ┌───────┐ ┌────┐│
110
+ │ │ primary │ │ cache │ │ queue │ │cable││
111
+ │ └─────────┘ └───────┘ └───────┘ └────┘│
112
+ └─────────────────────────────────────────┘
113
+ ```
114
+
115
+ ## Troubleshooting
116
+
117
+ ### Container non parte
118
+
119
+ Controlla i log per errori di connessione al database o migrazioni fallite.
120
+
121
+ ### Health check fallisce
122
+
123
+ - Verifica che la porta 3000 sia esposta
124
+ - Controlla che `/up` risponda (è il Rails health check endpoint)
125
+ - Aumenta `StartPeriod` se le migrazioni sono lente
126
+
127
+ ### Jobs non processati
128
+
129
+ Verifica che `SOLID_QUEUE_IN_PUMA=true` sia impostato nel Dockerfile (già configurato).
@@ -0,0 +1,58 @@
1
+ # syntax=docker/dockerfile:1
2
+ # check=error=true
3
+
4
+ ARG RUBY_VERSION=3.4.3
5
+ FROM docker.io/library/ruby:$RUBY_VERSION-slim-bookworm AS base
6
+
7
+ ARG NODE_VERSION=24.1.0
8
+ # Yarn version managed via Corepack + packageManager field in package.json
9
+
10
+ # Setup workdir
11
+ RUN mkdir /app
12
+ WORKDIR /app
13
+
14
+ # Setup ENV
15
+ ENV PORT=<%= rails_port %>
16
+ ENV BUNDLE_PATH=/bundle
17
+ ENV BUNDLE_BIN=/bundle/bin
18
+ ENV GEM_HOME=/bundle
19
+ ENV PATH="${BUNDLE_BIN}:${PATH}"
20
+ ENV PATH="/usr/local/node/bin:$PATH"
21
+ ENV LC_ALL=C.UTF-8
22
+ ENV LANG=C.UTF-8
23
+ ENV LANGUAGE=C.UTF-8
24
+
25
+ # System dependencies
26
+ RUN apt-get update \
27
+ && apt-get upgrade -y \
28
+ && apt-get install -y lsb-release curl imagemagick libpq5 libjemalloc2 \
29
+ && install -d /usr/share/postgresql-common/pgdg \
30
+ && curl -o /usr/share/postgresql-common/pgdg/apt.postgresql.org.asc --fail https://www.postgresql.org/media/keys/ACCC4CF8.asc \
31
+ && sh -c 'echo "deb [signed-by=/usr/share/postgresql-common/pgdg/apt.postgresql.org.asc] https://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list' \
32
+ && apt update \
33
+ && apt-get install -y build-essential curl libxslt-dev libxml2-dev libpq-dev libjemalloc-dev libyaml-dev git postgresql-client-16 \
34
+ && curl -sL https://github.com/nodenv/node-build/archive/master.tar.gz | tar xz -C /tmp/ \
35
+ && /tmp/node-build-master/bin/node-build "${NODE_VERSION}" /usr/local/node \
36
+ && corepack enable \
37
+ && rm -rf /tmp/node-build-master \
38
+ && rm -rf /var/lib/apt/lists/*
39
+
40
+ # Jemalloc setup
41
+ RUN ln -s /usr/lib/*/libjemalloc.so.2 /usr/lib/libjemalloc.so.2
42
+ ENV LD_PRELOAD="/usr/lib/libjemalloc.so.2"
43
+ ENV MALLOC_CONF="dirty_decay_ms:1000,narenas:2,background_thread:true"
44
+ ENV RUBY_YJIT_ENABLE="1"
45
+
46
+ # Update gems
47
+ RUN gem update --system \
48
+ && gem install bundler -v '=2.7.1' \
49
+ && gem cleanup
50
+
51
+ # Setup entrypoint
52
+ COPY bin/docker-entrypoint /app/bin/docker-entrypoint
53
+ RUN chmod +x /app/bin/docker-entrypoint
54
+ ENTRYPOINT ["/app/bin/docker-entrypoint"]
55
+
56
+ # Start the main process.
57
+ EXPOSE ${PORT}
58
+ CMD ["sh", "-c", "./bin/rails server -b 0.0.0.0 -p ${PORT:-<%= rails_port %>}"]