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

Merge branch 'issue-15109' into production.panel.sutty.nl

This commit is contained in:
f 2024-02-22 13:18:59 -03:00
commit 3ee6ea2464
No known key found for this signature in database
41 changed files with 1176 additions and 200 deletions

View file

@ -37,7 +37,9 @@ gem 'commonmarker'
gem 'devise'
gem 'devise-i18n'
gem 'devise_invitable'
gem 'distributed-press-api-client', '~> 0.3.0rc0'
gem 'redis-client'
gem 'hiredis-client'
gem 'distributed-press-api-client', '~> 0.4.0rc2'
gem 'email_address', git: 'https://github.com/fauno/email_address', branch: 'i18n'
gem 'exception_notification'
gem 'fast_blank'
@ -65,6 +67,7 @@ gem 'redis', '~> 4.0', require: %w[redis redis/connection/hiredis]
gem 'redis-rails'
gem 'rollups', git: 'https://github.com/fauno/rollup.git', branch: 'update'
gem 'rubyzip'
gem 'ruby-brs'
gem 'rugged', '1.5.0.1'
gem 'git_clone_url'
gem 'concurrent-ruby-ext'
@ -77,6 +80,9 @@ gem 'yaml_db', git: 'https://0xacab.org/sutty/yaml_db.git'
gem 'kaminari'
gem 'device_detector'
gem 'after_commit_everywhere', '~> 1.0'
gem 'aasm'
# database
gem 'hairtrigger'
gem 'pg'

View file

@ -27,73 +27,80 @@ GIT
GEM
remote: https://17.3.alpine.gems.sutty.nl/
specs:
actioncable (6.1.7.3)
actionpack (= 6.1.7.3)
activesupport (= 6.1.7.3)
aasm (5.5.0)
concurrent-ruby (~> 1.0)
actioncable (6.1.7.4)
actionpack (= 6.1.7.4)
activesupport (= 6.1.7.4)
nio4r (~> 2.0)
websocket-driver (>= 0.6.1)
actionmailbox (6.1.7.3)
actionpack (= 6.1.7.3)
activejob (= 6.1.7.3)
activerecord (= 6.1.7.3)
activestorage (= 6.1.7.3)
activesupport (= 6.1.7.3)
actionmailbox (6.1.7.4)
actionpack (= 6.1.7.4)
activejob (= 6.1.7.4)
activerecord (= 6.1.7.4)
activestorage (= 6.1.7.4)
activesupport (= 6.1.7.4)
mail (>= 2.7.1)
actionmailer (6.1.7.3)
actionpack (= 6.1.7.3)
actionview (= 6.1.7.3)
activejob (= 6.1.7.3)
activesupport (= 6.1.7.3)
actionmailer (6.1.7.4)
actionpack (= 6.1.7.4)
actionview (= 6.1.7.4)
activejob (= 6.1.7.4)
activesupport (= 6.1.7.4)
mail (~> 2.5, >= 2.5.4)
rails-dom-testing (~> 2.0)
actionpack (6.1.7.3)
actionview (= 6.1.7.3)
activesupport (= 6.1.7.3)
actionpack (6.1.7.4)
actionview (= 6.1.7.4)
activesupport (= 6.1.7.4)
rack (~> 2.0, >= 2.0.9)
rack-test (>= 0.6.3)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.2.0)
actiontext (6.1.7.3)
actionpack (= 6.1.7.3)
activerecord (= 6.1.7.3)
activestorage (= 6.1.7.3)
activesupport (= 6.1.7.3)
actiontext (6.1.7.4)
actionpack (= 6.1.7.4)
activerecord (= 6.1.7.4)
activestorage (= 6.1.7.4)
activesupport (= 6.1.7.4)
nokogiri (>= 1.8.5)
actionview (6.1.7.3)
activesupport (= 6.1.7.3)
actionview (6.1.7.4)
activesupport (= 6.1.7.4)
builder (~> 3.1)
erubi (~> 1.4)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.1, >= 1.2.0)
activejob (6.1.7.3)
activesupport (= 6.1.7.3)
activejob (6.1.7.4)
activesupport (= 6.1.7.4)
globalid (>= 0.3.6)
activemodel (6.1.7.3)
activesupport (= 6.1.7.3)
activerecord (6.1.7.3)
activemodel (= 6.1.7.3)
activesupport (= 6.1.7.3)
activestorage (6.1.7.3)
actionpack (= 6.1.7.3)
activejob (= 6.1.7.3)
activerecord (= 6.1.7.3)
activesupport (= 6.1.7.3)
activemodel (6.1.7.4)
activesupport (= 6.1.7.4)
activerecord (6.1.7.4)
activemodel (= 6.1.7.4)
activesupport (= 6.1.7.4)
activestorage (6.1.7.4)
actionpack (= 6.1.7.4)
activejob (= 6.1.7.4)
activerecord (= 6.1.7.4)
activesupport (= 6.1.7.4)
marcel (~> 1.0)
mini_mime (>= 1.1.0)
activesupport (6.1.7.3)
activesupport (6.1.7.4)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 1.6, < 2)
minitest (>= 5.1)
tzinfo (~> 2.0)
zeitwerk (~> 2.3)
addressable (2.8.4)
addressable (2.8.6)
public_suffix (>= 2.0.2, < 6.0)
adsp (1.0.10)
after_commit_everywhere (1.4.0)
activerecord (>= 4.2)
activesupport
ast (2.4.2)
autoprefixer-rails (10.4.13.0)
execjs (~> 2)
bcrypt (3.1.19-x86_64-linux-musl)
bcrypt (3.1.20-x86_64-linux-musl)
bcrypt_pbkdf (1.1.0-x86_64-linux-musl)
benchmark-ips (2.12.0)
bigdecimal (3.1.1)
bindex (0.8.1-x86_64-linux-musl)
blazer (2.6.5)
activerecord (>= 5)
@ -104,7 +111,8 @@ GEM
autoprefixer-rails (>= 9.1.0)
popper_js (>= 1.16.1, < 2)
sassc-rails (>= 2.0.0)
brakeman (5.4.1)
brakeman (6.1.1)
racc
builder (3.2.4)
bundler-audit (0.9.1)
bundler (>= 1.2.0, < 3)
@ -125,6 +133,7 @@ GEM
concurrent-ruby (1.2.2)
concurrent-ruby-ext (1.2.2-x86_64-linux-musl)
concurrent-ruby (= 1.2.2)
connection_pool (2.4.1)
crass (1.0.6)
database_cleaner (2.0.2)
database_cleaner-active_record (>= 2, < 3)
@ -132,7 +141,7 @@ GEM
activerecord (>= 5.a)
database_cleaner-core (~> 2.0.0)
database_cleaner-core (2.0.1)
date (3.3.3-x86_64-linux-musl)
date (3.3.4-x86_64-linux-musl)
dead_end (4.0.0)
derailed_benchmarks (2.1.2)
benchmark-ips (~> 2)
@ -146,8 +155,8 @@ GEM
rake (> 10, < 14)
ruby-statistics (>= 2.1)
thor (>= 0.19, < 2)
device_detector (1.1.1)
devise (4.9.2)
device_detector (1.1.2)
devise (4.9.3)
bcrypt (~> 3.0)
orm_adapter (~> 0.1)
railties (>= 4.1.0)
@ -155,14 +164,15 @@ GEM
warden (~> 1.2.3)
devise-i18n (1.11.0)
devise (>= 4.9.0)
devise_invitable (2.0.8)
devise_invitable (2.0.9)
actionmailer (>= 5.0)
devise (>= 4.6)
distributed-press-api-client (0.3.0rc0)
distributed-press-api-client (0.4.0rc2)
addressable (~> 2.3, >= 2.3.0)
climate_control
dry-schema
httparty (~> 0.18)
httparty-cache (~> 0.0.4)
json (~> 2.1, >= 2.1.0)
jwt (~> 2.6.0)
dotenv (2.8.1)
@ -171,10 +181,10 @@ GEM
railties (>= 3.2)
down (5.4.1)
addressable (~> 2.8)
dry-configurable (1.0.1)
dry-configurable (1.1.0)
dry-core (~> 1.0, < 2)
zeitwerk (~> 2.6)
dry-core (1.0.0)
dry-core (1.0.1)
concurrent-ruby (~> 1.0)
zeitwerk (~> 2.6)
dry-inflector (1.0.0)
@ -183,7 +193,7 @@ GEM
concurrent-ruby (~> 1.0)
dry-core (~> 1.0, < 2)
zeitwerk (~> 2.6)
dry-schema (1.13.1)
dry-schema (1.13.3)
concurrent-ruby (~> 1.0)
dry-configurable (~> 1.0, >= 1.0.1)
dry-core (~> 1.0, < 2)
@ -191,7 +201,8 @@ GEM
dry-logic (>= 1.4, < 2)
dry-types (>= 1.7, < 2)
zeitwerk (~> 2.6)
dry-types (1.7.1)
dry-types (1.7.2)
bigdecimal (~> 3.0)
concurrent-ruby (~> 1.0)
dry-core (~> 1.0)
dry-inflector (~> 1.0)
@ -224,25 +235,25 @@ GEM
ffi (~> 1.0)
git_clone_url (2.0.0)
uri-ssh_git (>= 2.0)
globalid (1.1.0)
activesupport (>= 5.0)
globalid (1.2.1)
activesupport (>= 6.1)
groupdate (6.2.1)
activesupport (>= 5.2)
hairtrigger (1.0.0)
activerecord (>= 6.0, < 8)
ruby2ruby (~> 2.4)
ruby_parser (~> 3.10)
haml (6.1.2-x86_64-linux-musl)
haml (6.3.0)
temple (>= 0.8.2)
thor
tilt
haml-lint (0.999.999)
haml_lint
haml_lint (0.45.0)
haml (>= 4.0, < 6.2)
haml_lint (0.53.0)
haml (>= 5.0)
parallel (~> 1.10)
rainbow
rubocop (>= 0.50.0)
rubocop (>= 1.0)
sysexits (~> 1.1)
hamlit (3.0.3-x86_64-linux-musl)
temple (>= 0.8.2)
@ -256,10 +267,14 @@ GEM
heapy (0.2.0)
thor
hiredis (0.6.3-x86_64-linux-musl)
hiredis-client (0.14.1-x86_64-linux-musl)
redis-client (= 0.14.1)
http_parser.rb (0.8.0-x86_64-linux-musl)
httparty (0.21.0)
mini_mime (>= 1.0.0)
multi_xml (>= 0.5.2)
httparty-cache (0.0.4)
httparty (~> 0.18)
i18n (1.14.1)
concurrent-ruby (~> 1.0)
icalendar (2.8.0)
@ -291,7 +306,7 @@ GEM
terminal-table (~> 2.0)
jekyll-commonmark (1.4.0)
commonmarker (~> 0.22)
jekyll-images (0.4.1)
jekyll-images (0.4.4)
jekyll (~> 4)
ruby-filemagic (~> 0.7)
ruby-vips (~> 2)
@ -301,7 +316,7 @@ GEM
sassc (> 2.0.1, < 3.0)
jekyll-watch (2.2.1)
listen (~> 3.0)
json (2.6.3-x86_64-linux-musl)
json (2.7.1-x86_64-linux-musl)
jwt (2.6.0)
kaminari (1.2.2)
activesupport (>= 4.1.0)
@ -330,12 +345,12 @@ GEM
loaf (0.10.0)
railties (>= 3.2)
lockbox (1.2.0)
lograge (0.12.0)
lograge (0.14.0)
actionpack (>= 4)
activesupport (>= 4)
railties (>= 4)
request_store (~> 1.0)
loofah (2.21.3)
loofah (2.22.0)
crass (~> 1.0.2)
nokogiri (>= 1.12.0)
mail (2.8.1)
@ -349,36 +364,37 @@ GEM
method_source (1.0.0)
mini_histogram (0.3.1)
mini_magick (4.12.0)
mini_mime (1.1.2)
mini_portile2 (2.8.2)
minitest (5.18.0)
mini_mime (1.1.5)
mini_portile2 (2.8.5)
minitest (5.21.1)
mobility (1.2.9)
i18n (>= 0.6.10, < 2)
request_store (~> 1.0)
multi_xml (0.6.0)
net-imap (0.3.4)
net-imap (0.4.9)
date
net-protocol
net-pop (0.1.2)
net-protocol
net-protocol (0.2.1)
net-protocol (0.2.2)
timeout
net-smtp (0.3.3)
net-smtp (0.4.0)
net-protocol
net-ssh (7.1.0)
net-ssh (7.2.1)
netaddr (2.0.6)
nio4r (2.5.9-x86_64-linux-musl)
nokogiri (1.15.4-x86_64-linux-musl)
nio4r (2.7.0-x86_64-linux-musl)
nokogiri (1.16.0-x86_64-linux-musl)
mini_portile2 (~> 2.8.2)
racc (~> 1.4)
orm_adapter (0.5.0)
pairing_heap (3.0.1)
parallel (1.23.0)
parser (3.2.2.1)
parallel (1.24.0)
parser (3.2.2.3)
ast (~> 2.4.1)
racc
pathutil (0.16.2)
forwardable-extended (~> 2.6)
pg (1.5.3-x86_64-linux-musl)
pg (1.5.4-x86_64-linux-musl)
pg_search (2.3.6)
activerecord (>= 5.2)
activesupport (>= 5.2)
@ -388,55 +404,57 @@ GEM
pry (0.14.2)
coderay (~> 1.1)
method_source (~> 1.0)
public_suffix (5.0.3)
puma (6.3.1-x86_64-linux-musl)
public_suffix (5.0.4)
puma (6.4.2-x86_64-linux-musl)
nio4r (~> 2.0)
pundit (2.3.0)
pundit (2.3.1)
activesupport (>= 3.0.0)
que (2.2.1)
racc (1.7.1-x86_64-linux-musl)
rack (2.2.7)
racc (1.7.3-x86_64-linux-musl)
rack (2.2.8)
rack-cors (2.0.1)
rack (>= 2.0.0)
rack-mini-profiler (3.1.0)
rack (>= 1.2.0)
rack-proxy (0.7.6)
rack-proxy (0.7.7)
rack
rack-test (2.1.0)
rack (>= 1.3)
rails (6.1.7.3)
actioncable (= 6.1.7.3)
actionmailbox (= 6.1.7.3)
actionmailer (= 6.1.7.3)
actionpack (= 6.1.7.3)
actiontext (= 6.1.7.3)
actionview (= 6.1.7.3)
activejob (= 6.1.7.3)
activemodel (= 6.1.7.3)
activerecord (= 6.1.7.3)
activestorage (= 6.1.7.3)
activesupport (= 6.1.7.3)
rails (6.1.7.4)
actioncable (= 6.1.7.4)
actionmailbox (= 6.1.7.4)
actionmailer (= 6.1.7.4)
actionpack (= 6.1.7.4)
actiontext (= 6.1.7.4)
actionview (= 6.1.7.4)
activejob (= 6.1.7.4)
activemodel (= 6.1.7.4)
activerecord (= 6.1.7.4)
activestorage (= 6.1.7.4)
activesupport (= 6.1.7.4)
bundler (>= 1.15.0)
railties (= 6.1.7.3)
railties (= 6.1.7.4)
sprockets-rails (>= 2.0.0)
rails-dom-testing (2.0.3)
activesupport (>= 4.2.0)
rails-dom-testing (2.2.0)
activesupport (>= 5.0.0)
minitest
nokogiri (>= 1.6)
rails-html-sanitizer (1.5.0)
loofah (~> 2.19, >= 2.19.1)
rails-i18n (7.0.7)
rails-html-sanitizer (1.6.0)
loofah (~> 2.21)
nokogiri (~> 1.14)
rails-i18n (7.0.8)
i18n (>= 0.7, < 2)
railties (>= 6.0.0, < 8)
rails_warden (0.6.0)
warden (>= 1.2.0)
railties (6.1.7.3)
actionpack (= 6.1.7.3)
activesupport (= 6.1.7.3)
railties (6.1.7.4)
actionpack (= 6.1.7.4)
activesupport (= 6.1.7.4)
method_source
rake (>= 12.2)
thor (~> 1.0)
rainbow (3.1.1)
rake (13.0.6)
rake (13.1.0)
rb-fsevent (0.11.2)
rb-inotify (0.10.1)
ffi (~> 1.0)
@ -448,6 +466,8 @@ GEM
redis-activesupport (5.3.0)
activesupport (>= 3, < 8)
redis-store (>= 1.3, < 2)
redis-client (0.14.1)
connection_pool
redis-rack (2.1.4)
rack (>= 2.0.8, < 3)
redis-store (>= 1.2, < 2)
@ -457,13 +477,13 @@ GEM
redis-store (>= 1.2, < 2)
redis-store (1.9.2)
redis (>= 4, < 6)
regexp_parser (2.8.0)
regexp_parser (2.9.0)
request_store (1.5.1)
rack (>= 1.4)
responders (3.1.0)
responders (3.1.1)
actionpack (>= 5.2)
railties (>= 5.2)
rexml (3.2.5)
rexml (3.2.6)
rgl (0.6.3)
pairing_heap (>= 0.3.0)
rexml (~> 3.2, >= 3.2.4)
@ -479,16 +499,19 @@ GEM
rubocop-ast (>= 1.24.1, < 2.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 1.4.0, < 3.0)
rubocop-ast (1.28.1)
rubocop-ast (1.30.0)
parser (>= 3.2.1.0)
rubocop-rails (2.19.1)
rubocop-rails (2.23.1)
activesupport (>= 4.2.0)
rack (>= 1.1)
rubocop (>= 1.33.0, < 2.0)
rubocop-ast (>= 1.30.0, < 2.0)
ruby-brs (1.3.3-x86_64-linux-musl)
adsp (~> 1.0)
ruby-filemagic (0.7.3-x86_64-linux-musl)
ruby-progressbar (1.13.0)
ruby-statistics (3.0.2)
ruby-vips (2.1.4)
ruby-vips (2.2.0)
ffi (~> 1.12)
ruby2ruby (2.5.0)
ruby_parser (~> 3.1)
@ -521,14 +544,14 @@ GEM
spring-watcher-listen (2.1.0)
listen (>= 2.7, < 4.0)
spring (>= 4)
sprockets (4.2.0)
sprockets (4.2.1)
concurrent-ruby (~> 1.0)
rack (>= 2.2.4, < 4)
sprockets-rails (3.4.2)
actionpack (>= 5.2)
activesupport (>= 5.2)
sprockets (>= 3.0.0)
sqlite3 (1.6.3-x86_64-linux-musl)
sqlite3 (1.7.0-x86_64-linux-musl)
mini_portile2 (~> 2.8.0)
stackprof (0.2.25-x86_64-linux-musl)
stream (0.5.5)
@ -537,13 +560,13 @@ GEM
jekyll (~> 4)
symbol-fstring (1.0.2-x86_64-linux-musl)
sysexits (1.2.0)
temple (0.10.1)
temple (0.10.3)
terminal-table (2.0.0)
unicode-display_width (~> 1.1, >= 1.1.1)
thor (1.3.0)
tilt (2.1.0)
tilt (2.3.0)
timecop (0.9.6)
timeout (0.3.2)
timeout (0.4.1)
turbolinks (5.2.1)
turbolinks-source (~> 5.2)
turbolinks-source (5.2.0)
@ -553,7 +576,7 @@ GEM
execjs (>= 0.3.0, < 3)
unf (0.1.4)
unf_ext
unf_ext (0.0.8.2-x86_64-linux-musl)
unf_ext (0.0.9-x86_64-linux-musl)
unicode-display_width (1.8.0)
uri-ssh_git (2.0.0)
validates_hostname (1.0.13)
@ -579,12 +602,14 @@ GEM
xpath (3.2.0)
nokogiri (~> 1.8)
yard (0.9.34)
zeitwerk (2.6.8)
zeitwerk (2.6.12)
PLATFORMS
x86_64-linux-musl
DEPENDENCIES
aasm
after_commit_everywhere (~> 1.0)
bcrypt (~> 3.1.7)
bcrypt_pbkdf
blazer
@ -601,7 +626,7 @@ DEPENDENCIES
devise
devise-i18n
devise_invitable
distributed-press-api-client (~> 0.3.0rc0)
distributed-press-api-client (~> 0.4.0rc2)
dotenv-rails
down
ed25519
@ -617,6 +642,7 @@ DEPENDENCIES
haml-lint
hamlit-rails
hiredis
hiredis-client
httparty
icalendar
image_processing
@ -650,10 +676,12 @@ DEPENDENCIES
rails-i18n
rails_warden
redis (~> 4.0)
redis-client
redis-rails
rgl
rollups!
rubocop-rails
ruby-brs
rubyzip
rugged (= 1.5.0.1)
safe_yaml

View file

@ -0,0 +1,79 @@
# frozen_string_literal: true
module Api
module V1
module Webhooks
module Concerns
# Helpers para webhooks
module WebhookConcern
extend ActiveSupport::Concern
included do
skip_before_action :verify_authenticity_token
# Responde con forbidden si falla la validación del token
rescue_from ActiveRecord::RecordNotFound, with: :platforms_answer
rescue_from ActiveRecord::RecordInvalid, with: :platforms_answer
private
# Valida el token que envía la plataforma en el webhook
#
# @return [String]
def token
@token ||=
begin
header = request.headers
token = header['X-Social-Inbox'].presence
token ||= header['X-Gitlab-Token'].presence
token ||= token_from_signature(header['X-Gitea-Signature'].presence)
token ||= token_from_signature(header['X-Hub-Signature-256'].presence, 'sha256=')
token
ensure
raise ActiveRecord::RecordNotFound, 'Proveedor no soportado' if token.blank?
end
end
# Valida token a partir de firma
#
# @param signature [String,nil]
# @param prepend [String]
# @return [String, nil]
def token_from_signature(signature, prepend = '')
return if signature.nil?
payload = request.raw_post
site.roles.where(temporal: false, rol: 'usuarie').pluck(:token).find do |token|
new_signature = prepend + OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), token, payload)
ActiveSupport::SecurityUtils.secure_compare(new_signature, signature.to_s)
end
end
# Encuentra el sitio a partir de la URL
#
# @return [Site]
def site
@site ||= Site.find_by_name!(params[:site_id])
end
# Encuentra le usuarie
#
# @return [Site]
def usuarie
@usuarie ||= site.roles.find_by!(temporal: false, rol: 'usuarie', token: token).usuarie
end
# Respuesta de error a plataformas
def platforms_answer(exception)
ExceptionNotifier.notify_exception(exception, data: { headers: request.headers.to_h })
head :forbidden
end
end
end
end
end
end
end

