feat: almacenar y renovar tokens de distributed press

This commit is contained in:
f 2023-01-20 18:22:08 -03:00
parent ecdbfeb568
commit 84b2968cdb
8 changed files with 154 additions and 2 deletions

View File

@ -39,6 +39,7 @@ gem 'commonmarker'
gem 'devise'
gem 'devise-i18n'
gem 'devise_invitable'
gem 'distributed-press-api-client', '~> 0.2.0'
gem 'email_address', git: 'https://github.com/fauno/email_address', branch: 'i18n'
gem 'exception_notification'
gem 'fast_blank'

View File

@ -115,6 +115,7 @@ GEM
xpath (>= 2.0, < 4.0)
chartkick (4.1.2)
childprocess (4.1.0)
climate_control (1.2.0)
coderay (1.1.3)
colorator (1.1.0)
commonmarker (0.21.2-x86_64-linux-musl)
@ -153,12 +154,45 @@ GEM
devise_invitable (2.0.5)
actionmailer (>= 5.0)
devise (>= 4.6)
distributed-press-api-client (0.2.0)
addressable (~> 2.3, >= 2.3.0)
climate_control
dry-schema
httparty (~> 0.18)
json (~> 2.1, >= 2.1.0)
jwt (~> 2.6.0)
dotenv (2.7.6)
dotenv-rails (2.7.6)
dotenv (= 2.7.6)
railties (>= 3.2)
down (5.2.4)
addressable (~> 2.8)
dry-configurable (1.0.1)
dry-core (~> 1.0, < 2)
zeitwerk (~> 2.6)
dry-core (1.0.0)
concurrent-ruby (~> 1.0)
zeitwerk (~> 2.6)
dry-inflector (1.0.0)
dry-initializer (3.1.1)
dry-logic (1.5.0)
concurrent-ruby (~> 1.0)
dry-core (~> 1.0, < 2)
zeitwerk (~> 2.6)
dry-schema (1.13.0)
concurrent-ruby (~> 1.0)
dry-configurable (~> 1.0, >= 1.0.1)
dry-core (~> 1.0, < 2)
dry-initializer (~> 3.0)
dry-logic (>= 1.5, < 2)
dry-types (>= 1.7, < 2)
zeitwerk (~> 2.6)
dry-types (1.7.0)
concurrent-ruby (~> 1.0)
dry-core (~> 1.0, < 2)
dry-inflector (~> 1.0, < 2)
dry-logic (>= 1.4, < 2)
zeitwerk (~> 2.6)
ed25519 (1.2.4-x86_64-linux-musl)
em-websocket (0.5.3)
eventmachine (>= 0.12.9)
@ -216,8 +250,8 @@ GEM
thor
hiredis (0.6.3-x86_64-linux-musl)
http_parser.rb (0.8.0-x86_64-linux-musl)
httparty (0.18.1)
mime-types (~> 3.0)
httparty (0.21.0)
mini_mime (>= 1.0.0)
multi_xml (>= 0.5.2)
i18n (1.8.11)
concurrent-ruby (~> 1.0)
@ -292,6 +326,7 @@ GEM
jekyll-write-and-commit-changes (0.2.1)
jekyll (~> 4)
rugged (~> 1)
jwt (2.6.0)
kaminari (1.2.1)
activesupport (>= 4.1.0)
kaminari-actionview (= 1.2.1)
@ -584,6 +619,7 @@ DEPENDENCIES
devise
devise-i18n
devise_invitable
distributed-press-api-client (~> 0.2.0)
dotenv-rails
down
ed25519

View File

@ -1,2 +1,3 @@
cleanup: bundle exec rake cleanup:everything
stats: bundle exec rake stats:process_all
distributed_press_renew_tokens: bundle exec rake distributed_press:tokens:renew

View File

@ -0,0 +1,17 @@
# frozen_string_literal: true
# Renueva los tokens de Distributed Press antes que se venzan,
# activando los callbacks que hacen que se refresque el token.
class RenewDistributedPressTokensJob < ApplicationJob
# Renueva todos los tokens a punto de vencer o informa el error sin
# detener la tarea si algo pasa.
def perform
DistributedPressPublisher.with_about_to_expire_tokens.find_each do |publisher|
publisher.touch
rescue DistributedPress::V1::Error => e
data = { instance: publisher.instance, expires_at: publisher.client.token.expires_at }
ExceptionNotifier.notify_exception(e, data: data)
end
end
end

View File

@ -0,0 +1,68 @@
# frozen_string_literal: true
require 'distributed_press/v1'
# Almacena el token de autenticación y la URL, por ahora solo vamos
# a tener uno, pero queda abierta la posibilidad de agregar más.
class DistributedPressPublisher < ApplicationRecord
# Cifrar la información del token en la base de datos
has_encrypted :token
# La instancia es única
validates_uniqueness_of :instance
# El token es necesario
validates_presence_of :token
# Mantener la fecha de vencimiento actualizada
before_save :update_expires_at_from_token!, :update_token_from_client!
# Devuelve todos los tokens que vencen en una hora
scope :with_about_to_expire_tokens, lambda {
where('expires_at > ? and expires_at < ?', Time.now, Time.now + 1.hour)
}
# Al cambiar el token genera un cliente nuevo
#
# @return [String]
def token=(new_token)
@client = nil
super
end
# Al cambiar la instancia genera un cliente nuevo
#
# @return [String]
def instance=(new_instance)
@client = nil
super
end
# Instancia un cliente de Distributed Press a partir del token. Al
# cargar un token a punto de vencer se renueva automáticamente.
#
# @return [DistributedPress::V1::Client]
def client
@client ||= DistributedPress::V1::Client.new(url: instance, token: token)
end
private
# Actualiza o desactiva la fecha de vencimiento a partir de la
# información del token.
#
# @return [nil]
def update_expires_at_from_token!
self.expires_at = client.token.forever? ? nil : client.token.expires_at
nil
end
# Actualiza el token a partir del cliente, que ya actualiza el token
# automáticamente.
#
# @return [nil]
def update_token_from_client!
self.token = client.token.to_s
nil
end
end

View File

@ -0,0 +1,14 @@
# frozen_string_literal: true
# Crea la tabla de publishers de Distributed Press que contiene las
# instancias y tokens
class CreateDistributedPressPublisher < ActiveRecord::Migration[6.1]
def change
create_table :distributed_press_publishers do |t|
t.timestamps
t.string :instance, unique: true
t.text :token_ciphertext, null: false
t.datetime :expires_at, null: true
end
end
end

View File

@ -0,0 +1,10 @@
# frozen_string_literal: true
namespace :distributed_press do
namespace :tokens do
desc 'Renew tokens'
task renew: :environment do
RenewDistributedPressTokensJob.perform_now
end
end
end

View File

@ -13,3 +13,8 @@ check program stats
with path "/usr/bin/foreman run -f /srv/Procfile -d /srv stats" as uid "rails" gid "www-data"
every "0 1 * * *"
if status != 0 then alert
check program distributed_press_tokens_renew
with path "/usr/bin/foreman run -f /srv/Procfile -d /srv distributed_press_tokens_renew" as uid "rails" gid "www-data"
every "0 3 * * *"
if status != 0 then alert