5
0
Fork 0
mirror of https://0xacab.org/sutty/sutty synced 2025-03-14 17:18:20 +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:
fauno 2025-01-26 05:30:59 +00:00
commit f2ddc1b653
11 changed files with 99 additions and 41 deletions

View file

@ -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<String>]
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<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
(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<String>]
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

View file

@ -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]

View file

@ -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!

View file

@ -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!

View file

@ -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)

View file

@ -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(**)

View file

@ -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

View file

@ -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!

View file

@ -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

View file

@ -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

View file

@ -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');