mirror of
https://0xacab.org/sutty/sutty
synced 2024-11-17 09:36:23 +00:00
Merge branch 'rails' into prosemirror
This commit is contained in:
commit
2d492538db
107 changed files with 1166 additions and 498 deletions
12
.env.example
12
.env.example
|
@ -1,7 +1,15 @@
|
|||
RAILS_ENV=production
|
||||
RAILS_ENV=
|
||||
IMAP_SERVER=
|
||||
DEFAULT_FROM=
|
||||
SKEL_SUTTY=https://0xacab.org/sutty/skel.sutty.nl
|
||||
SUTTY=sutty.nl
|
||||
SUTTY=sutty.local
|
||||
SUTTY_WITH_PORT=sutty.local:3000
|
||||
REDIS_SERVER=
|
||||
REDIS_CLIENT=
|
||||
# API authentication
|
||||
HTTP_BASIC_USER=
|
||||
HTTP_BASIC_PASSWORD=
|
||||
BLAZER_DATABASE_URL=
|
||||
BLAZER_SLACK_WEBHOOK_URL=
|
||||
BLAZER_USERNAME=
|
||||
BLAZER_PASSWORD=
|
||||
|
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -39,3 +39,6 @@
|
|||
yarn-debug.log*
|
||||
.yarn-integrity
|
||||
/vendor
|
||||
|
||||
*.key
|
||||
*.crt
|
||||
|
|
58
.rubocop.yml
58
.rubocop.yml
|
@ -1,61 +1,5 @@
|
|||
AllCops:
|
||||
TargetRubyVersion: '2.5'
|
||||
TargetRubyVersion: '2.6'
|
||||
|
||||
Style/AsciiComments:
|
||||
Enabled: false
|
||||
|
||||
# Sólo existe para molestarnos (?)
|
||||
Metrics/AbcSize:
|
||||
Enabled: false
|
||||
|
||||
Metrics/LineLength:
|
||||
Exclude:
|
||||
- 'db/schema.rb'
|
||||
- 'db/migrate/*.rb'
|
||||
- 'app/models/site.rb'
|
||||
|
||||
Metrics/MethodLength:
|
||||
Exclude:
|
||||
- 'db/schema.rb'
|
||||
- 'db/migrate/*.rb'
|
||||
- 'app/models/site.rb'
|
||||
- 'app/controllers/sites_controller.rb'
|
||||
- 'app/controllers/posts_controller.rb'
|
||||
- 'app/controllers/invitadxs_controller.rb'
|
||||
- 'app/controllers/i18n_controller.rb'
|
||||
- 'app/controllers/collaborations_controller.rb'
|
||||
- 'app/controllers/usuaries_controller.rb'
|
||||
- 'app/models/post.rb'
|
||||
|
||||
Metrics/BlockLength:
|
||||
Exclude:
|
||||
- 'config/environments/development.rb'
|
||||
- 'config/environments/production.rb'
|
||||
- 'config/initializers/devise.rb'
|
||||
- 'db/schema.rb'
|
||||
- 'config/routes.rb'
|
||||
- 'test/controllers/sites_controller_test.rb'
|
||||
|
||||
Metrics/ClassLength:
|
||||
Exclude:
|
||||
- 'app/models/site.rb'
|
||||
- 'app/controllers/posts_controller.rb'
|
||||
- 'app/controllers/sites_controller.rb'
|
||||
- 'test/models/post_test.rb'
|
||||
- 'test/controllers/sites_controller_test.rb'
|
||||
|
||||
Lint/HandleExceptions:
|
||||
Exclude:
|
||||
- 'app/controllers/posts_controller.rb'
|
||||
|
||||
Style/GuardClause:
|
||||
Exclude:
|
||||
- 'app/controllers/posts_controller.rb'
|
||||
|
||||
Metrics/PerceivedComplexity:
|
||||
Exclude:
|
||||
- 'app/controllers/posts_controller.rb'
|
||||
|
||||
Lint/UnreachableCode:
|
||||
Exclude:
|
||||
- 'app/policies/post_policy.rb'
|
||||
|
|
|
@ -1 +1 @@
|
|||
2.5.7
|
||||
2.6.5
|
||||
|
|
98
Dockerfile
98
Dockerfile
|
@ -2,7 +2,8 @@
|
|||
# el mismo repositorio de trabajo. Cuando tengamos CI/CD algunas cosas
|
||||
# como el tarball van a tener que cambiar porque ya vamos a haber hecho
|
||||
# un clone/pull limpio.
|
||||
FROM sutty/sdk-ruby:latest as build
|
||||
FROM sutty/oxipng:latest as oxipng
|
||||
FROM alpine:3.11 as build
|
||||
MAINTAINER "f <f@sutty.nl>"
|
||||
|
||||
ARG RAILS_MASTER_KEY
|
||||
|
@ -13,10 +14,21 @@ ENV SECRET_KEY_BASE solo_es_necesaria_para_correr_rake
|
|||
ENV RAILS_ENV production
|
||||
ENV RAILS_MASTER_KEY=$RAILS_MASTER_KEY
|
||||
|
||||
# Para compilar los assets en brotli
|
||||
RUN apk add --no-cache brotli libssh2
|
||||
RUN apk add --no-cache libxslt libxml2 tzdata ruby ruby-bundler ruby-json ruby-bigdecimal ruby-rake
|
||||
RUN apk add --no-cache postgresql-libs git yarn brotli libssh2 python
|
||||
|
||||
# Empezamos con la usuaria app creada por sdk-ruby
|
||||
# https://github.com/rubygems/rubygems/issues/2918
|
||||
# https://gitlab.alpinelinux.org/alpine/aports/issues/10808
|
||||
COPY ./rubygems-platform-musl.patch /tmp/
|
||||
RUN cd /usr/lib/ruby/2.6.0 && patch -Np 0 -i /tmp/rubygems-platform-musl.patch
|
||||
|
||||
# Agregar el usuario
|
||||
RUN addgroup -g 82 -S www-data
|
||||
RUN adduser -s /bin/sh -G www-data -h /home/app -D app
|
||||
RUN install -dm750 -o app -g www-data /home/app/sutty
|
||||
RUN gem install --no-document bundler:2.0.2
|
||||
|
||||
# Empezamos con la usuaria app
|
||||
USER app
|
||||
# Vamos a trabajar dentro de este directorio
|
||||
WORKDIR /home/app/sutty
|
||||
|
@ -24,25 +36,23 @@ WORKDIR /home/app/sutty
|
|||
# Copiamos solo el Gemfile para poder instalar las gemas necesarias
|
||||
COPY --chown=app:www-data ./Gemfile .
|
||||
COPY --chown=app:www-data ./Gemfile.lock .
|
||||
# XXX: No usamos la flag --production porque luego no nos deja
|
||||
# desinstalar las gemas de los assets
|
||||
# RUN --mount=type=cache,target=/home/app/.ccache \
|
||||
RUN bundle install --no-cache --path=./vendor --without='test development'
|
||||
# Vaciar la caché
|
||||
RUN rm vendor/ruby/2.5.0/cache/*.gem
|
||||
# Limpiar las librerías nativas, esto ahorra más espacio y uso de
|
||||
# memoria ya que no hay que cargar símbolos que no se van a usar.
|
||||
RUN find vendor -name "*.so" | xargs -rn 1 strip --strip-unneeded
|
||||
RUN rm vendor/ruby/2.6.0/cache/*.gem
|
||||
|
||||
# Copiar el repositorio git
|
||||
COPY --chown=app:www-data ./.git/ ./.git/
|
||||
# Hacer un tarball de los archivos desde el repositorio
|
||||
RUN git archive -o ../sutty.tar.gz HEAD
|
||||
# Hacer un clon limpio del repositorio en lugar de copiar todos los
|
||||
# archivos
|
||||
RUN cd .. && git clone sutty checkout
|
||||
|
||||
WORKDIR /home/app/checkout
|
||||
# Traer las gemas:
|
||||
RUN mv ../sutty/vendor ./vendor
|
||||
RUN mv ../sutty/.bundle ./.bundle
|
||||
|
||||
# Extraer archivos necesarios para compilar los assets
|
||||
RUN tar xf ../sutty.tar.gz Rakefile config app bin yarn.lock package.json
|
||||
# Instalar secretos
|
||||
COPY --chown=app:www-data ./config/credentials.yml.enc ./config/
|
||||
COPY --chown=app:root ./config/credentials.yml.enc ./config/
|
||||
# Pre-compilar los assets
|
||||
RUN bundle exec rake assets:precompile
|
||||
# Comprimirlos usando brotli
|
||||
|
@ -52,71 +62,51 @@ RUN find public -type f -name "*.gz" | sed -re "s/\.gz$//" | xargs -r brotli -k
|
|||
# assets ya están pre-compilados.
|
||||
RUN sed -re "/(uglifier|bootstrap|coffee-rails)/d" -i Gemfile
|
||||
RUN bundle clean
|
||||
RUN rm -rf ./node_modules ./tmp/cache ./.git
|
||||
|
||||
# Contenedor final
|
||||
FROM sutty/monit:latest
|
||||
ENV RAILS_ENV production
|
||||
|
||||
# Instalar oxipng
|
||||
COPY --from=oxipng --chown=root:root /root/.cargo/bin/oxipng /usr/bin/oxipng
|
||||
RUN chmod 755 /usr/bin/oxipng
|
||||
|
||||
# Instalar las dependencias, separamos la librería de base de datos para
|
||||
# poder reutilizar este primer paso desde otros contenedores
|
||||
RUN apk add --no-cache libxslt libxml2 tzdata ruby ruby-bundler ruby-json ruby-bigdecimal ruby-rake
|
||||
RUN apk add --no-cache postgresql-libs libssh2 file rsync git jpegoptim vips
|
||||
|
||||
# Chequear que la versión de ruby sea la correcta
|
||||
RUN test "2.5.7" = `ruby -e 'puts RUBY_VERSION'`
|
||||
RUN test "2.6.5" = `ruby -e 'puts RUBY_VERSION'`
|
||||
|
||||
# https://github.com/rubygems/rubygems/issues/2918
|
||||
# https://gitlab.alpinelinux.org/alpine/aports/issues/10808
|
||||
COPY ./rubygems-platform-musl.patch /tmp/
|
||||
RUN cd /usr/lib/ruby/2.6.0 && patch -Np 0 -i /tmp/rubygems-platform-musl.patch
|
||||
|
||||
RUN apk add --no-cache postgresql-libs libssh2
|
||||
# Necesitamos yarn para que Jekyll pueda generar los sitios
|
||||
# XXX: Eliminarlo cuando extraigamos la generación de sitios del proceso
|
||||
# principal
|
||||
RUN apk add --no-cache yarn
|
||||
# Instalar foreman para poder correr los servicios
|
||||
RUN gem install --no-document --no-user-install foreman
|
||||
RUN apk add --no-cache file
|
||||
RUN gem install --no-document --no-user-install bundler foreman
|
||||
|
||||
# Agregar el grupo del servidor web
|
||||
# Agregar el grupo del servidor web y la usuaria
|
||||
RUN addgroup -g 82 -S www-data
|
||||
# Agregar la usuaria
|
||||
RUN adduser -s /bin/sh -G www-data -h /srv/http -D app
|
||||
|
||||
# https://github.com/rubygems/rubygems/issues/2918
|
||||
# https://gitlab.alpinelinux.org/alpine/aports/issues/10808
|
||||
COPY ./rubygems-platform-musl.patch /tmp/
|
||||
RUN cd /usr/lib/ruby/2.5.0 && patch -Np 0 -i /tmp/rubygems-platform-musl.patch
|
||||
|
||||
# Convertirse en app para instalar
|
||||
USER app
|
||||
WORKDIR /srv/http
|
||||
|
||||
# Traer los archivos y colocarlos donde van definitivamente
|
||||
COPY --from=build --chown=app:www-data /home/app/sutty.tar.gz /tmp/
|
||||
RUN tar xf /tmp/sutty.tar.gz
|
||||
# Publicar el código!
|
||||
RUN mv /tmp/sutty.tar.gz ./public/
|
||||
|
||||
# Traer los assets compilados y las gemas
|
||||
COPY --from=build /home/app/sutty/public/assets public/assets
|
||||
COPY --from=build /home/app/sutty/public/packs public/packs
|
||||
COPY --from=build /home/app/sutty/vendor vendor
|
||||
COPY --from=build /home/app/sutty/.bundle .bundle
|
||||
COPY --from=build /home/app/sutty/Gemfile Gemfile
|
||||
COPY --from=build /home/app/sutty/Gemfile.lock Gemfile.lock
|
||||
COPY --from=build --chown=app:www-data /home/app/checkout /srv/http
|
||||
|
||||
# Volver a root para cerrar la compilación
|
||||
USER root
|
||||
|
||||
# Convertir la aplicación en solo lectura
|
||||
#RUN chown -R root:root /srv/http
|
||||
#RUN chmod -R o=g /srv/http
|
||||
#RUN chown -R app:www-data _deploy _sites
|
||||
|
||||
# Sincronizar los assets a un directorio compartido
|
||||
RUN apk add --no-cache rsync
|
||||
COPY ./sync_assets.sh /usr/local/bin/sync_assets
|
||||
RUN chmod 755 /usr/local/bin/sync_assets
|
||||
# Instalar la configuración de monit y comprobarla
|
||||
RUN install -m 640 -o root -g root ./monit.conf /etc/monit.d/sutty.conf
|
||||
RUN monit -t
|
||||
RUN apk add --no-cache git
|
||||
RUN install -m 755 /srv/http/sync_assets.sh /usr/local/bin/sync_assets
|
||||
# Instalar la configuración de monit
|
||||
RUN install -m 640 -o root -g root /srv/http/monit.conf /etc/monit.d/sutty.conf
|
||||
|
||||
# Mantener estos directorios!
|
||||
VOLUME "/srv/http/_deploy"
|
||||
|
|
21
Gemfile
21
Gemfile
|
@ -15,7 +15,7 @@ git_source(:github) do |repo_name|
|
|||
end
|
||||
|
||||
# Cambiar en Dockerfile también
|
||||
ruby '2.5.7'
|
||||
ruby '2.6.5'
|
||||
|
||||
gem 'dotenv-rails', require: 'dotenv/rails-now'
|
||||
|
||||
|
@ -24,7 +24,7 @@ gem 'rails', '~> 6'
|
|||
# Use Puma as the app server
|
||||
gem 'puma'
|
||||
# Use SCSS for stylesheets
|
||||
gem 'sass-rails', '~> 5.0'
|
||||
gem 'sassc-rails'
|
||||
# Use Uglifier as compressor for JavaScript assets
|
||||
gem 'uglifier', '>= 1.3.0'
|
||||
# See https://github.com/rails/execjs#readme for more supported runtimes
|
||||
|
@ -38,10 +38,7 @@ gem 'turbolinks', '~> 5'
|
|||
gem 'jbuilder', '~> 2.5'
|
||||
# Use ActiveModel has_secure_password
|
||||
gem 'bcrypt', '~> 3.1.7'
|
||||
|
||||
# Use Capistrano for deployment
|
||||
# gem 'capistrano-rails', group: :development
|
||||
|
||||
gem 'blazer'
|
||||
gem 'bootstrap', '~> 4'
|
||||
gem 'commonmarker'
|
||||
gem 'devise'
|
||||
|
@ -83,23 +80,17 @@ end
|
|||
group :development do
|
||||
# Access an IRB console on exception pages or by using <%= console %>
|
||||
# anywhere in the code.
|
||||
gem 'listen', '>= 3.0.5', '< 3.2'
|
||||
gem 'web-console', '>= 3.3.0'
|
||||
# Spring speeds up development by keeping your application running in
|
||||
# the background. Read more: https://github.com/rails/spring
|
||||
gem 'bcrypt_pbkdf'
|
||||
gem 'capistrano'
|
||||
gem 'capistrano-bundler'
|
||||
gem 'capistrano-passenger'
|
||||
gem 'capistrano-rails'
|
||||
gem 'capistrano-rbenv'
|
||||
gem 'brakeman'
|
||||
gem 'ed25519'
|
||||
gem 'haml-lint', require: false
|
||||
gem 'letter_opener'
|
||||
gem 'listen', '>= 3.0.5', '< 3.2'
|
||||
gem 'rbnacl', '< 5.0'
|
||||
gem 'rubocop-rails'
|
||||
gem 'spring'
|
||||
gem 'spring-watcher-listen', '~> 2.0.0'
|
||||
gem 'web-console', '>= 3.3.0'
|
||||
end
|
||||
|
||||
group :test do
|
||||
|
|
269
Gemfile.lock
269
Gemfile.lock
|
@ -16,56 +16,56 @@ GIT
|
|||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
actioncable (6.0.1)
|
||||
actionpack (= 6.0.1)
|
||||
actioncable (6.0.2.1)
|
||||
actionpack (= 6.0.2.1)
|
||||
nio4r (~> 2.0)
|
||||
websocket-driver (>= 0.6.1)
|
||||
actionmailbox (6.0.1)
|
||||
actionpack (= 6.0.1)
|
||||
activejob (= 6.0.1)
|
||||
activerecord (= 6.0.1)
|
||||
activestorage (= 6.0.1)
|
||||
activesupport (= 6.0.1)
|
||||
actionmailbox (6.0.2.1)
|
||||
actionpack (= 6.0.2.1)
|
||||
activejob (= 6.0.2.1)
|
||||
activerecord (= 6.0.2.1)
|
||||
activestorage (= 6.0.2.1)
|
||||
activesupport (= 6.0.2.1)
|
||||
mail (>= 2.7.1)
|
||||
actionmailer (6.0.1)
|
||||
actionpack (= 6.0.1)
|
||||
actionview (= 6.0.1)
|
||||
activejob (= 6.0.1)
|
||||
actionmailer (6.0.2.1)
|
||||
actionpack (= 6.0.2.1)
|
||||
actionview (= 6.0.2.1)
|
||||
activejob (= 6.0.2.1)
|
||||
mail (~> 2.5, >= 2.5.4)
|
||||
rails-dom-testing (~> 2.0)
|
||||
actionpack (6.0.1)
|
||||
actionview (= 6.0.1)
|
||||
activesupport (= 6.0.1)
|
||||
rack (~> 2.0)
|
||||
actionpack (6.0.2.1)
|
||||
actionview (= 6.0.2.1)
|
||||
activesupport (= 6.0.2.1)
|
||||
rack (~> 2.0, >= 2.0.8)
|
||||
rack-test (>= 0.6.3)
|
||||
rails-dom-testing (~> 2.0)
|
||||
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
||||
actiontext (6.0.1)
|
||||
actionpack (= 6.0.1)
|
||||
activerecord (= 6.0.1)
|
||||
activestorage (= 6.0.1)
|
||||
activesupport (= 6.0.1)
|
||||
actiontext (6.0.2.1)
|
||||
actionpack (= 6.0.2.1)
|
||||
activerecord (= 6.0.2.1)
|
||||
activestorage (= 6.0.2.1)
|
||||
activesupport (= 6.0.2.1)
|
||||
nokogiri (>= 1.8.5)
|
||||
actionview (6.0.1)
|
||||
activesupport (= 6.0.1)
|
||||
actionview (6.0.2.1)
|
||||
activesupport (= 6.0.2.1)
|
||||
builder (~> 3.1)
|
||||
erubi (~> 1.4)
|
||||
rails-dom-testing (~> 2.0)
|
||||
rails-html-sanitizer (~> 1.1, >= 1.2.0)
|
||||
activejob (6.0.1)
|
||||
activesupport (= 6.0.1)
|
||||
activejob (6.0.2.1)
|
||||
activesupport (= 6.0.2.1)
|
||||
globalid (>= 0.3.6)
|
||||
activemodel (6.0.1)
|
||||
activesupport (= 6.0.1)
|
||||
activerecord (6.0.1)
|
||||
activemodel (= 6.0.1)
|
||||
activesupport (= 6.0.1)
|
||||
activestorage (6.0.1)
|
||||
actionpack (= 6.0.1)
|
||||
activejob (= 6.0.1)
|
||||
activerecord (= 6.0.1)
|
||||
activemodel (6.0.2.1)
|
||||
activesupport (= 6.0.2.1)
|
||||
activerecord (6.0.2.1)
|
||||
activemodel (= 6.0.2.1)
|
||||
activesupport (= 6.0.2.1)
|
||||
activestorage (6.0.2.1)
|
||||
actionpack (= 6.0.2.1)
|
||||
activejob (= 6.0.2.1)
|
||||
activerecord (= 6.0.2.1)
|
||||
marcel (~> 0.3.1)
|
||||
activesupport (6.0.1)
|
||||
activesupport (6.0.2.1)
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
i18n (>= 0.7, < 2)
|
||||
minitest (~> 5.1)
|
||||
|
@ -73,34 +73,23 @@ GEM
|
|||
zeitwerk (~> 2.2)
|
||||
addressable (2.7.0)
|
||||
public_suffix (>= 2.0.2, < 5.0)
|
||||
airbrussh (1.3.4)
|
||||
sshkit (>= 1.6.1, != 1.7.0)
|
||||
ast (2.4.0)
|
||||
autoprefixer-rails (9.6.1.1)
|
||||
autoprefixer-rails (9.7.3)
|
||||
execjs
|
||||
bcrypt (3.1.13)
|
||||
bcrypt_pbkdf (1.0.1)
|
||||
bindex (0.8.1)
|
||||
bootstrap (4.3.1)
|
||||
blazer (2.2.1)
|
||||
activerecord (>= 5)
|
||||
chartkick (>= 3.2)
|
||||
railties (>= 5)
|
||||
safely_block (>= 0.1.1)
|
||||
bootstrap (4.4.1)
|
||||
autoprefixer-rails (>= 9.1.0)
|
||||
popper_js (>= 1.14.3, < 2)
|
||||
sassc-rails (>= 2.0.0)
|
||||
builder (3.2.3)
|
||||
capistrano (3.11.1)
|
||||
airbrussh (>= 1.0.0)
|
||||
i18n
|
||||
rake (>= 10.0.0)
|
||||
sshkit (>= 1.9.0)
|
||||
capistrano-bundler (1.6.0)
|
||||
capistrano (~> 3.1)
|
||||
capistrano-passenger (0.2.0)
|
||||
capistrano (~> 3.0)
|
||||
capistrano-rails (1.4.0)
|
||||
capistrano (~> 3.1)
|
||||
capistrano-bundler (~> 1.1)
|
||||
capistrano-rbenv (2.1.4)
|
||||
capistrano (~> 3.1)
|
||||
sshkit (~> 1.3)
|
||||
brakeman (4.7.2)
|
||||
builder (3.2.4)
|
||||
capybara (2.18.0)
|
||||
addressable
|
||||
mini_mime (>= 0.1.3)
|
||||
|
@ -108,11 +97,11 @@ GEM
|
|||
rack (>= 1.0.0)
|
||||
rack-test (>= 0.5.4)
|
||||
xpath (>= 2.0, < 4.0)
|
||||
childprocess (2.0.0)
|
||||
rake (< 13.0)
|
||||
chartkick (3.3.1)
|
||||
childprocess (3.0.0)
|
||||
coderay (1.1.2)
|
||||
colorator (1.1.0)
|
||||
commonmarker (0.20.1)
|
||||
commonmarker (0.20.2)
|
||||
ruby-enum (~> 0.5)
|
||||
concurrent-ruby (1.1.5)
|
||||
crass (1.0.5)
|
||||
|
@ -123,8 +112,8 @@ GEM
|
|||
railties (>= 4.1.0)
|
||||
responders
|
||||
warden (~> 1.2.3)
|
||||
devise-i18n (1.8.2)
|
||||
devise (>= 4.6)
|
||||
devise-i18n (1.9.0)
|
||||
devise (>= 4.7.1)
|
||||
devise_invitable (2.0.1)
|
||||
actionmailer (>= 5.0)
|
||||
devise (>= 4.6)
|
||||
|
@ -136,23 +125,24 @@ GEM
|
|||
em-websocket (0.5.1)
|
||||
eventmachine (>= 0.12.9)
|
||||
http_parser.rb (~> 0.6.0)
|
||||
email_address (0.1.11)
|
||||
netaddr (~> 2.0)
|
||||
email_address (0.1.12)
|
||||
netaddr (>= 2.0.4, < 3)
|
||||
simpleidn
|
||||
errbase (0.2.0)
|
||||
erubi (1.9.0)
|
||||
eventmachine (1.2.7)
|
||||
exception_notification (4.4.0)
|
||||
actionmailer (>= 4.0, < 7)
|
||||
activesupport (>= 4.0, < 7)
|
||||
execjs (2.7.0)
|
||||
factory_bot (5.0.2)
|
||||
factory_bot (5.1.1)
|
||||
activesupport (>= 4.2.0)
|
||||
factory_bot_rails (5.0.2)
|
||||
factory_bot (~> 5.0.2)
|
||||
factory_bot_rails (5.1.1)
|
||||
factory_bot (~> 5.1.0)
|
||||
railties (>= 4.2.0)
|
||||
ffi (1.11.1)
|
||||
ffi (1.11.3)
|
||||
forwardable-extended (2.6.0)
|
||||
friendly_id (5.2.5)
|
||||
friendly_id (5.3.0)
|
||||
activerecord (>= 4.0.0)
|
||||
globalid (0.4.2)
|
||||
activesupport (>= 4.2.0)
|
||||
|
@ -161,13 +151,12 @@ GEM
|
|||
tilt
|
||||
haml-lint (0.999.999)
|
||||
haml_lint
|
||||
haml_lint (0.33.0)
|
||||
haml_lint (0.34.1)
|
||||
haml (>= 4.0, < 5.2)
|
||||
rainbow
|
||||
rake (>= 10, < 13)
|
||||
rubocop (>= 0.50.0)
|
||||
sysexits (~> 1.1)
|
||||
hamlit (2.10.0)
|
||||
hamlit (2.11.0)
|
||||
temple (>= 0.8.2)
|
||||
thor
|
||||
tilt
|
||||
|
@ -180,13 +169,13 @@ GEM
|
|||
http_parser.rb (0.6.0)
|
||||
i18n (1.7.0)
|
||||
concurrent-ruby (~> 1.0)
|
||||
image_processing (1.9.3)
|
||||
image_processing (1.10.0)
|
||||
mini_magick (>= 4.9.5, < 5)
|
||||
ruby-vips (>= 2.0.13, < 3)
|
||||
inline_svg (1.5.2)
|
||||
inline_svg (1.6.0)
|
||||
activesupport (>= 3.0)
|
||||
nokogiri (>= 1.6)
|
||||
jaro_winkler (1.5.3)
|
||||
jaro_winkler (1.5.4)
|
||||
jbuilder (2.9.1)
|
||||
activesupport (>= 4.2.0)
|
||||
jekyll (4.0.0)
|
||||
|
@ -204,7 +193,7 @@ GEM
|
|||
rouge (~> 3.0)
|
||||
safe_yaml (~> 1.0)
|
||||
terminal-table (~> 1.8)
|
||||
jekyll-sass-converter (2.0.0)
|
||||
jekyll-sass-converter (2.0.1)
|
||||
sassc (> 2.0.1, < 3.0)
|
||||
jekyll-watch (2.2.1)
|
||||
listen (~> 3.0)
|
||||
|
@ -220,7 +209,7 @@ GEM
|
|||
rb-fsevent (~> 0.9, >= 0.9.4)
|
||||
rb-inotify (~> 0.9, >= 0.9.7)
|
||||
ruby_dep (~> 1.2)
|
||||
loofah (2.3.1)
|
||||
loofah (2.4.0)
|
||||
crass (~> 1.0.2)
|
||||
nokogiri (>= 1.5.9)
|
||||
mail (2.7.1)
|
||||
|
@ -237,48 +226,45 @@ GEM
|
|||
mobility (0.8.9)
|
||||
i18n (>= 0.6.10, < 2)
|
||||
request_store (~> 1.0)
|
||||
net-scp (2.0.0)
|
||||
net-ssh (>= 2.6.5, < 6.0.0)
|
||||
net-ssh (5.2.0)
|
||||
netaddr (2.0.3)
|
||||
netaddr (2.0.4)
|
||||
nio4r (2.5.2)
|
||||
nokogiri (1.10.5)
|
||||
nokogiri (1.10.7)
|
||||
mini_portile2 (~> 2.4.0)
|
||||
orm_adapter (0.5.0)
|
||||
parallel (1.17.0)
|
||||
parser (2.6.4.1)
|
||||
parallel (1.19.1)
|
||||
parser (2.7.0.1)
|
||||
ast (~> 2.4.0)
|
||||
pathutil (0.16.2)
|
||||
forwardable-extended (~> 2.6)
|
||||
pg (1.1.4)
|
||||
pg (1.2.0)
|
||||
popper_js (1.14.5)
|
||||
pry (0.12.2)
|
||||
coderay (~> 1.1.0)
|
||||
method_source (~> 0.9.0)
|
||||
public_suffix (4.0.1)
|
||||
puma (4.3.0)
|
||||
public_suffix (4.0.2)
|
||||
puma (4.3.1)
|
||||
nio4r (~> 2.0)
|
||||
pundit (2.1.0)
|
||||
activesupport (>= 3.0.0)
|
||||
rack (2.0.7)
|
||||
rack (2.0.8)
|
||||
rack-proxy (0.6.5)
|
||||
rack
|
||||
rack-test (1.1.0)
|
||||
rack (>= 1.0, < 3)
|
||||
rails (6.0.1)
|
||||
actioncable (= 6.0.1)
|
||||
actionmailbox (= 6.0.1)
|
||||
actionmailer (= 6.0.1)
|
||||
actionpack (= 6.0.1)
|
||||
actiontext (= 6.0.1)
|
||||
actionview (= 6.0.1)
|
||||
activejob (= 6.0.1)
|
||||
activemodel (= 6.0.1)
|
||||
activerecord (= 6.0.1)
|
||||
activestorage (= 6.0.1)
|
||||
activesupport (= 6.0.1)
|
||||
rails (6.0.2.1)
|
||||
actioncable (= 6.0.2.1)
|
||||
actionmailbox (= 6.0.2.1)
|
||||
actionmailer (= 6.0.2.1)
|
||||
actionpack (= 6.0.2.1)
|
||||
actiontext (= 6.0.2.1)
|
||||
actionview (= 6.0.2.1)
|
||||
activejob (= 6.0.2.1)
|
||||
activemodel (= 6.0.2.1)
|
||||
activerecord (= 6.0.2.1)
|
||||
activestorage (= 6.0.2.1)
|
||||
activesupport (= 6.0.2.1)
|
||||
bundler (>= 1.3.0)
|
||||
railties (= 6.0.1)
|
||||
railties (= 6.0.2.1)
|
||||
sprockets-rails (>= 2.0.0)
|
||||
rails-dom-testing (2.0.3)
|
||||
activesupport (>= 4.2.0)
|
||||
|
@ -290,16 +276,16 @@ GEM
|
|||
railties (>= 6.0.0, < 7)
|
||||
rails_warden (0.6.0)
|
||||
warden (>= 1.2.0)
|
||||
railties (6.0.1)
|
||||
actionpack (= 6.0.1)
|
||||
activesupport (= 6.0.1)
|
||||
railties (6.0.2.1)
|
||||
actionpack (= 6.0.2.1)
|
||||
activesupport (= 6.0.2.1)
|
||||
method_source
|
||||
rake (>= 0.8.7)
|
||||
thor (>= 0.20.3, < 2.0)
|
||||
rainbow (3.0.0)
|
||||
rake (12.3.3)
|
||||
rake (13.0.1)
|
||||
rb-fsevent (0.10.3)
|
||||
rb-inotify (0.10.0)
|
||||
rb-inotify (0.10.1)
|
||||
ffi (~> 1.0)
|
||||
rbnacl (4.0.2)
|
||||
ffi
|
||||
|
@ -311,52 +297,43 @@ GEM
|
|||
redis-activesupport (5.2.0)
|
||||
activesupport (>= 3, < 7)
|
||||
redis-store (>= 1.3, < 2)
|
||||
redis-rack (2.0.5)
|
||||
redis-rack (2.0.6)
|
||||
rack (>= 1.5, < 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.6.0)
|
||||
redis (>= 2.2, < 5)
|
||||
request_store (1.4.1)
|
||||
redis-store (1.8.1)
|
||||
redis (>= 4, < 5)
|
||||
request_store (1.5.0)
|
||||
rack (>= 1.4)
|
||||
responders (3.0.0)
|
||||
actionpack (>= 5.0)
|
||||
railties (>= 5.0)
|
||||
rouge (3.11.0)
|
||||
rubocop (0.74.0)
|
||||
rouge (3.14.0)
|
||||
rubocop (0.78.0)
|
||||
jaro_winkler (~> 1.5.1)
|
||||
parallel (~> 1.10)
|
||||
parser (>= 2.6)
|
||||
rainbow (>= 2.2.2, < 4.0)
|
||||
ruby-progressbar (~> 1.7)
|
||||
unicode-display_width (>= 1.4.0, < 1.7)
|
||||
rubocop-rails (2.3.2)
|
||||
rubocop-rails (2.4.1)
|
||||
rack (>= 1.1)
|
||||
rubocop (>= 0.72.0)
|
||||
ruby-enum (0.7.2)
|
||||
i18n
|
||||
ruby-progressbar (1.10.1)
|
||||
ruby-vips (2.0.15)
|
||||
ruby-vips (2.0.16)
|
||||
ffi (~> 1.9)
|
||||
ruby_dep (1.5.0)
|
||||
rubyzip (1.3.0)
|
||||
rugged (0.28.3.1)
|
||||
rubyzip (2.0.0)
|
||||
rugged (0.28.4.1)
|
||||
safe_yaml (1.0.5)
|
||||
sass (3.7.4)
|
||||
sass-listen (~> 4.0.0)
|
||||
sass-listen (4.0.0)
|
||||
rb-fsevent (~> 0.9, >= 0.9.4)
|
||||
rb-inotify (~> 0.9, >= 0.9.7)
|
||||
sass-rails (5.1.0)
|
||||
railties (>= 5.2.0)
|
||||
sass (~> 3.1)
|
||||
sprockets (>= 2.8, < 4.0)
|
||||
sprockets-rails (>= 2.0, < 4.0)
|
||||
tilt (>= 1.1, < 3)
|
||||
sassc (2.2.0)
|
||||
safely_block (0.3.0)
|
||||
errbase (>= 0.1.1)
|
||||
sassc (2.2.1)
|
||||
ffi (~> 1.9)
|
||||
sassc-rails (2.1.2)
|
||||
railties (>= 4.0.0)
|
||||
|
@ -364,41 +341,38 @@ GEM
|
|||
sprockets (> 3.0)
|
||||
sprockets-rails
|
||||
tilt
|
||||
selenium-webdriver (3.142.4)
|
||||
childprocess (>= 0.5, < 3.0)
|
||||
rubyzip (~> 1.2, >= 1.2.2)
|
||||
selenium-webdriver (3.142.7)
|
||||
childprocess (>= 0.5, < 4.0)
|
||||
rubyzip (>= 1.2.2)
|
||||
simpleidn (0.1.1)
|
||||
unf (~> 0.1.4)
|
||||
spring (2.1.0)
|
||||
spring-watcher-listen (2.0.1)
|
||||
listen (>= 2.7, < 4.0)
|
||||
spring (>= 1.2, < 3.0)
|
||||
sprockets (3.7.2)
|
||||
sprockets (4.0.0)
|
||||
concurrent-ruby (~> 1.0)
|
||||
rack (> 1, < 3)
|
||||
sprockets-rails (3.2.1)
|
||||
actionpack (>= 4.0)
|
||||
activesupport (>= 4.0)
|
||||
sprockets (>= 3.0.0)
|
||||
sqlite3 (1.4.1)
|
||||
sshkit (1.20.0)
|
||||
net-scp (>= 1.1.2)
|
||||
net-ssh (>= 2.8.0)
|
||||
sqlite3 (1.4.2)
|
||||
sucker_punch (2.1.2)
|
||||
concurrent-ruby (~> 1.0)
|
||||
sysexits (1.2.0)
|
||||
temple (0.8.2)
|
||||
terminal-table (1.8.0)
|
||||
unicode-display_width (~> 1.1, >= 1.1.1)
|
||||
thor (0.20.3)
|
||||
thor (1.0.1)
|
||||
thread_safe (0.3.6)
|
||||
tilt (2.0.9)
|
||||
turbolinks (5.2.0)
|
||||
tilt (2.0.10)
|
||||
turbolinks (5.2.1)
|
||||
turbolinks-source (~> 5.2)
|
||||
turbolinks-source (5.2.0)
|
||||
tzinfo (1.2.5)
|
||||
tzinfo (1.2.6)
|
||||
thread_safe (~> 0.1)
|
||||
uglifier (4.1.20)
|
||||
uglifier (4.2.0)
|
||||
execjs (>= 0.3.0, < 3)
|
||||
unf (0.1.4)
|
||||
unf_ext
|
||||
|
@ -414,7 +388,7 @@ GEM
|
|||
activemodel (>= 6.0.0)
|
||||
bindex (>= 0.4.0)
|
||||
railties (>= 6.0.0)
|
||||
webpacker (4.0.7)
|
||||
webpacker (4.2.2)
|
||||
activesupport (>= 4.2)
|
||||
rack-proxy (>= 0.6.1)
|
||||
railties (>= 4.2)
|
||||
|
@ -423,7 +397,7 @@ GEM
|
|||
websocket-extensions (0.1.4)
|
||||
xpath (3.2.0)
|
||||
nokogiri (~> 1.8)
|
||||
zeitwerk (2.2.1)
|
||||
zeitwerk (2.2.2)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
@ -431,12 +405,9 @@ PLATFORMS
|
|||
DEPENDENCIES
|
||||
bcrypt (~> 3.1.7)
|
||||
bcrypt_pbkdf
|
||||
blazer
|
||||
bootstrap (~> 4)
|
||||
capistrano
|
||||
capistrano-bundler
|
||||
capistrano-passenger
|
||||
capistrano-rails
|
||||
capistrano-rbenv
|
||||
brakeman
|
||||
capybara (~> 2.13)
|
||||
commonmarker
|
||||
database_cleaner
|
||||
|
@ -474,7 +445,7 @@ DEPENDENCIES
|
|||
rubocop-rails
|
||||
rubyzip
|
||||
rugged
|
||||
sass-rails (~> 5.0)
|
||||
sassc-rails
|
||||
selenium-webdriver
|
||||
spring
|
||||
spring-watcher-listen (~> 2.0.0)
|
||||
|
@ -489,7 +460,7 @@ DEPENDENCIES
|
|||
yaml_db!
|
||||
|
||||
RUBY VERSION
|
||||
ruby 2.5.7p206
|
||||
ruby 2.6.5p114
|
||||
|
||||
BUNDLED WITH
|
||||
2.0.2
|
||||
2.1.4
|
||||
|
|
18
Makefile
18
Makefile
|
@ -3,6 +3,9 @@ mkfile_path := $(abspath $(lastword $(MAKEFILE_LIST)))
|
|||
root_dir := $(patsubst %/,%,$(dir $(mkfile_path)))
|
||||
include $(root_dir)/.env
|
||||
|
||||
serve:
|
||||
bundle exec rails s -b "ssl://0.0.0.0:3000?key=config/sutty.local.key&cert=config/sutty.local.crt"
|
||||
|
||||
# Limpiar los archivos de testeo
|
||||
clean:
|
||||
rm -rf _sites/test-* _deploy/test-*
|
||||
|
@ -11,13 +14,22 @@ clean:
|
|||
build:
|
||||
docker build --build-arg="RAILS_MASTER_KEY=`cat config/master.key`" -t sutty/sutty .
|
||||
|
||||
save:
|
||||
docker save sutty/sutty:latest | gzip | ssh root@sutty.nl docker load
|
||||
|
||||
load:
|
||||
ssh root@sutty.nl sh -c "gunzip -c sutty.latest.gz | docker load"
|
||||
|
||||
# Crear el directorio donde se almacenan las gemas binarias
|
||||
../gems/:
|
||||
mkdir -p $@
|
||||
|
||||
ifeq ($(MAKECMDGOALS),convert-gems)
|
||||
gem_dir := $(shell readlink -f ../gems)
|
||||
gems := $(shell bundle show --paths | xargs -I {} sh -c 'test -d {}/ext && basename {}')
|
||||
gems := $(shell bundle show --paths | xargs -I {} sh -c 'test -f {}/ext/*/extconf.rb && basename {}')
|
||||
gems += $(shell bundle show --paths | xargs -I {} sh -c 'test -f {}/ext/extconf.rb && basename {}')
|
||||
gems_musl := $(patsubst %,$(gem_dir)/%-x86_64-linux-musl.gem,$(gems))
|
||||
endif
|
||||
|
||||
$(gem_dir)/%-x86_64-linux-musl.gem:
|
||||
docker run \
|
||||
|
@ -45,8 +57,8 @@ test-container: $(dirs)
|
|||
-v $(PWD)/root/public:/srv/http/_public \
|
||||
-v $(PWD)/config/credentials.yml.enc:/srv/http/config/credentials.yml.enc \
|
||||
-e RAILS_MASTER_KEY=`cat config/master.key` \
|
||||
-e RAILS_ENV=production \
|
||||
-it \
|
||||
--rm \
|
||||
--name=sutty \
|
||||
sutty/sutty \
|
||||
/bin/sh
|
||||
sutty/sutty /bin/sh
|
||||
|
|
BIN
app/assets/fonts/saira/v3/Saira-subset.woff2
Normal file
BIN
app/assets/fonts/saira/v3/Saira-subset.woff2
Normal file
Binary file not shown.
BIN
app/assets/fonts/saira/v3/Saira-subset.zopfli.woff
Normal file
BIN
app/assets/fonts/saira/v3/Saira-subset.zopfli.woff
Normal file
Binary file not shown.
BIN
app/assets/fonts/saira/v3/SairaBold-subset.woff2
Normal file
BIN
app/assets/fonts/saira/v3/SairaBold-subset.woff2
Normal file
Binary file not shown.
BIN
app/assets/fonts/saira/v3/SairaBold-subset.zopfli.woff
Normal file
BIN
app/assets/fonts/saira/v3/SairaBold-subset.zopfli.woff
Normal file
Binary file not shown.
BIN
app/assets/fonts/saira/v3/SairaMedium-subset.woff2
Normal file
BIN
app/assets/fonts/saira/v3/SairaMedium-subset.woff2
Normal file
Binary file not shown.
BIN
app/assets/fonts/saira/v3/SairaMedium-subset.zopfli.woff
Normal file
BIN
app/assets/fonts/saira/v3/SairaMedium-subset.zopfli.woff
Normal file
Binary file not shown.
|
@ -1,5 +1,6 @@
|
|||
//= require rails-ujs
|
||||
//= require turbolinks
|
||||
//= require input-tag/input-tag.js
|
||||
//= require input-map/input-map.js
|
||||
//= require zepto/dist/zepto.min.js
|
||||
//= require_tree .
|
||||
|
|
|
@ -7,4 +7,13 @@ $(document).on('turbolinks:load', function() {
|
|||
props: { ...this.dataset }
|
||||
});
|
||||
});
|
||||
|
||||
$('.mapable').each(function() {
|
||||
this.innerHTML = '';
|
||||
|
||||
new InputMap({
|
||||
target: this,
|
||||
props: { ...this.dataset }
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -20,6 +20,18 @@ $magenta: #f206f9;
|
|||
--background: #{$black};
|
||||
--color: #{$cyan};
|
||||
}
|
||||
trix-toolbar {
|
||||
.trix-button--icon {
|
||||
background-color: var(--color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trix-toolbar {
|
||||
background-color: var(--background);
|
||||
position: sticky;
|
||||
top: 0;
|
||||
|
||||
}
|
||||
|
||||
// TODO: Encontrar la forma de generar esto desde los locales de Rails
|
||||
|
@ -34,7 +46,8 @@ $custom-file-text: (
|
|||
font-weight: 500;
|
||||
font-display: optional;
|
||||
src: local('Saira Medium'), local('Saira-Medium'),
|
||||
font-url('saira/v3/SairaMedium.ttf') format('truetype');
|
||||
font-url('saira/v3/SairaMedium-subset.woff2') format('woff2'),
|
||||
font-url('saira/v3/SairaMedium-subset.zopfli.woff') format('woff');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
|
@ -43,7 +56,8 @@ $custom-file-text: (
|
|||
font-weight: 700;
|
||||
font-display: optional;
|
||||
src: local('Saira Bold'), local('Saira-Bold'),
|
||||
font-url('saira/v3/SairaBold.ttf') format('truetype');
|
||||
font-url('saira/v3/SairaBold-subset.woff2') format('woff2'),
|
||||
font-url('saira/v3/SairaBold-subset.zopfli.woff') format('woff');
|
||||
}
|
||||
|
||||
body {
|
||||
|
@ -128,7 +142,9 @@ ol.breadcrumb {
|
|||
transition: all 3s;
|
||||
}
|
||||
|
||||
.mapable,
|
||||
.taggable {
|
||||
.input-map,
|
||||
.input-tag {
|
||||
legend {
|
||||
@extend .sr-only
|
||||
|
@ -144,7 +160,7 @@ ol.breadcrumb {
|
|||
&[type=text] {
|
||||
@extend .form-control;
|
||||
display: inline-block;
|
||||
width: calc(100% - 90px);
|
||||
width: calc(100% - 93px);
|
||||
}
|
||||
|
||||
&[type=checkbox] {
|
||||
|
@ -169,6 +185,8 @@ svg {
|
|||
color: var(--background);
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
margin-right: 0.3rem;
|
||||
margin-bottom: 0.3rem;
|
||||
|
||||
&:hover {
|
||||
color: var(--background);
|
||||
|
|
|
@ -4,9 +4,6 @@ module Api
|
|||
module V1
|
||||
# API
|
||||
class BaseController < ActionController::Base
|
||||
http_basic_authenticate_with name: ENV['HTTP_BASIC_USER'],
|
||||
password: ENV['HTTP_BASIC_PASSWORD']
|
||||
|
||||
protect_from_forgery with: :null_session
|
||||
respond_to :json
|
||||
end
|
||||
|
|
42
app/controllers/api/v1/csp_reports_controller.rb
Normal file
42
app/controllers/api/v1/csp_reports_controller.rb
Normal file
|
@ -0,0 +1,42 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Api
|
||||
module V1
|
||||
# Recibe los reportes de Content Security Policy
|
||||
class CspReportsController < BaseController
|
||||
# Crea un reporte de CSP intercambiando los guiones medios por
|
||||
# bajos
|
||||
#
|
||||
# TODO: Aplicar rate_limit
|
||||
def create
|
||||
csp = CspReport.new(csp_report_params.to_h.map do |k, v|
|
||||
{ k.tr('-', '_') => v }
|
||||
end.inject(&:merge))
|
||||
|
||||
csp.id = SecureRandom.uuid
|
||||
csp.save
|
||||
|
||||
render json: {}, status: :created
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only#Violation_report_syntax
|
||||
def csp_report_params
|
||||
params.require(:'csp-report')
|
||||
.permit(:disposition,
|
||||
:referrer,
|
||||
:'blocked-uri',
|
||||
:'document-uri',
|
||||
:'effective-directive',
|
||||
:'original-policy',
|
||||
:'script-sample',
|
||||
:'status-code',
|
||||
:'violated-directive',
|
||||
:'line-number',
|
||||
:'column-number',
|
||||
:'source-file')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -4,8 +4,12 @@ module Api
|
|||
module V1
|
||||
# API para sitios
|
||||
class SitesController < BaseController
|
||||
http_basic_authenticate_with name: ENV['HTTP_BASIC_USER'],
|
||||
password: ENV['HTTP_BASIC_PASSWORD']
|
||||
|
||||
def index
|
||||
render json: Site.all.order(:name).pluck(:name)
|
||||
render json: Site.all.order(:name).pluck(:name) +
|
||||
DeployAlternativeDomain.all.map(&:hostname)
|
||||
end
|
||||
|
||||
# Detecta si se puede generar un certificado
|
||||
|
|
|
@ -12,7 +12,8 @@ class PostsController < ApplicationController
|
|||
@layout = params.dig(:layout).try :to_sym
|
||||
# TODO: Aplicar policy_scope
|
||||
@posts = @site.posts(lang: I18n.locale)
|
||||
@posts.sort_by! :order, :date
|
||||
@posts.sort_by!(:order, :date).reverse!
|
||||
@usuarie = @site.usuarie? current_usuarie
|
||||
end
|
||||
|
||||
def show
|
||||
|
@ -34,7 +35,7 @@ class PostsController < ApplicationController
|
|||
usuarie: current_usuarie,
|
||||
params: params)
|
||||
|
||||
if service.create.persisted?
|
||||
if (@post = service.create.persisted?)
|
||||
redirect_to site_posts_path(@site)
|
||||
else
|
||||
render 'posts/new'
|
||||
|
|
|
@ -8,7 +8,7 @@ class SitesController < ApplicationController
|
|||
# Ver un listado de sitios
|
||||
def index
|
||||
authorize Site
|
||||
@sites = current_usuarie.sites
|
||||
@sites = current_usuarie.sites.order(:title)
|
||||
end
|
||||
|
||||
# No tenemos propiedades de un sitio aún, así que vamos al listado de
|
||||
|
|
|
@ -186,7 +186,11 @@ document.addEventListener('turbolinks:load', e => {
|
|||
onlyBody: true,
|
||||
dragHandler: '.handle',
|
||||
}).on('drop', (from, to, el, mode) => {
|
||||
$('.reorder').val((i, v) => i)
|
||||
$('.submit-reorder').removeClass('d-none')
|
||||
Array.from(document.querySelectorAll('.reorder'))
|
||||
.reverse()
|
||||
.map((o,i) => o.value = i)
|
||||
|
||||
Array.from(document.querySelectorAll('.submit-reorder'))
|
||||
.map(s => s.classList.remove('d-none'))
|
||||
})
|
||||
})
|
||||
|
|
|
@ -15,6 +15,9 @@ class DeployJob < ApplicationJob
|
|||
# No es opcional
|
||||
unless @deployed[:deploy_local]
|
||||
@site.update_attribute :status, 'waiting'
|
||||
notify_usuaries
|
||||
|
||||
# Hacer fallar la tarea
|
||||
raise DeployException, deploy_local.build_stats.last.log
|
||||
end
|
||||
|
||||
|
|
4
app/models/csp_report.rb
Normal file
4
app/models/csp_report.rb
Normal file
|
@ -0,0 +1,4 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Almacena un reporte de CSP
|
||||
class CspReport < ApplicationRecord; end
|
23
app/models/deploy_alternative_domain.rb
Normal file
23
app/models/deploy_alternative_domain.rb
Normal file
|
@ -0,0 +1,23 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Soportar dominios alternativos
|
||||
class DeployAlternativeDomain < Deploy
|
||||
store :values, accessors: %i[hostname], coder: JSON
|
||||
|
||||
# Generar un link simbólico del sitio principal al alternativo
|
||||
def deploy
|
||||
File.symlink?(destination) ||
|
||||
File.symlink(site.hostname, destination).zero?
|
||||
end
|
||||
|
||||
# No hay límite para los dominios alternativos
|
||||
def limit; end
|
||||
|
||||
def size
|
||||
File.size destination
|
||||
end
|
||||
|
||||
def destination
|
||||
File.join(Rails.root, '_deploy', hostname.gsub(/\.\z/, ''))
|
||||
end
|
||||
end
|
|
@ -13,7 +13,11 @@ class DeployLocal < Deploy
|
|||
# Pasamos variables de entorno mínimas para no filtrar secretos de
|
||||
# Sutty
|
||||
def deploy
|
||||
mkdir && yarn && bundle && jekyll_build
|
||||
return false unless mkdir
|
||||
return false unless yarn
|
||||
return false unless bundle
|
||||
|
||||
jekyll_build
|
||||
end
|
||||
|
||||
# Sólo permitimos un deploy local
|
||||
|
@ -49,7 +53,8 @@ class DeployLocal < Deploy
|
|||
{
|
||||
'HOME' => home_dir,
|
||||
'PATH' => paths.join(':'),
|
||||
'JEKYLL_ENV' => Rails.env
|
||||
'JEKYLL_ENV' => Rails.env,
|
||||
'LANG' => ENV['LANG']
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -61,6 +66,10 @@ class DeployLocal < Deploy
|
|||
File.exist? yarn_lock
|
||||
end
|
||||
|
||||
def gem
|
||||
run %(gem install bundler --no-document)
|
||||
end
|
||||
|
||||
# Corre yarn dentro del repositorio
|
||||
def yarn
|
||||
return unless yarn_lock?
|
||||
|
|
4
app/models/metadata_color.rb
Normal file
4
app/models/metadata_color.rb
Normal file
|
@ -0,0 +1,4 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Un campo de correo
|
||||
class MetadataColor < MetadataString; end
|
5
app/models/metadata_email.rb
Normal file
5
app/models/metadata_email.rb
Normal file
|
@ -0,0 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Un campo de correo
|
||||
# TODO: Validar que tenga un formato correo
|
||||
class MetadataEmail < MetadataString; end
|
23
app/models/metadata_geo.rb
Normal file
23
app/models/metadata_geo.rb
Normal file
|
@ -0,0 +1,23 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Define un campo de coordenadas geográficas
|
||||
class MetadataGeo < MetadataTemplate
|
||||
def default_value
|
||||
{ 'lat' => nil, 'lng' => nil }
|
||||
end
|
||||
|
||||
def empty?
|
||||
value == default_value
|
||||
end
|
||||
|
||||
def to_param
|
||||
{ name => %i[lat lng] }
|
||||
end
|
||||
|
||||
def save
|
||||
self[:value] = {
|
||||
'lat' => self[:value]['lat'].to_f,
|
||||
'lng' => self[:value]['lng'].to_f
|
||||
}
|
||||
end
|
||||
end
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
# Un campo de orden
|
||||
class MetadataOrder < MetadataTemplate
|
||||
# El valor según la posición del post en la relación, siguiendo el
|
||||
# orden cronológico inverso
|
||||
# El valor según la posición del post en la relación ordenada por
|
||||
# fecha, a fecha más alta, posición más alta
|
||||
def default_value
|
||||
site.posts(lang: post.lang.value).index(post)
|
||||
site.posts(lang: post.lang.value).sort_by(:date).index(post)
|
||||
end
|
||||
end
|
||||
|
|
23
app/models/metadata_related_posts.rb
Normal file
23
app/models/metadata_related_posts.rb
Normal file
|
@ -0,0 +1,23 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Devuelve una lista de títulos y UUID de todos los posts del mismo
|
||||
# idioma que el actual, para usar con input-map.js
|
||||
class MetadataRelatedPosts < MetadataArray
|
||||
# Genera un Hash de { title | slug => uuid }
|
||||
def values
|
||||
site.posts(lang: lang).map do |p|
|
||||
{ title(p) => p.uuid.value }
|
||||
end.inject(:merge)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def title(post)
|
||||
post.try(:title).try(:value) || post.try(:slug).try(:value)
|
||||
end
|
||||
|
||||
# TODO: Traer el idioma actual de otra forma
|
||||
def lang
|
||||
post.try(:lang).try(:value) || I18n.locale
|
||||
end
|
||||
end
|
|
@ -15,6 +15,8 @@ MetadataTemplate = Struct.new(:site, :document, :name, :label, :type,
|
|||
|
||||
# Valores posibles, busca todos los valores actuales en otros
|
||||
# artículos del mismo sitio
|
||||
#
|
||||
# TODO: Implementar lang!
|
||||
def values
|
||||
site.everything_of(name)
|
||||
end
|
||||
|
|
8
app/models/metadata_uuid.rb
Normal file
8
app/models/metadata_uuid.rb
Normal file
|
@ -0,0 +1,8 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Asigna un identificador único al artículo
|
||||
class MetadataUuid < MetadataTemplate
|
||||
def default_value
|
||||
SecureRandom.uuid
|
||||
end
|
||||
end
|
|
@ -1,7 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'jekyll/utils'
|
||||
|
||||
# Esta clase representa un post en un sitio jekyll e incluye métodos
|
||||
# para modificarlos y crear nuevos.
|
||||
#
|
||||
|
@ -13,7 +11,16 @@ class Post < OpenStruct
|
|||
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].freeze
|
||||
PUBLIC_ATTRIBUTES = %i[lang date uuid].freeze
|
||||
|
||||
class << self
|
||||
# Obtiene el layout sin leer el Document
|
||||
def find_layout(doc)
|
||||
SafeYAML.load(IO.foreach(doc.path).lazy.grep(/^layout: /).take(1).first)
|
||||
.try(:[], 'layout')
|
||||
.try(:to_sym)
|
||||
end
|
||||
end
|
||||
|
||||
# Redefinir el inicializador de OpenStruct
|
||||
#
|
||||
|
@ -33,6 +40,10 @@ class Post < OpenStruct
|
|||
# MetadataFactory devuelve un tipo de campo por cada campo. A
|
||||
# partir de ahí se pueden obtener los valores actuales y una lista
|
||||
# de valores por defecto.
|
||||
#
|
||||
# XXX: En el primer intento de hacerlo más óptimo, movimos esta
|
||||
# lógica a instanciación bajo demanda, pero no solo no logramos
|
||||
# optimizar sino que aumentamos el tiempo de carga :/
|
||||
layout.metadata.each_pair do |name, template|
|
||||
send "#{name}=".to_sym,
|
||||
MetadataFactory.build(document: document,
|
||||
|
@ -47,31 +58,27 @@ class Post < OpenStruct
|
|||
required: template['required'])
|
||||
end
|
||||
|
||||
# TODO: Llamar dinámicamente
|
||||
load_lang!
|
||||
load_slug!
|
||||
load_date!
|
||||
load_path!
|
||||
load_uuid!
|
||||
|
||||
# Leer el documento
|
||||
read
|
||||
# XXX: No usamos Post#read porque a esta altura todavía no sabemos
|
||||
# nada del Document
|
||||
document.read! if File.exist? document.path
|
||||
end
|
||||
|
||||
# TODO: Convertir a UUID?
|
||||
def id
|
||||
path.basename
|
||||
end
|
||||
|
||||
def sha1
|
||||
Digest::SHA1.hexdigest id
|
||||
def updated_at
|
||||
File.mtime(path.absolute)
|
||||
end
|
||||
|
||||
# Levanta un error si al construir el artículo no pasamos un atributo.
|
||||
def default_attributes_missing(**args)
|
||||
DEFAULT_ATTRIBUTES.each do |attr|
|
||||
i18n = I18n.t("exceptions.post.#{attr}_missing")
|
||||
|
||||
raise ArgumentError, i18n unless args[attr].present?
|
||||
end
|
||||
end
|
||||
|
||||
# Solo ejecuta la magia de OpenStruct si el campo existe en la
|
||||
# plantilla
|
||||
|
@ -82,14 +89,18 @@ class Post < OpenStruct
|
|||
# XXX: rubocop dice que tenemos que usar super cuando ya lo estamos
|
||||
# usando...
|
||||
def method_missing(mid, *args)
|
||||
unless attribute? mid
|
||||
# Limpiar el nombre del atributo, para que todos los ayudantes
|
||||
# reciban el método en limpio
|
||||
name = attribute_name mid
|
||||
|
||||
unless attribute? name
|
||||
raise NoMethodError, I18n.t('exceptions.post.no_method',
|
||||
method: mid)
|
||||
end
|
||||
|
||||
# Definir los attribute_*
|
||||
new_attribute_was(mid)
|
||||
new_attribute_changed(mid)
|
||||
new_attribute_was(name)
|
||||
new_attribute_changed(name)
|
||||
|
||||
# OpenStruct
|
||||
super(mid, *args)
|
||||
|
@ -101,12 +112,15 @@ class Post < OpenStruct
|
|||
# Detecta si es un atributo válido o no, a partir de la tabla de la
|
||||
# plantilla
|
||||
def attribute?(mid)
|
||||
attrs = DEFAULT_ATTRIBUTES + PRIVATE_ATTRIBUTES + PUBLIC_ATTRIBUTES
|
||||
if singleton_class.method_defined? :attributes
|
||||
(attrs + attributes).include? attribute_name(mid)
|
||||
else
|
||||
attrs.include? attribute_name(mid)
|
||||
included = DEFAULT_ATTRIBUTES.include?(mid) ||
|
||||
PRIVATE_ATTRIBUTES.include?(mid) ||
|
||||
PUBLIC_ATTRIBUTES.include?(mid)
|
||||
|
||||
if !included && singleton_class.method_defined?(:attributes)
|
||||
included = attributes.include? mid
|
||||
end
|
||||
|
||||
included
|
||||
end
|
||||
|
||||
# Devuelve los strong params para el layout
|
||||
|
@ -133,8 +147,10 @@ class Post < OpenStruct
|
|||
{ metadata.to_s => template.value }
|
||||
end.compact.inject(:merge)
|
||||
|
||||
# TODO: Convertir a Metadata?
|
||||
# Asegurarse que haya un layout
|
||||
yaml['layout'] = layout.name.to_s
|
||||
yaml['uuid'] = uuid.value
|
||||
# Y que no se procese liquid
|
||||
yaml['liquid'] = false
|
||||
yaml['usuaries'] = usuaries.map(&:id).uniq
|
||||
|
@ -156,8 +172,8 @@ class Post < OpenStruct
|
|||
|
||||
# Guarda los cambios
|
||||
# rubocop:disable Metrics/CyclomaticComplexity
|
||||
def save(validation = true)
|
||||
return false if validation && !valid?
|
||||
def save(validate: true)
|
||||
return false if validate && !valid?
|
||||
# Salir si tenemos que cambiar el nombre del archivo y no pudimos
|
||||
return false if !new? && path_changed? && !update_path!
|
||||
return false unless save_attributes!
|
||||
|
@ -176,7 +192,7 @@ class Post < OpenStruct
|
|||
return unless written?
|
||||
|
||||
document.path = path.absolute
|
||||
document.read
|
||||
document.read!
|
||||
end
|
||||
|
||||
def new?
|
||||
|
@ -197,12 +213,6 @@ class Post < OpenStruct
|
|||
|
||||
# Detecta si el artículo es válido para guardar
|
||||
def valid?
|
||||
validate
|
||||
errors.blank?
|
||||
end
|
||||
|
||||
# Requisitos para que el post sea válido
|
||||
def validate
|
||||
self.errors = {}
|
||||
|
||||
layout.metadata.keys.map(&:to_sym).each do |metadata|
|
||||
|
@ -210,8 +220,9 @@ class Post < OpenStruct
|
|||
|
||||
errors[metadata] = template.errors unless template.valid?
|
||||
end
|
||||
|
||||
errors.blank?
|
||||
end
|
||||
alias validate! validate
|
||||
|
||||
# Guarda los cambios en el archivo destino
|
||||
def write
|
||||
|
@ -239,18 +250,35 @@ class Post < OpenStruct
|
|||
end
|
||||
alias update update_attributes
|
||||
|
||||
# El Document guarda un Array de los ids de Usuarie. Si está vacío,
|
||||
# no hacemos una consulta vacía. Si no, traemos todes les Usuaries
|
||||
# por su id y convertimos a Array para poder agregar o quitar luego
|
||||
# sin pasar por ActiveRecord.
|
||||
def usuaries
|
||||
@usuaries ||= Usuarie.where(id: document_usuaries).to_a
|
||||
@usuaries ||= if (d = document_usuaries).empty?
|
||||
[]
|
||||
else
|
||||
Usuarie.where(id: d).to_a
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Levanta un error si al construir el artículo no pasamos un atributo.
|
||||
def default_attributes_missing(**args)
|
||||
DEFAULT_ATTRIBUTES.each do |attr|
|
||||
i18n = I18n.t("exceptions.post.#{attr}_missing")
|
||||
|
||||
raise ArgumentError, i18n unless args[attr].present?
|
||||
end
|
||||
end
|
||||
|
||||
def document_usuaries
|
||||
document.data.fetch('usuaries', [])
|
||||
end
|
||||
|
||||
def new_attribute_was(method)
|
||||
attr_was = (attribute_name(method).to_s + '_was').to_sym
|
||||
attr_was = "#{method}_was".to_sym
|
||||
return attr_was if singleton_class.method_defined? attr_was
|
||||
|
||||
define_singleton_method(attr_was) do
|
||||
|
@ -265,7 +293,7 @@ class Post < OpenStruct
|
|||
|
||||
# Pregunta si el atributo cambió
|
||||
def new_attribute_changed(method)
|
||||
attr_changed = (attribute_name(method).to_s + '_changed?').to_sym
|
||||
attr_changed = "#{method}_changed?".to_sym
|
||||
|
||||
return attr_changed if singleton_class.method_defined? attr_changed
|
||||
|
||||
|
@ -314,6 +342,13 @@ class Post < OpenStruct
|
|||
required: true)
|
||||
end
|
||||
|
||||
def load_uuid!
|
||||
self.uuid = MetadataUuid.new(document: document, site: site,
|
||||
layout: layout, name: :uuid,
|
||||
type: :uuid, post: self,
|
||||
required: true)
|
||||
end
|
||||
|
||||
# Ejecuta la acción de guardado en cada atributo
|
||||
def save_attributes!
|
||||
attributes.map do |attr|
|
||||
|
|
|
@ -31,15 +31,18 @@ class PostRelation < Array
|
|||
post
|
||||
end
|
||||
|
||||
alias sort_by_generic sort_by
|
||||
alias sort_by_generic! sort_by!
|
||||
|
||||
# Permite ordenar los artículos por sus atributos
|
||||
#
|
||||
# XXX: Prestar atención cuando estamos mezclando artículos con
|
||||
# diferentes tipos de atributos.
|
||||
def sort_by!(*attrs)
|
||||
sort_by_generic! do |post|
|
||||
def sort_by(*attrs)
|
||||
sort_by_generic do |post|
|
||||
attrs.map do |attr|
|
||||
# TODO: detectar el tipo de atributo faltante y obtener el valor
|
||||
# por defecto para hacer la comparación
|
||||
return 0 unless post.attributes.include? attr
|
||||
|
||||
post.public_send(attr).value
|
||||
|
@ -47,12 +50,20 @@ class PostRelation < Array
|
|||
end
|
||||
end
|
||||
|
||||
def sort_by!(*attrs)
|
||||
replace sort_by(*attrs)
|
||||
end
|
||||
|
||||
alias find_generic find
|
||||
|
||||
# Encontra un post por su id convertido a SHA1
|
||||
def find(id, sha1: false)
|
||||
# Encontrar un post por su UUID
|
||||
def find(id, uuid: false)
|
||||
find_generic do |p|
|
||||
p.sha1 == (sha1 ? id : Digest::SHA1.hexdigest(id))
|
||||
if uuid
|
||||
p.uuid.value == id
|
||||
else
|
||||
p.id == id
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -65,8 +76,10 @@ class PostRelation < Array
|
|||
end
|
||||
|
||||
# Intenta guardar todos y devuelve true si pudo
|
||||
def save_all
|
||||
map(&:save).all?
|
||||
def save_all(validate: true)
|
||||
map do |post|
|
||||
post.save(validate: validate)
|
||||
end.all?
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
@ -9,7 +9,10 @@ class Site < ApplicationRecord
|
|||
# @see app/services/site_service.rb
|
||||
DEPLOYS = %i[local www zip].freeze
|
||||
|
||||
validates :name, uniqueness: true, hostname: true
|
||||
validates :name, uniqueness: true, hostname: {
|
||||
allow_root_label: true
|
||||
}
|
||||
|
||||
validates :design_id, presence: true
|
||||
validate :deploy_local_presence
|
||||
validates_inclusion_of :status, in: %w[waiting enqueued building]
|
||||
|
@ -173,14 +176,10 @@ class Site < ApplicationRecord
|
|||
|
||||
@posts[lang] = PostRelation.new site: self
|
||||
|
||||
# Jekyll lee los documentos en orden cronológico pero los invierte
|
||||
# durante la renderización. Usamos el orden cronológico inverso por
|
||||
# defecto para mostrar los artículos más nuevos primero.
|
||||
docs = collections[lang.to_s].try(:docs).try(:sort) { |a, b| b <=> a }
|
||||
# No fallar si no existe colección para este idioma
|
||||
# XXX: queremos fallar silenciosamente?
|
||||
(docs || []).each do |doc|
|
||||
layout = layouts[doc.data['layout'].to_sym]
|
||||
(collections[lang.to_s].try(:docs) || []).each do |doc|
|
||||
layout = layouts[Post.find_layout(doc)]
|
||||
|
||||
@posts[lang].build(document: doc, layout: layout, lang: lang)
|
||||
end
|
||||
|
|
|
@ -79,7 +79,7 @@ class Site
|
|||
end
|
||||
|
||||
# Guardamos los cambios
|
||||
unless doc.save(false)
|
||||
unless doc.save(validate: false)
|
||||
log.write "#{doc.path.relative} no se pudo guardar\n"
|
||||
end
|
||||
|
||||
|
|
|
@ -8,8 +8,8 @@ PostService = Struct.new(:site, :usuarie, :post, :params, keyword_init: true) do
|
|||
#
|
||||
# @return Post
|
||||
def create
|
||||
# TODO: Implementar layout
|
||||
self.post = site.posts(lang: params[:post][:lang] || I18n.locale).build
|
||||
self.post = site.posts(lang: params[:post][:lang] || I18n.locale)
|
||||
.build(layout: params[:post][:layout])
|
||||
post.usuaries << usuarie
|
||||
params[:post][:draft] = true if site.invitade? usuarie
|
||||
|
||||
|
@ -40,25 +40,31 @@ PostService = Struct.new(:site, :usuarie, :post, :params, keyword_init: true) do
|
|||
end
|
||||
|
||||
# Reordena todos los posts que soporten orden de acuerdo a un hash de
|
||||
# ids y nuevas posiciones. La posición actual la da la posición en
|
||||
# uuids y nuevas posiciones. La posición actual la da la posición en
|
||||
# el array.
|
||||
#
|
||||
# { sha1 => 2, sha1 => 1, sha1 => 0 }
|
||||
# { uuid => 2, uuid => 1, uuid => 0 }
|
||||
def reorder
|
||||
posts = site.posts(lang: lang)
|
||||
reorder = params.require(:post).permit(reorder: {}).try(:[], :reorder)
|
||||
modified = PostRelation.new(site: site)
|
||||
|
||||
files = reorder.keys.map do |id|
|
||||
post = posts.find(id, sha1: true)
|
||||
files = reorder.keys.map do |uuid|
|
||||
post = posts.find(uuid, uuid: true)
|
||||
order = reorder[uuid].to_i
|
||||
|
||||
next unless post
|
||||
next unless post.attributes.include? :order
|
||||
next if post.order.value == order
|
||||
|
||||
post.usuaries << usuarie
|
||||
post.order.value = reorder[id].to_i
|
||||
modified << post
|
||||
post.order.value = order
|
||||
post.path.absolute
|
||||
end.compact
|
||||
|
||||
# TODO: Implementar transacciones!
|
||||
posts.save_all && commit(action: :reorder, file: files)
|
||||
modified.save_all(validate: false) &&
|
||||
commit(action: :reorder, file: files)
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
= t('.greeting', recipient: @email)
|
||||
\
|
||||
= t('.instruction')
|
||||
\
|
||||
= confirmation_url(@resource, confirmation_token: @token)
|
6
app/views/devise/mailer/email_changed.text.haml
Normal file
6
app/views/devise/mailer/email_changed.text.haml
Normal file
|
@ -0,0 +1,6 @@
|
|||
= t('.greeting', recipient: @email)
|
||||
\
|
||||
- if @resource.try(:unconfirmed_email?)
|
||||
= t('.message', email: @resource.unconfirmed_email)
|
||||
- else
|
||||
= t('.message', email: @resource.email)
|
3
app/views/devise/mailer/password_change.text.haml
Normal file
3
app/views/devise/mailer/password_change.text.haml
Normal file
|
@ -0,0 +1,3 @@
|
|||
= t('.greeting', recipient: @resource.email)
|
||||
\
|
||||
= t('.message')
|
|
@ -0,0 +1,9 @@
|
|||
= t('.greeting', recipient: @resource.email)
|
||||
\
|
||||
= t('.instruction')
|
||||
\
|
||||
= edit_password_url(@resource, reset_password_token: @token)
|
||||
\
|
||||
= t('.instruction_2')
|
||||
\
|
||||
= t('.instruction_3')
|
7
app/views/devise/mailer/unlock_instructions.text.haml
Normal file
7
app/views/devise/mailer/unlock_instructions.text.haml
Normal file
|
@ -0,0 +1,7 @@
|
|||
= t('.greeting', recipient: @resource.email)
|
||||
\
|
||||
= t('.message')
|
||||
\
|
||||
= t('.instruction')
|
||||
\
|
||||
= unlock_url(@resource, unlock_token: @token)
|
|
@ -5,9 +5,8 @@
|
|||
|
||||
.row.align-items-center.justify-content-center.full-height
|
||||
.col-md-5.align-self-center
|
||||
.sr-only
|
||||
%h2= t('.sign_up')
|
||||
%p= t('.help')
|
||||
%h2= t('.sign_up')
|
||||
%p= t('.help')
|
||||
|
||||
= form_for(resource,
|
||||
as: resource_name,
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
%br/
|
||||
|
||||
- if devise_mapping.registerable? && controller_name != 'registrations'
|
||||
= link_to t('.sign_up'), new_registration_path(resource_name)
|
||||
= link_to t('.sign_up'), new_registration_path(resource_name),
|
||||
class: 'btn btn-lg btn-block btn-success'
|
||||
%br/
|
||||
|
||||
- if devise_mapping.recoverable?
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
%nav.navbar
|
||||
%a.navbar-brand.d-none.d-sm-block{ href: '/' }
|
||||
= inline_svg 'sutty.svg', class: 'black', aria: true,
|
||||
= inline_svg_tag 'sutty.svg', class: 'black', aria: true,
|
||||
title: t('svg.sutty.title'), desc: t('svg.sutty.desc')
|
||||
|
||||
- if crumbs
|
||||
|
@ -21,9 +21,6 @@
|
|||
|
||||
- if current_usuarie
|
||||
%ul.navbar-nav
|
||||
%li.nav-item
|
||||
= link_to t('.mutual_aid'), mutual_aid_url(local_channel),
|
||||
class: 'btn'
|
||||
%li.nav-item
|
||||
= link_to t('.logout'), destroy_usuarie_session_path,
|
||||
method: :delete, role: 'button', class: 'btn'
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
= yield
|
||||
|
||||
\
|
||||
= '-- '
|
||||
= t('.signature')
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
-# Botones de guardado
|
||||
= render 'posts/submit', site: site, post: post
|
||||
|
||||
= hidden_field_tag 'post[layout]', params[:layout] || 'post'
|
||||
|
||||
-# Dibuja cada atributo
|
||||
- post.attributes.each do |attribute|
|
||||
- metadata = post.send(attribute)
|
||||
|
|
3
app/views/posts/attribute_ro/_color.haml
Normal file
3
app/views/posts/attribute_ro/_color.haml
Normal file
|
@ -0,0 +1,3 @@
|
|||
%tr{ id: attribute }
|
||||
%th= post_label_t(attribute, post: post)
|
||||
%td{ style: "background-color: #{metadata.value}" } = metadata.value
|
|
@ -1,3 +1,3 @@
|
|||
%tr{ id: attribute }
|
||||
%th= post_label_t(attribute, post: post)
|
||||
%td= sanitize_markdown metadata.value, tags: tags
|
||||
%td= metadata.value
|
||||
|
|
4
app/views/posts/attribute_ro/_email.haml
Normal file
4
app/views/posts/attribute_ro/_email.haml
Normal file
|
@ -0,0 +1,4 @@
|
|||
%tr{ id: attribute }
|
||||
%th= post_label_t(attribute, post: post)
|
||||
%td
|
||||
%a{ href: "mailto:#{metadata.value}" }= metadata.value
|
|
@ -2,6 +2,5 @@
|
|||
%th= post_label_t(attribute, :path, post: post)
|
||||
%td
|
||||
- if metadata.value['path'].present?
|
||||
%figure
|
||||
= link_to url_for(metadata.static_file)
|
||||
%figcaption= metadata.value['description']
|
||||
= link_to t('.download'), url_for(metadata.static_file)
|
||||
%p= metadata.value['description']
|
||||
|
|
9
app/views/posts/attribute_ro/_geo.haml
Normal file
9
app/views/posts/attribute_ro/_geo.haml
Normal file
|
@ -0,0 +1,9 @@
|
|||
- lat = metadata.value['lat']
|
||||
- lng = metadata.value['lng']
|
||||
%tr{ id: attribute }
|
||||
%th= post_label_t(attribute, post: post)
|
||||
%td
|
||||
= link_to t('posts.attributes.geo.uri'), "geo:#{lat},#{lng}"
|
||||
%br/
|
||||
= link_to t('posts.attributes.geo.osm'),
|
||||
"https://www.openstreetmap.org/?mlat=#{lat}&mlon=#{lng}#map=9/#{lat}/#{lng}"
|
11
app/views/posts/attribute_ro/_related_posts.haml
Normal file
11
app/views/posts/attribute_ro/_related_posts.haml
Normal file
|
@ -0,0 +1,11 @@
|
|||
%tr{ id: attribute }
|
||||
%th= post_label_t(attribute, post: post)
|
||||
%td
|
||||
%ul
|
||||
- metadata.value.each do |v|
|
||||
- p = site.posts(lang: post.lang.value).find(v, uuid: true)
|
||||
-#
|
||||
XXX: Ignorar todos los posts no encontrados (ej: fueron
|
||||
borrados o el uuid cambió)
|
||||
- next unless p
|
||||
%li= link_to p.title.value, site_post_path(site, p.id)
|
3
app/views/posts/attribute_ro/_uuid.haml
Normal file
3
app/views/posts/attribute_ro/_uuid.haml
Normal file
|
@ -0,0 +1,3 @@
|
|||
%tr{ id: attribute }
|
||||
%th= post_label_t(attribute, post: post)
|
||||
%td= metadata.value
|
6
app/views/posts/attributes/_color.haml
Normal file
6
app/views/posts/attributes/_color.haml
Normal file
|
@ -0,0 +1,6 @@
|
|||
.form-group
|
||||
= label_tag "post_#{attribute}", post_label_t(attribute, post: post)
|
||||
= color_field 'post', attribute, value: metadata.value,
|
||||
**field_options(attribute, metadata)
|
||||
= render 'posts/attribute_feedback',
|
||||
post: post, attribute: attribute, metadata: metadata
|
|
@ -1,6 +1,6 @@
|
|||
.form-group
|
||||
= label_tag "post_#{attribute}", post_label_t(attribute, post: post)
|
||||
= date_field 'post', attribute, value: metadata.value.strftime('%F'),
|
||||
= date_field 'post', attribute, value: metadata.value.to_date.strftime('%F'),
|
||||
**field_options(attribute, metadata)
|
||||
= render 'posts/attribute_feedback',
|
||||
post: post, attribute: attribute, metadata: metadata
|
||||
|
|
6
app/views/posts/attributes/_email.haml
Normal file
6
app/views/posts/attributes/_email.haml
Normal file
|
@ -0,0 +1,6 @@
|
|||
.form-group
|
||||
= label_tag "post_#{attribute}", post_label_t(attribute, post: post)
|
||||
= email_field 'post', attribute, value: metadata.value,
|
||||
**field_options(attribute, metadata)
|
||||
= render 'posts/attribute_feedback',
|
||||
post: post, attribute: attribute, metadata: metadata
|
19
app/views/posts/attributes/_geo.haml
Normal file
19
app/views/posts/attributes/_geo.haml
Normal file
|
@ -0,0 +1,19 @@
|
|||
.row
|
||||
.col
|
||||
.form-group
|
||||
= label_tag "post_#{attribute}_lat",
|
||||
post_label_t(attribute, :lat, post: post)
|
||||
= text_field(*field_name_for('post', attribute, :lat),
|
||||
value: metadata.value['lat'],
|
||||
**field_options(attribute, metadata))
|
||||
= render 'posts/attribute_feedback',
|
||||
post: post, attribute: [attribute, :lat], metadata: metadata
|
||||
.col
|
||||
.form-group
|
||||
= label_tag "post_#{attribute}_lng",
|
||||
post_label_t(attribute, :lng, post: post)
|
||||
= text_field(*field_name_for('post', attribute, :lng),
|
||||
value: metadata.value['lng'],
|
||||
**field_options(attribute, metadata))
|
||||
= render 'posts/attribute_feedback',
|
||||
post: post, attribute: [attribute, :lat], metadata: metadata
|
19
app/views/posts/attributes/_related_posts.haml
Normal file
19
app/views/posts/attributes/_related_posts.haml
Normal file
|
@ -0,0 +1,19 @@
|
|||
.form-group
|
||||
= label_tag "post_#{attribute}", post_label_t(attribute, post: post)
|
||||
|
||||
.mapable{ data: { values: metadata.value.to_json,
|
||||
'default-values': metadata.values.to_json,
|
||||
name: "post[#{attribute}][]", list: id_for_datalist(attribute),
|
||||
remove: 'false', legend: post_label_t(attribute, post: post),
|
||||
described: id_for_help(attribute) } }
|
||||
|
||||
= text_field(*field_name_for('post', attribute, '[]'),
|
||||
value: metadata.value.join(', '),
|
||||
**field_options(attribute, metadata))
|
||||
|
||||
= render 'posts/attribute_feedback',
|
||||
post: post, attribute: attribute, metadata: metadata
|
||||
|
||||
%datalist{ id: id_for_datalist(attribute) }
|
||||
- metadata.values.keys.each do |value|
|
||||
%option{ value: value }
|
1
app/views/posts/attributes/_uuid.haml
Normal file
1
app/views/posts/attributes/_uuid.haml
Normal file
|
@ -0,0 +1 @@
|
|||
-# nada
|
|
@ -25,62 +25,61 @@
|
|||
|
||||
%section.col
|
||||
= render 'layouts/flash'
|
||||
- if @posts.present?
|
||||
.row
|
||||
.col
|
||||
= form_tag site_posts_reorder_path, method: :post do
|
||||
= submit_tag t('posts.reorder'), class: 'btn submit-reorder d-none'
|
||||
-# TODO: Permitir cambiar el idioma
|
||||
%table.table.table-condensed.table-draggable
|
||||
%tbody
|
||||
- @posts.each_with_index do |post, i|
|
||||
-#
|
||||
saltearse el post a menos que esté en la categoría por
|
||||
la que estamos filtrando
|
||||
- if @category
|
||||
- next unless post.attributes.include? :categories
|
||||
- next unless post.categories.value.include?(@category)
|
||||
- if @layout
|
||||
- next unless post.layout.name == @layout
|
||||
- next unless policy(post).show?
|
||||
%tr
|
||||
%td
|
||||
.handle
|
||||
= image_tag 'arrows-alt-v.svg'
|
||||
= hidden_field 'post[reorder]', post.sha1,
|
||||
value: i, class: 'reorder'
|
||||
%td
|
||||
%small
|
||||
= link_to post.layout.name.to_s.humanize,
|
||||
site_posts_path(@site, layout: post.layout.name)
|
||||
%br/
|
||||
= link_to post.title.value,
|
||||
site_post_path(@site, post.id)
|
||||
- if post.attributes.include? :draft
|
||||
- if post.draft.value
|
||||
%span.badge.badge-primary
|
||||
= post_label_t(:draft, post: post)
|
||||
- if post.attributes.include? :categories
|
||||
- unless post.categories.value.empty?
|
||||
%br
|
||||
%small
|
||||
- post.categories.value.each do |c|
|
||||
= link_to c, site_posts_path(@site, category: c)
|
||||
|
||||
%td
|
||||
= post.date.value.strftime('%F')
|
||||
%br/
|
||||
= post.try(:order).try(:value)
|
||||
%td
|
||||
- if policy(post).edit?
|
||||
= link_to t('posts.edit'),
|
||||
edit_site_post_path(@site, post.id),
|
||||
class: 'btn'
|
||||
- if policy(post).destroy?
|
||||
= link_to t('posts.destroy'),
|
||||
site_post_path(@site, post.id),
|
||||
class: 'btn',
|
||||
method: :delete,
|
||||
data: { confirm: t('posts.confirm_destroy') }
|
||||
- else
|
||||
- if @posts.empty?
|
||||
%h2= t('posts.none')
|
||||
- else
|
||||
= form_tag site_posts_reorder_path, method: :post do
|
||||
= submit_tag t('posts.reorder'), class: 'btn submit-reorder'
|
||||
-# TODO: Permitir cambiar el idioma
|
||||
%table.table.table-condensed.table-draggable
|
||||
%tbody
|
||||
- @posts.each_with_index do |post, i|
|
||||
-#
|
||||
saltearse el post a menos que esté en la categoría por
|
||||
la que estamos filtrando
|
||||
- if @category
|
||||
- next unless post.attributes.include? :categories
|
||||
- next unless post.categories.value.include?(@category)
|
||||
- if @layout
|
||||
- next unless post.layout.name == @layout
|
||||
- next unless @usuarie || policy(post).show?
|
||||
%tr
|
||||
%td
|
||||
.handle
|
||||
= image_tag 'arrows-alt-v.svg'
|
||||
-# Orden más alto es mayor prioridad
|
||||
= hidden_field 'post[reorder]', post.uuid.value,
|
||||
value: @posts.length - i, class: 'reorder'
|
||||
%td
|
||||
%small
|
||||
= link_to post.layout.name.to_s.humanize,
|
||||
site_posts_path(@site, layout: post.layout.name)
|
||||
%br/
|
||||
= link_to post.title.value,
|
||||
site_post_path(@site, post.id)
|
||||
- if post.attributes.include? :draft
|
||||
- if post.draft.value
|
||||
%span.badge.badge-primary
|
||||
= post_label_t(:draft, post: post)
|
||||
- if post.attributes.include? :categories
|
||||
- unless post.categories.value.empty?
|
||||
%br
|
||||
%small
|
||||
- post.categories.value.each do |c|
|
||||
= link_to c, site_posts_path(@site, category: c)
|
||||
|
||||
%td
|
||||
= post.date.value.strftime('%F')
|
||||
%br/
|
||||
= post.try(:order).try(:value)
|
||||
%td
|
||||
- if @usuarie || policy(post).edit?
|
||||
= link_to t('posts.edit'),
|
||||
edit_site_post_path(@site, post.id),
|
||||
class: 'btn'
|
||||
- if @usuarie || policy(post).destroy?
|
||||
= link_to t('posts.destroy'),
|
||||
site_post_path(@site, post.id),
|
||||
class: 'btn',
|
||||
method: :delete,
|
||||
data: { confirm: t('posts.confirm_destroy') }
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
= render("posts/attribute_ro/#{metadata.type}",
|
||||
post: @post, attribute: attr,
|
||||
metadata: metadata,
|
||||
site: @site,
|
||||
tags: all_html_tags)
|
||||
|
||||
-# Mostrar todo lo que no va en el front_matter (el contenido)
|
||||
|
@ -33,4 +34,4 @@
|
|||
- next if @post.send(attr).front_matter?
|
||||
|
||||
%section{ id: attr }
|
||||
= sanitize_markdown @post.send(attr).value, tags: all_html_tags
|
||||
= raw @post.send(attr).value
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
link: nil
|
||||
- else
|
||||
= form_tag site_enqueue_path(site),
|
||||
method: :post, class: 'form-inline' do
|
||||
method: :post, class: 'form-inline inline' do
|
||||
= button_tag type: 'submit',
|
||||
class: 'btn no-border-radius',
|
||||
title: t('help.sites.enqueue'),
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
= f.text_field :name,
|
||||
class: form_control(site, :name),
|
||||
required: true,
|
||||
pattern: '^([a-z0-9][a-z0-9\-]*)?[a-z0-9]$',
|
||||
pattern: '^([a-z0-9][a-z0-9\-]*)?[a-z0-9\.]$',
|
||||
minlength: 1,
|
||||
maxlength: 63
|
||||
- if invalid? site, :name
|
||||
|
@ -86,14 +86,20 @@
|
|||
|
||||
%hr/
|
||||
|
||||
.form-group
|
||||
%h2= t('.deploys.title')
|
||||
%p.lead= t('.help.deploys')
|
||||
- if site.persisted?
|
||||
.form-group
|
||||
%h2= t('.deploys.title')
|
||||
%p.lead= t('.help.deploys')
|
||||
|
||||
= f.fields_for :deploys do |deploy|
|
||||
= render "deploys/#{deploy.object.type.underscore}",
|
||||
deploy: deploy, site: site
|
||||
%hr/
|
||||
- else
|
||||
= f.fields_for :deploys do |deploy|
|
||||
= render "deploys/#{deploy.object.type.underscore}",
|
||||
deploy: deploy, site: site
|
||||
%hr/
|
||||
- next unless deploy.object.is_a? DeployLocal
|
||||
|
||||
= deploy.hidden_field :type
|
||||
|
||||
.form-group
|
||||
= f.submit submit, class: 'btn btn-lg btn-block'
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
= render 'layouts/breadcrumb',
|
||||
crumbs: [link_to(t('sites.index.title'), sites_path),
|
||||
t('.title', site: @site.name)]
|
||||
.row
|
||||
.col
|
||||
.row.justify-content-center
|
||||
.col-md-8
|
||||
%h1= t('.title', site: @site.name)
|
||||
|
||||
= render 'form', site: @site, submit: t('.submit')
|
||||
|
|
|
@ -9,6 +9,9 @@
|
|||
class: 'btn'
|
||||
|
||||
%section.col
|
||||
- if @sites.empty?
|
||||
:markdown
|
||||
#{t('.welcome')}
|
||||
%table.table.table-condensed
|
||||
%tbody
|
||||
- @sites.each do |site|
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
= render 'layouts/breadcrumb',
|
||||
crumbs: [link_to(t('sites.index.title'), sites_path), t('.title')]
|
||||
|
||||
.row
|
||||
.col
|
||||
.row.justify-content-center
|
||||
.col-md-8
|
||||
%h1= t('.title')
|
||||
%p.lead= t('.help')
|
||||
|
||||
= render 'form', site: @site, submit: t('.submit')
|
||||
|
|
77
config/blazer.yml
Normal file
77
config/blazer.yml
Normal file
|
@ -0,0 +1,77 @@
|
|||
# see https://github.com/ankane/blazer for more info
|
||||
|
||||
data_sources:
|
||||
main:
|
||||
url: <%= ENV["BLAZER_DATABASE_URL"] %>
|
||||
|
||||
# statement timeout, in seconds
|
||||
# none by default
|
||||
# timeout: 15
|
||||
|
||||
# caching settings
|
||||
# can greatly improve speed
|
||||
# off by default
|
||||
# cache:
|
||||
# mode: slow # or all
|
||||
# expires_in: 60 # min
|
||||
# slow_threshold: 15 # sec, only used in slow mode
|
||||
|
||||
# wrap queries in a transaction for safety
|
||||
# not necessary if you use a read-only user
|
||||
# true by default
|
||||
# use_transaction: false
|
||||
|
||||
smart_variables:
|
||||
site_id: 'select id, name from sites order by name asc'
|
||||
# zone_id: "SELECT id, name FROM zones ORDER BY name ASC"
|
||||
# period: ["day", "week", "month"]
|
||||
# status: {0: "Active", 1: "Archived"}
|
||||
|
||||
linked_columns:
|
||||
# user_id: "/admin/users/{value}"
|
||||
|
||||
smart_columns:
|
||||
site_id: 'select id, name from sites where id in {value}'
|
||||
# user_id: "SELECT id, name FROM users WHERE id IN {value}"
|
||||
|
||||
# create audits
|
||||
audit: true
|
||||
|
||||
# change the time zone
|
||||
# time_zone: "Pacific Time (US & Canada)"
|
||||
|
||||
# class name of the user model
|
||||
user_class: Usuarie
|
||||
|
||||
# method name for the current user
|
||||
user_method: current_usuarie
|
||||
|
||||
# method name for the display name
|
||||
user_name: email
|
||||
|
||||
# custom before_action to use for auth
|
||||
# before_action_method: require_admin
|
||||
|
||||
# email to send checks from
|
||||
from_email: blazer@<%= ENV.fetch('SUTTY', 'sutty.nl') %>
|
||||
|
||||
# webhook for Slack
|
||||
# slack_webhook_url: <%= ENV["BLAZER_SLACK_WEBHOOK_URL"] %>
|
||||
|
||||
check_schedules:
|
||||
- "1 day"
|
||||
- "1 hour"
|
||||
- "5 minutes"
|
||||
|
||||
override_csp: true
|
||||
|
||||
# enable anomaly detection
|
||||
# note: with trend, time series are sent to https://trendapi.org
|
||||
# anomaly_checks: trend / r
|
||||
|
||||
# enable forecasting
|
||||
# note: with trend, time series are sent to https://trendapi.org
|
||||
# forecasting: trend
|
||||
|
||||
# enable map
|
||||
# mapbox_access_token: <%= ENV["MAPBOX_ACCESS_TOKEN"] %>
|
|
@ -6,31 +6,35 @@
|
|||
# For further information see the following documentation
|
||||
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
|
||||
|
||||
# Rails.application.config.content_security_policy do |policy|
|
||||
# policy.default_src :self, :https
|
||||
# policy.font_src :self, :https, :data
|
||||
# policy.img_src :self, :https, :data
|
||||
# policy.object_src :none
|
||||
# policy.script_src :self, :https
|
||||
# policy.style_src :self, :https
|
||||
# # If you are using webpack-dev-server then specify
|
||||
# # webpack-dev-server host
|
||||
# policy.connect_src :self, :https, "http://localhost:3035",
|
||||
# "ws://localhost:3035" if Rails.env.development?
|
||||
Rails.application.config.content_security_policy do |policy|
|
||||
policy.default_src :self
|
||||
# XXX: Varios scripts generan estilos en línea
|
||||
policy.style_src :self, :unsafe_inline
|
||||
# Repetimos la default para poder saber cuál es la política en falta
|
||||
policy.script_src :self
|
||||
policy.font_src :self
|
||||
# TODO: Permitimos cargar imágenes remotas?
|
||||
# XXX: Los íconos de Trix se cargan vía data:
|
||||
policy.img_src :self, :data
|
||||
# Ya no usamos applets!
|
||||
policy.object_src :none
|
||||
if Rails.env.development?
|
||||
policy.connect_src :self,
|
||||
'http://localhost:3035',
|
||||
'ws://localhost:3035'
|
||||
end
|
||||
|
||||
# # Specify URI for violation reports
|
||||
# # policy.report_uri "/csp-violation-report-endpoint"
|
||||
# end
|
||||
# Specify URI for violation reports
|
||||
policy.report_uri "https://api.#{ENV.fetch('SUTTY_WITH_PORT', 'sutty.nl')}/v1/csp_reports.json"
|
||||
end
|
||||
|
||||
# If you are using UJS then enable automatic nonce generation
|
||||
# Rails.application.config.content_security_policy_nonce_generator =
|
||||
# -> request { SecureRandom.base64(16) }
|
||||
# Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) }
|
||||
|
||||
# Set the nonce only to specific directives
|
||||
# Rails.application.config.content_security_policy_nonce_directives =
|
||||
# %w(script-src)
|
||||
# Rails.application.config.content_security_policy_nonce_directives = %w(script-src)
|
||||
|
||||
# Report CSP violations to a specified URI
|
||||
# For further information see the following documentation:
|
||||
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only
|
||||
# Rails.application.config.content_security_policy_report_only = true
|
||||
Rails.application.config.content_security_policy_report_only = false
|
||||
|
|
|
@ -1,6 +1,36 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'jekyll/document'
|
||||
|
||||
String.include CoreExtensions::String::StripTags
|
||||
Jekyll::Document.include CoreExtensions::Jekyll::Document::Path
|
||||
|
||||
# Lazy Loading de Jekyll, deshabilitando la instanciación de elementos
|
||||
# que no necesitamos
|
||||
#
|
||||
# TODO: Aplicar monkey patches en otro lado...
|
||||
module Jekyll
|
||||
Site.class_eval do
|
||||
def setup
|
||||
ensure_not_in_dest
|
||||
end
|
||||
end
|
||||
|
||||
Reader.class_eval do
|
||||
def retrieve_posts(_); end
|
||||
|
||||
def retrieve_dirs(_, _, _); end
|
||||
|
||||
def retrieve_pages(_, _); end
|
||||
|
||||
def retrieve_static_files(_, _); end
|
||||
end
|
||||
|
||||
ThemeAssetsReader.class_eval do
|
||||
def read; end
|
||||
end
|
||||
|
||||
# Prevenir la lectura del documento
|
||||
Document.class_eval do
|
||||
alias_method :read!, :read
|
||||
def read; end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -24,7 +24,7 @@ Devise.setup do |config|
|
|||
# config.mailer = 'Devise::Mailer'
|
||||
|
||||
# Configure the parent class responsible to send e-mails.
|
||||
# config.parent_mailer = 'ActionMailer::Base'
|
||||
config.parent_mailer = 'ApplicationMailer'
|
||||
|
||||
# ==> ORM configuration
|
||||
# Load and configure the ORM. Supports :active_record (default) and
|
||||
|
@ -197,7 +197,7 @@ Devise.setup do |config|
|
|||
# website without confirming their account.
|
||||
# Default is 0.days, meaning the user cannot access the website
|
||||
# without confirming their account.
|
||||
# config.allow_unconfirmed_access_for = 2.days
|
||||
config.allow_unconfirmed_access_for = 2.days
|
||||
|
||||
# A period that the user is allowed to confirm their account before
|
||||
# their token becomes invalid. For example, if set to 3.days, the user
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
Rails.application.configure do
|
||||
next if ENV['RAILS_ENV'] == 'test'
|
||||
next unless ENV['RAILS_ENV'] == 'development'
|
||||
|
||||
domain = ENV.fetch('SUTTY', 'sutty.nl')
|
||||
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Be sure to restart your server when you modify this file.
|
||||
|
||||
# Add new mime types for use in respond_to blocks:
|
||||
# Mime::Type.register "text/richtext", :rtf
|
||||
Mime::Type.register 'application/csp-report', :json
|
||||
|
|
|
@ -44,7 +44,7 @@ en:
|
|||
locked: Your account is locked.
|
||||
not_found_in_database: Invalid %{authentication_keys} or password.
|
||||
timeout: Your session expired. Please sign in again to continue.
|
||||
unauthenticated: You need to sign in or sign up before continuing.
|
||||
unauthenticated: Hi! You need to sign in or sign up before managing your sites.
|
||||
unconfirmed: You have to confirm your email address before continuing.
|
||||
mailer:
|
||||
confirmation_instructions:
|
||||
|
@ -103,6 +103,7 @@ en:
|
|||
we_need_your_current_password_to_confirm_your_changes: We need your current password to confirm your changes
|
||||
new:
|
||||
sign_up: Sign up
|
||||
help: We only ask for an e-mail address and a password. The password is safely stored, no one else besides you knows it! You'll also receive an e-mail to confirm your account.
|
||||
signed_up: Welcome! You have signed up successfully.
|
||||
signed_up_but_inactive: You have signed up successfully. However, we could not sign you in because your account is not yet activated.
|
||||
signed_up_but_locked: You have signed up successfully. However, we could not sign you in because your account is locked.
|
||||
|
|
|
@ -16,7 +16,7 @@ es:
|
|||
last_sign_in_ip: IP del último inicio
|
||||
locked_at: Fecha de bloqueo
|
||||
password: Contraseña
|
||||
password_confirmation: Confirmación de la contraseña
|
||||
password_confirmation: Confirma tu contraseña
|
||||
remember_created_at: Fecha de 'Recordarme'
|
||||
remember_me: Recordarme
|
||||
reset_password_sent_at: Fecha de envío de código para contraseña
|
||||
|
@ -44,7 +44,7 @@ es:
|
|||
locked: Tu cuenta está bloqueada.
|
||||
not_found_in_database: "%{authentication_keys} o contraseña inválidos."
|
||||
timeout: Tu sesión expiró. Por favor, inicia sesión nuevamente para continuar.
|
||||
unauthenticated: Tienes que iniciar sesión o registrarte para poder continuar.
|
||||
unauthenticated: "¡Hola! Tienes que iniciar sesión o registrarte para poder gestionar tus sitios."
|
||||
unconfirmed: Te enviamos un correo electrónico para confirmar tu cuenta, por favor acéptalo para poder continuar.
|
||||
mailer:
|
||||
confirmation_instructions:
|
||||
|
@ -80,7 +80,7 @@ es:
|
|||
edit:
|
||||
change_my_password: Cambiar mi contraseña
|
||||
change_your_password: Cambia tu contraseña
|
||||
confirm_new_password: Confirme la nueva contraseña
|
||||
confirm_new_password: Confirma la nueva contraseña
|
||||
new_password: Nueva contraseña
|
||||
new:
|
||||
forgot_your_password: "¿Has olvidado tu contraseña?"
|
||||
|
@ -103,7 +103,7 @@ es:
|
|||
we_need_your_current_password_to_confirm_your_changes: Necesitamos tu contraseña actual para confirmar los cambios.
|
||||
new:
|
||||
sign_up: Registrarme por primera vez
|
||||
email: O simplemente continuar con tu dirección de correo y contraseña
|
||||
help: Para registrarte solo pedimos una dirección de correo y una contraseña. La contraseña se almacena de forma segura, ¡nadie más que vos la sabe! Recibirás un correo de confirmación de cuenta.
|
||||
signed_up: Bienvenide. Tu cuenta fue creada.
|
||||
signed_up_but_inactive: Tu cuenta ha sido creada correctamente. Sin embargo, no hemos podido iniciar la sesión porque tu cuenta aún no está activada.
|
||||
signed_up_but_locked: Tu cuenta ha sido creada correctamente. Sin embargo, no hemos podido iniciar la sesión porque que tu cuenta está bloqueada.
|
||||
|
|
|
@ -148,7 +148,6 @@ en:
|
|||
anexo: 'Appendix'
|
||||
simple: 'Simple'
|
||||
sites:
|
||||
static_file_migration: 'File migration'
|
||||
index: 'This is the list of sites you can edit.'
|
||||
edit_translations: "You can edit texts from your site other than
|
||||
posts', and you can also translate them to other languages."
|
||||
|
@ -247,11 +246,27 @@ en:
|
|||
average: 'Average building time'
|
||||
maximum: 'Maximum building time'
|
||||
sites:
|
||||
static_file_migration: 'File migration'
|
||||
index:
|
||||
title: 'Sites'
|
||||
pull: 'Upgrade'
|
||||
help: 'This is the list of sites you can edit'
|
||||
visit: 'Visitar el sitio'
|
||||
visit: 'Visit the site'
|
||||
welcome: |
|
||||
# Welcome!
|
||||
|
||||
You have no sites yet. You can generate all the sites you want
|
||||
with the **Create site** button.
|
||||
|
||||
Para ver los cambios, usa el botón **Publicar cambios** en cada
|
||||
sitio y espera unos segundos. También recibirás un correo de
|
||||
notificación.
|
||||
|
||||
To see your changes, use the **Publish changes** button on each
|
||||
site and wait a few seconds. You'll also receive an e-mail
|
||||
notification.
|
||||
|
||||
[Create my first site](/sites/new)
|
||||
repository:
|
||||
config: 'Changes in config'
|
||||
actions: 'Actions'
|
||||
|
@ -332,6 +347,9 @@ en:
|
|||
en: 'English'
|
||||
ar: 'Arabic'
|
||||
posts:
|
||||
attribute_ro:
|
||||
file:
|
||||
download: Download file
|
||||
show:
|
||||
front_matter: Post metadata
|
||||
submit:
|
||||
|
@ -348,6 +366,11 @@ en:
|
|||
required:
|
||||
label: ' (required)'
|
||||
feedback: 'This field cannot be empty!'
|
||||
uuid:
|
||||
label: 'Unique identifier'
|
||||
geo:
|
||||
uri: 'Open in app'
|
||||
osm: 'Open in web map'
|
||||
reorder: 'Reorder posts'
|
||||
sort:
|
||||
by: 'Sort by'
|
||||
|
|
|
@ -71,7 +71,7 @@ es:
|
|||
usuarie:
|
||||
email: 'Correo electrónico'
|
||||
password: 'Contraseña'
|
||||
password_confirmation: 'Confirmación de contraseña'
|
||||
password_confirmation: 'Confirma tu contraseña'
|
||||
current_password: 'Contraseña actual'
|
||||
lang: Idioma principal
|
||||
remember_me: Recordarme
|
||||
|
@ -149,7 +149,6 @@ es:
|
|||
anexo: 'Anexo'
|
||||
simple: 'Simple'
|
||||
sites:
|
||||
static_file_migration: 'Migración de archivos'
|
||||
index: 'Este es el listado de sitios que puedes editar.'
|
||||
edit_translations: 'Puedes editar los textos que salen en tu sitio
|
||||
que no corresponden a artículos aquí, además de traducirlos a
|
||||
|
@ -252,11 +251,24 @@ es:
|
|||
average: 'Tiempo promedio de generación'
|
||||
maximum: 'Tiempo máximo de generación'
|
||||
sites:
|
||||
static_file_migration: 'Migración de archivos'
|
||||
index:
|
||||
title: 'Sitios'
|
||||
pull: 'Actualizar'
|
||||
help: 'Este es el listado de sitios que puedes editar'
|
||||
visit: 'Visitar el sitio'
|
||||
welcome: |
|
||||
# ¡Bienvenide!
|
||||
|
||||
Todavía no tienes ningún sitio. Puedes crear todos los sitios
|
||||
que quieras usando el botón **Crear sitio**.
|
||||
|
||||
Para ver los cambios, usa el botón **Publicar cambios** en cada
|
||||
sitio y espera unos segundos. También recibirás un correo de
|
||||
notificación.
|
||||
|
||||
[Crear mi primer sitio](/sites/new)
|
||||
|
||||
repository:
|
||||
config: 'Cambios en la configuración'
|
||||
actions: 'Acciones'
|
||||
|
@ -272,6 +284,7 @@ es:
|
|||
new:
|
||||
title: 'Crear un sitio'
|
||||
submit: 'Crear sitio'
|
||||
help: 'Podrás editar estas opciones más adelante en la configuración del sitio.'
|
||||
edit:
|
||||
title: 'Editar %{site}'
|
||||
submit: 'Guardar cambios'
|
||||
|
@ -342,6 +355,9 @@ es:
|
|||
en: 'inglés'
|
||||
ar: 'árabe'
|
||||
posts:
|
||||
attribute_ro:
|
||||
file:
|
||||
download: Descargar archivo
|
||||
show:
|
||||
front_matter: Metadatos del artículo
|
||||
submit:
|
||||
|
@ -358,6 +374,11 @@ es:
|
|||
required:
|
||||
label: ' (requerido)'
|
||||
feedback: '¡Este campo no puede estar vacío!'
|
||||
uuid:
|
||||
label: 'Identificador único'
|
||||
geo:
|
||||
uri: 'Abrir en aplicación'
|
||||
osm: 'Abrir en mapa web'
|
||||
reorder: 'Reordenar artículos'
|
||||
sort:
|
||||
by: 'Ordenar por'
|
||||
|
|
|
@ -38,3 +38,22 @@ preload_app!
|
|||
plugin :tmp_restart
|
||||
|
||||
pidfile 'tmp/puma.pid'
|
||||
|
||||
# If you are preloading your application and using Active Record, it's
|
||||
# recommended that you close any connections to the database before workers
|
||||
# are forked to prevent connection leakage.
|
||||
#
|
||||
before_fork do
|
||||
ActiveRecord::Base.connection_pool.disconnect! if defined?(ActiveRecord)
|
||||
end
|
||||
|
||||
# The code in the `on_worker_boot` will be called if you are using
|
||||
# clustered mode by specifying a number of `workers`. After each worker
|
||||
# process is booted, this block will be run. If you are using the `preload_app!`
|
||||
# option, you will want to use this block to reconnect to any threads
|
||||
# or connections that may have been created at application boot, as Ruby
|
||||
# cannot share connections between processes.
|
||||
#
|
||||
on_worker_boot do
|
||||
ActiveRecord::Base.establish_connection if defined?(ActiveRecord)
|
||||
end
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
Rails.application.routes.draw do
|
||||
devise_for :usuaries
|
||||
mount Blazer::Engine, at: 'blazer'
|
||||
|
||||
root 'application#index'
|
||||
|
||||
|
@ -18,6 +19,7 @@ Rails.application.routes.draw do
|
|||
constraints subdomain: 'api' do
|
||||
scope module: 'api' do
|
||||
namespace :v1 do
|
||||
resources :csp_reports, only: %i[create]
|
||||
get 'sites/allowed', to: 'sites#allowed'
|
||||
resources :sites, only: %i[index]
|
||||
end
|
||||
|
|
|
@ -57,10 +57,10 @@ development:
|
|||
|
||||
# Reference: https://webpack.js.org/configuration/dev-server/
|
||||
dev_server:
|
||||
https: false
|
||||
host: localhost
|
||||
https: true
|
||||
host: <%= ENV.fetch('SUTTY', 'localhost') %>
|
||||
port: 3035
|
||||
public: localhost:3035
|
||||
public: <%= ENV.fetch('SUTTY', 'localhost') %>:3035
|
||||
hmr: false
|
||||
# Inline should be set to true if using HMR
|
||||
inline: true
|
||||
|
|
65
db/migrate/20200118155319_create_access_log.rb
Normal file
65
db/migrate/20200118155319_create_access_log.rb
Normal file
|
@ -0,0 +1,65 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class CreateAccessLog < ActiveRecord::Migration[6.0]
|
||||
def change
|
||||
create_table :access_logs, id: :uuid do |t|
|
||||
t.string :host, index: true
|
||||
|
||||
t.float :msec
|
||||
|
||||
t.string :server_protocol
|
||||
t.string :request_method
|
||||
t.string :request_completion
|
||||
|
||||
t.string :uri, index: true
|
||||
t.string :query_string
|
||||
t.integer :status, index: true
|
||||
|
||||
t.string :sent_http_content_type
|
||||
t.string :sent_http_content_encoding
|
||||
t.string :sent_http_etag
|
||||
t.datetime :sent_http_last_modified
|
||||
|
||||
t.string :http_accept
|
||||
t.string :http_accept_encoding
|
||||
t.string :http_accept_language
|
||||
|
||||
t.string :http_pragma
|
||||
t.string :http_cache_control
|
||||
t.string :http_if_none_match
|
||||
|
||||
t.string :http_dnt
|
||||
t.string :http_user_agent, index: true
|
||||
t.string :http_referer, index: true
|
||||
|
||||
t.float :request_time
|
||||
|
||||
t.integer :bytes_sent
|
||||
t.integer :body_bytes_sent
|
||||
t.integer :request_length
|
||||
|
||||
t.string :http_connection
|
||||
t.string :pipe
|
||||
t.integer :connection_requests
|
||||
|
||||
t.string :geoip2_data_country_name, index: true
|
||||
t.string :geoip2_data_city_name, index: true
|
||||
|
||||
t.string :ssl_server_name
|
||||
t.string :ssl_protocol
|
||||
t.string :ssl_early_data
|
||||
t.string :ssl_session_reused
|
||||
t.string :ssl_curves
|
||||
t.string :ssl_ciphers
|
||||
t.string :ssl_cipher
|
||||
|
||||
t.string :sent_http_x_xss_protection
|
||||
t.string :sent_http_x_frame_options
|
||||
t.string :sent_http_x_content_type_options
|
||||
t.string :sent_http_strict_transport_security
|
||||
|
||||
t.string :nginx_version
|
||||
t.integer :pid
|
||||
end
|
||||
end
|
||||
end
|
7
db/migrate/20200126175158_change_last_modified.rb
Normal file
7
db/migrate/20200126175158_change_last_modified.rb
Normal file
|
@ -0,0 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ChangeLastModified < ActiveRecord::Migration[6.0]
|
||||
def change
|
||||
change_column :access_logs, :sent_http_last_modified, :string
|
||||
end
|
||||
end
|
17
db/migrate/20200205173039_create_csp_reports.rb
Normal file
17
db/migrate/20200205173039_create_csp_reports.rb
Normal file
|
@ -0,0 +1,17 @@
|
|||
class CreateCspReports < ActiveRecord::Migration[6.0]
|
||||
def change
|
||||
create_table :csp_reports, id: :uuid do |t|
|
||||
t.timestamps
|
||||
|
||||
t.string :disposition
|
||||
t.string :referrer
|
||||
t.string :blocked_uri
|
||||
t.string :document_uri
|
||||
t.string :effective_directive
|
||||
t.string :original_policy
|
||||
t.string :script_sample
|
||||
t.string :status_code
|
||||
t.string :violated_directive
|
||||
end
|
||||
end
|
||||
end
|
7
db/migrate/20200206151057_add_source_to_csp_reports.rb
Normal file
7
db/migrate/20200206151057_add_source_to_csp_reports.rb
Normal file
|
@ -0,0 +1,7 @@
|
|||
class AddSourceToCspReports < ActiveRecord::Migration[6.0]
|
||||
def change
|
||||
add_column :csp_reports, :column_number, :integer
|
||||
add_column :csp_reports, :line_number, :integer
|
||||
add_column :csp_reports, :source_file, :string
|
||||
end
|
||||
end
|
51
db/migrate/20200206163257_install_blazer.rb
Normal file
51
db/migrate/20200206163257_install_blazer.rb
Normal file
|
@ -0,0 +1,51 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Blazer
|
||||
class InstallBlazer < ActiveRecord::Migration[6.0]
|
||||
def change
|
||||
return unless Rails.env.production?
|
||||
|
||||
create_table :blazer_queries do |t|
|
||||
t.references :creator
|
||||
t.string :name
|
||||
t.text :description
|
||||
t.text :statement
|
||||
t.string :data_source
|
||||
t.timestamps null: false
|
||||
end
|
||||
|
||||
create_table :blazer_audits do |t|
|
||||
t.references :user
|
||||
t.references :query
|
||||
t.text :statement
|
||||
t.string :data_source
|
||||
t.timestamp :created_at
|
||||
end
|
||||
|
||||
create_table :blazer_dashboards do |t|
|
||||
t.references :creator
|
||||
t.text :name
|
||||
t.timestamps null: false
|
||||
end
|
||||
|
||||
create_table :blazer_dashboard_queries do |t|
|
||||
t.references :dashboard
|
||||
t.references :query
|
||||
t.integer :position
|
||||
t.timestamps null: false
|
||||
end
|
||||
|
||||
create_table :blazer_checks do |t|
|
||||
t.references :creator
|
||||
t.references :query
|
||||
t.string :state
|
||||
t.string :schedule
|
||||
t.text :emails
|
||||
t.text :slack_channels
|
||||
t.string :check_type
|
||||
t.text :message
|
||||
t.timestamp :last_run_at
|
||||
t.timestamps null: false
|
||||
end
|
||||
end
|
||||
end
|
|
@ -12,7 +12,10 @@
|
|||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 20_190_829_180_743) do
|
||||
ActiveRecord::Schema.define(version: 20_200_206_151_057) do
|
||||
# Could not dump table "access_logs" because of following StandardError
|
||||
# Unknown type '' for column 'id'
|
||||
|
||||
create_table 'action_text_rich_texts', force: :cascade do |t|
|
||||
t.string 'name', null: false
|
||||
t.text 'body'
|
||||
|
@ -55,6 +58,9 @@ ActiveRecord::Schema.define(version: 20_190_829_180_743) do
|
|||
t.index ['deploy_id'], name: 'index_build_stats_on_deploy_id'
|
||||
end
|
||||
|
||||
# Could not dump table "csp_reports" because of following StandardError
|
||||
# Unknown type 'uuid' for column 'id'
|
||||
|
||||
create_table 'deploys', force: :cascade do |t|
|
||||
t.datetime 'created_at', null: false
|
||||
t.datetime 'updated_at', null: false
|
||||
|
@ -132,6 +138,7 @@ ActiveRecord::Schema.define(version: 20_190_829_180_743) do
|
|||
t.string 'status', default: 'waiting'
|
||||
t.text 'description'
|
||||
t.string 'title'
|
||||
t.boolean 'colaboracion_anonima', default: false
|
||||
t.index ['design_id'], name: 'index_sites_on_design_id'
|
||||
t.index ['licencia_id'], name: 'index_sites_on_licencia_id'
|
||||
t.index ['name'], name: 'index_sites_on_name', unique: true
|
||||
|
|
|
@ -4,15 +4,15 @@
|
|||
gem: 'sutty-theme-none'
|
||||
url: 'https://sutty.nl'
|
||||
disabled: true
|
||||
description_en: "Upload your own theme. [This feature is in development, help us!](https://sutty.neocities.org/en/#contact)"
|
||||
description_es: "Subir tu propio diseño. [Esta posibilidad está en desarrollo, ¡ayudanos!](https://sutty.neocities.org/es/#contacto)"
|
||||
description_en: "Upload your own theme. [This feature is in development, help us!](https://sutty.nl/en/#contact)"
|
||||
description_es: "Subir tu propio diseño. [Esta posibilidad está en desarrollo, ¡ayudanos!](https://sutty.nl/#contacto)"
|
||||
- name_en: 'I want you to create a site for me'
|
||||
name_es: 'Quiero que desarrollen mi sitio'
|
||||
gem: 'sutty-theme-custom'
|
||||
url: 'https://sutty.nl'
|
||||
disabled: true
|
||||
description_en: "If you want us to create your site, you're welcome to [contact us!](https://sutty.neocities.org/en/#contact) :)"
|
||||
description_es: "Si querés que desarrollemos tu sitio, [escribinos](https://sutty.neocities.org/es/#contacto) :)"
|
||||
description_en: "If you want us to create your site, you're welcome to [contact us!](https://sutty.nl/en/#contact) :)"
|
||||
description_es: "Si querés que desarrollemos tu sitio, [escribinos](https://sutty.nl/#contacto) :)"
|
||||
- name_en: 'Minima'
|
||||
name_es: 'Mínima'
|
||||
gem: 'minima'
|
||||
|
@ -25,5 +25,5 @@
|
|||
gem: 'sutty-theme-own'
|
||||
url: 'https://jekyllthemes.org'
|
||||
disabled: true
|
||||
description_en: "We're working to add more themes for you to use. [Contact us!](https://sutty.neocities.org/en/#contact)"
|
||||
description_es: "Estamos trabajando para que puedas tener más diseños. [¡Escribinos!](https://sutty.neocities.org/es/#contacto)"
|
||||
description_en: "We're working to add more themes for you to use. [Contact us!](https://sutty.nl/en/#contact)"
|
||||
description_es: "Estamos trabajando para que puedas tener más diseños. [¡Escribinos!](https://sutty.nl/#contacto)"
|
||||
|
|
|
@ -17,3 +17,4 @@
|
|||
- name: mail
|
||||
- name: email
|
||||
- name: xmpp
|
||||
- name: radicale
|
||||
|
|
|
@ -23,3 +23,11 @@ otra vez.
|
|||
|
||||
Lo más controlado sería enviar exactamente el id del post con su nueva
|
||||
ubicación en el orden. Esta es la implementación anterior.
|
||||
|
||||
***
|
||||
|
||||
El orden es descendiente (fechas más nuevas primero), pero el orden que
|
||||
estuvimos usando es ascendientes (números más bajos primero). Es más
|
||||
simple invertir la lógica y hacer todo el orden descendiente. Para eso
|
||||
los artículos más nuevos tienen que tener el número de orden
|
||||
correspondiente a la posición en el array ordenado por fecha.
|
||||
|
|
28
doc/uuid.md
Normal file
28
doc/uuid.md
Normal file
|
@ -0,0 +1,28 @@
|
|||
# Identificadores para los artículos
|
||||
|
||||
Para poder vincular artículos entre sí y para otros usos, necesitamos
|
||||
identificarlos únicamente. Un identificador incremental es problemático
|
||||
porque tendríamos que mantener el estado y poder responder preguntas
|
||||
como ¿cuál es el último identificador que asignamos?
|
||||
|
||||
Para poder identificar artículos sin mantener estado, usamos
|
||||
[UUIDs](https://en.wikipedia.org/wiki/Universally_unique_identifier),
|
||||
que son cadenas aleatorias que se pueden asignar adhoc. Así, en lugar
|
||||
de un ID numérico que va incrementando, podemos asociar cadenas al
|
||||
estilo `fb4a5048-5fa1-4b85-b70e-6c502feecdb9` (generada con la
|
||||
herramienta `uuidgen`).
|
||||
|
||||
## MetadataUUID
|
||||
|
||||
Cada artículo se crea con un metadato `uuid` cuyo valor por defecto es
|
||||
un UUID autogenerado utilizando `SecureRandom.uuid`. Este valor no
|
||||
cambia (a menos que se lo vacíe intencionalmente).
|
||||
|
||||
## Migración
|
||||
|
||||
Para todos los artículos que existen, hay que escribir una migración que
|
||||
se los agregue.
|
||||
|
||||
Para esto hay que cargar sitio por sitio, recorrer los artículos
|
||||
asignando un UUID y guardando todos los cambios como un solo commit de
|
||||
git.
|
|
@ -1,5 +1,6 @@
|
|||
check process sutty with pidfile /srv/http/tmp/puma.pid
|
||||
start program = "/bin/sh -c 'cd /srv/http && foreman start migrate && foreman start sutty'" as uid app
|
||||
start program = "/bin/sh -c 'cd /srv/http && foreman start migrate && foreman start sutty'"
|
||||
as uid "app" and gid "www-data"
|
||||
stop program = "/bin/sh -c 'cat /srv/http/tmp/puma.pid | xargs kill'"
|
||||
|
||||
check program sync_assets
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
"@rails/activestorage": "^6.0.0",
|
||||
"@rails/webpacker": "^4.0.7",
|
||||
"commonmark": "^0.29.0",
|
||||
"input-map": "https://0xacab.org/sutty/input-map.git",
|
||||
"input-tag": "https://0xacab.org/sutty/input-tag.git",
|
||||
"prosemirror-commands": "^1.0.8",
|
||||
"prosemirror-gapcursor": "^1.0.4",
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue