5
0
Fork 0
mirror of https://0xacab.org/sutty/sutty synced 2024-11-22 21:26:21 +00:00

Merge branch 'panel.testing.sutty.nl' of 0xacab.org:sutty/sutty into panel.testing.sutty.nl

This commit is contained in:
maki 2024-05-15 17:00:43 -03:00
commit ffff78c0cb
90 changed files with 297 additions and 287 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,26 @@ 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"
- "panel.testing.sutty.nl"
except: except:
- "schedules" - "schedules"
cache: cache:
@ -30,14 +54,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 +69,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

@ -118,10 +118,11 @@ GEM
bundler-audit (0.9.1) bundler-audit (0.9.1)
bundler (>= 1.2.0, < 3) bundler (>= 1.2.0, < 3)
thor (~> 1.0) thor (~> 1.0)
capybara (2.18.0) capybara (3.40.0)
addressable addressable
matrix
mini_mime (>= 0.1.3) mini_mime (>= 0.1.3)
nokogiri (~> 1.8) nokogiri (~> 1.11)
rack (>= 1.6.0) rack (>= 1.6.0)
rack-test (>= 0.6.3) rack-test (>= 0.6.3)
regexp_parser (>= 1.5, < 3.0) regexp_parser (>= 1.5, < 3.0)
@ -361,13 +362,14 @@ GEM
net-pop net-pop
net-smtp net-smtp
marcel (1.0.2) marcel (1.0.2)
matrix (0.4.2)
memory_profiler (1.0.1) memory_profiler (1.0.1)
mercenary (0.4.0) mercenary (0.4.0)
method_source (1.0.0) method_source (1.0.0)
mini_histogram (0.3.1) mini_histogram (0.3.1)
mini_magick (4.12.0) mini_magick (4.12.0)
mini_mime (1.1.5) mini_mime (1.1.5)
mini_portile2 (2.8.5) mini_portile2 (2.8.6)
minitest (5.21.1) minitest (5.21.1)
mobility (1.2.9) mobility (1.2.9)
i18n (>= 0.6.10, < 2) i18n (>= 0.6.10, < 2)
@ -387,7 +389,7 @@ GEM
net-ssh (7.2.1) net-ssh (7.2.1)
netaddr (2.0.6) netaddr (2.0.6)
nio4r (2.7.0-x86_64-linux-musl) nio4r (2.7.0-x86_64-linux-musl)
nokogiri (1.16.0-x86_64-linux-musl) nokogiri (1.16.5-x86_64-linux-musl)
mini_portile2 (~> 2.8.2) mini_portile2 (~> 2.8.2)
racc (~> 1.4) racc (~> 1.4)
orm_adapter (0.5.0) orm_adapter (0.5.0)
@ -408,7 +410,7 @@ GEM
pry (0.14.2) pry (0.14.2)
coderay (~> 1.1) coderay (~> 1.1)
method_source (~> 1.0) method_source (~> 1.0)
public_suffix (5.0.4) public_suffix (5.0.5)
puma (6.4.2-x86_64-linux-musl) puma (6.4.2-x86_64-linux-musl)
nio4r (~> 2.0) nio4r (~> 2.0)
pundit (2.3.1) pundit (2.3.1)
@ -487,7 +489,7 @@ GEM
redis-store (>= 1.2, < 2) redis-store (>= 1.2, < 2)
redis-store (1.9.2) redis-store (1.9.2)
redis (>= 4, < 6) redis (>= 4, < 6)
regexp_parser (2.9.0) regexp_parser (2.9.2)
request_store (1.5.1) request_store (1.5.1)
rack (>= 1.4) rack (>= 1.4)
responders (3.1.1) responders (3.1.1)
@ -646,6 +648,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",

BIN
public/packs/css/application-5688882d.css (Stored with Git LFS)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
public/packs/manifest.json (Stored with Git LFS)

Binary file not shown.

BIN
public/packs/manifest.json.br (Stored with Git LFS)

Binary file not shown.

BIN
public/packs/manifest.json.gz (Stored with Git LFS)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

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"