5
0
Fork 0
mirror of https://0xacab.org/sutty/sutty synced 2024-11-28 14:16:22 +00:00

Merge branch 'rails' into panel.testing.sutty.nl

This commit is contained in:
f 2024-05-15 15:45:21 -03:00
commit b1ed50ec6c
No known key found for this signature in database
61 changed files with 288 additions and 194 deletions

View file

@ -1,3 +1,6 @@
stages:
- "test"
- "deploy"
.apk-add: &apk-add .apk-add: &apk-add
- "apk add go-task diffutils gitlab_ci_log_section" - "apk add go-task diffutils gitlab_ci_log_section"
.disable-hainish: &disable-hainish .disable-hainish: &disable-hainish
@ -6,9 +9,15 @@
- paths: - paths:
- "vendor/ruby" - "vendor/ruby"
- ".bundle" - ".bundle"
key:
files:
- "Gemfile.lock"
.cache-node: &cache-node .cache-node: &cache-node
- paths: - paths:
- "node_modules" - "node_modules"
key:
files:
- "yarn.lock"
.cache-task: &cache-task .cache-task: &cache-task
- paths: - paths:
- ".task" - ".task"
@ -18,11 +27,25 @@ variables:
LC_ALL: "C.UTF-8" LC_ALL: "C.UTF-8"
HAINISH: "" HAINISH: ""
cache: cache:
push:
stage: "test"
only:
- "rails"
except:
- "schedules"
before_script:
- "git config --global user.email \"${GIT_USER_EMAIL:-$GITLAB_USER_EMAIL}\""
- "git config --global user.name \"${GIT_USER_NAME:-$GITLAB_USER_NAME}\""
- "git remote set-url --push origin \"https://GITLAB_CI_PUSH_TOKEN:${GITLAB_CI_PUSH_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}.git\""
script:
- "git commit --allow-empty -m \"ci: test [skip ci]\""
- "git push -o ci.skip origin HEAD:${CI_COMMIT_BRANCH}"
assets: assets:
stage: "deploy" stage: "deploy"
only: only:
- "rails" - "rails"
- "17.3.alpine.panel.sutty.nl" - "production.panel.sutty.nl"
- "panel.sutty.nl"
except: except:
- "schedules" - "schedules"
cache: cache:
@ -30,14 +53,14 @@ assets:
- *cache-node - *cache-node
- *cache-task - *cache-task
before_script: before_script:
- *apk-add
- "gitlab_ci_log_section --name git --header=\"Configuring git\"" - "gitlab_ci_log_section --name git --header=\"Configuring git\""
- "git config --global user.email \"${GIT_USER_EMAIL:-$GITLAB_USER_EMAIL}\"" - "git config --global user.email \"${GIT_USER_EMAIL:-$GITLAB_USER_EMAIL}\""
- "git config --global user.name \"${GIT_USER_NAME:-$GITLAB_USER_NAME}\"" - "git config --global user.name \"${GIT_USER_NAME:-$GITLAB_USER_NAME}\""
- "git remote set-url --push origin \"https://${GITLAB_USERNAME}:${GITLAB_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}.git\"" - "git remote set-url --push origin \"https://GITLAB_CI_PUSH_TOKEN:${GITLAB_CI_PUSH_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}.git\""
- "gitlab_ci_log_section --name git --end" - "gitlab_ci_log_section --name git --end"
- "gitlab_ci_log_section --name apk --header=\"Installing dependencies\"" - "gitlab_ci_log_section --name apk --header=\"Installing dependencies\""
- "apk add brotli" - "apk add brotli"
- *apk-add
- *disable-hainish - *disable-hainish
- "gitlab_ci_log_section --name apk --end" - "gitlab_ci_log_section --name apk --end"
script: script:
@ -45,7 +68,7 @@ assets:
- "go-task assets" - "go-task assets"
after_script: after_script:
- "git add public && git commit -m \"ci: assets [skip ci]\"" - "git add public && git commit -m \"ci: assets [skip ci]\""
- "git push -o ci.skip" - "git push -o ci.skip origin HEAD:${CI_COMMIT_BRANCH}"
gem-audit: gem-audit:
stage: "test" stage: "test"
only: only:

View file

@ -80,6 +80,7 @@ gem 'yaml_db', git: 'https://0xacab.org/sutty/yaml_db.git'
gem 'kaminari' gem 'kaminari'
gem 'device_detector' gem 'device_detector'
gem 'htmlbeautifier' gem 'htmlbeautifier'
gem 'dry-schema'
gem 'rubanok' gem 'rubanok'
gem 'after_commit_everywhere', '~> 1.0' gem 'after_commit_everywhere', '~> 1.0'

View file

@ -646,6 +646,7 @@ DEPENDENCIES
distributed-press-api-client (~> 0.4.1) distributed-press-api-client (~> 0.4.1)
dotenv-rails dotenv-rails
down down
dry-schema
ed25519 ed25519
email_address! email_address!
exception_notification exception_notification

View file

@ -185,9 +185,13 @@ tasks:
- "test -f ../hain/usr/bin/bundler-audit" - "test -f ../hain/usr/bin/bundler-audit"
rubocop: rubocop:
desc: "Ruby linting" desc: "Ruby linting"
deps:
- "gems"
cmds: cmds:
- "./bin/modified_files | ./bin/with_extension rb | xargs -r {{.HAINISH}} bundle exec rubocop {{.CLI_ARGS}}" - "./bin/modified_files | ./bin/with_extension rb | xargs -r {{.HAINISH}} bundle exec rubocop {{.CLI_ARGS}}"
haml-lint: haml-lint:
desc: "HAML linting" desc: "HAML linting"
deps:
- "gems"
cmds: cmds:
- "./bin/modified_files | ./bin/with_extension haml | xargs -r {{.HAINISH}} bundle exec haml-lint {{.CLI_ARGS}}" - "./bin/modified_files | ./bin/with_extension haml | xargs -r {{.HAINISH}} bundle exec haml-lint {{.CLI_ARGS}}"

View file

@ -18,7 +18,7 @@ module Api
# Si todo salió bien, enviar los correos y redirigir al sitio. # Si todo salió bien, enviar los correos y redirigir al sitio.
# El sitio nos dice a dónde tenemos que ir. # El sitio nos dice a dónde tenemos que ir.
ContactJob.perform_later site.id, ContactJob.perform_later site,
params[:form], params[:form],
contact_params.to_h.symbolize_keys, contact_params.to_h.symbolize_keys,
params[:redirect] params[:redirect]

View file

@ -11,7 +11,7 @@ module Api
# respondemos con lo mismo. # respondemos con lo mismo.
def create def create
if (site&.airbrake_valid? airbrake_token) && !detected_device.bot? if (site&.airbrake_valid? airbrake_token) && !detected_device.bot?
BacktraceJob.perform_later site_id: params[:site_id], BacktraceJob.perform_later site: site,
params: airbrake_params.to_h params: airbrake_params.to_h
end end

View file

