mirror of
https://0xacab.org/sutty/sutty
synced 2025-03-14 20:08:19 +00:00
Merge branch 'issue-18074' into 'rails'
Draft: fix: optimizar api de sitios #18074 See merge request sutty/sutty!289
This commit is contained in:
commit
f2ddc1b653
11 changed files with 99 additions and 41 deletions
|
@ -4,51 +4,88 @@ module Api
|
||||||
module V1
|
module V1
|
||||||
# API para sitios
|
# API para sitios
|
||||||
class SitesController < BaseController
|
class SitesController < BaseController
|
||||||
|
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'],
|
http_basic_authenticate_with name: ENV['HTTP_BASIC_USER'],
|
||||||
password: ENV['HTTP_BASIC_PASSWORD']
|
password: ENV['HTTP_BASIC_PASSWORD']
|
||||||
|
end
|
||||||
|
|
||||||
# Lista de nombres de dominios a emitir certificados
|
# Lista de nombres de dominios a emitir certificados
|
||||||
def index
|
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
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
# @param query [ActiveRecord::Relation]
|
||||||
|
# @return [Array<String>]
|
||||||
|
def hostname_of(query)
|
||||||
|
query.pluck(Arel.sql("values->>'hostname'")).compact.uniq
|
||||||
|
end
|
||||||
|
|
||||||
def canonicalize(name)
|
def canonicalize(name)
|
||||||
name.end_with?('.') ? name[0..-2] : "#{name}.#{Site.domain}"
|
name.end_with?('.') ? name[0..-2] : "#{name}.#{Site.domain}"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Es un subdominio directo del dominio principal
|
||||||
|
#
|
||||||
|
# @param name [String]
|
||||||
|
# @return [Bool]
|
||||||
def subdomain?(name)
|
def subdomain?(name)
|
||||||
name.end_with? ".#{Site.domain}"
|
name.end_with?(SUBDOMAIN) && name.split('.').count == (PARTS + 1)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Dominios alternativos
|
# Es un dominio de prueba
|
||||||
def alternative_names
|
#
|
||||||
(DeployAlternativeDomain.all.map(&:hostname) + DeployLocalizedDomain.all.map(&:hostname)).map do |name|
|
# @param name [String]
|
||||||
canonicalize name
|
# @return [Bool]
|
||||||
end.reject do |name|
|
def testing?(name)
|
||||||
subdomain? name
|
name.end_with?(TESTING_SUBDOMAIN) && name.split('.').count == (PARTS + 2)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Nombres de los sitios
|
||||||
|
#
|
||||||
|
# @param name [String]
|
||||||
|
# @return [Array<String>]
|
||||||
|
def sites_names
|
||||||
|
Site.all.order(:name).pluck(:name)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Dominios alternativos, incluyendo todas las clases derivadas de
|
||||||
|
# esta.
|
||||||
|
#
|
||||||
|
# @return [Array<String>]
|
||||||
|
def alternative_names
|
||||||
|
hostname_of(DeployAlternativeDomain.all)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Obtener todos los sitios con API habilitada, es decir formulario
|
# Obtener todos los sitios con API habilitada, es decir formulario
|
||||||
# de contacto y/o colaboración anónima.
|
# de contacto y/o colaboración anónima.
|
||||||
#
|
#
|
||||||
# TODO: Optimizar
|
# @return [Array<String>]
|
||||||
def api_names
|
def api_names
|
||||||
Site.where(contact: true)
|
Site.where(contact: true)
|
||||||
.or(Site.where(colaboracion_anonima: true))
|
.or(Site.where(colaboracion_anonima: true))
|
||||||
.select("'api.' || name as name").map(&:name).map do |name|
|
.pluck(:name).map do |name|
|
||||||
canonicalize name
|
"api.#{name}"
|
||||||
end.reject do |name|
|
|
||||||
subdomain? name
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Todos los dominios con WWW habilitado
|
# Todos los dominios con WWW habilitado
|
||||||
def www_names
|
def www_names
|
||||||
Site.where(id: DeployWww.all.pluck(:site_id)).select("'www.' || name as name").map(&:name).map do |name|
|
Site.where(id: DeployWww.all.pluck(:site_id)).pluck(:name).map do |name|
|
||||||
canonicalize name
|
"www.#{name}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
# Soportar dominios alternativos
|
# Soportar dominios alternativos
|
||||||
class DeployAlternativeDomain < Deploy
|
class DeployAlternativeDomain < Deploy
|
||||||
store :values, accessors: %i[hostname], coder: JSON
|
store_accessor :values, :hostname
|
||||||
|
|
||||||
DEPENDENCIES = %i[deploy_local]
|
DEPENDENCIES = %i[deploy_local]
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,10 @@ require 'distributed_press/v1/client/site'
|
||||||
# Al ser publicado, envía los archivos en un tarball y actualiza la
|
# Al ser publicado, envía los archivos en un tarball y actualiza la
|
||||||
# información.
|
# información.
|
||||||
class DeployDistributedPress < Deploy
|
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_create :create_remote_site!
|
||||||
before_destroy :delete_remote_site!
|
before_destroy :delete_remote_site!
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
# Genera una versión onion
|
# Genera una versión onion
|
||||||
class DeployHiddenService < DeployWww
|
class DeployHiddenService < DeployWww
|
||||||
store :values, accessors: %i[onion], coder: JSON
|
store_accessor :values, :onion
|
||||||
|
|
||||||
before_create :create_hidden_service!
|
before_create :create_hidden_service!
|
||||||
|
|
||||||
|
|
|
@ -3,8 +3,6 @@
|
||||||
# Alojamiento local, solo genera el sitio, con lo que no necesita hacer
|
# Alojamiento local, solo genera el sitio, con lo que no necesita hacer
|
||||||
# nada más
|
# nada más
|
||||||
class DeployLocal < Deploy
|
class DeployLocal < Deploy
|
||||||
store :values, accessors: %i[], coder: JSON
|
|
||||||
|
|
||||||
before_destroy :remove_destination!
|
before_destroy :remove_destination!
|
||||||
|
|
||||||
def bundle(output: false)
|
def bundle(output: false)
|
||||||
|
|
|
@ -2,7 +2,8 @@
|
||||||
|
|
||||||
# Soportar dominios localizados
|
# Soportar dominios localizados
|
||||||
class DeployLocalizedDomain < DeployAlternativeDomain
|
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
|
# Generar un link simbólico del sitio principal al alternativo
|
||||||
def deploy(**)
|
def deploy(**)
|
||||||
|
|
|
@ -3,11 +3,15 @@
|
||||||
# Sincroniza sitios a servidores remotos usando Rsync. El servidor
|
# Sincroniza sitios a servidores remotos usando Rsync. El servidor
|
||||||
# remoto tiene que tener rsync instalado.
|
# remoto tiene que tener rsync instalado.
|
||||||
class DeployRsync < Deploy
|
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]
|
DEPENDENCIES = %i[deploy_local deploy_zip]
|
||||||
|
|
||||||
def deploy(output: false)
|
def deploy(output: false)
|
||||||
|
raise(ArgumentError, 'destination no está configurado') if destination.blank?
|
||||||
|
|
||||||
ssh? && rsync(output: output)
|
ssh? && rsync(output: output)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -18,13 +22,6 @@ class DeployRsync < Deploy
|
||||||
deploy_local.size
|
deploy_local.size
|
||||||
end
|
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]
|
# @return [String]
|
||||||
def url
|
def url
|
||||||
"https://#{hostname}/"
|
"https://#{hostname}/"
|
||||||
|
@ -43,9 +40,9 @@ class DeployRsync < Deploy
|
||||||
ssh_available = false
|
ssh_available = false
|
||||||
|
|
||||||
Net::SSH.start(host, user, verify_host_key: tofu, timeout: 5) do |ssh|
|
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
|
# 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}"
|
"#{host_key.ssh_type} #{host_key.fingerprint}"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -74,7 +71,7 @@ class DeployRsync < Deploy
|
||||||
#
|
#
|
||||||
# @return [Symbol]
|
# @return [Symbol]
|
||||||
def tofu
|
def tofu
|
||||||
values[:host_keys].present? ? :always : :accept_new
|
self.host_keys.present? ? :always : :accept_new
|
||||||
end
|
end
|
||||||
|
|
||||||
# Devuelve el par user host
|
# Devuelve el par user host
|
||||||
|
|
|
@ -2,8 +2,6 @@
|
||||||
|
|
||||||
# Vincula la versión del sitio con www a la versión sin
|
# Vincula la versión del sitio con www a la versión sin
|
||||||
class DeployWww < Deploy
|
class DeployWww < Deploy
|
||||||
store :values, accessors: %i[], coder: JSON
|
|
||||||
|
|
||||||
DEPENDENCIES = %i[deploy_local]
|
DEPENDENCIES = %i[deploy_local]
|
||||||
|
|
||||||
before_destroy :remove_destination!
|
before_destroy :remove_destination!
|
||||||
|
|
|
@ -6,8 +6,6 @@ require 'zip'
|
||||||
#
|
#
|
||||||
# TODO: Firmar con minisign
|
# TODO: Firmar con minisign
|
||||||
class DeployZip < Deploy
|
class DeployZip < Deploy
|
||||||
store :values, accessors: %i[], coder: JSON
|
|
||||||
|
|
||||||
DEPENDENCIES = %i[deploy_local]
|
DEPENDENCIES = %i[deploy_local]
|
||||||
|
|
||||||
# Una vez que el sitio está generado, tomar todos los archivos y
|
# Una vez que el sitio está generado, tomar todos los archivos y
|
||||||
|
|
|
@ -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
|
|
@ -882,8 +882,8 @@ CREATE TABLE public.deploys (
|
||||||
updated_at timestamp without time zone NOT NULL,
|
updated_at timestamp without time zone NOT NULL,
|
||||||
site_id integer,
|
site_id integer,
|
||||||
type character varying,
|
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'),
|
('20240316203721'),
|
||||||
('20240318183846'),
|
('20240318183846'),
|
||||||
('20240319124212'),
|
('20240319124212'),
|
||||||
('20240319144735');
|
('20240319144735'),
|
||||||
|
('20241223185830');
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue