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

Merge branch 'rails' of 0xacab.org:sutty/sutty into issue-13498
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed

This commit is contained in:
f 2023-10-20 11:07:19 -03:00
commit 341929a7bf
No known key found for this signature in database
173 changed files with 1353 additions and 501 deletions

11
.editorconfig Normal file
View file

@ -0,0 +1,11 @@
root = true
[*]
end_of_line = lf
insert_final_newline = true
charset = utf-8
indent_style = space
indent_size = 2
[Makefile]
indent_style = tab

2
.gitattributes vendored Normal file
View file

@ -0,0 +1,2 @@
public/assets/** filter=lfs diff=lfs merge=lfs -text
public/packs/** filter=lfs diff=lfs merge=lfs -text

7
.gitignore vendored
View file

@ -34,12 +34,7 @@
/config/master.key
/config/credentials.yml.enc
/public/packs
/public/packs-test
/public/assets
/public/assets-production
/public/packs
/public/packs-production
/node_modules
/yarn-error.log
yarn-debug.log*
@ -49,8 +44,6 @@ yarn-debug.log*
*.key
*.crt
/public/packs
/public/packs-test
/node_modules
/yarn-error.log
yarn-debug.log*

View file

@ -20,6 +20,7 @@ pipeline:
branch:
- "rails"
- "panel.sutty.nl"
- "17.3.alpine.panel.sutty.nl"
event: "push"
path:
include:
@ -27,7 +28,7 @@ pipeline:
- ".dockerignore"
- ".woodpecker.yml"
assets:
image: "gitea.nulo.in/sutty/panel:${ALPINE_VERSION}-${RUBY_VERSION}.${RUBY_PATCH}"
image: "gitea.nulo.in/sutty/panel:3.14.10-2.7.8"
commands:
- "apk add python2 dotenv openssh-client brotli"
- "install -d -m 700 ~/.ssh/"
@ -51,6 +52,9 @@ pipeline:
- "git add public && git commit -m \"ci: assets [skip ci]\""
- "git pull upstream ${CI_COMMIT_BRANCH}"
- "git push upstream ${CI_COMMIT_BRANCH}"
environment:
- "RUBY_VERSION=${RUBY_VERSION}"
- "GEMS_SOURCE=https://14.3.alpine.gems.sutty.nl"
secrets:
- "SSH_KEY"
- "KNOWN_HOSTS"
@ -65,8 +69,15 @@ pipeline:
- "app/javascript/**/*"
- "package.json"
- "yarn.lock"
matrix:
ALPINE_VERSION: "3.14.10"
RUBY_VERSION: "2.7"
RUBY_PATCH: "8"
matrix:
include:
- ALPINE_VERSION: "3.17.3"
RUBY_VERSION: "3.1"
RUBY_PATCH: "4"
- ALPINE_VERSION: "3.14.10"
RUBY_VERSION: "2.7"
RUBY_PATCH: "8"

30
Gemfile
View file

@ -1,14 +1,13 @@
# frozen_string_literal: true
puts 'Usa haini.sh para generar un entorno de trabajo reproducible'
source 'https://gems.sutty.nl'
source ENV.fetch('GEMS_SOURCE', 'https://17.3.alpine.gems.sutty.nl')
ruby '~> 2.7'
ruby "~> #{ENV.fetch('RUBY_VERSION', '3.1')}"
gem 'dotenv-rails', require: 'dotenv/rails-now'
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '~> 6'
gem 'rails', '~> 6.1.0'
# Use Puma as the app server
gem 'puma'
@ -33,13 +32,14 @@ gem 'turbolinks', '~> 5'
gem 'jbuilder', '~> 2.5'
# Use ActiveModel has_secure_password
gem 'bcrypt', '~> 3.1.7'
gem 'safely_block', '~> 0.3.0'
gem 'blazer'
gem 'chartkick'
gem 'commonmarker'
gem 'devise'
gem 'devise-i18n'
gem 'devise_invitable'
gem 'distributed-press-api-client', '~> 0.2.3'
gem 'distributed-press-api-client', '~> 0.3.0rc0'
gem 'njalla-api-client', '~> 0.2.0'
gem 'email_address', git: 'https://github.com/fauno/email_address', branch: 'i18n'
gem 'exception_notification'
@ -51,10 +51,10 @@ gem 'image_processing'
gem 'icalendar'
gem 'inline_svg'
gem 'httparty'
gem 'safe_yaml'
gem 'jekyll', '~> 4.2'
gem 'safe_yaml', require: false
gem 'jekyll', '~> 4.2.0'
gem 'jekyll-data'
gem 'jekyll-commonmark'
gem 'jekyll-commonmark', '~> 1.4.0'
gem 'jekyll-images'
gem 'jekyll-include-cache'
gem 'sutty-liquid', '>= 0.7.3'
@ -65,11 +65,12 @@ gem 'mobility'
gem 'pundit'
gem 'rails-i18n'
gem 'rails_warden'
gem 'redis', require: %w[redis redis/connection/hiredis]
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 'rugged'
gem 'rugged', '1.5.0.1'
gem 'git_clone_url'
gem 'concurrent-ruby-ext'
gem 'que'
gem 'symbol-fstring', require: 'fstring/all'
@ -78,6 +79,7 @@ gem 'validates_hostname'
gem 'webpacker'
gem 'yaml_db', git: 'https://0xacab.org/sutty/yaml_db.git'
gem 'kaminari'
gem 'device_detector'
# database
gem 'hairtrigger'
@ -111,7 +113,7 @@ group :development, :test do
gem 'pry'
# Adds support for Capybara system testing and selenium driver
gem 'capybara', '~> 2.13'
gem 'selenium-webdriver'
gem 'selenium-webdriver', '~> 4.8.0'
gem 'sqlite3'
end
@ -119,11 +121,11 @@ group :development do
gem 'brakeman'
gem 'haml-lint', require: false
gem 'letter_opener'
gem 'listen', '>= 3.0.5', '< 3.2'
gem 'listen'
gem 'rubocop-rails'
gem 'spring'
gem 'spring-watcher-listen', '~> 2.0.0'
gem 'web-console', '>= 3.3.0'
gem 'spring-watcher-listen'
gem 'web-console'
end
group :test do

View file

@ -25,86 +25,86 @@ GIT
groupdate (>= 5.2)
GEM
remote: https://gems.sutty.nl/
remote: https://17.3.alpine.gems.sutty.nl/
specs:
actioncable (6.1.4.1)
actionpack (= 6.1.4.1)
activesupport (= 6.1.4.1)
actioncable (6.1.7.3)
actionpack (= 6.1.7.3)
activesupport (= 6.1.7.3)
nio4r (~> 2.0)
websocket-driver (>= 0.6.1)
actionmailbox (6.1.4.1)
actionpack (= 6.1.4.1)
activejob (= 6.1.4.1)
activerecord (= 6.1.4.1)
activestorage (= 6.1.4.1)
activesupport (= 6.1.4.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)
mail (>= 2.7.1)
actionmailer (6.1.4.1)
actionpack (= 6.1.4.1)
actionview (= 6.1.4.1)
activejob (= 6.1.4.1)
activesupport (= 6.1.4.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)
mail (~> 2.5, >= 2.5.4)
rails-dom-testing (~> 2.0)
actionpack (6.1.4.1)
actionview (= 6.1.4.1)
activesupport (= 6.1.4.1)
actionpack (6.1.7.3)
actionview (= 6.1.7.3)
activesupport (= 6.1.7.3)
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.4.1)
actionpack (= 6.1.4.1)
activerecord (= 6.1.4.1)
activestorage (= 6.1.4.1)
activesupport (= 6.1.4.1)
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)
nokogiri (>= 1.8.5)
actionview (6.1.4.1)
activesupport (= 6.1.4.1)
actionview (6.1.7.3)
activesupport (= 6.1.7.3)
builder (~> 3.1)
erubi (~> 1.4)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.1, >= 1.2.0)
activejob (6.1.4.1)
activesupport (= 6.1.4.1)
activejob (6.1.7.3)
activesupport (= 6.1.7.3)
globalid (>= 0.3.6)
activemodel (6.1.4.1)
activesupport (= 6.1.4.1)
activerecord (6.1.4.1)
activemodel (= 6.1.4.1)
activesupport (= 6.1.4.1)
activestorage (6.1.4.1)
actionpack (= 6.1.4.1)
activejob (= 6.1.4.1)
activerecord (= 6.1.4.1)
activesupport (= 6.1.4.1)
marcel (~> 1.0.0)
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)
marcel (~> 1.0)
mini_mime (>= 1.1.0)
activesupport (6.1.4.1)
activesupport (6.1.7.3)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 1.6, < 2)
minitest (>= 5.1)
tzinfo (~> 2.0)
zeitwerk (~> 2.3)
addressable (2.8.0)
public_suffix (>= 2.0.2, < 5.0)
addressable (2.8.4)
public_suffix (>= 2.0.2, < 6.0)
ast (2.4.2)
autoprefixer-rails (10.3.3.0)
autoprefixer-rails (10.4.13.0)
execjs (~> 2)
bcrypt (3.1.16-x86_64-linux-musl)
bcrypt (3.1.19-x86_64-linux-musl)
bcrypt_pbkdf (1.1.0-x86_64-linux-musl)
benchmark-ips (2.9.2)
benchmark-ips (2.12.0)
bindex (0.8.1-x86_64-linux-musl)
blazer (2.4.7)
blazer (2.6.5)
activerecord (>= 5)
chartkick (>= 3.2)
railties (>= 5)
safely_block (>= 0.1.1)
bootstrap (4.6.0)
bootstrap (4.6.2)
autoprefixer-rails (>= 9.1.0)
popper_js (>= 1.14.3, < 2)
popper_js (>= 1.16.1, < 2)
sassc-rails (>= 2.0.0)
brakeman (5.1.2)
brakeman (5.4.1)
builder (3.2.4)
capybara (2.18.0)
addressable
@ -113,22 +113,24 @@ GEM
rack (>= 1.0.0)
rack-test (>= 0.5.4)
xpath (>= 2.0, < 4.0)
chartkick (4.1.2)
childprocess (4.1.0)
chartkick (5.0.2)
climate_control (1.2.0)
coderay (1.1.3)
colorator (1.1.0)
commonmarker (0.21.2-x86_64-linux-musl)
ruby-enum (~> 0.5)
commonmarker (0.23.10-x86_64-linux-musl)
concurrent-ruby (1.2.2)
concurrent-ruby-ext (1.2.2-x86_64-linux-musl)
concurrent-ruby (= 1.2.2)
crass (1.0.6)
database_cleaner (2.0.1)
database_cleaner-active_record (~> 2.0.0)
database_cleaner-active_record (2.0.1)
database_cleaner (2.0.2)
database_cleaner-active_record (>= 2, < 3)
database_cleaner-active_record (2.1.0)
activerecord (>= 5.a)
database_cleaner-core (~> 2.0.0)
database_cleaner-core (2.0.1)
dead_end (3.1.0)
derailed_benchmarks (2.1.1)
date (3.3.3-x86_64-linux-musl)
dead_end (4.0.0)
derailed_benchmarks (2.1.2)
benchmark-ips (~> 2)
dead_end
get_process_mem (~> 0)
@ -140,29 +142,30 @@ GEM
rake (> 10, < 14)
ruby-statistics (>= 2.1)
thor (>= 0.19, < 2)
devise (4.8.0)
device_detector (1.1.1)
devise (4.9.2)
bcrypt (~> 3.0)
orm_adapter (~> 0.1)
railties (>= 4.1.0)
responders
warden (~> 1.2.3)
devise-i18n (1.10.1)
devise (>= 4.8.0)
devise_invitable (2.0.5)
devise-i18n (1.11.0)
devise (>= 4.9.0)
devise_invitable (2.0.8)
actionmailer (>= 5.0)
devise (>= 4.6)
distributed-press-api-client (0.2.2)
distributed-press-api-client (0.3.0rc0)
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)
dotenv (2.8.1)
dotenv-rails (2.8.1)
dotenv (= 2.8.1)
railties (>= 3.2)
down (5.2.4)
down (5.4.1)
addressable (~> 2.8)
dry-configurable (1.0.1)
dry-core (~> 1.0, < 2)
@ -176,65 +179,68 @@ GEM
concurrent-ruby (~> 1.0)
dry-core (~> 1.0, < 2)
zeitwerk (~> 2.6)
dry-schema (1.13.0)
dry-schema (1.13.1)
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-logic (>= 1.4, < 2)
dry-types (>= 1.7, < 2)
zeitwerk (~> 2.6)
dry-types (1.7.0)
dry-types (1.7.1)
concurrent-ruby (~> 1.0)
dry-core (~> 1.0, < 2)
dry-inflector (~> 1.0, < 2)
dry-logic (>= 1.4, < 2)
dry-core (~> 1.0)
dry-inflector (~> 1.0)
dry-logic (~> 1.4)
zeitwerk (~> 2.6)
ed25519 (1.2.4-x86_64-linux-musl)
ed25519 (1.3.0-x86_64-linux-musl)
em-websocket (0.5.3)
eventmachine (>= 0.12.9)
http_parser.rb (~> 0)
errbase (0.2.1)
erubi (1.10.0)
errbase (0.2.2)
erubi (1.12.0)
eventmachine (1.2.7-x86_64-linux-musl)
exception_notification (4.4.3)
actionmailer (>= 4.0, < 7)
activesupport (>= 4.0, < 7)
exception_notification (4.5.0)
actionmailer (>= 5.2, < 8)
activesupport (>= 5.2, < 8)
execjs (2.8.1)
factory_bot (6.2.0)
factory_bot (6.2.1)
activesupport (>= 5.0.0)
factory_bot_rails (6.2.0)
factory_bot (~> 6.2.0)
railties (>= 5.0.0)
fast_blank (1.0.1-x86_64-linux-musl)
fast_jsonparser (0.5.0-x86_64-linux-musl)
ffi (1.15.4-x86_64-linux-musl)
ffi (1.15.5-x86_64-linux-musl)
flamegraph (0.9.5)
forwardable-extended (2.6.0)
friendly_id (5.4.2)
friendly_id (5.5.0)
activerecord (>= 4.0.0)
get_process_mem (0.2.7)
ffi (~> 1.0)
globalid (0.6.0)
git_clone_url (2.0.0)
uri-ssh_git (>= 2.0)
globalid (1.1.0)
activesupport (>= 5.0)
groupdate (6.1.0)
groupdate (6.2.1)
activesupport (>= 5.2)
hairtrigger (0.2.24)
activerecord (>= 5.0, < 7)
hairtrigger (1.0.0)
activerecord (>= 6.0, < 8)
ruby2ruby (~> 2.4)
ruby_parser (~> 3.10)
haml (5.2.2)
temple (>= 0.8.0)
haml (6.1.2-x86_64-linux-musl)
temple (>= 0.8.2)
thor
tilt
haml-lint (0.999.999)
haml_lint
haml_lint (0.37.1)
haml (>= 4.0, < 5.3)
haml_lint (0.45.0)
haml (>= 4.0, < 6.2)
parallel (~> 1.10)
rainbow
rubocop (>= 0.50.0)
sysexits (~> 1.1)
hamlit (2.15.1-x86_64-linux-musl)
hamlit (3.0.3-x86_64-linux-musl)
temple (>= 0.8.2)
thor
tilt
@ -250,20 +256,21 @@ GEM
httparty (0.21.0)
mini_mime (>= 1.0.0)
multi_xml (>= 0.5.2)
i18n (1.8.11)
i18n (1.14.1)
concurrent-ruby (~> 1.0)
icalendar (2.7.1)
icalendar (2.8.0)
ice_cube (~> 0.16)
ice_cube (0.16.4)
image_processing (1.12.1)
image_processing (1.12.2)
mini_magick (>= 4.9.5, < 5)
ruby-vips (>= 2.0.17, < 3)
inline_svg (1.7.2)
inline_svg (1.9.0)
activesupport (>= 3.0)
nokogiri (>= 1.6)
jbuilder (2.11.3)
jbuilder (2.11.5)
actionview (>= 5.0.0)
activesupport (>= 5.0.0)
jekyll (4.2.1)
jekyll (4.2.2)
addressable (~> 2.4)
colorator (~> 1.0)
em-websocket (~> 0.5)
@ -278,243 +285,219 @@ GEM
rouge (~> 3.0)
safe_yaml (~> 1.0)
terminal-table (~> 2.0)
jekyll-commonmark (1.3.2)
commonmarker (~> 0.14, < 0.22)
jekyll (>= 3.7, < 5.0)
jekyll-commonmark (1.4.0)
commonmarker (~> 0.22)
jekyll-data (1.1.2)
jekyll (>= 3.3, < 5.0.0)
jekyll-dotenv (0.2.0)
dotenv (~> 2.7)
jekyll (~> 4)
jekyll-feed (0.15.1)
jekyll (>= 3.7, < 5.0)
jekyll-hardlinks (0.1.2)
jekyll (~> 4)
jekyll-ignore-layouts (0.1.2)
jekyll (~> 4)
jekyll-images (0.3.2)
jekyll-images (0.4.1)
jekyll (~> 4)
ruby-filemagic (~> 0.7)
ruby-vips (~> 2)
jekyll-include-cache (0.2.1)
jekyll (>= 3.7, < 5.0)
jekyll-linked-posts (0.4.2)
jekyll (~> 4)
jekyll-locales (0.1.13)
jekyll-lunr (0.3.0)
loofah (~> 2.4)
jekyll-order (0.1.4)
jekyll-relative-urls (0.0.6)
jekyll (~> 4)
jekyll-sass-converter (2.1.0)
jekyll-sass-converter (2.2.0)
sassc (> 2.0.1, < 3.0)
jekyll-seo-tag (2.7.1)
jekyll (>= 3.8, < 5.0)
jekyll-spree-client (0.1.19)
fast_blank (~> 1)
spree-api-client (>= 0.2.4)
jekyll-turbolinks (0.0.5)
jekyll (~> 4)
turbolinks-source (~> 5)
jekyll-unique-urls (0.1.1)
jekyll (~> 4)
jekyll-watch (2.2.1)
listen (~> 3.0)
jekyll-write-and-commit-changes (0.2.1)
jekyll (~> 4)
rugged (~> 1)
json (2.6.3-x86_64-linux-musl)
jwt (2.6.0)
kaminari (1.2.1)
kaminari (1.2.2)
activesupport (>= 4.1.0)
kaminari-actionview (= 1.2.1)
kaminari-activerecord (= 1.2.1)
kaminari-core (= 1.2.1)
kaminari-actionview (1.2.1)
kaminari-actionview (= 1.2.2)
kaminari-activerecord (= 1.2.2)
kaminari-core (= 1.2.2)
kaminari-actionview (1.2.2)
actionview
kaminari-core (= 1.2.1)
kaminari-activerecord (1.2.1)
kaminari-core (= 1.2.2)
kaminari-activerecord (1.2.2)
activerecord
kaminari-core (= 1.2.1)
kaminari-core (1.2.1)
kramdown (2.3.1)
kaminari-core (= 1.2.2)
kaminari-core (1.2.2)
kramdown (2.4.0)
rexml
kramdown-parser-gfm (1.1.0)
kramdown (~> 2.0)
launchy (2.5.0)
addressable (~> 2.7)
letter_opener (1.7.0)
launchy (~> 2.2)
liquid (4.0.3)
listen (3.1.5)
rb-fsevent (~> 0.9, >= 0.9.4)
rb-inotify (~> 0.9, >= 0.9.7)
ruby_dep (~> 1.2)
launchy (2.5.2)
addressable (~> 2.8)
letter_opener (1.8.1)
launchy (>= 2.2, < 3)
liquid (4.0.4)
listen (3.8.0)
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
loaf (0.10.0)
railties (>= 3.2)
lockbox (0.6.6)
lograge (0.11.2)
lockbox (1.2.0)
lograge (0.12.0)
actionpack (>= 4)
activesupport (>= 4)
railties (>= 4)
request_store (~> 1.0)
loofah (2.12.0)
loofah (2.21.3)
crass (~> 1.0.2)
nokogiri (>= 1.5.9)
mail (2.7.1)
nokogiri (>= 1.12.0)
mail (2.8.1)
mini_mime (>= 0.1.1)
net-imap
net-pop
net-smtp
marcel (1.0.2)
memory_profiler (1.0.0)
memory_profiler (1.0.1)
mercenary (0.4.0)
method_source (1.0.0)
mime-types (3.4.1)
mime-types-data (~> 3.2015)
mime-types-data (3.2021.1115)
mini_histogram (0.3.1)
mini_magick (4.11.0)
mini_magick (4.12.0)
mini_mime (1.1.2)
mini_portile2 (2.6.1)
minitest (5.14.4)
mobility (1.2.4)
mini_portile2 (2.8.2)
minitest (5.18.0)
mobility (1.2.9)
i18n (>= 0.6.10, < 2)
request_store (~> 1.0)
multi_xml (0.6.0)
net-ssh (6.1.0)
netaddr (2.0.5)
nio4r (2.5.8-x86_64-linux-musl)
nokogiri (1.12.5-x86_64-linux-musl)
mini_portile2 (~> 2.6.1)
racc (~> 1.4)
net-imap (0.3.4)
date
net-protocol
net-pop (0.1.2)
net-protocol
net-protocol (0.2.1)
timeout
net-smtp (0.3.3)
net-protocol
net-ssh (7.1.0)
netaddr (2.0.6)
nio4r (2.5.9-x86_64-linux-musl)
njalla-api-client (0.2.0)
dry-schema
httparty (~> 0.18)
nokogiri (1.15.4-x86_64-linux-musl)
mini_portile2 (~> 2.8.2)
racc (~> 1.4)
orm_adapter (0.5.0)
pairing_heap (3.0.0)
parallel (1.21.0)
parser (3.0.2.0)
pairing_heap (3.0.1)
parallel (1.23.0)
parser (3.2.2.1)
ast (~> 2.4.1)
pathutil (0.16.2)
forwardable-extended (~> 2.6)
pg (1.2.3-x86_64-linux-musl)
pg_search (2.3.5)
pg (1.5.3-x86_64-linux-musl)
pg_search (2.3.6)
activerecord (>= 5.2)
activesupport (>= 5.2)
popper_js (1.16.0)
prometheus_exporter (1.0.0)
popper_js (1.16.1)
prometheus_exporter (2.0.8)
webrick
pry (0.14.1)
pry (0.14.2)
coderay (~> 1.1)
method_source (~> 1.0)
public_suffix (4.0.6)
puma (5.5.2-x86_64-linux-musl)
public_suffix (5.0.3)
puma (6.3.1-x86_64-linux-musl)
nio4r (~> 2.0)
pundit (2.1.1)
pundit (2.3.0)
activesupport (>= 3.0.0)
racc (1.6.0-x86_64-linux-musl)
que (2.2.0)
rack (2.2.3)
rack-cors (1.1.1)
que (2.2.1)
racc (1.7.1-x86_64-linux-musl)
rack (2.2.7)
rack-cors (2.0.1)
rack (>= 2.0.0)
rack-mini-profiler (2.3.3)
rack-mini-profiler (3.1.0)
rack (>= 1.2.0)
rack-proxy (0.7.0)
rack-proxy (0.7.6)
rack
rack-test (1.1.0)
rack (>= 1.0, < 3)
rails (6.1.4.1)
actioncable (= 6.1.4.1)
actionmailbox (= 6.1.4.1)
actionmailer (= 6.1.4.1)
actionpack (= 6.1.4.1)
actiontext (= 6.1.4.1)
actionview (= 6.1.4.1)
activejob (= 6.1.4.1)
activemodel (= 6.1.4.1)
activerecord (= 6.1.4.1)
activestorage (= 6.1.4.1)
activesupport (= 6.1.4.1)
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)
bundler (>= 1.15.0)
railties (= 6.1.4.1)
railties (= 6.1.7.3)
sprockets-rails (>= 2.0.0)
rails-dom-testing (2.0.3)
activesupport (>= 4.2.0)
nokogiri (>= 1.6)
rails-html-sanitizer (1.4.2)
loofah (~> 2.3)
rails-i18n (6.0.0)
rails-html-sanitizer (1.5.0)
loofah (~> 2.19, >= 2.19.1)
rails-i18n (7.0.7)
i18n (>= 0.7, < 2)
railties (>= 6.0.0, < 7)
railties (>= 6.0.0, < 8)
rails_warden (0.6.0)
warden (>= 1.2.0)
railties (6.1.4.1)
actionpack (= 6.1.4.1)
activesupport (= 6.1.4.1)
railties (6.1.7.3)
actionpack (= 6.1.7.3)
activesupport (= 6.1.7.3)
method_source
rake (>= 0.13)
rake (>= 12.2)
thor (~> 1.0)
rainbow (3.0.0)
rainbow (3.1.1)
rake (13.0.6)
rb-fsevent (0.11.0)
rb-fsevent (0.11.2)
rb-inotify (0.10.1)
ffi (~> 1.0)
redis (4.5.1)
redis-actionpack (5.2.0)
actionpack (>= 5, < 7)
redis (4.8.1)
redis-actionpack (5.3.0)
actionpack (>= 5, < 8)
redis-rack (>= 2.1.0, < 3)
redis-store (>= 1.1.0, < 2)
redis-activesupport (5.2.1)
activesupport (>= 3, < 7)
redis-activesupport (5.3.0)
activesupport (>= 3, < 8)
redis-store (>= 1.3, < 2)
redis-rack (2.1.3)
redis-rack (2.1.4)
rack (>= 2.0.8, < 3)
redis-store (>= 1.2, < 2)
redis-rails (5.0.2)
redis-actionpack (>= 5.0, < 6)
redis-activesupport (>= 5.0, < 6)
redis-store (>= 1.2, < 2)
redis-store (1.9.0)
redis (>= 4, < 5)
regexp_parser (2.1.1)
request_store (1.5.0)
redis-store (1.9.2)
redis (>= 4, < 6)
regexp_parser (2.8.0)
request_store (1.5.1)
rack (>= 1.4)
responders (3.0.1)
actionpack (>= 5.0)
railties (>= 5.0)
responders (3.1.0)
actionpack (>= 5.2)
railties (>= 5.2)
rexml (3.2.5)
rgl (0.6.2)
rgl (0.6.3)
pairing_heap (>= 0.3.0)
rexml (~> 3.2, >= 3.2.4)
stream (~> 0.5.3)
rouge (3.26.1)
rubocop (1.23.0)
rouge (3.30.0)
rubocop (1.42.0)
json (~> 2.3)
parallel (~> 1.10)
parser (>= 3.0.0.0)
parser (>= 3.1.2.1)
rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 1.8, < 3.0)
rexml
rubocop-ast (>= 1.12.0, < 2.0)
rexml (>= 3.2.5, < 4.0)
rubocop-ast (>= 1.24.1, < 2.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 1.4.0, < 3.0)
rubocop-ast (1.13.0)
parser (>= 3.0.1.1)
rubocop-rails (2.12.4)
rubocop-ast (1.28.1)
parser (>= 3.2.1.0)
rubocop-rails (2.19.1)
activesupport (>= 4.2.0)
rack (>= 1.1)
rubocop (>= 1.7.0, < 2.0)
ruby-enum (0.9.0)
i18n
ruby-filemagic (0.7.2-x86_64-linux-musl)
ruby-progressbar (1.11.0)
ruby-statistics (3.0.0)
rubocop (>= 1.33.0, < 2.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)
ffi (~> 1.12)
ruby2ruby (2.4.4)
ruby2ruby (2.5.0)
ruby_parser (~> 3.1)
sexp_processor (~> 4.6)
ruby_dep (1.5.0)
ruby_parser (3.18.1)
ruby_parser (3.20.1)
sexp_processor (~> 4.16)
rubyzip (2.3.2)
rugged (1.2.0-x86_64-linux-musl)
rugged (1.5.0.1-x86_64-linux-musl)
safe_yaml (1.0.6)
safely_block (0.3.0)
errbase (>= 0.1.1)
@ -526,56 +509,55 @@ GEM
sprockets (> 3.0)
sprockets-rails
tilt
selenium-webdriver (4.1.0)
childprocess (>= 0.5, < 5.0)
selenium-webdriver (4.8.6)
rexml (~> 3.2, >= 3.2.5)
rubyzip (>= 1.2.2)
rubyzip (>= 1.2.2, < 3.0)
websocket (~> 1.0)
semantic_range (3.0.0)
sexp_processor (4.16.0)
sexp_processor (4.17.0)
simpleidn (0.2.1)
unf (~> 0.1.4)
sourcemap (0.1.1)
spree-api-client (0.2.4)
fast_blank (~> 1)
httparty (~> 0.18.0)
spring (2.1.1)
spring-watcher-listen (2.0.1)
spring (4.1.1)
spring-watcher-listen (2.1.0)
listen (>= 2.7, < 4.0)
spring (>= 1.2, < 3.0)
sprockets (4.0.2)
spring (>= 4)
sprockets (4.2.0)
concurrent-ruby (~> 1.0)
rack (> 1, < 3)
sprockets-rails (3.4.1)
rack (>= 2.2.4, < 4)
sprockets-rails (3.4.2)
actionpack (>= 5.2)
activesupport (>= 5.2)
sprockets (>= 3.0.0)
sqlite3 (1.4.2-x86_64-linux-musl)
stackprof (0.2.17-x86_64-linux-musl)
sutty-archives (2.5.4)
jekyll (>= 3.6, < 5.0)
sutty-liquid (0.7.4)
sqlite3 (1.6.3-x86_64-linux-musl)
mini_portile2 (~> 2.8.0)
stackprof (0.2.25-x86_64-linux-musl)
stream (0.5.5)
sutty-liquid (0.11.11)
fast_blank (~> 1.0)
jekyll (~> 4)
symbol-fstring (1.0.2-x86_64-linux-musl)
sysexits (1.2.0)
temple (0.8.2)
temple (0.10.1)
terminal-table (2.0.0)
unicode-display_width (~> 1.1, >= 1.1.1)
thor (1.1.0)
tilt (2.0.10)
timecop (0.9.4)
thor (1.2.2)
tilt (2.1.0)
timecop (0.9.6)
timeout (0.3.2)
turbolinks (5.2.1)
turbolinks-source (~> 5.2)
turbolinks-source (5.2.0)
tzinfo (2.0.4)
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
uglifier (4.2.0)
execjs (>= 0.3.0, < 3)
unf (0.1.4)
unf_ext
unf_ext (0.0.8-x86_64-linux-musl)
unf_ext (0.0.8.2-x86_64-linux-musl)
unicode-display_width (1.8.0)
validates_hostname (1.0.11)
uri-ssh_git (2.0.0)
validates_hostname (1.0.13)
activerecord (>= 3.0)
activesupport (>= 3.0)
warden (1.2.9)
@ -585,21 +567,21 @@ GEM
activemodel (>= 6.0.0)
bindex (>= 0.4.0)
railties (>= 6.0.0)
webpacker (5.4.3)
webpacker (5.4.4)
activesupport (>= 5.2)
rack-proxy (>= 0.6.1)
railties (>= 5.2)
semantic_range (>= 2.3.0)
webrick (1.7.0)
websocket-driver (0.7.5-x86_64-linux-musl)
webrick (1.8.1)
websocket (1.2.9)
websocket-driver (0.7.6-x86_64-linux-musl)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.5)
xpath (3.2.0)
nokogiri (~> 1.8)
zeitwerk (2.5.1)
zeitwerk (2.6.8)
PLATFORMS
ruby
x86_64-linux-musl
DEPENDENCIES
@ -614,10 +596,11 @@ DEPENDENCIES
concurrent-ruby-ext
database_cleaner
derailed_benchmarks
device_detector
devise
devise-i18n
devise_invitable
distributed-press-api-client (~> 0.2.3)
distributed-press-api-client (~> 0.3.0rc0)
dotenv-rails
down
ed25519
@ -625,9 +608,10 @@ DEPENDENCIES
exception_notification
factory_bot_rails
fast_blank
fast_jsonparser
fast_jsonparser (~> 0.5.0)
flamegraph
friendly_id
git_clone_url
hairtrigger
haml-lint
hamlit-rails
@ -637,14 +621,14 @@ DEPENDENCIES
image_processing
inline_svg
jbuilder (~> 2.5)
jekyll (~> 4.2)
jekyll-commonmark
jekyll (~> 4.2.0)
jekyll-commonmark (~> 1.4.0)
jekyll-data
jekyll-images
jekyll-include-cache
kaminari
letter_opener
listen (>= 3.0.5, < 3.2)
listen
loaf
lockbox
lograge
@ -652,7 +636,7 @@ DEPENDENCIES
mini_magick
mobility
net-ssh
njalla-api-client
njalla-api-client (~> 0.2.0)
nokogiri
pg
pg_search
@ -663,22 +647,23 @@ DEPENDENCIES
que
rack-cors
rack-mini-profiler
rails (~> 6)
rails (~> 6.1.0)
rails-i18n
rails_warden
redis
redis (~> 4.0)
redis-rails
rgl
rollups!
rubocop-rails
rubyzip
rugged
rugged (= 1.5.0.1)
safe_yaml
safely_block (~> 0.3.0)
sassc-rails
selenium-webdriver
selenium-webdriver (~> 4.8.0)
sourcemap
spring
spring-watcher-listen (~> 2.0.0)
spring-watcher-listen
sqlite3
stackprof
sutty-liquid (>= 0.7.3)
@ -688,12 +673,12 @@ DEPENDENCIES
turbolinks (~> 5)
uglifier (>= 1.3.0)
validates_hostname
web-console (>= 3.3.0)
web-console
webpacker
yaml_db!
RUBY VERSION
ruby 2.7.1p83
ruby 3.1.4p223
BUNDLED WITH
2.2.2
2.4.17

