mirror of
https://0xacab.org/sutty/sutty
synced 2024-11-16 04:31:41 +00:00
Merge branch 'issue-14169' into 'rails'
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
activity pub #14169 See merge request sutty/sutty!202
This commit is contained in:
commit
beec951cdf
22 changed files with 268 additions and 53 deletions
2
Gemfile
2
Gemfile
|
@ -39,7 +39,7 @@ gem 'commonmarker'
|
||||||
gem 'devise'
|
gem 'devise'
|
||||||
gem 'devise-i18n'
|
gem 'devise-i18n'
|
||||||
gem 'devise_invitable'
|
gem 'devise_invitable'
|
||||||
gem 'distributed-press-api-client', '~> 0.2.3'
|
gem 'distributed-press-api-client', '~> 0.3.0rc0'
|
||||||
gem 'njalla-api-client', '~> 0.2.0'
|
gem 'njalla-api-client', '~> 0.2.0'
|
||||||
gem 'email_address', git: 'https://github.com/fauno/email_address', branch: 'i18n'
|
gem 'email_address', git: 'https://github.com/fauno/email_address', branch: 'i18n'
|
||||||
gem 'exception_notification'
|
gem 'exception_notification'
|
||||||
|
|
|
@ -154,7 +154,7 @@ GEM
|
||||||
devise_invitable (2.0.8)
|
devise_invitable (2.0.8)
|
||||||
actionmailer (>= 5.0)
|
actionmailer (>= 5.0)
|
||||||
devise (>= 4.6)
|
devise (>= 4.6)
|
||||||
distributed-press-api-client (0.2.4)
|
distributed-press-api-client (0.3.0rc0)
|
||||||
addressable (~> 2.3, >= 2.3.0)
|
addressable (~> 2.3, >= 2.3.0)
|
||||||
climate_control
|
climate_control
|
||||||
dry-schema
|
dry-schema
|
||||||
|
@ -600,7 +600,7 @@ DEPENDENCIES
|
||||||
devise
|
devise
|
||||||
devise-i18n
|
devise-i18n
|
||||||
devise_invitable
|
devise_invitable
|
||||||
distributed-press-api-client (~> 0.2.3)
|
distributed-press-api-client (~> 0.3.0rc0)
|
||||||
dotenv-rails
|
dotenv-rails
|
||||||
down
|
down
|
||||||
ed25519
|
ed25519
|
||||||
|
|
|
@ -22,7 +22,12 @@ class BuildStatsController < ApplicationController
|
||||||
|
|
||||||
@table = site.deployment_list.map do |deploy|
|
@table = site.deployment_list.map do |deploy|
|
||||||
type = deploy.class.name.underscore
|
type = deploy.class.name.underscore
|
||||||
urls = deploy.respond_to?(:urls) ? deploy.urls : [deploy.url].compact
|
urls = deploy.urls.map do |url|
|
||||||
|
URI.parse(url)
|
||||||
|
rescue URI::Error
|
||||||
|
nil
|
||||||
|
end.compact
|
||||||
|
|
||||||
urls = [nil] if urls.empty?
|
urls = [nil] if urls.empty?
|
||||||
build_stat = deploy.build_stats.where(status: true).last
|
build_stat = deploy.build_stats.where(status: true).last
|
||||||
seconds = build_stat&.seconds || 0
|
seconds = build_stat&.seconds || 0
|
||||||
|
|
|
@ -51,7 +51,11 @@ class DeployJob < ApplicationJob
|
||||||
status = d.deploy(output: @output)
|
status = d.deploy(output: @output)
|
||||||
seconds = d.build_stats.last.try(:seconds) || 0
|
seconds = d.build_stats.last.try(:seconds) || 0
|
||||||
size = d.size
|
size = d.size
|
||||||
urls = d.respond_to?(:urls) ? d.urls : [d.url].compact
|
urls = d.urls.map do |url|
|
||||||
|
URI.parse url
|
||||||
|
rescue URI::Error
|
||||||
|
nil
|
||||||
|
end.compact
|
||||||
rescue StandardError => e
|
rescue StandardError => e
|
||||||
status = false
|
status = false
|
||||||
seconds ||= 0
|
seconds ||= 0
|
||||||
|
|
|
@ -52,7 +52,7 @@ class DeployMailer < ApplicationMailer
|
||||||
t << (row.map do |k, v|
|
t << (row.map do |k, v|
|
||||||
case k
|
case k
|
||||||
when :seconds then v[:human]
|
when :seconds then v[:human]
|
||||||
when :urls then url
|
when :urls then url.to_s
|
||||||
else v
|
else v
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
|
@ -23,6 +23,11 @@ class Deploy < ApplicationRecord
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# @return [Array]
|
||||||
|
def urls
|
||||||
|
[url].compact
|
||||||
|
end
|
||||||
|
|
||||||
def limit
|
def limit
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
end
|
end
|
||||||
|
@ -55,6 +60,22 @@ class Deploy < ApplicationRecord
|
||||||
@gems_dir ||= Rails.root.join('_storage', 'gems', site.name)
|
@gems_dir ||= Rails.root.join('_storage', 'gems', site.name)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Un entorno que solo tiene lo que necesitamos
|
||||||
|
#
|
||||||
|
# @return [Hash]
|
||||||
|
def env
|
||||||
|
# XXX: This doesn't support Windows paths :B
|
||||||
|
paths = [File.dirname(`which bundle`), '/usr/local/bin', '/usr/bin', '/bin']
|
||||||
|
|
||||||
|
# Las variables de entorno extra no pueden superponerse al local.
|
||||||
|
extra_env.merge({
|
||||||
|
'HOME' => home_dir,
|
||||||
|
'PATH' => paths.join(':'),
|
||||||
|
'JEKYLL_ENV' => Rails.env,
|
||||||
|
'LANG' => ENV['LANG'],
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
# Corre un comando, lo registra en la base de datos y devuelve el
|
# Corre un comando, lo registra en la base de datos y devuelve el
|
||||||
# estado.
|
# estado.
|
||||||
#
|
#
|
||||||
|
@ -98,6 +119,11 @@ class Deploy < ApplicationRecord
|
||||||
@local_env ||= {}
|
@local_env ||= {}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Devuelve opciones para jekyll build
|
||||||
|
#
|
||||||
|
# @return [String,nil]
|
||||||
|
def flags_for_build(**args); end
|
||||||
|
|
||||||
# Trae todas las dependencias
|
# Trae todas las dependencias
|
||||||
#
|
#
|
||||||
# @return [Array]
|
# @return [Array]
|
||||||
|
@ -107,6 +133,21 @@ class Deploy < ApplicationRecord
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
# Escribe el contenido en un archivo temporal y ejecuta el bloque
|
||||||
|
# provisto con el archivo como parámetro
|
||||||
|
#
|
||||||
|
# @param :content [String]
|
||||||
|
def with_tempfile(content, &block)
|
||||||
|
Tempfile.create(SecureRandom.hex) do |file|
|
||||||
|
file.write content.to_s
|
||||||
|
file.rewind
|
||||||
|
file.close
|
||||||
|
|
||||||
|
# @yieldparam :file [File]
|
||||||
|
yield file
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# @param [String]
|
# @param [String]
|
||||||
# @return [String]
|
# @return [String]
|
||||||
def readable_cmd(cmd)
|
def readable_cmd(cmd)
|
||||||
|
@ -117,7 +158,14 @@ class Deploy < ApplicationRecord
|
||||||
@deploy_local ||= site.deploys.find_by(type: 'DeployLocal')
|
@deploy_local ||= site.deploys.find_by(type: 'DeployLocal')
|
||||||
end
|
end
|
||||||
|
|
||||||
def non_local_deploys
|
# Consigue todas las variables de entorno configuradas por otros
|
||||||
@non_local_deploys ||= site.deploys.where.not(type: 'DeployLocal')
|
# deploys.
|
||||||
|
#
|
||||||
|
# @return [Hash]
|
||||||
|
def extra_env
|
||||||
|
@extra_env ||=
|
||||||
|
site.deployment_list.reduce({}) do |extra, deploy|
|
||||||
|
extra.merge deploy.local_env
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -62,32 +62,24 @@ class DeployLocal < Deploy
|
||||||
FileUtils.rm_rf(File.join(site.path, '.jekyll-cache'))
|
FileUtils.rm_rf(File.join(site.path, '.jekyll-cache'))
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
# Opciones necesarias para la compilación del sitio
|
||||||
|
|
||||||
def mkdir
|
|
||||||
FileUtils.mkdir_p destination
|
|
||||||
end
|
|
||||||
|
|
||||||
# Un entorno que solo tiene lo que necesitamos
|
|
||||||
#
|
#
|
||||||
# @return [Hash]
|
# @return [Hash]
|
||||||
def env
|
def local_env
|
||||||
# XXX: This doesn't support Windows paths :B
|
@local_env ||= {
|
||||||
paths = [File.dirname(`which bundle`), '/usr/local/bin', '/usr/bin', '/bin']
|
|
||||||
|
|
||||||
# Las variables de entorno extra no pueden superponerse al local.
|
|
||||||
extra_env.merge({
|
|
||||||
'HOME' => home_dir,
|
|
||||||
'PATH' => paths.join(':'),
|
|
||||||
'SPREE_API_KEY' => site.tienda_api_key,
|
'SPREE_API_KEY' => site.tienda_api_key,
|
||||||
'SPREE_URL' => site.tienda_url,
|
'SPREE_URL' => site.tienda_url,
|
||||||
'AIRBRAKE_PROJECT_ID' => site.id.to_s,
|
'AIRBRAKE_PROJECT_ID' => site.id.to_s,
|
||||||
'AIRBRAKE_PROJECT_KEY' => site.airbrake_api_key,
|
'AIRBRAKE_PROJECT_KEY' => site.airbrake_api_key,
|
||||||
'JEKYLL_ENV' => Rails.env,
|
|
||||||
'LANG' => ENV['LANG'],
|
|
||||||
'YARN_CACHE_FOLDER' => yarn_cache_dir,
|
'YARN_CACHE_FOLDER' => yarn_cache_dir,
|
||||||
'GEMS_SOURCE' => ENV['GEMS_SOURCE']
|
'GEMS_SOURCE' => ENV['GEMS_SOURCE']
|
||||||
})
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def mkdir
|
||||||
|
FileUtils.mkdir_p destination
|
||||||
end
|
end
|
||||||
|
|
||||||
def yarn_cache_dir
|
def yarn_cache_dir
|
||||||
|
@ -142,7 +134,11 @@ class DeployLocal < Deploy
|
||||||
end
|
end
|
||||||
|
|
||||||
def jekyll_build(output: false)
|
def jekyll_build(output: false)
|
||||||
run %(bundle exec jekyll build --trace --profile --destination "#{escaped_destination}"), output: output
|
with_tempfile(site.private_key_pem) do |file|
|
||||||
|
flags = extra_flags(private_key: file)
|
||||||
|
|
||||||
|
run %(bundle exec jekyll build --trace --profile #{flags} --destination "#{escaped_destination}"), output: output
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# no debería haber espacios ni caracteres especiales, pero por si
|
# no debería haber espacios ni caracteres especiales, pero por si
|
||||||
|
@ -156,17 +152,13 @@ class DeployLocal < Deploy
|
||||||
FileUtils.rm_rf destination
|
FileUtils.rm_rf destination
|
||||||
end
|
end
|
||||||
|
|
||||||
# Consigue todas las variables de entorno configuradas por otros
|
# Genera opciones extra desde los otros deploys
|
||||||
# deploys.
|
|
||||||
#
|
#
|
||||||
# @deprecated Solo tenía sentido para Distributed Press v0
|
# @param :args [Hash]
|
||||||
# @return [Hash]
|
# @return [String]
|
||||||
def extra_env
|
def extra_flags(**args)
|
||||||
@extra_env ||=
|
site.deployment_list.map do |deploy|
|
||||||
non_local_deploys.reduce({}) do |extra_env, deploy|
|
deploy.flags_for_build(**args)
|
||||||
extra_env.tap do |e|
|
end.compact.join(' ')
|
||||||
e.merge! deploy.local_env
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
55
app/models/deploy_social_distributed_press.rb
Normal file
55
app/models/deploy_social_distributed_press.rb
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'distributed_press/v1/social/client'
|
||||||
|
|
||||||
|
# Publicar novedades al Fediverso
|
||||||
|
class DeploySocialDistributedPress < Deploy
|
||||||
|
# Solo luego de publicar remotamente
|
||||||
|
DEPENDENCIES = %i[deploy_distributed_press deploy_rsync deploy_full_rsync]
|
||||||
|
|
||||||
|
# Envía las notificaciones
|
||||||
|
def deploy(output: false)
|
||||||
|
with_tempfile(site.private_key_pem) do |file|
|
||||||
|
key = Shellwords.escape file.path
|
||||||
|
dest = Shellwords.escape destination
|
||||||
|
|
||||||
|
run %(bundle exec jekyll notify --trace --key #{key} --destination "#{dest}"), output: output
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Igual que DeployLocal
|
||||||
|
#
|
||||||
|
# @return [String]
|
||||||
|
def destination
|
||||||
|
File.join(Rails.root, '_deploy', site.hostname)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Solo uno
|
||||||
|
#
|
||||||
|
# @return [Integer]
|
||||||
|
def limit
|
||||||
|
1
|
||||||
|
end
|
||||||
|
|
||||||
|
# Espacio ocupado, pero no podemos calcularlo
|
||||||
|
#
|
||||||
|
# @return [Integer]
|
||||||
|
def size
|
||||||
|
0
|
||||||
|
end
|
||||||
|
|
||||||
|
# El perfil de actor
|
||||||
|
#
|
||||||
|
# @return [String,nil]
|
||||||
|
def url
|
||||||
|
site.data.dig('activity_pub', 'actor')
|
||||||
|
end
|
||||||
|
|
||||||
|
# Genera la opción de llave privada para jekyll build
|
||||||
|
#
|
||||||
|
# @params :args [Hash]
|
||||||
|
# @return [String]
|
||||||
|
def flags_for_build(**args)
|
||||||
|
"--key #{Shellwords.escape args[:private_key].path}"
|
||||||
|
end
|
||||||
|
end
|
|
@ -4,8 +4,12 @@
|
||||||
#
|
#
|
||||||
# Esto es increíblemente difícil de lograr que salga bien!
|
# Esto es increíblemente difícil de lograr que salga bien!
|
||||||
class MetadataBoolean < MetadataTemplate
|
class MetadataBoolean < MetadataTemplate
|
||||||
|
# El valor por defecto es una versión booleana de lo que diga (o no
|
||||||
|
# diga) el esquema
|
||||||
|
#
|
||||||
|
# @return [Boolean]
|
||||||
def default_value
|
def default_value
|
||||||
false
|
!!super
|
||||||
end
|
end
|
||||||
|
|
||||||
# Los checkboxes son especiales porque la especificación de HTML
|
# Los checkboxes son especiales porque la especificación de HTML
|
||||||
|
|
19
app/models/metadata_created_at.rb
Normal file
19
app/models/metadata_created_at.rb
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# Fecha y hora de creación
|
||||||
|
class MetadataCreatedAt < MetadataTemplate
|
||||||
|
# Por defecto la hora actual, pero por retrocompatibilidad, queremos
|
||||||
|
# la fecha de publicación
|
||||||
|
def default_value
|
||||||
|
if post.date.value.to_date < Time.now.to_date
|
||||||
|
post.date.value
|
||||||
|
else
|
||||||
|
Time.now
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Nunca cambia
|
||||||
|
def value=(new_value)
|
||||||
|
value
|
||||||
|
end
|
||||||
|
end
|
|
@ -12,7 +12,7 @@ class Post
|
||||||
DEFAULT_ATTRIBUTES = %i[site document layout].freeze
|
DEFAULT_ATTRIBUTES = %i[site document layout].freeze
|
||||||
# Otros atributos que no vienen en los metadatos
|
# Otros atributos que no vienen en los metadatos
|
||||||
PRIVATE_ATTRIBUTES = %i[path slug attributes errors].freeze
|
PRIVATE_ATTRIBUTES = %i[path slug attributes errors].freeze
|
||||||
PUBLIC_ATTRIBUTES = %i[lang date uuid].freeze
|
PUBLIC_ATTRIBUTES = %i[lang date uuid created_at].freeze
|
||||||
ATTR_SUFFIXES = %w[? =].freeze
|
ATTR_SUFFIXES = %w[? =].freeze
|
||||||
|
|
||||||
attr_reader :attributes, :errors, :layout, :site, :document
|
attr_reader :attributes, :errors, :layout, :site, :document
|
||||||
|
@ -217,6 +217,11 @@ class Post
|
||||||
post: self, required: true)
|
post: self, required: true)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# La fecha de creación inmodificable del post
|
||||||
|
def created_at
|
||||||
|
@metadata[:created_at] ||= MetadataCreatedAt.new(document: document, site: site, layout: layout, name: :created_at, type: :created_at, post: self, required: true)
|
||||||
|
end
|
||||||
|
|
||||||
# Detecta si es un atributo válido o no, a partir de la tabla de la
|
# Detecta si es un atributo válido o no, a partir de la tabla de la
|
||||||
# plantilla
|
# plantilla
|
||||||
def attribute?(mid)
|
def attribute?(mid)
|
||||||
|
@ -267,6 +272,7 @@ class Post
|
||||||
# Y que no se procese liquid
|
# Y que no se procese liquid
|
||||||
yaml['liquid'] = false
|
yaml['liquid'] = false
|
||||||
yaml['usuaries'] = usuaries.map(&:id).uniq
|
yaml['usuaries'] = usuaries.map(&:id).uniq
|
||||||
|
yaml['created_at'] = created_at.value
|
||||||
yaml['last_modified_at'] = modified_at
|
yaml['last_modified_at'] = modified_at
|
||||||
|
|
||||||
"#{yaml.to_yaml}---\n\n#{body}"
|
"#{yaml.to_yaml}---\n\n#{body}"
|
||||||
|
|
|
@ -10,6 +10,7 @@ class Site < ApplicationRecord
|
||||||
include Site::DeployDependencies
|
include Site::DeployDependencies
|
||||||
include Site::BuildStats
|
include Site::BuildStats
|
||||||
include Site::LayoutOrdering
|
include Site::LayoutOrdering
|
||||||
|
include Site::SocialDistributedPress
|
||||||
include Tienda
|
include Tienda
|
||||||
|
|
||||||
# Cifrar la llave privada que cifra y decifra campos ocultos. Sutty
|
# Cifrar la llave privada que cifra y decifra campos ocultos. Sutty
|
||||||
|
@ -18,10 +19,6 @@ class Site < ApplicationRecord
|
||||||
# protege de acceso al panel de Sutty!
|
# protege de acceso al panel de Sutty!
|
||||||
encrypts :private_key
|
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 distributed_press].freeze
|
|
||||||
|
|
||||||
validates :name, uniqueness: true, hostname: {
|
validates :name, uniqueness: true, hostname: {
|
||||||
allow_root_label: true
|
allow_root_label: true
|
||||||
}
|
}
|
||||||
|
|
23
app/models/site/social_distributed_press.rb
Normal file
23
app/models/site/social_distributed_press.rb
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class Site
|
||||||
|
# Agrega soporte para Social Distributed Press en los sitios
|
||||||
|
module SocialDistributedPress
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
encrypts :private_key_pem
|
||||||
|
|
||||||
|
before_save :generate_private_key_pem!, unless: :private_key_pem?
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
# Genera la llave privada y la almacena
|
||||||
|
#
|
||||||
|
# @return [nil]
|
||||||
|
def generate_private_key_pem!
|
||||||
|
self.private_key_pem ||= DistributedPress::V1::Social::Client.new(public_key_url: nil, key_size: 2048).private_key.export
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -54,9 +54,8 @@ SiteService = Struct.new(:site, :usuarie, :params, keyword_init: true) do
|
||||||
|
|
||||||
# Genera los Deploy necesarios para el sitio a menos que ya los tenga.
|
# Genera los Deploy necesarios para el sitio a menos que ya los tenga.
|
||||||
def build_deploys
|
def build_deploys
|
||||||
Site::DEPLOYS.map { |deploy| "Deploy#{deploy.to_s.camelcase}" }
|
Deploy.subclasses.each do |deploy|
|
||||||
.each do |deploy|
|
next if site.deploys.find_by type: deploy.name
|
||||||
next if site.deploys.find_by type: deploy
|
|
||||||
|
|
||||||
site.deploys.build type: deploy
|
site.deploys.build type: deploy
|
||||||
end
|
end
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
- row[:urls].each do |url|
|
- row[:urls].each do |url|
|
||||||
%tr
|
%tr
|
||||||
%th{ scope: 'row' }= row[:title]
|
%th{ scope: 'row' }= row[:title]
|
||||||
%td= link_to_if url.present?, url, url, class: 'word-break-all'
|
%td= link_to_if (url.present? && url.scheme.present?), url.to_s, url.to_s, class: 'word-break-all'
|
||||||
%td
|
%td
|
||||||
%time{ datetime: row[:seconds][:machine] }= row[:seconds][:human]
|
%time{ datetime: row[:seconds][:machine] }= row[:seconds][:human]
|
||||||
%td= row[:size]
|
%td= row[:size]
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
%tr
|
%tr
|
||||||
%td= row[:title]
|
%td= row[:title]
|
||||||
%td= row[:status]
|
%td= row[:status]
|
||||||
%td= link_to_if url.present?, url, url
|
%td= link_to_if (url.present? && url.scheme.present?), url.to_s, url.to_s
|
||||||
%td
|
%td
|
||||||
%time{ datetime: row[:seconds][:machine] }= row[:seconds][:human]
|
%time{ datetime: row[:seconds][:machine] }= row[:seconds][:human]
|
||||||
%td= row[:size]
|
%td= row[:size]
|
||||||
|
|
21
app/views/deploys/_deploy_social_distributed_press.haml
Normal file
21
app/views/deploys/_deploy_social_distributed_press.haml
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
-# Publicar a la web distribuida
|
||||||
|
|
||||||
|
.row
|
||||||
|
.col
|
||||||
|
= deploy.hidden_field :id
|
||||||
|
= deploy.hidden_field :type
|
||||||
|
.custom-control.custom-switch
|
||||||
|
-#
|
||||||
|
El checkbox invierte la lógica de destrucción porque queremos
|
||||||
|
crear el deploy si está activado y destruirlo si está
|
||||||
|
desactivado.
|
||||||
|
= deploy.check_box :_destroy,
|
||||||
|
{ checked: deploy.object.persisted?, class: 'custom-control-input' },
|
||||||
|
'0', '1'
|
||||||
|
= deploy.label :_destroy, class: 'custom-control-label' do
|
||||||
|
%h3= t('.title')
|
||||||
|
= sanitize_markdown t('.help'),
|
||||||
|
tags: %w[p strong em a]
|
||||||
|
|
||||||
|
|
||||||
|
%hr/
|
4
app/views/posts/attribute_ro/_created_at.haml
Normal file
4
app/views/posts/attribute_ro/_created_at.haml
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
%tr{ id: attribute }
|
||||||
|
%th= post_label_t(attribute, post: post)
|
||||||
|
%td{ dir: dir, lang: locale }
|
||||||
|
%time{ datetime: metadata.value.xmlschema }= l metadata.value
|
1
app/views/posts/attributes/_created_at.haml
Normal file
1
app/views/posts/attributes/_created_at.haml
Normal file
|
@ -0,0 +1 @@
|
||||||
|
-# nada
|
|
@ -123,6 +123,10 @@ en:
|
||||||
title: Distributed Web
|
title: Distributed Web
|
||||||
success: Success!
|
success: Success!
|
||||||
error: Error
|
error: Error
|
||||||
|
deploy_social_distributed_press:
|
||||||
|
title: Fediverse
|
||||||
|
success: Success!
|
||||||
|
error: Error
|
||||||
deploy_reindex:
|
deploy_reindex:
|
||||||
title: Reindex
|
title: Reindex
|
||||||
success: Success!
|
success: Success!
|
||||||
|
@ -307,6 +311,14 @@ en:
|
||||||
indefinitely.
|
indefinitely.
|
||||||
|
|
||||||
[Learn more](https://sutty.nl/learn-more-about-publish-to-dweb-functionality/)
|
[Learn more](https://sutty.nl/learn-more-about-publish-to-dweb-functionality/)
|
||||||
|
deploy_social_distributed_press:
|
||||||
|
title: 'Publish on the Fediverse'
|
||||||
|
help: |
|
||||||
|
By using the ActivityPub protocol, people on the Fediverse
|
||||||
|
([Mastodon](https://joinmastodon.org/servers),
|
||||||
|
[Pixelfed](https://pixelfed.social/site/about), and
|
||||||
|
[others](https://fediverse.party/)) can follow your site,
|
||||||
|
receive news and interact with them.
|
||||||
stats:
|
stats:
|
||||||
index:
|
index:
|
||||||
title: Statistics
|
title: Statistics
|
||||||
|
@ -512,6 +524,8 @@ en:
|
||||||
feedback: 'This field cannot be empty!'
|
feedback: 'This field cannot be empty!'
|
||||||
uuid:
|
uuid:
|
||||||
label: 'Unique identifier'
|
label: 'Unique identifier'
|
||||||
|
created_at:
|
||||||
|
label: 'Created at'
|
||||||
geo:
|
geo:
|
||||||
uri: 'Open in app'
|
uri: 'Open in app'
|
||||||
osm: 'Open in web map'
|
osm: 'Open in web map'
|
||||||
|
|
|
@ -123,6 +123,10 @@ es:
|
||||||
title: Web distribuida
|
title: Web distribuida
|
||||||
success: ¡Éxito!
|
success: ¡Éxito!
|
||||||
error: Hubo un error
|
error: Hubo un error
|
||||||
|
deploy_social_distributed_press:
|
||||||
|
title: Fediverso
|
||||||
|
success: ¡Éxito!
|
||||||
|
error: Hubo un error
|
||||||
deploy_reindex:
|
deploy_reindex:
|
||||||
title: Reindexación
|
title: Reindexación
|
||||||
success: ¡Éxito!
|
success: ¡Éxito!
|
||||||
|
@ -312,6 +316,14 @@ es:
|
||||||
copias de tu contenido indefinidamente.
|
copias de tu contenido indefinidamente.
|
||||||
|
|
||||||
[Saber más](https://sutty.nl/saber-mas-sobre-publicar-a-la-web-distribuida/)
|
[Saber más](https://sutty.nl/saber-mas-sobre-publicar-a-la-web-distribuida/)
|
||||||
|
deploy_social_distributed_press:
|
||||||
|
title: 'Publicar al Fediverso'
|
||||||
|
help: |
|
||||||
|
Utilizando el protocolo ActivityPub, otras personas en el
|
||||||
|
Fediverso ([Mastodon](https://joinmastodon.org/servers),
|
||||||
|
[Pixelfed](https://pixelfed.social/site/about) y
|
||||||
|
[otros](https://fediverse.party/)) pueden seguir a tu sitio,
|
||||||
|
recibir novedades e interactuar con ellas.
|
||||||
stats:
|
stats:
|
||||||
index:
|
index:
|
||||||
title: Estadísticas
|
title: Estadísticas
|
||||||
|
@ -520,6 +532,8 @@ es:
|
||||||
feedback: '¡Este campo no puede estar vacío!'
|
feedback: '¡Este campo no puede estar vacío!'
|
||||||
uuid:
|
uuid:
|
||||||
label: 'Identificador único'
|
label: 'Identificador único'
|
||||||
|
created_at:
|
||||||
|
label: 'Fecha de creación'
|
||||||
geo:
|
geo:
|
||||||
uri: 'Abrir en aplicación'
|
uri: 'Abrir en aplicación'
|
||||||
osm: 'Abrir en mapa web'
|
osm: 'Abrir en mapa web'
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# Almacena las llaves privadas de cada sitio
|
||||||
|
class AddPrivateKeyPemCiphertextToSites < ActiveRecord::Migration[6.1]
|
||||||
|
# Agrega la columna cifrada
|
||||||
|
def change
|
||||||
|
add_column :sites, :private_key_pem_ciphertext, :text
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in a new issue