# frozen_string_literal: true require 'distributed_press/v1/client/site' require 'njalla/v1' # Soportar Distributed Press APIv1 # # Usa tokens de publicación efímeros para todas las acciones. # # Al ser creado, genera el sitio en la instancia de Distributed Press # configurada y almacena el ID. # # 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], coder: JSON before_create :create_remote_site!, :create_njalla_records! # Actualiza la información y luego envía los cambios # # @param :output [Bool] # @return [Bool] def deploy status = false log = [] time_start create_remote_site! if remote_site_id.blank? create_njalla_records! if remote_info['njalla'].blank? save if remote_site_id.blank? || remote_info['njalla'].blank? raise DeployJob::DeployException, '' end site_client.tap do |c| stdout = Thread.new(publisher.logger_out) do |io| until io.eof? line = io.gets puts line if output log << line end end update remote_info: c.show(publishing_site).to_h status = c.publish(publishing_site, deploy_local.destination) publisher.logger.close stdout.join end time_stop create_stat! status, log.join status end def limit; end def size deploy_local.size end def destination; end # Devuelve las URLs de todos los protocolos def urls remote_info[:links].values.map do |protocol| [ protocol[:link], protocol[:gateway] ] end.flatten.compact.select do |link| link.include? '://' end end private # El cliente de la API # # TODO: cuando soportemos más, tiene que haber una relación entre # DeployDistributedPress y DistributedPressPublisher. # # @return [DistributedPressPublisher] def publisher @publisher ||= DistributedPressPublisher.first end # El cliente para actualizar el sitio # # @return [DistributedPress::V1::Client::Site] def site_client DistributedPress::V1::Client::Site.new(publisher.client) end # Genera el esquema de datos para poder publicar el sitio # # @return [DistributedPress::V1::Schemas::PublishingSite] def publishing_site DistributedPress::V1::Schemas::PublishingSite.new.call(id: remote_site_id) end # Genera el esquema de datos para crear el sitio # # @return [DistributedPressPublisher::V1::Schemas::NewSite] def create_site DistributedPress::V1::Schemas::NewSite.new.call(domain: hostname, protocols: { http: true, ipfs: true, hyper: true }) end # Crea el sitio en la instancia con el hostname especificado # # @return [nil] def create_remote_site! created_site = site_client.create(create_site) self.remote_site_id = created_site[:id] self.remote_info = created_site.to_h rescue DistributedPress::V1::Error => e ExceptionNotifier.notify_exception(e, data: { site: site.name }) ensure nil end # Crea los registros en Njalla # # @return [nil] def create_njalla_records! # XXX: Esto depende de nuestro DNS actual, cuando lo migremos hay # que eliminarlo. unless site.name.end_with? '.' self.remote_info['njalla'] = {} self.remote_info['njalla']['a'] = njalla.add_record(name: site.name, type: 'CNAME', content: "#{Site.domain}.").to_h self.remote_info['njalla']['ns'] = njalla.add_record(name: "_dnslink.#{site.name}", type: 'NS', content: "#{publisher.hostname}.").to_h end rescue HTTParty::Error => e ExceptionNotifier.notify_exception(e, data: { site: site.name }) self.remote_info['njalla'] = nil ensure nil end # Registra lo que sucedió # # @param status [Bool] # @param log [String] # @return [nil] def create_stat!(status, log) build_stats.create action: publisher.to_s,log: log, seconds: time_spent_in_seconds, bytes: size, status: status nil end # Actualizar registros en Njalla # # @return [Njalla::V1::Domain] def njalla @njalla ||= begin client = Njalla::V1::Client.new(token: ENV['NJALLA_TOKEN']) Njalla::V1::Domain.new(domain: Site.domain, client: client) end end end