5
0
Fork 0
mirror of https://0xacab.org/sutty/sutty synced 2024-05-17 20:40:49 +00:00

Merge branch 'rails' into production.panel.sutty.nl

This commit is contained in:
f 2024-02-22 12:00:27 -03:00
commit cf77514bb6
No known key found for this signature in database
123 changed files with 2249 additions and 1599 deletions

View file

@ -1,8 +1,8 @@
NODE_OPTIONS=--openssl-legacy-provider
# pwgen -1 32
RAILS_MASTER_KEY=11111111111111111111111111111111
RAILS_GROUPS=assets
DELEGATE=athshe.sutty.nl
HAINISH=../haini.sh/haini.sh
DELEGATE=panel.sutty.nl
DATABASE_URL=postgres://suttier@postgresql.sutty.local/sutty
RAILS_ENV=development
IMAP_SERVER=
@ -37,3 +37,5 @@ AIRBRAKE_API_KEY=
GITLAB_URI=https://0xacab.org
GITLAB_PROJECT=
GITLAB_TOKEN=
PGVER=15
PGPID=/run/postgresql.pid

1
.env.development Normal file
View file

@ -0,0 +1 @@
HAINISH=../haini.sh/haini.sh

6
.gitignore vendored
View file

@ -28,7 +28,7 @@
/data/*
/_storage/*
.env*
.env.*
# Ignore master key for decrypting credentials and more.
/config/master.key
@ -48,3 +48,7 @@ yarn-debug.log*
/yarn-error.log
yarn-debug.log*
.yarn-integrity
/.task
/.yardoc
/public/doc/

View file

@ -1,33 +1,99 @@
image: "gitea.nulo.in/sutty/panel:3.14.10-2.7.8-panel.sutty.nl"
.apk-add: &apk-add
- "apk add go-task diffutils"
.disable-hainish: &disable-hainish
- "rm -f .env.development"
.cache-ruby: &cache-ruby
- paths:
- "vendor/ruby"
- ".bundle"
.cache-node: &cache-node
- paths:
- "node_modules"
.cache-task: &cache-task
- paths:
- ".task"
image: "registry.0xacab.org/sutty/sutty:3.17.3-3.1.4-rails"
variables:
RAILS_ENV: "production"
LC_ALL: "C.UTF-8"
HAINISH: ""
cache:
paths:
- "vendor/ruby"
assets:
stage: "build"
rules:
- if: "$CI_COMMIT_BRANCH == \"panel.sutty.nl\""
- if: "$CI_COMMIT_BRANCH"
changes:
compare_to: "refs/heads/rails"
paths:
- "package.json"
- "app/javascript/**/*"
- "app/assets/**/*"
stage: "deploy"
only:
- "rails"
- "17.3.alpine.panel.sutty.nl"
except:
- "schedules"
cache:
- *cache-ruby
- *cache-node
- *cache-task
before_script:
- "git config --global user.email \"${GIT_USER_EMAIL:-$GITLAB_USER_EMAIL}\""
- "git config --global user.name \"${GIT_USER_NAME:-$GITLAB_USER_NAME}\""
- "git remote set-url --push origin \"https://${GITLAB_USERNAME}:${GITLAB_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}.git\""
- "apk add python2 dotenv brotli"
- "mv config/credentials.yml.enc.ci config/credentials.yml.enc"
- "cp .env.example .env"
- "dotenv bundle install --path=vendor"
- "apk add brotli"
- *apk-add
- *disable-hainish
script:
- "dotenv RAILS_ENV=production bundle exec rails webpacker:clobber"
- "dotenv RAILS_ENV=production bundle exec rails assets:precompile"
- "dotenv RAILS_ENV=production bundle exec rails assets:clean"
- "go-task assets"
after_script:
- "git add public && git commit -m \"ci: assets [skip ci]\""
- "git push -o ci.skip"
gem-audit:
stage: "test"
only:
- "schedules"
cache:
- *cache-ruby
before_script:
- *apk-add
- *disable-hainish
script:
- "go-task gem-audit"
node-audit:
stage: "test"
only:
- "schedules"
cache:
- *cache-node
before_script:
- *apk-add
- *disable-hainish
script:
- "apk add go-task"
- "go-task node-audit"
brakeman:
stage: "test"
cache:
- *cache-ruby
rules:
- if: "$CI_PIPELINE_SOURCE == 'merge_request_event'"
before_script:
- *apk-add
- *disable-hainish
script:
- "go-task bundle -- exec brakeman"
rubocop:
stage: "test"
cache:
- *cache-ruby
rules:
- if: "$CI_PIPELINE_SOURCE == 'merge_request_event'"
before_script:
- *apk-add
- *disable-hainish
script:
- "./bin/modified_files | ./bin/with_extension rb | xargs -r go-task bundle -- exec rubocop"
haml:
stage: "test"
cache:
- *cache-ruby
rules:
- if: "$CI_PIPELINE_SOURCE == 'merge_request_event'"
before_script:
- *apk-add
- *disable-hainish
script:
- "./bin/modified_files | ./bin/with_extension haml | xargs -r go-task bundle -- exec haml-lint"

View file

@ -19,7 +19,6 @@ pipeline:
when:
branch:
- "rails"
- "panel.sutty.nl"
- "17.3.alpine.panel.sutty.nl"
event: "push"
path:
@ -27,55 +26,8 @@ pipeline:
- "Dockerfile"
- ".dockerignore"
- ".woodpecker.yml"
assets:
image: "gitea.nulo.in/sutty/panel:3.14.10-2.7.8"
commands:
- "apk add python2 dotenv openssh-client brotli"
- "install -d -m 700 ~/.ssh/"
- "echo \"$${KNOWN_HOSTS}\" | base64 -d >> ~/.ssh/known_hosts"
- "chmod 600 ~/.ssh/known_hosts"
- "eval $(ssh-agent -s)"
- "echo \"$${SSH_KEY}\" | base64 -d | ssh-add -"
- "ssh $${ORIGIN%:*}"
- "git config user.name Woodpecker"
- "git config user.email ci@sutty.coop.ar"
- "git remote add upstream $${ORIGIN}"
- "git checkout -B ${CI_COMMIT_BRANCH}"
- "mv config/credentials.yml.enc.ci config/credentials.yml.enc"
- "yarn"
- "cp .env.example .env"
- "dotenv bundle install --path=vendor"
- "dotenv RAILS_ENV=production bundle exec rails webpacker:clobber"
- "dotenv RAILS_ENV=production bundle exec rails assets:precompile"
- "dotenv RAILS_ENV=production bundle exec rails assets:clean"
- "git add public && git commit -m \"ci: assets [skip ci]\""
- "git push upstream ${CI_COMMIT_BRANCH}"
environment:
- "RUBY_VERSION=${RUBY_VERSION}"
- "GEMS_SOURCE=https://14.3.alpine.gems.sutty.nl"
secrets:
- "SSH_KEY"
- "KNOWN_HOSTS"
- "ORIGIN"
when:
branch:
- "rails"
- "panel.sutty.nl"
path:
include:
- "app/assets/**/*"
- "app/javascript/**/*"
- "package.json"
- "yarn.lock"
matrix:
ALPINE_VERSION: "3.14.10"
RUBY_VERSION: "2.7"
RUBY_PATCH: "8"
matrix:
include:
- ALPINE_VERSION: "3.17.3"
RUBY_VERSION: "3.1"
RUBY_PATCH: "4"
- ALPINE_VERSION: "3.14.10"
RUBY_VERSION: "2.7"
RUBY_PATCH: "8"

View file

@ -1,6 +1,6 @@
ARG RUBY_VERSION=2.7
ARG RUBY_PATCH=8
ARG ALPINE_VERSION=3.14.10
ARG RUBY_VERSION=3.1
ARG RUBY_PATCH=4
ARG ALPINE_VERSION=3.17.3
ARG BASE_IMAGE=registry.nulo.in/sutty/rails
FROM ${BASE_IMAGE}:${ALPINE_VERSION}-${RUBY_VERSION}.${RUBY_PATCH}
ARG PANDOC_VERSION=2.18

10
Gemfile
View file

@ -1,15 +1,9 @@
# frozen_string_literal: true
if ENV['RAILS_ENV'] != 'production' && ENV['HAIN_ENV'].nil?
puts 'Usa haini.sh para generar un entorno de trabajo reproducible'
end
source ENV.fetch('GEMS_SOURCE', 'https://17.3.alpine.gems.sutty.nl')
ruby "~> #{ENV.fetch('RUBY_VERSION', '3.1')}"
gem 'dotenv-rails', require: 'dotenv/rails-now'
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '~> 6.1.0'
# Use Puma as the app server
@ -112,6 +106,7 @@ end
group :development, :test do
gem 'derailed_benchmarks'
gem 'dotenv-rails'
gem 'pry'
gem 'capybara'
gem 'selenium-webdriver'
@ -119,8 +114,9 @@ group :development, :test do
end
group :development do
gem 'reek'
gem 'yard'
gem 'brakeman'
gem 'bundler-audit'
gem 'haml-lint', require: false
gem 'letter_opener'
gem 'listen'

View file