View file

@ -0,0 +1,25 @@
# frozen_string_literal: true
module Api
module V1
module Webhooks
# Recibe webhooks y lanza un PullJob
class PullController < BaseController
include WebhookConcern
# Trae los cambios a partir de un post de Webhooks:
# (Gitlab, Github, Gitea, etc)
#
# @return [nil]
def pull
message = I18n.with_locale(site.default_locale) do
I18n.t('webhooks.pull.message')
end
GitPullJob.perform_later(site, usuarie, message)
head :ok
end
end
end
end
end

View file

@ -0,0 +1,165 @@
# frozen_string_literal: true
module Api
module V1
module Webhooks
# Recibe webhooks de la Social Inbox
#
# @see {https://www.w3.org/TR/activitypub/}
class SocialInboxController < BaseController
include Api::V1::Webhooks::Concerns::WebhookConcern
# Cuando una actividad ingresa en la cola de moderación, la
# recibimos por acá
#
# Vamos a recibir Create, Update, Delete, Follow, Undo y obtener
# el objeto dentro de cada una para guardar un estado asociado
# al sitio.
#
# El objeto del estado puede ser un objeto o une actore,
# dependiendo de la actividad.
def moderationqueued
# Devuelve un error si el token no es válido
usuarie.present?
ActivityPub.transaction do
# Crea todos los registros necesarios y actualiza el estado
actor.present?
instance.present?
object.present?
activity_pub.present?
activity.update_activity_pub_state!
end
rescue ActiveRecord::RecordInvalid => e
ExceptionNotifier.notify_exception(e,
data: { site: site.name, usuarie: usuarie.email,
activity: original_activity })
ensure
head :accepted
end
# Cuando aprobamos una actividad, recibimos la confirmación y
# cambiamos el estado.
def onapproved
ActivityPub.transaction do
activity_pub.approve! if activity_pub.waiting?
end
head :accepted
end
# Cuando rechazamos una actividad, recibimos la confirmación y
# cambiamos el estado
def onrejected
ActivityPub.transaction do
activity_pub.reject! if activity_pub.waiting?
end
head :accepted
end
private
# Si el objeto ya viene incorporado en la actividad o lo tenemos
# que traer remotamente.
#
# @return [Bool]
def object_embedded?
@object_embedded ||= original_activity[:object].is_a?(Hash)
end
# Encuentra la URI del objeto o falla si no la encuentra.
#
# @return [String]
def object_uri
@object_uri ||=
case original_activity[:object]
when String then original_activity[:object]
when Hash then original_activity.dig(:object, :id)
end
ensure
raise ActiveRecord::RecordNotFound, 'object id missing' unless @object_uri
end
# Atajo a la instancia
#
# @return [ActivityPub::Instance]
def instance
actor.instance
end
# Genera un objeto a partir de la actividad. Si el objeto ya
# existe, actualiza su contenido. Si el objeto no viene
# incorporado, obtenemos el contenido más tarde.
#
# @return [ActivityPub::Object]
def object
@object ||= ActivityPub::Object.find_or_initialize_by(uri: object_uri).tap do |o|
# XXX: Si el objeto es una actividad, esto siempre va a ser
# Generic
o.type ||= 'ActivityPub::Object::Generic'
o.content = original_object if object_embedded?
o.save!
# XXX: el objeto necesita ser guardado antes de poder
# procesarlo
ActivityPub::FetchJob.perform_later(site: site, object: o) unless object_embedded?
end
end
# Genera el seguimiento del estado del objeto con respecto al
# sitio.
#
# @return [ActivityPub]
def activity_pub
@activity_pub ||= site.activity_pubs.find_or_create_by!(site: site, object: object)
end
# Crea la actividad y la vincula con el estado
#
# @return [ActivityPub::Activity]
def activity
@activity ||= ActivityPub::Activity.type_from(original_activity).new(uri: original_activity[:id],
activity_pub: activity_pub).tap do |a|
a.content = original_activity.dup
a.content[:object] = object.uri
a.save!
end
end
# Actor, si no hay instancia, la crea en el momento
#
# @return [Actor]
def actor
@actor ||= ActivityPub::Actor.find_or_initialize_by(uri: original_activity[:actor]).tap do |a|
next if a.instance
a.instance = ActivityPub::Instance.find_or_create_by(hostname: URI.parse(a.uri).hostname)
a.save!
end
end
# Descubre la actividad recibida, generando un error si la
# actividad no está dirigida a nosotres.
#
# @todo Validar formato
# @return [Hash]
def original_activity
@original_activity ||= FastJsonparser.parse(request.raw_post).tap do |activity|
raise '@context missing' unless activity[:@context].presence
raise 'id missing' unless activity[:id].presence
raise 'object missing' unless activity[:object].presence
rescue RuntimeError => e
raise ActiveRecord::RecordNotFound, e.message
end
end
# @return [Hash,String]
def original_object
@original_object ||= original_activity[:object].dup
end
end
end
end
end

