diff --git a/app/controllers/api/v1/sites_controller.rb b/app/controllers/api/v1/sites_controller.rb index 05abc38a..8f3cbaa2 100644 --- a/app/controllers/api/v1/sites_controller.rb +++ b/app/controllers/api/v1/sites_controller.rb @@ -4,51 +4,88 @@ module Api module V1 # API para sitios class SitesController < BaseController - http_basic_authenticate_with name: ENV['HTTP_BASIC_USER'], - password: ENV['HTTP_BASIC_PASSWORD'] + SUBDOMAIN = ".#{Site.domain}" + TESTING_SUBDOMAIN = ".testing.#{Site.domain}" + PARTS = Site.domain.split('.').count + + if Rails.env.production? + http_basic_authenticate_with name: ENV['HTTP_BASIC_USER'], + password: ENV['HTTP_BASIC_PASSWORD'] + end # Lista de nombres de dominios a emitir certificados def index - render json: alternative_names + api_names + www_names + all_names = sites_names.concat(alternative_names).concat(www_names).concat(api_names).uniq.map do |name| + canonicalize name + end.reject do |name| + subdomain? name + end.reject do |name| + testing? name + end.uniq + + render json: all_names end private + # @param query [ActiveRecord::Relation] + # @return [Array] + def hostname_of(query) + query.pluck(Arel.sql("values->>'hostname'")).compact.uniq + end + def canonicalize(name) name.end_with?('.') ? name[0..-2] : "#{name}.#{Site.domain}" end + # Es un subdominio directo del dominio principal + # + # @param name [String] + # @return [Bool] def subdomain?(name) - name.end_with? ".#{Site.domain}" + name.end_with?(SUBDOMAIN) && name.split('.').count == (PARTS + 1) end - # Dominios alternativos + # Es un dominio de prueba + # + # @param name [String] + # @return [Bool] + def testing?(name) + name.end_with?(TESTING_SUBDOMAIN) && name.split('.').count == (PARTS + 2) + end + + # Nombres de los sitios + # + # @param name [String] + # @return [Array] + def sites_names + Site.all.order(:name).pluck(:name) + end + + # Dominios alternativos, incluyendo todas las clases derivadas de + # esta. + # + # @return [Array] def alternative_names - (DeployAlternativeDomain.all.map(&:hostname) + DeployLocalizedDomain.all.map(&:hostname)).map do |name| - canonicalize name - end.reject do |name| - subdomain? name - end + hostname_of(DeployAlternativeDomain.all) end # Obtener todos los sitios con API habilitada, es decir formulario # de contacto y/o colaboración anónima. # - # TODO: Optimizar + # @return [Array] def api_names Site.where(contact: true) .or(Site.where(colaboracion_anonima: true)) - .select("'api.' || name as name").map(&:name).map do |name| - canonicalize name - end.reject do |name| - subdomain? name + .pluck(:name).map do |name| + "api.#{name}" end end # Todos los dominios con WWW habilitado def www_names - Site.where(id: DeployWww.all.pluck(:site_id)).select("'www.' || name as name").map(&:name).map do |name| - canonicalize name + Site.where(id: DeployWww.all.pluck(:site_id)).pluck(:name).map do |name| + "www.#{name}" end end end diff --git a/app/models/deploy_alternative_domain.rb b/app/models/deploy_alternative_domain.rb index 75b69180..293b032b 100644 --- a/app/models/deploy_alternative_domain.rb +++ b/app/models/deploy_alternative_domain.rb @@ -2,7 +2,7 @@ # Soportar dominios alternativos class DeployAlternativeDomain < Deploy - store :values, accessors: %i[hostname], coder: JSON + store_accessor :values, :hostname DEPENDENCIES = %i[deploy_local] diff --git a/app/models/deploy_distributed_press.rb b/app/models/deploy_distributed_press.rb index 95f311e4..fe8b3f2f 100644 --- a/app/models/deploy_distributed_press.rb +++ b/app/models/deploy_distributed_press.rb @@ -12,7 +12,10 @@ require 'distributed_press/v1/client/site' # Al ser publicado, envía los archivos en un tarball y actualiza la # información. class DeployDistributedPress < Deploy - store :values, accessors: %i[hostname remote_site_id remote_info distributed_press_publisher_id], coder: JSON + store_accessor :values, :hostname + store_accessor :values, :remote_site_id + store_accessor :values, :remote_info + store_accessor :values, :distributed_press_publisher_id before_create :create_remote_site! before_destroy :delete_remote_site! diff --git a/app/models/deploy_hidden_service.rb b/app/models/deploy_hidden_service.rb index 25c0c217..b558edba 100644 --- a/app/models/deploy_hidden_service.rb +++ b/app/models/deploy_hidden_service.rb @@ -2,7 +2,7 @@ # Genera una versión onion class DeployHiddenService < DeployWww - store :values, accessors: %i[onion], coder: JSON + store_accessor :values, :onion before_create :create_hidden_service! diff --git a/app/models/deploy_local.rb b/app/models/deploy_local.rb index 29a31f8c..14b00a2f 100644 --- a/app/models/deploy_local.rb +++ b/app/models/deploy_local.rb @@ -3,8 +3,6 @@ # Alojamiento local, solo genera el sitio, con lo que no necesita hacer # nada más class DeployLocal < Deploy - store :values, accessors: %i[], coder: JSON - before_destroy :remove_destination! def bundle(output: false) diff --git a/app/models/deploy_localized_domain.rb b/app/models/deploy_localized_domain.rb index 59e17dcd..5a8c1689 100644 --- a/app/models/deploy_localized_domain.rb +++ b/app/models/deploy_localized_domain.rb @@ -2,7 +2,8 @@ # Soportar dominios localizados class DeployLocalizedDomain < DeployAlternativeDomain - store :values, accessors: %i[hostname locale], coder: JSON + store_accessor :values, :hostname + store_accessor :values, :locale # Generar un link simbólico del sitio principal al alternativo def deploy(**) diff --git a/app/models/deploy_rsync.rb b/app/models/deploy_rsync.rb index fcc5a65d..50aafd70 100644 --- a/app/models/deploy_rsync.rb +++ b/app/models/deploy_rsync.rb @@ -3,11 +3,15 @@ # Sincroniza sitios a servidores remotos usando Rsync. El servidor # remoto tiene que tener rsync instalado. class DeployRsync < Deploy - store :values, accessors: %i[hostname destination host_keys], coder: JSON + store_accessor :values, :hostname + store_accessor :values, :destination + store_accessor :values, :host_keys DEPENDENCIES = %i[deploy_local deploy_zip] def deploy(output: false) + raise(ArgumentError, 'destination no está configurado') if destination.blank? + ssh? && rsync(output: output) end @@ -18,13 +22,6 @@ class DeployRsync < Deploy deploy_local.size end - # Devolver el destino o lanzar un error si no está configurado - def destination - values[:destination].tap do |d| - raise(ArgumentError, 'destination no está configurado') if d.blank? - end - end - # @return [String] def url "https://#{hostname}/" @@ -43,9 +40,9 @@ class DeployRsync < Deploy ssh_available = false Net::SSH.start(host, user, verify_host_key: tofu, timeout: 5) do |ssh| - if values[:host_keys].blank? + if self.host_keys.blank? # Guardar las llaves que se encontraron en la primera conexión - values[:host_keys] = ssh.transport.host_keys.map do |host_key| + self.host_keys = ssh.transport.host_keys.map do |host_key| "#{host_key.ssh_type} #{host_key.fingerprint}" end @@ -74,7 +71,7 @@ class DeployRsync < Deploy # # @return [Symbol] def tofu - values[:host_keys].present? ? :always : :accept_new + self.host_keys.present? ? :always : :accept_new end # Devuelve el par user host diff --git a/app/models/deploy_www.rb b/app/models/deploy_www.rb index bb25cc64..aafb518f 100644 --- a/app/models/deploy_www.rb +++ b/app/models/deploy_www.rb @@ -2,8 +2,6 @@ # Vincula la versión del sitio con www a la versión sin class DeployWww < Deploy - store :values, accessors: %i[], coder: JSON - DEPENDENCIES = %i[deploy_local] before_destroy :remove_destination! diff --git a/app/models/deploy_zip.rb b/app/models/deploy_zip.rb index 85005470..9f538f3e 100644 --- a/app/models/deploy_zip.rb +++ b/app/models/deploy_zip.rb @@ -6,8 +6,6 @@ require 'zip' # # TODO: Firmar con minisign class DeployZip < Deploy - store :values, accessors: %i[], coder: JSON - DEPENDENCIES = %i[deploy_local] # Una vez que el sitio está generado, tomar todos los archivos y diff --git a/db/migrate/20241223185830_change_deploys_values_type_to_json_b.rb b/db/migrate/20241223185830_change_deploys_values_type_to_json_b.rb new file mode 100644 index 00000000..5824a899 --- /dev/null +++ b/db/migrate/20241223185830_change_deploys_values_type_to_json_b.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +class ChangeDeploysValuesTypeToJsonB < ActiveRecord::Migration[6.1] + def up + add_column :deploys, :values_2, :jsonb, default: {} + + Deploy.find_each do |deploy| + deploy.update values_2: JSON.parse(deploy.values) + end + + remove_column :deploys, :values + rename_column :deploys, :values_2, :values + end + + def down + add_column :deploys, :values_2, :text + + Deploy.find_each do |deploy| + deploy.update values_2: deploy.values.to_json + end + + remove_column :deploys, :values + rename_column :deploys, :values_2, :values + end +end diff --git a/db/structure.sql b/db/structure.sql index 21cf04d0..962a4069 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -882,8 +882,8 @@ CREATE TABLE public.deploys ( updated_at timestamp without time zone NOT NULL, site_id integer, type character varying, - "values" text, - rol_id integer + rol_id integer, + "values" jsonb DEFAULT '{}'::jsonb ); @@ -2719,6 +2719,7 @@ INSERT INTO "schema_migrations" (version) VALUES ('20240316203721'), ('20240318183846'), ('20240319124212'), -('20240319144735'); +('20240319144735'), +('20241223185830');