mirror of
https://0xacab.org/sutty/sutty
synced 2024-11-16 17:46:21 +00:00
Los sitios pueden tener una ubicación canónica
This commit is contained in:
parent
baf6d203b8
commit
dcaac06fa4
8 changed files with 140 additions and 80 deletions
|
@ -7,6 +7,7 @@ class Site < ApplicationRecord
|
|||
include Site::Forms
|
||||
include Site::FindAndReplace
|
||||
include Site::Api
|
||||
include Site::Deployment
|
||||
include Tienda
|
||||
|
||||
# Cifrar la llave privada que cifra y decifra campos ocultos. Sutty
|
||||
|
@ -15,19 +16,11 @@ class Site < ApplicationRecord
|
|||
# protege de acceso al panel de Sutty!
|
||||
encrypts :private_key
|
||||
|
||||
# TODO: Hacer que los diferentes tipos de deploy se auto registren
|
||||
# @see app/services/site_service.rb
|
||||
DEPLOYS = %i[local private www zip hidden_service].freeze
|
||||
|
||||
validates :name, uniqueness: true, hostname: {
|
||||
allow_root_label: true
|
||||
}
|
||||
|
||||
validates :design_id, presence: true
|
||||
validates_uniqueness_of :name
|
||||
validates_inclusion_of :status, in: %w[waiting enqueued building]
|
||||
validates_presence_of :title
|
||||
validates :description, length: { in: 50..160 }
|
||||
validate :deploy_local_presence
|
||||
validate :compatible_layouts, on: :update
|
||||
|
||||
attr_reader :incompatible_layouts
|
||||
|
@ -38,9 +31,6 @@ class Site < ApplicationRecord
|
|||
belongs_to :licencia
|
||||
|
||||
has_many :log_entries, dependent: :destroy
|
||||
has_many :deploys, dependent: :destroy
|
||||
has_many :access_logs, through: :deploys
|
||||
has_many :build_stats, through: :deploys
|
||||
has_many :roles, dependent: :destroy
|
||||
has_many :usuaries, -> { where('roles.rol = ?', 'usuarie') },
|
||||
through: :roles
|
||||
|
@ -59,13 +49,11 @@ class Site < ApplicationRecord
|
|||
after_initialize :load_jekyll
|
||||
after_create :load_jekyll, :static_file_migration!
|
||||
# Cambiar el nombre del directorio
|
||||
before_update :update_name!, :update_deploy_local_hostname!, if: :name_changed?
|
||||
before_update :update_name!, if: :name_changed?
|
||||
before_save :add_private_key_if_missing!
|
||||
# Guardar la configuración si hubo cambios
|
||||
after_save :sync_attributes_with_config!
|
||||
|
||||
accepts_nested_attributes_for :deploys, allow_destroy: true
|
||||
|
||||
# El sitio en Jekyll
|
||||
attr_reader :jekyll
|
||||
|
||||
|
@ -86,46 +74,6 @@ class Site < ApplicationRecord
|
|||
@repository ||= Site::Repository.new path
|
||||
end
|
||||
|
||||
# El primer deploy del sitio
|
||||
#
|
||||
# @return [DeployLocal]
|
||||
def deploy_local
|
||||
@deploy_local ||= deploys.order(created_at: :asc).find_by(type: 'DeployLocal')
|
||||
end
|
||||
|
||||
# Todas las URLs posibles para este sitio, ordenados según fecha de
|
||||
# creación.
|
||||
#
|
||||
# @param :slash [Boolean] Con o sin / al final, por defecto con
|
||||
# @return [Array]
|
||||
def urls(slash: true)
|
||||
deploys.order(created_at: :asc).map(&:url).map do |url|
|
||||
slash ? "#{url}/" : url
|
||||
end
|
||||
end
|
||||
|
||||
# Todos los hostnames, ordenados según fecha de creación.
|
||||
#
|
||||
# @return [Array]
|
||||
def hostnames
|
||||
deploys.order(created_at: :asc).pluck(:hostname)
|
||||
end
|
||||
|
||||
# Obtiene la URL principal
|
||||
#
|
||||
# @param :slash [Boolean]
|
||||
# @return [String]
|
||||
def url(slash: true)
|
||||
deploy_local.url.tap do |url|
|
||||
"#{url}/" if slash
|
||||
end
|
||||
end
|
||||
|
||||
# TODO: Usar DeployCanonical
|
||||
def hostname
|
||||
deploy_local.hostname
|
||||
end
|
||||
|
||||
def invitade?(usuarie)
|
||||
!invitades.find_by(id: usuarie.id).nil?
|
||||
end
|
||||
|
@ -468,29 +416,11 @@ class Site < ApplicationRecord
|
|||
config.hostname = hostname
|
||||
end
|
||||
|
||||
# Si cambia el nombre queremos actualizarlo en el DeployLocal
|
||||
def update_deploy_local_hostname!
|
||||
deploy_local&.update hostname: name
|
||||
end
|
||||
|
||||
# Migra los archivos a Sutty
|
||||
def static_file_migration!
|
||||
Site::StaticFileMigration.new(site: self).migrate!
|
||||
end
|
||||
|
||||
# Valida si el sitio tiene al menos una forma de alojamiento asociada
|
||||
# y es la local
|
||||
#
|
||||
# TODO: Volver opcional el alojamiento local, pero ahora mismo está
|
||||
# atado a la generación del sitio así que no puede faltar
|
||||
def deploy_local_presence
|
||||
# Usamos size porque queremos saber la cantidad de deploys sin
|
||||
# guardar también
|
||||
return if deploys.size.positive? && deploys.map(&:type).include?('DeployLocal')
|
||||
|
||||
errors.add(:deploys, I18n.t('activerecord.errors.models.site.attributes.deploys.deploy_local_presence'))
|
||||
end
|
||||
|
||||
# Valida que al cambiar de plantilla no tengamos artículos en layouts
|
||||
# inexistentes.
|
||||
def compatible_layouts
|
||||
|
|
99
app/models/site/deployment.rb
Normal file
99
app/models/site/deployment.rb
Normal file
|
@ -0,0 +1,99 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Site
|
||||
# Abstrae todo el comportamiento de publicación del sitio en un
|
||||
# módulo.
|
||||
module Deployment
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
# TODO: Hacer que los diferentes tipos de deploy se auto registren
|
||||
# @see app/services/site_service.rb
|
||||
DEPLOYS = %i[local private www zip hidden_service].freeze
|
||||
|
||||
validates :name,
|
||||
format: { with: /\A[0-9a-z\-]+\z/,
|
||||
message: I18n.t('activerecord.errors.models.site.attributes.name.no_subdomains') }
|
||||
validates :name, hostname: true
|
||||
validates_presence_of :canonical_deploy
|
||||
validate :deploy_local_presence
|
||||
|
||||
has_one :canonical_deploy, class_name: 'Deploy'
|
||||
has_many :deploys, dependent: :destroy
|
||||
has_many :access_logs, through: :deploys
|
||||
has_many :build_stats, through: :deploys
|
||||
|
||||
before_validation :deploy_local_is_default_canonical_deploy!, unless: :canonical_deploy_id?
|
||||
before_update :update_deploy_local_hostname!, if: :name_changed?
|
||||
|
||||
accepts_nested_attributes_for :deploys, allow_destroy: true
|
||||
|
||||
# El primer deploy del sitio, si no existe en la base de datos es
|
||||
# porque recién estamos creando el sitio y todavía no se guardó.
|
||||
#
|
||||
# @return [DeployLocal]
|
||||
def deploy_local
|
||||
@deploy_local ||= deploys.order(created_at: :asc).find_by(type: 'DeployLocal') || deploys.find do |d|
|
||||
d.type == 'DeployLocal'
|
||||
end
|
||||
end
|
||||
|
||||
# Obtiene la URL principal
|
||||
#
|
||||
# @param :slash [Boolean]
|
||||
# @return [String]
|
||||
def canonical_url(slash: true)
|
||||
canonical_deploy.url.tap do |url|
|
||||
"#{url}/" if slash
|
||||
end
|
||||
end
|
||||
alias_method :url, :canonical_url
|
||||
|
||||
# Devuelve todas las URLs posibles
|
||||
#
|
||||
# @param :slash [Boolean]
|
||||
# @return [Array]
|
||||
def urls(slash: true)
|
||||
deploys.map(&:url).map do |url|
|
||||
slash ? "#{url}/" : url
|
||||
end
|
||||
end
|
||||
|
||||
# Obtiene el hostname principal
|
||||
#
|
||||
# @return [String]
|
||||
def hostname
|
||||
canonical_deploy.hostname
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Si cambia el nombre queremos actualizarlo en el DeployLocal y
|
||||
# recargar el deploy canónico para tomar el nombre que
|
||||
# corresponda.
|
||||
def update_deploy_local_hostname!
|
||||
deploy_local.update(hostname: name)
|
||||
canonical_deploy.reload if canonical_deploy == deploy_local
|
||||
end
|
||||
|
||||
# Si no asignamos un deploy canónico en el momento le asignamos el
|
||||
# deploy local
|
||||
def deploy_local_is_default_canonical_deploy!
|
||||
self.canonical_deploy ||= deploy_local
|
||||
end
|
||||
|
||||
# Valida si el sitio tiene al menos una forma de alojamiento asociada
|
||||
# y es la local
|
||||
#
|
||||
# TODO: Volver opcional el alojamiento local, pero ahora mismo está
|
||||
# atado a la generación del sitio así que no puede faltar
|
||||
def deploy_local_presence
|
||||
# Usamos size porque queremos saber la cantidad de deploys sin
|
||||
# guardar también
|
||||
return if deploys.size.positive? && deploys.map(&:type).include?('DeployLocal')
|
||||
|
||||
errors.add(:deploys, I18n.t('activerecord.errors.models.site.attributes.deploys.deploy_local_presence'))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -145,6 +145,8 @@ en:
|
|||
models:
|
||||
site:
|
||||
attributes:
|
||||
name:
|
||||
no_subdomains: 'Name cannot contain dots'
|
||||
deploys:
|
||||
deploy_local_presence: 'We need to be build the site!'
|
||||
design_id:
|
||||
|
|
|
@ -145,6 +145,8 @@ es:
|
|||
models:
|
||||
site:
|
||||
attributes:
|
||||
name:
|
||||
no_subdomains: 'El nombre no puede contener puntos'
|
||||
deploys:
|
||||
deploy_local_presence: '¡Necesitamos poder generar el sitio!'
|
||||
design_id:
|
||||
|
|
26
db/migrate/20210809155434_add_canonical_deploy_to_sites.rb
Normal file
26
db/migrate/20210809155434_add_canonical_deploy_to_sites.rb
Normal file
|
@ -0,0 +1,26 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Los sitios pueden tener muchos tipos de publicación pero solo uno es
|
||||
# el principal. Al usar un campo específico, podemos validar mejor su
|
||||
# presencia y modificación.
|
||||
#
|
||||
# El valor por defecto es 0 para poder crear la columna sin modificarla
|
||||
# después, pero la idea es que nunca haya ceros.
|
||||
class AddCanonicalDeployToSites < ActiveRecord::Migration[6.1]
|
||||
def up
|
||||
add_belongs_to :sites, :canonical_deploy, index: true, null: false, default: 0
|
||||
|
||||
# Si el sitio tenía un dominio alternativo, usar ese en lugar del
|
||||
# local, asumiendo que es el primero de todos los posibles.
|
||||
Site.find_each do |site|
|
||||
deploy = site.deploys.order(created_at: :asc).find_by_type('DeployAlternativeDomain')
|
||||
deploy ||= site.deploy_local
|
||||
|
||||
site.update canonical_deploy_id: deploy.id
|
||||
end
|
||||
end
|
||||
|
||||
def down
|
||||
remove_belongs_to :sites, :canonical_deploy, index: true
|
||||
end
|
||||
end
|
|
@ -21,13 +21,14 @@ module Api
|
|||
end
|
||||
|
||||
test 'el sitio tiene que existir' do
|
||||
hostname = @site.hostname
|
||||
@site.destroy
|
||||
|
||||
get v1_site_contact_cookie_url(@site.hostname, **@host)
|
||||
get v1_site_contact_cookie_url(hostname, **@host)
|
||||
|
||||
assert_not cookies[@site.name]
|
||||
|
||||
post v1_site_contact_url(site_id: @site.hostname, form: :contacto, **@host),
|
||||
post v1_site_contact_url(site_id: hostname, form: :contacto, **@host),
|
||||
params: {
|
||||
name: SecureRandom.hex,
|
||||
pronouns: SecureRandom.hex,
|
||||
|
|
|
@ -23,7 +23,7 @@ module Api
|
|||
|
||||
test 'se puede obtener un listado de todos' do
|
||||
get v1_sites_url(host: "api.#{Site.domain}"), headers: @authorization, as: :json
|
||||
assert_equal Site.all.pluck(:name), JSON.parse(response.body)
|
||||
assert_equal Deploy.all.pluck(:hostname), JSON.parse(response.body)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -26,18 +26,18 @@ class SiteTest < ActiveSupport::TestCase
|
|||
assert_not site2.valid?
|
||||
end
|
||||
|
||||
test 'el nombre del sitio puede contener subdominios' do
|
||||
test 'el nombre del sitio no puede contener subdominios' do
|
||||
@site = build :site, name: 'hola.chau'
|
||||
site.validate
|
||||
|
||||
assert_not site.errors.messages[:name].present?
|
||||
assert site.errors.messages[:name].present?
|
||||
end
|
||||
|
||||
test 'el nombre del sitio puede terminar con punto' do
|
||||
test 'el nombre del sitio no puede terminar con punto' do
|
||||
@site = build :site, name: 'hola.chau.'
|
||||
site.validate
|
||||
|
||||
assert_not site.errors.messages[:name].present?
|
||||
assert site.errors.messages[:name].present?
|
||||
end
|
||||
|
||||
test 'el nombre del sitio no puede contener wildcard' do
|
||||
|
|
Loading…
Reference in a new issue