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.
- checksums.yaml +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +134 -0
- data/CHANGELOG.md +29 -0
- data/CLAUDE.md +84 -0
- data/LICENSE.txt +21 -0
- data/README.md +174 -0
- data/RELEASE.md +25 -0
- data/Rakefile +10 -0
- data/exe/better_app_gen +6 -0
- data/lib/better_app_gen/app_generator.rb +75 -0
- data/lib/better_app_gen/cli.rb +136 -0
- data/lib/better_app_gen/configuration.rb +90 -0
- data/lib/better_app_gen/dependency_checker.rb +129 -0
- data/lib/better_app_gen/errors.rb +56 -0
- data/lib/better_app_gen/generators/base.rb +219 -0
- data/lib/better_app_gen/generators/database.rb +64 -0
- data/lib/better_app_gen/generators/docker.rb +73 -0
- data/lib/better_app_gen/generators/gemfile.rb +26 -0
- data/lib/better_app_gen/generators/home_controller.rb +34 -0
- data/lib/better_app_gen/generators/locale.rb +24 -0
- data/lib/better_app_gen/generators/rails_app.rb +60 -0
- data/lib/better_app_gen/generators/simple_form.rb +33 -0
- data/lib/better_app_gen/generators/solid_stack.rb +18 -0
- data/lib/better_app_gen/generators/vite.rb +122 -0
- data/lib/better_app_gen/templates/app/controllers/home_controller.rb.erb +4 -0
- data/lib/better_app_gen/templates/app/helpers/home_helper.rb.erb +2 -0
- data/lib/better_app_gen/templates/app/views/home/index.html.erb.erb +2 -0
- data/lib/better_app_gen/templates/app/views/layouts/application.html.erb.erb +33 -0
- data/lib/better_app_gen/templates/bin/dev.erb +11 -0
- data/lib/better_app_gen/templates/bin/docker-entrypoint.erb +7 -0
- data/lib/better_app_gen/templates/bin/docker-entrypoint.prod.erb +12 -0
- data/lib/better_app_gen/templates/config/application.rb.erb +80 -0
- data/lib/better_app_gen/templates/config/database.yml.erb +65 -0
- data/lib/better_app_gen/templates/config/initializers/active_record_schema_settings.rb.erb +3 -0
- data/lib/better_app_gen/templates/config/initializers/better_vite_helper.rb.erb +5 -0
- data/lib/better_app_gen/templates/config/initializers/simple_form.rb.erb +21 -0
- data/lib/better_app_gen/templates/config/locales/it.yml.erb +68 -0
- data/lib/better_app_gen/templates/config/locales/simple_form.it.yml.erb +49 -0
- data/lib/better_app_gen/templates/config/routes.rb.erb +15 -0
- data/lib/better_app_gen/templates/db/cable_migrate/create_solid_cable_schema.rb.erb +15 -0
- data/lib/better_app_gen/templates/db/cache_migrate/create_solid_cache_schema.rb.erb +16 -0
- data/lib/better_app_gen/templates/db/migrate/create_shared_schema.rb.erb +31 -0
- data/lib/better_app_gen/templates/db/migrate/enable_uuid_extension.rb.erb +7 -0
- data/lib/better_app_gen/templates/db/queue_migrate/create_solid_queue_schema.rb.erb +133 -0
- data/lib/better_app_gen/templates/docker/DEPLOY.md.erb +129 -0
- data/lib/better_app_gen/templates/docker/Dockerfile.dev.erb +58 -0
- data/lib/better_app_gen/templates/docker/Dockerfile.prod.erb +172 -0
- data/lib/better_app_gen/templates/docker/compose.runner.yml.erb +6 -0
- data/lib/better_app_gen/templates/docker/compose.yml.erb +86 -0
- data/lib/better_app_gen/templates/docker/env.docker.erb +28 -0
- data/lib/better_app_gen/templates/lib/tasks/db.rake.erb +28 -0
- data/lib/better_app_gen/templates/public/robots.txt.erb +1 -0
- data/lib/better_app_gen/templates/root/Procfile.dev.erb +2 -0
- data/lib/better_app_gen/templates/root/env.example.erb +27 -0
- data/lib/better_app_gen/templates/root/gitignore.erb +404 -0
- data/lib/better_app_gen/templates/root/yarnrc.yml.erb +6 -0
- data/lib/better_app_gen/templates/script/dc-attach.erb +13 -0
- data/lib/better_app_gen/templates/script/dc-build.erb +8 -0
- data/lib/better_app_gen/templates/script/dc-down.erb +8 -0
- data/lib/better_app_gen/templates/script/dc-logs-tail.erb +16 -0
- data/lib/better_app_gen/templates/script/dc-logs.erb +17 -0
- data/lib/better_app_gen/templates/script/dc-rails.erb +8 -0
- data/lib/better_app_gen/templates/script/dc-restart.erb +11 -0
- data/lib/better_app_gen/templates/script/dc-shell.erb +12 -0
- data/lib/better_app_gen/templates/script/dc-up.erb +11 -0
- data/lib/better_app_gen/templates/vite/application.css.erb +12 -0
- data/lib/better_app_gen/templates/vite/application.js.erb +6 -0
- data/lib/better_app_gen/templates/vite/controllers/application.js.erb +9 -0
- data/lib/better_app_gen/templates/vite/controllers/hello_controller.js.erb +7 -0
- data/lib/better_app_gen/templates/vite/controllers/index.js.erb +8 -0
- data/lib/better_app_gen/templates/vite/postcss.config.js.erb +5 -0
- data/lib/better_app_gen/templates/vite/vite.config.js.erb +48 -0
- data/lib/better_app_gen/version.rb +5 -0
- data/lib/better_app_gen.rb +23 -0
- 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 %>}"]
|