View file

@ -48,8 +48,6 @@ help: always ## Ayuda
@echo -e "\nArgumentos:\n"
@grep -E "^[a-z\-]+ \?=.*##" Makefile | sed -re "s/(.*) \?=.*##(.*)/\1;\2/" | column -s ";" -t | sed -re "s/^([^ ]+) /\x1B[38;5;197m\1\x1B[0m/"
assets: public/packs/manifest.json.br ## Compilar los assets
test: always ## Ejecutar los tests
$(MAKE) rake args="test RAILS_ENV=test $(args)"
@ -110,21 +108,13 @@ save: ## Subir la imagen Docker al nodo delegado
date +%F | xargs -I {} git tag -f $(container)-{}
@echo -e "\a"
ota-js: assets ## Actualizar Javascript en el nodo delegado
rsync -avi --delete-after --chown 1000:82 public/ root@$(delegate):/srv/sutty/srv/http/data/_$(public)/
ssh root@$(delegate) docker exec $(container) sh -c "cat /srv/http/tmp/puma.pid | xargs -r kill -USR2"
ota: ## Actualizar Rails en el nodo delegado
ssh $(delegate) git -C /srv/sutty/srv/http/panel.sutty.nl pull ; true
git push
ssh $(delegate) git -C /srv/sutty/srv/http/panel.sutty.nl pull
ssh $(delegate) git -C /srv/sutty/srv/http/panel.sutty.nl lfs prune
ssh $(delegate) chown -R 1000:82 /srv/sutty/srv/http/panel.sutty.nl
ssh $(delegate) docker exec $(container) rails reload
# Todos los archivos de assets. Si alguno cambia, se van a recompilar
# los assets que luego se suben al nodo delegado.
assets := package.json yarn.lock $(shell find app/assets/ app/javascript/ -type f)
public/packs/manifest.json.br: $(assets)
$(hain) 'PANEL_URL=https://panel.sutty.nl RAILS_ENV=production NODE_ENV=production bundle exec rake assets:precompile assets:clean'
# Correr un test en particular por ejemplo
# `make test/models/usuarie_test.rb`
tests := $(shell find test/ -name "*_test.rb")