@ -25,7 +25,7 @@ GIT
groupdate (>= 5.2)
GEM
remote: https://gems.sutty.nl/
remote: https://17.3.alpine.gems.sutty.nl/
specs:
actioncable (6.1.7.3)
actionpack (= 6.1.7.3)
@ -89,6 +89,8 @@ GEM
addressable (2.8.4)
public_suffix (>= 2.0.2, < 6.0)
ast (2.4.2)
autoprefixer-rails (10.4.13.0)
execjs (~> 2)
bcrypt (3.1.19-x86_64-linux-musl)
bcrypt_pbkdf (1.1.0-x86_64-linux-musl)
benchmark-ips (2.12.0)
@ -98,8 +100,15 @@ GEM
chartkick (>= 3.2)
railties (>= 5)
safely_block (>= 0.1.1)
bootstrap (4.6.2)
autoprefixer-rails (>= 9.1.0)
popper_js (>= 1.16.1, < 2)
sassc-rails (>= 2.0.0)
brakeman (5.4.1)
builder (3.2.4)
bundler-audit (0.9.1)
bundler (>= 1.2.0, < 3)
thor (~> 1.0)
capybara (2.18.0)
addressable
mini_mime (>= 0.1.3)
@ -198,6 +207,7 @@ GEM
exception_notification (4.5.0)
actionmailer (>= 5.2, < 8)
activesupport (>= 5.2, < 8)
execjs (2.8.1)
factory_bot (6.2.1)
activesupport (>= 5.0.0)
factory_bot_rails (6.2.0)
@ -309,7 +319,6 @@ GEM
rexml
kramdown-parser-gfm (1.1.0)
kramdown (~> 2.0)
kwalify (0.7.2)
launchy (2.5.2)
addressable (~> 2.8)
letter_opener (1.8.1)
@ -373,6 +382,7 @@ GEM
pg_search (2.3.6)
activerecord (>= 5.2)
activesupport (>= 5.2)
popper_js (1.16.1)
prometheus_exporter (2.0.8)
webrick
pry (0.14.2)
@ -447,10 +457,6 @@ GEM
redis-store (>= 1.2, < 2)
redis-store (1.9.2)
redis (>= 4, < 6)
reek (6.1.4)
kwalify (~> 0.7.0)
parser (~> 3.2.0)
rainbow (>= 2.0, < 4.0)
regexp_parser (2.8.0)
request_store (1.5.1)
rack (>= 1.4)
@ -496,6 +502,12 @@ GEM
errbase (>= 0.1.1)
sassc (2.4.0-x86_64-linux-musl)
ffi (~> 1.9)
sassc-rails (2.1.2)
railties (>= 4.0.0)
sassc (>= 2.0)
sprockets (> 3.0)
sprockets-rails
tilt
selenium-webdriver (4.9.1)
rexml (~> 3.2, >= 3.2.5)
rubyzip (>= 1.2.2, < 3.0)
@ -528,7 +540,7 @@ GEM
temple (0.10.1)
terminal-table (2.0.0)
unicode-display_width (~> 1.1, >= 1.1.1)
thor (1.2.2)
thor (1.3.0)
tilt (2.1.0)
timecop (0.9.6)
timeout (0.3.2)
@ -537,6 +549,8 @@ GEM
turbolinks-source (5.2.0)
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
uglifier (4.2.0)
execjs (>= 0.3.0, < 3)
unf (0.1.4)
unf_ext
unf_ext (0.0.8.2-x86_64-linux-musl)
@ -564,6 +578,7 @@ GEM
websocket-extensions (0.1.5)
xpath (3.2.0)
nokogiri (~> 1.8)
yard (0.9.34)
zeitwerk (2.6.8)
PLATFORMS
@ -573,7 +588,9 @@ DEPENDENCIES
bcrypt (~> 3.1.7)
bcrypt_pbkdf
blazer
bootstrap (~> 4)
brakeman
bundler-audit
capybara
chartkick
commonmarker
@ -607,7 +624,6 @@ DEPENDENCIES
jbuilder (~> 2.5)
jekyll (~> 4.2.0)
jekyll-commonmark (~> 1.4.0)
jekyll-data
jekyll-images
jekyll-include-cache
kaminari
@ -635,7 +651,6 @@ DEPENDENCIES
rails_warden
redis (~> 4.0)
redis-rails
reek
rgl
rollups!
rubocop-rails
@ -643,6 +658,7 @@ DEPENDENCIES
rugged (= 1.5.0.1)
safe_yaml
safely_block (~> 0.3.0)
sassc-rails
selenium-webdriver
sourcemap
spring
@ -654,10 +670,12 @@ DEPENDENCIES
terminal-table
timecop
turbolinks (~> 5)
uglifier (>= 1.3.0)
validates_hostname
web-console
webpacker
yaml_db!
yard
RUBY VERSION
ruby 3.1.4p223

143
Makefile
View file

@ -1,143 +0,0 @@
SHELL := /bin/bash
.DEFAULT_GOAL := help
# Copiar el archivo de configuración y avisar cuando hay que
# actualizarlo.
.env: .env.example
@test -f $@ || cp -v $< $@
@test -f $@ && echo "Revisa $@ para actualizarlo con respecto a $<"
@test -f $@ && diff -auN --color $@ $<
include .env
export
# XXX: El espacio antes del comentario cuenta como espacio
args ?=## Argumentos para Hain
commit ?= origin/rails## Commit desde el que actualizar
env ?= staging## Entorno del nodo delegado
sutty ?= $(SUTTY)## Dirección local
delegate ?= $(DELEGATE)## Cambia el nodo delegado
hain ?= ENV_FILE=.env $(HAINISH)## Ubicación de Hainish
# El nodo delegado tiene dos entornos, production y staging.
# Dependiendo del entorno que elijamos, se van a generar los assets y el
# contenedor y subirse a un servidor u otro. No utilizamos CI/CD (aún).
#
# Production es el entorno de panel.sutty.nl
ifeq ($(env),production)
container ?= panel
## TODO: Cambiar a otra cosa
branch ?= rails
public ?= public
endif
# Staging es el entorno de panel.staging.sutty.nl
ifeq ($(env),staging)
container := staging
branch := staging
public := staging
endif
help: always ## Ayuda
@echo -e "Sutty\n" | sed -re "s/^.*/\x1B[38;5;197m&\x1B[0m/"
@echo -e "Servidor: https://panel.$(SUTTY_WITH_PORT)/\n"
@echo -e "Uso: make TAREA args=\"ARGUMENTOS\"\n"
@echo -e "Tareas:\n"
@grep -E "^[a-z\-]+:.*##" Makefile | sed -re "s/(.*):.*##(.*)/\1;\2/" | column -s ";" -t | sed -re "s/^([^ ]+) /\x1B[38;5;197m\1\x1B[0m/"
@echo -e "\nArgumentos:\n"
@grep -E "^[a-z\-]+ \?=.*##" Makefile | sed -re "s/(.*) \?=.*##(.*)/\1;\2/" | column -s ";" -t | sed -re "s/^([^ ]+) /\x1B[38;5;197m\1\x1B[0m/"
assets: public/packs/manifest.json.br ## Compilar los assets
git add public/assets/ public/packs/ && NADA=true git commit -m "assets [skip ci]" ; true
test: always ## Ejecutar los tests
$(MAKE) rake args="test RAILS_ENV=test $(args)"
postgresql: /etc/hosts ## Iniciar la base de datos
pgrep postgres >/dev/null || $(hain) postgresql
serve-js: /etc/hosts node_modules ## Iniciar el servidor de desarrollo de Javascript
$(hain) 'bundle exec ./bin/webpack-dev-server'
serve: /etc/hosts postgresql Gemfile.lock ## Iniciar el servidor de desarrollo de Rails
$(MAKE) rails args=server
rails: ## Corre rails dentro del entorno de desarrollo (pasar argumentos con args=).
$(MAKE) bundle args="exec rails $(args)"
rake: ## Corre rake dentro del entorno de desarrollo (pasar argumentos con args=).
$(MAKE) bundle args="exec rake $(args)"
bundle: ## Corre bundle dentro del entorno de desarrollo (pasar argumentos con args=).
$(hain) 'bundle $(args)'
psql := haini.sh psql -h $(PG_HOST) -U $(PG_USER) -p $(PG_PORT) -d sutty
copy-table:
test -n "$(table)"
echo "truncate $(table) $(cascade);" | $(psql)
ssh $(delegate) docker exec postgresql pg_dump -U sutty -d sutty -t $(table) | $(psql)
psql:
$(psql)
rubocop: ## Yutea el código que está por ser commiteado
git status --porcelain \
| grep -E "^(A|M)" \
| sed "s/^...//" \
| grep ".rb$$" \
| ../haini.sh/haini.sh "xargs -r ./bin/rubocop --auto-correct"
audit: ## Encuentra dependencias con vulnerabilidades
$(hain) 'gem install bundler-audit'
$(hain) 'bundle audit --update'
brakeman: ## Busca posibles vulnerabilidades en Sutty
$(MAKE) bundle args='exec brakeman'
yarn: ## Tareas de yarn
$(hain) 'yarn $(args)'
clean: ## Limpieza
rm -rf _sites/test-* _deploy/test-* log/*.log tmp/cache tmp/letter_opener tmp/miniprofiler tmp/storage
build: Gemfile.lock ## Generar la imagen Docker
time docker build --build-arg="BRANCH=$(branch)" --build-arg="RAILS_MASTER_KEY=`cat config/master.key`" -t sutty/$(container) .
docker tag sutty/$(container):latest sutty:keep
@echo -e "\a"
save: ## Subir la imagen Docker al nodo delegado
time docker save sutty/$(container):latest | ssh root@$(delegate) docker load
date +%F | xargs -I {} git tag -f $(container)-{}
@echo -e "\a"
ota: ## Actualizar Rails en el nodo delegado
git push
ssh $(delegate) git -C /srv/sutty/srv/http/panel.sutty.nl pull
ssh $(delegate) git -C /srv/sutty/srv/http/panel.sutty.nl lfs prune
ssh $(delegate) chown -R 1000:82 /srv/sutty/srv/http/panel.sutty.nl
ssh $(delegate) docker exec $(container) rails reload
# Correr un test en particular por ejemplo
# `make test/models/usuarie_test.rb`
tests := $(shell find test/ -name "*_test.rb")
$(tests): always
$(MAKE) test args="TEST=$@"
# Agrega las direcciones locales al sistema
/etc/hosts: always
@echo "Chequeando si es necesario agregar el dominio local $(SUTTY)"
@grep -q " $(SUTTY)$$" $@ || echo -e "127.0.0.1 $(SUTTY)\n::1 $(SUTTY)" | sudo tee -a $@
@grep -q " api.$(SUTTY)$$" $@ || echo -e "127.0.0.1 api.$(SUTTY)\n::1 api.$(SUTTY)" | sudo tee -a $@
@grep -q " panel.$(SUTTY)$$" $@ || echo -e "127.0.0.1 panel.$(SUTTY)\n::1 panel.$(SUTTY)" | sudo tee -a $@
@grep -q " postgresql.$(SUTTY)$$" $@ || echo -e "127.0.0.1 postgresql.$(SUTTY)\n::1 postgresql.$(SUTTY)" | sudo tee -a $@
# Instala las dependencias de Javascript
node_modules: package.json
$(MAKE) yarn
# Instala las dependencias de Rails
Gemfile.lock: Gemfile
$(MAKE) bundle args=install
.PHONY: always

View file

@ -1,5 +1,5 @@
cleanup: bundle exec rake cleanup:everything
stats: bundle exec rake stats:process_all
distributed_press_tokens_renew: bundle exec rake distributed_press:tokens:renew
que: daemonize -c /srv/ -p /srv/tmp/que.pid -u rails /usr/local/bin/syslogize bundle exec que
emergency_cleanup: bundle exec rake cleanup:everything BEFORE=7
que: daemonize -c /srv/ -p /srv/tmp/que.pid -u rails /usr/local/bin/syslogize bundle exec que
stats: bundle exec rake stats:process_all

View file

@ -17,14 +17,32 @@ Para más información visita el [sitio de Sutty](https://sutty.nl/).
### Desarrollar
Todas las tareas se gestionan con `make`, por favor instala GNU Make
antes de comenzar.
Para facilitar la gestión de dependencias y entorno de desarrollo,
instala [haini.sh](https://0xacab.org/sutty/haini.sh)
Todas las tareas se gestionan con `go-task`. [Instrucciones de
instalación (en inglés)](https://taskfile.dev/installation/)
```bash
make help
go-task
```
[Leer la documentación](https://docs.sutty.nl/)
### Variables de entorno
Las variables de entorno por defecto se encuentran en el archivo `.env`.
Para modificar las opciones, crear o modificar el archivo `.env.local`
con valores distintos.
### Documentación
Para navegar la documentación del código usando YARD:
```bash
go-task doc serve
```
Y luego navegar a <https://panel.sutty.local:3000/doc/>
## English
@ -39,10 +57,29 @@ For more information, visit [Sutty's website](https://sutty.nl/en/).
### Development
Every task is run via `make`, please install GNU Make before developing.
To facilitate dependencies and dev environment, install
[haini.sh](https://0xacab.org/sutty/haini.sh)
Every task is run via `go-task`. [Installation
instructions](https://taskfile.dev/installation/).
```bash
make help
go-task
```
[Read the documentation](https://docs.sutty.nl/en/)
### Environment variables
Default env vars are store on `.env`. For local options, copy them to
`.env.local`.
### Documentation
To browse documentation using YARD:
```bash
go-task doc serve
```
And then open <https://panel.sutty.local:3000/doc/>

185
Taskfile.yaml Normal file
View file

@ -0,0 +1,185 @@
---
version: "3"
vars:
CURRENT_BRANCH:
sh: "git rev-parse --abbrev-ref HEAD"
shopt:
- "globstar"
dotenv:
- ".env.development"
- ".env"
- ".env.local"
- ".env.development.local"
tasks:
credentials:
desc: "Generate credentials file"
cmds:
- "cp --no-clobber config/credentials.yml.enc.ci config/credentials.yml.enc"
sources:
- "config/credentials.yml.enc.ci"
generates:
- "config/credentials.yml.enc"
gems:
desc: "Install gems"
deps:
- "credentials"
cmds:
- "{{.HAINISH}} bundle config set --local path './vendor'"
- "{{.HAINISH}} bundle install"
sources:
- "Gemfile"
generates:
- "Gemfile.lock"
status:
- "test -d vendor/ruby"
clean:
desc: "Clean"
cmds:
- "rm -rf _sites/test-* _deploy/test-* log/*.log tmp/cache tmp/letter_opener tmp/miniprofiler tmp/storage"
node-modules:
desc: "Install Node modules"
cmds:
- "{{.HAINISH}} yarn"
sources:
- "package.json"
- "yarn.lock"
status:
- "test -d node_modules"
assets:
desc: "Generate assets"
deps:
- "node-modules"
- "gems"
cmds:
- "git lfs fetch"
- "git lfs checkout"
- task: "rails"
vars:
CLI_ARGS: "webpacker:clobber RAILS_ENV=production"
- task: "rails"
vars:
CLI_ARGS: "assets:precompile RAILS_ENV=production"
- task: "rails"
vars:
CLI_ARGS: "assets:clean RAILS_ENV=production"
sources:
- "package.json"
- "yarn.lock"
- "app/assets/**/*"
- "app/javascript/**/*"
generates:
- "public/packs/manifest.json"
hosts:
desc: "Local DNS resolution for hostnames"
interactive: true
cmds:
- "echo -e \"127.0.0.1 panel.{{.SUTTY}} api.{{.SUTTY}} postgresql.{{.SUTTY}}\" | sudo tee -a /etc/hosts"
- "echo -e \"::1 panel.{{.SUTTY}} api.{{.SUTTY}} postgresql.{{.SUTTY}}\" | sudo tee -a /etc/hosts"
status:
- "grep -q \" panel.{{.SUTTY}} \" /etc/hosts"
database-init:
desc: "Database install"
cmds:
- "{{.HAINISH}} /usr/bin/initdb --locale en_US.utf8 -E UTF8 -D /var/lib/postgresql/{{.PGVER}}/data"
- "echo \"host all all samenet trust\" >> ../hain/var/lib/postgresql/{{.PGVER}}/data/pg_hba.conf"
- "echo \"listen_addresses = '*'\" >> ../hain/var/lib/postgresql/{{.PGVER}}/data/postgresql.conf"
- "echo \"external_pid_file = '{{.PGPID}}'\" >> ../hain/var/lib/postgresql/{{.PGVER}}/data/postgresql.conf"
- "install -dm755 ../hain/run/postgresql"
status:
- "test -d ../hain/var/lib/postgresql/{{.PGVER}}/data"
- "test -f ../hain/var/lib/postgresql/{{.PGVER}}/data/postgresql.conf"
database:
desc: "Database"
deps:
- "database-init"
cmds:
- "{{.HAINISH}} daemonize -c /var/lib/postgresql/{{.PGVER}}/data /usr/bin/postgres -D /var/lib/postgresql/{{.PGVER}}/data"
status:
- "test -f ../hain{{.PGPID}}"
- "pgrep -F ../hain{{.PGPID}}"
prepare:
desc: "Create database or run pending migrations"
deps:
- "database"
cmds:
- task: "rails"
vars:
CLI_ARGS: "db:prepare"
serve:
desc: "Run Rails development server"
deps:
- "prepare"
- "gems"
cmds:
- ": == Development server running at https://panel.{{.SUTTY_WITH_PORT}} =="
- task: "rails"
vars:
CLI_ARGS: "server"
status:
- "test -f tmp/pids/server.pid"
- "pgrep -F tmp/pids/server.pid"
yarn:
desc: "Yarn. Call with: go-task yarn -- arguments"
deps:
- "node-modules"
cmds:
- "{{.HAINISH}} yarn {{.CLI_ARGS}}"
- defer:
task: "notify"
bundle:
desc: "Bundle. Call with: go-task bundle -- arguments"
interactive: true
deps:
- "gems"
cmds:
- "{{.HAINISH}} bundle {{.CLI_ARGS}}"
- defer:
task: "notify"
rails:
desc: "Rails. Call with: go-task rails -- arguments"
cmds:
- task: "bundle"
vars:
CLI_ARGS: "exec rails {{.CLI_ARGS}}"
console:
desc: "Rails console"
interactive: true
cmds:
- task: "rails"
vars:
CLI_ARGS: "console"
doc:
desc: "Build documentation"
deps:
- "gems"
cmds:
- task: "bundle"
vars:
CLI_ARGS: "exec yardoc -o public/doc app lib config db"
gem-audit:
desc: "Audit Gem dependencies"
deps:
- "gems"
- "bundler-audit"
cmds:
- task: "bundle"
vars:
CLI_ARGS: "audit --update"
node-audit:
desc: "Audit Node dependencies"
deps:
- "node-modules"
cmds:
- task: "yarn"
vars:
CLI_ARGS: "audit"
notify:
internal: true
cmds:
- "echo -e \"\a\""
bundler-audit:
internal: true
cmds:
- "{{.HAINISH}} gem install bundler-audit"
status:
- "test -f ../hain/usr/bin/bundler-audit"

View file