@ -14,7 +14,7 @@ class ApplicationController < ActionController::Base
after_action :store_location! after_action :store_location!
before_action do before_action do
Rack::MiniProfiler.authorize_request if current_usuarie&.email&.ends_with?('@' + ENV.fetch('SUTTY', 'sutty.nl')) Rack::MiniProfiler.authorize_request if current_usuarie&.email&.ends_with?("@#{ENV.fetch('SUTTY', 'sutty.nl')}")
end end
# No tenemos índice de sutty, vamos directamente a ver el listado de # No tenemos índice de sutty, vamos directamente a ver el listado de
@ -58,9 +58,7 @@ class ApplicationController < ActionController::Base
def current_locale def current_locale
locale = params[:change_locale_to] locale = params[:change_locale_to]
if locale.present? && I18n.locale_available?(locale) session[:locale] = params[:change_locale_to] if locale.present? && I18n.locale_available?(locale)
session[:locale] = params[:change_locale_to]
end
session[:locale] || current_usuarie&.lang || I18n.locale session[:locale] || current_usuarie&.lang || I18n.locale
end end
@ -121,5 +119,4 @@ class ApplicationController < ActionController::Base
session[:usuarie_return_to] = request.fullpath session[:usuarie_return_to] = request.fullpath
end end
end end

View file

@ -4,7 +4,7 @@ class EnvController < ActionController::Base
skip_before_action :verify_authenticity_token skip_before_action :verify_authenticity_token
def index def index
@site = Site.find_by_name('panel') @site = Site.find_by_name('panel') || Site.first
stale? @site if @site stale? @site if @site
end end

View file

@ -13,7 +13,7 @@ module ApplicationHelper
root = names.shift root = names.shift
names.each do |n| names.each do |n|
root += '[' + n.to_s + ']' root += "[#{n}]"
end end
[root, name] [root, name]
@ -41,7 +41,7 @@ module ApplicationHelper
def plain_field_name_for(*names) def plain_field_name_for(*names)
root, name = field_name_for(*names) root, name = field_name_for(*names)
root + '[' + name.to_s + ']' "#{root}[#{name}]"
end end
def distance_of_time_in_words_if_more_than_a_minute(seconds) def distance_of_time_in_words_if_more_than_a_minute(seconds)

View file

@ -11,14 +11,33 @@ class ActivityPub
class FetchJob < ApplicationJob class FetchJob < ApplicationJob
self.priority = 50 self.priority = 50
attr_reader :object, :response
# Notificar errores de JSON con el contenido, tomar los errores de
# validación y conexión como errores temporales y notificar todo lo
# demás sin reintentar.
#
# @param error [Exception]
# @return [Bool]
discard_on(FastJsonparser::ParseError) do |error|
ExceptionNotifier.notify_exception(error, data: { site: site.name, object: object.uri, body: response.body })
end
retry_on ActiveRecord::RecordInvalid
retry_on SocketError, wait: ApplicationJob.random_wait
retry_on SystemCallError, wait: ApplicationJob.random_wait
retry_on Net::OpenTimeout, wait: ApplicationJob.random_wait
retry_on OpenSSL::OpenSSLError, wait: ApplicationJob.random_wait
def perform(site:, object_id:) def perform(site:, object_id:)
ActivityPub::Object.transaction do ActivityPub::Object.transaction do
object = ::ActivityPub::Object.find(object_id) @site = site
@object = ::ActivityPub::Object.find(object_id)
return if object.blank? return if object.blank?
return if object.activity_pubs.where(aasm_state: 'removed').count.positive? return if object.activity_pubs.where(aasm_state: 'removed').count.positive?
response = site.social_inbox.dereferencer.get(uri: object.uri) @response = site.social_inbox.dereferencer.get(uri: object.uri)
# @todo Fallar cuando la respuesta no funcione? # @todo Fallar cuando la respuesta no funcione?
# @todo Eliminar en 410 Gone # @todo Eliminar en 410 Gone
@ -31,7 +50,8 @@ class ActivityPub
content = FastJsonparser.parse(response.body) content = FastJsonparser.parse(response.body)
# Modificar atómicamente # Modificar atómicamente
::ActivityPub::Object.lock.find(object_id).update!(content: content, type: ActivityPub::Object.type_from(content).name) ::ActivityPub::Object.lock.find(object_id).update!(content: content,
type: ActivityPub::Object.type_from(content).name)
object = ::ActivityPub::Object.find(object_id) object = ::ActivityPub::Object.find(object_id)
# Actualiza la mención # Actualiza la mención
@ -39,8 +59,6 @@ class ActivityPub
# Arreglar las relaciones con actividades también # Arreglar las relaciones con actividades también
ActivityPub.where(object_id: object.id).update_all(object_type: object.type, updated_at: Time.now) ActivityPub.where(object_id: object.id).update_all(object_type: object.type, updated_at: Time.now)
rescue FastJsonparser::ParseError => e
ExceptionNotifier.notify_exception(e, data: { site: site.name, object: object.uri, body: response.body })
end end
end end
end end

View file

@ -3,7 +3,9 @@
class ActivityPub class ActivityPub
# Procesar las actividades a medida que llegan # Procesar las actividades a medida que llegan
class ProcessJob < ApplicationJob class ProcessJob < ApplicationJob
attr_reader :body, :initial_state attr_reader :body
retry_on ActiveRecord::RecordInvalid
# Procesa la actividad en segundo plano # Procesa la actividad en segundo plano
# #
@ -12,7 +14,6 @@ class ActivityPub
def perform(site:, body:, initial_state: :paused) def perform(site:, body:, initial_state: :paused)
@site = site @site = site
@body = body @body = body
@initial_state = initial_state
ActiveRecord::Base.connection_pool.with_connection do ActiveRecord::Base.connection_pool.with_connection do
::ActivityPub.transaction do ::ActivityPub.transaction do
@ -28,24 +29,6 @@ class ActivityPub
end end
end end
# Al generar una excepción, en lugar de seguir intentando, enviamos
# el reporte.
def handle_error(error)
case error
when ActiveRecord::RecordInvalid then retry_in(ApplicationJob.random_wait)
else
ExceptionNotifier.notify_exception(
error,
data: {
site: site.name,
body: body,
initial_state: initial_state,
activity: original_activity,
message: 'Esta acción se canceló automáticamente, para regenerarla, volver a correr el proceso con los mismos parámetros.'
})
end
end
private private
# Si el objeto ya viene incorporado en la actividad o lo tenemos # Si el objeto ya viene incorporado en la actividad o lo tenemos

View file

@ -43,7 +43,9 @@ class ActivityPub
# Si alguna falló, reintentar # Si alguna falló, reintentar
raise if logs.present? raise if logs.present?
rescue Exception => e rescue Exception => e
ExceptionNotifier.notify_exception(e, data: { site: site.name, logs: logs, blocklist: blocklist, allowlist: allowlist, pauselist: pauselist }) ExceptionNotifier.notify_exception(e,
data: { site: site.name, logs: logs, blocklist: blocklist,
allowlist: allowlist, pauselist: pauselist })
raise raise
end end

View file

@ -8,16 +8,17 @@ class ApplicationJob < ActiveJob::Base
# superpongan tareas # superpongan tareas
# #
# @return [Array<Integer>] # @return [Array<Integer>]
RANDOM_WAIT = [3, 5, 7, 11, 13] RANDOM_WAIT = [3, 5, 7, 11, 13].freeze
# @return [ActiveSupport::Duration] # @return [ActiveSupport::Duration]
def self.random_wait def self.random_wait
RANDOM_WAIT.sample.seconds RANDOM_WAIT.sample.seconds
end end
private attr_reader :site
def site # Si falla por cualquier cosa informar y descartar
@site ||= Site.find @params[:site_id] discard_on(Exception) do |job, error|
ExceptionNotifier.notify_exception(error, data: { job: job })
end end
end end

View file