View file

@ -10,7 +10,7 @@ module Api
# solo si la API key es verificable. Del otro lado siempre
# respondemos con lo mismo.
def create
if site&.airbrake_valid? airbrake_token
if (site&.airbrake_valid? airbrake_token) && !detected_device.bot?
BacktraceJob.perform_later site_id: params[:site_id],
params: airbrake_params.to_h
end
@ -34,6 +34,11 @@ module Api
def airbrake_token
@airbrake_token ||= params[:key]
end
# @return [DeviceDetector]
def detected_device
@detected_device ||= DeviceDetector.new(request.headers['User-Agent'], request.headers)
end
end
end
end

View file

@ -0,0 +1,77 @@
# 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

@ -59,7 +59,11 @@ class ApplicationController < ActionController::Base
#
# @return [String,Symbol]
def current_locale
session[:locale] = params[:change_locale_to] if params[:change_locale_to].present?
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
end

View file

@ -22,7 +22,12 @@ class BuildStatsController < ApplicationController
@table = site.deployment_list.map do |deploy|
type = deploy.class.name.underscore
urls = deploy.respond_to?(:urls) ? deploy.urls : [deploy.url].compact
urls = deploy.urls.map do |url|
URI.parse(url)
rescue URI::Error
nil
end.compact
urls = [nil] if urls.empty?
build_stat = deploy.build_stats.where(status: true).last
seconds = build_stat&.seconds || 0

View file

@ -5,6 +5,7 @@ class EnvController < ActionController::Base
def index
@site = Site.find_by_name('panel')
stale? @site
stale? @site if @site
end
end

View file

@ -24,6 +24,7 @@ class PostsController < ApplicationController
# Todos los artículos de este sitio para el idioma actual
@posts = site.indexed_posts.where(locale: locale)
@posts = @posts.page(filter_params.delete(:page)) if site.pagination
# De este tipo
@posts = @posts.where(layout: filter_params[:layout]) if filter_params[:layout]
# Que estén dentro de la categoría
@ -154,7 +155,7 @@ class PostsController < ApplicationController
#
# @return [Hash]
def filter_params
@filter_params ||= params.permit(:q, :category, :layout).to_hash.select do |_, v|
@filter_params ||= params.permit(:q, :category, :layout, :page).to_hash.select do |_, v|
v.present?
end.transform_keys(&:to_sym)
end

View file

@ -57,6 +57,7 @@ class SitesController < ApplicationController
usuarie: current_usuarie)
if service.update.valid?
flash[:notice] = I18n.t('sites.update.post')
redirect_to site_posts_path(site, locale: site.default_locale)
else
render 'edit'

View file

@ -2,11 +2,21 @@
import { Notifier } from '@airbrake/browser'
window.airbrake = new Notifier({
projectId: window.env.AIRBRAKE_SITE_ID,
projectKey: window.env.AIRBRAKE_API_KEY,
try {
window.airbrake = new Notifier({
projectId: window.env.AIRBRAKE_PROJECT_ID,
projectKey: window.env.AIRBRAKE_PROJECT_KEY,
host: window.env.PANEL_URL
})
});
console.originalError = console.error;
console.error = (...e) => {
window.airbrake.notify(e.join(" "));
return console.originalError(...e);
};
} catch(e) {
console.error(e);
}
import 'core-js/stable'
import 'regenerator-runtime/runtime'

View file