@ -196,7 +196,7 @@ fieldset {
&[type=button] {
@extend .btn;
@extend .btn-info;
@extend .btn-secondary;
@extend .m-0;
}
}
@ -210,10 +210,6 @@ svg {
}
.btn {
background-color: var(--foreground);
color: var(--background);
border: none;
border-radius: 0;
margin-right: 0.3rem;
margin-bottom: 0.3rem;
@ -253,7 +249,7 @@ svg {
color: $magenta;
}
.btn {
.btn-secondary {
background-color: $white;
color: $black;
border: none;

View file

@ -7,3 +7,22 @@ $cyan: #13fefe;
--background: #{$black};
--color: #{$cyan};
}
.btn-secondary {
background-color: $white;
color: $black;
border: none;
&:hover {
color: $black;
background-color: $cyan;
}
&:active {
background-color: $cyan;
}
&:focus {
box-shadow: 0 0 0 0.2rem $cyan;
}
}

View file

@ -16,12 +16,14 @@ module ActiveStorage
end
end
rescue_from ActiveRecord::RecordNotFound, with: :page_not_found
# Asociar el archivo subido al sitio correspondiente. Cada sitio
# tiene su propio servicio de subida de archivos.
def update
if (token = decode_verified_token)
if acceptable_content?(token)
blob = ActiveStorage::Blob.find_by_key token[:key]
blob = ActiveStorage::Blob.find_by_key! token[:key]
site = Site.find_by_name token[:service_name]
if remote_file?(token)
@ -59,6 +61,11 @@ module ActiveStorage
def remote_file?(token)
token[:content_type] == 'sutty/download-from-url'
end
def page_not_found(exception)
head :not_found
ExceptionNotifier.notify_exception(exception, data: {params: params.to_hash})
end
end
end
end

View file

@ -68,7 +68,7 @@ module Api
# respuesta de error a plataformas
def platforms_answer(exception)
ExceptionNotifier.notify_exception(exception, data: { headers: request.headers.to_h }
ExceptionNotifier.notify_exception(exception, data: { headers: request.headers.to_h })
head :forbidden
end

View file

@ -110,27 +110,6 @@ class SitesController < ApplicationController
redirect_to sites_path
end
# Obtiene y streamea archivos estáticos desde el repositorio mismo,
# pero sólo los públicos (es decir los archivos subidos desde Sutty).
def static_file
authorize site
file = params.require(:file) + '.' + params.require(:format)
raise ActionController::RoutingError.new(nil, nil) unless file.start_with? 'public/'
path = site.relative_path file
raise ActionController::RoutingError.new(nil, nil) unless File.exist? path
# TODO: Hacer esto usa recursos, pero menos que generar el sitio
# cada vez. Para poder usar X-Accel tendríamos que montar los
# repositorios en el servidor web, cosa que no queremos, o hacer
# links simbólicos desde todos los public, o usar un servidor web
# local que soporte sendfile mejor que Rails (nghttpd?)
send_file path
end
private
def site

View file

@ -96,7 +96,7 @@ module ActiveStorage
end
def blob_for(key)
ActiveStorage::Blob.find_by(key: key, service_name: name)
ActiveStorage::Blob.find_by!(key: key, service_name: name)
end
end
end

View file

@ -55,20 +55,9 @@ class Deploy < ApplicationRecord
site.path
end
# Un entorno que solo tiene lo que necesitamos
#
# @return [Hash]
def env
# XXX: This doesn't support Windows paths :B
paths = [File.dirname(`which bundle`), '/usr/local/bin', '/usr/bin', '/bin']
# Las variables de entorno extra no pueden superponerse al local.
extra_env.merge({
'HOME' => home_dir,
'PATH' => paths.join(':'),
'JEKYLL_ENV' => Rails.env,
'LANG' => ENV['LANG'],
})
# XXX: Ver DeployLocal#bundle
def gems_dir
@gems_dir ||= Rails.root.join('_storage', 'gems', site.name)
end
# Un entorno que solo tiene lo que necesitamos

View file

@ -7,11 +7,6 @@ class DeployLocal < Deploy
before_destroy :remove_destination!
def git_lfs(output: false)
run %(git lfs fetch), output: output
run %(git lfs checkout), output: output
end
def bundle(output: false)
run %(bundle config set --local clean 'true'), output: output
run %(bundle config set --local deployment 'true'), output: output if site.gemfile_lock_path?
@ -21,6 +16,11 @@ class DeployLocal < Deploy
run %(bundle install), output: output
end
def git_lfs(output: false)
run %(git lfs fetch), output: output
run %(git lfs checkout), output: output
end
# Realizamos la construcción del sitio usando Jekyll y un entorno
# limpio para no pasarle secretos
#

View file

@ -107,8 +107,10 @@ class Post
src = element.attributes['src']
next unless src&.value&.start_with? 'public/'
file = MetadataFile.new(site: site, post: self, document: document, layout: layout)
file.value['path'] = src.value
src.value = Rails.application.routes.url_helpers.site_static_file_url(site, file: src.value)
src.value = Rails.application.routes.url_helpers.url_for(file.static_file)
end
# Notificar a les usuaries que están viendo una previsualización

View file

@ -158,19 +158,19 @@ class Site < ApplicationRecord
# Traer la ruta del sitio
def path
File.join(Site.site_path, name)
::File.join(Site.site_path, name)
end
# La ruta anterior
def path_was
File.join(Site.site_path, name_was)
::File.join(Site.site_path, name_was)
end
# Limpiar la ruta y unirla con el separador de directorios del
# sistema operativo. Como si algún día fuera a cambiar o
# soportáramos Windows :P
def relative_path(suspicious_path)
File.join(path, *suspicious_path.gsub('..', '/').gsub('./', '').squeeze('/').split('/'))
::File.join(path, *suspicious_path.gsub('..', '/').gsub('./', '').squeeze('/').split('/'))
end
# Obtiene la lista de traducciones actuales
@ -359,7 +359,7 @@ class Site < ApplicationRecord
end
def jekyll?
File.directory? path
::File.directory? path
end
def jekyll
@ -377,7 +377,7 @@ class Site < ApplicationRecord
# documentos de Jekyll hacia Sutty para que podamos leer los datos que
# necesitamos.
def load_jekyll
return unless name.present? && File.directory?(path)
return unless name.present? && ::File.directory?(path)
reload_jekyll!
end
@ -405,7 +405,7 @@ class Site < ApplicationRecord
# metadatos de Document
@configuration =
::Jekyll.configuration('source' => path,
'destination' => File.join(path, '_site'),
'destination' => ::File.join(path, '_site'),
'safe' => true, 'watch' => false,
'quiet' => true, 'excerpt_separator' => '')
@ -430,7 +430,7 @@ class Site < ApplicationRecord
# El directorio donde se almacenan los sitios
def self.site_path
@site_path ||= File.realpath(ENV.fetch('SITE_PATH', Rails.root.join('_sites')))
@site_path ||= ::File.realpath(ENV.fetch('SITE_PATH', Rails.root.join('_sites')))
end
def self.default
@ -461,7 +461,7 @@ class Site < ApplicationRecord
end
def gemfile_lock_path?
File.exist? gemfile_lock_path
::File.exist? gemfile_lock_path
end
private
@ -552,6 +552,12 @@ class Site < ApplicationRecord
I18n.t('activerecord.errors.models.site.attributes.design_id.layout_incompatible.error'))
end
def run_in_path(&block)
Site.one_at_a_time.synchronize do
Dir.chdir path, &block
end
end
# Instala las gemas cuando es necesario:
#
# * El sitio existe
@ -567,6 +573,7 @@ class Site < ApplicationRecord
if !gems_installed? || gemfile_updated? || gemfile_lock_updated?
deploy_local.bundle
touch
::File.touch(gemfile_path)
end
end
@ -587,12 +594,16 @@ class Site < ApplicationRecord
# Detecta si el Gemfile fue modificado
def gemfile_updated?
updated_at < File.mtime(File.join(path, 'Gemfile'))
updated_at < ::File.mtime(gemfile_path)
end
def gemfile_path
@gemfile_path ||= ::File.join(path, 'Gemfile')
end
# @return [String]
def gemfile_lock_path
@gemfile_lock_path ||= File.join(path, 'Gemfile.lock')
@gemfile_lock_path ||= ::File.join(path, 'Gemfile.lock')
end
# Detecta si el Gemfile.lock fue modificado con respecto al sitio o al
@ -600,8 +611,8 @@ class Site < ApplicationRecord
def gemfile_lock_updated?
return false unless gemfile_lock_path?
[updated_at, File.mtime(File.join(path, 'Gemfile'))].any? do |compare|
compare < File.mtime(gemfile_lock_path)
[updated_at, ::File.mtime(::File.join(path, 'Gemfile'))].any? do |compare|
compare < ::File.mtime(gemfile_lock_path)
end
end
end

View file

@ -21,7 +21,7 @@ class Site
# Leer el archivo de configuración y setear los atributos en el
# objeto actual, creando los metodos de ostruct
def read
data = YAML.safe_load(File.read(path))
data = YAML.safe_load(File.read(path), permitted_classes: [Time])
@hash = data.hash
data.each do |key, value|

View file

@ -16,7 +16,7 @@ class Site
#
# @return [nil]
def generate_private_key_pem!
self.private_key_pem ||= DistributedPress::V1::Social::Client.new(public_key_url: nil, key_size: 2048).private_key.export
self.private_key_pem ||= ::DistributedPress::V1::Social::Client.new(public_key_url: nil, key_size: 2048).private_key.export
end
end
end

View file

@ -31,6 +31,7 @@ class CleanupService
site.deploys.find_each(&:cleanup!)
site.repository.gc
lfs_cleanup
site.touch
end
end
@ -45,7 +46,14 @@ class CleanupService
Rails.logger.info "Limpiando repositorio git de #{site.name}"
site.repository.gc
lfs_cleanup
site.touch
end
end
private
def lfs_cleanup
site.repository.git_sh("git", "lfs", "prune")
site.repository.git_sh("git", "lfs", "dedup")
end
end

View file

@ -25,4 +25,4 @@
class: 'form-control'
.form-group
= f.submit t('.submit'), class: 'btn btn-lg btn-block'
= f.submit t('.submit'), class: 'btn btn-secondary btn-lg btn-block'

View file

@ -30,5 +30,5 @@
placeholder: t('activerecord.attributes.usuarie.email')
.actions
= f.submit t('.resend_confirmation_instructions'),
class: 'btn btn-lg btn-block'
class: 'btn btn-secondary btn-lg btn-block'
= render 'devise/shared/links'

View file

@ -32,4 +32,4 @@
placeholder: t('activerecord.attributes.usuarie.password')
.actions
= f.submit t('devise.invitations.edit.submit_button'),
class: 'btn btn-lg btn-block'
class: 'btn btn-secondary btn-lg btn-block'

View file

@ -16,4 +16,4 @@
= f.text_field field, class: 'form-control'
.actions
= f.submit t('devise.invitations.new.submit_button'),
class: 'btn btn-lg btn-block'
class: 'btn btn-secondary btn-lg btn-block'

View file

@ -39,6 +39,6 @@
.actions
= f.submit t('.change_my_password'),
class: 'btn btn-lg btn-block'
class: 'btn btn-secondary btn-lg btn-block'
= render 'devise/shared/links'

View file

@ -20,5 +20,5 @@
placeholder: t('activerecord.attributes.usuarie.email')
.actions
= f.submit t('.send_me_reset_password_instructions'),
class: 'btn btn-lg btn-block'
class: 'btn btn-secondary btn-lg btn-block'
= render 'devise/shared/links'

View file

@ -55,7 +55,7 @@
= t('.we_need_your_current_password_to_confirm_your_changes')
.actions
= f.submit t('.update'),
class: 'btn btn-lg btn-block'
class: 'btn btn-secondary btn-lg btn-block'
%hr/
.sr-only
@ -63,4 +63,4 @@
= button_to t('.cancel_my_account'),
registration_path(resource_name),
data: { confirm: t('.are_you_sure') },
method: :delete, class: 'btn btn-block'
method: :delete, class: 'btn btn-secondary btn-block'

View file

@ -56,6 +56,6 @@
.actions
= f.submit t('.sign_up'),
class: 'btn btn-lg btn-block'
class: 'btn btn-secondary btn-lg btn-block'
= render 'devise/shared/links'

View file

@ -32,5 +32,5 @@
= t('login.remember_me', remember_for: distance_of_time_in_words(Usuarie.remember_for))
.actions
= f.submit t('.sign_in'),
class: 'btn btn-lg btn-block'
class: 'btn btn-secondary btn-lg btn-block'
= render 'devise/shared/links'

View file

@ -4,12 +4,12 @@
- if controller_name != 'sessions'
= link_to t('.sign_in'), new_session_path(resource_name, params: locale),
class: 'btn btn-lg btn-block btn-success'
class: 'btn btn-lg btn-block btn-secondary'
%br/
- if devise_mapping.registerable? && controller_name != 'registrations'
= link_to t('.sign_up'), new_registration_path(resource_name, params: locale),
class: 'btn btn-lg btn-block btn-success'
class: 'btn btn-lg btn-block btn-secondary'
%br/
- if devise_mapping.recoverable?

View file

@ -20,5 +20,5 @@
placeholder: t('activerecord.attributes.usuarie.email')
.actions
= f.submit t('.resend_unlock_instructions'),
class: 'btn btn-lg btn-block'
class: 'btn btn-secondary btn-lg btn-block'
= render 'devise/shared/links'

View file

@ -11,7 +11,7 @@
= select_tag 'to',
options_for_select(@options, @lang_to),
class: 'form-control'
= submit_tag t('i18n.translate'), class: 'btn', name: nil
= submit_tag t('i18n.translate'), class: 'btn btn-secondary', name: nil
- else
= t('i18n.translating.from')
= select_tag 'from',
@ -21,7 +21,7 @@
= select_tag 'to',
options_for_select(@options, @lang_to),
class: 'form-control'
= submit_tag t('i18n.change'), class: 'btn', name: nil
= submit_tag t('i18n.change'), class: 'btn btn-secondary', name: nil
= render 'layouts/help', help: t('help.i18n.index')
@ -33,16 +33,16 @@
= hidden_field 'i18n', 'lang_to', value: @lang_to
.form-group
.dropdown.inline
%button.btn.dropdown-toggle{type: 'button',
%button.btn.btn-secondary.dropdown-toggle{type: 'button',
data: { toggle: 'dropdown' },
aria: { haspopup: 'true', expanded: 'false' }}
= t('i18n.jump')
.dropdown-menu{aria: { labelledby: t('i18n.jump') }}
- @site.data.dig(@lang_from).each_pair do |section, content|
%a.dropdown-item{href: "##{section}"}= t("help.i18n.#{section}")
= submit_tag t('i18n.save'), class: 'btn'
= submit_tag t('i18n.save'), class: 'btn btn-secondary'
= render 'i18n/recursive', data: @site.data.dig(@lang_from), superkeys: []
.form-group
= submit_tag t('i18n.save'), class: 'btn'
= submit_tag t('i18n.save'), class: 'btn btn-secondary'

View file

@ -18,15 +18,15 @@
- if @site&.tienda?
%li.nav-item
= link_to t('.tienda'), @site.tienda_url,
role: 'button', class: 'btn'
role: 'button', class: 'btn btn-secondary'
%li.nav-item
= link_to t('.contact_us'), t('.contact_us_href'),
class: 'btn', rel: 'me', target: '_blank'
class: 'btn btn-secondary', rel: 'me', target: '_blank'
%li.nav-item
= link_to t('.logout'), main_app.destroy_usuarie_session_path,
method: :delete, role: 'button', class: 'btn'
method: :delete, role: 'button', class: 'btn btn-secondary'
- else
- params.permit!
- I18n.available_locales.each do |locale|

View file

@ -1,2 +1,2 @@
= link_to text, link, class: 'btn',
= link_to text, link, class: 'btn btn-secondary',
data: { toggle: 'tooltip' }, 'aria-role': 'button', title: tooltip

View file

@ -1,7 +1,7 @@
- invalid_help = site.config.fetch('invalid_help', t('.invalid_help'))
- sending_help = site.config.fetch('sending_help', t('.sending_help'))
.form-group
= submit_tag t('.save'), class: 'btn submit-post'
= submit_tag t('.save'), class: 'btn btn-secondary submit-post'
= render 'bootstrap/alert', class: 'invalid-help d-none' do
= invalid_help
= render 'bootstrap/alert', class: 'sending-help d-none' do

View file

@ -20,82 +20,82 @@
TODO: Eliminar todo el espacio en blanco para minificar HTML
.editor-toolbar{ style: 'z-index: 1' }
.editor-primary-toolbar.scrollbar-black
%button.btn{ type: 'button', title: t('editor.multimedia'), data: { editor_button: 'multimedia' } }>
%button.btn.btn-secondary{ type: 'button', title: t('editor.multimedia'), data: { editor_button: 'multimedia' } }>
%i.fa.fa-fw.fa-upload>
%span.sr-only>= t('editor.multimedia')
%button.btn{ type: 'button', title: t('editor.bold'), data: { editor_button: 'mark-bold' } }>
%button.btn.btn-secondary{ type: 'button', title: t('editor.bold'), data: { editor_button: 'mark-bold' } }>
%i.fa.fa-fw.fa-bold>
%span.sr-only>= t('editor.bold')
%button.btn{ type: 'button', title: t('editor.italic'), data: { editor_button: 'mark-italic' } }>
%button.btn.btn-secondary{ type: 'button', title: t('editor.italic'), data: { editor_button: 'mark-italic' } }>
%i.fa.fa-fw.fa-italic>
%span.sr-only>= t('editor.italic')
%button.btn{ type: 'button', title: t('editor.mark'), data: { editor_button: 'mark-mark' } }>
%button.btn.btn-secondary{ type: 'button', title: t('editor.mark'), data: { editor_button: 'mark-mark' } }>
%i.fa.fa-fw.fa-tint>
%span.sr-only>= t('editor.mark')
%button.btn{ type: 'button', title: t('editor.link'), data: { editor_button: 'mark-link' } }>
%button.btn.btn-secondary{ type: 'button', title: t('editor.link'), data: { editor_button: 'mark-link' } }>
%i.fa.fa-fw.fa-link>
%span.sr-only>= t('editor.link')
%button.btn{ type: 'button', title: t('editor.deleted'), data: { editor_button: 'mark-deleted' } }>
%button.btn.btn-secondary{ type: 'button', title: t('editor.deleted'), data: { editor_button: 'mark-deleted' } }>
%i.fa.fa-fw.fa-strikethrough>
%span.sr-only>= t('editor.deleted')
%button.btn{ type: 'button', title: t('editor.underline'), data: { editor_button: 'mark-underline' } }>
%button.btn.btn-secondary{ type: 'button', title: t('editor.underline'), data: { editor_button: 'mark-underline' } }>
%i.fa.fa-fw.fa-underline>
%span.sr-only>= t('editor.underline')
%button.btn{ type: 'button', title: t('editor.super'), data: { editor_button: 'mark-super' } }>
%button.btn.btn-secondary{ type: 'button', title: t('editor.super'), data: { editor_button: 'mark-super' } }>
%i.fa.fa-fw.fa-superscript>
%span.sr-only>= t('editor.super')
%button.btn{ type: 'button', title: t('editor.sub'), data: { editor_button: 'mark-sub' } }>
%button.btn.btn-secondary{ type: 'button', title: t('editor.sub'), data: { editor_button: 'mark-sub' } }>
%i.fa.fa-fw.fa-subscript>
%span.sr-only>= t('editor.sub')
%button.btn{ type: 'button', title: t('editor.small'), data: { editor_button: 'mark-small' } }>
%button.btn.btn-secondary{ type: 'button', title: t('editor.small'), data: { editor_button: 'mark-small' } }>
%i.fa.fa-fw.fa-subscript>
%span.sr-only>= t('editor.small')
%button.btn.mr-0{ type: 'button', title: t('editor.h1'), data: { editor_button: 'block-h1' } }>
%button.btn.btn-secondary.mr-0{ type: 'button', title: t('editor.h1'), data: { editor_button: 'block-h1' } }>
%i.fa.fa-fw.fa-heading>
1
%span.sr-only>= t('editor.h1')
%details.d-inline>
%summary.d-inline>
%span.btn.ml-0{ role: 'button', title: t('editor.more') }>
%span.btn.btn-secondary.ml-0{ role: 'button', title: t('editor.more') }>
%i.fa.fa-caret-right>
%span.sr-only= t('editor.more')
.d-inline>
%button.btn{ type: 'button', title: t('editor.h2'), data: { editor_button: 'block-h2' } }>
%button.btn.btn-secondary{ type: 'button', title: t('editor.h2'), data: { editor_button: 'block-h2' } }>
%i.fa.fa-fw.fa-heading>
2
%span.sr-only>= t('editor.h2')
%button.btn{ type: 'button', title: t('editor.h3'), data: { editor_button: 'block-h3' } }>
%button.btn.btn-secondary{ type: 'button', title: t('editor.h3'), data: { editor_button: 'block-h3' } }>
%i.fa.fa-fw.fa-heading>
3
%span.sr-only>= t('editor.h3')
%button.btn{ type: 'button', title: t('editor.h4'), data: { editor_button: 'block-h4' } }>
%button.btn.btn-secondary{ type: 'button', title: t('editor.h4'), data: { editor_button: 'block-h4' } }>
%i.fa.fa-fw.fa-heading>
4
%span.sr-only>= t('editor.h4')
%button.btn{ type: 'button', title: t('editor.h5'), data: { editor_button: 'block-h5' } }>
%button.btn.btn-secondary{ type: 'button', title: t('editor.h5'), data: { editor_button: 'block-h5' } }>
%i.fa.fa-fw.fa-heading>
5
%span.sr-only>= t('editor.h5')
%button.btn{ type: 'button', title: t('editor.h6'), data: { editor_button: 'block-h6' } }>
%button.btn.btn-secondary{ type: 'button', title: t('editor.h6'), data: { editor_button: 'block-h6' } }>
%i.fa.fa-fw.fa-heading>
6
%span.sr-only>= t('editor.h6')
%button.btn{ type: 'button', title: t('editor.ul'), data: { editor_button: 'block-unordered_list' } }>
%button.btn.btn-secondary{ type: 'button', title: t('editor.ul'), data: { editor_button: 'block-unordered_list' } }>
%i.fa.fa-fw.fa-list-ul>
%span.sr-only>= t('editor.ul')
%button.btn{ type: 'button', title: t('editor.ol'), data: { editor_button: 'block-ordered_list' } }>
%button.btn.btn-secondary{ type: 'button', title: t('editor.ol'), data: { editor_button: 'block-ordered_list' } }>
%i.fa.fa-fw.fa-list-ol>
%span.sr-only>= t('editor.ol')
%button.btn{ type: 'button', title: t('editor.left'), data: { editor_button: 'parentBlock-left' } }>
%button.btn.btn-secondary{ type: 'button', title: t('editor.left'), data: { editor_button: 'parentBlock-left' } }>
%i.fa.fa-fw.fa-align-left>
%span.sr-only>= t('editor.left')
%button.btn{ type: 'button', title: t('editor.center'), data: { editor_button: 'parentBlock-center' } }>
%button.btn.btn-secondary{ type: 'button', title: t('editor.center'), data: { editor_button: 'parentBlock-center' } }>
%i.fa.fa-fw.fa-align-center>
%span.sr-only>= t('editor.center')
%button.btn{ type: 'button', title: t('editor.right'), data: { editor_button: 'parentBlock-right' } }>
%button.btn.btn-secondary{ type: 'button', title: t('editor.right'), data: { editor_button: 'parentBlock-right' } }>
%i.fa.fa-fw.fa-align-right>
%span.sr-only>= t('editor.right')
%button.btn{ type: 'button', title: t('editor.blockquote'), data: { editor_button: 'block-blockquote' } }>
%button.btn.btn-secondary{ type: 'button', title: t('editor.blockquote'), data: { editor_button: 'block-blockquote' } }>
%i.fa.fa-fw.fa-quote-left>
%span.sr-only>= t('editor.blockquote')
@ -116,8 +116,8 @@
%label{ for: 'multimedia-alt' }= t('editor.description')
%input.form-control{ type: 'text', id: 'multimedia-alt', name: 'multimedia-alt' }/
.form-group
%button.btn{ type: 'button', id: 'multimedia-file-upload', name: 'multimedia-file-upload' }= t('editor.multimedia-upload')
%button.btn{ type: 'button', id: 'multimedia-remove', name: 'multimedia-remove' }= t('editor.multimedia-remove')
%button.btn.btn-secondary{ type: 'button', id: 'multimedia-file-upload', name: 'multimedia-file-upload' }= t('editor.multimedia-upload')
%button.btn.btn-secondary{ type: 'button', id: 'multimedia-remove', name: 'multimedia-remove' }= t('editor.multimedia-remove')
.form-group{ data: { editor_auxiliary: 'link' } }
%label{ for: 'link-url' }= t('editor.url')

View file

@ -22,13 +22,13 @@
= render 'schemas/row', site: @site, schema: schema, filter: @filter_params
- if policy(@site_stat).index?
= link_to t('stats.index.title'), site_stats_path(@site), class: 'btn'
= link_to t('stats.index.title'), site_stats_path(@site), class: 'btn btn-secondary'
- if policy(@site).edit?
= link_to t('sites.edit.btn', site: @site.title), edit_site_path(@site), class: 'btn'
= link_to t('sites.edit.btn', site: @site.title), edit_site_path(@site), class: 'btn btn-secondary'
- if policy(@site).private?
= link_to t('sites.private'), '../private/' + @site.name, class: 'btn', target: '_blank', rel: 'noopener'
= link_to t('sites.private'), '../private/' + @site.name, class: 'btn btn-secondary', target: '_blank', rel: 'noopener'
- if policy(SiteUsuarie.new(@site, current_usuarie)).index?
= render 'layouts/btn_with_tooltip',
@ -40,9 +40,9 @@
- if @site.design.credits
= render 'bootstrap/alert' do
= sanitize_markdown @site.design.credits
= link_to t('sites.donations.text'), t('sites.donations.url'), class: 'btn'
= link_to t('sites.donations.text'), t('sites.donations.url'), class: 'btn btn-secondary'
- if @site.design.designer_url
= link_to t('sites.designer_url'), @site.design.designer_url, class: 'btn'
= link_to t('sites.designer_url'), @site.design.designer_url, class: 'btn btn-secondary'
%section.col
.d-flex.justify-content-between.align-items-center.pl-2-plus.pr-2-plus.mb-2
@ -83,19 +83,19 @@
%div
- if reorder_allowed
= submit_tag t('posts.reorder.submit'), class: 'btn'
%button.btn{ data: { action: 'reorder#unselect' } }
%button.btn.btn-secondary{ data: { action: 'reorder#unselect' } }
= t('posts.reorder.unselect')
%span.badge{ data: { target: 'reorder.counter' } } 0
%button.btn{ data: { action: 'reorder#up' } }= t('posts.reorder.up')
%button.btn{ data: { action: 'reorder#down' } }= t('posts.reorder.down')
%button.btn{ data: { action: 'reorder#top' } }= t('posts.reorder.top')
%button.btn{ data: { action: 'reorder#bottom' } }= t('posts.reorder.bottom')
%button.btn.btn-secondary{ data: { action: 'reorder#up' } }= t('posts.reorder.up')
%button.btn.btn-secondary{ data: { action: 'reorder#down' } }= t('posts.reorder.down')
%button.btn.btn-secondary{ data: { action: 'reorder#top' } }= t('posts.reorder.top')
%button.btn.btn-secondary{ data: { action: 'reorder#bottom' } }= t('posts.reorder.bottom')
%input{ type: 'hidden', name: 'post[lang]', value: @locale }
- if @site.pagination
%div
= link_to_prev_page @posts, t('posts.prev'), class: 'btn'
= link_to_next_page @posts, t('posts.next'), class: 'btn'
= link_to_prev_page @posts, t('posts.prev'), class: 'btn btn-secondary'
= link_to_next_page @posts, t('posts.next'), class: 'btn btn-secondary'
%tbody
- dir = @site.data.dig(params[:locale], 'dir')
- size = @posts.size
@ -136,9 +136,9 @@
= post.order
%td.text-nowrap
- if @usuarie || policy(post).edit?
= link_to t('posts.edit'), edit_site_post_path(@site, post.path), class: 'btn btn-block'
= link_to t('posts.edit'), edit_site_post_path(@site, post.path), class: 'btn btn-secondary btn-block'
- if @usuarie || policy(post).destroy?
= link_to t('posts.destroy'), site_post_path(@site, post.path), class: 'btn btn-block', method: :delete, data: { confirm: t('posts.confirm_destroy') }
= link_to t('posts.destroy'), site_post_path(@site, post.path), class: 'btn btn-secondary btn-block', method: :delete, data: { confirm: t('posts.confirm_destroy') }
-#
Rescatar cualquier error en un post, notificarlo e
ignorar su renderización.

View file

@ -4,7 +4,7 @@
%article.content.table-responsive-md
= link_to t('posts.edit'),
edit_site_post_path(@site, @post.id),
class: 'btn btn-block'
class: 'btn btn-secondary btn-block'
%table.table.table-condensed
%thead

View file

@ -3,7 +3,7 @@
method: :post,
class: 'form-inline inline' do
= submit_tag site.enqueued? ? t('sites.enqueued') : t('sites.enqueue'),
class: "btn no-border-radius #{local_assigns[:class]}",
class: "btn btn-secondary #{local_assigns[:class]}",
title: site.enqueued? ? t('help.sites.enqueued') : t('help.sites.enqueue'),
data: { disable_with: t('sites.enqueued') },
disabled: site.enqueued?

View file

@ -72,10 +72,10 @@
.btn-group{ role: 'group', 'aria-label': t('.design.actions') }
- if design.url
= link_to t('.design.url'), design.url,
target: '_blank', class: 'btn'
target: '_blank', class: 'btn btn-secondary'
- if design.license
= link_to t('.design.license'), design.license,
target: '_blank', class: 'btn'
target: '_blank', class: 'btn btn-secondary'
%hr/
.form-group.licenses#license_id
@ -99,7 +99,7 @@
tags: %w[p a strong em ul ol li h1 h2 h3 h4 h5 h6]
- unless licencia.custom?
= link_to t('.licencia.url'), licencia.url, target: '_blank', class: 'btn', rel: 'noopener'
= link_to t('.licencia.url'), licencia.url, target: '_blank', class: 'btn btn-secondary', rel: 'noopener'
%hr/
@ -164,4 +164,4 @@
deploy: deploy_fields, site: site
.form-group
= f.submit submit, class: 'btn btn-lg btn-block'
= f.submit submit, class: 'btn btn-secondary btn-lg btn-block'

View file

@ -27,4 +27,4 @@
.row.justify-content-center
.col-12.col-lg-8
= link_to t('.merge.request'), site_pull_path(@site),
method: 'post', class: 'btn btn-lg'
method: 'post', class: 'btn btn-secondary btn-lg'

View file

@ -4,7 +4,7 @@
%p.lead= t('.help')
- if policy(Site).new?
= link_to t('sites.new.title'), new_site_path,
class: 'btn'
class: 'btn btn-secondary'
%section.col
- if @sites.empty?
@ -29,18 +29,18 @@
= site.title
%p.lead= site.description
%br
= link_to t('.visit'), site.url, class: 'btn'
= link_to t('.visit'), site.url, class: 'btn btn-secondary'
- if rol.temporal
= button_to t('sites.invitations.accept'),
site_usuaries_accept_invitation_path(site),
method: :patch,
title: t('help.sites.invitations.accept'),
class: 'btn'
class: 'btn btn-secondary'
= button_to t('sites.invitations.reject'),
site_usuaries_reject_invitation_path(site),
method: :patch,
title: t('help.sites.invitations.reject'),
class: 'btn'
class: 'btn btn-secondary'
- else
- if policy(site).show?
= render 'layouts/btn_with_tooltip',

View file

@ -11,11 +11,11 @@
%form.mb-5.form-inline{ method: 'get' }
- Stat::INTERVALS.each do |interval|
= link_to t(".#{interval}"), site_stats_path(interval: interval, urls: params[:urls], period_start: params[:period_start].to_date.try(:"beginning_of_#{interval}").to_date, period_end: params[:period_end]), class: "mb-0 btn #{'btn-primary active' if @interval == interval}"
= link_to t(".#{interval}"), site_stats_path(interval: interval, urls: params[:urls], period_start: params[:period_start].to_date.try(:"beginning_of_#{interval}").to_date, period_end: params[:period_end]), class: "mb-0 btn #{@interval == interval ? 'btn-primary active' : 'btn-secondary' }"
%input.form-control{ type: 'date', name: :period_start, value: params[:period_start] }
%input.form-control{ type: 'date', name: :period_end, value: params[:period_end] }
%button.btn.mb-0{ type: 'submit' }= t('.filter')
%button.btn.btn-secondary.mb-0{ type: 'submit' }= t('.filter')
.mb-5
%h2= t('.host.title', count: @hostnames.size)
@ -34,7 +34,7 @@
%textarea#urls.form-control{ name: 'urls', autocomplete: 'on', required: true, rows: @normalized_urls.size + 1, aria_describedby: 'help-urls' }= @normalized_urls.join("\n")
%small#help-urls.feedback.form-text.text-muted= t('.urls.help')
.form-group
%button.btn{ type: 'submit' }= t('.urls.submit')
%button.btn.btn-secondary{ type: 'submit' }= t('.urls.submit')
- if @normalized_urls.present?
= line_chart site_stats_uris_path(urls: @normalized_urls, **@chart_params), **@chart_options

View file

@ -9,13 +9,13 @@
- if @policy.invite?
= link_to t('.invite'),
site_usuaries_invite_path(@site, invite_as: u.to_s),
class: 'btn',
class: 'btn btn-secondary',
data: { toggle: 'tooltip' },
title: t('.help.invite', invite_as: u.to_s)
- if policy(Collaboration.new(@site)).collaborate?
= link_to t('.public_invite'),
site_collaborate_path(@site),
class: 'btn',
class: 'btn btn-secondary',
data: { toggle: 'tooltip' },
title: t('.help.public_invite')
%p.lead= t(".help.#{u}")
@ -38,7 +38,7 @@
- if @policy.demote? && @site.usuarie?(cuenta)
= link_to t('.demote.text'),
site_usuarie_demote_path(@site, cuenta),
class: 'btn',
class: 'btn btn-secondary',
data: { toggle: 'tooltip',
confirm: t('.demote.confirm') },
title: t('.help.demote'),
@ -46,7 +46,7 @@
- if @policy.promote? && @site.invitade?(cuenta)
= link_to t('.promote.text'),
site_usuarie_promote_path(@site, cuenta),
class: 'btn',
class: 'btn btn-secondary',
data: { toggle: 'tooltip',
confirm: t('.promote.confirm') },
title: t('.help.promote'),
@ -54,7 +54,7 @@
- if @policy.destroy?
= link_to t('.destroy.text'),
site_usuarie_path(@site, cuenta),
class: 'btn',
class: 'btn btn-secondary',
data: { toggle: 'tooltip',
confirm: t('.destroy.confirm') },
title: t('.help.destroy'),

View file

@ -13,4 +13,4 @@
invite_as: invite_as)
= f.text_area :invitaciones, class: 'form-control'
.form-group
= f.submit t('.submit'), class: 'btn'
= f.submit t('.submit'), class: 'btn btn-secondary'

8
bin/modified_files Executable file
View file

@ -0,0 +1,8 @@
#!/bin/sh
set -e
test -n "${CI_MERGE_REQUEST_DIFF_BASE_SHA}"
git diff --name-status ${CI_MERGE_REQUEST_DIFF_BASE_SHA} \
| grep -v "^D" \
| cut -f 2

4
bin/with_extension Executable file
View file

@ -0,0 +1,4 @@
#!/bin/sh
grep "\.${1}$"
exit 0

View file

@ -21,6 +21,17 @@ require 'rails/test_unit/railtie'
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)
if %w[development test].include? ENV['RAILS_ENV']
# https://github.com/bkeepers/dotenv/pull/453
Dotenv::Railtie.class_eval do
def overload
Dotenv.overload(*dotenv_files.reverse)
end
end
Dotenv::Railtie.overload
end
module Sutty
# Sutty!
class Application < Rails::Application

252
config/brakeman.ignore Normal file
View file

@ -0,0 +1,252 @@
{
"ignored_warnings": [
{
"warning_type": "Redirect",
"warning_code": 18,
"fingerprint": "0ae5c3990d49dfbfd4fd61874451f7a576d5056aca913068adf58c314625f810",
"check_name": "Redirect",
"message": "Possible unprotected redirect",
"file": "app/controllers/api/v1/posts_controller.rb",
"line": 20,
"link": "https://brakemanscanner.org/docs/warning_types/redirect/",
"code": "redirect_to((params[:redirect_to] or origin.to_s))",
"render_path": null,
"location": {
"type": "method",
"class": "Api::V1::PostsController",
"method": "create"
},
"user_input": "params[:redirect_to]",
"confidence": "High",
"cwe_id": [
601
],
"note": "https://0xacab.org/sutty/sutty/-/issues/14957"
},
{
"warning_type": "Denial of Service",
"warning_code": 76,
"fingerprint": "1947d1a2ae6e4bf718d0cc563e660efca96897165e9a8dd18186c1d7abe6ddf6",
"check_name": "RegexDoS",
"message": "Model attribute used in regular expression",
"file": "app/controllers/api/v1/base_controller.rb",
"line": 20,
"link": "https://brakemanscanner.org/docs/warning_types/denial_of_service/",
"code": "/\\.#{Site.domain}\\z/",
"render_path": null,
"location": {
"type": "method",
"class": "Api::V1::BaseController",
"method": "site_id"
},
"user_input": "Site.domain",
"confidence": "Medium",
"cwe_id": [
20,
185
],
"note": "No es un atributo, es una variable de entorno"
},
{
"warning_type": "Cross-Site Scripting",
"warning_code": 4,
"fingerprint": "28d98d08a15c4b3ad94a2cfa20a12573de12d99f1a30b3ca51074ee1f1886592",
"check_name": "LinkToHref",
"message": "Potentially unsafe model attribute in `link_to` href",
"file": "app/views/layouts/_breadcrumb.haml",
"line": 19,
"link": "https://brakemanscanner.org/docs/warning_types/link_to_href",
"code": "link_to(t(\".tienda\"), Site.find(params[:site_id]).tienda_url, :role => \"button\", :class => \"btn\")",
"render_path": [
{
"type": "controller",
"class": "Api::V1::NoticesController",
"method": "site",
"line": 31,
"file": "app/controllers/api/v1/notices_controller.rb",
"rendered": {
"name": "layouts/application",
"file": "app/views/layouts/application.html.haml"
}
},
{
"type": "template",
"name": "layouts/application",
"line": 25,
"file": "app/views/layouts/application.html.haml",
"rendered": {
"name": "layouts/_breadcrumb",
"file": "app/views/layouts/_breadcrumb.haml"
}
}
],
"location": {
"type": "template",
"template": "layouts/_breadcrumb"
},
"user_input": "Site.find(params[:site_id]).tienda_url",
"confidence": "Weak",
"cwe_id": [
79
],
"note": ""
},
{
"warning_type": "Redirect",
"warning_code": 18,
"fingerprint": "5034e51aaa1bac06d15fdde5956edffbfd65f94f5620a409526bbea896dc7b5f",
"check_name": "Redirect",
"message": "Possible unprotected redirect",
"file": "app/controllers/api/v1/contact_controller.rb",
"line": 26,
"link": "https://brakemanscanner.org/docs/warning_types/redirect/",
"code": "redirect_to((params[:redirect] or origin.to_s))",
"render_path": null,
"location": {
"type": "method",
"class": "Api::V1::ContactController",
"method": "receive"
},
"user_input": "params[:redirect]",
"confidence": "High",
"cwe_id": [
601
],
"note": "https://0xacab.org/sutty/sutty/-/issues/14957"
},
{
"warning_type": "Mass Assignment",
"warning_code": 70,
"fingerprint": "50582f39f8dfa900d3f2b5b9908b1592f8b8bd9e2d0b9d1cc05d77e5ede2d94e",
"check_name": "MassAssignment",
"message": "Specify exact keys allowed for mass assignment instead of using `permit!` which allows any keys",
"file": "app/views/layouts/_link_rel_alternate.haml",
"line": 2,
"link": "https://brakemanscanner.org/docs/warning_types/mass_assignment/",
"code": "params.permit!",
"render_path": [
{
"type": "controller",
"class": "Api::V1::BaseController",
"method": "site_id",
"line": 20,
"file": "app/controllers/api/v1/base_controller.rb",
"rendered": {
"name": "layouts/application",
"file": "app/views/layouts/application.html.haml"
}
},
{
"type": "template",
"name": "layouts/application",
"line": 21,
"file": "app/views/layouts/application.html.haml",
"rendered": {
"name": "layouts/_link_rel_alternate",
"file": "app/views/layouts/_link_rel_alternate.haml"
}
}
],
"location": {
"type": "template",
"template": "layouts/_link_rel_alternate"
},
"user_input": null,
"confidence": "Medium",
"cwe_id": [
915
],
"note": "https://0xacab.org/sutty/sutty/-/issues/14958"
},
{
"warning_type": "Mass Assignment",
"warning_code": 70,
"fingerprint": "b8e0aa898288bebb614ccc1340d169caa196d315c6ac2e4744081cc892c2ae97",
"check_name": "MassAssignment",
"message": "Specify exact keys allowed for mass assignment instead of using `permit!` which allows any keys",
"file": "app/views/layouts/_breadcrumb.haml",
"line": 30,
"link": "https://brakemanscanner.org/docs/warning_types/mass_assignment/",
"code": "params.permit!",
"render_path": [
{
"type": "controller",
"class": "Api::V1::BaseController",
"method": "site_id",
"line": 20,
"file": "app/controllers/api/v1/base_controller.rb",
"rendered": {
"name": "layouts/application",
"file": "app/views/layouts/application.html.haml"
}
},
{
"type": "template",
"name": "layouts/application",
"line": 25,
"file": "app/views/layouts/application.html.haml",
"rendered": {
"name": "layouts/_breadcrumb",
"file": "app/views/layouts/_breadcrumb.haml"
}
}
],
"location": {
"type": "template",
"template": "layouts/_breadcrumb"
},
"user_input": null,
"confidence": "Medium",
"cwe_id": [
915
],
"note": "https://0xacab.org/sutty/sutty/-/issues/14958"
},
{
"warning_type": "Cross-Site Scripting",
"warning_code": 4,
"fingerprint": "c051421c7cf4c2706b8e27bfd2f3b0661ec6a6df873da322a6b634b59e80351b",
"check_name": "LinkToHref",
"message": "Potentially unsafe model attribute in `link_to` href",
"file": "app/views/sites/_form.haml",
"line": 74,
"link": "https://brakemanscanner.org/docs/warning_types/link_to_href",
"code": "link_to(t(\".design.url\"), (Unresolved Model).new.url, :target => \"_blank\", :class => \"btn\")",
"render_path": [
{
"type": "controller",
"class": "SitesController",
"method": "new",
"line": 31,
"file": "app/controllers/sites_controller.rb",
"rendered": {
"name": "sites/new",
"file": "app/views/sites/new.haml"
}
},
{
"type": "template",
"name": "sites/new",
"line": 6,
"file": "app/views/sites/new.haml",
"rendered": {
"name": "sites/_form",
"file": "app/views/sites/_form.haml"
}
}
],
"location": {
"type": "template",
"template": "sites/_form"
},
"user_input": "(Unresolved Model).new.url",
"confidence": "Weak",
"cwe_id": [
79
],
"note": ""
}
],
"updated": "2024-01-11 18:12:14 -0300",
"brakeman_version": "5.4.1"
}

View file

@ -28,9 +28,6 @@ Rails.application.routes.draw do
# alias en nginx sin tener que usar expresiones regulares para
# detectar el nombre del sitio.
get '/sites/private/:site_id(*file)', to: 'private#show', constraints: { site_id: %r{[^/]+} }
# Obtener archivos estáticos desde el directorio público
get '/sites/:site_id/static_file/(*file)', to: 'sites#static_file', as: 'site_static_file',
constraints: { site_id: %r{[^/]+} }
get '/env.js', to: 'env#index'
match '/api/v3/projects/:site_id/notices' => 'api/v1/notices#create', via: %i[post]

View file

@ -58,6 +58,35 @@ CREATE EXTENSION IF NOT EXISTS pgcrypto WITH SCHEMA public;
COMMENT ON EXTENSION pgcrypto IS 'cryptographic functions';
--
-- Name: access_logs_before_insert_row_tr(); Type: FUNCTION; Schema: public; Owner: -
--
CREATE FUNCTION public.access_logs_before_insert_row_tr() RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN
new.created_at := to_timestamp(new.msec);
RETURN NEW;
END;
$$;
--
-- Name: indexed_posts_before_insert_update_row_tr(); Type: FUNCTION; Schema: public; Owner: -
--
CREATE FUNCTION public.indexed_posts_before_insert_update_row_tr() RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN
new.indexed_content := to_tsvector(('pg_catalog.' || new.dictionary)::regconfig, coalesce(new.title, '') || '
' || coalesce(new.content,''));
RETURN NEW;
END;
$$;
--
-- Name: que_validate_tags(jsonb); Type: FUNCTION; Schema: public; Owner: -
--
@ -117,35 +146,6 @@ WITH (fillfactor='90');
COMMENT ON TABLE public.que_jobs IS '7';
--
-- Name: access_logs_before_insert_row_tr(); Type: FUNCTION; Schema: public; Owner: -
--
CREATE FUNCTION public.access_logs_before_insert_row_tr() RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN
new.created_at := to_timestamp(new.msec);
RETURN NEW;
END;
$$;
--
-- Name: indexed_posts_before_insert_update_row_tr(); Type: FUNCTION; Schema: public; Owner: -
--
CREATE FUNCTION public.indexed_posts_before_insert_update_row_tr() RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN
new.indexed_content := to_tsvector(('pg_catalog.' || new.dictionary)::regconfig, coalesce(new.title, '') || '
' || coalesce(new.content,''));
RETURN NEW;
END;
$$;
--
-- Name: que_determine_job_state(public.que_jobs); Type: FUNCTION; Schema: public; Owner: -
--

View file

@ -19,10 +19,10 @@ check program stats
every "0 1 * * *"
if status != 0 then alert
check process que with pidfile /srv/tmp/que.pid
start program = "/usr/bin/foreman run -f /srv/Procfile -d /srv que"
stop program = "/bin/sh -c 'cat /srv/tmp/que.pid | xargs -r kill'"
check filesystem root with path /
if space usage > 80% for 5 times within 15 cycles then alert
if space usage > 90% for 5 cycles then exec "/usr/bin/foreman run -f /srv/Procfile -d /srv emergency_cleanup" as uid "rails" gid "www-data"
check process que with pidfile /srv/tmp/que.pid
start program = "/usr/bin/foreman run -f /srv/Procfile -d /srv que"
stop program = "/bin/sh -c 'cat /srv/tmp/que.pid | xargs -r kill'"

View file

@ -12,8 +12,8 @@
"@rails/actiontext": "^6.0.0",
"@rails/activestorage": "^6.1.3-1",
"@rails/ujs": "^6.1.3-1",
"@rails/webpacker": "5.2.1",
"@suttyweb/editor": "^0.1.24",
"@rails/webpacker": "5.4.4",
"@suttyweb/editor": "^0.1.25",
"babel-loader": "^8.2.2",
"chart.js": "^3.5.1",
"chartkick": "^4.0.5",

Binary file not shown.

BIN
public/packs/css/application-1224e21e.css (Stored with Git LFS) Normal file

Binary file not shown.

BIN
public/packs/css/application-1224e21e.css.br (Stored with Git LFS) Normal file

Binary file not shown.

BIN
public/packs/css/application-1224e21e.css.gz (Stored with Git LFS) Normal file

Binary file not shown.

BIN
public/packs/css/application-7d15ae94.css (Stored with Git LFS)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

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

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
public/packs/js/application-886df482a133d55527f8.js (Stored with Git LFS) Normal file

Binary file not shown.

BIN
public/packs/js/application-886df482a133d55527f8.js.br (Stored with Git LFS) Normal file

Binary file not shown.

BIN
public/packs/js/application-886df482a133d55527f8.js.gz (Stored with Git LFS) Normal file

Binary file not shown.

BIN
public/packs/js/application-886df482a133d55527f8.js.map (Stored with Git LFS) Normal file

Binary file not shown.

BIN
public/packs/js/application-886df482a133d55527f8.js.map.br (Stored with Git LFS) Normal file

Binary file not shown.

BIN
public/packs/js/application-886df482a133d55527f8.js.map.gz (Stored with Git LFS) Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

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