Compare commits

..

19 Commits

Author SHA1 Message Date
kibigo!
da2b6dda6a This is a better way of detecting frontends 2017-06-22 21:10:02 -07:00
kibigo!
ed82421870 Forgot to delete a debugging thing sry 😰 2017-06-22 20:38:08 -07:00
kibigo!
b3904c2553 MORE FRONTENDS (EASY MODE) WIN!!! 2017-06-22 20:15:11 -07:00
beatrix
101a4c6913 glitch the getting started image 2017-06-22 13:04:56 -04:00
kibigo!
11da74dbb7 Very minor styling improvements to toot-collapsing 2017-06-22 00:54:33 -07:00
kibigo!
51a86b32c3 Updates height upon collapsing 2017-06-21 20:17:12 -07:00
kibigo!
51b783cdbc Minor collapsing button improvements~ 2017-06-21 19:54:41 -07:00
kibigo!
3915f06b02 Collapsable toots [1/??] 2017-06-21 19:40:53 -07:00
kibigo!
07b1171c73 Profile Metadata HACK 😈 2017-06-20 19:44:43 -07:00
Go Shoemake
571576d6f7 Fixes drawer so stuff doesn't overflow 2017-06-20 23:46:17 +00:00
Charlotte Fields
7151ec7680 cybre cleanup 2017-06-20 23:46:17 +00:00
Chronister
a4b3009c1c cybrespace to 1.4.2 2017-06-20 23:46:17 +00:00
Chronister
ef30a92c71 All cybrespace changes through 5/28 2017-06-20 23:46:17 +00:00
Charlotte Fields
613aa55e03 adding cybre changes 2017-06-20 23:46:17 +00:00
beatrix-bitrot
88a08d54b6 update local modifications for cors and cp 2017-06-20 23:46:17 +00:00
beatrix-bitrot
3a69d70eae silly readme update to test automated deploys 2017-06-20 23:46:17 +00:00
beatrix-bitrot
9c778f4abf update README.md 2017-06-20 23:46:17 +00:00
beatrix
ac61ce5826 Update README.md 2017-06-20 23:46:17 +00:00
Beatrix Bitrot
73d6da32be CORS tweaks 2017-06-20 23:46:17 +00:00
1018 changed files with 9494 additions and 30839 deletions

View File