@ -51,7 +51,11 @@ class DeployJob < ApplicationJob
status = d.deploy(output: @output)
seconds = d.build_stats.last.try(:seconds) || 0
size = d.size
urls = d.respond_to?(:urls) ? d.urls : [d.url].compact
urls = d.urls.map do |url|
URI.parse url
rescue URI::Error
nil
end.compact
rescue StandardError => e
status = false
seconds ||= 0

13
app/jobs/git_pull_job.rb Normal file
View file

@ -0,0 +1,13 @@
# frozen_string_literal: true
# Permite traer los cambios desde webhooks
class GitPullJob < ApplicationJob
# @param :site [Site]
# @param :usuarie [Usuarie]
# @param :message [String]
# @return [nil]
def perform(site, usuarie, message)
site.repository.merge(usuarie, message) if site.repository.fetch&.positive?
end
end

11
app/jobs/git_push_job.rb Normal file
View file

@ -0,0 +1,11 @@
# frozen_string_literal: true
# Permite pushear los cambios cada vez que se
# hacen commits en un sitio
class GitPushJob < ApplicationJob
# @param :site [Site]
# @return [nil]
def perform(site)
site.repository.push if site.repository.origin
end
end

View file

@ -8,9 +8,9 @@ module ExceptionNotifier
# Recibe la excepción y empieza la tarea de notificación en segundo
# plano.
#
# @param [Exception]
# @param [Hash]
def call(exception, **options)
# @param :exception [Exception]
# @param :options [Hash]
def call(exception, options, &block)
case exception
when BacktraceJob::BacktraceException
GitlabNotifierJob.perform_later(exception, **options)

View file

@ -52,7 +52,7 @@ class DeployMailer < ApplicationMailer
t << (row.map do |k, v|
case k
when :seconds then v[:human]
when :urls then url
when :urls then url.to_s
else v
end
end)

View file

@ -23,6 +23,11 @@ class Deploy < ApplicationRecord
raise NotImplementedError
end
# @return [Array]
def urls
[url].compact
end
def limit
raise NotImplementedError
end
@ -55,6 +60,22 @@ class Deploy < ApplicationRecord
@gems_dir ||= Rails.root.join('_storage', 'gems', site.name)
end
# Un entorno que solo tiene lo que necesitamos
#
# @return [Hash]
def env
# XXX: This doesn't support Windows paths :B
paths = [File.dirname(`which bundle`), '/usr/local/bin', '/usr/bin', '/bin']
# Las variables de entorno extra no pueden superponerse al local.
extra_env.merge({
'HOME' => home_dir,
'PATH' => paths.join(':'),
'JEKYLL_ENV' => Rails.env,
'LANG' => ENV['LANG'],
})
end
# Corre un comando, lo registra en la base de datos y devuelve el
# estado.
#
@ -65,8 +86,7 @@ class Deploy < ApplicationRecord
lines = []
time_start
Dir.chdir(site.path) do
Open3.popen2e(env, cmd, unsetenv_others: true) do |_, o, t|
Open3.popen2e(env, cmd, unsetenv_others: true, chdir: site.path) do |_, o, t|
# TODO: Enviar a un websocket para ver el proceso en vivo?
Thread.new do
o.each do |line|
@ -81,7 +101,6 @@ class Deploy < ApplicationRecord
r = t.value
end
end
time_stop
build_stats.create action: readable_cmd(cmd),
@ -100,6 +119,11 @@ class Deploy < ApplicationRecord
@local_env ||= {}
end
# Devuelve opciones para jekyll build
#
# @return [String,nil]
def flags_for_build(**args); end
# Trae todas las dependencias
#
# @return [Array]
@ -109,6 +133,21 @@ class Deploy < ApplicationRecord
private
# Escribe el contenido en un archivo temporal y ejecuta el bloque
# provisto con el archivo como parámetro
#
# @param :content [String]
def with_tempfile(content, &block)
Tempfile.create(SecureRandom.hex) do |file|
file.write content.to_s
file.rewind
file.close
# @yieldparam :file [File]
yield file
end
end
# @param [String]
# @return [String]
def readable_cmd(cmd)
@ -119,7 +158,14 @@ class Deploy < ApplicationRecord
@deploy_local ||= site.deploys.find_by(type: 'DeployLocal')
end
def non_local_deploys
@non_local_deploys ||= site.deploys.where.not(type: 'DeployLocal')
# Consigue todas las variables de entorno configuradas por otros
# deploys.
#
# @return [Hash]
def extra_env
@extra_env ||=
site.deployment_list.reduce({}) do |extra, deploy|
extra.merge deploy.local_env
end
end
end

View file

@ -52,7 +52,12 @@ class DeployDistributedPress < Deploy
end
end
begin
status = c.publish(publishing_site, deploy_local.destination)
rescue DistributedPress::V1::Error => e
ExceptionNotifier.notify_exception(e, data: { site: site.name })
status = false
end
if status
self.remote_info[:distributed_press] = c.show(publishing_site).to_h

View file

@ -62,32 +62,24 @@ class DeployLocal < Deploy
FileUtils.rm_rf(File.join(site.path, '.jekyll-cache'))
end
private
def mkdir
FileUtils.mkdir_p destination
end
# Un entorno que solo tiene lo que necesitamos
# Opciones necesarias para la compilación del sitio
#
# @return [Hash]
def env
# XXX: This doesn't support Windows paths :B
paths = [File.dirname(`which bundle`), '/usr/local/bin', '/usr/bin', '/bin']
# Las variables de entorno extra no pueden superponerse al local.
extra_env.merge({
'HOME' => home_dir,
'PATH' => paths.join(':'),
def local_env
@local_env ||= {
'SPREE_API_KEY' => site.tienda_api_key,
'SPREE_URL' => site.tienda_url,
'AIRBRAKE_PROJECT_ID' => site.id.to_s,
'AIRBRAKE_PROJECT_KEY' => site.airbrake_api_key,
'JEKYLL_ENV' => Rails.env,
'LANG' => ENV['LANG'],
'YARN_CACHE_FOLDER' => yarn_cache_dir,
'GEMS_SOURCE' => ENV['GEMS_SOURCE']
})
}
end
private
def mkdir
FileUtils.mkdir_p destination
end
def yarn_cache_dir
@ -138,11 +130,20 @@ class DeployLocal < Deploy
end
def bundle(output: false)
run %(bundle install --deployment --no-cache --path="#{gems_dir}" --clean --without test development), output: output
run %(bundle config set --local clean 'true'), output: output
run %(bundle config set --local deployment 'true'), output: output
run %(bundle config set --local path '#{gems_dir}'), output: output
run %(bundle config set --local without 'test development'), output: output
run %(bundle config set --local cache_all 'false'), output: output
run %(bundle install), output: output
end
def jekyll_build(output: false)
run %(bundle exec jekyll build --trace --profile --destination "#{escaped_destination}"), output: output
with_tempfile(site.private_key_pem) do |file|
flags = extra_flags(private_key: file)
run %(bundle exec jekyll build --trace --profile #{flags} --destination "#{escaped_destination}"), output: output
end
end
# no debería haber espacios ni caracteres especiales, pero por si
@ -156,17 +157,13 @@ class DeployLocal < Deploy
FileUtils.rm_rf destination
end
# Consigue todas las variables de entorno configuradas por otros
# deploys.
# Genera opciones extra desde los otros deploys
#
# @deprecated Solo tenía sentido para Distributed Press v0
# @return [Hash]
def extra_env
@extra_env ||=
non_local_deploys.reduce({}) do |extra_env, deploy|
extra_env.tap do |e|
e.merge! deploy.local_env
end
end
# @param :args [Hash]
# @return [String]
def extra_flags(**args)
site.deployment_list.map do |deploy|
deploy.flags_for_build(**args)
end.compact.join(' ')
end
end

View file

@ -0,0 +1,55 @@
# frozen_string_literal: true
require 'distributed_press/v1/social/client'
# Publicar novedades al Fediverso
class DeploySocialDistributedPress < Deploy
# Solo luego de publicar remotamente
DEPENDENCIES = %i[deploy_distributed_press deploy_rsync deploy_full_rsync]
# Envía las notificaciones
def deploy(output: false)
with_tempfile(site.private_key_pem) do |file|
key = Shellwords.escape file.path
dest = Shellwords.escape destination
run %(bundle exec jekyll notify --trace --key #{key} --destination "#{dest}"), output: output
end
end
# Igual que DeployLocal
#
# @return [String]
def destination
File.join(Rails.root, '_deploy', site.hostname)
end
# Solo uno
#
# @return [Integer]
def limit
1
end
# Espacio ocupado, pero no podemos calcularlo
#
# @return [Integer]
def size
0
end
# El perfil de actor
#
# @return [String,nil]
def url
site.data.dig('activity_pub', 'actor')
end
# Genera la opción de llave privada para jekyll build
#
# @params :args [Hash]
# @return [String]
def flags_for_build(**args)
"--key #{Shellwords.escape args[:private_key].path}"
end
end

View file

@ -4,8 +4,12 @@
#
# Esto es increíblemente difícil de lograr que salga bien!
class MetadataBoolean < MetadataTemplate
# El valor por defecto es una versión booleana de lo que diga (o no
# diga) el esquema
#
# @return [Boolean]
def default_value
false
!!super
end
# Los checkboxes son especiales porque la especificación de HTML

View file

@ -0,0 +1,19 @@
# frozen_string_literal: true
# Fecha y hora de creación
class MetadataCreatedAt < MetadataTemplate
# Por defecto la hora actual, pero por retrocompatibilidad, queremos
# la fecha de publicación
def default_value
if post.date.value.to_date < Time.now.to_date
post.date.value
else
Time.now
end
end
# Nunca cambia
def value=(new_value)
value
end
end

View file

@ -34,7 +34,7 @@ class MetadataRelatedPosts < MetadataArray
end
def title(post)
"#{post&.title&.value || post&.slug&.value} (#{post.layout.humanized_name})"
"#{post&.title&.value || post&.slug&.value} #{post&.date&.value.strftime('%F')} (#{post.layout.humanized_name})"
end
# Encuentra el filtro

View file

@ -202,7 +202,7 @@ MetadataTemplate = Struct.new(:site, :document, :name, :label, :type,
def allowed_attributes
@allowed_attributes ||= %w[style href src alt controls data-align data-multimedia data-multimedia-inner id
name].freeze
name start].freeze
end
def allowed_tags

View file

@ -12,7 +12,7 @@ class Post
DEFAULT_ATTRIBUTES = %i[site document layout].freeze
# Otros atributos que no vienen en los metadatos
PRIVATE_ATTRIBUTES = %i[path slug attributes errors].freeze
PUBLIC_ATTRIBUTES = %i[lang date uuid].freeze
PUBLIC_ATTRIBUTES = %i[lang date uuid created_at].freeze
ATTR_SUFFIXES = %w[? =].freeze
attr_reader :attributes, :errors, :layout, :site, :document
@ -217,6 +217,11 @@ class Post
post: self, required: true)
end
# La fecha de creación inmodificable del post
def created_at
@metadata[:created_at] ||= MetadataCreatedAt.new(document: document, site: site, layout: layout, name: :created_at, type: :created_at, post: self, required: true)
end
# Detecta si es un atributo válido o no, a partir de la tabla de la
# plantilla
def attribute?(mid)
@ -267,6 +272,7 @@ class Post
# Y que no se procese liquid
yaml['liquid'] = false
yaml['usuaries'] = usuaries.map(&:id).uniq
yaml['created_at'] = created_at.value
yaml['last_modified_at'] = modified_at
"#{yaml.to_yaml}---\n\n#{body}"

View file

@ -14,6 +14,8 @@ class Rol < ApplicationRecord
validates_inclusion_of :rol, in: ROLES
before_save :add_token_if_missing!
def invitade?
rol == INVITADE
end
@ -25,4 +27,11 @@ class Rol < ApplicationRecord
def self.role?(rol)
ROLES.include? rol
end
private
# Asegurarse que tenga un token
def add_token_if_missing!
self.token ||= SecureRandom.hex(64)
end
end

View file

@ -10,6 +10,7 @@ class Site < ApplicationRecord
include Site::DeployDependencies
include Site::BuildStats
include Site::LayoutOrdering
include Site::SocialDistributedPress
include Tienda
# Cifrar la llave privada que cifra y decifra campos ocultos. Sutty
@ -18,10 +19,6 @@ class Site < ApplicationRecord
# protege de acceso al panel de Sutty!
encrypts :private_key
# TODO: Hacer que los diferentes tipos de deploy se auto registren
# @see app/services/site_service.rb
DEPLOYS = %i[local private www zip hidden_service distributed_press].freeze
validates :name, uniqueness: true, hostname: {
allow_root_label: true
}
@ -447,6 +444,10 @@ class Site < ApplicationRecord
find_by(name: "#{Site.domain}.")
end
def self.one_at_a_time
@@one_at_a_time ||= Thread::Mutex.new
end
def reset
@read = false
@layouts = nil
@ -474,6 +475,9 @@ class Site < ApplicationRecord
return if jekyll?
Rugged::Repository.clone_at ENV['SKEL_SUTTY'], path
# Necesita un bloque
repository.rugged.remotes.rename('origin', 'upstream') {}
end
# Elimina el directorio del sitio
@ -553,8 +557,10 @@ class Site < ApplicationRecord
end
def run_in_path(&block)
Site.one_at_a_time.synchronize do
Dir.chdir path, &block
end
end
# Instala las gemas cuando es necesario:
#

View file

@ -37,7 +37,7 @@ class Site
author = GitAuthor.new email: "sutty@#{Site.domain}", name: 'Sutty'
repository.commit(file: modified,
repository.commit(add: modified,
message: I18n.t('sites.find_and_replace'),
usuarie: author)
end

View file

@ -8,8 +8,6 @@ class Site
extend ActiveSupport::Concern
included do
# TODO: Debería ser un Job?
after_create :index_posts!
has_many :indexed_posts, dependent: :destroy
def index_posts!

View file

@ -29,7 +29,7 @@ class Site
# Obtiene el origin
#
# @return [Rugged::Remote]
# @return [Rugged::Remote, nil]
def origin
@origin ||= rugged.remotes.find do |remote|
remote.name == 'origin'
@ -54,7 +54,7 @@ class Site
# Incorpora los cambios en el repositorio actual
#
# @return [Rugged::Commit]
def merge(usuarie)
def merge(usuarie, message = I18n.t('sites.fetch.merge.message'))
merge = rugged.merge_commits(head_commit, remote_head_commit)
# No hacemos nada si hay conflictos, pero notificarnos
@ -69,12 +69,16 @@ class Site
.create(rugged, update_ref: 'HEAD',
parents: [head_commit, remote_head_commit],
tree: merge.write_tree(rugged),
message: I18n.t('sites.fetch.merge.message'),
message: message,
author: author(usuarie), committer: committer)
# Forzamos el checkout para mover el HEAD al último commit y
# escribir los cambios
rugged.checkout 'HEAD', strategy: :force
git_sh("git", "lfs", "fetch", "origin", default_branch)
# reemplaza los pointers por los archivos correspondientes
git_sh("git", "lfs", "checkout")
commit
end
@ -114,14 +118,21 @@ class Site
end
# Guarda los cambios en git
def commit(file:, usuarie:, message:, remove: false)
file = [file] unless file.respond_to? :each
#
# @param :add [Array] Archivos a agregar
# @param :rm [Array] Archivos a eliminar
# @param :usuarie [Usuarie] Quién hace el commit
# @param :message [String] Mensaje
def commit(add: [], rm: [], usuarie:, message:)
# Cargar el árbol actual
rugged.index.read_tree rugged.head.target.tree
file.each do |f|
remove ? rm(f) : add(f)
add.each do |file|
rugged.index.add(relativize(file))
end
rm.each do |file|
rugged.index.remove(relativize(file))
end
# Escribir los cambios para que el repositorio se vea tal cual
@ -142,41 +153,58 @@ class Site
{ name: 'Sutty', email: "sutty@#{Site.domain}", time: Time.now }
end
def add(file)
rugged.index.add(relativize(file))
end
def rm(file)
rugged.index.remove(relativize(file))
end
# Garbage collection
#
# @return [Boolean]
def gc
env = { 'PATH' => '/usr/bin', 'LANG' => ENV['LANG'], 'HOME' => path }
cmd = 'git gc'
r = nil
Dir.chdir(path) do
Open3.popen2e(env, cmd, unsetenv_others: true) do |_, _, t|
r = t.value
end
git_sh("git", "gc")
end
r&.success?
# Pushea cambios al repositorio remoto
#
# @param :remote [Rugged::Remote]
# @return [Boolean, nil]
def push(remote = origin)
remote.push(rugged.head.canonical_name, credentials: credentials_for(remote))
git_sh('git', 'lfs', 'push', remote.name, default_branch)
end
private
# @deprecated
def credentials
@credentials ||= credentials_for(origin)
end
# Si Sutty tiene una llave privada de tipo ED25519, devuelve las
# credenciales necesarias para trabajar con repositorios remotos.
#
# @param :remote [Rugged::Remote]
# @return [Nil, Rugged::Credentials::SshKey]
def credentials
def credentials_for(remote)
return unless File.exist? private_key
@credentials ||= Rugged::Credentials::SshKey.new username: 'git', publickey: public_key, privatekey: private_key
Rugged::Credentials::SshKey.new username: username_for(remote), publickey: public_key, privatekey: private_key
end
# Obtiene el nombre de usuario para el repositorio remoto, por
# defecto git
#
# @param :remote [Rugged::Remote]
# @return [String]
def username_for(remote)
username = parse_url(remote.url)&.user if remote.respond_to? :url
username || 'git'
end
# @param :url [String]
# @return [URI, nil]
def parse_url(url)
GitCloneUrl.parse(url)
rescue URI::Error => e
ExceptionNotifier.notify_exception(e, data: { path: path, url: url })
nil
end
# @return [String]
@ -192,5 +220,20 @@ class Site
def relativize(file)
Pathname.new(file).relative_path_from(Pathname.new(path)).to_s
end
# Ejecuta un comando de git
#
# @param :args [Array]
# @return [Boolean]
def git_sh(*args)
env = { 'PATH' => '/usr/bin', 'LANG' => ENV['LANG'], 'HOME' => path }
r = nil
Open3.popen2e(env, *args, unsetenv_others: true, chdir: path) do |_, _, t|
r = t.value
end
r&.success?
end
end
end

View file

@ -0,0 +1,23 @@
# frozen_string_literal: true
class Site
# Agrega soporte para Social Distributed Press en los sitios
module SocialDistributedPress
extend ActiveSupport::Concern
included do
encrypts :private_key_pem
before_save :generate_private_key_pem!, unless: :private_key_pem?
private
# Genera la llave privada y la almacena
#
# @return [nil]
def generate_private_key_pem!
self.private_key_pem ||= DistributedPress::V1::Social::Client.new(public_key_url: nil, key_size: 2048).private_key.export
end
end
end
end

View file

@ -10,6 +10,7 @@ class Usuarie < ApplicationRecord
validates_uniqueness_of :email
validates_with EmailAddress::ActiveRecordValidator, field: :email
validate :locale_available!
before_create :lang_from_locale!
before_update :remove_confirmation_invitation_inconsistencies!
@ -78,4 +79,15 @@ class Usuarie < ApplicationRecord
self.invitation_accepted_at ||= Time.now.utc
end
end
# Muestra un error si el idioma no está disponible al cambiar el
# idioma de la cuenta.
#
# @return [nil]
def locale_available!
return if I18n.locale_available? self.lang
errors.add(:lang, I18n.t('activerecord.errors.models.usuarie.attributes.lang.not_available'))
nil
end
end

View file

@ -22,7 +22,7 @@ class LfsObjectService
Site::Writer.new(site: site, file: path, content: pointer).save
# Commitear el pointer
site.repository.commit(file: path, usuarie: author, message: File.basename(path))
site.repository.commit(add: [path], usuarie: author, message: File.basename(path))
# Eliminar el pointer
FileUtils.rm(path)

View file

@ -16,7 +16,7 @@ PostService = Struct.new(:site, :usuarie, :post, :params, keyword_init: true) do
post.slug.value = p[:slug] if p[:slug].present?
end
commit(action: :created, file: update_related_posts) if post.update(post_params)
commit(action: :created, add: update_related_posts) if post.update(post_params)
update_site_license!
@ -34,7 +34,7 @@ PostService = Struct.new(:site, :usuarie, :post, :params, keyword_init: true) do
# Los artículos anónimos siempre son borradores
params[:draft] = true
commit(action: :created) if post.update(anon_post_params)
commit(action: :created, add: [post.path.absolute]) if post.update(anon_post_params)
post
end
@ -42,11 +42,17 @@ PostService = Struct.new(:site, :usuarie, :post, :params, keyword_init: true) do
post.usuaries << usuarie
params[:post][:draft] = true if site.invitade? usuarie
# Eliminar ("mover") el archivo si cambió de ubicación.
if post.update(post_params)
rm = []
rm << post.path.value_was if post.path.changed?
# Es importante que el artículo se guarde primero y luego los
# relacionados.
commit(action: :updated, file: update_related_posts) if post.update(post_params)
commit(action: :updated, add: update_related_posts, rm: rm)
update_site_license!
end
# Devolver el post aunque no se haya salvado para poder rescatar los
# errores
@ -56,7 +62,7 @@ PostService = Struct.new(:site, :usuarie, :post, :params, keyword_init: true) do
def destroy
post.destroy!
commit(action: :destroyed) if post.destroyed?
commit(action: :destroyed, rm: [post.path.absolute]) if post.destroyed?
post
end
@ -85,17 +91,19 @@ PostService = Struct.new(:site, :usuarie, :post, :params, keyword_init: true) do
# TODO: Implementar transacciones!
posts.save_all(validate: false) &&
commit(action: :reorder, file: files)
commit(action: :reorder, add: files)
end
private
def commit(action:, file: nil)
site.repository.commit(file: file || post.path.absolute,
def commit(action:, add: [], rm: [])
site.repository.commit(add: add,
rm: rm,
usuarie: usuarie,
remove: action == :destroyed,
message: I18n.t("post_service.#{action}",
title: post&.title&.value))
GitPushJob.perform_later(site)
end
# Solo permitir cambiar estos atributos de cada articulo

View file

@ -33,6 +33,7 @@ SiteService = Struct.new(:site, :usuarie, :params, keyword_init: true) do
add_licencias &&
add_code_of_conduct &&
add_privacy_policy &&
site.index_posts! &&
deploy
end
@ -54,9 +55,8 @@ SiteService = Struct.new(:site, :usuarie, :params, keyword_init: true) do
# Genera los Deploy necesarios para el sitio a menos que ya los tenga.
def build_deploys
Site::DEPLOYS.map { |deploy| "Deploy#{deploy.to_s.camelcase}" }
.each do |deploy|
next if site.deploys.find_by type: deploy
Deploy.subclasses.each do |deploy|
next if site.deploys.find_by type: deploy.name
site.deploys.build type: deploy
end
@ -94,9 +94,11 @@ SiteService = Struct.new(:site, :usuarie, :params, keyword_init: true) do
def commit_config(action:)
site.repository
.commit(usuarie: usuarie,
file: site.config.path,
add: [site.config.path],
message: I18n.t("site_service.#{action}",
name: site.name))
GitPushJob.perform_later(site)
end
def add_role(temporal: true, rol: 'invitade')

View file

@ -14,7 +14,7 @@
- row[:urls].each do |url|
%tr
%th{ scope: 'row' }= row[:title]
%td= link_to_if url.present?, url, url, class: 'word-break-all'
%td= link_to_if (url.present? && url.scheme.present?), url.to_s, url.to_s, class: 'word-break-all'
%td
%time{ datetime: row[:seconds][:machine] }= row[:seconds][:human]
%td= row[:size]

View file

@ -13,7 +13,7 @@
%tr
%td= row[:title]
%td= row[:status]
%td= link_to_if url.present?, url, url
%td= link_to_if (url.present? && url.scheme.present?), url.to_s, url.to_s
%td
%time{ datetime: row[:seconds][:machine] }= row[:seconds][:human]
%td= row[:size]

View file

@ -0,0 +1,21 @@
-# Publicar a la web distribuida
.row
.col
= deploy.hidden_field :id
= deploy.hidden_field :type
.custom-control.custom-switch
-#
El checkbox invierte la lógica de destrucción porque queremos
crear el deploy si está activado y destruirlo si está
desactivado.
= deploy.check_box :_destroy,
{ checked: deploy.object.persisted?, class: 'custom-control-input' },
'0', '1'
= deploy.label :_destroy, class: 'custom-control-label' do
%h3= t('.title')
= sanitize_markdown t('.help'),
tags: %w[p strong em a]
%hr/

View file

@ -1,4 +1,5 @@
= cache @site do
- if @site
= cache @site do
:plain
window.env = {
AIRBRAKE_SITE_ID: #{@site.id},

View file

@ -1,4 +1,4 @@
- flash.each do |type, message|
- unless type == 'js'
= render 'bootstrap/alert' do
= message
= sanitize_markdown message, tags: %w[a strong em]

View file

@ -0,0 +1,4 @@
%tr{ id: attribute }
%th= post_label_t(attribute, post: post)
%td{ dir: dir, lang: locale }
%time{ datetime: metadata.value.xmlschema }= l metadata.value

View file

@ -0,0 +1 @@
-# nada

View file

@ -84,7 +84,10 @@
%button.btn{ data: { action: 'reorder#top' } }= t('posts.reorder.top')
%button.btn{ data: { action: 'reorder#bottom' } }= t('posts.reorder.bottom')
- if @site.pagination
%div
= link_to_prev_page @posts, t('posts.prev'), class: 'btn'
= link_to_next_page @posts, t('posts.next'), class: 'btn'
%tbody
- dir = @site.data.dig(params[:locale], 'dir')
- size = @posts.size

View file

@ -46,6 +46,7 @@
.invalid-feedback= site.errors.messages[:description].join(', ')
%hr/
- unless site.persisted?
.form-group#design_id
%h2= t('.design.title')
%p.lead= t('.help.design')

View file

@ -142,7 +142,7 @@ Rails.application.configure do
}
config.action_mailer.default_options = { from: ENV.fetch('DEFAULT_FROM', "noreply@sutty.nl") }
config.middleware.use ExceptionNotification::Rack, gitlab: {}, ignore_exceptions: (['DeployJob::DeployAlreadyRunningException'] + ExceptionNotifier.ignored_exceptions)
config.middleware.use ExceptionNotification::Rack, gitlab: {}, ignore_exceptions: ['DeployJob::DeployAlreadyRunningException']
Rails.application.routes.default_url_options[:host] = "panel.#{ENV.fetch('SUTTY', 'sutty.nl')}"
Rails.application.routes.default_url_options[:protocol] = 'https'

View file

@ -91,6 +91,15 @@ module Jekyll
spec.name == name
end
unless spec
I18n.with_locale(locale) do
raise Jekyll::Errors::InvalidThemeName, I18n.t('activerecord.errors.models.site.attributes.design_id.missing_gem', theme: name)
rescue Jekyll::Errors::InvalidThemeName => e
ExceptionNotifier.notify_exception(e, data: { theme: name, site: File.basename(site.source) })
raise
end
end
ruby_version = Gem::Version.new(RUBY_VERSION)
ruby_version.canonical_segments[2] = 0
base_path = Rails.root.join('_storage', 'gems', File.basename(site.source), 'ruby',
@ -114,6 +123,11 @@ module Jekyll
private
def gemspec; end
# @return [Symbol]
def locale
@locale ||= (site.config['locale'] || site.config['lang'] || I18n.locale).to_sym
end
end
# No necesitamos los archivos de la plantilla

View file

@ -0,0 +1,3 @@
DeviceDetector.configure do |config|
config.max_cache_keys = 5_000 # to check if not too much
end

View file

@ -4,5 +4,5 @@ ActiveJob::Serializers.add_serializers ActiveJob::Serializers::ExceptionSerializ
# Notificar los errores
Que.error_notifier = proc do |error, job|
ExceptionNotifier.notify_exception(error, data: (job || {}))
ExceptionNotifier.notify_exception(error, data: (job.dup || {}))
end

View file

@ -123,6 +123,10 @@ en:
title: Distributed Web
success: Success!
error: Error
deploy_social_distributed_press:
title: Fediverse
success: Success!
error: Error
deploy_reindex:
title: Reindex
success: Success!
@ -191,9 +195,14 @@ en:
deploys:
deploy_local_presence: 'We need to be build the site!'
design_id:
missing_gem: "Site is configured to use %{theme} theme, but the corresponding gem is missing from Gemfile"
layout_incompatible:
error: "Design can't be changed because there are posts with incompatible layouts"
help: "Your site has posts with layouts only compatible with the current design. If you change it, the site won't work as you expect. If you're trying out designs, you can delete posts in the following incompatible layouts:: %{layouts}."
usuarie:
attributes:
lang:
not_available: "This language is not yet available, would you help us by translating Sutty into it?"
errors:
argument_error: 'Argument `%{argument}` must be an instance of %{class}'
unknown_locale: 'Unknown %{locale} locale'
@ -307,6 +316,14 @@ en:
indefinitely.
[Learn more](https://sutty.nl/learn-more-about-publish-to-dweb-functionality/)
deploy_social_distributed_press:
title: 'Publish on the Fediverse'
help: |
By using the ActivityPub protocol, people on the Fediverse
([Mastodon](https://joinmastodon.org/servers),
[Pixelfed](https://pixelfed.social/site/about), and
[others](https://fediverse.party/)) can follow your site,
receive news and interact with them.
stats:
index:
title: Statistics
@ -407,6 +424,8 @@ en:
title: 'Edit %{site}'
submit: 'Save changes'
btn: 'Configuration'
update:
post: "Your changes have been saved. **If you enabled a publication method, don't forget to publish changes.**"
form:
errors:
title: There were errors and we couldn't save your changes :(
@ -415,7 +434,7 @@ en:
name: "This will be the host name for your site, ie. **example**.sutty.nl. Choose an expression up to 63 characters. It can contain only lowercase letters, numbers and dashes, **and no spaces**. It can't start or end with a dash, or be entirely composed of numbers."
title: 'The title can be anything you want'
description: 'You site description that appears in search engines. Between 50 and 160 characters.'
design: 'Select the design for your site. You can change it later. We add more designs from time to time!'
design: 'Select the design for your site. We add more designs from time to time!'
licencia: 'Everything we publish has automatic copyright. This
means nobody can use our works without explicit permission. By
using licenses, we stablish conditions by which we want to share
@ -466,6 +485,9 @@ en:
success: 'Site upgrade has been completed. Your next build will run this upgrade :)'
error: "There was an error when trying to upgrade your site. This could be due to conflicts that couldn't be solved automatically. A report of the issue has already been sent to our admins. Sorry for the inconvenience! :("
message: 'Skeleton upgrade'
webhooks:
pull:
message: 'Webhooks pull'
footer:
powered_by: 'is developed by'
i18n:
@ -512,6 +534,8 @@ en:
feedback: 'This field cannot be empty!'
uuid:
label: 'Unique identifier'
created_at:
label: 'Created at'
geo:
uri: 'Open in app'
osm: 'Open in web map'
@ -685,7 +709,7 @@ en:
new: 'Create'
edit: 'Configure'
posts:
new: 'New %{layout}'
new: 'Add %{layout}'
edit: 'Editing'
usuaries:
index: 'Users'

View file

@ -123,6 +123,10 @@ es:
title: Web distribuida
success: ¡Éxito!
error: Hubo un error
deploy_social_distributed_press:
title: Fediverso
success: ¡Éxito!
error: Hubo un error
deploy_reindex:
title: Reindexación
success: ¡Éxito!
@ -191,9 +195,14 @@ es:
deploys:
deploy_local_presence: '¡Necesitamos poder generar el sitio!'
design_id:
missing_gem: "El sitio usa la plantilla %{theme} pero la gema correspondiente no se encuentra en el Gemfile"
layout_incompatible:
error: 'No se puede cambiar la plantilla porque hay artículos con formatos incompatibles'
help: 'En tu sitio hay artículos que solo son compatibles con el diseño actual, si cambias la plantilla el sitio no funcionará como esperas. Si estás probando plantillas, puedes eliminar los artículos en los formatos incompatibles: %{layouts}.'
usuarie:
attributes:
lang:
not_available: "Este idioma todavía no está disponible, ¿nos ayudas a agregarlo y mantenerlo?"
errors:
argument_error: 'El argumento `%{argument}` debe ser una instancia de %{class}'
unknown_locale: 'El idioma %{locale} es desconocido'
@ -312,6 +321,14 @@ es:
copias de tu contenido indefinidamente.
[Saber más](https://sutty.nl/saber-mas-sobre-publicar-a-la-web-distribuida/)
deploy_social_distributed_press:
title: 'Publicar al Fediverso'
help: |
Utilizando el protocolo ActivityPub, otras personas en el
Fediverso ([Mastodon](https://joinmastodon.org/servers),
[Pixelfed](https://pixelfed.social/site/about) y
[otros](https://fediverse.party/)) pueden seguir a tu sitio,
recibir novedades e interactuar con ellas.
stats:
index:
title: Estadísticas
@ -413,6 +430,8 @@ es:
title: 'Editar %{site}'
submit: 'Guardar cambios'
btn: 'Configuración'
update:
post: "Tus cambios han sido guardados. **Si agregaste un método de publicación, no te olvides de publicar cambios.**"
form:
errors:
title: Hubo errores y no pudimos guardar tus cambios :(
@ -421,7 +440,7 @@ es:
name: 'El nombre de tu sitio que formará parte de la dirección (**ejemplo**.sutty.nl). Solo puede contener hasta 63 letras minúsculas, números y guiones, pero **sin espacios**. No puede empezar ni terminar con guión, ni estar compuesto enteramente por números.'
title: 'El título de tu sitio puede ser lo que quieras.'
description: 'La descripción del sitio, que saldrá en buscadores. Entre 50 y 160 caracteres.'
design: 'Elegí el diseño que va a tener tu sitio aquí. Podés cambiarlo luego. De tanto en tanto vamos sumando diseños nuevos.'
design: 'Elegí el diseño que va a tener tu sitio aquí. De tanto en tanto vamos sumando diseños nuevos.'
licencia: 'Todo lo que publicamos posee automáticamente derechos
de autore. Esto significa que nadie puede hacer uso de nuestras
obras sin permiso explícito. Con las licencias establecemos
@ -474,6 +493,9 @@ es:
success: 'Ya se incorporaron los cambios en el sitio, se aplicarán en la próxima compilación que hagas :)'
error: 'Hubo un error al incorporar los cambios en el sitio. Esto puede deberse a conflictos entre cambios que no se pueden resolver automáticamente. Hemos enviado un reporte del problema a les administradores de Sutty para que estén al tanto de la situación. ¡Lo sentimos! :('
message: 'Actualización del esqueleto'
webhooks:
pull:
message: 'Traer los cambios a partir de un evento remoto'
footer:
powered_by: 'es desarrollada por'
i18n:
@ -520,6 +542,8 @@ es:
feedback: '¡Este campo no puede estar vacío!'
uuid:
label: 'Identificador único'
created_at:
label: 'Fecha de creación'
geo:
uri: 'Abrir en aplicación'
osm: 'Abrir en mapa web'
@ -693,7 +717,7 @@ es:
new: 'Crear'
edit: 'Configurar'
posts:
new: 'Nuevo %{layout}'
new: 'Agregar %{layout}'
edit: 'Editando'
usuaries:
index: 'Usuaries'

View file

@ -17,6 +17,8 @@ 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'
end
end
end

View file

@ -0,0 +1,8 @@
# frozen_string_literal: true
# Agrega la opción de paginación a los sitios
class AddPaginationToSite < ActiveRecord::Migration[6.1]
def change
add_column :sites, :pagination, :boolean, default: false
end
end

View file

@ -0,0 +1,12 @@
class AddTokenToRoles < ActiveRecord::Migration[6.1]
def up
add_column :roles, :token, :string
Rol.find_each do |m|
m.update_column( :token, SecureRandom.hex(64) )
end
end
def down
remove_column :roles, :token
end
end

View file

@ -0,0 +1,9 @@
# frozen_string_literal: true
# Almacena las llaves privadas de cada sitio
class AddPrivateKeyPemCiphertextToSites < ActiveRecord::Migration[6.1]
# Agrega la columna cifrada
def change
add_column :sites, :private_key_pem_ciphertext, :text
end
end

View file

@ -0,0 +1,20 @@
# frozen_string_literal: true
# Renombrar todos los repositorios que apunten a skel como su origin
class SiteRenameOriginToUpstream < ActiveRecord::Migration[6.1]
# Renombrar
def up
Site.find_each do |site|
next unless site.repository.origin&.url == ENV['SKEL_SUTTY']
site.repository.rugged.remotes.rename('origin', 'upstream') do |_|
Rails.logger.info "#{site.name}: renamed origin to upstream"
end
rescue Rugged::Error, Rugged::OSError => e
Rails.logger.warn "#{site.name}: #{e.message}"
end
end
# No se puede deshacer
def down; end
end

View file

@ -16,12 +16,14 @@ licencias.each do |l|
licencia.update l
end
unless Rails.env.test?
YAML.safe_load(File.read('db/seeds/sites.yml')).each do |site|
site = Site.find_or_create_by name: site['name']
site.update licencia: Licencia.first, design: Design.first,
title: site.name, description: 'x' * 50,
deploys: site.deploys.empty? ? [DeployLocal.new] : site.deploys
if CodeOfConduct.count.zero?
YAML.safe_load(File.read('db/seeds/codes_of_conduct.yml')).each do |coc|
CodeOfConduct.new(**coc).save!
end
end
if PrivacyPolicy.count.zero?
YAML.safe_load(File.read('db/seeds/privacy_policies.yml')).each do |pp|
PrivacyPolicy.new(**pp).save!
end
end

View file

@ -18,7 +18,7 @@
- name_en: 'Minima'
name_es: 'Mínima'
gem: 'sutty-minima'
url: 'https://0xacab.org/sutty/jekyll/minima'
url: 'https://minima.sutty.nl/'
description_en: "Sutty Minima is based on [Minima](https://jekyll.github.io/minima/), a blog-focused theme for Jekyll."
description_es: 'Sutty Mínima es una plantilla para blogs basada en [Mínima](https://jekyll.github.io/minima/).'
license: 'https://0xacab.org/sutty/jekyll/minima/-/blob/master/LICENSE.txt'
@ -75,6 +75,14 @@
credits_en: 'This template was made in collaboration with Librenauta in 15 hours!'
designer_url: 'https://copiona.com/donaunbit/'
priority: '70'
- name_en: 'Magazine'
name_es: 'Revista'
gem: 'compost-jekyll-theme'
url: 'https://two.compost.digital/'
description_en: 'A theme to create multimedia publications, based in COMPOST magazine'
description_es: 'Plantilla para crear publicaciones multimedia, basada en COMPOST magazine'
license: 'https://0xacab.org/sutty/jekyll/compost-jekyll-theme/-/blob/no-masters/LICENSE.txt'
priority: '40'
- name_en: 'Resource toolkit'
name_es: 'Recursero'
gem: 'recursero-jekyll-theme'

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show more