View file

@ -1,77 +0,0 @@
# frozen_string_literal: true
module Api
module V1
# Recibe webhooks y lanza un PullJob
class WebhooksController < BaseController
# responde con forbidden si falla la validación del token
rescue_from ActiveRecord::RecordNotFound, with: :platforms_answer
# Trae los cambios a partir de un post de Webhooks:
# (Gitlab, Github, Gitea, etc)
#
# @return [nil]
def pull
message = I18n.with_locale(site.default_locale) do
I18n.t('webhooks.pull.message')
end
GitPullJob.perform_later(site, usuarie, message)
head :ok
end
private
# encuentra el sitio a partir de la url
def site
@site ||= Site.find_by_name!(params[:site_id])
end
# valida el token que envía la plataforma del webhook
#
# @return [String]
def token
@token ||=
begin
# Gitlab
if request.headers['X-Gitlab-Token'].present?
request.headers['X-Gitlab-Token']
# Github
elsif request.headers['X-Hub-Signature-256'].present?
token_from_signature(request.headers['X-Hub-Signature-256'], 'sha256=')
# Gitea
elsif request.headers['X-Gitea-Signature'].present?
token_from_signature(request.headers['X-Gitea-Signature'])
else
raise ActiveRecord::RecordNotFound, 'proveedor no soportado'
end
end
end
# valida token a partir de firma de webhook
#
# @return [String, Boolean]
def token_from_signature(signature, prepend = '')
payload = request.body.read
site.roles.where(temporal: false, rol: 'usuarie').pluck(:token).find do |token|
new_signature = prepend + OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), token, payload)
ActiveSupport::SecurityUtils.secure_compare(new_signature, signature.to_s)
end.tap do |t|
raise ActiveRecord::RecordNotFound, 'token no encontrado' if t.nil?
end
end
# encuentra le usuarie
def usuarie
@usuarie ||= site.roles.find_by!(temporal: false, rol: 'usuarie', token: token).usuarie
end
# respuesta de error a plataformas
def platforms_answer(exception)
ExceptionNotifier.notify_exception(exception, data: { headers: request.headers.to_h })
head :forbidden
end
end
end
end

View file

@ -0,0 +1,28 @@
# frozen_string_literal: true
# Obtiene o actualiza el contenido de un objeto, usando las credenciales
# del sitio.
#
# XXX: Esto usa las credenciales del sitio para volver el objeto
# disponible para todo el CMS. Asumimos que el objeto devuelto es el
# mismo para todo el mundo y las credenciales solo son para
# autenticación.
class ActivityPub
class FetchJob < ApplicationJob
def perform(site:, object:)
ActivityPub::Object.transaction do
return if object.activity_pubs.where(aasm_state: 'removed').count.positive?
response = site.social_inbox.dereferencer.get(uri: object.uri)
# @todo Fallar cuando la respuesta no funcione?
return unless response.ok?
return if response.miss? && object.content.present?
content = FastJsonparser.parse(response.body)
object.update(content: content, type: ActivityPub::Object.type_from(content).name)
end
end
end
end

View file

@ -0,0 +1,62 @@
# frozen_string_literal: true
# = ActivityPub =
#
# El registro de actividades recibidas y su estado. Cuando recibimos
# una actividad, puede estar destinada a varies actores dentro de Sutty,
# con lo que generamos una cola para cada une.
#
# @see {https://www.w3.org/TR/activitypub/#client-to-server-interactions}
class ActivityPub < ApplicationRecord
include AASM
belongs_to :site
belongs_to :object, polymorphic: true
has_many :activities
validates :site_id, presence: true
validates :object_id, presence: true
validates :aasm_state, presence: true, inclusion: { in: %w[paused approved rejected reported removed] }
aasm do
# Todavía no hay una decisión sobre el objeto
state :paused, initial: true
# Estamos esperando respuesta desde la Social Inbox
state :waiting
# Le usuarie aprobó el objeto
state :approved
# Le usuarie rechazó el objeto
state :rejected
# Le usuarie reportó el objeto
state :reported
# Le actore eliminó el objeto
state :removed
# Recibir una acción de eliminación, eliminar el contenido de la
# base de datos. Esto elimina el contenido para todos los sitios
# porque estamos respetando lo que pidió le actore.
event :remove do
transitions to: :removed
before do
object.update(content: {}) unless object.content.empty?
end
end
# Si un objeto previamente aprobado fue actualizado, volvemos a
# pausarlo.
event :pause do
transitions from: %i[waiting approved rejected], to: :paused
end
# La actividad se aprueba
event :approve do
transitions from: :waiting, to: :approved
end
# La actividad fue rechazada
event :reject do
transitions from: :waiting, to: :rejected
end
end
end

View file

@ -0,0 +1,31 @@
# frozen_string_literal: true
# = Activity =
#
# Lleva un registro de las actividades que nos piden hacer remotamente.
#
# Las actividades pueden tener distintos destinataries (sitios/actores).
#
# @todo Obtener el contenido del objeto dinámicamente si no existe
# localmente, por ejemplo cuando la actividad crea un objeto pero lo
# envía como referencia en lugar de anidarlo.
#
# @see {https://www.w3.org/TR/activitypub/#client-to-server-interactions}
class ActivityPub
class Activity < ApplicationRecord
include ActivityPub::Concerns::JsonLdConcern
belongs_to :activity_pub
has_one :object, through: :activity_pub
validates :activity_pub_id, presence: true
# Siempre en orden descendiente para saber el último estado
default_scope -> { order(created_at: :desc) }
# Cambia la máquina de estados según el tipo de actividad
def update_activity_pub_state!
nil
end
end
end

View file

@ -0,0 +1,7 @@
# frozen_string_literal: true
class ActivityPub
class Activity
class Create < ActivityPub::Activity; end
end
end

View file

@ -0,0 +1,13 @@
# frozen_string_literal: true
class ActivityPub
class Activity
class Delete < ActivityPub::Activity
# Si estamos eliminando el objeto, tenemos que vaciar su contenido y
# cambiar el estado a borrado.
def update_activity_pub_state!
activity_pub.remove!
end
end
end
end

View file

@ -0,0 +1,7 @@
# frozen_string_literal: true
class ActivityPub
class Activity
class Flag < ActivityPub::Activity; end
end
end

View file

@ -0,0 +1,11 @@
# frozen_string_literal: true
# = Follow =
#
# Una actividad de seguimiento se refiere siempre a une actore (el
# sitio) y proviene de otre actore.
class ActivityPub
class Activity
class Follow < ActivityPub::Activity; end
end
end

View file

@ -0,0 +1,7 @@
# frozen_string_literal: true
class ActivityPub
class Activity
class Generic < ActivityPub::Activity; end
end
end

View file

@ -0,0 +1,27 @@
# frozen_string_literal: true
# = Undo =
#
# Deshace una actividad, dependiendo de la actividad a la que se
# refiere.
class ActivityPub
class Activity
class Undo < ActivityPub::Activity
# Una actividad de deshacer tiene anidada como objeto la actividad
# a deshacer. Para respetar la voluntad de le actore remote,
# tendríamos que eliminar cualquier actividad pendiente sobre el
# objeto.
#
# Sin embargo, estas acciones nunca deberían llegar a nuestra
# Inbox.
#
# @see {https://github.com/hyphacoop/social.distributed.press/issues/43}
def update_activity_pub_state!
ActivityPub.transaction do
ActivityPub::Activity.find_by(uri: content['object'])&.activity_pub&.remove!
activity_pub.remove!
end
end
end
end
end

View file

@ -0,0 +1,13 @@
# frozen_string_literal: true
class ActivityPub
class Activity
class Update < ActivityPub::Activity
# Si estamos actualizando el objeto, tenemos que devolverlo a estado
# de moderación
def update_activity_pub_state!
activity_pub.pause! if activity_pub.approved?
end
end
end
end

View file

@ -0,0 +1,15 @@
# frozen_string_literal: true
# = Actor =
#
# Actor es la entidad que realiza acciones en ActivityPub
#
# @todo Obtener el perfil dinámicamente
class ActivityPub
class Actor < ApplicationRecord
include ActivityPub::Concerns::JsonLdConcern
belongs_to :instance
has_many :activity_pubs, as: :object
end
end

View file

@ -0,0 +1,34 @@
# frozen_string_literal: true
class ActivityPub
module Concerns
module JsonLdConcern
extend ActiveSupport::Concern
included do
validates :uri, presence: true, uniqueness: true
# Cuando asignamos contenido, obtener la URI si no lo hicimos ya
before_save :uri_from_content!, unless: :uri?
# Obtiene un tipo de actividad a partir del tipo informado
#
# @param object [Hash]
# @return [Activity]
def self.type_from(object)
raise NameError unless object.is_a?(Hash)
"#{model_name.name}::#{object[:type].presence || 'Generic'}".constantize
rescue NameError
model_name.name.constantize::Generic
end
private
def uri_from_content!
self.uri = content[:id]
end
end
end
end
end

View file

@ -0,0 +1,23 @@
# frozen_string_literal: true
# = Instance =
#
# Representa cada instancia del fediverso que interactúa con la Social
# Inbox.
class ActivityPub
class Instance < ApplicationRecord
include AASM
validates :aasm_state, presence: true, inclusion: { in: %w[paused allowed blocked] }
validates :hostname, uniqueness: true, hostname: true
has_many :activity_pubs
has_many :actors
aasm do
state :paused, initial: true
state :allowed
state :blocked
end
end
end

View file

@ -0,0 +1,10 @@
# frozen_string_literal: true
# Almacena objetos de ActivityPub, como Note, Article, etc.
class ActivityPub
class Object < ApplicationRecord
include ActivityPub::Concerns::JsonLdConcern
has_many :activity_pubs, as: :object
end
end

View file

@ -0,0 +1,10 @@
# frozen_string_literal: true
# = Application =
#
# Una aplicación o instancia
class ActivityPub
class Object
class Application < ActivityPub::Object; end
end
end

View file

@ -0,0 +1,10 @@
# frozen_string_literal: true
# = Article =
#
# Representa artículos
class ActivityPub
class Object
class Article < ActivityPub::Object; end
end
end

View file

@ -0,0 +1,8 @@
# frozen_string_literal: true
# = Generic =
class ActivityPub
class Object
class Generic < ActivityPub::Object; end
end
end

View file

@ -0,0 +1,10 @@
# frozen_string_literal: true
# = Note =
#
# Representa notas, el tipo más común de objeto del Fediverso.
class ActivityPub
class Object
class Note < ActivityPub::Object; end
end
end

View file

@ -0,0 +1,10 @@
# frozen_string_literal: true
# = Organization =
#
# Una organización
class ActivityPub
class Object
class Organization < ActivityPub::Object; end
end
end

View file

@ -0,0 +1,10 @@
# frozen_string_literal: true
# = Person =
#
# Una persona, el perfil de une actore
class ActivityPub
class Object
class Person < ActivityPub::Object; end
end
end

View file

@ -10,10 +10,12 @@ require 'open3'
# :attributes`.
class Deploy < ApplicationRecord
belongs_to :site
belongs_to :rol
has_many :build_stats, dependent: :destroy
DEPENDENCIES = []
SOFT_DEPENDENCIES = []
DEPENDENCIES = [].freeze
SOFT_DEPENDENCIES = [].freeze
def deploy(**)
raise NotImplementedError
@ -72,7 +74,7 @@ class Deploy < ApplicationRecord
'HOME' => home_dir,
'PATH' => paths.join(':'),
'JEKYLL_ENV' => Rails.env,
'LANG' => ENV['LANG'],
'LANG' => ENV.fetch('LANG', nil)
})
end
@ -137,7 +139,7 @@ class Deploy < ApplicationRecord
# provisto con el archivo como parámetro
#
# @param :content [String]
def with_tempfile(content, &block)
def with_tempfile(content)
Tempfile.create(SecureRandom.hex) do |file|
file.write content.to_s
file.rewind

View file

@ -5,7 +5,9 @@ require 'distributed_press/v1/social/client'
# Publicar novedades al Fediverso
class DeploySocialDistributedPress < Deploy
# Solo luego de publicar remotamente
DEPENDENCIES = %i[deploy_distributed_press deploy_rsync deploy_full_rsync]
DEPENDENCIES = %i[deploy_distributed_press deploy_rsync deploy_full_rsync].freeze
after_save :create_hooks!
# Envía las notificaciones
def deploy(output: false)
@ -52,4 +54,45 @@ class DeploySocialDistributedPress < Deploy
def flags_for_build(**args)
"--key #{Shellwords.escape args[:private_key].path}"
end
private
# Obtiene el hostname de la API de Sutty
#
# @return [String]
def api_hostname
Rails.application.routes.default_url_options[:host].sub('panel', 'api')
end
# Crea los hooks en la Social Inbox para que nos avise de actividades
# nuevas
#
# @return [nil]
def create_hooks!
hook_client = site.social_inbox.hook
webhook_class = DistributedPress::V1::Social::Schemas::Webhook
hook_client.class::EVENTS.each do |event|
event_url = :"v1_site_webhooks_social_inbox_#{event}_url"
webhook =
webhook_class.new.call({
method: 'POST',
url: Rails.application.routes.url_helpers.public_send(
event_url, site_id: site.name, host: api_hostname
),
headers: {
'X-Social-Inbox': rol.token
}
})
raise ArgumentError, webhook.errors.messages if webhook.failure?
response = hook_client.put(event: event, hook: webhook)
raise ArgumentError, response.parsed_body unless response.ok?
rescue ArgumentError => e
ExceptionNotifier.notify_exception(e, data: { site_id: site.name, usuarie_id: rol.usuarie_id })
end
end
end

View file

@ -11,6 +11,7 @@ class Rol < ApplicationRecord
belongs_to :usuarie
belongs_to :site
has_many :deploys
validates_inclusion_of :rol, in: ROLES

View file

@ -1,5 +1,7 @@
# frozen_string_literal: true
require 'distributed_press/v1/social/client'
class Site
# Agrega soporte para Social Distributed Press en los sitios
module SocialDistributedPress
@ -8,15 +10,25 @@ class Site
included do
encrypts :private_key_pem
has_many :activity_pubs
before_save :generate_private_key_pem!, unless: :private_key_pem?
# @return [SocialInbox]
def social_inbox
@social_inbox ||= SocialInbox.new(site: self)
end
private
# Genera la llave privada y la almacena
#
# @return [nil]
def generate_private_key_pem!
self.private_key_pem ||= ::DistributedPress::V1::Social::Client.new(public_key_url: nil, key_size: 2048).private_key.export
self.private_key_pem ||= DistributedPress::V1::Social::Client.new(
public_key_url: nil,
key_size: 2048
).private_key.export
end
end
end

View file

@ -0,0 +1,74 @@
# frozen_string_literal: true
require 'distributed_press/v1/social/client'
require 'distributed_press/v1/social/hook'
require 'distributed_press/v1/social/dereferencer'
# Gestiona la Social Inbox de un sitio
class SocialInbox
# @return [Site]
attr_reader :site
# @param :site [Site]
def initialize(site:)
@site = site
end
# @return [String]
def actor
@actor ||=
begin
user = site.config.dig('activity_pub', 'username')
user ||= hostname.split('.', 2).first
"@#{user}@#{hostname}"
end
end
def actor_id
@actor_id ||= generate_uri do |uri|
uri.path = '/about.jsonld'
end
end
# @return [DistributedPress::V1::Social::Client]
def client
@client ||= DistributedPress::V1::Social::Client.new(
url: site.config.dig('activity_pub', 'url'),
public_key_url: public_key_url,
private_key_pem: site.private_key_pem,
logger: Rails.logger,
cache_store: :redis
)
end
# @return [DistributedPress::V1::Social::Dereferencer]
def dereferencer
@dereferencer ||= DistributedPress::V1::Social::Dereferencer.new(client: client)
end
# @return [DistributedPress::V1::Social::Hook]
def hook
@hook ||= DistributedPress::V1::Social::Hook.new(client: client, actor: actor)
end
# @return [String]
def public_key_url
@public_key_url ||= generate_uri do |uri|
uri.path = '/about.jsonld'
uri.fragment = 'main-key'
end
end
def hostname
@hostname ||=
site.config.dig('activity_pub', 'hostname') || site.hostname
end
# Genera una URI dentro de este sitio
#
# @return [String]
def generate_uri(&block)
@public_key_url ||= URI("https://#{hostname}").tap(&block).to_s
end
end

View file

@ -2,6 +2,10 @@
require_relative 'boot'
require 'aasm'
require 'redis-client'
require 'hiredis-client'
require 'brs'
require 'rails'
# Pick the frameworks you want:
require 'active_model/railtie'

View file

@ -18,7 +18,15 @@ Rails.application.routes.draw do
get :'contact/cookie', to: 'invitades#contact_cookie'
post :'contact/:form', to: 'contact#receive', as: :contact
post :'webhooks/pull', to: 'webhooks#pull'
namespace :webhooks do
post :pull, to: 'pull#pull'
scope :social_inbox do
post :moderationqueued, to: 'social_inbox#moderationqueued'
post :onapproved, to: 'social_inbox#onapproved'
post :onrejected, to: 'social_inbox#onrejected'
end
end
end
end
end

View file

@ -0,0 +1,18 @@
# frozen_string_literal: true
# Establece una relación entre roles y deploys
class AddRolToDeploys < ActiveRecord::Migration[6.1]
def up
add_column :deploys, :rol_id, :integer, index: true
Deploy.find_each do |deploy|
rol_id = deploy.site.roles.find_by(rol: 'usuarie', temporal: false).id
deploy.update_column(:rol_id, rol_id) if rol_id
end
end
def down
remove_column :deploys, :rol_id
end
end

View file

@ -0,0 +1,16 @@
# frozen_string_literal: true
# Actividades. Se asocian a un objeto y a una cola de moderación
class CreateActivityPubActivities < ActiveRecord::Migration[6.1]
def change
create_table :activity_pub_activities, id: :uuid do |t|
t.timestamps
t.uuid :activity_pub_id, index: true, null: false
t.string :type, null: false
t.string :uri, null: false
t.jsonb :content, default: {}
end
end
end

View file

@ -0,0 +1,12 @@
# frozen_string_literal: true
# Almacena actores de ActivityPub y los relaciona con actividades
class CreateActivityPubActors < ActiveRecord::Migration[6.1]
def change
create_table :activity_pub_actors, id: :uuid do |t|
t.timestamps
t.uuid :instance_id, index: true, null: false
t.string :uri, index: true, unique: true, null: false
end
end
end

View file

@ -0,0 +1,18 @@
# frozen_string_literal: true
# Registro de actividades.
class CreateActivityPubs < ActiveRecord::Migration[6.1]
def change
create_table :activity_pubs, id: :uuid do |t|
t.timestamps
t.bigint :site_id, null: false
t.uuid :object_id, null: false
t.string :object_type, null: false
t.string :aasm_state, null: false
t.index %i[site_id object_id object_type], unique: true
end
end
end

View file

@ -0,0 +1,17 @@
# frozen_string_literal: true
# Almacena objetos de ActivityPub. Los objetos pueden estar compartidos
# por toda la instancia.
class CreateActivityPubObjects < ActiveRecord::Migration[6.1]
def change
create_table :activity_pub_objects, id: :uuid do |t|
t.timestamps
t.uuid :actor_id, index: true, null: false
t.string :type, null: false
t.string :uri, null: false, unique: true
t.jsonb :content, default: {}
end
end
end

View file

@ -0,0 +1,13 @@
# frozen_string_literal: true
# Almacena las instancias
class CreateActivityPubInstances < ActiveRecord::Migration[6.1]
def change
create_table :activity_pub_instances, id: :uuid do |t|
t.timestamps
t.string :hostname, index: true, unique: true, null: false
t.string :aasm_state, null: false
t.jsonb :content, default: {}
end
end
end

View file

@ -0,0 +1,12 @@
# frozen_string_literal: true
# No es necesario vincular actores con objetos, porque la forma en que
# lo estábamos haciendo no se refiere a le actore del objeto, sino de
# acciones distintas sobre el mismo objeto, generado por une actore.
#
# Y ese valor ya lo podemos obtener desde attributedTo
class RemoveActorFromObjects < ActiveRecord::Migration[6.1]
def change
remove_column :activity_pub_objects, :actor_id, :uuid, index: true
end
end

View file

@ -494,6 +494,77 @@ CREATE SEQUENCE public.active_storage_variant_records_id_seq
ALTER SEQUENCE public.active_storage_variant_records_id_seq OWNED BY public.active_storage_variant_records.id;
--
-- Name: activity_pub_activities; Type: TABLE; Schema: public; Owner: -
--
CREATE TABLE public.activity_pub_activities (
id uuid DEFAULT gen_random_uuid() NOT NULL,
created_at timestamp(6) without time zone NOT NULL,
updated_at timestamp(6) without time zone NOT NULL,
activity_pub_id uuid NOT NULL,
type character varying NOT NULL,
uri character varying NOT NULL,
content jsonb DEFAULT '{}'::jsonb
);
--
-- Name: activity_pub_actors; Type: TABLE; Schema: public; Owner: -
--
CREATE TABLE public.activity_pub_actors (
id uuid DEFAULT gen_random_uuid() NOT NULL,
created_at timestamp(6) without time zone NOT NULL,
updated_at timestamp(6) without time zone NOT NULL,
instance_id uuid NOT NULL,
uri character varying NOT NULL
);
--
-- Name: activity_pub_instances; Type: TABLE; Schema: public; Owner: -
--
CREATE TABLE public.activity_pub_instances (
id uuid DEFAULT gen_random_uuid() NOT NULL,
created_at timestamp(6) without time zone NOT NULL,
updated_at timestamp(6) without time zone NOT NULL,
hostname character varying NOT NULL,
aasm_state character varying NOT NULL,
content jsonb DEFAULT '{}'::jsonb
);
--
-- Name: activity_pub_objects; Type: TABLE; Schema: public; Owner: -
--
CREATE TABLE public.activity_pub_objects (
id uuid DEFAULT gen_random_uuid() NOT NULL,
created_at timestamp(6) without time zone NOT NULL,
updated_at timestamp(6) without time zone NOT NULL,
type character varying NOT NULL,
uri character varying NOT NULL,
content jsonb DEFAULT '{}'::jsonb
);
--
-- Name: activity_pubs; Type: TABLE; Schema: public; Owner: -
--
CREATE TABLE public.activity_pubs (
id uuid DEFAULT gen_random_uuid() NOT NULL,
created_at timestamp(6) without time zone NOT NULL,
updated_at timestamp(6) without time zone NOT NULL,
site_id bigint NOT NULL,
object_id uuid NOT NULL,
object_type character varying NOT NULL,
aasm_state character varying NOT NULL
);
--
-- Name: ar_internal_metadata; Type: TABLE; Schema: public; Owner: -
--
@ -780,7 +851,8 @@ CREATE TABLE public.deploys (
updated_at timestamp without time zone NOT NULL,
site_id integer,
type character varying,
"values" text
"values" text,
rol_id integer
);
@ -1585,6 +1657,46 @@ ALTER TABLE ONLY public.active_storage_variant_records
ADD CONSTRAINT active_storage_variant_records_pkey PRIMARY KEY (id);
--
-- Name: activity_pub_activities activity_pub_activities_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY public.activity_pub_activities
ADD CONSTRAINT activity_pub_activities_pkey PRIMARY KEY (id);
--
-- Name: activity_pub_actors activity_pub_actors_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY public.activity_pub_actors
ADD CONSTRAINT activity_pub_actors_pkey PRIMARY KEY (id);
--
-- Name: activity_pub_instances activity_pub_instances_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY public.activity_pub_instances
ADD CONSTRAINT activity_pub_instances_pkey PRIMARY KEY (id);
--
-- Name: activity_pub_objects activity_pub_objects_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY public.activity_pub_objects
ADD CONSTRAINT activity_pub_objects_pkey PRIMARY KEY (id);
--
-- Name: activity_pubs activity_pubs_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY public.activity_pubs
ADD CONSTRAINT activity_pubs_pkey PRIMARY KEY (id);
--
-- Name: blazer_audits blazer_audits_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
@ -1885,6 +1997,41 @@ CREATE UNIQUE INDEX index_active_storage_blobs_on_key_and_service_name ON public
CREATE UNIQUE INDEX index_active_storage_variant_records_uniqueness ON public.active_storage_variant_records USING btree (blob_id, variation_digest);
--
-- Name: index_activity_pub_activities_on_activity_pub_id; Type: INDEX; Schema: public; Owner: -
--
CREATE INDEX index_activity_pub_activities_on_activity_pub_id ON public.activity_pub_activities USING btree (activity_pub_id);
--
-- Name: index_activity_pub_actors_on_instance_id; Type: INDEX; Schema: public; Owner: -
--
CREATE INDEX index_activity_pub_actors_on_instance_id ON public.activity_pub_actors USING btree (instance_id);
--
-- Name: index_activity_pub_actors_on_uri; Type: INDEX; Schema: public; Owner: -
--
CREATE INDEX index_activity_pub_actors_on_uri ON public.activity_pub_actors USING btree (uri);
--
-- Name: index_activity_pub_instances_on_hostname; Type: INDEX; Schema: public; Owner: -
--
CREATE INDEX index_activity_pub_instances_on_hostname ON public.activity_pub_instances USING btree (hostname);
--
-- Name: index_activity_pubs_on_site_id_and_object_id_and_object_type; Type: INDEX; Schema: public; Owner: -
--
CREATE UNIQUE INDEX index_activity_pubs_on_site_id_and_object_id_and_object_type ON public.activity_pubs USING btree (site_id, object_id, object_type);
--
-- Name: index_blazer_audits_on_query_id; Type: INDEX; Schema: public; Owner: -
--
@ -2346,6 +2493,13 @@ INSERT INTO "schema_migrations" (version) VALUES
('20230731195050'),
('20230829204127'),
('20230921155401'),
('20230927153926');
('20230927153926'),
('20240216170202'),
('20240219153919'),
('20240219175839'),
('20240219204011'),
('20240219204224'),
('20240220161414'),
('20240221184007');