@@ -15,15 +15,13 @@
"plugins": [
"syntax-dynamic-import",
["transform-object-rest-spread", { "useBuiltIns": true }],
"transform-decorators-legacy",
"transform-class-properties",
[
"react-intl",
{
"messagesDir": "./build/messages"
}
],
"preval"
]
],
"env": {
"development": {
@@ -45,7 +43,6 @@
]
}
],
"transform-react-inline-elements",
[
"transform-runtime",
{

View File

@@ -4,6 +4,7 @@ public/system
public/assets
public/packs
node_modules
storybook
neo4j
vendor/bundle
.DS_Store

View File

@@ -69,7 +69,7 @@ SMTP_FROM_ADDRESS=notifications@${APP_NAME}.nanoapp.io
# PAPERCLIP_ROOT_URL=/system
# Optional asset host for multi-server setups
# CDN_HOST=https://assets.example.com
# CDN_HOST=assets.example.com
# S3 (optional)
# S3_ENABLED=true

View File

@@ -26,22 +26,11 @@ LOCAL_HTTPS=true
# ALTERNATE_DOMAINS=example1.com,example2.com
# Application secrets
# Generate each with the `RAILS_ENV=production bundle exec rake secret` task (`docker-compose run --rm web rake secret` if you use docker compose)
# Generate each with the `rake secret` task (`docker-compose run --rm web rake secret` if you use docker compose)
PAPERCLIP_SECRET=
SECRET_KEY_BASE=
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 `RAILS_ENV=production bundle exec rake mastodon:webpush:generate_vapid_key` task (`docker-compose run --rm web rake mastodon:webpush:generate_vapid_key` if you use docker compose)
#
# For more information visit https://rossta.net/blog/using-the-web-push-api-with-vapid.html
VAPID_PRIVATE_KEY=
VAPID_PUBLIC_KEY=
# Registrations
# Single user mode will disable registrations and redirect frontpage to the first profile
# SINGLE_USER_MODE=true
@@ -69,7 +58,7 @@ SMTP_FROM_ADDRESS=notifications@example.com
#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
@@ -98,15 +87,6 @@ SMTP_FROM_ADDRESS=notifications@example.com
# S3_ENDPOINT=
# S3_SIGNATURE_VERSION=
# Swift (optional)
# SWIFT_ENABLED=true
# SWIFT_USERNAME=
# SWIFT_TENANT=
# SWIFT_PASSWORD=
# SWIFT_AUTH_URL=
# SWIFT_CONTAINER=
# SWIFT_OBJECT_URL=
# Optional alias for S3 if you want to use Cloudfront or Cloudflare in front
# S3_CLOUDFRONT_HOST=

View File

@@ -1,9 +1,7 @@
---
root: true
env:
browser: true
node: true
node: false
es6: true
parser: babel-eslint
@@ -49,20 +47,13 @@ rules:
- warn
- allow:
- error
- warn
no-fallthrough: error
no-irregular-whitespace: error
no-mixed-spaces-and-tabs: warn
no-nested-ternary: warn
no-trailing-spaces: warn
no-undef: error
no-unreachable: error
no-unused-expressions: error
no-unused-vars:
- error
- vars: all
args: after-used
ignoreRestSiblings: true
object-curly-spacing:
- error
- always
@@ -90,10 +81,7 @@ rules:
- 2
react/jsx-no-bind: error
react/jsx-no-duplicate-props: error
react/jsx-no-undef: error
react/jsx-tag-spacing: error
react/jsx-uses-react: error
react/jsx-uses-vars: error
react/jsx-wrap-multilines: error
react/no-multi-comp: off
react/no-string-refs: error
@@ -113,7 +101,7 @@ rules:
jsx-a11y/iframe-has-title: warn
jsx-a11y/img-has-alt: warn
jsx-a11y/img-redundant-alt: warn
jsx-a11y/label-has-for: off
jsx-a11y/label-has-for: warn
jsx-a11y/mouse-events-have-key-events: warn
jsx-a11y/no-access-key: warn
jsx-a11y/no-distracting-elements: warn
@@ -122,6 +110,6 @@ rules:
jsx-a11y/onclick-has-focus: warn
jsx-a11y/onclick-has-role: warn
jsx-a11y/role-has-required-aria-props: warn
jsx-a11y/role-supports-aria-props: off
jsx-a11y/role-supports-aria-props: warn
jsx-a11y/scope: warn
jsx-a11y/tabindex-no-positive: warn

14
.gitattributes vendored
View File

@@ -1,14 +0,0 @@
* text=auto eol=lf
*.eot -text
*.gif -text
*.gz -text
*.ico -text
*.jpg -text
*.mp3 -text
*.ogg -text
*.png -text
*.ttf -text
*.webm -text
*.woff -text
*.woff2 -text
spec/fixtures/requests/** -text !eol

View File

@@ -14,6 +14,7 @@ node_modules/
public/assets/
public/system/
spec/
storybook/
tmp/
.vagrant/
vendor/bundle/

View File

@@ -6,4 +6,3 @@ plugins:
- last 2 versions
- IE >= 11
- iOS >= 9
postcss-object-fit-images: {}

View File

@@ -10,7 +10,6 @@ AllCops:
- 'node_modules/**/*'
- 'Vagrantfile'
- 'vendor/**/*'
- 'lib/json_ld/*'
Bundler/OrderedGems:
Enabled: false
@@ -28,7 +27,6 @@ Metrics/AbcSize:
Max: 100
Metrics/BlockLength:
Max: 35
Exclude:
- 'lib/tasks/**/*'
@@ -37,10 +35,10 @@ Metrics/BlockNesting:
Metrics/ClassLength:
CountComments: false
Max: 300
Max: 200
Metrics/CyclomaticComplexity:
Max: 25
Max: 15
Metrics/LineLength:
AllowURI: true
@@ -55,11 +53,11 @@ Metrics/ModuleLength:
Max: 200
Metrics/ParameterLists:
Max: 5
Max: 4
CountKeywordArgs: true
Metrics/PerceivedComplexity:
Max: 20
Max: 10
Rails:
Enabled: true

View File

@@ -2,3 +2,4 @@ node_modules/
.cache/
docs/
spec/
storybook/

View File

@@ -6,9 +6,8 @@ cache:
- node_modules
- public/assets
- public/packs-test
- tmp/cache/babel-loader
dist: trusty
sudo: required
sudo: false
notifications:
email: false
@@ -33,7 +32,6 @@ addons:
- g++-6
- libprotobuf-dev
- protobuf-compiler
- libicu-dev
rvm:
- 2.3.4

View File

@@ -1,9 +1,5 @@
ffmpeg
libicu-dev
libidn11
libidn11-dev
libpq-dev
protobuf-compiler
libprotobuf-dev
ffmpeg
libxdamage1
libxfixes3
protobuf-compiler

View File

@@ -1,15 +0,0 @@
# CODEOWNERS for tootsuite/mastodon
# Translators
# To add translator, copy these lines, replace `fr` with appropriate language code and replace `@żelipapą` with user's GitHub nickname preceded by `@` sign or e-mail address.
# /app/javascript/mastodon/locales/fr.json @żelipapą
# /app/views/user_mailer/*.fr.html.erb @żelipapą
# /app/views/user_mailer/*.fr.text.erb @żelipapą
# /config/locales/*.fr.yml @żelipapą
# /config/locales/fr.yml @żelipapą
/app/javascript/mastodon/locales/pl.json @m4sk1n
/app/views/user_mailer/*.pl.html.erb @m4sk1n
/app/views/user_mailer/*.pl.text.erb @m4sk1n
/config/locales/*.pl.yml @m4sk1n
/config/locales/pl.yml @m4sk1n

View File

@@ -1,36 +1,3 @@
# Contributing to Mastodon Glitch Edition #
Thank you for your interest in contributing to the `glitch-soc` project!
Here are some guidelines, and ways you can help.
> (This document is a bit of a work-in-progress, so please bear with us.
> If you don't see what you're looking for here, please don't hesitate to reach out!)
## Planning ##
Right now a lot of the planning for this project takes place in our development Discord, or through GitHub Issues and Projects.
We're working on ways to improve the planning structure and better solicit feedback, and if you feel like you can help in this respect, feel free to give us a holler.
## Documentation ##
The documentation for this repository is available at [`glitch-soc/docs`](https://github.com/glitch-soc/docs) (online at [glitch-soc.github.io/docs/](https://glitch-soc.github.io/docs/)).
Right now, we've mostly focused on the features that make this fork different from upstream in some manner.
Adding screenshots, improving descriptions, and so forth are all ways to help contribute to the project even if you don't know any code.
## Frontend Development ##
Check out [the documentation here](https://glitch-soc.github.io/docs/contributing/frontend/) for more information.
## Backend Development ##
See the guidelines below.
- - -
You should also try to follow the guidelines set out in the original `CONTRIBUTING.md` from `tootsuite/mastodon`, reproduced below.
<blockquote>
CONTRIBUTING
============
@@ -82,5 +49,3 @@ It is expected that you have a working development environment set up (see back-
* If you are introducing new strings, they must be using localization methods
If the JavaScript or CSS assets won't compile due to a syntax error, it's a good sign that the pull request isn't ready for submission yet.
</blockquote>

View File

@@ -1,4 +1,4 @@
FROM ruby:2.4.1-alpine3.6
FROM ruby:2.4.1-alpine
LABEL maintainer="https://github.com/tootsuite/mastodon" \
description="A GNU Social-compatible microblogging server"
@@ -7,19 +7,16 @@ ENV UID=991 GID=991 \
RAILS_SERVE_STATIC_FILES=true \
RAILS_ENV=production NODE_ENV=production
ARG LIBICONV_VERSION=1.15
ARG LIBICONV_DOWNLOAD_SHA256=ccf536620a45458d26ba83887a983b96827001e92a13847b45e4925cc8913178
EXPOSE 3000 4000
WORKDIR /mastodon
RUN apk -U upgrade \
RUN echo "@edge https://nl.alpinelinux.org/alpine/edge/main" >> /etc/apk/repositories \
&& apk -U upgrade \
&& apk add -t build-dependencies \
build-base \
icu-dev \
libidn-dev \
libtool \
libxml2-dev \
libxslt-dev \
postgresql-dev \
protobuf-dev \
python \
@@ -28,34 +25,22 @@ RUN apk -U upgrade \
ffmpeg \
file \
git \
icu-libs \
imagemagick \
libidn \
imagemagick@edge \
libpq \
nodejs-npm \
nodejs \
libxml2 \
libxslt \
nodejs-npm@edge \
nodejs@edge \
protobuf \
su-exec \
tini \
yarn \
&& npm install -g npm@3 && npm install -g yarn \
&& update-ca-certificates \
&& wget -O libiconv.tar.gz "http://ftp.gnu.org/pub/gnu/libiconv/libiconv-$LIBICONV_VERSION.tar.gz" \
&& echo "$LIBICONV_DOWNLOAD_SHA256 *libiconv.tar.gz" | sha256sum -c - \
&& mkdir -p /tmp/src \
&& tar -xzf libiconv.tar.gz -C /tmp/src \
&& rm libiconv.tar.gz \
&& cd /tmp/src/libiconv-$LIBICONV_VERSION \
&& ./configure --prefix=/usr/local \
&& make -j$(getconf _NPROCESSORS_ONLN)\
&& make install \
&& libtool --finish /usr/local/lib \
&& cd /mastodon \
&& rm -rf /tmp/* /var/cache/apk/*
COPY Gemfile Gemfile.lock package.json yarn.lock /mastodon/
RUN bundle config build.nokogiri --with-iconv-lib=/usr/local/lib --with-iconv-include=/usr/local/include \
&& bundle install -j$(getconf _NPROCESSORS_ONLN) --deployment --without test development \
RUN bundle install --deployment --without test development \
&& yarn --ignore-optional --pure-lockfile
COPY . /mastodon

16
Gemfile
View File

@@ -15,32 +15,25 @@ gem 'pghero', '~> 1.7'
gem 'dotenv-rails', '~> 2.2'
gem 'aws-sdk', '~> 2.9'
gem 'fog-openstack', '~> 0.1'
gem 'paperclip', '~> 5.1'
gem 'paperclip-av-transcoder', '~> 0.6'
gem 'active_model_serializers', '~> 0.10'
gem 'addressable', '~> 2.5'
gem 'bootsnap'
gem 'browser'
gem 'charlock_holmes', '~> 0.7.5'
gem 'iso-639'
gem 'cld3', '~> 3.1'
gem 'devise', '~> 4.2'
gem 'devise-two-factor', '~> 3.0'
gem 'doorkeeper', '~> 4.2'
gem 'fast_blank', '~> 1.0'
gem 'goldfinger', '~> 2.0'
gem 'goldfinger', '~> 1.2'
gem 'hiredis', '~> 0.6'
gem 'redis-namespace', '~> 1.5'
gem 'htmlentities', '~> 4.3'
gem 'http', '~> 2.2'
gem 'http_accept_language', '~> 2.1'
gem 'httplog', '~> 0.99'
gem 'idn-ruby', require: 'idn'
gem 'kaminari', '~> 1.0'
gem 'link_header', '~> 0.0'
gem 'mime-types', '~> 3.1'
gem 'nokogiri', '~> 1.7'
gem 'oj', '~> 3.0'
gem 'ostatus2', '~> 2.0'
@@ -53,7 +46,6 @@ gem 'rack-timeout', '~> 0.4'
gem 'rails-i18n', '~> 5.0'
gem 'rails-settings-cached', '~> 0.6'
gem 'redis', '~> 3.3', require: ['redis', 'redis/connection/hiredis']
gem 'mario-redis-lock', '~> 1.2', require: 'redis_lock'
gem 'rqrcode', '~> 0.10'
gem 'ruby-oembed', '~> 0.12', require: 'oembed'
gem 'sanitize', '~> 4.4'
@@ -68,10 +60,6 @@ gem 'statsd-instrument', '~> 2.1'
gem 'twitter-text', '~> 1.14'
gem 'tzinfo-data', '~> 1.2017'
gem 'webpacker', '~> 2.0'
gem 'webpush'
gem 'json-ld-preloaded', '~> 2.2.1'
gem 'rdf-normalize', '~> 0.3.1'
group :development, :test do
gem 'fabrication', '~> 2.16'
@@ -85,7 +73,7 @@ group :test do
gem 'capybara', '~> 2.14'
gem 'climate_control', '~> 0.2'
gem 'faker', '~> 1.7'
gem 'microformats', '~> 4.0'
gem 'microformats2', '~> 3.0'
gem 'rails-controller-testing', '~> 1.0'
gem 'rspec-sidekiq', '~> 3.0'
gem 'simplecov', '~> 0.14', require: false

View File

@@ -1,52 +1,47 @@
GEM
remote: https://rubygems.org/
specs:
actioncable (5.1.3)
actionpack (= 5.1.3)
actioncable (5.1.1)
actionpack (= 5.1.1)
nio4r (~> 2.0)
websocket-driver (~> 0.6.1)
actionmailer (5.1.3)
actionpack (= 5.1.3)
actionview (= 5.1.3)
activejob (= 5.1.3)
actionmailer (5.1.1)
actionpack (= 5.1.1)
actionview (= 5.1.1)
activejob (= 5.1.1)
mail (~> 2.5, >= 2.5.4)
rails-dom-testing (~> 2.0)
actionpack (5.1.3)
actionview (= 5.1.3)
activesupport (= 5.1.3)
actionpack (5.1.1)
actionview (= 5.1.1)
activesupport (= 5.1.1)
rack (~> 2.0)
rack-test (~> 0.6.3)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.0.2)
actionview (5.1.3)
activesupport (= 5.1.3)
actionview (5.1.1)
activesupport (= 5.1.1)
builder (~> 3.1)
erubi (~> 1.4)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.0.3)
active_model_serializers (0.10.6)
actionpack (>= 4.1, < 6)
activemodel (>= 4.1, < 6)
case_transform (>= 0.2)
jsonapi-renderer (>= 0.1.1.beta1, < 0.2)
active_record_query_trace (1.5.4)
activejob (5.1.3)
activesupport (= 5.1.3)
activejob (5.1.1)
activesupport (= 5.1.1)
globalid (>= 0.3.6)
activemodel (5.1.3)
activesupport (= 5.1.3)
activerecord (5.1.3)
activemodel (= 5.1.3)
activesupport (= 5.1.3)
activemodel (5.1.1)
activesupport (= 5.1.1)
activerecord (5.1.1)
activemodel (= 5.1.1)
activesupport (= 5.1.1)
arel (~> 8.0)
activesupport (5.1.3)
activesupport (5.1.1)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (~> 0.7)
minitest (~> 5.1)
tzinfo (~> 1.1)
addressable (2.5.2)
public_suffix (>= 2.0.2, < 4.0)
airbrussh (1.3.0)
addressable (2.5.1)
public_suffix (~> 2.0, >= 2.0.2)
airbrussh (1.2.0)
sshkit (>= 1.6.1, != 1.7.0)
annotate (2.7.2)
activerecord (>= 3.2, < 6.0)
@@ -57,14 +52,14 @@ GEM
encryptor (~> 3.0.0)
av (0.9.0)
cocaine (~> 0.5.3)
aws-sdk (2.10.21)
aws-sdk-resources (= 2.10.21)
aws-sdk-core (2.10.21)
aws-sdk (2.9.37)
aws-sdk-resources (= 2.9.37)
aws-sdk-core (2.9.37)
aws-sigv4 (~> 1.0)
jmespath (~> 1.0)
aws-sdk-resources (2.10.21)
aws-sdk-core (= 2.10.21)
aws-sigv4 (1.0.1)
aws-sdk-resources (2.9.37)
aws-sdk-core (= 2.9.37)
aws-sigv4 (1.0.0)
bcrypt (3.1.11)
better_errors (2.1.1)
coderay (>= 1.0.0)
@@ -72,18 +67,17 @@ GEM
rack (>= 0.9.0)
binding_of_caller (0.7.2)
debug_inspector (>= 0.0.1)
bootsnap (1.1.2)
bootsnap (1.0.0)
msgpack (~> 1.0)
brakeman (3.7.2)
browser (2.4.0)
brakeman (3.6.2)
builder (3.2.3)
bullet (5.5.1)
activesupport (>= 3.0.0)
uniform_notifier (~> 1.10.0)
bundler-audit (0.6.0)
bundler-audit (0.5.0)
bundler (~> 1.2)
thor (~> 0.18)
capistrano (3.8.2)
capistrano (3.8.1)
airbrussh (>= 1.0.0)
i18n
rake (>= 10.0.0)
@@ -99,18 +93,15 @@ GEM
sshkit (~> 1.3)
capistrano-yarn (2.0.2)
capistrano (~> 3.0)
capybara (2.14.4)
capybara (2.14.2)
addressable
mime-types (>= 1.16)
nokogiri (>= 1.3.3)
rack (>= 1.0.0)
rack-test (>= 0.5.4)
xpath (~> 2.0)
case_transform (0.2)
activesupport
charlock_holmes (0.7.5)
chunky_png (1.3.8)
cld3 (3.1.3)
cld3 (3.1.2)
ffi (>= 1.1.0, < 1.10.0)
climate_control (0.2.0)
cocaine (0.5.8)
@@ -150,40 +141,26 @@ GEM
thread
thread_safe
encryptor (3.0.0)
erubi (1.6.1)
erubi (1.6.0)
erubis (2.7.0)
et-orbi (1.0.5)
et-orbi (1.0.4)
tzinfo
excon (0.58.0)
execjs (2.7.0)
fabrication (2.16.2)
fabrication (2.16.1)
faker (1.7.3)
i18n (~> 0.5)
fast_blank (1.0.0)
ffi (1.9.18)
fog-core (1.45.0)
builder
excon (~> 0.58)
formatador (~> 0.2)
fog-json (1.0.2)
fog-core (~> 1.0)
multi_json (~> 1.10)
fog-openstack (0.1.21)
fog-core (>= 1.40)
fog-json (>= 1.0)
ipaddress (>= 0.8)
formatador (0.2.5)
fuubar (2.2.0)
rspec-core (~> 3.0)
ruby-progressbar (~> 1.4)
globalid (0.4.0)
activesupport (>= 4.2.0)
goldfinger (2.0.1)
addressable (~> 2.5)
http (~> 2.2)
nokogiri (~> 1.8)
oj (~> 3.0)
hamlit (2.8.4)
goldfinger (1.2.0)
addressable (~> 2.4)
http (~> 2.0)
nokogiri (~> 1.6)
hamlit (2.8.1)
temple (>= 0.8.0)
thor
tilt
@@ -192,12 +169,9 @@ GEM
activesupport (>= 4.0.1)
hamlit (>= 1.2.0)
railties (>= 4.0.1)
hamster (3.0.0)
concurrent-ruby (~> 1.0)
hashdiff (0.3.5)
hashdiff (0.3.4)
highline (1.7.8)
hiredis (0.6.1)
hkdf (0.3.0)
htmlentities (4.3.4)
http (2.2.2)
addressable (~> 2.3)
@@ -207,13 +181,13 @@ GEM
http-cookie (1.0.3)
domain_name (~> 0.5)
http-form_data (1.0.3)
http_accept_language (2.1.1)
http_accept_language (2.1.0)
http_parser.rb (0.6.0)
httplog (0.99.7)
httplog (0.99.3)
colorize
rack
i18n (0.8.6)
i18n-tasks (0.9.16)
i18n (0.8.4)
i18n-tasks (0.9.15)
activesupport (>= 4.0.2)
ast (>= 2.1.0)
easy_translate (>= 0.5.0)
@@ -223,20 +197,8 @@ GEM
parser (>= 2.2.3.0)
rainbow (~> 2.2)
terminal-table (>= 1.5.1)
idn-ruby (0.1.0)
ipaddress (0.8.3)
iso-639 (0.2.8)
jmespath (1.3.1)
json (2.1.0)
json-ld (2.1.5)
multi_json (~> 1.12)
rdf (~> 2.2)
json-ld-preloaded (2.2.1)
json-ld (~> 2.1, >= 2.1.5)
multi_json (~> 1.11)
rdf (~> 2.2)
jsonapi-renderer (0.1.3)
jwt (1.5.6)
kaminari (1.0.1)
activesupport (>= 4.1.0)
kaminari-actionview (= 1.0.1)
@@ -266,10 +228,8 @@ GEM
nokogiri (>= 1.5.9)
mail (2.6.6)
mime-types (>= 1.16, < 4)
mario-redis-lock (1.2.0)
redis (~> 3, >= 3.0.5)
method_source (0.8.2)
microformats (4.0.7)
microformats2 (3.1.0)
json
nokogiri
mime-types (3.1)
@@ -277,7 +237,7 @@ GEM
mime-types-data (3.2016.0521)
mimemagic (0.3.2)
mini_portile2 (2.2.0)
minitest (5.10.3)
minitest (5.10.2)
msgpack (1.1.0)
multi_json (1.12.1)
net-scp (1.2.1)
@@ -288,8 +248,8 @@ GEM
mini_portile2 (~> 2.2.0)
nokogumbo (1.4.13)
nokogiri
oj (3.3.4)
openssl (2.0.4)
oj (3.1.0)
openssl (2.0.3)
orm_adapter (0.5.0)
ostatus2 (2.0.1)
addressable (~> 2.4)
@@ -307,14 +267,14 @@ GEM
av (~> 0.9.0)
paperclip (>= 2.5.2)
parallel (1.11.2)
parallel_tests (2.14.2)
parallel_tests (2.14.1)
parallel
parser (2.4.0.0)
ast (~> 2.2)
pg (0.21.0)
pg (0.20.0)
pghero (1.7.0)
activerecord
pkg-config (1.2.4)
pkg-config (1.2.3)
powerpack (0.1.1)
pry (0.10.4)
coderay (~> 1.1.0)
@@ -322,7 +282,7 @@ GEM
slop (~> 3.4)
pry-rails (0.3.6)
pry (>= 0.10.4)
public_suffix (3.0.0)
public_suffix (2.0.5)
puma (3.9.1)
pundit (1.1.0)
activesupport (>= 3.0.0)
@@ -337,17 +297,17 @@ GEM
rack-test (0.6.3)
rack (>= 1.0)
rack-timeout (0.4.2)
rails (5.1.3)
actioncable (= 5.1.3)
actionmailer (= 5.1.3)
actionpack (= 5.1.3)
actionview (= 5.1.3)
activejob (= 5.1.3)
activemodel (= 5.1.3)
activerecord (= 5.1.3)
activesupport (= 5.1.3)
bundler (>= 1.3.0)
railties (= 5.1.3)
rails (5.1.1)
actioncable (= 5.1.1)
actionmailer (= 5.1.1)
actionpack (= 5.1.1)
actionview (= 5.1.1)
activejob (= 5.1.1)
activemodel (= 5.1.1)
activerecord (= 5.1.1)
activesupport (= 5.1.1)
bundler (>= 1.3.0, < 2.0)
railties (= 5.1.1)
sprockets-rails (>= 2.0.0)
rails-controller-testing (1.0.2)
actionpack (~> 5.x, >= 5.0.1)
@@ -361,28 +321,23 @@ GEM
rails-i18n (5.0.4)
i18n (~> 0.7)
railties (~> 5.0)
rails-settings-cached (0.6.6)
rails-settings-cached (0.6.5)
rails (>= 4.2.0)
railties (5.1.3)
actionpack (= 5.1.3)
activesupport (= 5.1.3)
railties (5.1.1)
actionpack (= 5.1.1)
activesupport (= 5.1.1)
method_source
rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0)
rainbow (2.2.2)
rake
rake (12.0.0)
rdf (2.2.8)
hamster (~> 3.0)
link_header (~> 0.0, >= 0.0.8)
rdf-normalize (0.3.2)
rdf (~> 2.0)
redis (3.3.3)
redis-actionpack (5.0.1)
actionpack (>= 4.0, < 6)
redis-rack (>= 1, < 3)
redis-store (>= 1.1.0, < 1.4.0)
redis-activesupport (5.0.3)
redis-activesupport (5.0.2)
activesupport (>= 3, < 6)
redis-store (~> 1.3.0)
redis-namespace (1.5.3)
@@ -418,7 +373,7 @@ GEM
rspec-expectations (~> 3.6.0)
rspec-mocks (~> 3.6.0)
rspec-support (~> 3.6.0)
rspec-sidekiq (3.0.3)
rspec-sidekiq (3.0.1)
rspec-core (~> 3.0, >= 3.0.0)
sidekiq (>= 2.4.0)
rspec-support (3.6.0)
@@ -439,10 +394,10 @@ GEM
nokogiri (>= 1.4.4)
nokogumbo (~> 1.4.1)
sass (3.4.24)
scss_lint (0.54.0)
scss_lint (0.53.0)
rake (>= 0.9, < 13)
sass (~> 3.4.20)
sidekiq (5.0.4)
sidekiq (5.0.2)
concurrent-ruby (~> 1.0)
connection_pool (~> 2.2, >= 2.2.0)
rack-protection (>= 1.5.0)
@@ -450,12 +405,12 @@ GEM
sidekiq-bulk (0.1.1)
activesupport
sidekiq
sidekiq-scheduler (2.1.8)
sidekiq-scheduler (2.1.5)
redis (~> 3)
rufus-scheduler (~> 3.2)
sidekiq (>= 3)
tilt (>= 1.4.0)
sidekiq-unique-jobs (5.0.9)
sidekiq-unique-jobs (5.0.8)
sidekiq (>= 4.0, <= 6.0)
thor (~> 0)
simple-navigation (4.0.5)
@@ -479,15 +434,15 @@ GEM
sshkit (1.13.1)
net-scp (>= 1.1.2)
net-ssh (>= 2.8.0)
statsd-instrument (2.1.4)
statsd-instrument (2.1.2)
temple (0.8.0)
terminal-table (1.8.0)
unicode-display_width (~> 1.1, >= 1.1.1)
thor (0.20.0)
thor (0.19.4)
thread (0.2.2)
thread_safe (0.3.6)
tilt (2.0.8)
twitter-text (1.14.7)
tilt (2.0.7)
twitter-text (1.14.5)
unf (~> 0.1.0)
tzinfo (1.2.3)
thread_safe (~> 0.1)
@@ -498,7 +453,7 @@ GEM
unf (0.1.4)
unf_ext
unf_ext (0.0.7.4)
unicode-display_width (1.3.0)
unicode-display_width (1.2.1)
uniform_notifier (1.10.0)
warden (1.2.7)
rack (>= 1.0)
@@ -510,9 +465,6 @@ GEM
activesupport (>= 4.2)
multi_json (~> 1.2)
railties (>= 4.2)
webpush (0.3.2)
hkdf (~> 0.2)
jwt
websocket-driver (0.6.5)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.2)
@@ -523,7 +475,6 @@ PLATFORMS
ruby
DEPENDENCIES
active_model_serializers (~> 0.10)
active_record_query_trace (~> 1.5)
addressable (~> 2.5)
annotate (~> 2.7)
@@ -532,7 +483,6 @@ DEPENDENCIES
binding_of_caller (~> 0.7)
bootsnap
brakeman (~> 3.6)
browser
bullet (~> 5.5)
bundler-audit (~> 0.5)
capistrano (~> 3.8)
@@ -540,7 +490,6 @@ DEPENDENCIES
capistrano-rbenv (~> 2.1)
capistrano-yarn (~> 2.0)
capybara (~> 2.14)
charlock_holmes (~> 0.7.5)
cld3 (~> 3.1)
climate_control (~> 0.2)
devise (~> 4.2)
@@ -550,9 +499,8 @@ DEPENDENCIES
fabrication (~> 2.16)
faker (~> 1.7)
fast_blank (~> 1.0)
fog-openstack (~> 0.1)
fuubar (~> 2.2)
goldfinger (~> 2.0)
goldfinger (~> 1.2)
hamlit-rails (~> 0.2)
hiredis (~> 0.6)
htmlentities (~> 4.3)
@@ -560,17 +508,12 @@ DEPENDENCIES
http_accept_language (~> 2.1)
httplog (~> 0.99)
i18n-tasks (~> 0.9)
idn-ruby
iso-639
json-ld-preloaded (~> 2.2.1)
kaminari (~> 1.0)
letter_opener (~> 1.4)
letter_opener_web (~> 1.3)
link_header (~> 0.0)
lograge (~> 0.5)
mario-redis-lock (~> 1.2)
microformats (~> 4.0)
mime-types (~> 3.1)
microformats2 (~> 3.0)
nokogiri (~> 1.7)
oj (~> 3.0)
ostatus2 (~> 2.0)
@@ -592,7 +535,6 @@ DEPENDENCIES
rails-controller-testing (~> 1.0)
rails-i18n (~> 5.0)
rails-settings-cached (~> 0.6)
rdf-normalize (~> 0.3.1)
redis (~> 3.3)
redis-namespace (~> 1.5)
redis-rails (~> 5.0)
@@ -617,10 +559,9 @@ DEPENDENCIES
uglifier (~> 3.2)
webmock (~> 3.0)
webpacker (~> 2.0)
webpush
RUBY VERSION
ruby 2.4.1p111
BUNDLED WITH
1.15.4
1.15.1

View File

@@ -1,10 +1,5 @@
# Mastodon Glitch Edition #
> Now with automated deploys!
[![Build Status](https://travis-ci.org/glitch-soc/mastodon.svg?branch=master)](https://travis-ci.org/glitch-soc/mastodon)
Mastodon Glitch Edition
========
Now with automated deploys!
So here's the deal: we all work on this code, and then it runs on dev.glitch.social and anyone who uses that does so absolutely at their own risk. can you dig it?
- You can view documentation for this project at [glitch-soc.github.io/docs/](https://glitch-soc.github.io/docs/).
- And contributing guidelines are available [here](CONTRIBUTING.md) and [here](https://glitch-soc.github.io/docs/contributing/).

7
Vagrantfile vendored
View File

@@ -35,8 +35,6 @@ sudo apt-get install \
postgresql-contrib \
protobuf-compiler \
yarn \
libicu-dev \
libidn11-dev \
libprotobuf-dev \
libreadline-dev \
-y
@@ -44,12 +42,9 @@ sudo apt-get install \
# Install rvm
read RUBY_VERSION < .ruby-version
gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3
curl -sSL https://raw.githubusercontent.com/rvm/rvm/stable/binscripts/rvm-installer | bash -s stable --ruby=$RUBY_VERSION
curl -sSL https://get.rvm.io | bash -s stable --ruby=$RUBY_VERSION
source /home/vagrant/.rvm/scripts/rvm
# Install Ruby
rvm install ruby-$RUBY_VERSION
# Configure database
sudo -u postgres createuser -U postgres vagrant -s
sudo -u postgres createdb -U postgres mastodon_development

View File

@@ -2,7 +2,7 @@
"name": "Mastodon",
"description": "A GNU Social-compatible microblogging server",
"repository": "https://github.com/tootsuite/mastodon",
"logo": "https://github.com/tootsuite.png",
"logo": "https://github.com/tootsuite/mastodon/raw/master/app/assets/images/logo.png",
"env": {
"HEROKU": {
"description": "Leave this as true",

View File

@@ -2,12 +2,9 @@
class AboutController < ApplicationController
before_action :set_body_classes
before_action :set_instance_presenter, only: [:show, :more, :terms]
before_action :set_instance_presenter, only: [:show, :more]
def show
serializable_resource = ActiveModelSerializers::SerializableResource.new(InitialStatePresenter.new(initial_state_params), serializer: InitialStateSerializer)
@initial_state_json = serializable_resource.to_json
end
def show; end
def more; end
@@ -18,7 +15,6 @@ class AboutController < ApplicationController
def new_user
User.new.tap(&:build_account)
end
helper_method :new_user
def set_instance_presenter
@@ -28,11 +24,4 @@ class AboutController < ApplicationController
def set_body_classes
@body_classes = 'about-body'
end
def initial_state_params
{
settings: {},
token: current_session&.token,
}
end
end

View File

@@ -2,83 +2,26 @@
class AccountsController < ApplicationController
include AccountControllerConcern
include SignatureVerification
def show
respond_to do |format|
format.html do
@pinned_statuses = []
if current_account && @account.blocking?(current_account)
@statuses = []
return
end
@pinned_statuses = cache_collection(@account.pinned_statuses, Status) if show_pinned_statuses?
@statuses = filtered_statuses.paginate_by_max_id(20, params[:max_id], params[:since_id])
@statuses = cache_collection(@statuses, Status)
@next_url = next_url unless @statuses.empty?
@statuses = @account.statuses.permitted_for(@account, current_account).paginate_by_max_id(20, params[:max_id], params[:since_id])
@statuses = cache_collection(@statuses, Status)
end
format.atom do
@entries = @account.stream_entries.where(hidden: false).with_includes.paginate_by_max_id(20, params[:max_id], params[:since_id])
render xml: OStatus::AtomSerializer.render(OStatus::AtomSerializer.new.feed(@account, @entries.reject { |entry| entry.status.nil? }))
render xml: AtomSerializer.render(AtomSerializer.new.feed(@account, @entries.to_a))
end
format.json do
render json: @account, serializer: ActivityPub::ActorSerializer, adapter: ActivityPub::Adapter, content_type: 'application/activity+json'
end
format.activitystreams2
end
end
private
def show_pinned_statuses?
[replies_requested?, media_requested?, params[:max_id].present?, params[:since_id].present?].none?
end
def filtered_statuses
default_statuses.tap do |statuses|
statuses.merge!(only_media_scope) if media_requested?
statuses.merge!(no_replies_scope) unless replies_requested?
end
end
def default_statuses
@account.statuses.where(visibility: [:public, :unlisted])
end
def only_media_scope
Status.where(id: account_media_status_ids)
end
def account_media_status_ids
@account.media_attachments.attached.reorder(nil).select(:status_id).distinct
end
def no_replies_scope
Status.without_replies
end
def set_account
@account = Account.find_local!(params[:username])
end
def next_url
if media_requested?
short_account_media_url(@account, max_id: @statuses.last.id)
elsif replies_requested?
short_account_with_replies_url(@account, max_id: @statuses.last.id)
else
short_account_url(@account, max_id: @statuses.last.id)
end
end
def media_requested?
request.path.ends_with?('/media')
end
def replies_requested?
request.path.ends_with?('/with_replies')
end
end

View File

@@ -1,40 +0,0 @@
# frozen_string_literal: true
class ActivityPub::InboxesController < Api::BaseController
include SignatureVerification
before_action :set_account
def create
if signed_request_account
upgrade_account
process_payload
head 201
else
head 202
end
end
private
def set_account
@account = Account.find_local!(params[:account_username]) if params[:account_username]
end
def body
@body ||= request.body.read
end
def upgrade_account
if signed_request_account.ostatus?
signed_request_account.update(last_webfingered_at: nil)
ResolveRemoteAccountWorker.perform_async(signed_request_account.acct)
end
Pubsubhubbub::UnsubscribeWorker.perform_async(signed_request_account.id) if signed_request_account.subscribed?
end
def process_payload
ActivityPub::ProcessingWorker.perform_async(signed_request_account.id, body.force_encoding('UTF-8'))
end
end

View File

@@ -1,27 +0,0 @@
# frozen_string_literal: true
class ActivityPub::OutboxesController < Api::BaseController
before_action :set_account
def show
@statuses = @account.statuses.permitted_for(@account, current_account).paginate_by_max_id(20, params[:max_id], params[:since_id])
@statuses = cache_collection(@statuses, Status)
render json: outbox_presenter, serializer: ActivityPub::CollectionSerializer, adapter: ActivityPub::Adapter, content_type: 'application/activity+json'
end
private
def set_account
@account = Account.find_local!(params[:account_username])
end
def outbox_presenter
ActivityPub::CollectionPresenter.new(
id: account_outbox_url(@account),
type: :ordered,
size: @account.statuses_count,
items: @statuses
)
end
end

View File

@@ -17,13 +17,13 @@ module Admin
end
def unsubscribe
Pubsubhubbub::UnsubscribeWorker.perform_async(@account.id)
UnsubscribeService.new.call(@account)
redirect_to admin_account_path(@account.id)
end
def redownload
@account.reset_avatar!
@account.reset_header!
@account.avatar = @account.avatar_remote_url
@account.header = @account.header_remote_url
@account.save!
redirect_to admin_account_path(@account.id)

View File

@@ -6,26 +6,15 @@ module Admin
@instances = ordered_instances
end
def resubscribe
params.require(:by_domain)
Pubsubhubbub::SubscribeWorker.push_bulk(subscribeable_accounts.pluck(:id))
redirect_to admin_instances_path
end
private
def paginated_instances
Account.remote.by_domain_accounts.page(params[:page])
end
helper_method :paginated_instances
def ordered_instances
paginated_instances.map { |account| Instance.new(account) }
end
def subscribeable_accounts
Account.with_followers.remote.where(domain: params[:by_domain])
end
end
end

View File

@@ -5,14 +5,7 @@ module Admin
include Authorization
before_action :set_report
before_action :set_status, only: [:update, :destroy]
def create
@form = Form::StatusBatch.new(form_status_batch_params)
flash[:alert] = t('admin.statuses.failed_to_execute') unless @form.save
redirect_to admin_report_path(@report)
end
before_action :set_status
def update
@status.update(status_params)
@@ -22,7 +15,7 @@ module Admin
def destroy
authorize @status, :destroy?
RemovalWorker.perform_async(@status.id)
render json: @status
redirect_to admin_report_path(@report)
end
private
@@ -31,10 +24,6 @@ module Admin
params.require(:status).permit(:sensitive)
end
def form_status_batch_params
params.require(:form_status_batch).permit(:action, status_ids: [])
end
def set_report
@report = Report.find(params[:report_id])
end

View File

@@ -8,9 +8,7 @@ module Admin
@reports = filtered_reports.page(params[:page])
end
def show
@form = Form::StatusBatch.new
end
def show; end
def update
process_report

View File

@@ -8,22 +8,13 @@ module Admin
site_title
site_description
site_extended_description
site_terms
open_registrations
closed_registrations_message
open_deletion
timeline_preview
bootstrap_timeline_accounts
).freeze
BOOLEAN_SETTINGS = %w(
open_registrations
open_deletion
timeline_preview
).freeze
BOOLEAN_SETTINGS = %w(open_registrations).freeze
def edit
@admin_settings = Form::AdminSettings.new
@settings = Setting.all_as_records
end
def update
@@ -32,19 +23,19 @@ module Admin
setting.update(value: value_for_update(key, value))
end
flash[:notice] = I18n.t('generic.changes_saved_msg')
flash[:notice] = 'Success!'
redirect_to edit_admin_settings_path
end
private
def settings_params
params.require(:form_admin_settings).permit(ADMIN_SETTINGS)
params.permit(ADMIN_SETTINGS)
end
def value_for_update(key, value)
if BOOLEAN_SETTINGS.include?(key)
value == '1'
value == 'true'
else
value
end

View File

@@ -1,69 +0,0 @@
# frozen_string_literal: true
module Admin
class StatusesController < BaseController
include Authorization
helper_method :current_params
before_action :set_account
before_action :set_status, only: [:update, :destroy]
PER_PAGE = 20
def index
@statuses = @account.statuses
if params[:media]
account_media_status_ids = @account.media_attachments.attached.reorder(nil).select(:status_id).distinct
@statuses.merge!(Status.where(id: account_media_status_ids))
end
@statuses = @statuses.preload(:media_attachments, :mentions).page(params[:page]).per(PER_PAGE)
@form = Form::StatusBatch.new
end
def create
@form = Form::StatusBatch.new(form_status_batch_params)
flash[:alert] = t('admin.statuses.failed_to_execute') unless @form.save
redirect_to admin_account_statuses_path(@account.id, current_params)
end
def update
@status.update(status_params)
redirect_to admin_account_statuses_path(@account.id, current_params)
end
def destroy
authorize @status, :destroy?
RemovalWorker.perform_async(@status.id)
render json: @status
end
private
def status_params
params.require(:status).permit(:sensitive)
end
def form_status_batch_params
params.require(:form_status_batch).permit(:action, status_ids: [])
end
def set_status
@status = @account.statuses.find(params[:id])
end
def set_account
@account = Account.find(params[:account_id])
end
def current_params
page = (params[:page] || 1).to_i
{
media: params[:media],
page: page > 1 && page,
}.select { |_, value| value.present? }
end
end
end

View File

@@ -0,0 +1,27 @@
# frozen_string_literal: true
class Api::ActivityPub::ActivitiesController < Api::BaseController
include Authorization
# before_action :set_follow, only: [:show_follow]
before_action :set_status, only: [:show_status]
respond_to :activitystreams2
# Show a status in AS2 format, as either an Announce (reblog) or a Create (post) activity.
def show_status
authorize @status, :show?
if @status.reblog?
render :show_status_announce
else
render :show_status_create
end
end
private
def set_status
@status = Status.find(params[:id])
end
end

View File

@@ -0,0 +1,19 @@
# frozen_string_literal: true
class Api::ActivityPub::NotesController < Api::BaseController
include Authorization
before_action :set_status
respond_to :activitystreams2
def show
authorize @status, :show?
end
private
def set_status
@status = Status.find(params[:id])
end
end

View File

@@ -0,0 +1,69 @@
# frozen_string_literal: true
class Api::ActivityPub::OutboxController < Api::BaseController
before_action :set_account
respond_to :activitystreams2
def show
if params[:max_id] || params[:since_id]
show_outbox_page
else
show_base_outbox
end
end
private
def show_base_outbox
@statuses = Status.as_outbox_timeline(@account)
@statuses = cache_collection(@statuses)
set_maps(@statuses)
set_first_last_page(@statuses)
render :show
end
def show_outbox_page
all_statuses = Status.as_outbox_timeline(@account)
@statuses = all_statuses.paginate_by_max_id(limit_param(DEFAULT_STATUSES_LIMIT), params[:max_id], params[:since_id])
all_statuses = cache_collection(all_statuses)
@statuses = cache_collection(@statuses)
set_maps(@statuses)
set_first_last_page(all_statuses)
@next_page_url = api_activitypub_outbox_url(pagination_params(max_id: @statuses.last.id)) unless @statuses.empty?
@prev_page_url = api_activitypub_outbox_url(pagination_params(since_id: @statuses.first.id)) unless @statuses.empty?
@paginated = @next_page_url || @prev_page_url
@part_of_url = api_activitypub_outbox_url
set_pagination_headers(@next_page_url, @prev_page_url)
render :show_page
end
def cache_collection(raw)
super(raw, Status)
end
def set_account
@account = Account.find(params[:id])
end
def set_first_last_page(statuses) # rubocop:disable Style/AccessorMethodName
return if statuses.empty?
@first_page_url = api_activitypub_outbox_url(max_id: statuses.first.id + 1)
@last_page_url = api_activitypub_outbox_url(since_id: statuses.last.id - 1)
end
def pagination_params(core_params)
params.permit(:local, :limit).merge(core_params)
end
end

View File

@@ -17,7 +17,11 @@ class Api::BaseController < ApplicationController
render json: { error: 'Record not found' }, status: 404
end
rescue_from HTTP::Error, Mastodon::UnexpectedResponseError do
rescue_from Goldfinger::Error do
render json: { error: 'Remote account could not be resolved' }, status: 422
end
rescue_from HTTP::Error do
render json: { error: 'Remote data could not be fetched' }, status: 503
end
@@ -43,7 +47,7 @@ class Api::BaseController < ApplicationController
links = []
links << [next_path, [%w(rel next)]] if next_path
links << [prev_path, [%w(rel prev)]] if prev_path
response.headers['Link'] = LinkHeader.new(links) unless links.empty?
response.headers['Link'] = LinkHeader.new(links)
end
def limit_param(default_limit)
@@ -62,11 +66,10 @@ class Api::BaseController < ApplicationController
end
def require_user!
if current_user
set_user_activity
else
render json: { error: 'This method requires an authenticated user' }, status: 422
end
current_resource_owner
set_user_activity
rescue ActiveRecord::RecordNotFound
render json: { error: 'This method requires an authenticated user' }, status: 422
end
def render_empty

View File

@@ -4,14 +4,15 @@ class Api::OEmbedController < Api::BaseController
respond_to :json
def show
@status = status_finder.status
render json: @status, serializer: OEmbedSerializer, width: maxwidth_or_default, height: maxheight_or_default
@stream_entry = find_stream_entry.stream_entry
@width = maxwidth_or_default
@height = maxheight_or_default
end
private
def status_finder
StatusFinder.new(params[:url])
def find_stream_entry
StreamEntryFinder.new(params[:url])
end
def maxwidth_or_default

View File

@@ -1,8 +1,6 @@
# frozen_string_literal: true
class Api::PushController < Api::BaseController
include SignatureVerification
def update
response, status = process_push_request
render plain: response, status: status
@@ -13,7 +11,7 @@ class Api::PushController < Api::BaseController
def process_push_request
case hub_mode
when 'subscribe'
Pubsubhubbub::SubscribeService.new.call(account_from_topic, hub_callback, hub_secret, hub_lease_seconds, verified_domain)
Pubsubhubbub::SubscribeService.new.call(account_from_topic, hub_callback, hub_secret, hub_lease_seconds)
when 'unsubscribe'
Pubsubhubbub::UnsubscribeService.new.call(account_from_topic, hub_callback)
else
@@ -59,10 +57,6 @@ class Api::PushController < Api::BaseController
TagManager.instance.web_domain?(hub_topic_domain)
end
def verified_domain
return signed_request_account.domain if signed_request_account
end
def hub_topic_domain
hub_topic_uri.host + (hub_topic_uri.port ? ":#{hub_topic_uri.port}" : '')
end

View File

@@ -42,7 +42,7 @@ class Api::SubscriptionsController < Api::BaseController
end
def lease_seconds_or_default
(params['hub.lease_seconds'] || 1.day).to_i.seconds
(params['hub.lease_seconds'] || 86_400).to_i.seconds
end
def set_account

View File

@@ -1,20 +1,18 @@
# frozen_string_literal: true
class Api::V1::Accounts::CredentialsController < Api::BaseController
before_action -> { doorkeeper_authorize! :read }, except: [:update]
before_action -> { doorkeeper_authorize! :write }, only: [:update]
before_action :require_user!
def show
@account = current_account
render json: @account, serializer: REST::CredentialAccountSerializer
render 'api/v1/accounts/show'
end
def update
current_account.update!(account_params)
@account = current_account
UpdateAccountService.new.call(@account, account_params, raise_error: true)
ActivityPub::UpdateDistributionWorker.perform_async(@account.id)
render json: @account, serializer: REST::CredentialAccountSerializer
render 'api/v1/accounts/show'
end
private

View File

@@ -9,7 +9,7 @@ class Api::V1::Accounts::FollowerAccountsController < Api::BaseController
def index
@accounts = load_accounts
render json: @accounts, each_serializer: REST::AccountSerializer
render 'api/v1/accounts/index'
end
private

View File

@@ -9,7 +9,7 @@ class Api::V1::Accounts::FollowingAccountsController < Api::BaseController
def index
@accounts = load_accounts
render json: @accounts, each_serializer: REST::AccountSerializer
render 'api/v1/accounts/index'
end
private

View File

@@ -8,15 +8,16 @@ class Api::V1::Accounts::RelationshipsController < Api::BaseController
def index
@accounts = Account.where(id: account_ids).select('id')
render json: @accounts, each_serializer: REST::RelationshipSerializer, relationships: relationships
@following = Account.following_map(account_ids, current_user.account_id)
@followed_by = Account.followed_by_map(account_ids, current_user.account_id)
@blocking = Account.blocking_map(account_ids, current_user.account_id)
@muting = Account.muting_map(account_ids, current_user.account_id)
@requested = Account.requested_map(account_ids, current_user.account_id)
@domain_blocking = Account.domain_blocking_map(account_ids, current_user.account_id)
end
private
def relationships
AccountRelationshipsPresenter.new(@accounts, current_user.account_id)
end
def account_ids
@_account_ids ||= Array(params[:id]).map(&:to_i)
end

View File

@@ -8,7 +8,8 @@ class Api::V1::Accounts::SearchController < Api::BaseController
def show
@accounts = account_search
render json: @accounts, each_serializer: REST::AccountSerializer
render 'api/v1/accounts/index'
end
private

View File

@@ -9,7 +9,6 @@ class Api::V1::Accounts::StatusesController < Api::BaseController
def index
@statuses = load_statuses
render json: @statuses, each_serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id)
end
private
@@ -19,7 +18,9 @@ class Api::V1::Accounts::StatusesController < Api::BaseController
end
def load_statuses
cached_account_statuses
cached_account_statuses.tap do |statuses|
set_maps(statuses)
end
end
def cached_account_statuses
@@ -29,7 +30,6 @@ class Api::V1::Accounts::StatusesController < Api::BaseController
def account_statuses
default_statuses.tap do |statuses|
statuses.merge!(only_media_scope) if params[:only_media]
statuses.merge!(pinned_scope) if params[:pinned]
statuses.merge!(no_replies_scope) if params[:exclude_replies]
end
end
@@ -54,10 +54,6 @@ class Api::V1::Accounts::StatusesController < Api::BaseController
@account.media_attachments.attached.reorder(nil).select(:status_id).distinct
end
def pinned_scope
@account.pinned_statuses
end
def no_replies_scope
Status.without_replies
end

View File

@@ -8,41 +8,49 @@ class Api::V1::AccountsController < Api::BaseController
respond_to :json
def show
render json: @account, serializer: REST::AccountSerializer
end
def show; end
def follow
FollowService.new.call(current_user.account, @account.acct)
options = @account.locked? ? {} : { following_map: { @account.id => true }, requested_map: { @account.id => false } }
render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships(options)
set_relationship
render :relationship
end
def block
BlockService.new.call(current_user.account, @account)
render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships
@following = { @account.id => false }
@followed_by = { @account.id => false }
@blocking = { @account.id => true }
@requested = { @account.id => false }
@muting = { @account.id => current_account.muting?(@account.id) }
@domain_blocking = { @account.id => current_account.domain_blocking?(@account.domain) }
render :relationship
end
def mute
MuteService.new.call(current_user.account, @account, notifications: params[:notifications])
render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships
MuteService.new.call(current_user.account, @account)
set_relationship
render :relationship
end
def unfollow
UnfollowService.new.call(current_user.account, @account)
render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships
set_relationship
render :relationship
end
def unblock
UnblockService.new.call(current_user.account, @account)
render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships
set_relationship
render :relationship
end
def unmute
UnmuteService.new.call(current_user.account, @account)
render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships
set_relationship
render :relationship
end
private
@@ -51,7 +59,12 @@ class Api::V1::AccountsController < Api::BaseController
@account = Account.find(params[:id])
end
def relationships(options = {})
AccountRelationshipsPresenter.new([@account.id], current_user.account_id, options)
def set_relationship
@following = Account.following_map([@account.id], current_user.account_id)
@followed_by = Account.followed_by_map([@account.id], current_user.account_id)
@blocking = Account.blocking_map([@account.id], current_user.account_id)
@muting = Account.muting_map([@account.id], current_user.account_id)
@requested = Account.requested_map([@account.id], current_user.account_id)
@domain_blocking = Account.domain_blocking_map([@account.id], current_user.account_id)
end
end

View File

@@ -5,7 +5,6 @@ class Api::V1::AppsController < Api::BaseController
def create
@app = Doorkeeper::Application.create!(application_options)
render json: @app, serializer: REST::ApplicationSerializer
end
private

View File

@@ -9,7 +9,6 @@ class Api::V1::BlocksController < Api::BaseController
def index
@accounts = load_accounts
render json: @accounts, each_serializer: REST::AccountSerializer
end
private

View File

@@ -1,8 +0,0 @@
# frozen_string_literal: true
require 'mastodon/extension'
class Api::V1::ExtensionsController < Api::BaseController
def index
render json: Mastodon::Extension.all
end
end

View File

@@ -9,18 +9,21 @@ class Api::V1::FavouritesController < Api::BaseController
def index
@statuses = load_statuses
render json: @statuses, each_serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id)
end
private
def load_statuses
cached_favourites
cached_favourites.tap do |statuses|
set_maps(statuses)
end
end
def cached_favourites
cache_collection(
Status.reorder(nil).joins(:favourites).merge(results),
Status.where(
id: results.map(&:status_id)
),
Status
)
end

View File

@@ -7,7 +7,6 @@ class Api::V1::FollowRequestsController < Api::BaseController
def index
@accounts = load_accounts
render json: @accounts, each_serializer: REST::AccountSerializer
end
def authorize

View File

@@ -10,13 +10,7 @@ class Api::V1::FollowsController < Api::BaseController
raise ActiveRecord::RecordNotFound if follow_params[:uri].blank?
@account = FollowService.new.call(current_user.account, target_uri).try(:target_account)
if @account.nil?
username, domain = target_uri.split('@')
@account = Account.find_remote!(username, domain)
end
render json: @account, serializer: REST::AccountSerializer
render :show
end
private

View File

@@ -3,7 +3,5 @@
class Api::V1::InstancesController < Api::BaseController
respond_to :json
def show
render json: {}, serializer: REST::InstanceSerializer
end
def show; end
end

View File

@@ -11,7 +11,6 @@ class Api::V1::MediaController < Api::BaseController
def create
@media = current_account.media_attachments.create!(file: media_params[:file])
render json: @media, serializer: REST::MediaAttachmentSerializer
rescue Paperclip::Errors::NotIdentifiedByImageMagickError
render json: file_type_error, status: 422
rescue Paperclip::Error

View File

@@ -8,15 +8,9 @@ class Api::V1::MutesController < Api::BaseController
respond_to :json
def index
@data = @accounts = load_accounts
render json: @accounts, each_serializer: REST::AccountSerializer
@accounts = load_accounts
end
def details
@data = @mutes = load_mutes
render json: @mutes, each_serializer: REST::MuteSerializer
end
private
def load_accounts
@@ -27,10 +21,6 @@ class Api::V1::MutesController < Api::BaseController
Account.includes(:muted_by).references(:muted_by)
end
def load_mutes
paginated_mutes.includes(:account, :target_account).to_a
end
def paginated_mutes
Mute.where(account: current_account).paginate_by_max_id(
limit_param(DEFAULT_ACCOUNTS_LIMIT),
@@ -45,34 +35,26 @@ class Api::V1::MutesController < Api::BaseController
def next_path
if records_continue?
url_for pagination_params(max_id: pagination_max_id)
api_v1_mutes_url pagination_params(max_id: pagination_max_id)
end
end
def prev_path
unless@data.empty?
url_for pagination_params(since_id: pagination_since_id)
unless @accounts.empty?
api_v1_mutes_url pagination_params(since_id: pagination_since_id)
end
end
def pagination_max_id
if params[:action] == "details"
@mutes.last.id
else
@accounts.last.muted_by_ids.last
end
@accounts.last.muted_by_ids.last
end
def pagination_since_id
if params[:action] == "details"
@mutes.first.id
else
@accounts.first.muted_by_ids.first
end
@accounts.first.muted_by_ids.first
end
def records_continue?
@data.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
@accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
end
def pagination_params(core_params)

View File

@@ -11,12 +11,11 @@ class Api::V1::NotificationsController < Api::BaseController
def index
@notifications = load_notifications
render json: @notifications, each_serializer: REST::NotificationSerializer, relationships: StatusRelationshipsPresenter.new(target_statuses_from_notifications, current_user&.account_id)
set_maps_for_notification_target_statuses
end
def show
@notification = current_account.notifications.find(params[:id])
render json: @notification, serializer: REST::NotificationSerializer
end
def clear
@@ -24,20 +23,11 @@ class Api::V1::NotificationsController < Api::BaseController
render_empty
end
def destroy
dismiss
end
def dismiss
current_account.notifications.find_by!(id: params[:id]).destroy!
render_empty
end
def destroy_multiple
current_account.notifications.where(id: params[:ids]).destroy_all
render_empty
end
private
def load_notifications
@@ -56,6 +46,10 @@ class Api::V1::NotificationsController < Api::BaseController
current_account.notifications.browserable(exclude_types)
end
def set_maps_for_notification_target_statuses
set_maps target_statuses_from_notifications
end
def target_statuses_from_notifications
@notifications.reject { |notification| notification.target_status.nil? }.map(&:target_status)
end

View File

@@ -9,7 +9,6 @@ class Api::V1::ReportsController < Api::BaseController
def index
@reports = current_account.reports
render json: @reports, each_serializer: REST::ReportSerializer
end
def create
@@ -18,10 +17,7 @@ class Api::V1::ReportsController < Api::BaseController
status_ids: reported_status_ids,
comment: report_params[:comment]
)
User.admins.includes(:account).each { |u| AdminMailer.new_report(u.account, @report).deliver_later }
render json: @report, serializer: REST::ReportSerializer
render :show
end
private

View File

@@ -1,16 +1,12 @@
# frozen_string_literal: true
class Api::V1::SearchController < Api::BaseController
RESULTS_LIMIT = 10
before_action -> { doorkeeper_authorize! :read }
before_action :require_user!
RESULTS_LIMIT = 5
respond_to :json
def index
@search = Search.new(search_results)
render json: @search, serializer: REST::SearchSerializer
@search = OpenStruct.new(search_results)
end
private

View File

@@ -11,7 +11,7 @@ class Api::V1::Statuses::FavouritedByAccountsController < Api::BaseController
def index
@accounts = load_accounts
render json: @accounts, each_serializer: REST::AccountSerializer
render 'api/v1/statuses/accounts'
end
private

View File

@@ -10,7 +10,7 @@ class Api::V1::Statuses::FavouritesController < Api::BaseController
def create
@status = favourited_status
render json: @status, serializer: REST::StatusSerializer
render 'api/v1/statuses/show'
end
def destroy
@@ -19,7 +19,7 @@ class Api::V1::Statuses::FavouritesController < Api::BaseController
UnfavouriteWorker.perform_async(current_user.account_id, @status.id)
render json: @status, serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new([@status], current_user&.account_id, favourites_map: @favourites_map)
render 'api/v1/statuses/show'
end
private

View File

@@ -14,14 +14,14 @@ class Api::V1::Statuses::MutesController < Api::BaseController
current_account.mute_conversation!(@conversation)
@mutes_map = { @conversation.id => true }
render json: @status, serializer: REST::StatusSerializer
render 'api/v1/statuses/show'
end
def destroy
current_account.unmute_conversation!(@conversation)
@mutes_map = { @conversation.id => false }
render json: @status, serializer: REST::StatusSerializer
render 'api/v1/statuses/show'
end
private

View File

@@ -1,28 +0,0 @@
# frozen_string_literal: true
class Api::V1::Statuses::PinsController < Api::BaseController
include Authorization
before_action -> { doorkeeper_authorize! :write }
before_action :require_user!
before_action :set_status
respond_to :json
def create
StatusPin.create!(account: current_account, status: @status)
render json: @status, serializer: REST::StatusSerializer
end
def destroy
pin = StatusPin.find_by(account: current_account, status: @status)
pin&.destroy!
render json: @status, serializer: REST::StatusSerializer
end
private
def set_status
@status = Status.find(params[:status_id])
end
end

View File

@@ -11,7 +11,7 @@ class Api::V1::Statuses::RebloggedByAccountsController < Api::BaseController
def index
@accounts = load_accounts
render json: @accounts, each_serializer: REST::AccountSerializer
render 'api/v1/statuses/accounts'
end
private

View File

@@ -10,7 +10,7 @@ class Api::V1::Statuses::ReblogsController < Api::BaseController
def create
@status = ReblogService.new.call(current_user.account, status_for_reblog)
render json: @status, serializer: REST::StatusSerializer
render 'api/v1/statuses/show'
end
def destroy
@@ -20,7 +20,7 @@ class Api::V1::Statuses::ReblogsController < Api::BaseController
authorize status_for_destroy, :unreblog?
RemovalWorker.perform_async(status_for_destroy.id)
render json: @status, serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new([@status], current_user&.account_id, reblogs_map: @reblogs_map)
render 'api/v1/statuses/show'
end
private

View File

@@ -13,7 +13,6 @@ class Api::V1::StatusesController < Api::BaseController
def show
cached = Rails.cache.read(@status.cache_key)
@status = cached unless cached.nil?
render json: @status, serializer: REST::StatusSerializer
end
def context
@@ -22,20 +21,15 @@ class Api::V1::StatusesController < Api::BaseController
loaded_ancestors = cache_collection(ancestors_results, Status)
loaded_descendants = cache_collection(descendants_results, Status)
@context = Context.new(ancestors: loaded_ancestors, descendants: loaded_descendants)
statuses = [@status] + @context.ancestors + @context.descendants
@context = OpenStruct.new(ancestors: loaded_ancestors, descendants: loaded_descendants)
statuses = [@status] + @context[:ancestors] + @context[:descendants]
render json: @context, serializer: REST::ContextSerializer, relationships: StatusRelationshipsPresenter.new(statuses, current_user&.account_id)
set_maps(statuses)
end
def card
@card = @status.preview_cards.first
if @card.nil?
render_empty
else
render json: @card, serializer: REST::PreviewCardSerializer
end
@card = PreviewCard.find_by(status: @status)
render_empty if @card.nil?
end
def create
@@ -49,7 +43,7 @@ class Api::V1::StatusesController < Api::BaseController
application: doorkeeper_token.application,
idempotency: request.headers['Idempotency-Key'])
render json: @status, serializer: REST::StatusSerializer
render :show
end
def destroy

View File

@@ -9,13 +9,15 @@ class Api::V1::Timelines::HomeController < Api::BaseController
def show
@statuses = load_statuses
render json: @statuses, each_serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id)
render 'api/v1/timelines/show'
end
private
def load_statuses
cached_home_statuses
cached_home_statuses.tap do |statuses|
set_maps(statuses)
end
end
def cached_home_statuses

View File

@@ -7,13 +7,15 @@ class Api::V1::Timelines::PublicController < Api::BaseController
def show
@statuses = load_statuses
render json: @statuses, each_serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id)
render 'api/v1/timelines/show'
end
private
def load_statuses
cached_public_statuses
cached_public_statuses.tap do |statuses|
set_maps(statuses)
end
end
def cached_public_statuses

View File

@@ -8,7 +8,7 @@ class Api::V1::Timelines::TagController < Api::BaseController
def show
@statuses = load_statuses
render json: @statuses, each_serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id)
render 'api/v1/timelines/show'
end
private
@@ -18,7 +18,9 @@ class Api::V1::Timelines::TagController < Api::BaseController
end
def load_statuses
cached_tagged_statuses
cached_tagged_statuses.tap do |statuses|
set_maps(statuses)
end
end
def cached_tagged_statuses

View File

@@ -1,17 +0,0 @@
# frozen_string_literal: true
class Api::Web::EmbedsController < Api::BaseController
respond_to :json
before_action :require_user!
def create
status = StatusFinder.new(params[:url]).status
render json: status, serializer: OEmbedSerializer, width: 400
rescue ActiveRecord::RecordNotFound
oembed = OEmbed::Providers.get(params[:url])
render json: Oj.dump(oembed.fields)
rescue OEmbed::NotFound
render json: {}, status: :not_found
end
end

View File

@@ -1,52 +0,0 @@
# frozen_string_literal: true
class Api::Web::PushSubscriptionsController < Api::BaseController
respond_to :json
before_action :require_user!
def create
params.require(:subscription).require(:endpoint)
params.require(:subscription).require(:keys).require([:auth, :p256dh])
active_session = current_session
unless active_session.web_push_subscription.nil?
active_session.web_push_subscription.destroy!
active_session.update!(web_push_subscription: nil)
end
# Mobile devices do not support regular notifications, so we enable push notifications by default
alerts_enabled = active_session.detection.device.mobile? || active_session.detection.device.tablet?
data = {
alerts: {
follow: alerts_enabled,
favourite: alerts_enabled,
reblog: alerts_enabled,
mention: alerts_enabled,
},
}
web_subscription = ::Web::PushSubscription.create!(
endpoint: params[:subscription][:endpoint],
key_p256dh: params[:subscription][:keys][:p256dh],
key_auth: params[:subscription][:keys][:auth],
data: data
)
active_session.update!(web_push_subscription: web_subscription)
render json: web_subscription.as_payload
end
def update
params.require([:id, :data])
web_subscription = ::Web::PushSubscription.find(params[:id])
web_subscription.update!(data: params[:data])
render json: web_subscription.as_payload
end
end

View File

@@ -11,7 +11,6 @@ class ApplicationController < ActionController::Base
include UserTrackingConcern
helper_method :current_account
helper_method :current_session
helper_method :single_user_mode?
rescue_from ActionController::RoutingError, with: :not_found
@@ -43,10 +42,6 @@ class ApplicationController < ActionController::Base
forbidden if current_user.account.suspended?
end
def after_sign_out_path_for(_resource_or_scope)
new_user_session_path
end
protected
def forbidden
@@ -73,10 +68,6 @@ class ApplicationController < ActionController::Base
@current_account ||= current_user.try(:account)
end
def current_session
@current_session ||= SessionActivation.find_by(session_id: cookies.signed['_session_id'])
end
def cache_collection(raw, klass)
return raw unless klass.respond_to?(:with_includes)

View File

@@ -2,10 +2,4 @@
class Auth::ConfirmationsController < Devise::ConfirmationsController
layout 'auth'
def show
super do |user|
BootstrapTimelineWorker.perform_async(user.account_id) if user.errors.empty?
end
end
end

View File

@@ -1,20 +1,5 @@
# frozen_string_literal: true
class Auth::PasswordsController < Devise::PasswordsController
before_action :check_validity_of_reset_password_token, only: :edit
layout 'auth'
private
def check_validity_of_reset_password_token
unless reset_password_token_is_valid?
flash[:error] = I18n.t('auth.invalid_reset_password_token')
redirect_to new_password_path(resource_name)
end
end
def reset_password_token_is_valid?
resource_class.with_reset_password_token(params[:reset_password_token]).present?
end
end

View File

@@ -5,7 +5,6 @@ class Auth::RegistrationsController < Devise::RegistrationsController
before_action :check_enabled_registrations, only: [:new, :create]
before_action :configure_sign_up_params, only: [:create]
before_action :set_sessions, only: [:edit, :update]
def destroy
not_found
@@ -42,8 +41,4 @@ class Auth::RegistrationsController < Devise::RegistrationsController
def determine_layout
%w(edit update).include?(action_name) ? 'admin' : 'auth'
end
def set_sessions
@sessions = current_user.session_activations
end
end

View File

@@ -1,7 +1,7 @@
# frozen_string_literal: true
class AuthorizeFollowsController < ApplicationController
layout 'modal'
layout 'public'
before_action :authenticate_user!
@@ -15,7 +15,7 @@ class AuthorizeFollowsController < ApplicationController
if @account.nil?
render :error
else
render :success
redirect_to web_url("accounts/#{@account.id}")
end
rescue ActiveRecord::RecordNotFound, Mastodon::NotPermittedError
render :error

View File

@@ -23,7 +23,6 @@ module AccountControllerConcern
[
webfinger_account_link,
atom_account_url_link,
actor_url_link,
]
)
end
@@ -42,13 +41,6 @@ module AccountControllerConcern
]
end
def actor_url_link
[
ActivityPub::TagManager.instance.uri_for(@account),
[%w(rel alternate), %w(type application/activity+json)],
]
end
def webfinger_account_url
webfinger_url(resource: @account.to_webfinger_s)
end

View File

@@ -1,106 +0,0 @@
# frozen_string_literal: true
# Implemented according to HTTP signatures (Draft 6)
# <https://tools.ietf.org/html/draft-cavage-http-signatures-06>
module SignatureVerification
extend ActiveSupport::Concern
def signed_request?
request.headers['Signature'].present?
end
def signed_request_account
return @signed_request_account if defined?(@signed_request_account)
unless signed_request?
@signed_request_account = nil
return
end
raw_signature = request.headers['Signature']
signature_params = {}
raw_signature.split(',').each do |part|
parsed_parts = part.match(/([a-z]+)="([^"]+)"/i)
next if parsed_parts.nil? || parsed_parts.size != 3
signature_params[parsed_parts[1]] = parsed_parts[2]
end
if incompatible_signature?(signature_params)
@signed_request_account = nil
return
end
account = account_from_key_id(signature_params['keyId'])
if account.nil?
@signed_request_account = nil
return
end
signature = Base64.decode64(signature_params['signature'])
compare_signed_string = build_signed_string(signature_params['headers'])
if account.keypair.public_key.verify(OpenSSL::Digest::SHA256.new, signature, compare_signed_string)
@signed_request_account = account
@signed_request_account
else
@signed_request_account = nil
end
end
def request_body
@request_body ||= request.raw_post
end
private
def build_signed_string(signed_headers)
signed_headers = 'date' if signed_headers.blank?
signed_headers.split(' ').map do |signed_header|
if signed_header == Request::REQUEST_TARGET
"#{Request::REQUEST_TARGET}: #{request.method.downcase} #{request.path}"
elsif signed_header == 'digest'
"digest: #{body_digest}"
else
"#{signed_header}: #{request.headers[to_header_name(signed_header)]}"
end
end.join("\n")
end
def matches_time_window?
begin
time_sent = DateTime.httpdate(request.headers['Date'])
rescue ArgumentError
return false
end
(Time.now.utc - time_sent).abs <= 30
end
def body_digest
"SHA-256=#{Digest::SHA256.base64digest(request_body)}"
end
def to_header_name(name)
name.split(/-/).map(&:capitalize).join('-')
end
def incompatible_signature?(signature_params)
signature_params['keyId'].blank? ||
signature_params['signature'].blank? ||
signature_params['algorithm'].blank? ||
signature_params['algorithm'] != 'rsa-sha256'
end
def account_from_key_id(key_id)
if key_id.start_with?('acct:')
ResolveRemoteAccountService.new.call(key_id.gsub(/\Aacct:/, ''))
elsif !ActivityPub::TagManager.instance.local_uri?(key_id)
account = ActivityPub::TagManager.instance.uri_to_resource(key_id, Account)
account ||= ActivityPub::FetchRemoteKeyService.new.call(key_id)
account
end
end
end

View File

@@ -5,24 +5,5 @@ class FollowerAccountsController < ApplicationController
def index
@follows = Follow.where(target_account: @account).recent.page(params[:page]).per(FOLLOW_PER_PAGE).preload(:account)
respond_to do |format|
format.html
format.json do
render json: collection_presenter, serializer: ActivityPub::CollectionSerializer, adapter: ActivityPub::Adapter, content_type: 'application/activity+json'
end
end
end
private
def collection_presenter
ActivityPub::CollectionPresenter.new(
id: account_followers_url(@account),
type: :ordered,
size: @account.followers_count,
items: @follows.map { |f| ActivityPub::TagManager.instance.uri_for(f.account) }
)
end
end

View File

@@ -5,24 +5,5 @@ class FollowingAccountsController < ApplicationController
def index
@follows = Follow.where(account: @account).recent.page(params[:page]).per(FOLLOW_PER_PAGE).preload(:target_account)
respond_to do |format|
format.html
format.json do
render json: collection_presenter, serializer: ActivityPub::CollectionSerializer, adapter: ActivityPub::Adapter, content_type: 'application/activity+json'
end
end
end
private
def collection_presenter
ActivityPub::CollectionPresenter.new(
id: account_following_index_url(@account),
type: :ordered,
size: @account.following_count,
items: @follows.map { |f| ActivityPub::TagManager.instance.uri_for(f.target_account) }
)
end
end

View File

@@ -2,11 +2,14 @@
class HomeController < ApplicationController
before_action :authenticate_user!
before_action :set_initial_state_json
def index
@body_classes = 'app-body'
@frontend = (params[:frontend] and Rails.configuration.x.available_frontends.include? params[:frontend] + '.js') ? params[:frontend] : 'mastodon'
@body_classes = 'app-body'
@token = find_or_create_access_token.token
@web_settings = Web::Setting.find_by(user: current_user)&.data || {}
@admin = Account.find_local(Setting.site_contact_username)
@streaming_api_base_url = Rails.configuration.x.streaming_api_base_url
@frontend = (params[:frontend] and Rails.configuration.x.available_frontends.include? params[:frontend] + '.js') ? params[:frontend] : 'mastodon'
end
private
@@ -15,18 +18,13 @@ class HomeController < ApplicationController
redirect_to(single_user_mode? ? account_path(Account.first) : about_path) unless user_signed_in?
end
def set_initial_state_json
serializable_resource = ActiveModelSerializers::SerializableResource.new(InitialStatePresenter.new(initial_state_params), serializer: InitialStateSerializer)
@initial_state_json = serializable_resource.to_json
end
def initial_state_params
{
settings: Web::Setting.find_by(user: current_user)&.data || {},
push_subscription: current_account.user.web_push_subscription(current_session),
current_account: current_account,
token: current_session.token,
admin: Account.find_local(Setting.site_contact_username),
}
def find_or_create_access_token
Doorkeeper::AccessToken.find_or_create_for(
Doorkeeper::Application.where(superapp: true).first,
current_user.id,
Doorkeeper::OAuth::Scopes.from_string('read write follow'),
Doorkeeper.configuration.access_token_expires_in,
Doorkeeper.configuration.refresh_token_enabled?
)
end
end

View File

@@ -1,18 +0,0 @@
# frozen_string_literal: true
class IntentsController < ApplicationController
def show
uri = Addressable::URI.parse(params[:uri])
if uri.scheme == 'web+mastodon'
case uri.host
when 'follow'
return redirect_to authorize_follow_path(acct: uri.query_values['uri'].gsub(/\Aacct:/, ''))
when 'share'
return redirect_to share_path(text: uri.query_values['text'])
end
end
not_found
end
end

View File

@@ -1,7 +1,7 @@
# frozen_string_literal: true
class RemoteFollowController < ApplicationController
layout 'modal'
layout 'public'
before_action :set_account
before_action :gone, if: :suspended_account?

View File

@@ -1,72 +0,0 @@
# frozen_string_literal: true
class Settings::ApplicationsController < ApplicationController
layout 'admin'
before_action :authenticate_user!
before_action :set_application, only: [:show, :update, :destroy, :regenerate]
before_action :prepare_scopes, only: [:create, :update]
def index
@applications = current_user.applications.page(params[:page])
end
def new
@application = Doorkeeper::Application.new(
redirect_uri: Doorkeeper.configuration.native_redirect_uri,
scopes: 'read write follow'
)
end
def show; end
def create
@application = current_user.applications.build(application_params)
if @application.save
redirect_to settings_applications_path, notice: I18n.t('applications.created')
else
render :new
end
end
def update
if @application.update(application_params)
redirect_to settings_applications_path, notice: I18n.t('generic.changes_saved_msg')
else
render :show
end
end
def destroy
@application.destroy
redirect_to settings_applications_path, notice: I18n.t('applications.destroyed')
end
def regenerate
@access_token = current_user.token_for_app(@application)
@access_token.destroy
redirect_to settings_application_path(@application), notice: I18n.t('applications.token_regenerated')
end
private
def set_application
@application = current_user.applications.find(params[:id])
end
def application_params
params.require(:doorkeeper_application).permit(
:name,
:redirect_uri,
:scopes,
:website
)
end
def prepare_scopes
scopes = params.fetch(:doorkeeper_application, {}).fetch(:scopes, nil)
params[:doorkeeper_application][:scopes] = scopes.join(' ') if scopes.is_a? Array
end
end

View File

@@ -34,13 +34,9 @@ class Settings::PreferencesController < ApplicationController
def user_settings_params
params.require(:user).permit(
:setting_default_privacy,
:setting_default_sensitive,
:setting_unfollow_modal,
:setting_boost_modal,
:setting_delete_modal,
:setting_auto_play_gif,
:setting_system_font_ui,
:setting_noindex,
notification_emails: %i(follow follow_request reblog favourite mention digest),
interactions: %i(must_be_follower must_be_following)
)

View File

@@ -14,8 +14,7 @@ class Settings::ProfilesController < ApplicationController
def show; end
def update
if UpdateAccountService.new.call(@account, account_params)
ActivityPub::UpdateDistributionWorker.perform_async(@account.id)
if @account.update(account_params)
redirect_to settings_profile_path, notice: I18n.t('generic.changes_saved_msg')
else
render :show

View File

@@ -1,17 +0,0 @@
# frozen_string_literal: true
class Settings::SessionsController < ApplicationController
before_action :set_session, only: :destroy
def destroy
@session.destroy!
flash[:notice] = I18n.t('sessions.revoke_success')
redirect_to edit_user_registration_path
end
private
def set_session
@session = current_user.session_activations.find(params[:id])
end
end

View File

@@ -7,9 +7,7 @@ module Settings
before_action :authenticate_user!
before_action :verify_otp_required, only: [:create]
def show
@confirmation = Form::TwoFactorConfirmation.new
end
def show; end
def create
current_user.otp_secret = User.generate_otp_secret(32)
@@ -18,30 +16,15 @@ module Settings
end
def destroy
if acceptable_code?
current_user.otp_required_for_login = false
current_user.save!
redirect_to settings_two_factor_authentication_path
else
flash.now[:alert] = I18n.t('two_factor_authentication.wrong_code')
@confirmation = Form::TwoFactorConfirmation.new
render :show
end
current_user.otp_required_for_login = false
current_user.save!
redirect_to settings_two_factor_authentication_path
end
private
def confirmation_params
params.require(:form_two_factor_confirmation).permit(:code)
end
def verify_otp_required
redirect_to settings_two_factor_authentication_path if current_user.otp_required_for_login?
end
def acceptable_code?
current_user.validate_and_consume_otp!(confirmation_params[:code]) ||
current_user.invalidate_otp_backup_code!(confirmation_params[:code])
end
end
end

View File

@@ -1,30 +0,0 @@
# frozen_string_literal: true
class SharesController < ApplicationController
layout 'modal'
before_action :authenticate_user!
before_action :set_body_classes
def show
serializable_resource = ActiveModelSerializers::SerializableResource.new(InitialStatePresenter.new(initial_state_params), serializer: InitialStateSerializer)
@initial_state_json = serializable_resource.to_json
end
private
def initial_state_params
{
settings: Web::Setting.find_by(user: current_user)&.data || {},
push_subscription: current_account.user.web_push_subscription(current_session),
current_account: current_account,
token: current_session.token,
admin: Account.find_local(Setting.site_contact_username),
text: params[:text],
}
end
def set_body_classes
@body_classes = 'compose-standalone'
end
end

View File

@@ -9,30 +9,12 @@ class StatusesController < ApplicationController
before_action :set_status
before_action :set_link_headers
before_action :check_account_suspension
before_action :redirect_to_original, only: [:show]
def show
respond_to do |format|
format.html do
@ancestors = @status.reply? ? cache_collection(@status.ancestors(current_account), Status) : []
@descendants = cache_collection(@status.descendants(current_account), Status)
@ancestors = @status.reply? ? cache_collection(@status.ancestors(current_account), Status) : []
@descendants = cache_collection(@status.descendants(current_account), Status)
render 'stream_entries/show'
end
format.json do
render json: @status, serializer: ActivityPub::NoteSerializer, adapter: ActivityPub::Adapter, content_type: 'application/activity+json'
end
end
end
def activity
render json: @status, serializer: ActivityPub::ActivitySerializer, adapter: ActivityPub::Adapter, content_type: 'application/activity+json'
end
def embed
response.headers['X-Frame-Options'] = 'ALLOWALL'
render 'stream_entries/embed', layout: 'embedded'
render 'stream_entries/show'
end
private
@@ -42,12 +24,7 @@ class StatusesController < ApplicationController
end
def set_link_headers
response.headers['Link'] = LinkHeader.new(
[
[account_stream_entry_url(@account, @status.stream_entry, format: 'atom'), [%w(rel alternate), %w(type application/atom+xml)]],
[ActivityPub::TagManager.instance.uri_for(@status), [%w(rel alternate), %w(type application/activity+json)]],
]
)
response.headers['Link'] = LinkHeader.new([[account_stream_entry_url(@account, @status.stream_entry, format: 'atom'), [%w(rel alternate), %w(type application/atom+xml)]]])
end
def set_status
@@ -64,8 +41,4 @@ class StatusesController < ApplicationController
def check_account_suspension
gone if @account.suspended?
end
def redirect_to_original
redirect_to ::TagManager.instance.url_for(@status.reblog) if @status.reblog?
end
end

View File

@@ -2,7 +2,6 @@
class StreamEntriesController < ApplicationController
include Authorization
include SignatureVerification
layout 'public'
@@ -19,13 +18,16 @@ class StreamEntriesController < ApplicationController
end
format.atom do
render xml: OStatus::AtomSerializer.render(OStatus::AtomSerializer.new.entry(@stream_entry, true))
render xml: AtomSerializer.render(AtomSerializer.new.entry(@stream_entry, true))
end
end
end
def embed
redirect_to embed_short_account_status_url(@account, @stream_entry.activity), status: 301
response.headers['X-Frame-Options'] = 'ALLOWALL'
return gone if @stream_entry.activity.nil?
render layout: 'embedded'
end
private
@@ -35,12 +37,7 @@ class StreamEntriesController < ApplicationController
end
def set_link_headers
response.headers['Link'] = LinkHeader.new(
[
[account_stream_entry_url(@account, @stream_entry, format: 'atom'), [%w(rel alternate), %w(type application/atom+xml)]],
[ActivityPub::TagManager.instance.uri_for(@stream_entry.activity), [%w(rel alternate), %w(type application/activity+json)]],
]
)
response.headers['Link'] = LinkHeader.new([[account_stream_entry_url(@account, @stream_entry, format: 'atom'), [%w(rel alternate), %w(type application/atom+xml)]]])
end
def set_stream_entry

View File

@@ -5,26 +5,7 @@ class TagsController < ApplicationController
def show
@tag = Tag.find_by!(name: params[:id].downcase)
@statuses = Status.as_tag_timeline(@tag, current_account, params[:local]).paginate_by_max_id(20, params[:max_id])
@statuses = @tag.nil? ? [] : Status.as_tag_timeline(@tag, current_account, params[:local]).paginate_by_max_id(20, params[:max_id])
@statuses = cache_collection(@statuses, Status)
respond_to do |format|
format.html
format.json do
render json: collection_presenter, serializer: ActivityPub::CollectionSerializer, adapter: ActivityPub::Adapter, content_type: 'application/activity+json'
end
end
end
private
def collection_presenter
ActivityPub::CollectionPresenter.new(
id: tag_url(@tag),
type: :ordered,
size: @tag.statuses.count,
items: @statuses.map { |s| ActivityPub::TagManager.instance.uri_for(s) }
)
end
end

View File

@@ -0,0 +1,8 @@
# frozen_string_literal: true
module Activitystreams2BuilderHelper
# Gets a usable name for an account, using display name or username.
def account_name(account)
account.display_name.presence || account.username
end
end

View File

@@ -6,21 +6,15 @@ module Admin::FilterHelper
FILTERS = ACCOUNT_FILTERS + REPORT_FILTERS
def filter_link_to(text, link_to_params, link_class_params = link_to_params)
new_url = filtered_url_for(link_to_params)
new_class = filtered_url_for(link_class_params)
link_to text, new_url, class: filter_link_class(new_class)
def filter_link_to(text, more_params)
new_url = filtered_url_for(more_params)
link_to text, new_url, class: filter_link_class(new_url)
end
def table_link_to(icon, text, path, options = {})
link_to safe_join([fa_icon(icon), text]), path, options.merge(class: 'table-action-link')
end
def selected?(more_params)
new_url = filtered_url_for(more_params)
filter_link_class(new_url) == 'selected' ? true : false
end
private
def filter_params(more_params)

View File

@@ -5,10 +5,6 @@ module ApplicationHelper
current_page?(path) ? 'active' : ''
end
def active_link_to(label, path, options = {})
link_to label, path, options.merge(class: active_nav_class(path))
end
def show_landing_strip?
!user_signed_in? && !single_user_mode?
end
@@ -35,11 +31,7 @@ module ApplicationHelper
Rails.env.production? ? site_title : "#{site_title} (Dev)"
end
def fa_icon(icon, attributes = {})
class_names = attributes[:class]&.split(' ') || []
class_names << 'fa'
class_names += icon.split(' ').map { |cl| "fa-#{cl}" }
content_tag(:i, nil, attributes.merge(class: class_names.join(' ')))
def fa_icon(icon)
content_tag(:i, nil, class: 'fa ' + icon.split(' ').map { |cl| "fa-#{cl}" }.join(' '))
end
end

View File

@@ -1,24 +0,0 @@
# frozen_string_literal: true
module EmojiHelper
def emojify(text)
return text if text.blank?
text.gsub(emoji_pattern) do |match|
emoji = Emoji.instance.unicode($1) # rubocop:disable Style/PerlBackrefs
if emoji
emoji
else
match
end
end
end
def emoji_pattern
@emoji_pattern ||=
/(?<=[^[:alnum:]:]|\n|^)
(#{Emoji.instance.names.map { |name| Regexp.escape(name) }.join('|')})
(?=[^[:alnum:]:]|$)/x
end
end

View File

@@ -0,0 +1,17 @@
# frozen_string_literal: true
module HttpHelper
def http_client(options = {})
timeout = { write: 10, connect: 10, read: 10 }.merge(options)
HTTP.headers(user_agent: user_agent)
.timeout(:per_operation, timeout)
.follow
end
private
def user_agent
@user_agent ||= "#{HTTP::Request::USER_AGENT} (Mastodon/#{Mastodon::Version}; +http://#{Rails.configuration.x.local_domain}/)"
end
end

View File

@@ -2,7 +2,7 @@
module InstanceHelper
def site_title
Setting.site_title.presence || site_hostname
Setting.site_title.to_s
end
def site_hostname

View File

@@ -1,52 +0,0 @@
# frozen_string_literal: true
module JsonLdHelper
def equals_or_includes?(haystack, needle)
haystack.is_a?(Array) ? haystack.include?(needle) : haystack == needle
end
def first_of_value(value)
value.is_a?(Array) ? value.first : value
end
def value_or_id(value)
value.is_a?(String) || value.nil? ? value : value['id']
end
def supported_context?(json)
!json.nil? && equals_or_includes?(json['@context'], ActivityPub::TagManager::CONTEXT)
end
def canonicalize(json)
graph = RDF::Graph.new << JSON::LD::API.toRdf(json)
graph.dump(:normalize)
end
def fetch_resource(uri)
response = build_request(uri).perform
return if response.code != 200
body_to_json(response.to_s)
end
def body_to_json(body)
body.is_a?(String) ? Oj.load(body, mode: :strict) : body
rescue Oj::ParseError
nil
end
def merge_context(context, new_context)
if context.is_a?(Array)
context << new_context
else
[context, new_context]
end
end
private
def build_request(uri)
request = Request.new(:get, uri)
request.add_headers('Accept' => 'application/activity+json, application/ld+json')
request
end
end

View File

@@ -11,15 +11,7 @@ module RoutingHelper
end
end
def full_asset_url(source, options = {})
source = ActionController::Base.helpers.asset_url(source, options) unless use_storage?
URI.join(root_url, source).to_s
end
private
def use_storage?
Rails.configuration.x.use_s3 || Rails.configuration.x.use_swift
def full_asset_url(source)
Rails.configuration.x.use_s3 ? source : URI.join(root_url, ActionController::Base.helpers.asset_url(source)).to_s
end
end

View File

@@ -19,7 +19,6 @@ module SettingsHelper
io: 'Ido',
it: 'Italiano',
ja: '日本語',
ko: '한국어',
nl: 'Nederlands',
no: 'Norsk',
oc: 'Occitan',
@@ -30,7 +29,6 @@ module SettingsHelper
th: 'ภาษาไทย',
tr: 'Türkçe',
uk: 'Українська',
zh: '中文',
'zh-CN': '简体中文',
'zh-HK': '繁體中文(香港)',
'zh-TW': '繁體中文(臺灣)',
@@ -40,23 +38,7 @@ module SettingsHelper
HUMAN_LOCALES[locale]
end
def filterable_languages
I18n.available_locales.map { |locale| locale.to_s.split('-').first.to_sym }.uniq
end
def hash_to_object(hash)
HashObject.new(hash)
end
def session_device_icon(session)
device = session.detection.device
if device.mobile?
'mobile'
elsif device.tablet?
'tablet'
else
'desktop'
end
end
end

View File

@@ -1,7 +1,7 @@
# frozen_string_literal: true
module StreamEntriesHelper
EMBEDDED_CONTROLLER = 'statuses'
EMBEDDED_CONTROLLER = 'stream_entries'
EMBEDDED_ACTION = 'embed'
def display_name(account)

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