@ -6,10 +6,10 @@ class BacktraceJob < ApplicationJob
EMPTY_SOURCEMAP = { 'mappings' => '' }.freeze EMPTY_SOURCEMAP = { 'mappings' => '' }.freeze
attr_reader :params, :site_id attr_reader :params
def perform(site_id:, params:) def perform(site:, params:)
@site_id = site_id @site = site
@params = params @params = params
unless sources.empty? unless sources.empty?
@ -44,10 +44,6 @@ class BacktraceJob < ApplicationJob
private private
def site
@site ||= Site.find_by_id(site_id)
end
# Obtiene todos los archivos del backtrace solo si los puede descargar # Obtiene todos los archivos del backtrace solo si los puede descargar
# desde fuentes seguras. # desde fuentes seguras.
# #
@ -59,9 +55,7 @@ class BacktraceJob < ApplicationJob
x['backtrace'] x['backtrace']
end.flatten.map do |x| end.flatten.map do |x|
x['file'].split('@').last x['file'].split('@').last
end.uniq.select do |x| end.uniq.grep(%r{\Ahttps://})
%r{\Ahttps://} =~ x
end
end end
# Descarga y devuelve los datos de un archivo # Descarga y devuelve los datos de un archivo

View file

@ -5,10 +5,8 @@ class ContactJob < ApplicationJob
# @param [Integer] # @param [Integer]
# @param [String] # @param [String]
# @param [Hash] # @param [Hash]
def perform(site_id, form_name, form, origin = nil) def perform(site, form_name, form, origin = nil)
# Retrocompabilidad al actualizar a 2.7.1 @site = site
# @see ApplicationJob#site
@params = { site_id: site_id }
# Sanitizar los valores # Sanitizar los valores
form.each_key do |key| form.each_key do |key|
@ -23,7 +21,7 @@ class ContactJob < ApplicationJob
usuaries.each_slice(10) do |u| usuaries.each_slice(10) do |u|
ContactMailer.with(form_name: form_name, ContactMailer.with(form_name: form_name,
form: form, form: form,
site_id: site_id, site: site,
usuaries_emails: u, usuaries_emails: u,
origin: origin) origin: origin)
.notify_usuaries.deliver_now .notify_usuaries.deliver_now

View file

@ -11,44 +11,36 @@ class DeployJob < ApplicationJob
# Lanzar lo antes posible # Lanzar lo antes posible
self.priority = 10 self.priority = 10
def handle_error(error) retry_on DeployAlreadyRunningException, wait: 1.minute
case error discard_on DeployTimedOutException
when DeployAlreadyRunningException then retry_in 1.minute
when DeployTimedOutException then expire
else super
end
end
# rubocop:disable Metrics/MethodLength # rubocop:disable Metrics/MethodLength
def perform(site, notify: true, time: Time.now, output: false) def perform(site, notify: true, time: Time.now, output: false)
@output = output @site = site
ActiveRecord::Base.connection_pool.with_connection do ActiveRecord::Base.connection_pool.with_connection do
@site = Site.find(site)
# Si ya hay una tarea corriendo, aplazar esta. Si estuvo # Si ya hay una tarea corriendo, aplazar esta. Si estuvo
# esperando más de 10 minutos, recuperar el estado anterior. # esperando más de 10 minutos, recuperar el estado anterior.
# #
# Como el trabajo actual se aplaza al siguiente, arrastrar la # Como el trabajo actual se aplaza al siguiente, arrastrar la
# hora original para poder ir haciendo timeouts. # hora original para poder ir haciendo timeouts.
if @site.building? if site.building?
notify = false notify = false
if 10.minutes.ago >= time raise DeployAlreadyRunningException unless 10.minutes.ago >= time
raise DeployTimedOutException, raise DeployTimedOutException,
"#{@site.name} la tarea estuvo más de 10 minutos esperando, volviendo al estado original" "#{site.name} la tarea estuvo más de 10 minutos esperando, volviendo al estado original"
else
raise DeployAlreadyRunningException
end
end end
@deployed = {} @deployed = {}
@site.update status: 'building' site.update status: 'building'
@site.deployment_list.each do |d| site.deployment_list.each do |d|
begin begin
raise DeployException, 'Una dependencia falló' if failed_dependencies? d raise DeployException, 'Una dependencia falló' if failed_dependencies? d
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.urls.map do |url| urls = d.urls.map do |url|
@ -57,9 +49,7 @@ class DeployJob < ApplicationJob
nil nil
end.compact end.compact
if d == @site.deployment_list.last && !status raise DeployException, 'Falló la compilación' if d == site.deployment_list.last && !status
raise DeployException, 'Falló la compilación'
end
rescue StandardError => e rescue StandardError => e
status = false status = false
seconds ||= 0 seconds ||= 0
@ -78,7 +68,7 @@ class DeployJob < ApplicationJob
} }
end end
return unless @output return unless output
puts(Terminal::Table.new do |t| puts(Terminal::Table.new do |t|
t << (%w[type] + @deployed.values.first.keys) t << (%w[type] + @deployed.values.first.keys)
@ -90,12 +80,12 @@ class DeployJob < ApplicationJob
rescue DeployTimedOutException => e rescue DeployTimedOutException => e
notify_exception e notify_exception e
ensure ensure
if @site.present? if site.present?
@site.update status: 'waiting' site.update status: 'waiting'
notify_usuaries if notify notify_usuaries if notify
puts "\a" if @output puts "\a" if output
end end
end end
end end
@ -125,7 +115,7 @@ class DeployJob < ApplicationJob
# @param :deploy [Deploy] # @param :deploy [Deploy]
def notify_exception(exception, deploy = nil) def notify_exception(exception, deploy = nil)
data = { data = {
site: @site.id, site: site.name,
deploy: deploy&.type, deploy: deploy&.type,
log: deploy&.build_stats&.last&.log, log: deploy&.build_stats&.last&.log,
failed_dependencies: (failed_dependencies(deploy) if deploy) failed_dependencies: (failed_dependencies(deploy) if deploy)
@ -135,8 +125,10 @@ class DeployJob < ApplicationJob
end end
def notify_usuaries def notify_usuaries
@site.roles.where(rol: 'usuarie', temporal: false).pluck(:usuarie_id).each do |usuarie| usuarie_ids = site.roles.where(rol: 'usuarie', temporal: false).pluck(:usuarie_id)
DeployMailer.with(usuarie: usuarie, site: @site.id)
Usuarie.where(id: usuarie_ids).find_each do |usuarie|
DeployMailer.with(usuarie: usuarie, site: site)
.deployed(@deployed) .deployed(@deployed)
.deliver_now .deliver_now
end end

View file

@ -7,6 +7,8 @@ class GitPullJob < ApplicationJob
# @param :message [String] # @param :message [String]
# @return [nil] # @return [nil]
def perform(site, usuarie, message) def perform(site, usuarie, message)
@site = site
return unless site.repository.origin return unless site.repository.origin
site.repository.fetch site.repository.fetch

View file

@ -6,6 +6,8 @@ class GitPushJob < ApplicationJob
# @param :site [Site] # @param :site [Site]
# @return [nil] # @return [nil]
def perform(site) def perform(site)
@site = site
site.repository.push if site.repository.origin site.repository.push if site.repository.origin
end end
end end

View file

@ -15,8 +15,7 @@
# Lo mismo para salir de mantenimiento, agregando el atributo # Lo mismo para salir de mantenimiento, agregando el atributo
# are_we_back: true al crear el Maintenance. # are_we_back: true al crear el Maintenance.
class MaintenanceJob < ApplicationJob class MaintenanceJob < ApplicationJob
def perform(maintenance_id:) def perform(maintenance:)
maintenance = Maintenance.find(maintenance_id)
# Decidir cuál vamos a enviar según el estado de Maintenance # Decidir cuál vamos a enviar según el estado de Maintenance
mailer = maintenance.are_we_back ? :were_back : :notice mailer = maintenance.are_we_back ? :were_back : :notice

View file

@ -6,9 +6,6 @@ class PeriodicJob < ApplicationJob
STARTING_INTERVAL = Stat::INTERVALS.first STARTING_INTERVAL = Stat::INTERVALS.first
# Tener el sitio a mano
attr_reader :site
# Descartar y notificar si pasó algo más. # Descartar y notificar si pasó algo más.
# #
# XXX: En realidad deberíamos seguir reintentando? # XXX: En realidad deberíamos seguir reintentando?

View file

@ -7,8 +7,8 @@ class StatCollectionJob < PeriodicJob
STAT_NAME = 'stat_collection_job' STAT_NAME = 'stat_collection_job'
def perform(site_id:, once: true) def perform(site:, once: true)
@site = Site.find site_id @site = site
beginning = beginning_of_interval beginning = beginning_of_interval
stat = site.stats.create! name: STAT_NAME stat = site.stats.create! name: STAT_NAME
@ -22,7 +22,7 @@ class StatCollectionJob < PeriodicJob
rollup.average(:seconds) rollup.average(:seconds)
end end
dimensions = { site_id: site_id } dimensions = { site_id: site.id }
reduce_rollup(name: 'builds', operation: :sum, dimensions: dimensions) reduce_rollup(name: 'builds', operation: :sum, dimensions: dimensions)
reduce_rollup(name: 'space_used', operation: :average, dimensions: dimensions) reduce_rollup(name: 'space_used', operation: :average, dimensions: dimensions)

View file

@ -16,8 +16,8 @@ class UriCollectionJob < PeriodicJob
IMAGES = %w[.png .jpg .jpeg .gif .webp .jfif].freeze IMAGES = %w[.png .jpg .jpeg .gif .webp .jfif].freeze
STAT_NAME = 'uri_collection_job' STAT_NAME = 'uri_collection_job'
def perform(site_id:, once: true) def perform(site:, once: true)
@site = Site.find site_id @site = site
# Obtener el principio del intervalo anterior # Obtener el principio del intervalo anterior
beginning_of_interval beginning_of_interval

View file

@ -14,6 +14,8 @@ module Jekyll
extend ActiveSupport::Concern extend ActiveSupport::Concern
included do included do
DATA_EXTENSIONS = %w[.yaml .yml .json .csv .tsv].freeze
def read_data_to(dir, data) def read_data_to(dir, data)
return unless File.directory?(dir) && !@entry_filter.symlink?(dir) return unless File.directory?(dir) && !@entry_filter.symlink?(dir)
@ -24,7 +26,7 @@ module Jekyll
if File.directory?(path) if File.directory?(path)
read_data_to(path, data[sanitize_filename(entry)] = {}) read_data_to(path, data[sanitize_filename(entry)] = {})
else elsif DATA_EXTENSIONS.include?(File.extname(entry))
key = sanitize_filename(File.basename(entry, ".*")) key = sanitize_filename(File.basename(entry, ".*"))
data[key] = read_data_file(path) data[key] = read_data_file(path)
end end

View file

@ -10,7 +10,7 @@ class ApplicationMailer < ActionMailer::Base
private private
def site def site
@site ||= Site.find @params[:site_id] @site ||= @params[:site]
end end
def inline_logo! def inline_logo!

View file

@ -13,8 +13,7 @@ class DeployMailer < ApplicationMailer
# rubocop:disable Metrics/AbcSize # rubocop:disable Metrics/AbcSize
def deployed(deploys = {}) def deployed(deploys = {})
usuarie = Usuarie.find(params[:usuarie]) usuarie = params[:usuarie]
site = usuarie.sites.find(params[:site])
hostname = site.hostname hostname = site.hostname
deploys ||= {} deploys ||= {}

View file

@ -1,9 +0,0 @@
# frozen_string_literal: true
class InvitadxMailer < ApplicationMailer
def confirmation_required
@invitadx = params[:invitadx]
@site = params[:site]
mail from: "#{@site.config.dig('title')} <#{ENV.fetch('DEFAULT_FROM', 'sutty@kefir.red')}>", to: @invitadx.email, subject: t('.subject')
end
end

View file

@ -64,7 +64,7 @@ class ActivityPub < ApplicationRecord
# Array<String> o mezcla y obtener el que más nos convenga o # Array<String> o mezcla y obtener el que más nos convenga o
# adivinar uno. # adivinar uno.
when Array when Array
links = object['url'].map.with_index do |link, i| links = object['url'].map.with_index do |link, _i|
case link case link
when Hash then link when Hash then link
else { 'href' => link.to_s } else { 'href' => link.to_s }
@ -93,7 +93,8 @@ class ActivityPub < ApplicationRecord
# Gestionar todos los errores # Gestionar todos los errores
error_on_all_events do |e| error_on_all_events do |e|
ExceptionNotifier.notify_exception(e, data: { site: site.name, activity_pub: self.id, activity: activities.first.uri }) ExceptionNotifier.notify_exception(e,
data: { site: site.name, activity_pub: id, activity: activities.first.uri })
end end
# Se puede volver a pausa en caso de actualización remota, para # Se puede volver a pausa en caso de actualización remota, para

View file

@ -37,7 +37,7 @@ class ActivityPub
HOSTNAME_HEADERS = { HOSTNAME_HEADERS = {
'mastodon' => '#domain', 'mastodon' => '#domain',
'fediblock' => 'domain' 'fediblock' => 'domain'
} }.freeze
def client def client
@client ||= Client.new @client ||= Client.new

View file

@ -39,13 +39,14 @@ class ActivityPub
def referenced(site) def referenced(site)
require 'distributed_press/v1/social/referenced_object' require 'distributed_press/v1/social/referenced_object'
@referenced ||= DistributedPress::V1::Social::ReferencedObject.new(object: content, dereferencer: site.social_inbox.dereferencer) @referenced ||= DistributedPress::V1::Social::ReferencedObject.new(object: content,
dereferencer: site.social_inbox.dereferencer)
end end
private private
def uri_is_content_id? def uri_is_content_id?
return if self.uri == content['id'] return if uri == content['id']
errors.add(:activity_pub_objects, 'El ID del objeto no coincide con su URI') errors.add(:activity_pub_objects, 'El ID del objeto no coincide con su URI')
end end

View file

@ -40,7 +40,8 @@ class ActivityPub
def content def content
{ {
'@context' => 'https://www.w3.org/ns/activitystreams', '@context' => 'https://www.w3.org/ns/activitystreams',
'id' => Rails.application.routes.url_helpers.v1_activity_pub_remote_flag_url(self, host: site.social_inbox_hostname), 'id' => Rails.application.routes.url_helpers.v1_activity_pub_remote_flag_url(self,
host: site.social_inbox_hostname),
'type' => 'Flag', 'type' => 'Flag',
'actor' => main_site.social_inbox.actor_id, 'actor' => main_site.social_inbox.actor_id,
'content' => message.to_s, 'content' => message.to_s,
@ -53,7 +54,7 @@ class ActivityPub
# #
# @return [Site] # @return [Site]
def main_site def main_site
@main_site ||= Site.find(ENV.fetch('PANEL_ACTOR_SITE_ID') { 1 }) @main_site ||= Site.find(ENV.fetch('PANEL_ACTOR_SITE_ID', 1))
end end
end end
end end

View file

@ -2,4 +2,28 @@
class ApplicationRecord < ActiveRecord::Base class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true self.abstract_class = true
# Obtener una lista filtrada de atributos al momento de serializar
#
# @return [String]
def to_yaml(options = {})
pruned_attributes.to_yaml(options)
end
# Devuelve todos los atributos menos los filtrados
#
# @return [Hash]
def pruned_attributes
self.class.inspection_filter.filter(serializable_hash)
end
# @param coder [Psych::Coder]
# @return nil
def encode_with(coder)
pruned_attributes.each_pair do |attr, value|
coder[attr] = value
end
nil
end
end end

View file

@ -17,7 +17,7 @@ module Tienda
return t if new_record? return t if new_record?
t.blank? ? 'https://' + name + '.' + ENV.fetch('TIENDA', 'tienda.sutty.nl') : t t.blank? ? "https://#{name}.#{ENV.fetch('TIENDA', 'tienda.sutty.nl')}" : t
end end
end end
end end

View file

@ -45,7 +45,8 @@ class FediblockState < ApplicationRecord
private private
def block_instances! def block_instances!
ActivityPub::InstanceModerationJob.perform_later(site: site, hostnames: fediblock.hostnames, perform_remotely: false) ActivityPub::InstanceModerationJob.perform_later(site: site, hostnames: fediblock.hostnames,
perform_remotely: false)
end end
# Pausar todas las moderaciones de las instancias que no estén # Pausar todas las moderaciones de las instancias que no estén

View file

@ -16,7 +16,9 @@ class InstanceModeration < ApplicationRecord
state :blocked state :blocked
error_on_all_events do |e| error_on_all_events do |e|
ExceptionNotifier.notify_exception(e, data: { site: site.name, instance: instance.hostname, instance_moderation: id }) ExceptionNotifier.notify_exception(e,
data: { site: site.name, instance: instance.hostname,
instance_moderation: id })
end end
after_all_events do after_all_events do

View file

@ -11,7 +11,7 @@ class LogEntry < ApplicationRecord
def resend def resend
return if sent return if sent
ContactJob.perform_later site_id, params[:form], params ContactJob.perform_later site, params[:form], params
end end
def params def params

View file

@ -142,7 +142,7 @@ MetadataTemplate = Struct.new(:site, :document, :name, :label, :type,
# En caso de que algún campo necesite realizar acciones antes de ser # En caso de que algún campo necesite realizar acciones antes de ser
# guardado # guardado
def save def save
if !changed? unless changed?
self[:value] = document_value if private? self[:value] = document_value if private?
return true return true

View file

@ -11,8 +11,11 @@ class Site < ApplicationRecord
include Site::BuildStats include Site::BuildStats
include Site::LayoutOrdering include Site::LayoutOrdering
include Site::SocialDistributedPress include Site::SocialDistributedPress
include Site::DefaultOptions
include Tienda include Tienda
self.filter_attributes += [/_key/, /_ciphertext\z/]
# Cifrar la llave privada que cifra y decifra campos ocultos. Sutty # Cifrar la llave privada que cifra y decifra campos ocultos. Sutty
# tiene acceso pero los datos se guardan cifrados en el sitio. Esto # tiene acceso pero los datos se guardan cifrados en el sitio. Esto
# protege información privada en repositorios públicos, pero no la # protege información privada en repositorios públicos, pero no la
@ -235,11 +238,11 @@ class Site < ApplicationRecord
# layouts. Si pasamos un layout que no existe, obtenemos un # layouts. Si pasamos un layout que no existe, obtenemos un
# NoMethodError # NoMethodError
@layouts_struct ||= Struct.new(*layout_keys, keyword_init: true) @layouts_struct ||= Struct.new(*layout_keys, keyword_init: true)
@layouts ||= @layouts_struct.new(**data['layouts'].map do |name, metadata| @layouts ||= @layouts_struct.new(**data['layouts'].to_h do |name, metadata|
[name.to_sym, [name.to_sym,
Layout.new(site: self, name: name.to_sym, meta: metadata.delete('meta')&.with_indifferent_access, Layout.new(site: self, name: name.to_sym, meta: metadata.delete('meta')&.with_indifferent_access,
metadata: metadata.with_indifferent_access)] metadata: metadata.with_indifferent_access)]
end.to_h) end)
end end
# TODO: Si la estructura de datos no existe, vamos a producir una # TODO: Si la estructura de datos no existe, vamos a producir una
@ -314,7 +317,7 @@ class Site < ApplicationRecord
end end
def reload def reload
super.tap do |s| super.tap do |_s|
reload_jekyll! reload_jekyll!
end end
self self
@ -403,7 +406,7 @@ class Site < ApplicationRecord
def clone_skel! def clone_skel!
return if jekyll? return if jekyll?
Rugged::Repository.clone_at(ENV['SKEL_SUTTY'], path, checkout_branch: design.gem) Rugged::Repository.clone_at(ENV.fetch('SKEL_SUTTY', nil), path, checkout_branch: design.gem)
# Necesita un bloque # Necesita un bloque
repository.rugged.remotes.rename('origin', 'upstream') {} repository.rugged.remotes.rename('origin', 'upstream') {}
@ -497,12 +500,12 @@ class Site < ApplicationRecord
deploy_local = deploys.find_by_type('DeployLocal') deploy_local = deploys.find_by_type('DeployLocal')
deploy_local.git_lfs deploy_local.git_lfs
if !gems_installed? || gemfile_updated? || gemfile_lock_updated? return unless !gems_installed? || gemfile_updated? || gemfile_lock_updated?
deploy_local.bundle deploy_local.bundle
touch touch
FileUtils.touch(gemfile_path) FileUtils.touch(gemfile_path)
end end
end
def gem_path def gem_path
@gem_path ||= @gem_path ||=

View file

@ -0,0 +1,44 @@
# frozen_string_literal: true
require 'dry-schema'
class Site
# Las opciones por defecto se aplican durante la creación del sitio y
# luego se permite a les usuaries modificarlas según quieran. Por el
# momento las opciones nuevas que aparezcan no modifican un sitio que
# ya existe.
module DefaultOptions
extend ActiveSupport::Concern
Schema = Dry::Schema.Params do
optional(:colaboracion_anonima).value(:bool)
optional(:contact).value(:bool)
optional(:acepta_invitades).value(:bool)
optional(:slugify_mode).value(included_in?: Jekyll::Utils::SLUGIFY_MODES)
optional(:pagination).value(:bool)
end
included do
validate :validate_options_from_theme!, if: :persisted?
# @return [Dry::Schema::Result]
def options_from_theme
@options_from_theme ||= Schema.call(data['sutty'])
end
def update_options_from_theme
return true if options_from_theme.to_h.blank?
update(**options_from_theme.to_h)
end
private
def validate_options_from_theme!
options_from_theme.errors.each do |error|
errors.add(:default_options, "#{error.path.map(&:to_s).join('/')} #{error} (#{error.input})")
end
end
end
end
end

View file

@ -45,7 +45,7 @@ class SocialInbox
# @param url [String] # @param url [String]
# @return [DistributedPress::V1::Social::Client] # @return [DistributedPress::V1::Social::Client]
def client_for(url) def client_for(url)
raise "Falló generar un cliente" if url.blank? raise 'Falló generar un cliente' if url.blank?
@client_for ||= {} @client_for ||= {}
@client_for[url] ||= @client_for[url] ||=
@ -54,7 +54,7 @@ class SocialInbox
public_key_url: public_key_url, public_key_url: public_key_url,
private_key_pem: site.private_key_pem, private_key_pem: site.private_key_pem,
logger: Rails.logger, logger: Rails.logger,
cache_store: HTTParty::Cache::Store::Redis.new(redis_url: ENV['REDIS_SERVER']) cache_store: HTTParty::Cache::Store::Redis.new(redis_url: ENV.fetch('REDIS_SERVER', nil))
) )
end end

View file

@ -21,6 +21,8 @@ class Usuarie < ApplicationRecord
has_many :blazer_audits, foreign_key: 'user_id', class_name: 'Blazer::Audit' has_many :blazer_audits, foreign_key: 'user_id', class_name: 'Blazer::Audit'
has_many :blazer_queries, foreign_key: 'creator_id', class_name: 'Blazer::Query' has_many :blazer_queries, foreign_key: 'creator_id', class_name: 'Blazer::Query'
self.filter_attributes += [/\Aemail\z/, /\Aencrypted_password\z/]
def name def name
email.split('@', 2).first email.split('@', 2).first
end end
@ -74,18 +76,18 @@ class Usuarie < ApplicationRecord
# Si le usuarie (re)confirma su cuenta con una invitación pendiente, # Si le usuarie (re)confirma su cuenta con una invitación pendiente,
# considerarla aceptada también. # considerarla aceptada también.
def accept_invitation_after_confirmation! def accept_invitation_after_confirmation!
if confirmed? return unless confirmed?
self.invitation_token = nil self.invitation_token = nil
self.invitation_accepted_at ||= Time.now.utc self.invitation_accepted_at ||= Time.now.utc
end end
end
# Muestra un error si el idioma no está disponible al cambiar el # Muestra un error si el idioma no está disponible al cambiar el
# idioma de la cuenta. # idioma de la cuenta.
# #
# @return [nil] # @return [nil]
def locale_available! def locale_available!
return if I18n.locale_available? self.lang return if I18n.locale_available? lang
errors.add(:lang, I18n.t('activerecord.errors.models.usuarie.attributes.lang.not_available')) errors.add(:lang, I18n.t('activerecord.errors.models.usuarie.attributes.lang.not_available'))
nil nil

View file

@ -5,7 +5,7 @@
SiteService = Struct.new(:site, :usuarie, :params, keyword_init: true) do SiteService = Struct.new(:site, :usuarie, :params, keyword_init: true) do
def deploy def deploy
site.enqueue! site.enqueue!
DeployJob.perform_later site.id DeployJob.perform_later site
end end
# Crea un sitio, agrega un rol nuevo y guarda los cambios a la # Crea un sitio, agrega un rol nuevo y guarda los cambios a la
@ -31,6 +31,7 @@ SiteService = Struct.new(:site, :usuarie, :params, keyword_init: true) do
add_role_to_deploys! role add_role_to_deploys! role
site.save && site.save &&
site.update_options_from_theme &&
site.config.write && site.config.write &&
commit_config(action: :create) && commit_config(action: :create) &&
site.reset.nil? && site.reset.nil? &&
@ -236,8 +237,6 @@ SiteService = Struct.new(:site, :usuarie, :params, keyword_init: true) do
end end
end end
private
# Asignar un rol a cada deploy si no lo tenía ya # Asignar un rol a cada deploy si no lo tenía ya
def add_role_to_deploys!(role = current_role) def add_role_to_deploys!(role = current_role)
site.deploys.each do |deploy| site.deploys.each do |deploy|
@ -249,7 +248,7 @@ SiteService = Struct.new(:site, :usuarie, :params, keyword_init: true) do
@current_role ||= usuarie.rol_for_site(site) @current_role ||= usuarie.rol_for_site(site)
end end
def with_all_locales(&block) def with_all_locales
site.locales.map do |locale| site.locales.map do |locale|
next unless I18n.available_locales.include? locale next unless I18n.available_locales.include? locale

View file

@ -2,7 +2,7 @@
.d-flex.flex-row.w-100 .d-flex.flex-row.w-100
- local = { report: { class: 'ml-auto', data: { confirm: t('.confirm_report') } } } - local = { report: { class: 'ml-auto', data: { confirm: t('.confirm_report') } } }
- ActorModeration.events.each do |actor_event| - ActorModeration.events.each do |actor_event|
- possible = !actor_moderation.public_send(:"may_#{actor_event}?") - possible = actor_moderation.public_send(:"may_#{actor_event}?")
%div{ class: local.dig(actor_event, :class) } %div{ class: local.dig(actor_event, :class) }
= render 'components/btn_base', = render 'components/btn_base',
text: t(".text_#{actor_event}"), text: t(".text_#{actor_event}"),

View file

@ -2,7 +2,7 @@
= cache @site do = cache @site do
:plain :plain
window.env = { window.env = {
AIRBRAKE_SITE_ID: #{@site.id}, AIRBRAKE_PROJECT_ID: #{@site.id},
AIRBRAKE_API_KEY: "#{@site.airbrake_api_key}", AIRBRAKE_PROJECT_KEY: "#{@site.airbrake_api_key}",
PANEL_URL: "#{ENV['PANEL_URL']}" PANEL_URL: "#{ENV['PANEL_URL']}"
} }

View file

@ -13,8 +13,8 @@
- else - else
= link_to crumb.name, crumb.url, class: 'line-clamp-1' = link_to crumb.name, crumb.url, class: 'line-clamp-1'
%ul.navbar-nav.order-1.order-md-2
- if @current_usuarie || current_usuarie - if @current_usuarie || current_usuarie
%ul.navbar-nav.order-1.order-md-2
- if @site&.tienda? - if @site&.tienda?
%li.nav-item %li.nav-item
= link_to t('.tienda'), @site.tienda_url, = link_to t('.tienda'), @site.tienda_url,

View file

@ -15,7 +15,7 @@
= csrf_meta_tags = csrf_meta_tags
= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' = stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload'
= stylesheet_link_tag 'dark', rel: 'alternate stylesheet', media: 'all', 'data-turbolinks-track': 'reload', title: t('dark') = stylesheet_link_tag 'dark', rel: 'alternate stylesheet', media: 'all', 'data-turbolinks-track': 'reload', title: t('dark')
= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' = javascript_pack_tag 'application', 'data-turbolinks-track': 'reload', defer: true
= stylesheet_pack_tag 'application', 'data-turbolinks-track': 'reload' = stylesheet_pack_tag 'application', 'data-turbolinks-track': 'reload'
= favicon_link_tag 'sutty_cuadrada.png', rel: 'apple-touch-icon', type: 'image/png' = favicon_link_tag 'sutty_cuadrada.png', rel: 'apple-touch-icon', type: 'image/png'
= render 'layouts/link_rel_alternate' = render 'layouts/link_rel_alternate'

View file

@ -43,7 +43,6 @@
= render 'schemas/row', site: @site, schema: schema, filter: @filter_params = render 'schemas/row', site: @site, schema: schema, filter: @filter_params
- if policy(@site_stat).index? - if policy(@site_stat).index?
= link_to t('stats.index.title'), site_stats_path(@site), class: 'btn btn-secondary' = link_to t('stats.index.title'), site_stats_path(@site), class: 'btn btn-secondary'
@ -141,10 +140,11 @@
%br/ %br/
= post.order = post.order
%td.text-nowrap %td.text-nowrap
.d-flex.flex-row.align-items-start
- if @usuarie || policy(post).edit? - if @usuarie || policy(post).edit?
= link_to t('posts.edit'), edit_site_post_path(@site, post.path), class: 'btn btn-secondary btn-block' = link_to t('posts.edit_post'), edit_site_post_path(@site, post.path), class: 'btn btn-secondary'
- if @usuarie || policy(post).destroy? - if @usuarie || policy(post).destroy?
= link_to t('posts.destroy'), site_post_path(@site, post.path), class: 'btn btn-secondary btn-block', method: :delete, data: { confirm: t('posts.confirm_destroy') } = link_to t('posts.destroy'), site_post_path(@site, post.path), class: 'btn btn-secondary', method: :delete, data: { confirm: t('posts.confirm_destroy') }
-# -#
Rescatar cualquier error en un post, notificarlo e Rescatar cualquier error en un post, notificarlo e
ignorar su renderización. ignorar su renderización.

View file

@ -62,7 +62,7 @@ Rails.application.configure do
config.log_tags = %i[request_id] config.log_tags = %i[request_id]
# Use a different cache store in production. # Use a different cache store in production.
config.cache_store = :redis_cache_store, { url: ENV['REDIS_SERVER'] } config.cache_store = :redis_cache_store, { url: ENV.fetch('REDIS_SERVER', nil) }
config.action_mailer.perform_caching = false config.action_mailer.perform_caching = false
@ -87,7 +87,7 @@ Rails.application.configure do
config.lograge.enabled = true config.lograge.enabled = true
# Use default logging formatter so that PID and timestamp are not # Use default logging formatter so that PID and timestamp are not
# suppressed. # suppressed.
config.log_formatter = ::Logger::Formatter.new config.log_formatter = Logger::Formatter.new
# Use a different logger for distributed setups. # Use a different logger for distributed setups.
require 'syslog/logger' require 'syslog/logger'
@ -140,9 +140,10 @@ Rails.application.configure do
domain: ENV.fetch('SUTTY', 'sutty.nl'), domain: ENV.fetch('SUTTY', 'sutty.nl'),
enable_starttls_auto: false enable_starttls_auto: false
} }
config.action_mailer.default_options = { from: ENV.fetch('DEFAULT_FROM', "noreply@sutty.nl") } config.action_mailer.default_options = { from: ENV.fetch('DEFAULT_FROM', 'noreply@sutty.nl') }
config.middleware.use ExceptionNotification::Rack, gitlab: {}, error_grouping: true, ignore_exceptions: ['DeployJob::DeployAlreadyRunningException'] config.middleware.use ExceptionNotification::Rack, gitlab: {}, error_grouping: true,
ignore_exceptions: ['DeployJob::DeployAlreadyRunningException']
Rails.application.routes.default_url_options[:host] = "panel.#{ENV.fetch('SUTTY', 'sutty.nl')}" Rails.application.routes.default_url_options[:host] = "panel.#{ENV.fetch('SUTTY', 'sutty.nl')}"
Rails.application.routes.default_url_options[:protocol] = 'https' Rails.application.routes.default_url_options[:protocol] = 'https'

View file

@ -5,4 +5,5 @@
# Configure sensitive parameters which will be filtered from the log file. # Configure sensitive parameters which will be filtered from the log file.
Rails.application.config.filter_parameters += %i[ Rails.application.config.filter_parameters += %i[
password passw secret token _key crypt salt certificate otp ssn key password passw secret token _key crypt salt certificate otp ssn key
_pem _ciphertext email
] ]

View file

@ -1,5 +1,5 @@
# frozen_string_literal: true # frozen_string_literal: true
Que::Web.use(Rack::Auth::Basic) do |user, password| Que::Web.use(Rack::Auth::Basic) do |user, password|
[user, password] == [ENV['HTTP_BASIC_USER'], ENV['HTTP_BASIC_PASSWORD']] [user, password] == [ENV.fetch('HTTP_BASIC_USER', nil), ENV.fetch('HTTP_BASIC_PASSWORD', nil)]
end end

View file

@ -5,7 +5,7 @@ class AddDefaultToDistributedPressPublisher < ActiveRecord::Migration[6.1]
def up def up
add_column :distributed_press_publishers, :default, :boolean, default: false add_column :distributed_press_publishers, :default, :boolean, default: false
DistributedPressPublisher.last.update(default: true) DistributedPressPublisher.last&.update(default: true)
end end
def down def down

View file

@ -16,9 +16,7 @@ class CreateFediblockStates < ActiveRecord::Migration[6.1]
# Todas las listas están activas por defecto # Todas las listas están activas por defecto
DeploySocialDistributedPress.find_each do |deploy| DeploySocialDistributedPress.find_each do |deploy|
ActivityPub::Fediblock.find_each do |fediblock| ActivityPub::Fediblock.find_each do |fediblock|
FediblockState.create(site: deploy.site, fediblock: fediblock, aasm_state: 'disabled').tap do |f| FediblockState.create(site: deploy.site, fediblock: fediblock, aasm_state: 'disabled').tap(&:enable!)
f.enable!
end
end end
end end
end end

View file

@ -4,9 +4,11 @@
# decompresión # decompresión
class BrsDecompressorCorruptedSourceError < ActiveRecord::Migration[6.1] class BrsDecompressorCorruptedSourceError < ActiveRecord::Migration[6.1]
def up def up
raise unless HTTParty.get("https://mas.to/api/v2/instance", headers: { "Accept-Encoding": "br;q=1.0,gzip;q=1.0,deflate;q=0.6,identity;q=0.3" }).ok? raise unless HTTParty.get('https://mas.to/api/v2/instance',
headers: { 'Accept-Encoding': 'br;q=1.0,gzip;q=1.0,deflate;q=0.6,identity;q=0.3' }).ok?
QueJob.where("last_error_message like '%BRS::DecompressorCorruptedSourceError%'").update_all(error_count: 0, run_at: Time.now) QueJob.where("last_error_message like '%BRS::DecompressorCorruptedSourceError%'").update_all(error_count: 0,
run_at: Time.now)
end end
def down; end def down; end

View file

@ -4,7 +4,9 @@
class FixActivityType < ActiveRecord::Migration[6.1] class FixActivityType < ActiveRecord::Migration[6.1]
def up def up
%w[Like Announce].each do |type| %w[Like Announce].each do |type|
ActivityPub::Activity.where(Arel.sql("content->>'type' = '#{type}'")).update_all(type: "ActivityPub::Activity::#{type}", updated_at: Time.now) ActivityPub::Activity.where(Arel.sql("content->>'type' = '#{type}'")).update_all(
type: "ActivityPub::Activity::#{type}", updated_at: Time.now
)
end end
end end

View file

@ -3,7 +3,7 @@
# De alguna forma se guardaron objetos duplicados! # De alguna forma se guardaron objetos duplicados!
class FixDuplicateObjects < ActiveRecord::Migration[6.1] class FixDuplicateObjects < ActiveRecord::Migration[6.1]
def up def up
ActivityPub::Object.group(:uri).count.select { |_, v| v > 1 }.keys.each do |uri| ActivityPub::Object.group(:uri).count.select { |_, v| v > 1 }.each_key do |uri|
objects = ActivityPub::Object.where(uri: uri) objects = ActivityPub::Object.where(uri: uri)
deleted_ids = objects[1..].map(&:delete).map(&:id) deleted_ids = objects[1..].map(&:delete).map(&:id)

View file

@ -14,9 +14,7 @@ class AddFedipactToFediblocks < ActiveRecord::Migration[6.1]
) )
DeploySocialDistributedPress.find_each do |deploy| DeploySocialDistributedPress.find_each do |deploy|
FediblockState.create(site: deploy.site, fediblock: fedipact, aasm_state: 'disabled').tap do |f| FediblockState.create(site: deploy.site, fediblock: fedipact, aasm_state: 'disabled').tap(&:enable!)
f.enable!
end
end end
end end

View file

@ -4,14 +4,14 @@
# no es válida y por eso teníamos objetos duplicados. # no es válida y por eso teníamos objetos duplicados.
class AddMissingUniqueIndexes < ActiveRecord::Migration[6.1] class AddMissingUniqueIndexes < ActiveRecord::Migration[6.1]
def up def up
ActivityPub::Object.group(:uri).count.select { |_, v| v > 1 }.keys.each do |uri| ActivityPub::Object.group(:uri).count.select { |_, v| v > 1 }.each_key do |uri|
objects = ActivityPub::Object.where(uri: uri) objects = ActivityPub::Object.where(uri: uri)
deleted_ids = objects[1..].map(&:delete).map(&:id) deleted_ids = objects[1..].map(&:delete).map(&:id)
ActivityPub.where(object_id: deleted_ids).update_all(object_id: objects.first.id, updated_at: Time.now) ActivityPub.where(object_id: deleted_ids).update_all(object_id: objects.first.id, updated_at: Time.now)
end end
ActivityPub::Actor.group(:uri).count.select { |_, v| v > 1 }.keys.each do |uri| ActivityPub::Actor.group(:uri).count.select { |_, v| v > 1 }.each_key do |uri|
objects = ActivityPub::Actor.where(uri: uri) objects = ActivityPub::Actor.where(uri: uri)
deleted_ids = objects[1..].map(&:delete).map(&:id) deleted_ids = objects[1..].map(&:delete).map(&:id)
@ -21,7 +21,7 @@ class AddMissingUniqueIndexes < ActiveRecord::Migration[6.1]
ActivityPub::RemoteFlag.where(actor_id: deleted_ids).update_all(actor_id: objects.first.id, updated_at: Time.now) ActivityPub::RemoteFlag.where(actor_id: deleted_ids).update_all(actor_id: objects.first.id, updated_at: Time.now)
end end
ActivityPub::Instance.group(:hostname).count.select { |_, v| v > 1 }.keys.each do |hostname| ActivityPub::Instance.group(:hostname).count.select { |_, v| v > 1 }.each_key do |hostname|
objects = ActivityPub::Instance.where(hostname: hostname) objects = ActivityPub::Instance.where(hostname: hostname)
deleted_ids = objects[1..].map(&:delete).map(&:id) deleted_ids = objects[1..].map(&:delete).map(&:id)

View file

@ -91,6 +91,14 @@
description_en: "We're working towards adding more themes for you to use. [Contact us!](https://sutty.nl/en/#contact)" description_en: "We're working towards adding more themes for you to use. [Contact us!](https://sutty.nl/en/#contact)"
description_es: "Estamos trabajando para que puedas tener más diseños. [¡Escribinos!](https://sutty.nl/#contacto)" description_es: "Estamos trabajando para que puedas tener más diseños. [¡Escribinos!](https://sutty.nl/#contacto)"
priority: '3' priority: '3'
- name_en: 'Worker-recovered factory'
name_es: 'Empresa recuperada'
gem: 'empresa-recuperada-jekyll-theme'
url: 'https://empresa-recuperada.sutty.nl/'
disabled: true
description_en: "A template for [empresas recuperadas](https://en.wikipedia.org/wiki/Workers%27_self-management#Empresas_recuperadas_movement). We're working towards adding more themes for you to use. [Contact us!](https://sutty.nl/en/#contact)"
description_es: "Estamos trabajando para que puedas tener más diseños. [¡Escribinos!](https://sutty.nl/#contacto)"
priority: '3'
- name_en: 'More themes' - name_en: 'More themes'
name_es: 'Más plantillas' name_es: 'Más plantillas'
gem: 'sutty-theme-own' gem: 'sutty-theme-own'

View file

@ -3,9 +3,9 @@
namespace :stats do namespace :stats do
desc 'Process stats' desc 'Process stats'
task process_all: :environment do task process_all: :environment do
Site.all.pluck(:id).each do |site_id| Site.all.find_each do |site|
UriCollectionJob.perform_now site_id: site_id, once: true UriCollectionJob.perform_now site: site, once: true
StatCollectionJob.perform_now site_id: site_id, once: true StatCollectionJob.perform_now site: site, once: true
end end
end end
end end

View file

@ -13,7 +13,7 @@
"@rails/activestorage": "^6.1.3-1", "@rails/activestorage": "^6.1.3-1",
"@rails/ujs": "^6.1.3-1", "@rails/ujs": "^6.1.3-1",
"@rails/webpacker": "5.4.4", "@rails/webpacker": "5.4.4",
"@suttyweb/editor": "^0.1.27", "@suttyweb/editor": "^0.1.29",
"babel-loader": "^8.2.2", "babel-loader": "^8.2.2",
"bs-custom-file-input": "^1.3.4", "bs-custom-file-input": "^1.3.4",
"chart.js": "^3.5.1", "chart.js": "^3.5.1",

View file

@ -1975,10 +1975,10 @@
resolved "https://registry.npmjs.org/@stimulus/webpack-helpers/-/webpack-helpers-1.1.1.tgz" resolved "https://registry.npmjs.org/@stimulus/webpack-helpers/-/webpack-helpers-1.1.1.tgz"
integrity sha512-XOkqSw53N9072FLHvpLM25PIwy+ndkSSbnTtjKuyzsv8K5yfkFB2rv68jU1pzqYa9FZLcvZWP4yazC0V38dx9A== integrity sha512-XOkqSw53N9072FLHvpLM25PIwy+ndkSSbnTtjKuyzsv8K5yfkFB2rv68jU1pzqYa9FZLcvZWP4yazC0V38dx9A==
"@suttyweb/editor@^0.1.27": "@suttyweb/editor@^0.1.29":
version "0.1.27" version "0.1.29"
resolved "https://registry.yarnpkg.com/@suttyweb/editor/-/editor-0.1.27.tgz#9415a0b767e72dbe4fbf42ce87e62fb8f5125c31" resolved "https://registry.yarnpkg.com/@suttyweb/editor/-/editor-0.1.29.tgz#8b5c6ae4e4d546002a96ecd65765d77d2a88d415"
integrity sha512-Ts9TZtGiRIaHm+ffVBRl+/nuVcANWZNtFsrGacoajgEsagaIyA1cq8qjiNpPoM5ne9vTba3cAaLP04V/uEIhBw== integrity sha512-GshI8wE5UqXge2RhwAUxUXTRLPoOX7US9xVu1aLqT/deT/hDyN9S3PxVn9cJBf7uPHEqBzYXGDKWWF79PLqGHw==
dependencies: dependencies:
"@floating-ui/dom" "^1.5.1" "@floating-ui/dom" "^1.5.1"
linkifyjs "^4.1.1" linkifyjs "^4.1.1"