Merge branch 'main' of https://github.com/glitch-soc/mastodon
This commit is contained in:
commit
5012cb42ae
|
@ -20,7 +20,7 @@
|
|||
"forwardPorts": [3000, 4000],
|
||||
|
||||
// Use 'postCreateCommand' to run commands after the container is created.
|
||||
"postCreateCommand": "bundle install --path vendor/bundle && yarn install && ./bin/rails db:setup",
|
||||
"postCreateCommand": "bundle install --path vendor/bundle && yarn install && git checkout -- Gemfile.lock && ./bin/rails db:setup",
|
||||
|
||||
// Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
|
||||
"remoteUser": "vscode"
|
||||
|
|
|
@ -27,6 +27,7 @@ services:
|
|||
ES_ENABLED: 'true'
|
||||
ES_HOST: es
|
||||
ES_PORT: '9200'
|
||||
LIBRE_TRANSLATE_ENDPOINT: http://libretranslate:5000
|
||||
# Overrides default command so things don't shut down after the process ends.
|
||||
command: sleep infinity
|
||||
networks:
|
||||
|
@ -72,6 +73,12 @@ services:
|
|||
soft: -1
|
||||
hard: -1
|
||||
|
||||
libretranslate:
|
||||
image: libretranslate/libretranslate:v1.2.9
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- internal_network
|
||||
|
||||
volumes:
|
||||
postgres-data:
|
||||
redis-data:
|
||||
|
|
254
.env.nanobox
254
.env.nanobox
|
@ -1,254 +0,0 @@
|
|||
# Service dependencies
|
||||
# You may set REDIS_URL instead for more advanced options
|
||||
REDIS_HOST=$DATA_REDIS_HOST
|
||||
REDIS_PORT=6379
|
||||
# REDIS_DB=0
|
||||
|
||||
# You may set DATABASE_URL instead for more advanced options
|
||||
DB_HOST=$DATA_DB_HOST
|
||||
DB_USER=$DATA_DB_USER
|
||||
DB_NAME=gonano
|
||||
DB_PASS=$DATA_DB_PASS
|
||||
DB_PORT=5432
|
||||
|
||||
# DATABASE_URL=postgresql://$DATA_DB_USER:$DATA_DB_PASS@$DATA_DB_HOST/gonano
|
||||
|
||||
# Optional Elasticsearch configuration
|
||||
ES_ENABLED=true
|
||||
ES_HOST=$DATA_ELASTIC_HOST
|
||||
ES_PORT=9200
|
||||
|
||||
BIND=0.0.0.0
|
||||
|
||||
# Federation
|
||||
# Note: Changing LOCAL_DOMAIN at a later time will cause unwanted side effects, including breaking all existing federation.
|
||||
# LOCAL_DOMAIN should *NOT* contain the protocol part of the domain e.g https://example.com.
|
||||
LOCAL_DOMAIN=${APP_NAME}.nanoapp.io
|
||||
|
||||
# Changing LOCAL_HTTPS in production is no longer supported. (Mastodon will always serve https:// links)
|
||||
|
||||
# Use this only if you need to run mastodon on a different domain than the one used for federation.
|
||||
# You can read more about this option on https://github.com/tootsuite/documentation/blob/master/Running-Mastodon/Serving_a_different_domain.md
|
||||
# DO *NOT* USE THIS UNLESS YOU KNOW *EXACTLY* WHAT YOU ARE DOING.
|
||||
# WEB_DOMAIN=mastodon.example.com
|
||||
|
||||
# Use this if you want to have several aliases handler@example1.com
|
||||
# handler@example2.com etc. for the same user. LOCAL_DOMAIN should not
|
||||
# be added. Comma separated values
|
||||
# ALTERNATE_DOMAINS=example1.com,example2.com
|
||||
|
||||
# Application secrets
|
||||
# Generate each with the `rake secret` task (`nanobox run bundle exec rake secret`)
|
||||
SECRET_KEY_BASE=$SECRET_KEY_BASE
|
||||
OTP_SECRET=$OTP_SECRET
|
||||
|
||||
# VAPID keys (used for push notifications)
|
||||
# You can generate the keys using the following command (first is the private key, second is the public one)
|
||||
# You should only generate this once per instance. If you later decide to change it, all push subscription will
|
||||
# be invalidated, requiring the users to access the website again to resubscribe.
|
||||
#
|
||||
# Generate with `rake mastodon:webpush:generate_vapid_key` task (`nanobox run bundle exec rake mastodon:webpush:generate_vapid_key`)
|
||||
#
|
||||
# For more information visit https://rossta.net/blog/using-the-web-push-api-with-vapid.html
|
||||
VAPID_PRIVATE_KEY=$VAPID_PRIVATE_KEY
|
||||
VAPID_PUBLIC_KEY=$VAPID_PUBLIC_KEY
|
||||
|
||||
# Registrations
|
||||
# Single user mode will disable registrations and redirect frontpage to the first profile
|
||||
# SINGLE_USER_MODE=true
|
||||
# Prevent registrations with following e-mail domains
|
||||
# EMAIL_DOMAIN_BLACKLIST=example1.com|example2.de|etc
|
||||
# Only allow registrations with the following e-mail domains
|
||||
# EMAIL_DOMAIN_WHITELIST=example1.com|example2.de|etc
|
||||
|
||||
# Optionally change default language
|
||||
# DEFAULT_LOCALE=de
|
||||
|
||||
# E-mail configuration
|
||||
# Note: Mailgun and SparkPost (https://sparkpo.st/smtp) each have good free tiers
|
||||
# If you want to use an SMTP server without authentication (e.g local Postfix relay)
|
||||
# then set SMTP_AUTH_METHOD and SMTP_OPENSSL_VERIFY_MODE to 'none' and
|
||||
# *comment* SMTP_LOGIN and SMTP_PASSWORD (leaving them blank is not enough).
|
||||
SMTP_SERVER=$SMTP_SERVER
|
||||
SMTP_PORT=587
|
||||
SMTP_LOGIN=$SMTP_LOGIN
|
||||
SMTP_PASSWORD=$SMTP_PASSWORD
|
||||
SMTP_FROM_ADDRESS=notifications@${APP_NAME}.nanoapp.io
|
||||
#SMTP_REPLY_TO=
|
||||
#SMTP_DOMAIN= # defaults to LOCAL_DOMAIN
|
||||
#SMTP_DELIVERY_METHOD=smtp # delivery method can also be sendmail
|
||||
#SMTP_AUTH_METHOD=plain
|
||||
#SMTP_CA_FILE=/etc/ssl/certs/ca-certificates.crt
|
||||
#SMTP_OPENSSL_VERIFY_MODE=peer
|
||||
#SMTP_ENABLE_STARTTLS_AUTO=true
|
||||
#SMTP_TLS=true
|
||||
|
||||
# Optional user upload path and URL (images, avatars). Default is :rails_root/public/system. If you set this variable, you are responsible for making your HTTP server (eg. nginx) serve these files.
|
||||
# PAPERCLIP_ROOT_PATH=/var/lib/mastodon/public-system
|
||||
# PAPERCLIP_ROOT_URL=/system
|
||||
|
||||
# Optional asset host for multi-server setups
|
||||
# The asset host must allow cross origin request from WEB_DOMAIN or LOCAL_DOMAIN
|
||||
# if WEB_DOMAIN is not set. For example, the server may have the
|
||||
# following header field:
|
||||
# Access-Control-Allow-Origin: https://example.com/
|
||||
# CDN_HOST=https://assets.example.com
|
||||
|
||||
# S3 (optional)
|
||||
# The attachment host must allow cross origin request from WEB_DOMAIN or
|
||||
# LOCAL_DOMAIN if WEB_DOMAIN is not set. For example, the server may have the
|
||||
# following header field:
|
||||
# Access-Control-Allow-Origin: https://192.168.1.123:9000/
|
||||
# S3_ENABLED=true
|
||||
# S3_BUCKET=
|
||||
# AWS_ACCESS_KEY_ID=
|
||||
# AWS_SECRET_ACCESS_KEY=
|
||||
# S3_REGION=
|
||||
# S3_PROTOCOL=http
|
||||
# S3_HOSTNAME=192.168.1.123:9000
|
||||
|
||||
# S3 (Minio Config (optional) Please check Minio instance for details)
|
||||
# The attachment host must allow cross origin request - see the description
|
||||
# above.
|
||||
# S3_ENABLED=true
|
||||
# S3_BUCKET=
|
||||
# AWS_ACCESS_KEY_ID=
|
||||
# AWS_SECRET_ACCESS_KEY=
|
||||
# S3_REGION=
|
||||
# S3_PROTOCOL=https
|
||||
# S3_HOSTNAME=
|
||||
# S3_ENDPOINT=
|
||||
# S3_SIGNATURE_VERSION=
|
||||
|
||||
# Google Cloud Storage (optional)
|
||||
# Use S3 compatible API. Since GCS does not support Multipart Upload,
|
||||
# increase the value of S3_MULTIPART_THRESHOLD to disable Multipart Upload.
|
||||
# The attachment host must allow cross origin request - see the description
|
||||
# above.
|
||||
# S3_ENABLED=true
|
||||
# AWS_ACCESS_KEY_ID=
|
||||
# AWS_SECRET_ACCESS_KEY=
|
||||
# S3_REGION=
|
||||
# S3_PROTOCOL=https
|
||||
# S3_HOSTNAME=storage.googleapis.com
|
||||
# S3_ENDPOINT=https://storage.googleapis.com
|
||||
# S3_MULTIPART_THRESHOLD=52428801 # 50.megabytes
|
||||
|
||||
# Swift (optional)
|
||||
# The attachment host must allow cross origin request - see the description
|
||||
# above.
|
||||
# SWIFT_ENABLED=true
|
||||
# SWIFT_USERNAME=
|
||||
# For Keystone V3, the value for SWIFT_TENANT should be the project name
|
||||
# SWIFT_TENANT=
|
||||
# SWIFT_PASSWORD=
|
||||
# Some OpenStack V3 providers require PROJECT_ID (optional)
|
||||
# SWIFT_PROJECT_ID=
|
||||
# Keystone V2 and V3 URLs are supported. Use a V3 URL if possible to avoid
|
||||
# issues with token rate-limiting during high load.
|
||||
# SWIFT_AUTH_URL=
|
||||
# SWIFT_CONTAINER=
|
||||
# SWIFT_OBJECT_URL=
|
||||
# SWIFT_REGION=
|
||||
# Defaults to 'default'
|
||||
# SWIFT_DOMAIN_NAME=
|
||||
# Defaults to 60 seconds. Set to 0 to disable
|
||||
# SWIFT_CACHE_TTL=
|
||||
|
||||
# Optional alias for S3 (e.g. to serve files on a custom domain, possibly using Cloudfront or Cloudflare)
|
||||
# S3_ALIAS_HOST=
|
||||
|
||||
# Streaming API integration
|
||||
# STREAMING_API_BASE_URL=
|
||||
|
||||
# Advanced settings
|
||||
# If you need to use pgBouncer, you need to disable prepared statements:
|
||||
# PREPARED_STATEMENTS=false
|
||||
|
||||
# Cluster number setting for streaming API server.
|
||||
# If you comment out following line, cluster number will be `numOfCpuCores - 1`.
|
||||
# STREAMING_CLUSTER_NUM=1
|
||||
|
||||
# Docker mastodon user
|
||||
# If you use Docker, you may want to assign UID/GID manually.
|
||||
# UID=1000
|
||||
# GID=1000
|
||||
|
||||
# LDAP authentication (optional)
|
||||
# LDAP_ENABLED=true
|
||||
# LDAP_HOST=localhost
|
||||
# LDAP_PORT=389
|
||||
# LDAP_METHOD=simple_tls
|
||||
# LDAP_BASE=
|
||||
# LDAP_BIND_DN=
|
||||
# LDAP_PASSWORD=
|
||||
# LDAP_UID=cn
|
||||
# LDAP_MAIL=mail
|
||||
# LDAP_SEARCH_FILTER=(|(%{uid}=%{email})(%{mail}=%{email}))
|
||||
# LDAP_UID_CONVERSION_ENABLED=true
|
||||
# LDAP_UID_CONVERSION_SEARCH=., -
|
||||
# LDAP_UID_CONVERSION_REPLACE=_
|
||||
|
||||
# PAM authentication (optional)
|
||||
# PAM authentication uses for the email generation the "email" pam variable
|
||||
# and optional as fallback PAM_DEFAULT_SUFFIX
|
||||
# The pam environment variable "email" is provided by:
|
||||
# https://github.com/devkral/pam_email_extractor
|
||||
# PAM_ENABLED=true
|
||||
# Fallback email domain for email address generation (LOCAL_DOMAIN by default)
|
||||
# PAM_EMAIL_DOMAIN=example.com
|
||||
# Name of the pam service (pam "auth" section is evaluated)
|
||||
# PAM_DEFAULT_SERVICE=rpam
|
||||
# Name of the pam service used for checking if an user can register (pam "account" section is evaluated) (nil (disabled) by default)
|
||||
# PAM_CONTROLLED_SERVICE=rpam
|
||||
|
||||
# Optional CAS authentication (cf. omniauth-cas) :
|
||||
# CAS_ENABLED=true
|
||||
# CAS_URL=https://sso.myserver.com/
|
||||
# CAS_HOST=sso.myserver.com/
|
||||
# CAS_PORT=443
|
||||
# CAS_SSL=true
|
||||
# CAS_VALIDATE_URL=
|
||||
# CAS_CALLBACK_URL=
|
||||
# CAS_LOGOUT_URL=
|
||||
# CAS_LOGIN_URL=
|
||||
# CAS_UID_FIELD='user'
|
||||
# CAS_CA_PATH=
|
||||
# CAS_DISABLE_SSL_VERIFICATION=false
|
||||
# CAS_UID_KEY='user'
|
||||
# CAS_NAME_KEY='name'
|
||||
# CAS_EMAIL_KEY='email'
|
||||
# CAS_NICKNAME_KEY='nickname'
|
||||
# CAS_FIRST_NAME_KEY='firstname'
|
||||
# CAS_LAST_NAME_KEY='lastname'
|
||||
# CAS_LOCATION_KEY='location'
|
||||
# CAS_IMAGE_KEY='image'
|
||||
# CAS_PHONE_KEY='phone'
|
||||
# CAS_SECURITY_ASSUME_EMAIL_IS_VERIFIED=true
|
||||
|
||||
# Optional SAML authentication (cf. omniauth-saml)
|
||||
# SAML_ENABLED=true
|
||||
# SAML_ACS_URL=http://localhost:3000/auth/auth/saml/callback
|
||||
# SAML_ISSUER=https://example.com
|
||||
# SAML_IDP_SSO_TARGET_URL=https://idp.testshib.org/idp/profile/SAML2/Redirect/SSO
|
||||
# SAML_IDP_CERT=
|
||||
# SAML_IDP_CERT_FINGERPRINT=
|
||||
# SAML_NAME_IDENTIFIER_FORMAT=
|
||||
# SAML_CERT=
|
||||
# SAML_PRIVATE_KEY=
|
||||
# SAML_SECURITY_WANT_ASSERTION_SIGNED=true
|
||||
# SAML_SECURITY_WANT_ASSERTION_ENCRYPTED=true
|
||||
# SAML_SECURITY_ASSUME_EMAIL_IS_VERIFIED=true
|
||||
# SAML_ATTRIBUTES_STATEMENTS_UID="urn:oid:0.9.2342.19200300.100.1.1"
|
||||
# SAML_ATTRIBUTES_STATEMENTS_EMAIL="urn:oid:1.3.6.1.4.1.5923.1.1.1.6"
|
||||
# SAML_ATTRIBUTES_STATEMENTS_FULL_NAME="urn:oid:2.16.840.1.113730.3.1.241"
|
||||
# SAML_ATTRIBUTES_STATEMENTS_FIRST_NAME="urn:oid:2.5.4.42"
|
||||
# SAML_ATTRIBUTES_STATEMENTS_LAST_NAME="urn:oid:2.5.4.4"
|
||||
# SAML_UID_ATTRIBUTE="urn:oid:0.9.2342.19200300.100.1.1"
|
||||
# SAML_ATTRIBUTES_STATEMENTS_VERIFIED=
|
||||
# SAML_ATTRIBUTES_STATEMENTS_VERIFIED_EMAIL=
|
||||
|
||||
# Use HTTP proxy for outgoing request (optional)
|
||||
# http_proxy=http://gateway.local:8118
|
||||
# Access control for hidden service.
|
||||
# ALLOW_ACCESS_TO_HIDDEN_SERVICE=true
|
|
@ -10,6 +10,9 @@ on:
|
|||
paths:
|
||||
- .github/workflows/build-image.yml
|
||||
- Dockerfile
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build-image:
|
||||
runs-on: ubuntu-latest
|
||||
|
|
|
@ -9,6 +9,9 @@ on:
|
|||
env:
|
||||
RAILS_ENV: test
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
check-i18n:
|
||||
runs-on: ubuntu-latest
|
||||
|
|
|
@ -55,7 +55,7 @@ jobs:
|
|||
with:
|
||||
node-version: 16.x
|
||||
cache: yarn
|
||||
- name: Intall dependencies
|
||||
- name: Install dependencies
|
||||
run: yarn install --frozen-lockfile
|
||||
- name: Set-up RuboCop Problem Mathcher
|
||||
uses: r7kamura/rubocop-problem-matchers-action@v1
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
mastodon
|
|
@ -1 +1 @@
|
|||
3.0.3
|
||||
3.0.4
|
||||
|
|
|
@ -5,7 +5,7 @@ SHELL ["/bin/bash", "-c"]
|
|||
RUN echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections
|
||||
|
||||
# Install Node v16 (LTS)
|
||||
ENV NODE_VER="16.15.1"
|
||||
ENV NODE_VER="16.17.1"
|
||||
RUN ARCH= && \
|
||||
dpkgArch="$(dpkg --print-architecture)" && \
|
||||
case "${dpkgArch##*-}" in \
|
||||
|
@ -19,7 +19,7 @@ RUN ARCH= && \
|
|||
esac && \
|
||||
echo "Etc/UTC" > /etc/localtime && \
|
||||
apt-get update && \
|
||||
apt-get install -y --no-install-recommends ca-certificates wget python apt-utils && \
|
||||
apt-get install -y --no-install-recommends ca-certificates wget python3 apt-utils && \
|
||||
cd ~ && \
|
||||
wget -q https://nodejs.org/download/release/v$NODE_VER/node-v$NODE_VER-linux-$ARCH.tar.gz && \
|
||||
tar xf node-v$NODE_VER-linux-$ARCH.tar.gz && \
|
||||
|
@ -27,7 +27,7 @@ RUN ARCH= && \
|
|||
mv node-v$NODE_VER-linux-$ARCH /opt/node
|
||||
|
||||
# Install Ruby 3.0
|
||||
ENV RUBY_VER="3.0.3"
|
||||
ENV RUBY_VER="3.0.4"
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends build-essential \
|
||||
bison libyaml-dev libgdbm-dev libreadline-dev libjemalloc-dev \
|
||||
|
|
28
Gemfile
28
Gemfile
|
@ -7,7 +7,7 @@ gem 'pkg-config', '~> 1.4'
|
|||
gem 'rexml', '~> 3.2'
|
||||
|
||||
gem 'puma', '~> 5.6'
|
||||
gem 'rails', '~> 6.1.6'
|
||||
gem 'rails', '~> 6.1.7'
|
||||
gem 'sprockets', '~> 3.7.2'
|
||||
gem 'thor', '~> 1.2'
|
||||
gem 'rack', '~> 2.2.4'
|
||||
|
@ -16,7 +16,7 @@ gem 'hamlit-rails', '~> 0.2'
|
|||
gem 'pg', '~> 1.4'
|
||||
gem 'makara', '~> 0.5'
|
||||
gem 'pghero', '~> 2.8'
|
||||
gem 'dotenv-rails', '~> 2.7'
|
||||
gem 'dotenv-rails', '~> 2.8'
|
||||
|
||||
gem 'aws-sdk-s3', '~> 1.114', require: false
|
||||
gem 'fog-core', '<= 2.1.0'
|
||||
|
@ -26,7 +26,7 @@ gem 'blurhash', '~> 0.1'
|
|||
|
||||
gem 'active_model_serializers', '~> 0.10'
|
||||
gem 'addressable', '~> 2.8'
|
||||
gem 'bootsnap', '~> 1.12.0', require: false
|
||||
gem 'bootsnap', '~> 1.13.0', require: false
|
||||
gem 'browser'
|
||||
gem 'charlock_holmes', '~> 0.7.7'
|
||||
gem 'chewy', '~> 7.2'
|
||||
|
@ -46,16 +46,16 @@ gem 'omniauth-rails_csrf_protection', '~> 0.1'
|
|||
|
||||
gem 'color_diff', '~> 0.1'
|
||||
gem 'discard', '~> 1.2'
|
||||
gem 'doorkeeper', '~> 5.5'
|
||||
gem 'doorkeeper', '~> 5.6'
|
||||
gem 'ed25519', '~> 1.3'
|
||||
gem 'fast_blank', '~> 1.0'
|
||||
gem 'fastimage'
|
||||
gem 'hiredis', '~> 0.6'
|
||||
gem 'redis-namespace', '~> 1.8'
|
||||
gem 'redis-namespace', '~> 1.9'
|
||||
gem 'htmlentities', '~> 4.3'
|
||||
gem 'http', '~> 5.1'
|
||||
gem 'http_accept_language', '~> 2.1'
|
||||
gem 'httplog', '~> 1.5.0'
|
||||
gem 'httplog', '~> 1.6.0'
|
||||
gem 'idn-ruby', require: 'idn'
|
||||
gem 'kaminari', '~> 1.2'
|
||||
gem 'link_header', '~> 0.0'
|
||||
|
@ -91,8 +91,8 @@ gem 'tty-prompt', '~> 0.23', require: false
|
|||
gem 'twitter-text', '~> 3.1.0'
|
||||
gem 'tzinfo-data', '~> 1.2022'
|
||||
gem 'webpacker', '~> 5.4'
|
||||
gem 'webpush', '~> 0.3'
|
||||
gem 'webauthn', '~> 3.0.0.alpha1'
|
||||
gem 'webpush', git: 'https://github.com/ClearlyClaire/webpush.git', ref: 'f14a4d52e201128b1b00245d11b6de80d6cfdcd9'
|
||||
gem 'webauthn', '~> 2.5'
|
||||
|
||||
gem 'json-ld'
|
||||
gem 'json-ld-preloaded', '~> 3.2'
|
||||
|
@ -101,10 +101,10 @@ gem 'rdf-normalize', '~> 0.5'
|
|||
gem 'redcarpet', '~> 3.5'
|
||||
|
||||
group :development, :test do
|
||||
gem 'fabrication', '~> 2.29'
|
||||
gem 'fabrication', '~> 2.30'
|
||||
gem 'fuubar', '~> 2.5'
|
||||
gem 'i18n-tasks', '~> 1.0', require: false
|
||||
gem 'pry-byebug', '~> 3.9'
|
||||
gem 'pry-byebug', '~> 3.10'
|
||||
gem 'pry-rails', '~> 0.3'
|
||||
gem 'rspec-rails', '~> 5.1'
|
||||
end
|
||||
|
@ -116,13 +116,13 @@ end
|
|||
group :test do
|
||||
gem 'capybara', '~> 3.37'
|
||||
gem 'climate_control', '~> 0.2'
|
||||
gem 'faker', '~> 2.21'
|
||||
gem 'faker', '~> 2.23'
|
||||
gem 'microformats', '~> 4.4'
|
||||
gem 'rails-controller-testing', '~> 1.0'
|
||||
gem 'rspec-sidekiq', '~> 3.1'
|
||||
gem 'simplecov', '~> 0.21', require: false
|
||||
gem 'webmock', '~> 3.14'
|
||||
gem 'rspec_junit_formatter', '~> 0.5'
|
||||
gem 'webmock', '~> 3.18'
|
||||
gem 'rspec_junit_formatter', '~> 0.6'
|
||||
end
|
||||
|
||||
group :development do
|
||||
|
@ -136,7 +136,7 @@ group :development do
|
|||
gem 'memory_profiler'
|
||||
gem 'rubocop', '~> 1.30', require: false
|
||||
gem 'rubocop-rails', '~> 2.15', require: false
|
||||
gem 'brakeman', '~> 5.2', require: false
|
||||
gem 'brakeman', '~> 5.3', require: false
|
||||
gem 'bundler-audit', '~> 0.9', require: false
|
||||
|
||||
gem 'capistrano', '~> 3.17'
|
||||
|
|
296
Gemfile.lock
296
Gemfile.lock
|
@ -1,40 +1,49 @@
|
|||
GIT
|
||||
remote: https://github.com/ClearlyClaire/webpush.git
|
||||
revision: f14a4d52e201128b1b00245d11b6de80d6cfdcd9
|
||||
ref: f14a4d52e201128b1b00245d11b6de80d6cfdcd9
|
||||
specs:
|
||||
webpush (0.3.8)
|
||||
hkdf (~> 0.2)
|
||||
jwt (~> 2.0)
|
||||
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
actioncable (6.1.6)
|
||||
actionpack (= 6.1.6)
|
||||
activesupport (= 6.1.6)
|
||||
actioncable (6.1.7)
|
||||
actionpack (= 6.1.7)
|
||||
activesupport (= 6.1.7)
|
||||
nio4r (~> 2.0)
|
||||
websocket-driver (>= 0.6.1)
|
||||
actionmailbox (6.1.6)
|
||||
actionpack (= 6.1.6)
|
||||
activejob (= 6.1.6)
|
||||
activerecord (= 6.1.6)
|
||||
activestorage (= 6.1.6)
|
||||
activesupport (= 6.1.6)
|
||||
actionmailbox (6.1.7)
|
||||
actionpack (= 6.1.7)
|
||||
activejob (= 6.1.7)
|
||||
activerecord (= 6.1.7)
|
||||
activestorage (= 6.1.7)
|
||||
activesupport (= 6.1.7)
|
||||
mail (>= 2.7.1)
|
||||
actionmailer (6.1.6)
|
||||
actionpack (= 6.1.6)
|
||||
actionview (= 6.1.6)
|
||||
activejob (= 6.1.6)
|
||||
activesupport (= 6.1.6)
|
||||
actionmailer (6.1.7)
|
||||
actionpack (= 6.1.7)
|
||||
actionview (= 6.1.7)
|
||||
activejob (= 6.1.7)
|
||||
activesupport (= 6.1.7)
|
||||
mail (~> 2.5, >= 2.5.4)
|
||||
rails-dom-testing (~> 2.0)
|
||||
actionpack (6.1.6)
|
||||
actionview (= 6.1.6)
|
||||
activesupport (= 6.1.6)
|
||||
actionpack (6.1.7)
|
||||
actionview (= 6.1.7)
|
||||
activesupport (= 6.1.7)
|
||||
rack (~> 2.0, >= 2.0.9)
|
||||
rack-test (>= 0.6.3)
|
||||
rails-dom-testing (~> 2.0)
|
||||
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
||||
actiontext (6.1.6)
|
||||
actionpack (= 6.1.6)
|
||||
activerecord (= 6.1.6)
|
||||
activestorage (= 6.1.6)
|
||||
activesupport (= 6.1.6)
|
||||
actiontext (6.1.7)
|
||||
actionpack (= 6.1.7)
|
||||
activerecord (= 6.1.7)
|
||||
activestorage (= 6.1.7)
|
||||
activesupport (= 6.1.7)
|
||||
nokogiri (>= 1.8.5)
|
||||
actionview (6.1.6)
|
||||
activesupport (= 6.1.6)
|
||||
actionview (6.1.7)
|
||||
activesupport (= 6.1.7)
|
||||
builder (~> 3.1)
|
||||
erubi (~> 1.4)
|
||||
rails-dom-testing (~> 2.0)
|
||||
|
@ -45,31 +54,31 @@ GEM
|
|||
case_transform (>= 0.2)
|
||||
jsonapi-renderer (>= 0.1.1.beta1, < 0.3)
|
||||
active_record_query_trace (1.8)
|
||||
activejob (6.1.6)
|
||||
activesupport (= 6.1.6)
|
||||
activejob (6.1.7)
|
||||
activesupport (= 6.1.7)
|
||||
globalid (>= 0.3.6)
|
||||
activemodel (6.1.6)
|
||||
activesupport (= 6.1.6)
|
||||
activerecord (6.1.6)
|
||||
activemodel (= 6.1.6)
|
||||
activesupport (= 6.1.6)
|
||||
activestorage (6.1.6)
|
||||
actionpack (= 6.1.6)
|
||||
activejob (= 6.1.6)
|
||||
activerecord (= 6.1.6)
|
||||
activesupport (= 6.1.6)
|
||||
activemodel (6.1.7)
|
||||
activesupport (= 6.1.7)
|
||||
activerecord (6.1.7)
|
||||
activemodel (= 6.1.7)
|
||||
activesupport (= 6.1.7)
|
||||
activestorage (6.1.7)
|
||||
actionpack (= 6.1.7)
|
||||
activejob (= 6.1.7)
|
||||
activerecord (= 6.1.7)
|
||||
activesupport (= 6.1.7)
|
||||
marcel (~> 1.0)
|
||||
mini_mime (>= 1.1.0)
|
||||
activesupport (6.1.6)
|
||||
activesupport (6.1.7)
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
i18n (>= 1.6, < 2)
|
||||
minitest (>= 5.1)
|
||||
tzinfo (~> 2.0)
|
||||
zeitwerk (~> 2.3)
|
||||
addressable (2.8.0)
|
||||
public_suffix (>= 2.0.2, < 5.0)
|
||||
addressable (2.8.1)
|
||||
public_suffix (>= 2.0.2, < 6.0)
|
||||
aes_key_wrap (1.1.0)
|
||||
airbrussh (1.4.0)
|
||||
airbrussh (1.4.1)
|
||||
sshkit (>= 1.6.1, != 1.7.0)
|
||||
android_key_attestation (0.3.0)
|
||||
annotate (3.2.0)
|
||||
|
@ -79,7 +88,7 @@ GEM
|
|||
attr_encrypted (3.1.0)
|
||||
encryptor (~> 3.0.0)
|
||||
attr_required (1.0.1)
|
||||
awrence (1.1.1)
|
||||
awrence (1.2.1)
|
||||
aws-eventstream (1.2.0)
|
||||
aws-partitions (1.587.0)
|
||||
aws-sdk-core (3.130.2)
|
||||
|
@ -101,12 +110,11 @@ GEM
|
|||
coderay (>= 1.0.0)
|
||||
erubi (>= 1.0.0)
|
||||
rack (>= 0.9.0)
|
||||
better_html (1.0.16)
|
||||
actionview (>= 4.0)
|
||||
activesupport (>= 4.0)
|
||||
better_html (2.0.1)
|
||||
actionview (>= 6.0)
|
||||
activesupport (>= 6.0)
|
||||
ast (~> 2.0)
|
||||
erubi (~> 1.4)
|
||||
html_tokenizer (~> 0.0.6)
|
||||
parser (>= 2.4)
|
||||
smart_properties
|
||||
bindata (2.4.10)
|
||||
|
@ -114,22 +122,22 @@ GEM
|
|||
debug_inspector (>= 0.0.1)
|
||||
blurhash (0.1.6)
|
||||
ffi (~> 1.14)
|
||||
bootsnap (1.12.0)
|
||||
bootsnap (1.13.0)
|
||||
msgpack (~> 1.2)
|
||||
brakeman (5.2.3)
|
||||
brakeman (5.3.1)
|
||||
browser (4.2.0)
|
||||
brpoplpush-redis_script (0.1.2)
|
||||
concurrent-ruby (~> 1.0, >= 1.0.5)
|
||||
redis (>= 1.0, <= 5.0)
|
||||
builder (3.2.4)
|
||||
bullet (7.0.2)
|
||||
bullet (7.0.3)
|
||||
activesupport (>= 3.0.0)
|
||||
uniform_notifier (~> 1.11)
|
||||
bundler-audit (0.9.1)
|
||||
bundler (>= 1.2.0, < 3)
|
||||
thor (~> 1.0)
|
||||
byebug (11.1.3)
|
||||
capistrano (3.17.0)
|
||||
capistrano (3.17.1)
|
||||
airbrussh (>= 1.0.0)
|
||||
i18n
|
||||
rake (>= 10.0.0)
|
||||
|
@ -167,10 +175,10 @@ GEM
|
|||
coderay (1.1.3)
|
||||
color_diff (0.1)
|
||||
concurrent-ruby (1.1.10)
|
||||
connection_pool (2.2.5)
|
||||
cose (1.0.0)
|
||||
connection_pool (2.3.0)
|
||||
cose (1.2.1)
|
||||
cbor (~> 0.5.9)
|
||||
openssl-signature_algorithm (~> 0.4.0)
|
||||
openssl-signature_algorithm (~> 1.0)
|
||||
crack (0.4.5)
|
||||
rexml
|
||||
crass (1.0.6)
|
||||
|
@ -198,11 +206,11 @@ GEM
|
|||
docile (1.3.4)
|
||||
domain_name (0.5.20190701)
|
||||
unf (>= 0.0.5, < 1.0.0)
|
||||
doorkeeper (5.5.4)
|
||||
doorkeeper (5.6.0)
|
||||
railties (>= 5)
|
||||
dotenv (2.7.6)
|
||||
dotenv-rails (2.7.6)
|
||||
dotenv (= 2.7.6)
|
||||
dotenv (2.8.1)
|
||||
dotenv-rails (2.8.1)
|
||||
dotenv (= 2.8.1)
|
||||
railties (>= 3.2)
|
||||
ed25519 (1.3.0)
|
||||
elasticsearch (7.13.3)
|
||||
|
@ -215,12 +223,12 @@ GEM
|
|||
faraday (~> 1)
|
||||
multi_json
|
||||
encryptor (3.0.0)
|
||||
erubi (1.10.0)
|
||||
erubi (1.11.0)
|
||||
et-orbi (1.2.7)
|
||||
tzinfo
|
||||
excon (0.76.0)
|
||||
fabrication (2.29.0)
|
||||
faker (2.21.0)
|
||||
fabrication (2.30.0)
|
||||
faker (2.23.0)
|
||||
i18n (>= 1.8.11, < 2)
|
||||
faraday (1.9.3)
|
||||
faraday-em_http (~> 1.0)
|
||||
|
@ -292,7 +300,6 @@ GEM
|
|||
highline (2.0.3)
|
||||
hiredis (0.6.3)
|
||||
hkdf (0.3.0)
|
||||
html_tokenizer (0.0.7)
|
||||
htmlentities (4.3.4)
|
||||
http (5.1.0)
|
||||
addressable (~> 2.8)
|
||||
|
@ -304,15 +311,15 @@ GEM
|
|||
http-form_data (2.3.0)
|
||||
http_accept_language (2.1.1)
|
||||
httpclient (2.8.3)
|
||||
httplog (1.5.0)
|
||||
rack (>= 1.0)
|
||||
httplog (1.6.0)
|
||||
rack (>= 2.0)
|
||||
rainbow (>= 2.0.0)
|
||||
i18n (1.10.0)
|
||||
i18n (1.12.0)
|
||||
concurrent-ruby (~> 1.0)
|
||||
i18n-tasks (1.0.11)
|
||||
i18n-tasks (1.0.12)
|
||||
activesupport (>= 4.0.2)
|
||||
ast (>= 2.1.0)
|
||||
better_html (~> 1.0)
|
||||
better_html (>= 1.0, < 3.0)
|
||||
erubi
|
||||
highline (>= 2.0.0)
|
||||
i18n
|
||||
|
@ -329,18 +336,18 @@ GEM
|
|||
activesupport (>= 4.2)
|
||||
aes_key_wrap
|
||||
bindata
|
||||
json-ld (3.2.0)
|
||||
json-ld (3.2.3)
|
||||
htmlentities (~> 4.3)
|
||||
json-canonicalization (~> 0.3)
|
||||
link_header (~> 0.0, >= 0.0.8)
|
||||
multi_json (~> 1.15)
|
||||
rack (~> 2.2)
|
||||
rdf (~> 3.2)
|
||||
rdf (~> 3.2, >= 3.2.9)
|
||||
json-ld-preloaded (3.2.0)
|
||||
json-ld (~> 3.2)
|
||||
rdf (~> 3.2)
|
||||
jsonapi-renderer (0.2.2)
|
||||
jwt (2.2.2)
|
||||
jwt (2.4.1)
|
||||
kaminari (1.2.2)
|
||||
activesupport (>= 4.1.0)
|
||||
kaminari-actionview (= 1.2.2)
|
||||
|
@ -377,7 +384,7 @@ GEM
|
|||
activesupport (>= 4)
|
||||
railties (>= 4)
|
||||
request_store (~> 1.0)
|
||||
loofah (2.18.0)
|
||||
loofah (2.19.0)
|
||||
crass (~> 1.0.2)
|
||||
nokogiri (>= 1.5.9)
|
||||
mail (2.7.1)
|
||||
|
@ -398,16 +405,16 @@ GEM
|
|||
mime-types-data (3.2022.0105)
|
||||
mini_mime (1.1.2)
|
||||
mini_portile2 (2.8.0)
|
||||
minitest (5.16.0)
|
||||
msgpack (1.5.2)
|
||||
minitest (5.16.3)
|
||||
msgpack (1.5.4)
|
||||
multi_json (1.15.0)
|
||||
multipart-post (2.1.1)
|
||||
net-ldap (0.17.1)
|
||||
net-scp (3.0.0)
|
||||
net-ssh (>= 2.6.5, < 7.0.0)
|
||||
net-ssh (6.1.0)
|
||||
net-scp (4.0.0.rc1)
|
||||
net-ssh (>= 2.6.5, < 8.0.0)
|
||||
net-ssh (7.0.1)
|
||||
nio4r (2.5.8)
|
||||
nokogiri (1.13.7)
|
||||
nokogiri (1.13.8)
|
||||
mini_portile2 (~> 2.8.0)
|
||||
racc (~> 1.4)
|
||||
nsa (0.2.8)
|
||||
|
@ -415,8 +422,8 @@ GEM
|
|||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
sidekiq (>= 3.5)
|
||||
statsd-ruby (~> 1.4, >= 1.4.0)
|
||||
oj (3.13.17)
|
||||
omniauth (1.9.1)
|
||||
oj (3.13.21)
|
||||
omniauth (1.9.2)
|
||||
hashie (>= 3.4.6)
|
||||
rack (>= 1.6.2, < 3)
|
||||
omniauth-cas (2.0.0)
|
||||
|
@ -439,20 +446,21 @@ GEM
|
|||
validate_email
|
||||
validate_url
|
||||
webfinger (>= 1.0.1)
|
||||
openssl (2.2.0)
|
||||
openssl-signature_algorithm (0.4.0)
|
||||
openssl (3.0.0)
|
||||
openssl-signature_algorithm (1.2.1)
|
||||
openssl (> 2.0, < 3.1)
|
||||
orm_adapter (0.5.0)
|
||||
ox (2.14.11)
|
||||
parallel (1.22.1)
|
||||
parser (3.1.2.0)
|
||||
parser (3.1.2.1)
|
||||
ast (~> 2.4.1)
|
||||
parslet (2.0.0)
|
||||
pastel (0.8.0)
|
||||
tty-color (~> 0.5)
|
||||
pg (1.4.1)
|
||||
pg (1.4.3)
|
||||
pghero (2.8.3)
|
||||
activerecord (>= 5)
|
||||
pkg-config (1.4.7)
|
||||
pkg-config (1.4.9)
|
||||
posix-spawn (0.3.15)
|
||||
premailer (1.14.2)
|
||||
addressable
|
||||
|
@ -462,16 +470,16 @@ GEM
|
|||
actionmailer (>= 3)
|
||||
premailer (~> 1.7, >= 1.7.9)
|
||||
private_address_check (0.5.0)
|
||||
pry (0.13.1)
|
||||
pry (0.14.1)
|
||||
coderay (~> 1.1)
|
||||
method_source (~> 1.0)
|
||||
pry-byebug (3.9.0)
|
||||
pry-byebug (3.10.1)
|
||||
byebug (~> 11.0)
|
||||
pry (~> 0.13.0)
|
||||
pry (>= 0.13, < 0.15)
|
||||
pry-rails (0.3.9)
|
||||
pry (>= 0.10.4)
|
||||
public_suffix (4.0.7)
|
||||
puma (5.6.4)
|
||||
public_suffix (5.0.0)
|
||||
puma (5.6.5)
|
||||
nio4r (~> 2.0)
|
||||
pundit (2.2.0)
|
||||
activesupport (>= 3.0.0)
|
||||
|
@ -490,22 +498,22 @@ GEM
|
|||
rack (>= 2.1.0)
|
||||
rack-proxy (0.7.0)
|
||||
rack
|
||||
rack-test (1.1.0)
|
||||
rack (>= 1.0, < 3)
|
||||
rails (6.1.6)
|
||||
actioncable (= 6.1.6)
|
||||
actionmailbox (= 6.1.6)
|
||||
actionmailer (= 6.1.6)
|
||||
actionpack (= 6.1.6)
|
||||
actiontext (= 6.1.6)
|
||||
actionview (= 6.1.6)
|
||||
activejob (= 6.1.6)
|
||||
activemodel (= 6.1.6)
|
||||
activerecord (= 6.1.6)
|
||||
activestorage (= 6.1.6)
|
||||
activesupport (= 6.1.6)
|
||||
rack-test (2.0.2)
|
||||
rack (>= 1.3)
|
||||
rails (6.1.7)
|
||||
actioncable (= 6.1.7)
|
||||
actionmailbox (= 6.1.7)
|
||||
actionmailer (= 6.1.7)
|
||||
actionpack (= 6.1.7)
|
||||
actiontext (= 6.1.7)
|
||||
actionview (= 6.1.7)
|
||||
activejob (= 6.1.7)
|
||||
activemodel (= 6.1.7)
|
||||
activerecord (= 6.1.7)
|
||||
activestorage (= 6.1.7)
|
||||
activesupport (= 6.1.7)
|
||||
bundler (>= 1.15.0)
|
||||
railties (= 6.1.6)
|
||||
railties (= 6.1.7)
|
||||
sprockets-rails (>= 2.0.0)
|
||||
rails-controller-testing (1.0.5)
|
||||
actionpack (>= 5.0.1.rc1)
|
||||
|
@ -521,22 +529,22 @@ GEM
|
|||
railties (>= 6.0.0, < 7)
|
||||
rails-settings-cached (0.6.6)
|
||||
rails (>= 4.2.0)
|
||||
railties (6.1.6)
|
||||
actionpack (= 6.1.6)
|
||||
activesupport (= 6.1.6)
|
||||
railties (6.1.7)
|
||||
actionpack (= 6.1.7)
|
||||
activesupport (= 6.1.7)
|
||||
method_source
|
||||
rake (>= 12.2)
|
||||
thor (~> 1.0)
|
||||
rainbow (3.1.1)
|
||||
rake (13.0.6)
|
||||
rdf (3.2.3)
|
||||
rdf (3.2.9)
|
||||
link_header (~> 0.0, >= 0.0.8)
|
||||
rdf-normalize (0.5.0)
|
||||
rdf (~> 3.2)
|
||||
redcarpet (3.5.1)
|
||||
redis (4.5.1)
|
||||
redis-namespace (1.8.2)
|
||||
redis (>= 3.0.4)
|
||||
redis-namespace (1.9.0)
|
||||
redis (>= 4)
|
||||
regexp_parser (2.5.0)
|
||||
request_store (1.5.1)
|
||||
rack (>= 1.4)
|
||||
|
@ -546,7 +554,7 @@ GEM
|
|||
rexml (3.2.5)
|
||||
rotp (6.2.0)
|
||||
rpam2 (4.0.2)
|
||||
rqrcode (2.1.1)
|
||||
rqrcode (2.1.2)
|
||||
chunky_png (~> 1.0)
|
||||
rqrcode_core (~> 1.0)
|
||||
rqrcode_core (1.2.0)
|
||||
|
@ -569,8 +577,8 @@ GEM
|
|||
rspec-sidekiq (3.1.0)
|
||||
rspec-core (~> 3.0, >= 3.0.0)
|
||||
sidekiq (>= 2.4.0)
|
||||
rspec-support (3.11.0)
|
||||
rspec_junit_formatter (0.5.1)
|
||||
rspec-support (3.11.1)
|
||||
rspec_junit_formatter (0.6.0)
|
||||
rspec-core (>= 2, < 4, != 2.12.0)
|
||||
rubocop (1.30.1)
|
||||
parallel (~> 1.10)
|
||||
|
@ -602,12 +610,11 @@ GEM
|
|||
scenic (1.6.0)
|
||||
activerecord (>= 4.0.0)
|
||||
railties (>= 4.0.0)
|
||||
securecompare (1.0.0)
|
||||
semantic_range (3.0.0)
|
||||
sidekiq (6.5.1)
|
||||
connection_pool (>= 2.2.2)
|
||||
sidekiq (6.5.7)
|
||||
connection_pool (>= 2.2.5)
|
||||
rack (~> 2.0)
|
||||
redis (>= 4.2.0)
|
||||
redis (>= 4.5.0, < 5)
|
||||
sidekiq-bulk (0.2.0)
|
||||
sidekiq
|
||||
sidekiq-scheduler (4.0.2)
|
||||
|
@ -615,7 +622,7 @@ GEM
|
|||
rufus-scheduler (~> 3.2)
|
||||
sidekiq (>= 4)
|
||||
tilt (>= 1.4.0)
|
||||
sidekiq-unique-jobs (7.1.25)
|
||||
sidekiq-unique-jobs (7.1.27)
|
||||
brpoplpush-redis_script (> 0.1.1, <= 2.0.0)
|
||||
concurrent-ruby (~> 1.0, >= 1.0.5)
|
||||
sidekiq (>= 5.0, < 8.0)
|
||||
|
@ -642,7 +649,7 @@ GEM
|
|||
sshkit (1.21.2)
|
||||
net-scp (>= 1.1.2)
|
||||
net-ssh (>= 2.8.0)
|
||||
stackprof (0.2.19)
|
||||
stackprof (0.2.21)
|
||||
statsd-ruby (1.5.0)
|
||||
stoplight (3.0.0)
|
||||
strong_migrations (0.7.9)
|
||||
|
@ -658,9 +665,10 @@ GEM
|
|||
climate_control (>= 0.0.3, < 1.0)
|
||||
thor (1.2.1)
|
||||
tilt (2.0.10)
|
||||
tpm-key_attestation (0.9.0)
|
||||
tpm-key_attestation (0.11.0)
|
||||
bindata (~> 2.4)
|
||||
openssl-signature_algorithm (~> 0.4.0)
|
||||
openssl (> 2.0, < 3.1)
|
||||
openssl-signature_algorithm (~> 1.0)
|
||||
tty-color (0.6.0)
|
||||
tty-cursor (0.7.1)
|
||||
tty-prompt (0.23.1)
|
||||
|
@ -674,14 +682,14 @@ GEM
|
|||
twitter-text (3.1.0)
|
||||
idn-ruby
|
||||
unf (~> 0.1.0)
|
||||
tzinfo (2.0.4)
|
||||
tzinfo (2.0.5)
|
||||
concurrent-ruby (~> 1.0)
|
||||
tzinfo-data (1.2022.1)
|
||||
tzinfo-data (1.2022.4)
|
||||
tzinfo (>= 1.0.0)
|
||||
unf (0.1.4)
|
||||
unf_ext
|
||||
unf_ext (0.0.8.2)
|
||||
unicode-display_width (2.1.0)
|
||||
unicode-display_width (2.3.0)
|
||||
uniform_notifier (1.16.0)
|
||||
validate_email (0.1.6)
|
||||
activemodel (>= 3.0)
|
||||
|
@ -691,20 +699,19 @@ GEM
|
|||
public_suffix
|
||||
warden (1.2.9)
|
||||
rack (>= 2.0.9)
|
||||
webauthn (3.0.0.alpha1)
|
||||
webauthn (2.5.2)
|
||||
android_key_attestation (~> 0.3.0)
|
||||
awrence (~> 1.1)
|
||||
bindata (~> 2.4)
|
||||
cbor (~> 0.5.9)
|
||||
cose (~> 1.0)
|
||||
openssl (~> 2.0)
|
||||
cose (~> 1.1)
|
||||
openssl (>= 2.2, < 3.1)
|
||||
safety_net_attestation (~> 0.4.0)
|
||||
securecompare (~> 1.0)
|
||||
tpm-key_attestation (~> 0.9.0)
|
||||
tpm-key_attestation (~> 0.11.0)
|
||||
webfinger (1.2.0)
|
||||
activesupport
|
||||
httpclient (>= 2.4)
|
||||
webmock (3.14.0)
|
||||
webmock (3.18.1)
|
||||
addressable (>= 2.8.0)
|
||||
crack (>= 0.3.2)
|
||||
hashdiff (>= 0.4.0, < 2.0.0)
|
||||
|
@ -713,14 +720,11 @@ GEM
|
|||
rack-proxy (>= 0.6.1)
|
||||
railties (>= 5.2)
|
||||
semantic_range (>= 2.3.0)
|
||||
webpush (0.3.8)
|
||||
hkdf (~> 0.2)
|
||||
jwt (~> 2.0)
|
||||
websocket-driver (0.7.5)
|
||||
websocket-extensions (>= 0.1.0)
|
||||
websocket-extensions (0.1.5)
|
||||
wisper (2.0.1)
|
||||
xorcist (1.1.2)
|
||||
xorcist (1.1.3)
|
||||
xpath (3.2.0)
|
||||
nokogiri (~> 1.8)
|
||||
zeitwerk (2.6.0)
|
||||
|
@ -737,8 +741,8 @@ DEPENDENCIES
|
|||
better_errors (~> 2.9)
|
||||
binding_of_caller (~> 1.0)
|
||||
blurhash (~> 0.1)
|
||||
bootsnap (~> 1.12.0)
|
||||
brakeman (~> 5.2)
|
||||
bootsnap (~> 1.13.0)
|
||||
brakeman (~> 5.3)
|
||||
browser
|
||||
bullet (~> 7.0)
|
||||
bundler-audit (~> 0.9)
|
||||
|
@ -758,11 +762,11 @@ DEPENDENCIES
|
|||
devise-two-factor (~> 4.0)
|
||||
devise_pam_authenticatable2 (~> 9.2)
|
||||
discard (~> 1.2)
|
||||
doorkeeper (~> 5.5)
|
||||
dotenv-rails (~> 2.7)
|
||||
doorkeeper (~> 5.6)
|
||||
dotenv-rails (~> 2.8)
|
||||
ed25519 (~> 1.3)
|
||||
fabrication (~> 2.29)
|
||||
faker (~> 2.21)
|
||||
fabrication (~> 2.30)
|
||||
faker (~> 2.23)
|
||||
fast_blank (~> 1.0)
|
||||
fastimage
|
||||
fog-core (<= 2.1.0)
|
||||
|
@ -775,7 +779,7 @@ DEPENDENCIES
|
|||
htmlentities (~> 4.3)
|
||||
http (~> 5.1)
|
||||
http_accept_language (~> 2.1)
|
||||
httplog (~> 1.5.0)
|
||||
httplog (~> 1.6.0)
|
||||
i18n-tasks (~> 1.0)
|
||||
idn-ruby
|
||||
json-ld
|
||||
|
@ -807,26 +811,26 @@ DEPENDENCIES
|
|||
posix-spawn
|
||||
premailer-rails
|
||||
private_address_check (~> 0.5)
|
||||
pry-byebug (~> 3.9)
|
||||
pry-byebug (~> 3.10)
|
||||
pry-rails (~> 0.3)
|
||||
puma (~> 5.6)
|
||||
pundit (~> 2.2)
|
||||
rack (~> 2.2.4)
|
||||
rack-attack (~> 6.6)
|
||||
rack-cors (~> 1.1)
|
||||
rails (~> 6.1.6)
|
||||
rails (~> 6.1.7)
|
||||
rails-controller-testing (~> 1.0)
|
||||
rails-i18n (~> 6.0)
|
||||
rails-settings-cached (~> 0.6)
|
||||
rdf-normalize (~> 0.5)
|
||||
redcarpet (~> 3.5)
|
||||
redis (~> 4.5)
|
||||
redis-namespace (~> 1.8)
|
||||
redis-namespace (~> 1.9)
|
||||
rexml (~> 3.2)
|
||||
rqrcode (~> 2.1)
|
||||
rspec-rails (~> 5.1)
|
||||
rspec-sidekiq (~> 3.1)
|
||||
rspec_junit_formatter (~> 0.5)
|
||||
rspec_junit_formatter (~> 0.6)
|
||||
rubocop (~> 1.30)
|
||||
rubocop-rails (~> 2.15)
|
||||
ruby-progressbar (~> 1.11)
|
||||
|
@ -848,8 +852,8 @@ DEPENDENCIES
|
|||
tty-prompt (~> 0.23)
|
||||
twitter-text (~> 3.1.0)
|
||||
tzinfo-data (~> 1.2022)
|
||||
webauthn (~> 3.0.0.alpha1)
|
||||
webmock (~> 3.14)
|
||||
webauthn (~> 2.5)
|
||||
webmock (~> 3.18)
|
||||
webpacker (~> 5.4)
|
||||
webpush (~> 0.3)
|
||||
webpush!
|
||||
xorcist (~> 1.1)
|
||||
|
|
|
@ -10,17 +10,17 @@ class AboutController < ApplicationController
|
|||
before_action :require_open_federation!, only: [:show, :more]
|
||||
before_action :set_body_classes, only: :show
|
||||
before_action :set_instance_presenter
|
||||
before_action :set_expires_in, only: [:more, :terms]
|
||||
before_action :set_expires_in, only: [:more]
|
||||
before_action :set_registration_form_time, only: :show
|
||||
|
||||
skip_before_action :require_functional!, only: [:more, :terms]
|
||||
skip_before_action :require_functional!, only: [:more]
|
||||
|
||||
def show; end
|
||||
|
||||
def more
|
||||
flash.now[:notice] = I18n.t('about.instance_actor_flash') if params[:instance_actor]
|
||||
|
||||
toc_generator = TOCGenerator.new(@instance_presenter.site_extended_description)
|
||||
toc_generator = TOCGenerator.new(@instance_presenter.extended_description)
|
||||
|
||||
@rules = Rule.ordered
|
||||
@contents = toc_generator.html
|
||||
|
@ -28,8 +28,6 @@ class AboutController < ApplicationController
|
|||
@blocks = DomainBlock.with_user_facing_limitations.by_severity if display_blocks?
|
||||
end
|
||||
|
||||
def terms; end
|
||||
|
||||
helper_method :display_blocks?
|
||||
helper_method :display_blocks_rationale?
|
||||
helper_method :public_fetch_mode?
|
||||
|
|
|
@ -7,7 +7,7 @@ class AccountsController < ApplicationController
|
|||
include AccountControllerConcern
|
||||
include SignatureAuthentication
|
||||
|
||||
before_action :require_signature!, if: -> { request.format == :json && authorized_fetch_mode? }
|
||||
before_action :require_account_signature!, if: -> { request.format == :json && authorized_fetch_mode? }
|
||||
before_action :set_cache_headers
|
||||
before_action :set_body_classes
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ class ActivityPub::ClaimsController < ActivityPub::BaseController
|
|||
|
||||
skip_before_action :authenticate_user!
|
||||
|
||||
before_action :require_signature!
|
||||
before_action :require_account_signature!
|
||||
before_action :set_claim_result
|
||||
|
||||
def create
|
||||
|
|
|
@ -4,7 +4,7 @@ class ActivityPub::CollectionsController < ActivityPub::BaseController
|
|||
include SignatureVerification
|
||||
include AccountOwnedConcern
|
||||
|
||||
before_action :require_signature!, if: :authorized_fetch_mode?
|
||||
before_action :require_account_signature!, if: :authorized_fetch_mode?
|
||||
before_action :set_items
|
||||
before_action :set_size
|
||||
before_action :set_type
|
||||
|
|
|
@ -4,7 +4,7 @@ class ActivityPub::FollowersSynchronizationsController < ActivityPub::BaseContro
|
|||
include SignatureVerification
|
||||
include AccountOwnedConcern
|
||||
|
||||
before_action :require_signature!
|
||||
before_action :require_account_signature!
|
||||
before_action :set_items
|
||||
before_action :set_cache_headers
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ class ActivityPub::InboxesController < ActivityPub::BaseController
|
|||
include AccountOwnedConcern
|
||||
|
||||
before_action :skip_unknown_actor_activity
|
||||
before_action :require_signature!
|
||||
before_action :require_actor_signature!
|
||||
skip_before_action :authenticate_user!
|
||||
|
||||
def create
|
||||
|
@ -49,17 +49,17 @@ class ActivityPub::InboxesController < ActivityPub::BaseController
|
|||
end
|
||||
|
||||
def upgrade_account
|
||||
if signed_request_account.ostatus?
|
||||
if signed_request_account&.ostatus?
|
||||
signed_request_account.update(last_webfingered_at: nil)
|
||||
ResolveAccountWorker.perform_async(signed_request_account.acct)
|
||||
end
|
||||
|
||||
DeliveryFailureTracker.reset!(signed_request_account.inbox_url)
|
||||
DeliveryFailureTracker.reset!(signed_request_actor.inbox_url)
|
||||
end
|
||||
|
||||
def process_collection_synchronization
|
||||
raw_params = request.headers['Collection-Synchronization']
|
||||
return if raw_params.blank? || ENV['DISABLE_FOLLOWERS_SYNCHRONIZATION'] == 'true'
|
||||
return if raw_params.blank? || ENV['DISABLE_FOLLOWERS_SYNCHRONIZATION'] == 'true' || signed_request_account.nil?
|
||||
|
||||
# Re-using the syntax for signature parameters
|
||||
tree = SignatureParamsParser.new.parse(raw_params)
|
||||
|
@ -71,6 +71,6 @@ class ActivityPub::InboxesController < ActivityPub::BaseController
|
|||
end
|
||||
|
||||
def process_payload
|
||||
ActivityPub::ProcessingWorker.perform_async(signed_request_account.id, body, @account&.id)
|
||||
ActivityPub::ProcessingWorker.perform_async(signed_request_actor.id, body, @account&.id, signed_request_actor.class.name)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,7 +6,7 @@ class ActivityPub::OutboxesController < ActivityPub::BaseController
|
|||
include SignatureVerification
|
||||
include AccountOwnedConcern
|
||||
|
||||
before_action :require_signature!, if: :authorized_fetch_mode?
|
||||
before_action :require_account_signature!, if: :authorized_fetch_mode?
|
||||
before_action :set_statuses
|
||||
before_action :set_cache_headers
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ class ActivityPub::RepliesController < ActivityPub::BaseController
|
|||
|
||||
DESCENDANTS_LIMIT = 60
|
||||
|
||||
before_action :require_signature!, if: :authorized_fetch_mode?
|
||||
before_action :require_account_signature!, if: :authorized_fetch_mode?
|
||||
before_action :set_status
|
||||
before_action :set_cache_headers
|
||||
before_action :set_replies
|
||||
|
|
|
@ -16,7 +16,11 @@ module Admin
|
|||
def batch
|
||||
authorize :account, :index?
|
||||
|
||||
@form = Form::AccountBatch.new(form_account_batch_params.merge(current_account: current_account, action: action_from_button))
|
||||
@form = Form::AccountBatch.new(form_account_batch_params)
|
||||
@form.current_account = current_account
|
||||
@form.action = action_from_button
|
||||
@form.select_all_matching = params[:select_all_matching]
|
||||
@form.query = filtered_accounts
|
||||
@form.save
|
||||
rescue ActionController::ParameterMissing
|
||||
flash[:alert] = I18n.t('admin.accounts.no_account_selected')
|
||||
|
|
|
@ -23,6 +23,7 @@ module Admin
|
|||
@role.current_account = current_account
|
||||
|
||||
if @role.save
|
||||
log_action :create, @role
|
||||
redirect_to admin_roles_path
|
||||
else
|
||||
render :new
|
||||
|
@ -39,6 +40,7 @@ module Admin
|
|||
@role.current_account = current_account
|
||||
|
||||
if @role.update(resource_params)
|
||||
log_action :update, @role
|
||||
redirect_to admin_roles_path
|
||||
else
|
||||
render :edit
|
||||
|
@ -48,6 +50,7 @@ module Admin
|
|||
def destroy
|
||||
authorize @role, :destroy?
|
||||
@role.destroy!
|
||||
log_action :destroy, @role
|
||||
redirect_to admin_roles_path
|
||||
end
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ module Admin
|
|||
@user.current_account = current_account
|
||||
|
||||
if @user.update(resource_params)
|
||||
log_action :change_role, @user
|
||||
redirect_to admin_account_path(@user.account_id), notice: I18n.t('admin.accounts.change_role.changed_msg')
|
||||
else
|
||||
render :show
|
||||
|
|
|
@ -131,4 +131,10 @@ class Api::BaseController < ApplicationController
|
|||
def disallow_unauthenticated_api_access?
|
||||
authorized_fetch_mode?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def respond_with_error(code)
|
||||
render json: { error: Rack::Utils::HTTP_STATUS_CODES[code] }, status: code
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Accounts::FollowerAccountsController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :read, :'read:accounts' }
|
||||
before_action -> { authorize_if_got_token! :read, :'read:accounts' }
|
||||
before_action :set_account
|
||||
after_action :insert_pagination_headers
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Accounts::FollowingAccountsController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :read, :'read:accounts' }
|
||||
before_action -> { authorize_if_got_token! :read, :'read:accounts' }
|
||||
before_action :set_account
|
||||
after_action :insert_pagination_headers
|
||||
|
||||
|
|
|
@ -30,12 +30,12 @@ class Api::V1::AccountsController < Api::BaseController
|
|||
self.response_body = Oj.dump(response.body)
|
||||
self.status = response.status
|
||||
rescue ActiveRecord::RecordInvalid => e
|
||||
render json: ValidationErrorFormatter.new(e, :'account.username' => :username, :'invite_request.text' => :reason).as_json, status: :unprocessable_entity
|
||||
render json: ValidationErrorFormatter.new(e, 'account.username': :username, 'invite_request.text': :reason).as_json, status: :unprocessable_entity
|
||||
end
|
||||
|
||||
def follow
|
||||
follow = FollowService.new.call(current_user.account, @account, reblogs: params.key?(:reblogs) ? truthy_param?(:reblogs) : nil, notify: params.key?(:notify) ? truthy_param?(:notify) : nil, with_rate_limit: true)
|
||||
options = @account.locked? || current_user.account.silenced? ? {} : { following_map: { @account.id => { reblogs: follow.show_reblogs?, notify: follow.notify? } }, requested_map: { @account.id => false } }
|
||||
follow = FollowService.new.call(current_user.account, @account, reblogs: params.key?(:reblogs) ? truthy_param?(:reblogs) : nil, notify: params.key?(:notify) ? truthy_param?(:notify) : nil, languages: params.key?(:languages) ? params[:languages] : nil, with_rate_limit: true)
|
||||
options = @account.locked? || current_user.account.silenced? ? {} : { following_map: { @account.id => { reblogs: follow.show_reblogs?, notify: follow.notify?, languages: follow.languages } }, requested_map: { @account.id => false } }
|
||||
|
||||
render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships(**options)
|
||||
end
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Admin::CanonicalEmailBlocksController < Api::BaseController
|
||||
include Authorization
|
||||
include AccountableConcern
|
||||
|
||||
LIMIT = 100
|
||||
|
||||
before_action -> { authorize_if_got_token! :'admin:read', :'admin:read:canonical_email_blocks' }, only: [:index, :show, :test]
|
||||
before_action -> { authorize_if_got_token! :'admin:write', :'admin:write:canonical_email_blocks' }, except: [:index, :show, :test]
|
||||
|
||||
before_action :set_canonical_email_blocks, only: :index
|
||||
before_action :set_canonical_email_blocks_from_test, only: [:test]
|
||||
before_action :set_canonical_email_block, only: [:show, :destroy]
|
||||
|
||||
after_action :verify_authorized
|
||||
after_action :insert_pagination_headers, only: :index
|
||||
|
||||
PAGINATION_PARAMS = %i(limit).freeze
|
||||
|
||||
def index
|
||||
authorize :canonical_email_block, :index?
|
||||
render json: @canonical_email_blocks, each_serializer: REST::Admin::CanonicalEmailBlockSerializer
|
||||
end
|
||||
|
||||
def show
|
||||
authorize @canonical_email_block, :show?
|
||||
render json: @canonical_email_block, serializer: REST::Admin::CanonicalEmailBlockSerializer
|
||||
end
|
||||
|
||||
def test
|
||||
authorize :canonical_email_block, :test?
|
||||
render json: @canonical_email_blocks, each_serializer: REST::Admin::CanonicalEmailBlockSerializer
|
||||
end
|
||||
|
||||
def create
|
||||
authorize :canonical_email_block, :create?
|
||||
|
||||
@canonical_email_block = CanonicalEmailBlock.create!(resource_params)
|
||||
log_action :create, @canonical_email_block
|
||||
|
||||
render json: @canonical_email_block, serializer: REST::Admin::CanonicalEmailBlockSerializer
|
||||
end
|
||||
|
||||
def destroy
|
||||
authorize @canonical_email_block, :destroy?
|
||||
|
||||
@canonical_email_block.destroy!
|
||||
log_action :destroy, @canonical_email_block
|
||||
|
||||
render json: @canonical_email_block, serializer: REST::Admin::CanonicalEmailBlockSerializer
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def resource_params
|
||||
params.permit(:canonical_email_hash, :email)
|
||||
end
|
||||
|
||||
def set_canonical_email_blocks
|
||||
@canonical_email_blocks = CanonicalEmailBlock.order(id: :desc).to_a_paginated_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id))
|
||||
end
|
||||
|
||||
def set_canonical_email_blocks_from_test
|
||||
@canonical_email_blocks = CanonicalEmailBlock.matching_email(params[:email])
|
||||
end
|
||||
|
||||
def set_canonical_email_block
|
||||
@canonical_email_block = CanonicalEmailBlock.find(params[:id])
|
||||
end
|
||||
|
||||
def insert_pagination_headers
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
end
|
||||
|
||||
def next_path
|
||||
api_v1_admin_canonical_email_blocks_url(pagination_params(max_id: pagination_max_id)) if records_continue?
|
||||
end
|
||||
|
||||
def prev_path
|
||||
api_v1_admin_canonical_email_blocks_url(pagination_params(min_id: pagination_since_id)) unless @canonical_email_blocks.empty?
|
||||
end
|
||||
|
||||
def pagination_max_id
|
||||
@canonical_email_blocks.last.id
|
||||
end
|
||||
|
||||
def pagination_since_id
|
||||
@canonical_email_blocks.first.id
|
||||
end
|
||||
|
||||
def records_continue?
|
||||
@canonical_email_blocks.size == limit_param(LIMIT)
|
||||
end
|
||||
|
||||
def pagination_params(core_params)
|
||||
params.slice(*PAGINATION_PARAMS).permit(*PAGINATION_PARAMS).merge(core_params)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,90 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Admin::EmailDomainBlocksController < Api::BaseController
|
||||
include Authorization
|
||||
include AccountableConcern
|
||||
|
||||
LIMIT = 100
|
||||
|
||||
before_action -> { authorize_if_got_token! :'admin:read', :'admin:read:email_domain_blocks' }, only: [:index, :show]
|
||||
before_action -> { authorize_if_got_token! :'admin:write', :'admin:write:email_domain_blocks' }, except: [:index, :show]
|
||||
before_action :set_email_domain_blocks, only: :index
|
||||
before_action :set_email_domain_block, only: [:show, :destroy]
|
||||
|
||||
after_action :verify_authorized
|
||||
after_action :insert_pagination_headers, only: :index
|
||||
|
||||
PAGINATION_PARAMS = %i(
|
||||
limit
|
||||
).freeze
|
||||
|
||||
def create
|
||||
authorize :email_domain_block, :create?
|
||||
|
||||
@email_domain_block = EmailDomainBlock.create!(resource_params)
|
||||
log_action :create, @email_domain_block
|
||||
|
||||
render json: @email_domain_block, serializer: REST::Admin::EmailDomainBlockSerializer
|
||||
end
|
||||
|
||||
def index
|
||||
authorize :email_domain_block, :index?
|
||||
render json: @email_domain_blocks, each_serializer: REST::Admin::EmailDomainBlockSerializer
|
||||
end
|
||||
|
||||
def show
|
||||
authorize @email_domain_block, :show?
|
||||
render json: @email_domain_block, serializer: REST::Admin::EmailDomainBlockSerializer
|
||||
end
|
||||
|
||||
def destroy
|
||||
authorize @email_domain_block, :destroy?
|
||||
|
||||
@email_domain_block.destroy!
|
||||
log_action :destroy, @email_domain_block
|
||||
|
||||
render json: @email_domain_block, serializer: REST::Admin::EmailDomainBlockSerializer
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_email_domain_blocks
|
||||
@email_domain_blocks = EmailDomainBlock.order(id: :desc).to_a_paginated_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id))
|
||||
end
|
||||
|
||||
def set_email_domain_block
|
||||
@email_domain_block = EmailDomainBlock.find(params[:id])
|
||||
end
|
||||
|
||||
def resource_params
|
||||
params.permit(:domain)
|
||||
end
|
||||
|
||||
def insert_pagination_headers
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
end
|
||||
|
||||
def next_path
|
||||
api_v1_admin_email_domain_blocks_url(pagination_params(max_id: pagination_max_id)) if records_continue?
|
||||
end
|
||||
|
||||
def prev_path
|
||||
api_v1_admin_email_domain_blocks_url(pagination_params(min_id: pagination_since_id)) unless @email_domain_blocks.empty?
|
||||
end
|
||||
|
||||
def pagination_max_id
|
||||
@email_domain_blocks.last.id
|
||||
end
|
||||
|
||||
def pagination_since_id
|
||||
@email_domain_blocks.first.id
|
||||
end
|
||||
|
||||
def records_continue?
|
||||
@email_domain_blocks.size == limit_param(LIMIT)
|
||||
end
|
||||
|
||||
def pagination_params(core_params)
|
||||
params.slice(*PAGINATION_PARAMS).permit(*PAGINATION_PARAMS).merge(core_params)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,99 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Admin::IpBlocksController < Api::BaseController
|
||||
include Authorization
|
||||
include AccountableConcern
|
||||
|
||||
LIMIT = 100
|
||||
|
||||
before_action -> { authorize_if_got_token! :'admin:read', :'admin:read:ip_blocks' }, only: [:index, :show]
|
||||
before_action -> { authorize_if_got_token! :'admin:write', :'admin:write:ip_blocks' }, except: [:index, :show]
|
||||
before_action :set_ip_blocks, only: :index
|
||||
before_action :set_ip_block, only: [:show, :update, :destroy]
|
||||
|
||||
after_action :verify_authorized
|
||||
after_action :insert_pagination_headers, only: :index
|
||||
|
||||
PAGINATION_PARAMS = %i(
|
||||
limit
|
||||
).freeze
|
||||
|
||||
def create
|
||||
authorize :ip_block, :create?
|
||||
|
||||
@ip_block = IpBlock.create!(resource_params)
|
||||
log_action :create, @ip_block
|
||||
|
||||
render json: @ip_block, serializer: REST::Admin::IpBlockSerializer
|
||||
end
|
||||
|
||||
def index
|
||||
authorize :ip_block, :index?
|
||||
render json: @ip_blocks, each_serializer: REST::Admin::IpBlockSerializer
|
||||
end
|
||||
|
||||
def show
|
||||
authorize @ip_block, :show?
|
||||
render json: @ip_block, serializer: REST::Admin::IpBlockSerializer
|
||||
end
|
||||
|
||||
def update
|
||||
authorize @ip_block, :update?
|
||||
|
||||
@ip_block.update(resource_params)
|
||||
log_action :update, @ip_block
|
||||
|
||||
render json: @ip_block, serializer: REST::Admin::IpBlockSerializer
|
||||
end
|
||||
|
||||
def destroy
|
||||
authorize @ip_block, :destroy?
|
||||
|
||||
@ip_block.destroy!
|
||||
log_action :destroy, @ip_block
|
||||
|
||||
render json: @ip_block, serializer: REST::Admin::IpBlockSerializer
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_ip_blocks
|
||||
@ip_blocks = IpBlock.order(id: :desc).to_a_paginated_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id))
|
||||
end
|
||||
|
||||
def set_ip_block
|
||||
@ip_block = IpBlock.find(params[:id])
|
||||
end
|
||||
|
||||
def resource_params
|
||||
params.permit(:ip, :severity, :comment, :expires_in)
|
||||
end
|
||||
|
||||
def insert_pagination_headers
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
end
|
||||
|
||||
def next_path
|
||||
api_v1_admin_ip_blocks_url(pagination_params(max_id: pagination_max_id)) if records_continue?
|
||||
end
|
||||
|
||||
def prev_path
|
||||
api_v1_admin_ip_blocks_url(pagination_params(min_id: pagination_since_id)) unless @ip_blocks.empty?
|
||||
end
|
||||
|
||||
def pagination_max_id
|
||||
@ip_blocks.last.id
|
||||
end
|
||||
|
||||
def pagination_since_id
|
||||
@ip_blocks.first.id
|
||||
end
|
||||
|
||||
def records_continue?
|
||||
@ip_blocks.size == limit_param(LIMIT)
|
||||
end
|
||||
|
||||
def pagination_params(core_params)
|
||||
params.slice(*PAGINATION_PARAMS).permit(*PAGINATION_PARAMS).merge(core_params)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,44 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Filters::StatusesController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :read, :'read:filters' }, only: [:index, :show]
|
||||
before_action -> { doorkeeper_authorize! :write, :'write:filters' }, except: [:index, :show]
|
||||
before_action :require_user!
|
||||
|
||||
before_action :set_status_filters, only: :index
|
||||
before_action :set_status_filter, only: [:show, :destroy]
|
||||
|
||||
def index
|
||||
render json: @status_filters, each_serializer: REST::FilterStatusSerializer
|
||||
end
|
||||
|
||||
def create
|
||||
@status_filter = current_account.custom_filters.find(params[:filter_id]).statuses.create!(resource_params)
|
||||
|
||||
render json: @status_filter, serializer: REST::FilterStatusSerializer
|
||||
end
|
||||
|
||||
def show
|
||||
render json: @status_filter, serializer: REST::FilterStatusSerializer
|
||||
end
|
||||
|
||||
def destroy
|
||||
@status_filter.destroy!
|
||||
render_empty
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_status_filters
|
||||
filter = current_account.custom_filters.includes(:statuses).find(params[:filter_id])
|
||||
@status_filters = filter.statuses
|
||||
end
|
||||
|
||||
def set_status_filter
|
||||
@status_filter = CustomFilterStatus.includes(:custom_filter).where(custom_filter: { account: current_account }).find(params[:id])
|
||||
end
|
||||
|
||||
def resource_params
|
||||
params.permit(:status_id)
|
||||
end
|
||||
end
|
|
@ -6,6 +6,6 @@ class Api::V1::InstancesController < Api::BaseController
|
|||
|
||||
def show
|
||||
expires_in 3.minutes, public: true
|
||||
render_with_cache json: {}, serializer: REST::InstanceSerializer, root: 'instance'
|
||||
render_with_cache json: InstancePresenter.new, serializer: REST::V1::InstanceSerializer, root: 'instance'
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Statuses::TranslationsController < Api::BaseController
|
||||
include Authorization
|
||||
|
||||
before_action -> { doorkeeper_authorize! :read, :'read:statuses' }
|
||||
before_action :set_status
|
||||
before_action :set_translation
|
||||
|
||||
rescue_from TranslationService::NotConfiguredError, with: :not_found
|
||||
rescue_from TranslationService::UnexpectedResponseError, TranslationService::QuotaExceededError, TranslationService::TooManyRequestsError, with: :service_unavailable
|
||||
|
||||
def create
|
||||
render json: @translation, serializer: REST::TranslationSerializer
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_status
|
||||
@status = Status.find(params[:status_id])
|
||||
authorize @status, :show?
|
||||
rescue Mastodon::NotPermittedError
|
||||
not_found
|
||||
end
|
||||
|
||||
def set_translation
|
||||
@translation = TranslateStatusService.new.call(@status, content_locale)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,8 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Api::V2::InstancesController < Api::V1::InstancesController
|
||||
def show
|
||||
expires_in 3.minutes, public: true
|
||||
render_with_cache json: InstancePresenter.new, serializer: REST::InstanceSerializer, root: 'instance'
|
||||
end
|
||||
end
|
|
@ -83,7 +83,7 @@ class Auth::RegistrationsController < Devise::RegistrationsController
|
|||
end
|
||||
|
||||
def check_enabled_registrations
|
||||
redirect_to root_path if single_user_mode? || omniauth_only? || !allowed_registrations?
|
||||
redirect_to root_path if single_user_mode? || omniauth_only? || !allowed_registrations? || ip_blocked?
|
||||
end
|
||||
|
||||
def allowed_registrations?
|
||||
|
@ -94,6 +94,10 @@ class Auth::RegistrationsController < Devise::RegistrationsController
|
|||
ENV['OMNIAUTH_ONLY'] == 'true'
|
||||
end
|
||||
|
||||
def ip_blocked?
|
||||
IpBlock.where(severity: :sign_up_block).where('ip >>= ?', request.remote_ip.to_s).exists?
|
||||
end
|
||||
|
||||
def invite_code
|
||||
if params[:user]
|
||||
params[:user][:invite_code]
|
||||
|
|
|
@ -3,7 +3,11 @@
|
|||
module AccountableConcern
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
def log_action(action, target, options = {})
|
||||
Admin::ActionLog.create(account: current_account, action: action, target: target, recorded_changes: options.stringify_keys)
|
||||
def log_action(action, target)
|
||||
Admin::ActionLog.create(
|
||||
account: current_account,
|
||||
action: action,
|
||||
target: target
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -45,10 +45,14 @@ module SignatureVerification
|
|||
end
|
||||
end
|
||||
|
||||
def require_signature!
|
||||
def require_account_signature!
|
||||
render plain: signature_verification_failure_reason, status: signature_verification_failure_code unless signed_request_account
|
||||
end
|
||||
|
||||
def require_actor_signature!
|
||||
render plain: signature_verification_failure_reason, status: signature_verification_failure_code unless signed_request_actor
|
||||
end
|
||||
|
||||
def signed_request?
|
||||
request.headers['Signature'].present?
|
||||
end
|
||||
|
@ -68,7 +72,11 @@ module SignatureVerification
|
|||
end
|
||||
|
||||
def signed_request_account
|
||||
return @signed_request_account if defined?(@signed_request_account)
|
||||
signed_request_actor.is_a?(Account) ? signed_request_actor : nil
|
||||
end
|
||||
|
||||
def signed_request_actor
|
||||
return @signed_request_actor if defined?(@signed_request_actor)
|
||||
|
||||
raise SignatureVerificationError, 'Request not signed' unless signed_request?
|
||||
raise SignatureVerificationError, 'Incompatible request signature. keyId and signature are required' if missing_required_signature_parameters?
|
||||
|
@ -78,26 +86,30 @@ module SignatureVerification
|
|||
verify_signature_strength!
|
||||
verify_body_digest!
|
||||
|
||||
account = account_from_key_id(signature_params['keyId'])
|
||||
actor = actor_from_key_id(signature_params['keyId'])
|
||||
|
||||
raise SignatureVerificationError, "Public key not found for key #{signature_params['keyId']}" if account.nil?
|
||||
raise SignatureVerificationError, "Public key not found for key #{signature_params['keyId']}" if actor.nil?
|
||||
|
||||
signature = Base64.decode64(signature_params['signature'])
|
||||
compare_signed_string = build_signed_string
|
||||
|
||||
return account unless verify_signature(account, signature, compare_signed_string).nil?
|
||||
return actor unless verify_signature(actor, signature, compare_signed_string).nil?
|
||||
|
||||
account = stoplight_wrap_request { account.possibly_stale? ? account.refresh! : account_refresh_key(account) }
|
||||
actor = stoplight_wrap_request { actor_refresh_key!(actor) }
|
||||
|
||||
raise SignatureVerificationError, "Public key not found for key #{signature_params['keyId']}" if account.nil?
|
||||
raise SignatureVerificationError, "Public key not found for key #{signature_params['keyId']}" if actor.nil?
|
||||
|
||||
return account unless verify_signature(account, signature, compare_signed_string).nil?
|
||||
return actor unless verify_signature(actor, signature, compare_signed_string).nil?
|
||||
|
||||
@signature_verification_failure_reason = "Verification failed for #{account.username}@#{account.domain} #{account.uri} using rsa-sha256 (RSASSA-PKCS1-v1_5 with SHA-256)"
|
||||
@signed_request_account = nil
|
||||
fail_with! "Verification failed for #{actor.to_log_human_identifier} #{actor.uri} using rsa-sha256 (RSASSA-PKCS1-v1_5 with SHA-256)"
|
||||
rescue SignatureVerificationError => e
|
||||
@signature_verification_failure_reason = e.message
|
||||
@signed_request_account = nil
|
||||
fail_with! e.message
|
||||
rescue HTTP::Error, OpenSSL::SSL::SSLError => e
|
||||
fail_with! "Failed to fetch remote data: #{e.message}"
|
||||
rescue Mastodon::UnexpectedResponseError
|
||||
fail_with! 'Failed to fetch remote data (got unexpected reply from server)'
|
||||
rescue Stoplight::Error::RedLight
|
||||
fail_with! 'Fetching attempt skipped because of recent connection failure'
|
||||
end
|
||||
|
||||
def request_body
|
||||
|
@ -106,6 +118,11 @@ module SignatureVerification
|
|||
|
||||
private
|
||||
|
||||
def fail_with!(message)
|
||||
@signature_verification_failure_reason = message
|
||||
@signed_request_actor = nil
|
||||
end
|
||||
|
||||
def signature_params
|
||||
@signature_params ||= begin
|
||||
raw_signature = request.headers['Signature']
|
||||
|
@ -138,13 +155,23 @@ module SignatureVerification
|
|||
digests = request.headers['Digest'].split(',').map { |digest| digest.split('=', 2) }.map { |key, value| [key.downcase, value] }
|
||||
sha256 = digests.assoc('sha-256')
|
||||
raise SignatureVerificationError, "Mastodon only supports SHA-256 in Digest header. Offered algorithms: #{digests.map(&:first).join(', ')}" if sha256.nil?
|
||||
raise SignatureVerificationError, "Invalid Digest value. Computed SHA-256 digest: #{body_digest}; given: #{sha256[1]}" if body_digest != sha256[1]
|
||||
|
||||
return if body_digest == sha256[1]
|
||||
|
||||
digest_size = begin
|
||||
Base64.strict_decode64(sha256[1].strip).length
|
||||
rescue ArgumentError
|
||||
raise SignatureVerificationError, "Invalid Digest value. The provided Digest value is not a valid base64 string. Given digest: #{sha256[1]}"
|
||||
end
|
||||
|
||||
def verify_signature(account, signature, compare_signed_string)
|
||||
if account.keypair.public_key.verify(OpenSSL::Digest.new('SHA256'), signature, compare_signed_string)
|
||||
@signed_request_account = account
|
||||
@signed_request_account
|
||||
raise SignatureVerificationError, "Invalid Digest value. The provided Digest value is not a SHA-256 digest. Given digest: #{sha256[1]}" if digest_size != 32
|
||||
raise SignatureVerificationError, "Invalid Digest value. Computed SHA-256 digest: #{body_digest}; given: #{sha256[1]}"
|
||||
end
|
||||
|
||||
def verify_signature(actor, signature, compare_signed_string)
|
||||
if actor.keypair.public_key.verify(OpenSSL::Digest.new('SHA256'), signature, compare_signed_string)
|
||||
@signed_request_actor = actor
|
||||
@signed_request_actor
|
||||
end
|
||||
rescue OpenSSL::PKey::RSAError
|
||||
nil
|
||||
|
@ -207,7 +234,7 @@ module SignatureVerification
|
|||
signature_params['keyId'].blank? || signature_params['signature'].blank?
|
||||
end
|
||||
|
||||
def account_from_key_id(key_id)
|
||||
def actor_from_key_id(key_id)
|
||||
domain = key_id.start_with?('acct:') ? key_id.split('@').last : key_id
|
||||
|
||||
if domain_not_allowed?(domain)
|
||||
|
@ -216,27 +243,34 @@ module SignatureVerification
|
|||
end
|
||||
|
||||
if key_id.start_with?('acct:')
|
||||
stoplight_wrap_request { ResolveAccountService.new.call(key_id.gsub(/\Aacct:/, '')) }
|
||||
stoplight_wrap_request { ResolveAccountService.new.call(key_id.gsub(/\Aacct:/, ''), suppress_errors: false) }
|
||||
elsif !ActivityPub::TagManager.instance.local_uri?(key_id)
|
||||
account = ActivityPub::TagManager.instance.uri_to_resource(key_id, Account)
|
||||
account ||= stoplight_wrap_request { ActivityPub::FetchRemoteKeyService.new.call(key_id, id: false) }
|
||||
account = ActivityPub::TagManager.instance.uri_to_actor(key_id)
|
||||
account ||= stoplight_wrap_request { ActivityPub::FetchRemoteKeyService.new.call(key_id, id: false, suppress_errors: false) }
|
||||
account
|
||||
end
|
||||
rescue Mastodon::HostValidationError
|
||||
nil
|
||||
rescue Mastodon::PrivateNetworkAddressError => e
|
||||
raise SignatureVerificationError, "Requests to private network addresses are disallowed (tried to query #{e.host})"
|
||||
rescue Mastodon::HostValidationError, ActivityPub::FetchRemoteActorService::Error, ActivityPub::FetchRemoteKeyService::Error, Webfinger::Error => e
|
||||
raise SignatureVerificationError, e.message
|
||||
end
|
||||
|
||||
def stoplight_wrap_request(&block)
|
||||
Stoplight("source:#{request.remote_ip}", &block)
|
||||
.with_fallback { nil }
|
||||
.with_threshold(1)
|
||||
.with_cool_off_time(5.minutes.seconds)
|
||||
.with_error_handler { |error, handle| error.is_a?(HTTP::Error) || error.is_a?(OpenSSL::SSL::SSLError) ? handle.call(error) : raise(error) }
|
||||
.run
|
||||
end
|
||||
|
||||
def account_refresh_key(account)
|
||||
return if account.local? || !account.activitypub?
|
||||
ActivityPub::FetchRemoteAccountService.new.call(account.uri, only_key: true)
|
||||
def actor_refresh_key!(actor)
|
||||
return if actor.local? || !actor.activitypub?
|
||||
return actor.refresh! if actor.respond_to?(:refresh!) && actor.possibly_stale?
|
||||
|
||||
ActivityPub::FetchRemoteActorService.new.call(actor.uri, only_key: true, suppress_errors: false)
|
||||
rescue Mastodon::PrivateNetworkAddressError => e
|
||||
raise SignatureVerificationError, "Requests to private network addresses are disallowed (tried to query #{e.host})"
|
||||
rescue Mastodon::HostValidationError, ActivityPub::FetchRemoteActorService::Error, Webfinger::Error => e
|
||||
raise SignatureVerificationError, e.message
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Filters::StatusesController < ApplicationController
|
||||
layout 'admin'
|
||||
|
||||
before_action :authenticate_user!
|
||||
before_action :set_filter
|
||||
before_action :set_status_filters
|
||||
before_action :set_pack
|
||||
before_action :set_body_classes
|
||||
|
||||
PER_PAGE = 20
|
||||
|
||||
def index
|
||||
@status_filter_batch_action = Form::StatusFilterBatchAction.new
|
||||
end
|
||||
|
||||
def batch
|
||||
@status_filter_batch_action = Form::StatusFilterBatchAction.new(status_filter_batch_action_params.merge(current_account: current_account, filter_id: params[:filter_id], type: action_from_button))
|
||||
@status_filter_batch_action.save!
|
||||
rescue ActionController::ParameterMissing
|
||||
flash[:alert] = I18n.t('admin.statuses.no_status_selected')
|
||||
ensure
|
||||
redirect_to edit_filter_path(@filter)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_pack
|
||||
use_pack 'admin'
|
||||
end
|
||||
|
||||
def set_filter
|
||||
@filter = current_account.custom_filters.find(params[:filter_id])
|
||||
end
|
||||
|
||||
def set_status_filters
|
||||
@status_filters = @filter.statuses.preload(:status).page(params[:page]).per(PER_PAGE)
|
||||
end
|
||||
|
||||
def status_filter_batch_action_params
|
||||
params.require(:form_status_filter_batch_action).permit(status_filter_ids: [])
|
||||
end
|
||||
|
||||
def action_from_button
|
||||
if params[:remove]
|
||||
'remove'
|
||||
end
|
||||
end
|
||||
|
||||
def set_body_classes
|
||||
@body_classes = 'admin'
|
||||
end
|
||||
end
|
|
@ -9,7 +9,7 @@ class FiltersController < ApplicationController
|
|||
before_action :set_body_classes
|
||||
|
||||
def index
|
||||
@filters = current_account.custom_filters.includes(:keywords).order(:phrase)
|
||||
@filters = current_account.custom_filters.includes(:keywords, :statuses).order(:phrase)
|
||||
end
|
||||
|
||||
def new
|
||||
|
|
|
@ -4,7 +4,7 @@ class FollowerAccountsController < ApplicationController
|
|||
include AccountControllerConcern
|
||||
include SignatureVerification
|
||||
|
||||
before_action :require_signature!, if: -> { request.format == :json && authorized_fetch_mode? }
|
||||
before_action :require_account_signature!, if: -> { request.format == :json && authorized_fetch_mode? }
|
||||
before_action :set_cache_headers
|
||||
|
||||
skip_around_action :set_locale, if: -> { request.format == :json }
|
||||
|
|
|
@ -4,7 +4,7 @@ class FollowingAccountsController < ApplicationController
|
|||
include AccountControllerConcern
|
||||
include SignatureVerification
|
||||
|
||||
before_action :require_signature!, if: -> { request.format == :json && authorized_fetch_mode? }
|
||||
before_action :require_account_signature!, if: -> { request.format == :json && authorized_fetch_mode? }
|
||||
before_action :set_cache_headers
|
||||
|
||||
skip_around_action :set_locale, if: -> { request.format == :json }
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
|
||||
class HomeController < ApplicationController
|
||||
before_action :redirect_unauthenticated_to_permalinks!
|
||||
before_action :authenticate_user!
|
||||
|
||||
before_action :set_pack
|
||||
before_action :set_referrer_policy_header
|
||||
before_action :set_instance_presenter
|
||||
|
||||
def index
|
||||
@body_classes = 'app-body'
|
||||
|
@ -16,7 +16,10 @@ class HomeController < ApplicationController
|
|||
def redirect_unauthenticated_to_permalinks!
|
||||
return if user_signed_in?
|
||||
|
||||
redirect_to(PermalinkRedirector.new(request.path).redirect_path || default_redirect_path)
|
||||
redirect_path = PermalinkRedirector.new(request.path).redirect_path
|
||||
redirect_path ||= default_redirect_path
|
||||
|
||||
redirect_to(redirect_path) if redirect_path.present?
|
||||
end
|
||||
|
||||
def set_pack
|
||||
|
@ -24,8 +27,10 @@ class HomeController < ApplicationController
|
|||
end
|
||||
|
||||
def default_redirect_path
|
||||
if request.path.start_with?('/web') || whitelist_mode?
|
||||
if whitelist_mode?
|
||||
new_user_session_path
|
||||
elsif request.path.start_with?('/web')
|
||||
nil
|
||||
elsif single_user_mode?
|
||||
short_account_path(Account.local.without_suspended.where('id > 0').first)
|
||||
else
|
||||
|
@ -36,4 +41,8 @@ class HomeController < ApplicationController
|
|||
def set_referrer_policy_header
|
||||
response.headers['Referrer-Policy'] = 'origin'
|
||||
end
|
||||
|
||||
def set_instance_presenter
|
||||
@instance_presenter = InstancePresenter.new
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class PrivacyController < ApplicationController
|
||||
layout 'public'
|
||||
|
||||
before_action :set_pack
|
||||
|
||||
before_action :set_instance_presenter
|
||||
before_action :set_expires_in
|
||||
|
||||
skip_before_action :require_functional!
|
||||
|
||||
def show; end
|
||||
|
||||
private
|
||||
|
||||
def set_pack
|
||||
use_pack 'public'
|
||||
end
|
||||
|
||||
def set_instance_presenter
|
||||
@instance_presenter = InstancePresenter.new
|
||||
end
|
||||
|
||||
def set_expires_in
|
||||
expires_in 0, public: true
|
||||
end
|
||||
end
|
|
@ -58,7 +58,7 @@ class Settings::PreferencesController < Settings::BaseController
|
|||
:setting_trends,
|
||||
:setting_crop_images,
|
||||
:setting_always_send_emails,
|
||||
notification_emails: %i(follow follow_request reblog favourite mention digest report pending_account trending_tag trending_link trending_status appeal),
|
||||
notification_emails: %i(follow follow_request reblog favourite mention report pending_account trending_tag trending_link trending_status appeal),
|
||||
interactions: %i(must_be_follower must_be_following must_be_following_dm)
|
||||
)
|
||||
end
|
||||
|
|
|
@ -8,7 +8,7 @@ class StatusesController < ApplicationController
|
|||
|
||||
layout 'public'
|
||||
|
||||
before_action :require_signature!, only: [:show, :activity], if: -> { request.format == :json && authorized_fetch_mode? }
|
||||
before_action :require_account_signature!, only: [:show, :activity], if: -> { request.format == :json && authorized_fetch_mode? }
|
||||
before_action :set_status
|
||||
before_action :set_instance_presenter
|
||||
before_action :set_link_headers
|
||||
|
|
|
@ -8,7 +8,7 @@ class TagsController < ApplicationController
|
|||
|
||||
layout 'public'
|
||||
|
||||
before_action :require_signature!, if: -> { request.format == :json && authorized_fetch_mode? }
|
||||
before_action :require_account_signature!, if: -> { request.format == :json && authorized_fetch_mode? }
|
||||
before_action :authenticate_user!, if: :whitelist_mode?
|
||||
before_action :set_local
|
||||
before_action :set_tag
|
||||
|
|
|
@ -2,64 +2,29 @@
|
|||
|
||||
module Admin::ActionLogsHelper
|
||||
def log_target(log)
|
||||
if log.target
|
||||
linkable_log_target(log.target)
|
||||
else
|
||||
log_target_from_history(log.target_type, log.recorded_changes)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def linkable_log_target(record)
|
||||
case record.class.name
|
||||
case log.target_type
|
||||
when 'Account'
|
||||
link_to record.acct, admin_account_path(record.id)
|
||||
link_to log.human_identifier, admin_account_path(log.target_id)
|
||||
when 'User'
|
||||
link_to record.account.acct, admin_account_path(record.account_id)
|
||||
when 'CustomEmoji'
|
||||
record.shortcode
|
||||
link_to log.human_identifier, admin_account_path(log.route_param)
|
||||
when 'UserRole'
|
||||
link_to log.human_identifier, admin_roles_path(log.target_id)
|
||||
when 'Report'
|
||||
link_to "##{record.id}", admin_report_path(record)
|
||||
link_to "##{log.human_identifier}", admin_report_path(log.target_id)
|
||||
when 'DomainBlock', 'DomainAllow', 'EmailDomainBlock', 'UnavailableDomain'
|
||||
link_to record.domain, "https://#{record.domain}"
|
||||
link_to log.human_identifier, "https://#{log.human_identifier}"
|
||||
when 'Status'
|
||||
link_to record.account.acct, ActivityPub::TagManager.instance.url_for(record)
|
||||
link_to log.human_identifier, log.permalink
|
||||
when 'AccountWarning'
|
||||
link_to record.target_account.acct, admin_account_path(record.target_account_id)
|
||||
link_to log.human_identifier, admin_account_path(log.target_id)
|
||||
when 'Announcement'
|
||||
link_to truncate(record.text), edit_admin_announcement_path(record.id)
|
||||
when 'IpBlock'
|
||||
"#{record.ip}/#{record.ip.prefix} (#{I18n.t("simple_form.labels.ip_block.severities.#{record.severity}")})"
|
||||
when 'Instance'
|
||||
record.domain
|
||||
link_to truncate(log.human_identifier), edit_admin_announcement_path(log.target_id)
|
||||
when 'IpBlock', 'Instance', 'CustomEmoji'
|
||||
log.human_identifier
|
||||
when 'CanonicalEmailBlock'
|
||||
content_tag(:samp, log.human_identifier[0...7], title: log.human_identifier)
|
||||
when 'Appeal'
|
||||
link_to record.account.acct, disputes_strike_path(record.strike)
|
||||
end
|
||||
end
|
||||
|
||||
def log_target_from_history(type, attributes)
|
||||
case type
|
||||
when 'User'
|
||||
attributes['username']
|
||||
when 'CustomEmoji'
|
||||
attributes['shortcode']
|
||||
when 'DomainBlock', 'DomainAllow', 'EmailDomainBlock', 'UnavailableDomain'
|
||||
link_to attributes['domain'], "https://#{attributes['domain']}"
|
||||
when 'Status'
|
||||
tmp_status = Status.new(attributes.except('reblogs_count', 'favourites_count'))
|
||||
|
||||
if tmp_status.account
|
||||
link_to tmp_status.account&.acct || "##{tmp_status.account_id}", admin_account_path(tmp_status.account_id)
|
||||
else
|
||||
I18n.t('admin.action_logs.deleted_status')
|
||||
end
|
||||
when 'Announcement'
|
||||
truncate(attributes['text'].is_a?(Array) ? attributes['text'].last : attributes['text'])
|
||||
when 'IpBlock'
|
||||
"#{attributes['ip']}/#{attributes['ip'].prefix} (#{I18n.t("simple_form.labels.ip_block.severities.#{attributes['severity']}")})"
|
||||
when 'Instance'
|
||||
attributes['domain']
|
||||
link_to log.human_identifier, disputes_strike_path(log.route_param)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,18 +6,71 @@ import ready from '../mastodon/ready';
|
|||
|
||||
const batchCheckboxClassName = '.batch-checkbox input[type="checkbox"]';
|
||||
|
||||
const showSelectAll = () => {
|
||||
const selectAllMatchingElement = document.querySelector('.batch-table__select-all');
|
||||
selectAllMatchingElement.classList.add('active');
|
||||
};
|
||||
|
||||
const hideSelectAll = () => {
|
||||
const selectAllMatchingElement = document.querySelector('.batch-table__select-all');
|
||||
const hiddenField = document.querySelector('#select_all_matching');
|
||||
const selectedMsg = document.querySelector('.batch-table__select-all .selected');
|
||||
const notSelectedMsg = document.querySelector('.batch-table__select-all .not-selected');
|
||||
|
||||
selectAllMatchingElement.classList.remove('active');
|
||||
selectedMsg.classList.remove('active');
|
||||
notSelectedMsg.classList.add('active');
|
||||
hiddenField.value = '0';
|
||||
};
|
||||
|
||||
delegate(document, '#batch_checkbox_all', 'change', ({ target }) => {
|
||||
const selectAllMatchingElement = document.querySelector('.batch-table__select-all');
|
||||
|
||||
[].forEach.call(document.querySelectorAll(batchCheckboxClassName), (content) => {
|
||||
content.checked = target.checked;
|
||||
});
|
||||
|
||||
if (selectAllMatchingElement) {
|
||||
if (target.checked) {
|
||||
showSelectAll();
|
||||
} else {
|
||||
hideSelectAll();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
delegate(document, '.batch-table__select-all button', 'click', () => {
|
||||
const hiddenField = document.querySelector('#select_all_matching');
|
||||
const active = hiddenField.value === '1';
|
||||
const selectedMsg = document.querySelector('.batch-table__select-all .selected');
|
||||
const notSelectedMsg = document.querySelector('.batch-table__select-all .not-selected');
|
||||
|
||||
if (active) {
|
||||
hiddenField.value = '0';
|
||||
selectedMsg.classList.remove('active');
|
||||
notSelectedMsg.classList.add('active');
|
||||
} else {
|
||||
hiddenField.value = '1';
|
||||
notSelectedMsg.classList.remove('active');
|
||||
selectedMsg.classList.add('active');
|
||||
}
|
||||
});
|
||||
|
||||
delegate(document, batchCheckboxClassName, 'change', () => {
|
||||
const checkAllElement = document.querySelector('#batch_checkbox_all');
|
||||
const selectAllMatchingElement = document.querySelector('.batch-table__select-all');
|
||||
|
||||
if (checkAllElement) {
|
||||
checkAllElement.checked = [].every.call(document.querySelectorAll(batchCheckboxClassName), (content) => content.checked);
|
||||
checkAllElement.indeterminate = !checkAllElement.checked && [].some.call(document.querySelectorAll(batchCheckboxClassName), (content) => content.checked);
|
||||
|
||||
if (selectAllMatchingElement) {
|
||||
if (checkAllElement.checked) {
|
||||
showSelectAll();
|
||||
} else {
|
||||
hideSelectAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import api from 'flavours/glitch/util/api';
|
||||
import api from '../api';
|
||||
|
||||
export const ACCOUNT_NOTE_SUBMIT_REQUEST = 'ACCOUNT_NOTE_SUBMIT_REQUEST';
|
||||
export const ACCOUNT_NOTE_SUBMIT_SUCCESS = 'ACCOUNT_NOTE_SUBMIT_SUCCESS';
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import api, { getLinks } from 'flavours/glitch/util/api';
|
||||
import api, { getLinks } from '../api';
|
||||
import { importAccount, importFetchedAccount, importFetchedAccounts } from './importer';
|
||||
|
||||
export const ACCOUNT_FETCH_REQUEST = 'ACCOUNT_FETCH_REQUEST';
|
||||
|
@ -553,10 +553,12 @@ export function expandFollowingFail(id, error) {
|
|||
|
||||
export function fetchRelationships(accountIds) {
|
||||
return (dispatch, getState) => {
|
||||
const loadedRelationships = getState().get('relationships');
|
||||
const state = getState();
|
||||
const loadedRelationships = state.get('relationships');
|
||||
const newAccountIds = accountIds.filter(id => loadedRelationships.get(id, null) === null);
|
||||
const signedIn = !!state.getIn(['meta', 'me']);
|
||||
|
||||
if (newAccountIds.length === 0) {
|
||||
if (!signedIn || newAccountIds.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import api from 'flavours/glitch/util/api';
|
||||
import api from '../api';
|
||||
import { normalizeAnnouncement } from './importer/normalizer';
|
||||
|
||||
export const ANNOUNCEMENTS_FETCH_REQUEST = 'ANNOUNCEMENTS_FETCH_REQUEST';
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
export const APP_LAYOUT_CHANGE = 'APP_LAYOUT_CHANGE';
|
||||
|
||||
export const changeLayout = layout => ({
|
||||
type: APP_LAYOUT_CHANGE,
|
||||
layout,
|
||||
});
|
|
@ -1,4 +1,4 @@
|
|||
import api, { getLinks } from 'flavours/glitch/util/api';
|
||||
import api, { getLinks } from '../api';
|
||||
import { fetchRelationships } from './accounts';
|
||||
import { importFetchedAccounts } from './importer';
|
||||
import { openModal } from './modal';
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import api, { getLinks } from 'flavours/glitch/util/api';
|
||||
import api, { getLinks } from '../api';
|
||||
import { importFetchedStatuses } from './importer';
|
||||
|
||||
export const BOOKMARKED_STATUSES_FETCH_REQUEST = 'BOOKMARKED_STATUSES_FETCH_REQUEST';
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import api from 'flavours/glitch/util/api';
|
||||
import api from '../api';
|
||||
import { CancelToken, isCancel } from 'axios';
|
||||
import { throttle } from 'lodash';
|
||||
import { search as emojiSearch } from 'flavours/glitch/util/emoji/emoji_mart_search_light';
|
||||
import { search as emojiSearch } from 'flavours/glitch/features/emoji/emoji_mart_search_light';
|
||||
import { useEmoji } from './emojis';
|
||||
import { tagHistory } from 'flavours/glitch/util/settings';
|
||||
import { recoverHashtags } from 'flavours/glitch/util/hashtag';
|
||||
import resizeImage from 'flavours/glitch/util/resize_image';
|
||||
import { tagHistory } from '../settings';
|
||||
import { recoverHashtags } from 'flavours/glitch/utils/hashtag';
|
||||
import resizeImage from 'flavours/glitch/utils/resize_image';
|
||||
import { importFetchedAccounts } from './importer';
|
||||
import { updateTimeline } from './timelines';
|
||||
import { showAlertForError } from './alerts';
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import api, { getLinks } from 'flavours/glitch/util/api';
|
||||
import api, { getLinks } from '../api';
|
||||
import {
|
||||
importFetchedAccounts,
|
||||
importFetchedStatuses,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import api from 'flavours/glitch/util/api';
|
||||
import api from '../api';
|
||||
|
||||
export const CUSTOM_EMOJIS_FETCH_REQUEST = 'CUSTOM_EMOJIS_FETCH_REQUEST';
|
||||
export const CUSTOM_EMOJIS_FETCH_SUCCESS = 'CUSTOM_EMOJIS_FETCH_SUCCESS';
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import api from 'flavours/glitch/util/api';
|
||||
import api from '../api';
|
||||
import { importFetchedAccounts } from './importer';
|
||||
import { fetchRelationships } from './accounts';
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import api, { getLinks } from 'flavours/glitch/util/api';
|
||||
import api, { getLinks } from '../api';
|
||||
|
||||
export const DOMAIN_BLOCK_REQUEST = 'DOMAIN_BLOCK_REQUEST';
|
||||
export const DOMAIN_BLOCK_SUCCESS = 'DOMAIN_BLOCK_SUCCESS';
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import api, { getLinks } from 'flavours/glitch/util/api';
|
||||
import api, { getLinks } from '../api';
|
||||
import { importFetchedStatuses } from './importer';
|
||||
|
||||
export const FAVOURITED_STATUSES_FETCH_REQUEST = 'FAVOURITED_STATUSES_FETCH_REQUEST';
|
||||
|
|
|
@ -1,9 +1,24 @@
|
|||
import api from 'flavours/glitch/util/api';
|
||||
import api from '../api';
|
||||
import { openModal } from './modal';
|
||||
|
||||
export const FILTERS_FETCH_REQUEST = 'FILTERS_FETCH_REQUEST';
|
||||
export const FILTERS_FETCH_SUCCESS = 'FILTERS_FETCH_SUCCESS';
|
||||
export const FILTERS_FETCH_FAIL = 'FILTERS_FETCH_FAIL';
|
||||
|
||||
export const FILTERS_STATUS_CREATE_REQUEST = 'FILTERS_STATUS_CREATE_REQUEST';
|
||||
export const FILTERS_STATUS_CREATE_SUCCESS = 'FILTERS_STATUS_CREATE_SUCCESS';
|
||||
export const FILTERS_STATUS_CREATE_FAIL = 'FILTERS_STATUS_CREATE_FAIL';
|
||||
|
||||
export const FILTERS_CREATE_REQUEST = 'FILTERS_CREATE_REQUEST';
|
||||
export const FILTERS_CREATE_SUCCESS = 'FILTERS_CREATE_SUCCESS';
|
||||
export const FILTERS_CREATE_FAIL = 'FILTERS_CREATE_FAIL';
|
||||
|
||||
export const initAddFilter = (status, { contextType }) => dispatch =>
|
||||
dispatch(openModal('FILTER', {
|
||||
statusId: status?.get('id'),
|
||||
contextType: contextType,
|
||||
}));
|
||||
|
||||
export const fetchFilters = () => (dispatch, getState) => {
|
||||
dispatch({
|
||||
type: FILTERS_FETCH_REQUEST,
|
||||
|
@ -11,7 +26,7 @@ export const fetchFilters = () => (dispatch, getState) => {
|
|||
});
|
||||
|
||||
api(getState)
|
||||
.get('/api/v1/filters')
|
||||
.get('/api/v2/filters')
|
||||
.then(({ data }) => dispatch({
|
||||
type: FILTERS_FETCH_SUCCESS,
|
||||
filters: data,
|
||||
|
@ -24,3 +39,55 @@ export const fetchFilters = () => (dispatch, getState) => {
|
|||
skipAlert: true,
|
||||
}));
|
||||
};
|
||||
|
||||
export const createFilterStatus = (params, onSuccess, onFail) => (dispatch, getState) => {
|
||||
dispatch(createFilterStatusRequest());
|
||||
|
||||
api(getState).post(`/api/v1/filters/${params.filter_id}/statuses`, params).then(response => {
|
||||
dispatch(createFilterStatusSuccess(response.data));
|
||||
if (onSuccess) onSuccess();
|
||||
}).catch(error => {
|
||||
dispatch(createFilterStatusFail(error));
|
||||
if (onFail) onFail();
|
||||
});
|
||||
};
|
||||
|
||||
export const createFilterStatusRequest = () => ({
|
||||
type: FILTERS_STATUS_CREATE_REQUEST,
|
||||
});
|
||||
|
||||
export const createFilterStatusSuccess = filter_status => ({
|
||||
type: FILTERS_STATUS_CREATE_SUCCESS,
|
||||
filter_status,
|
||||
});
|
||||
|
||||
export const createFilterStatusFail = error => ({
|
||||
type: FILTERS_STATUS_CREATE_FAIL,
|
||||
error,
|
||||
});
|
||||
|
||||
export const createFilter = (params, onSuccess, onFail) => (dispatch, getState) => {
|
||||
dispatch(createFilterRequest());
|
||||
|
||||
api(getState).post('/api/v2/filters', params).then(response => {
|
||||
dispatch(createFilterSuccess(response.data));
|
||||
if (onSuccess) onSuccess(response.data);
|
||||
}).catch(error => {
|
||||
dispatch(createFilterFail(error));
|
||||
if (onFail) onFail();
|
||||
});
|
||||
};
|
||||
|
||||
export const createFilterRequest = () => ({
|
||||
type: FILTERS_CREATE_REQUEST,
|
||||
});
|
||||
|
||||
export const createFilterSuccess = filter => ({
|
||||
type: FILTERS_CREATE_SUCCESS,
|
||||
filter,
|
||||
});
|
||||
|
||||
export const createFilterFail = error => ({
|
||||
type: FILTERS_CREATE_FAIL,
|
||||
error,
|
||||
});
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import api from 'flavours/glitch/util/api';
|
||||
import api from '../api';
|
||||
import { importFetchedAccounts } from './importer';
|
||||
|
||||
export const HISTORY_FETCH_REQUEST = 'HISTORY_FETCH_REQUEST';
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import api from 'flavours/glitch/util/api';
|
||||
import api from '../api';
|
||||
|
||||
export const IDENTITY_PROOFS_ACCOUNT_FETCH_REQUEST = 'IDENTITY_PROOFS_ACCOUNT_FETCH_REQUEST';
|
||||
export const IDENTITY_PROOFS_ACCOUNT_FETCH_SUCCESS = 'IDENTITY_PROOFS_ACCOUNT_FETCH_SUCCESS';
|
||||
|
|
|
@ -5,6 +5,7 @@ export const ACCOUNTS_IMPORT = 'ACCOUNTS_IMPORT';
|
|||
export const STATUS_IMPORT = 'STATUS_IMPORT';
|
||||
export const STATUSES_IMPORT = 'STATUSES_IMPORT';
|
||||
export const POLLS_IMPORT = 'POLLS_IMPORT';
|
||||
export const FILTERS_IMPORT = 'FILTERS_IMPORT';
|
||||
|
||||
function pushUnique(array, object) {
|
||||
if (array.every(element => element.id !== object.id)) {
|
||||
|
@ -28,6 +29,10 @@ export function importStatuses(statuses) {
|
|||
return { type: STATUSES_IMPORT, statuses };
|
||||
}
|
||||
|
||||
export function importFilters(filters) {
|
||||
return { type: FILTERS_IMPORT, filters };
|
||||
}
|
||||
|
||||
export function importPolls(polls) {
|
||||
return { type: POLLS_IMPORT, polls };
|
||||
}
|
||||
|
@ -61,11 +66,16 @@ export function importFetchedStatuses(statuses) {
|
|||
const accounts = [];
|
||||
const normalStatuses = [];
|
||||
const polls = [];
|
||||
const filters = [];
|
||||
|
||||
function processStatus(status) {
|
||||
pushUnique(normalStatuses, normalizeStatus(status, getState().getIn(['statuses', status.id]), getState().get('local_settings')));
|
||||
pushUnique(accounts, status.account);
|
||||
|
||||
if (status.filtered) {
|
||||
status.filtered.forEach(result => pushUnique(filters, result.filter));
|
||||
}
|
||||
|
||||
if (status.reblog && status.reblog.id) {
|
||||
processStatus(status.reblog);
|
||||
}
|
||||
|
@ -80,6 +90,7 @@ export function importFetchedStatuses(statuses) {
|
|||
dispatch(importPolls(polls));
|
||||
dispatch(importFetchedAccounts(accounts));
|
||||
dispatch(importStatuses(normalStatuses));
|
||||
dispatch(importFilters(filters));
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import escapeTextContentForBrowser from 'escape-html';
|
||||
import emojify from 'flavours/glitch/util/emoji';
|
||||
import { unescapeHTML } from 'flavours/glitch/util/html';
|
||||
import { autoHideCW } from 'flavours/glitch/util/content_warning';
|
||||
import emojify from 'flavours/glitch/features/emoji/emoji';
|
||||
import { unescapeHTML } from 'flavours/glitch/utils/html';
|
||||
import { autoHideCW } from 'flavours/glitch/utils/content_warning';
|
||||
|
||||
const domParser = new DOMParser();
|
||||
|
||||
|
@ -42,6 +42,14 @@ export function normalizeAccount(account) {
|
|||
return account;
|
||||
}
|
||||
|
||||
export function normalizeFilterResult(result) {
|
||||
const normalResult = { ...result };
|
||||
|
||||
normalResult.filter = normalResult.filter.id;
|
||||
|
||||
return normalResult;
|
||||
}
|
||||
|
||||
export function normalizeStatus(status, normalOldStatus, settings) {
|
||||
const normalStatus = { ...status };
|
||||
normalStatus.account = status.account.id;
|
||||
|
@ -54,6 +62,10 @@ export function normalizeStatus(status, normalOldStatus, settings) {
|
|||
normalStatus.poll = status.poll.id;
|
||||
}
|
||||
|
||||
if (status.filtered) {
|
||||
normalStatus.filtered = status.filtered.map(normalizeFilterResult);
|
||||
}
|
||||
|
||||
// Only calculate these values when status first encountered and
|
||||
// when the underlying values change. Otherwise keep the ones
|
||||
// already in the reducer
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import api from 'flavours/glitch/util/api';
|
||||
import api from '../api';
|
||||
import { importFetchedAccounts, importFetchedStatus } from './importer';
|
||||
|
||||
export const REBLOG_REQUEST = 'REBLOG_REQUEST';
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import api from 'flavours/glitch/util/api';
|
||||
import api from '../api';
|
||||
import { importFetchedAccounts } from './importer';
|
||||
import { showAlertForError } from './alerts';
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { expandSpoilers, disableSwiping } from 'flavours/glitch/util/initial_state';
|
||||
import { expandSpoilers, disableSwiping } from 'flavours/glitch/initial_state';
|
||||
import { openModal } from './modal';
|
||||
|
||||
export const LOCAL_SETTING_CHANGE = 'LOCAL_SETTING_CHANGE';
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import api from 'flavours/glitch/util/api';
|
||||
import api from '../api';
|
||||
import { debounce } from 'lodash';
|
||||
import compareId from 'flavours/glitch/util/compare_id';
|
||||
import compareId from '../compare_id';
|
||||
import { List as ImmutableList } from 'immutable';
|
||||
|
||||
export const MARKERS_FETCH_REQUEST = 'MARKERS_FETCH_REQUEST';
|
||||
export const MARKERS_FETCH_SUCCESS = 'MARKERS_FETCH_SUCCESS';
|
||||
|
@ -11,7 +12,7 @@ export const synchronouslySubmitMarkers = () => (dispatch, getState) => {
|
|||
const accessToken = getState().getIn(['meta', 'access_token'], '');
|
||||
const params = _buildParams(getState());
|
||||
|
||||
if (Object.keys(params).length === 0) {
|
||||
if (Object.keys(params).length === 0 || accessToken === '') {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -63,7 +64,7 @@ export const synchronouslySubmitMarkers = () => (dispatch, getState) => {
|
|||
const _buildParams = (state) => {
|
||||
const params = {};
|
||||
|
||||
const lastHomeId = state.getIn(['timelines', 'home', 'items']).find(item => item !== null);
|
||||
const lastHomeId = state.getIn(['timelines', 'home', 'items'], ImmutableList()).find(item => item !== null);
|
||||
const lastNotificationId = state.getIn(['notifications', 'lastReadId']);
|
||||
|
||||
if (lastHomeId && compareId(lastHomeId, state.getIn(['markers', 'home'])) > 0) {
|
||||
|
@ -82,9 +83,10 @@ const _buildParams = (state) => {
|
|||
};
|
||||
|
||||
const debouncedSubmitMarkers = debounce((dispatch, getState) => {
|
||||
const accessToken = getState().getIn(['meta', 'access_token'], '');
|
||||
const params = _buildParams(getState());
|
||||
|
||||
if (Object.keys(params).length === 0) {
|
||||
if (Object.keys(params).length === 0 || accessToken === '') {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import api, { getLinks } from 'flavours/glitch/util/api';
|
||||
import api, { getLinks } from '../api';
|
||||
import { fetchRelationships } from './accounts';
|
||||
import { importFetchedAccounts } from './importer';
|
||||
import { openModal } from 'flavours/glitch/actions/modal';
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import api, { getLinks } from 'flavours/glitch/util/api';
|
||||
import api, { getLinks } from '../api';
|
||||
import IntlMessageFormat from 'intl-messageformat';
|
||||
import { fetchFollowRequests, fetchRelationships } from './accounts';
|
||||
import {
|
||||
|
@ -11,12 +11,10 @@ import { submitMarkers } from './markers';
|
|||
import { saveSettings } from './settings';
|
||||
import { defineMessages } from 'react-intl';
|
||||
import { List as ImmutableList } from 'immutable';
|
||||
import { unescapeHTML } from 'flavours/glitch/util/html';
|
||||
import { getFiltersRegex } from 'flavours/glitch/selectors';
|
||||
import { usePendingItems as preferPendingItems } from 'flavours/glitch/util/initial_state';
|
||||
import compareId from 'flavours/glitch/util/compare_id';
|
||||
import { searchTextFromRawStatus } from 'flavours/glitch/actions/importer/normalizer';
|
||||
import { requestNotificationPermission } from 'flavours/glitch/util/notifications';
|
||||
import { unescapeHTML } from 'flavours/glitch/utils/html';
|
||||
import { usePendingItems as preferPendingItems } from 'flavours/glitch/initial_state';
|
||||
import compareId from 'flavours/glitch/compare_id';
|
||||
import { requestNotificationPermission } from 'flavours/glitch/utils/notifications';
|
||||
|
||||
export const NOTIFICATIONS_UPDATE = 'NOTIFICATIONS_UPDATE';
|
||||
export const NOTIFICATIONS_UPDATE_NOOP = 'NOTIFICATIONS_UPDATE_NOOP';
|
||||
|
@ -74,20 +72,17 @@ export function updateNotifications(notification, intlMessages, intlLocale) {
|
|||
const showInColumn = activeFilter === 'all' ? getState().getIn(['settings', 'notifications', 'shows', notification.type], true) : activeFilter === notification.type;
|
||||
const showAlert = getState().getIn(['settings', 'notifications', 'alerts', notification.type], true);
|
||||
const playSound = getState().getIn(['settings', 'notifications', 'sounds', notification.type], true);
|
||||
const filters = getFiltersRegex(getState(), { contextType: 'notifications' });
|
||||
|
||||
let filtered = false;
|
||||
|
||||
if (['mention', 'status'].includes(notification.type)) {
|
||||
const dropRegex = filters[0];
|
||||
const regex = filters[1];
|
||||
const searchIndex = searchTextFromRawStatus(notification.status);
|
||||
if (['mention', 'status'].includes(notification.type) && notification.status.filtered) {
|
||||
const filters = notification.status.filtered.filter(result => result.filter.context.includes('notifications'));
|
||||
|
||||
if (dropRegex && dropRegex.test(searchIndex)) {
|
||||
if (filters.some(result => result.filter.filter_action === 'hide')) {
|
||||
return;
|
||||
}
|
||||
|
||||
filtered = regex && regex.test(searchIndex);
|
||||
filtered = filters.length > 0;
|
||||
}
|
||||
|
||||
if (['follow_request'].includes(notification.type)) {
|
||||
|
@ -158,16 +153,23 @@ const excludeTypesFromFilter = filter => {
|
|||
|
||||
const noOp = () => {};
|
||||
|
||||
export function expandNotifications({ maxId } = {}, done = noOp) {
|
||||
let expandNotificationsController = new AbortController();
|
||||
|
||||
export function expandNotifications({ maxId, forceLoad } = {}, done = noOp) {
|
||||
return (dispatch, getState) => {
|
||||
const activeFilter = getState().getIn(['settings', 'notifications', 'quickFilter', 'active']);
|
||||
const notifications = getState().get('notifications');
|
||||
const isLoadingMore = !!maxId;
|
||||
|
||||
if (notifications.get('isLoading')) {
|
||||
if (forceLoad) {
|
||||
expandNotificationsController.abort();
|
||||
expandNotificationsController = new AbortController();
|
||||
} else {
|
||||
done();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const params = {
|
||||
max_id: maxId,
|
||||
|
@ -191,7 +193,7 @@ export function expandNotifications({ maxId } = {}, done = noOp) {
|
|||
|
||||
dispatch(expandNotificationsRequest(isLoadingMore));
|
||||
|
||||
api(getState).get('/api/v1/notifications', { params }).then(response => {
|
||||
api(getState).get('/api/v1/notifications', { params, signal: expandNotificationsController.signal }).then(response => {
|
||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||
|
||||
dispatch(importFetchedAccounts(response.data.map(item => item.account)));
|
||||
|
@ -232,7 +234,7 @@ export function expandNotificationsFail(error, isLoadingMore) {
|
|||
type: NOTIFICATIONS_EXPAND_FAIL,
|
||||
error,
|
||||
skipLoading: !isLoadingMore,
|
||||
skipAlert: !isLoadingMore,
|
||||
skipAlert: !isLoadingMore || error.name === 'AbortError',
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -343,7 +345,7 @@ export function setFilter (filterType) {
|
|||
path: ['notifications', 'quickFilter', 'active'],
|
||||
value: filterType,
|
||||
});
|
||||
dispatch(expandNotifications());
|
||||
dispatch(expandNotifications({ forceLoad: true }));
|
||||
dispatch(saveSettings());
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import api from 'flavours/glitch/util/api';
|
||||
import api from '../api';
|
||||
import { importFetchedStatuses } from './importer';
|
||||
|
||||
export const PINNED_STATUSES_FETCH_REQUEST = 'PINNED_STATUSES_FETCH_REQUEST';
|
||||
export const PINNED_STATUSES_FETCH_SUCCESS = 'PINNED_STATUSES_FETCH_SUCCESS';
|
||||
export const PINNED_STATUSES_FETCH_FAIL = 'PINNED_STATUSES_FETCH_FAIL';
|
||||
|
||||
import { me } from 'flavours/glitch/util/initial_state';
|
||||
import { me } from 'flavours/glitch/initial_state';
|
||||
|
||||
export function fetchPinnedStatuses() {
|
||||
return (dispatch, getState) => {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import api from 'flavours/glitch/util/api';
|
||||
import api from '../api';
|
||||
import { importFetchedPoll } from './importer';
|
||||
|
||||
export const POLL_VOTE_REQUEST = 'POLL_VOTE_REQUEST';
|
||||
|
|
|
@ -1,19 +1,5 @@
|
|||
import {
|
||||
SET_BROWSER_SUPPORT,
|
||||
SET_SUBSCRIPTION,
|
||||
CLEAR_SUBSCRIPTION,
|
||||
SET_ALERTS,
|
||||
setAlerts,
|
||||
} from './setter';
|
||||
import { register, saveSettings } from './registerer';
|
||||
|
||||
export {
|
||||
SET_BROWSER_SUPPORT,
|
||||
SET_SUBSCRIPTION,
|
||||
CLEAR_SUBSCRIPTION,
|
||||
SET_ALERTS,
|
||||
register,
|
||||
};
|
||||
import { setAlerts } from './setter';
|
||||
import { saveSettings } from './registerer';
|
||||
|
||||
export function changeAlerts(path, value) {
|
||||
return dispatch => {
|
||||
|
@ -21,3 +7,11 @@ export function changeAlerts(path, value) {
|
|||
dispatch(saveSettings());
|
||||
};
|
||||
}
|
||||
|
||||
export {
|
||||
CLEAR_SUBSCRIPTION,
|
||||
SET_BROWSER_SUPPORT,
|
||||
SET_SUBSCRIPTION,
|
||||
SET_ALERTS,
|
||||
} from './setter';
|
||||
export { register } from './registerer';
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import api from 'flavours/glitch/util/api';
|
||||
import { pushNotificationsSetting } from 'flavours/glitch/util/settings';
|
||||
import api from '../../api';
|
||||
import { pushNotificationsSetting } from '../../settings';
|
||||
import { setBrowserSupport, setSubscription, clearSubscription } from './setter';
|
||||
|
||||
// Taken from https://www.npmjs.com/package/web-push
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import api from 'flavours/glitch/util/api';
|
||||
import api from '../api';
|
||||
import { openModal } from './modal';
|
||||
|
||||
export const REPORT_SUBMIT_REQUEST = 'REPORT_SUBMIT_REQUEST';
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
import api from 'flavours/glitch/util/api';
|
||||
|
||||
export const RULES_FETCH_REQUEST = 'RULES_FETCH_REQUEST';
|
||||
export const RULES_FETCH_SUCCESS = 'RULES_FETCH_SUCCESS';
|
||||
export const RULES_FETCH_FAIL = 'RULES_FETCH_FAIL';
|
||||
|
||||
export const fetchRules = () => (dispatch, getState) => {
|
||||
dispatch(fetchRulesRequest());
|
||||
|
||||
api(getState)
|
||||
.get('/api/v1/instance').then(({ data }) => dispatch(fetchRulesSuccess(data.rules)))
|
||||
.catch(err => dispatch(fetchRulesFail(err)));
|
||||
};
|
||||
|
||||
const fetchRulesRequest = () => ({
|
||||
type: RULES_FETCH_REQUEST,
|
||||
});
|
||||
|
||||
const fetchRulesSuccess = rules => ({
|
||||
type: RULES_FETCH_SUCCESS,
|
||||
rules,
|
||||
});
|
||||
|
||||
const fetchRulesFail = error => ({
|
||||
type: RULES_FETCH_FAIL,
|
||||
error,
|
||||
});
|
|
@ -1,4 +1,4 @@
|
|||
import api from 'flavours/glitch/util/api';
|
||||
import api from '../api';
|
||||
import { fetchRelationships } from './accounts';
|
||||
import { importFetchedAccounts, importFetchedStatuses } from './importer';
|
||||
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
import api from '../api';
|
||||
import { importFetchedAccount } from './importer';
|
||||
|
||||
export const SERVER_FETCH_REQUEST = 'Server_FETCH_REQUEST';
|
||||
export const SERVER_FETCH_SUCCESS = 'Server_FETCH_SUCCESS';
|
||||
export const SERVER_FETCH_FAIL = 'Server_FETCH_FAIL';
|
||||
|
||||
export const fetchServer = () => (dispatch, getState) => {
|
||||
dispatch(fetchServerRequest());
|
||||
|
||||
api(getState)
|
||||
.get('/api/v2/instance').then(({ data }) => {
|
||||
if (data.contact.account) dispatch(importFetchedAccount(data.contact.account));
|
||||
dispatch(fetchServerSuccess(data));
|
||||
}).catch(err => dispatch(fetchServerFail(err)));
|
||||
};
|
||||
|
||||
const fetchServerRequest = () => ({
|
||||
type: SERVER_FETCH_REQUEST,
|
||||
});
|
||||
|
||||
const fetchServerSuccess = server => ({
|
||||
type: SERVER_FETCH_SUCCESS,
|
||||
server,
|
||||
});
|
||||
|
||||
const fetchServerFail = error => ({
|
||||
type: SERVER_FETCH_FAIL,
|
||||
error,
|
||||
});
|
|
@ -1,4 +1,4 @@
|
|||
import api from 'flavours/glitch/util/api';
|
||||
import api from '../api';
|
||||
import { debounce } from 'lodash';
|
||||
import { showAlertForError } from './alerts';
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import api from 'flavours/glitch/util/api';
|
||||
import api from '../api';
|
||||
|
||||
import { deleteFromTimelines } from './timelines';
|
||||
import { importFetchedStatus, importFetchedStatuses } from './importer';
|
||||
|
@ -42,9 +42,9 @@ export function fetchStatusRequest(id, skipLoading) {
|
|||
};
|
||||
};
|
||||
|
||||
export function fetchStatus(id) {
|
||||
export function fetchStatus(id, forceFetch = false) {
|
||||
return (dispatch, getState) => {
|
||||
const skipLoading = getState().getIn(['statuses', id], null) !== null;
|
||||
const skipLoading = !forceFetch && getState().getIn(['statuses', id], null) !== null;
|
||||
|
||||
dispatch(fetchContext(id));
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// @ts-check
|
||||
|
||||
import { connectStream } from 'flavours/glitch/util/stream';
|
||||
import { connectStream } from '../stream';
|
||||
import {
|
||||
updateTimeline,
|
||||
deleteFromTimelines,
|
||||
|
@ -21,7 +21,6 @@ import {
|
|||
updateReaction as updateAnnouncementsReaction,
|
||||
deleteAnnouncement,
|
||||
} from './announcements';
|
||||
import { fetchFilters } from './filters';
|
||||
import { getLocale } from 'mastodon/locales';
|
||||
|
||||
const { messages } = getLocale();
|
||||
|
@ -97,9 +96,6 @@ export const connectTimelineStream = (timelineId, channelName, params = {}, opti
|
|||
case 'conversation':
|
||||
dispatch(updateConversations(JSON.parse(data.payload)));
|
||||
break;
|
||||
case 'filters_changed':
|
||||
dispatch(fetchFilters());
|
||||
break;
|
||||
case 'announcement':
|
||||
dispatch(updateAnnouncements(JSON.parse(data.payload)));
|
||||
break;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import api from 'flavours/glitch/util/api';
|
||||
import api from '../api';
|
||||
import { importFetchedAccounts } from './importer';
|
||||
import { fetchRelationships } from './accounts';
|
||||
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
import api from '../api';
|
||||
|
||||
export const HASHTAG_FETCH_REQUEST = 'HASHTAG_FETCH_REQUEST';
|
||||
export const HASHTAG_FETCH_SUCCESS = 'HASHTAG_FETCH_SUCCESS';
|
||||
export const HASHTAG_FETCH_FAIL = 'HASHTAG_FETCH_FAIL';
|
||||
|
||||
export const HASHTAG_FOLLOW_REQUEST = 'HASHTAG_FOLLOW_REQUEST';
|
||||
export const HASHTAG_FOLLOW_SUCCESS = 'HASHTAG_FOLLOW_SUCCESS';
|
||||
export const HASHTAG_FOLLOW_FAIL = 'HASHTAG_FOLLOW_FAIL';
|
||||
|
||||
export const HASHTAG_UNFOLLOW_REQUEST = 'HASHTAG_UNFOLLOW_REQUEST';
|
||||
export const HASHTAG_UNFOLLOW_SUCCESS = 'HASHTAG_UNFOLLOW_SUCCESS';
|
||||
export const HASHTAG_UNFOLLOW_FAIL = 'HASHTAG_UNFOLLOW_FAIL';
|
||||
|
||||
export const fetchHashtag = name => (dispatch, getState) => {
|
||||
dispatch(fetchHashtagRequest());
|
||||
|
||||
api(getState).get(`/api/v1/tags/${name}`).then(({ data }) => {
|
||||
dispatch(fetchHashtagSuccess(name, data));
|
||||
}).catch(err => {
|
||||
dispatch(fetchHashtagFail(err));
|
||||
});
|
||||
};
|
||||
|
||||
export const fetchHashtagRequest = () => ({
|
||||
type: HASHTAG_FETCH_REQUEST,
|
||||
});
|
||||
|
||||
export const fetchHashtagSuccess = (name, tag) => ({
|
||||
type: HASHTAG_FETCH_SUCCESS,
|
||||
name,
|
||||
tag,
|
||||
});
|
||||
|
||||
export const fetchHashtagFail = error => ({
|
||||
type: HASHTAG_FETCH_FAIL,
|
||||
error,
|
||||
});
|
||||
|
||||
export const followHashtag = name => (dispatch, getState) => {
|
||||
dispatch(followHashtagRequest(name));
|
||||
|
||||
api(getState).post(`/api/v1/tags/${name}/follow`).then(({ data }) => {
|
||||
dispatch(followHashtagSuccess(name, data));
|
||||
}).catch(err => {
|
||||
dispatch(followHashtagFail(name, err));
|
||||
});
|
||||
};
|
||||
|
||||
export const followHashtagRequest = name => ({
|
||||
type: HASHTAG_FOLLOW_REQUEST,
|
||||
name,
|
||||
});
|
||||
|
||||
export const followHashtagSuccess = (name, tag) => ({
|
||||
type: HASHTAG_FOLLOW_SUCCESS,
|
||||
name,
|
||||
tag,
|
||||
});
|
||||
|
||||
export const followHashtagFail = (name, error) => ({
|
||||
type: HASHTAG_FOLLOW_FAIL,
|
||||
name,
|
||||
error,
|
||||
});
|
||||
|
||||
export const unfollowHashtag = name => (dispatch, getState) => {
|
||||
dispatch(unfollowHashtagRequest(name));
|
||||
|
||||
api(getState).post(`/api/v1/tags/${name}/unfollow`).then(({ data }) => {
|
||||
dispatch(unfollowHashtagSuccess(name, data));
|
||||
}).catch(err => {
|
||||
dispatch(unfollowHashtagFail(name, err));
|
||||
});
|
||||
};
|
||||
|
||||
export const unfollowHashtagRequest = name => ({
|
||||
type: HASHTAG_UNFOLLOW_REQUEST,
|
||||
name,
|
||||
});
|
||||
|
||||
export const unfollowHashtagSuccess = (name, tag) => ({
|
||||
type: HASHTAG_UNFOLLOW_SUCCESS,
|
||||
name,
|
||||
tag,
|
||||
});
|
||||
|
||||
export const unfollowHashtagFail = (name, error) => ({
|
||||
type: HASHTAG_UNFOLLOW_FAIL,
|
||||
name,
|
||||
error,
|
||||
});
|
|
@ -1,11 +1,10 @@
|
|||
import { importFetchedStatus, importFetchedStatuses } from './importer';
|
||||
import { submitMarkers } from './markers';
|
||||
import api, { getLinks } from 'flavours/glitch/util/api';
|
||||
import api, { getLinks } from 'flavours/glitch/api';
|
||||
import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
|
||||
import compareId from 'flavours/glitch/util/compare_id';
|
||||
import { me, usePendingItems as preferPendingItems } from 'flavours/glitch/util/initial_state';
|
||||
import { getFiltersRegex } from 'flavours/glitch/selectors';
|
||||
import { searchTextFromRawStatus } from 'flavours/glitch/actions/importer/normalizer';
|
||||
import compareId from 'flavours/glitch/compare_id';
|
||||
import { me, usePendingItems as preferPendingItems } from 'flavours/glitch/initial_state';
|
||||
import { toServerSideType } from 'flavours/glitch/utils/filters';
|
||||
|
||||
export const TIMELINE_UPDATE = 'TIMELINE_UPDATE';
|
||||
export const TIMELINE_DELETE = 'TIMELINE_DELETE';
|
||||
|
@ -40,14 +39,13 @@ export function updateTimeline(timeline, status, accept) {
|
|||
return;
|
||||
}
|
||||
|
||||
const filters = getFiltersRegex(getState(), { contextType: timeline });
|
||||
const dropRegex = filters[0];
|
||||
const regex = filters[1];
|
||||
const text = searchTextFromRawStatus(status);
|
||||
let filtered = false;
|
||||
|
||||
if (status.account.id !== me) {
|
||||
filtered = (dropRegex && dropRegex.test(text)) || (regex && regex.test(text));
|
||||
if (status.filtered) {
|
||||
const contextType = toServerSideType(timeline);
|
||||
const filters = status.filtered.filter(result => result.filter.context.includes(contextType));
|
||||
|
||||
filtered = filters.length > 0;
|
||||
}
|
||||
|
||||
dispatch(importFetchedStatus(status));
|
||||
|
|
|
@ -1,32 +1,139 @@
|
|||
import api from 'flavours/glitch/util/api';
|
||||
import api, { getLinks } from '../api';
|
||||
import { importFetchedStatuses } from './importer';
|
||||
|
||||
export const TRENDS_FETCH_REQUEST = 'TRENDS_FETCH_REQUEST';
|
||||
export const TRENDS_FETCH_SUCCESS = 'TRENDS_FETCH_SUCCESS';
|
||||
export const TRENDS_FETCH_FAIL = 'TRENDS_FETCH_FAIL';
|
||||
export const TRENDS_TAGS_FETCH_REQUEST = 'TRENDS_TAGS_FETCH_REQUEST';
|
||||
export const TRENDS_TAGS_FETCH_SUCCESS = 'TRENDS_TAGS_FETCH_SUCCESS';
|
||||
export const TRENDS_TAGS_FETCH_FAIL = 'TRENDS_TAGS_FETCH_FAIL';
|
||||
|
||||
export const fetchTrends = () => (dispatch, getState) => {
|
||||
dispatch(fetchTrendsRequest());
|
||||
export const TRENDS_LINKS_FETCH_REQUEST = 'TRENDS_LINKS_FETCH_REQUEST';
|
||||
export const TRENDS_LINKS_FETCH_SUCCESS = 'TRENDS_LINKS_FETCH_SUCCESS';
|
||||
export const TRENDS_LINKS_FETCH_FAIL = 'TRENDS_LINKS_FETCH_FAIL';
|
||||
|
||||
export const TRENDS_STATUSES_FETCH_REQUEST = 'TRENDS_STATUSES_FETCH_REQUEST';
|
||||
export const TRENDS_STATUSES_FETCH_SUCCESS = 'TRENDS_STATUSES_FETCH_SUCCESS';
|
||||
export const TRENDS_STATUSES_FETCH_FAIL = 'TRENDS_STATUSES_FETCH_FAIL';
|
||||
|
||||
export const TRENDS_STATUSES_EXPAND_REQUEST = 'TRENDS_STATUSES_EXPAND_REQUEST';
|
||||
export const TRENDS_STATUSES_EXPAND_SUCCESS = 'TRENDS_STATUSES_EXPAND_SUCCESS';
|
||||
export const TRENDS_STATUSES_EXPAND_FAIL = 'TRENDS_STATUSES_EXPAND_FAIL';
|
||||
|
||||
export const fetchTrendingHashtags = () => (dispatch, getState) => {
|
||||
dispatch(fetchTrendingHashtagsRequest());
|
||||
|
||||
api(getState)
|
||||
.get('/api/v1/trends')
|
||||
.then(({ data }) => dispatch(fetchTrendsSuccess(data)))
|
||||
.catch(err => dispatch(fetchTrendsFail(err)));
|
||||
.get('/api/v1/trends/tags')
|
||||
.then(({ data }) => dispatch(fetchTrendingHashtagsSuccess(data)))
|
||||
.catch(err => dispatch(fetchTrendingHashtagsFail(err)));
|
||||
};
|
||||
|
||||
export const fetchTrendsRequest = () => ({
|
||||
type: TRENDS_FETCH_REQUEST,
|
||||
export const fetchTrendingHashtagsRequest = () => ({
|
||||
type: TRENDS_TAGS_FETCH_REQUEST,
|
||||
skipLoading: true,
|
||||
});
|
||||
|
||||
export const fetchTrendsSuccess = trends => ({
|
||||
type: TRENDS_FETCH_SUCCESS,
|
||||
export const fetchTrendingHashtagsSuccess = trends => ({
|
||||
type: TRENDS_TAGS_FETCH_SUCCESS,
|
||||
trends,
|
||||
skipLoading: true,
|
||||
});
|
||||
|
||||
export const fetchTrendsFail = error => ({
|
||||
type: TRENDS_FETCH_FAIL,
|
||||
export const fetchTrendingHashtagsFail = error => ({
|
||||
type: TRENDS_TAGS_FETCH_FAIL,
|
||||
error,
|
||||
skipLoading: true,
|
||||
skipAlert: true,
|
||||
});
|
||||
|
||||
export const fetchTrendingLinks = () => (dispatch, getState) => {
|
||||
dispatch(fetchTrendingLinksRequest());
|
||||
|
||||
api(getState)
|
||||
.get('/api/v1/trends/links')
|
||||
.then(({ data }) => dispatch(fetchTrendingLinksSuccess(data)))
|
||||
.catch(err => dispatch(fetchTrendingLinksFail(err)));
|
||||
};
|
||||
|
||||
export const fetchTrendingLinksRequest = () => ({
|
||||
type: TRENDS_LINKS_FETCH_REQUEST,
|
||||
skipLoading: true,
|
||||
});
|
||||
|
||||
export const fetchTrendingLinksSuccess = trends => ({
|
||||
type: TRENDS_LINKS_FETCH_SUCCESS,
|
||||
trends,
|
||||
skipLoading: true,
|
||||
});
|
||||
|
||||
export const fetchTrendingLinksFail = error => ({
|
||||
type: TRENDS_LINKS_FETCH_FAIL,
|
||||
error,
|
||||
skipLoading: true,
|
||||
skipAlert: true,
|
||||
});
|
||||
|
||||
export const fetchTrendingStatuses = () => (dispatch, getState) => {
|
||||
if (getState().getIn(['status_lists', 'trending', 'isLoading'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(fetchTrendingStatusesRequest());
|
||||
|
||||
api(getState).get('/api/v1/trends/statuses').then(response => {
|
||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||
dispatch(importFetchedStatuses(response.data));
|
||||
dispatch(fetchTrendingStatusesSuccess(response.data, next ? next.uri : null));
|
||||
}).catch(err => dispatch(fetchTrendingStatusesFail(err)));
|
||||
};
|
||||
|
||||
export const fetchTrendingStatusesRequest = () => ({
|
||||
type: TRENDS_STATUSES_FETCH_REQUEST,
|
||||
skipLoading: true,
|
||||
});
|
||||
|
||||
export const fetchTrendingStatusesSuccess = (statuses, next) => ({
|
||||
type: TRENDS_STATUSES_FETCH_SUCCESS,
|
||||
statuses,
|
||||
next,
|
||||
skipLoading: true,
|
||||
});
|
||||
|
||||
export const fetchTrendingStatusesFail = error => ({
|
||||
type: TRENDS_STATUSES_FETCH_FAIL,
|
||||
error,
|
||||
skipLoading: true,
|
||||
skipAlert: true,
|
||||
});
|
||||
|
||||
|
||||
export const expandTrendingStatuses = () => (dispatch, getState) => {
|
||||
const url = getState().getIn(['status_lists', 'trending', 'next'], null);
|
||||
|
||||
if (url === null || getState().getIn(['status_lists', 'trending', 'isLoading'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(expandTrendingStatusesRequest());
|
||||
|
||||
api(getState).get(url).then(response => {
|
||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||
dispatch(importFetchedStatuses(response.data));
|
||||
dispatch(expandTrendingStatusesSuccess(response.data, next ? next.uri : null));
|
||||
}).catch(error => {
|
||||
dispatch(expandTrendingStatusesFail(error));
|
||||
});
|
||||
};
|
||||
|
||||
export const expandTrendingStatusesRequest = () => ({
|
||||
type: TRENDS_STATUSES_EXPAND_REQUEST,
|
||||
});
|
||||
|
||||
export const expandTrendingStatusesSuccess = (statuses, next) => ({
|
||||
type: TRENDS_STATUSES_EXPAND_SUCCESS,
|
||||
statuses,
|
||||
next,
|
||||
});
|
||||
|
||||
export const expandTrendingStatusesFail = error => ({
|
||||
type: TRENDS_STATUSES_EXPAND_FAIL,
|
||||
error,
|
||||
});
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import axios from 'axios';
|
||||
import ready from './ready';
|
||||
import LinkHeader from 'http-link-header';
|
||||
import ready from './ready';
|
||||
|
||||
export const getLinks = response => {
|
||||
const value = response.headers.link;
|
|
@ -5,7 +5,7 @@ import includes from 'array-includes';
|
|||
import assign from 'object-assign';
|
||||
import values from 'object.values';
|
||||
import isNaN from 'is-nan';
|
||||
import { decode as decodeBase64 } from './base64';
|
||||
import { decode as decodeBase64 } from './utils/base64';
|
||||
import promiseFinally from 'promise.prototype.finally';
|
||||
|
||||
if (!Array.prototype.includes) {
|
|
@ -7,8 +7,9 @@ import Permalink from './permalink';
|
|||
import IconButton from './icon_button';
|
||||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import { me } from 'flavours/glitch/util/initial_state';
|
||||
import { me } from 'flavours/glitch/initial_state';
|
||||
import RelativeTimestamp from './relative_timestamp';
|
||||
import Skeleton from 'flavours/glitch/components/skeleton';
|
||||
|
||||
const messages = defineMessages({
|
||||
follow: { id: 'account.follow', defaultMessage: 'Follow' },
|
||||
|
@ -26,7 +27,7 @@ export default @injectIntl
|
|||
class Account extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
account: ImmutablePropTypes.map.isRequired,
|
||||
account: ImmutablePropTypes.map,
|
||||
onFollow: PropTypes.func.isRequired,
|
||||
onBlock: PropTypes.func.isRequired,
|
||||
onMute: PropTypes.func.isRequired,
|
||||
|
@ -77,7 +78,16 @@ class Account extends ImmutablePureComponent {
|
|||
} = this.props;
|
||||
|
||||
if (!account) {
|
||||
return <div />;
|
||||
return (
|
||||
<div className='account'>
|
||||
<div className='account__wrapper'>
|
||||
<div className='account__display-name'>
|
||||
<div className='account__avatar-wrapper'><Skeleton width={36} height={36} /></div>
|
||||
<DisplayName />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (hidden) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import api from 'flavours/glitch/util/api';
|
||||
import api from 'flavours/glitch/api';
|
||||
import { FormattedNumber } from 'react-intl';
|
||||
import { Sparklines, SparklinesCurve } from 'react-sparklines';
|
||||
import classNames from 'classnames';
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import api from 'flavours/glitch/util/api';
|
||||
import api from 'flavours/glitch/api';
|
||||
import { FormattedNumber } from 'react-intl';
|
||||
import { roundTo10 } from 'flavours/glitch/util/numbers';
|
||||
import { roundTo10 } from 'flavours/glitch/utils/numbers';
|
||||
import Skeleton from 'flavours/glitch/components/skeleton';
|
||||
|
||||
export default class Dimension extends React.PureComponent {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import api from 'flavours/glitch/util/api';
|
||||
import api from 'flavours/glitch/api';
|
||||
import { injectIntl, defineMessages } from 'react-intl';
|
||||
import classNames from 'classnames';
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import api from 'flavours/glitch/util/api';
|
||||
import api from 'flavours/glitch/api';
|
||||
import { FormattedMessage, FormattedNumber, FormattedDate } from 'react-intl';
|
||||
import classNames from 'classnames';
|
||||
import { roundTo10 } from 'flavours/glitch/util/numbers';
|
||||
import { roundTo10 } from 'flavours/glitch/utils/numbers';
|
||||
|
||||
const dateForCohort = cohort => {
|
||||
switch(cohort.frequency) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import api from 'flavours/glitch/util/api';
|
||||
import api from 'flavours/glitch/api';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import classNames from 'classnames';
|
||||
import Hashtag from 'flavours/glitch/components/hashtag';
|
||||
|
|
|
@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
|
|||
import { FormattedNumber } from 'react-intl';
|
||||
import TransitionMotion from 'react-motion/lib/TransitionMotion';
|
||||
import spring from 'react-motion/lib/spring';
|
||||
import { reduceMotion } from 'flavours/glitch/util/initial_state';
|
||||
import { reduceMotion } from 'flavours/glitch/initial_state';
|
||||
|
||||
const obfuscatedCount = count => {
|
||||
if (count < 0) {
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import unicodeMapping from 'flavours/glitch/util/emoji/emoji_unicode_mapping_light';
|
||||
import unicodeMapping from 'flavours/glitch/features/emoji/emoji_unicode_mapping_light';
|
||||
|
||||
import { assetHost } from 'flavours/glitch/util/config';
|
||||
import { assetHost } from 'flavours/glitch/utils/config';
|
||||
|
||||
export default class AutosuggestEmoji extends React.PureComponent {
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import { autoPlayGif } from 'flavours/glitch/util/initial_state';
|
||||
import { autoPlayGif } from 'flavours/glitch/initial_state';
|
||||
import classNames from 'classnames';
|
||||
|
||||
export default class Avatar extends React.PureComponent {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import { autoPlayGif } from 'flavours/glitch/util/initial_state';
|
||||
import { autoPlayGif } from 'flavours/glitch/initial_state';
|
||||
|
||||
export default class AvatarComposite extends React.PureComponent {
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import { autoPlayGif } from 'flavours/glitch/util/initial_state';
|
||||
import { autoPlayGif } from 'flavours/glitch/initial_state';
|
||||
|
||||
export default class AvatarOverlay extends React.PureComponent {
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { supportsPassiveEvents } from 'detect-passive-events';
|
||||
import { scrollTop } from 'flavours/glitch/util/scroll';
|
||||
import { scrollTop } from '../scroll';
|
||||
|
||||
export default class Column extends React.PureComponent {
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue