Merge branch 'master' into feature-circles
This commit is contained in:
commit
824d1b8893
906 changed files with 37100 additions and 11600 deletions
|
@ -1,4 +1,3 @@
|
|||
https://github.com/heroku/heroku-buildpack-apt
|
||||
https://github.com/Scalingo/ffmpeg-buildpack
|
||||
https://github.com/Scalingo/nodejs-buildpack
|
||||
https://github.com/Scalingo/ruby-buildpack
|
||||
|
|
|
@ -27,10 +27,10 @@ plugins:
|
|||
enabled: true
|
||||
eslint:
|
||||
enabled: true
|
||||
channel: eslint-6
|
||||
channel: eslint-7
|
||||
rubocop:
|
||||
enabled: true
|
||||
channel: rubocop-0-82
|
||||
channel: rubocop-0-92
|
||||
sass-lint:
|
||||
enabled: true
|
||||
exclude_patterns:
|
||||
|
|
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
name: Bug Report
|
||||
about: If something isn't working as expected
|
||||
|
||||
labels: bug
|
||||
---
|
||||
|
||||
<!-- Make sure that you are submitting a new bug that was not previously reported or already fixed -->
|
||||
|
|
1
.github/ISSUE_TEMPLATE/feature_request.md
vendored
1
.github/ISSUE_TEMPLATE/feature_request.md
vendored
|
@ -1,7 +1,6 @@
|
|||
---
|
||||
name: Feature Request
|
||||
about: I have a suggestion
|
||||
|
||||
---
|
||||
|
||||
<!-- Please use a concise and distinct title for the issue -->
|
||||
|
|
4
.github/dependabot.yml
vendored
4
.github/dependabot.yml
vendored
|
@ -11,7 +11,7 @@ updates:
|
|||
interval: weekly
|
||||
open-pull-requests-limit: 99
|
||||
allow:
|
||||
- dependency-type: all
|
||||
- dependency-type: direct
|
||||
|
||||
- package-ecosystem: bundler
|
||||
directory: "/"
|
||||
|
@ -19,4 +19,4 @@ updates:
|
|||
interval: weekly
|
||||
open-pull-requests-limit: 99
|
||||
allow:
|
||||
- dependency-type: all
|
||||
- dependency-type: direct
|
||||
|
|
185
.rubocop.yml
185
.rubocop.yml
|
@ -25,30 +25,68 @@ Layout/AccessModifierIndentation:
|
|||
Layout/EmptyLineAfterMagicComment:
|
||||
Enabled: false
|
||||
|
||||
Layout/EmptyLineAfterGuardClause:
|
||||
Enabled: false
|
||||
|
||||
Layout/EmptyLinesAroundAttributeAccessor:
|
||||
Enabled: true
|
||||
|
||||
Layout/HashAlignment:
|
||||
Enabled: false
|
||||
# EnforcedHashRocketStyle: table
|
||||
# EnforcedColonStyle: table
|
||||
|
||||
Layout/SpaceAroundMethodCallOperator:
|
||||
Enabled: true
|
||||
|
||||
Layout/SpaceInsideHashLiteralBraces:
|
||||
EnforcedStyle: space
|
||||
|
||||
Lint/DeprecatedOpenSSLConstant:
|
||||
Enabled: true
|
||||
|
||||
Lint/DuplicateElsifCondition:
|
||||
Enabled: true
|
||||
|
||||
Lint/MixedRegexpCaptureTypes:
|
||||
Enabled: true
|
||||
|
||||
Lint/RaiseException:
|
||||
Enabled: true
|
||||
|
||||
Lint/StructNewOverride:
|
||||
Enabled: true
|
||||
|
||||
Lint/UselessAccessModifier:
|
||||
ContextCreatingMethods:
|
||||
- class_methods
|
||||
|
||||
Metrics/AbcSize:
|
||||
Max: 100
|
||||
Exclude:
|
||||
- 'lib/mastodon/*_cli.rb'
|
||||
|
||||
Metrics/BlockLength:
|
||||
Max: 35
|
||||
Max: 55
|
||||
Exclude:
|
||||
- 'lib/tasks/**/*'
|
||||
- 'lib/mastodon/*_cli.rb'
|
||||
|
||||
Metrics/BlockNesting:
|
||||
Max: 3
|
||||
Exclude:
|
||||
- 'lib/mastodon/*_cli.rb'
|
||||
|
||||
Metrics/ClassLength:
|
||||
CountComments: false
|
||||
Max: 300
|
||||
Max: 400
|
||||
Exclude:
|
||||
- 'lib/mastodon/*_cli.rb'
|
||||
|
||||
Metrics/CyclomaticComplexity:
|
||||
Max: 25
|
||||
Exclude:
|
||||
- 'lib/mastodon/*_cli.rb'
|
||||
|
||||
Layout/LineLength:
|
||||
AllowURI: true
|
||||
|
@ -56,7 +94,9 @@ Layout/LineLength:
|
|||
|
||||
Metrics/MethodLength:
|
||||
CountComments: false
|
||||
Max: 55
|
||||
Max: 65
|
||||
Exclude:
|
||||
- 'lib/mastodon/*_cli.rb'
|
||||
|
||||
Metrics/ModuleLength:
|
||||
CountComments: false
|
||||
|
@ -67,34 +107,90 @@ Metrics/ParameterLists:
|
|||
CountKeywordArgs: true
|
||||
|
||||
Metrics/PerceivedComplexity:
|
||||
Max: 20
|
||||
Max: 25
|
||||
|
||||
Naming/MemoizedInstanceVariableName:
|
||||
Enabled: false
|
||||
|
||||
Naming/MethodParameterName:
|
||||
Enabled: true
|
||||
|
||||
Rails:
|
||||
Enabled: true
|
||||
|
||||
Rails/ApplicationController:
|
||||
Enabled: false
|
||||
Exclude:
|
||||
- 'app/controllers/well_known/**/*.rb'
|
||||
|
||||
Rails/BelongsTo:
|
||||
Enabled: false
|
||||
|
||||
Rails/ContentTag:
|
||||
Enabled: false
|
||||
|
||||
Rails/EnumHash:
|
||||
Enabled: false
|
||||
|
||||
Rails/HasAndBelongsToMany:
|
||||
Enabled: false
|
||||
|
||||
Rails/SkipsModelValidations:
|
||||
Enabled: false
|
||||
|
||||
Rails/HttpStatus:
|
||||
Enabled: false
|
||||
|
||||
Rails/Exit:
|
||||
Exclude:
|
||||
- 'lib/mastodon/*'
|
||||
- 'lib/cli.rb'
|
||||
|
||||
Rails/FilePath:
|
||||
Enabled: false
|
||||
|
||||
Rails/HasAndBelongsToMany:
|
||||
Enabled: false
|
||||
|
||||
Rails/HasManyOrHasOneDependent:
|
||||
Enabled: false
|
||||
|
||||
Rails/HelperInstanceVariable:
|
||||
Enabled: false
|
||||
|
||||
Rails/HttpStatus:
|
||||
Enabled: false
|
||||
|
||||
Rails/IndexBy:
|
||||
Enabled: false
|
||||
|
||||
Rails/InverseOf:
|
||||
Enabled: false
|
||||
|
||||
Rails/LexicallyScopedActionFilter:
|
||||
Enabled: false
|
||||
|
||||
Rails/OutputSafety:
|
||||
Enabled: true
|
||||
|
||||
Rails/RakeEnvironment:
|
||||
Enabled: false
|
||||
|
||||
Rails/RedundantForeignKey:
|
||||
Enabled: false
|
||||
|
||||
Rails/SkipsModelValidations:
|
||||
Enabled: false
|
||||
|
||||
Rails/UniqueValidationWithoutIndex:
|
||||
Enabled: false
|
||||
|
||||
Style/AccessorGrouping:
|
||||
Enabled: true
|
||||
|
||||
Style/AccessModifierDeclarations:
|
||||
Enabled: false
|
||||
|
||||
Style/ArrayCoercion:
|
||||
Enabled: true
|
||||
|
||||
Style/BisectedAttrAccessor:
|
||||
Enabled: true
|
||||
|
||||
Style/CaseLikeIf:
|
||||
Enabled: false
|
||||
|
||||
Style/ClassAndModuleChildren:
|
||||
Enabled: false
|
||||
|
||||
|
@ -109,6 +205,15 @@ Style/Documentation:
|
|||
Style/DoubleNegation:
|
||||
Enabled: true
|
||||
|
||||
Style/ExpandPathArguments:
|
||||
Enabled: false
|
||||
|
||||
Style/ExponentialNotation:
|
||||
Enabled: true
|
||||
|
||||
Style/FormatString:
|
||||
Enabled: false
|
||||
|
||||
Style/FormatStringToken:
|
||||
Enabled: false
|
||||
|
||||
|
@ -118,9 +223,33 @@ Style/FrozenStringLiteralComment:
|
|||
Style/GuardClause:
|
||||
Enabled: false
|
||||
|
||||
Style/HashAsLastArrayItem:
|
||||
Enabled: false
|
||||
|
||||
Style/HashEachMethods:
|
||||
Enabled: true
|
||||
|
||||
Style/HashLikeCase:
|
||||
Enabled: true
|
||||
|
||||
Style/HashTransformKeys:
|
||||
Enabled: true
|
||||
|
||||
Style/HashTransformValues:
|
||||
Enabled: false
|
||||
|
||||
Style/IfUnlessModifier:
|
||||
Enabled: false
|
||||
|
||||
Style/InverseMethods:
|
||||
Enabled: false
|
||||
|
||||
Style/Lambda:
|
||||
Enabled: false
|
||||
|
||||
Style/MutableConstant:
|
||||
Enabled: false
|
||||
|
||||
Style/PercentLiteralDelimiters:
|
||||
PreferredDelimiters:
|
||||
'%i': '()'
|
||||
|
@ -129,9 +258,36 @@ Style/PercentLiteralDelimiters:
|
|||
Style/PerlBackrefs:
|
||||
AutoCorrect: false
|
||||
|
||||
Style/RedundantAssignment:
|
||||
Enabled: false
|
||||
|
||||
Style/RedundantFetchBlock:
|
||||
Enabled: true
|
||||
|
||||
Style/RedundantFileExtensionInRequire:
|
||||
Enabled: true
|
||||
|
||||
Style/RedundantRegexpCharacterClass:
|
||||
Enabled: false
|
||||
|
||||
Style/RedundantRegexpEscape:
|
||||
Enabled: false
|
||||
|
||||
Style/RedundantReturn:
|
||||
Enabled: true
|
||||
|
||||
Style/RegexpLiteral:
|
||||
Enabled: false
|
||||
|
||||
Style/RescueStandardError:
|
||||
Enabled: false
|
||||
|
||||
Style/SignalException:
|
||||
Enabled: false
|
||||
|
||||
Style/SlicingWithRange:
|
||||
Enabled: true
|
||||
|
||||
Style/SymbolArray:
|
||||
Enabled: false
|
||||
|
||||
|
@ -140,3 +296,6 @@ Style/TrailingCommaInArrayLiteral:
|
|||
|
||||
Style/TrailingCommaInHashLiteral:
|
||||
EnforcedStyleForMultiline: 'comma'
|
||||
|
||||
Style/UnpackFirst:
|
||||
Enabled: false
|
||||
|
|
|
@ -1 +1 @@
|
|||
2.6.6
|
||||
2.7.2
|
||||
|
|
569
AUTHORS.md
569
AUTHORS.md
|
@ -5,38 +5,39 @@ Mastodon is available on [GitHub](https://github.com/tootsuite/mastodon)
|
|||
and provided thanks to the work of the following contributors:
|
||||
|
||||
* [Gargron](https://github.com/Gargron)
|
||||
* [dependabot-preview[bot]](https://github.com/apps/dependabot-preview)
|
||||
* [ThibG](https://github.com/ThibG)
|
||||
* [ykzts](https://github.com/ykzts)
|
||||
* [dependabot-preview[bot]](https://github.com/apps/dependabot-preview)
|
||||
* [dependabot[bot]](https://github.com/apps/dependabot)
|
||||
* [ykzts](https://github.com/ykzts)
|
||||
* [akihikodaki](https://github.com/akihikodaki)
|
||||
* [mjankowski](https://github.com/mjankowski)
|
||||
* [unarist](https://github.com/unarist)
|
||||
* [yiskah](https://github.com/yiskah)
|
||||
* [nolanlawson](https://github.com/nolanlawson)
|
||||
* [abcang](https://github.com/abcang)
|
||||
* [ysksn](https://github.com/ysksn)
|
||||
* [mayaeh](https://github.com/mayaeh)
|
||||
* [ysksn](https://github.com/ysksn)
|
||||
* [sorin-davidoi](https://github.com/sorin-davidoi)
|
||||
* [noellabo](https://github.com/noellabo)
|
||||
* [lynlynlynx](https://github.com/lynlynlynx)
|
||||
* [m4sk1n](mailto:me@m4sk.in)
|
||||
* [Marcin Mikołajczak](mailto:me@m4sk.in)
|
||||
* [Kjwon15](https://github.com/Kjwon15)
|
||||
* [noellabo](https://github.com/noellabo)
|
||||
* [renatolond](https://github.com/renatolond)
|
||||
* [alpaca-tc](https://github.com/alpaca-tc)
|
||||
* [jeroenpraat](https://github.com/jeroenpraat)
|
||||
* [nclm](https://github.com/nclm)
|
||||
* [ineffyble](https://github.com/ineffyble)
|
||||
* [shleeable](https://github.com/shleeable)
|
||||
* [zunda](https://github.com/zunda)
|
||||
* [shleeable](https://github.com/shleeable)
|
||||
* [Masoud Abkenar](mailto:ampbox@gmail.com)
|
||||
* [blackle](https://github.com/blackle)
|
||||
* [Quent-in](https://github.com/Quent-in)
|
||||
* [JantsoP](https://github.com/JantsoP)
|
||||
* [nullkal](https://github.com/nullkal)
|
||||
* [yookoala](https://github.com/yookoala)
|
||||
* [Sasha-Sorokin](https://github.com/Sasha-Sorokin)
|
||||
* [Brawaru](https://github.com/Brawaru)
|
||||
* [ariasuni](https://github.com/ariasuni)
|
||||
* [Aditoo17](https://github.com/Aditoo17)
|
||||
* [Quenty31](https://github.com/Quenty31)
|
||||
* [marek-lach](https://github.com/marek-lach)
|
||||
|
@ -45,9 +46,9 @@ and provided thanks to the work of the following contributors:
|
|||
* [danhunsaker](https://github.com/danhunsaker)
|
||||
* [eramdam](https://github.com/eramdam)
|
||||
* [takayamaki](https://github.com/takayamaki)
|
||||
* [ariasuni](https://github.com/ariasuni)
|
||||
* [masarakki](https://github.com/masarakki)
|
||||
* [ticky](https://github.com/ticky)
|
||||
* [trwnh](https://github.com/trwnh)
|
||||
* [ThisIsMissEm](https://github.com/ThisIsMissEm)
|
||||
* [hinaloe](https://github.com/hinaloe)
|
||||
* [hcmiya](https://github.com/hcmiya)
|
||||
|
@ -57,10 +58,10 @@ and provided thanks to the work of the following contributors:
|
|||
* [yukimochi](https://github.com/yukimochi)
|
||||
* [palindromordnilap](https://github.com/palindromordnilap)
|
||||
* [rkarabut](https://github.com/rkarabut)
|
||||
* [trwnh](https://github.com/trwnh)
|
||||
* [nightpool](https://github.com/nightpool)
|
||||
* [Artoria2e5](https://github.com/Artoria2e5)
|
||||
* [marrus-sh](https://github.com/marrus-sh)
|
||||
* [dunn](https://github.com/dunn)
|
||||
* [krainboltgreene](https://github.com/krainboltgreene)
|
||||
* [pfigel](https://github.com/pfigel)
|
||||
* [BoFFire](https://github.com/BoFFire)
|
||||
|
@ -84,25 +85,25 @@ and provided thanks to the work of the following contributors:
|
|||
* [ashleyhull-versent](https://github.com/ashleyhull-versent)
|
||||
* [yhirano55](https://github.com/yhirano55)
|
||||
* [rinsuki](https://github.com/rinsuki)
|
||||
* [dunn](https://github.com/dunn)
|
||||
* [devkral](https://github.com/devkral)
|
||||
* [camponez](https://github.com/camponez)
|
||||
* [hugogameiro](https://github.com/hugogameiro)
|
||||
* [SerCom_KC](mailto:szescxz@gmail.com)
|
||||
* [aschmitz](https://github.com/aschmitz)
|
||||
* [mfmfuyu](https://github.com/mfmfuyu)
|
||||
* [kedamaDQ](https://github.com/kedamaDQ)
|
||||
* [fpiesche](https://github.com/fpiesche)
|
||||
* [gandaro](https://github.com/gandaro)
|
||||
* [johnsudaar](https://github.com/johnsudaar)
|
||||
* [trebmuh](https://github.com/trebmuh)
|
||||
* [rmhasan](https://github.com/rmhasan)
|
||||
* [kedamaDQ](https://github.com/kedamaDQ)
|
||||
* [lindwurm](https://github.com/lindwurm)
|
||||
* [victorhck](mailto:victorhck@geeko.site)
|
||||
* [voidsatisfaction](https://github.com/voidsatisfaction)
|
||||
* [mkljczk](https://github.com/mkljczk)
|
||||
* [hikari-no-yume](https://github.com/hikari-no-yume)
|
||||
* [seefood](https://github.com/seefood)
|
||||
* [jackjennings](https://github.com/jackjennings)
|
||||
* [mfmfuyu](https://github.com/mfmfuyu)
|
||||
* [puckipedia](https://github.com/puckipedia)
|
||||
* [spla](mailto:spla@mastodont.cat)
|
||||
* [walf443](https://github.com/walf443)
|
||||
|
@ -111,14 +112,15 @@ and provided thanks to the work of the following contributors:
|
|||
* [Ashley](mailto:expenses@airmail.cc)
|
||||
* [xqus](https://github.com/xqus)
|
||||
* [pfm-eyesightjp](https://github.com/pfm-eyesightjp)
|
||||
* [Samy KACIMI](mailto:samy.kacimi@gmail.com)
|
||||
* [fakenine](https://github.com/fakenine)
|
||||
* [tsuwatch](https://github.com/tsuwatch)
|
||||
* [victorhck](https://github.com/victorhck)
|
||||
* [mkljczk](https://github.com/mkljczk)
|
||||
* [manuelviens](https://github.com/manuelviens)
|
||||
* [tateisu](https://github.com/tateisu)
|
||||
* [fvh-P](https://github.com/fvh-P)
|
||||
* [rtucker](https://github.com/rtucker)
|
||||
* [Anna e só](mailto:contraexemplos@gmail.com)
|
||||
* [dariusk](https://github.com/dariusk)
|
||||
* [kazu9su](https://github.com/kazu9su)
|
||||
* [Komic](https://github.com/Komic)
|
||||
* [lmorchard](https://github.com/lmorchard)
|
||||
|
@ -145,9 +147,9 @@ and provided thanks to the work of the following contributors:
|
|||
* [fhemberger](https://github.com/fhemberger)
|
||||
* [Gomasy](https://github.com/Gomasy)
|
||||
* [greysteil](https://github.com/greysteil)
|
||||
* [hencatsmith](https://github.com/hencatsmith)
|
||||
* [hendotcat](https://github.com/hendotcat)
|
||||
* [d6rkaiz](https://github.com/d6rkaiz)
|
||||
* [Reverite](https://github.com/Reverite)
|
||||
* [ladyisatis](https://github.com/ladyisatis)
|
||||
* [JohnD28](https://github.com/JohnD28)
|
||||
* [znz](https://github.com/znz)
|
||||
* [saper](https://github.com/saper)
|
||||
|
@ -160,14 +162,14 @@ and provided thanks to the work of the following contributors:
|
|||
* [leopku](https://github.com/leopku)
|
||||
* [SansPseudoFix](https://github.com/SansPseudoFix)
|
||||
* [spla](mailto:sp@mastodont.cat)
|
||||
* [tateisu](https://github.com/tateisu)
|
||||
* [tomfhowe](https://github.com/tomfhowe)
|
||||
* [noraworld](https://github.com/noraworld)
|
||||
* [lfuelling](https://github.com/lfuelling)
|
||||
* [theboss](https://github.com/theboss)
|
||||
* [aji-su](https://github.com/aji-su)
|
||||
* [nzws](https://github.com/nzws)
|
||||
* [duxovni](https://github.com/duxovni)
|
||||
* [smorimoto](https://github.com/smorimoto)
|
||||
* [mashirozx](https://github.com/mashirozx)
|
||||
* [178inaba](https://github.com/178inaba)
|
||||
* [acid-chicken](https://github.com/acid-chicken)
|
||||
* [xgess](https://github.com/xgess)
|
||||
|
@ -175,7 +177,6 @@ and provided thanks to the work of the following contributors:
|
|||
* [aablinov](https://github.com/aablinov)
|
||||
* [stalker314314](https://github.com/stalker314314)
|
||||
* [cutls](https://github.com/cutls)
|
||||
* [dariusk](https://github.com/dariusk)
|
||||
* [huertanix](https://github.com/huertanix)
|
||||
* [eleboucher](https://github.com/eleboucher)
|
||||
* [halkeye](https://github.com/halkeye)
|
||||
|
@ -183,7 +184,7 @@ and provided thanks to the work of the following contributors:
|
|||
* [treby](https://github.com/treby)
|
||||
* [jpdevries](https://github.com/jpdevries)
|
||||
* [gdpelican](https://github.com/gdpelican)
|
||||
* [kmichl](https://github.com/kmichl)
|
||||
* [Korbinian](mailto:kontakt@korbinian-michl.de)
|
||||
* [Kurtis Rainbolt-Greene](mailto:me@kurtisrainboltgreene.name)
|
||||
* [panarom](https://github.com/panarom)
|
||||
* [Dar13](https://github.com/Dar13)
|
||||
|
@ -225,6 +226,7 @@ and provided thanks to the work of the following contributors:
|
|||
* [aaribaud](https://github.com/aaribaud)
|
||||
* [pointlessone](https://github.com/pointlessone)
|
||||
* [Andrew](mailto:andrewlchronister@gmail.com)
|
||||
* [arielrodrigues](https://github.com/arielrodrigues)
|
||||
* [aurelien-reeves](https://github.com/aurelien-reeves)
|
||||
* [elegaanz](https://github.com/elegaanz)
|
||||
* [estuans](https://github.com/estuans)
|
||||
|
@ -238,6 +240,7 @@ and provided thanks to the work of the following contributors:
|
|||
* [muffinista](https://github.com/muffinista)
|
||||
* [cdutson](https://github.com/cdutson)
|
||||
* [farlistener](https://github.com/farlistener)
|
||||
* [divergentdave](https://github.com/divergentdave)
|
||||
* [DavidLibeau](https://github.com/DavidLibeau)
|
||||
* [dmerejkowsky](https://github.com/dmerejkowsky)
|
||||
* [ddevault](https://github.com/ddevault)
|
||||
|
@ -276,7 +279,7 @@ and provided thanks to the work of the following contributors:
|
|||
* [xPaw](https://github.com/xPaw)
|
||||
* [petzah](https://github.com/petzah)
|
||||
* [ignisf](https://github.com/ignisf)
|
||||
* [raymestalez](https://github.com/raymestalez)
|
||||
* [lumenwrites](https://github.com/lumenwrites)
|
||||
* [remram44](https://github.com/remram44)
|
||||
* [sts10](https://github.com/sts10)
|
||||
* [SuperSandro2000](https://github.com/SuperSandro2000)
|
||||
|
@ -286,8 +289,9 @@ and provided thanks to the work of the following contributors:
|
|||
* [Sir-Boops](https://github.com/Sir-Boops)
|
||||
* [stemid](https://github.com/stemid)
|
||||
* [sumdog](https://github.com/sumdog)
|
||||
* [OmmyZhang](https://github.com/OmmyZhang)
|
||||
* [ThomasLeister](https://github.com/ThomasLeister)
|
||||
* [mcat-ee](https://github.com/mcat-ee)
|
||||
* [Tom McAtee](mailto:a1608768@student.adelaide.edu.au)
|
||||
* [tototoshi](https://github.com/tototoshi)
|
||||
* [TrashMacNugget](https://github.com/TrashMacNugget)
|
||||
* [VirtuBox](https://github.com/VirtuBox)
|
||||
|
@ -314,11 +318,13 @@ and provided thanks to the work of the following contributors:
|
|||
* [matsurai25](https://github.com/matsurai25)
|
||||
* [mecab](https://github.com/mecab)
|
||||
* [nicobz25](https://github.com/nicobz25)
|
||||
* [niwatori24](https://github.com/niwatori24)
|
||||
* [oliverkeeble](https://github.com/oliverkeeble)
|
||||
* [partev](https://github.com/partev)
|
||||
* [pinfort](https://github.com/pinfort)
|
||||
* [rbaumert](https://github.com/rbaumert)
|
||||
* [rhoio](https://github.com/rhoio)
|
||||
* [santiagorodriguez96](https://github.com/santiagorodriguez96)
|
||||
* [sclaire-1](https://github.com/sclaire-1)
|
||||
* [umonaca](https://github.com/umonaca)
|
||||
* [usagi-f](https://github.com/usagi-f)
|
||||
|
@ -327,7 +333,7 @@ and provided thanks to the work of the following contributors:
|
|||
* [wxcafe](https://github.com/wxcafe)
|
||||
* [Grawl](https://github.com/Grawl)
|
||||
* [新都心(Neet Shin)](mailto:nucx@dio-vox.com)
|
||||
* [clarfon](https://github.com/clarfon)
|
||||
* [clarfonthey](https://github.com/clarfonthey)
|
||||
* [cygnan](https://github.com/cygnan)
|
||||
* [Awea](https://github.com/Awea)
|
||||
* [eai04191](https://github.com/eai04191)
|
||||
|
@ -358,11 +364,11 @@ and provided thanks to the work of the following contributors:
|
|||
* [schas002](https://github.com/schas002)
|
||||
* [contraexemplo](https://github.com/contraexemplo)
|
||||
* [abackstrom](https://github.com/abackstrom)
|
||||
* [arielrodrigues](https://github.com/arielrodrigues)
|
||||
* [orlea](https://github.com/orlea)
|
||||
* [armandfardeau](https://github.com/armandfardeau)
|
||||
* [raboof](https://github.com/raboof)
|
||||
* [jumbosushi](https://github.com/jumbosushi)
|
||||
* [acuteaura](https://github.com/acuteaura)
|
||||
* [ayumin](https://github.com/ayumin)
|
||||
* [bzg](https://github.com/bzg)
|
||||
* [BastienDurel](https://github.com/BastienDurel)
|
||||
|
@ -389,7 +395,7 @@ and provided thanks to the work of the following contributors:
|
|||
* [colindean](https://github.com/colindean)
|
||||
* [DeeUnderscore](https://github.com/DeeUnderscore)
|
||||
* [dachinat](https://github.com/dachinat)
|
||||
* [shapeshifter-system](https://github.com/shapeshifter-system)
|
||||
* [monsterpit-firedemon](https://github.com/monsterpit-firedemon)
|
||||
* [watilde](https://github.com/watilde)
|
||||
* [daprice](https://github.com/daprice)
|
||||
* [da2x](https://github.com/da2x)
|
||||
|
@ -400,14 +406,13 @@ and provided thanks to the work of the following contributors:
|
|||
* [singingwolfboy](https://github.com/singingwolfboy)
|
||||
* [caldwell](https://github.com/caldwell)
|
||||
* [davidcelis](https://github.com/davidcelis)
|
||||
* [divergentdave](https://github.com/divergentdave)
|
||||
* [davefp](https://github.com/davefp)
|
||||
* [yipdw](https://github.com/yipdw)
|
||||
* [debanshuk](https://github.com/debanshuk)
|
||||
* [mascali33](https://github.com/mascali33)
|
||||
* [DerekNonGeneric](https://github.com/DerekNonGeneric)
|
||||
* [dblandin](https://github.com/dblandin)
|
||||
* [Drew Gates](mailto:aranaur@users.noreply.github.com)
|
||||
* [Aranaur](https://github.com/Aranaur)
|
||||
* [dtschust](https://github.com/dtschust)
|
||||
* [Dryusdan](https://github.com/Dryusdan)
|
||||
* [d3vgru](https://github.com/d3vgru)
|
||||
|
@ -451,22 +456,25 @@ and provided thanks to the work of the following contributors:
|
|||
* [J Yeary](mailto:usbsnowcrash@users.noreply.github.com)
|
||||
* [jack-michaud](https://github.com/jack-michaud)
|
||||
* [Floppy](https://github.com/Floppy)
|
||||
* [loomchild](https://github.com/loomchild)
|
||||
* [jglauche](https://github.com/jglauche)
|
||||
* [jenkr55](https://github.com/jenkr55)
|
||||
* [hyenagirl64](https://github.com/hyenagirl64)
|
||||
* [press5](https://github.com/press5)
|
||||
* [TrollDecker](https://github.com/TrollDecker)
|
||||
* [jmontane](https://github.com/jmontane)
|
||||
* [Jarek Lipski](mailto:pub@loomchild.net)
|
||||
* [Jennifer Glauche](mailto:=^.^=@github19.jglauche.de)
|
||||
* [Jennifer Kruse](mailto:jenkr55@gmail.com)
|
||||
* [Jeremy Rose](mailto:nornagon@nornagon.net)
|
||||
* [Jessica](mailto:46502909+hyenagirl64@users.noreply.github.com)
|
||||
* [Jessica K. Litwin](mailto:jessica@litw.in)
|
||||
* [Jo Decker](mailto:trolldecker@users.noreply.github.com)
|
||||
* [Joan Montané](mailto:jmontane@users.noreply.github.com)
|
||||
* [Jonathan Klee](mailto:klee.jonathan@gmail.com)
|
||||
* [Jordan Guerder](mailto:jguerder@fr.pulseheberg.net)
|
||||
* [Joseph Mingrone](mailto:jehops@users.noreply.github.com)
|
||||
* [Josh Leeb-du Toit](mailto:mail@joshleeb.com)
|
||||
* [Joshua Wood](mailto:josh@joshuawood.net)
|
||||
* [Julien](mailto:tiwy57@users.noreply.github.com)
|
||||
* [Julien Deswaef](mailto:juego@requiem4tv.com)
|
||||
* [June Sallou](mailto:jnsll@users.noreply.github.com)
|
||||
* [Jérémy Benoist](mailto:j0k3r@users.noreply.github.com)
|
||||
* [KEINOS](mailto:github@keinos.com)
|
||||
* [Kairui Song | 宋恺睿](mailto:ryncsn@gmail.com)
|
||||
* [Keiji Matsuzaki](mailto:futoase@gmail.com)
|
||||
* [Kevin Liu](mailto:kevin@potatofrom.space)
|
||||
* [Kit Redgrave](mailto:qwertyitis@gmail.com)
|
||||
|
@ -482,7 +490,6 @@ and provided thanks to the work of the following contributors:
|
|||
* [Lukas Burk](mailto:jemus42@users.noreply.github.com)
|
||||
* [Manato Kameya](mailto:grabacr07+github@gmail.com)
|
||||
* [Mantas](mailto:mistermantas@users.noreply.github.com)
|
||||
* [Marcin Mikołajczak](mailto:me@mkljczk.pl)
|
||||
* [Mareena Kunjachan](mailto:mareenakunjachan@gmail.com)
|
||||
* [Marek Lach](mailto:marek.brohatwack.lach@gmail.com)
|
||||
* [Markus R](mailto:wirehack7@users.noreply.github.com)
|
||||
|
@ -529,10 +536,12 @@ and provided thanks to the work of the following contributors:
|
|||
* [Norayr Chilingarian](mailto:norayr@arnet.am)
|
||||
* [Noëlle Anthony](mailto:noelle.d.anthony@gmail.com)
|
||||
* [N氏](mailto:uenok.htc@gmail.com)
|
||||
* [OSAMU SATO](mailto:satosamu@gmail.com)
|
||||
* [Olivier Nicole](mailto:olivierthnicole@gmail.com)
|
||||
* [Oskari Noppa](mailto:noppa@users.noreply.github.com)
|
||||
* [Otakan](mailto:otakan951@gmail.com)
|
||||
* [Padraig Fahy](mailto:tech@padraigfahy.com)
|
||||
* [Patrice Ferlet](mailto:metal3d@gmail.com)
|
||||
* [PatrickRWells](mailto:32802366+patrickrwells@users.noreply.github.com)
|
||||
* [Paul](mailto:naydex.mc+github@gmail.com)
|
||||
* [Pete Keen](mailto:pete@petekeen.net)
|
||||
|
@ -574,7 +583,6 @@ and provided thanks to the work of the following contributors:
|
|||
* [TakesxiSximada](mailto:takesxi.sximada@gmail.com)
|
||||
* [Tao Bror Bojlén](mailto:brortao@users.noreply.github.com)
|
||||
* [Taras Gogol](mailto:taras2358@gmail.com)
|
||||
* [Tdxdxoz](mailto:tdxdxoz@gmail.com)
|
||||
* [TheInventrix](mailto:theinventrix@users.noreply.github.com)
|
||||
* [TheMainOne](mailto:50847364+theevilskeleton@users.noreply.github.com)
|
||||
* [Thomas Alberola](mailto:thomas@needacoffee.fr)
|
||||
|
@ -594,6 +602,7 @@ and provided thanks to the work of the following contributors:
|
|||
* [Wesley Ellis](mailto:tahnok@gmail.com)
|
||||
* [Wiktor](mailto:wiktor@metacode.biz)
|
||||
* [Wonderfall](mailto:wonderfall@schrodinger.io)
|
||||
* [Y.Yamashiro](mailto:shukukei@mojizuri.jp)
|
||||
* [YDrogen](mailto:ydrogen45@gmail.com)
|
||||
* [YMHuang](mailto:ymhuang@fmbase.tw)
|
||||
* [YOSHIOKA Eiichiro](mailto:yoshioka.eiichiro@gmail.com)
|
||||
|
@ -638,6 +647,7 @@ and provided thanks to the work of the following contributors:
|
|||
* [jumoru](mailto:jumoru@mailbox.org)
|
||||
* [kaiyou](mailto:pierre@jaury.eu)
|
||||
* [karlyeurl](mailto:karl.yeurl@gmail.com)
|
||||
* [kawaguchi](mailto:jiikko@users.noreply.github.com)
|
||||
* [kedama](mailto:32974885+kedamadq@users.noreply.github.com)
|
||||
* [kuro5hin](mailto:rusty@kuro5hin.org)
|
||||
* [leo60228](mailto:leo@60228.dev)
|
||||
|
@ -655,6 +665,7 @@ and provided thanks to the work of the following contributors:
|
|||
* [notozeki](mailto:notozeki@users.noreply.github.com)
|
||||
* [ntl-purism](mailto:57806346+ntl-purism@users.noreply.github.com)
|
||||
* [nzws](mailto:git-yuzu@svk.jp)
|
||||
* [proxy](mailto:51172302+3n-k1@users.noreply.github.com)
|
||||
* [rch850](mailto:rich850@gmail.com)
|
||||
* [roikale](mailto:roikale@users.noreply.github.com)
|
||||
* [rysiekpl](mailto:rysiek@hackerspace.pl)
|
||||
|
@ -694,122 +705,414 @@ This document is provided for informational purposes only. Since it is only upda
|
|||
|
||||
Following people have contributed to translation of Mastodon:
|
||||
|
||||
- Zoltán Gera (*Hungarian*)
|
||||
- Kristijan Tkalec (*Slovenian*)
|
||||
- Evert Prants (*Estonian*)
|
||||
- ᏦᏁᎢᎵᏫ 😷 (KNTRO) (*Spanish, Argentina*)
|
||||
- Sveinn í Felli (sveinki) (*Icelandic*)
|
||||
- qezwan (*Persian, Sorani (Kurdish)*)
|
||||
- Hồ Nhất Duy (kantcer) (*Vietnamese*)
|
||||
- taicv (*Vietnamese*)
|
||||
- Zoltán Gera (gerazo) (*Hungarian*)
|
||||
- ButterflyOfFire (BoFFire) (*French, Arabic, Kabyle*)
|
||||
- adrmzz (*Sardinian*)
|
||||
- Ramdziana F Y (rafeyu) (*Indonesian*)
|
||||
- Evert Prants (IcyDiamond) (*Estonian*)
|
||||
- Daniele Lira Mereb (danilmereb) (*Portuguese, Brazilian*)
|
||||
- Xosé M. (XoseM) (*Spanish, Galician*)
|
||||
- Kristijan Tkalec (lapor) (*Slovenian*)
|
||||
- stan ionut (stanionut12) (*Romanian*)
|
||||
- Besnik_b (*Albanian*)
|
||||
- Emanuel Pina (emanuelpina) (*Portuguese*)
|
||||
- Thai Localization (thl10n) (*Thai*)
|
||||
- 奈卜拉 (nebula_moe) (*Chinese Simplified*)
|
||||
- Jeong Arm (Kjwon15) (*Japanese, Korean, Esperanto*)
|
||||
- Michal Stanke (mstanke) (*Czech*)
|
||||
- Alix Rossi (palindromordnilap) (*French, Corsican*)
|
||||
- spla (*Spanish, Catalan*)
|
||||
- Imre Kristoffer Eilertsen (DandelionSprout) (*Norwegian*)
|
||||
- Jeroen (jeroenpraat) (*Dutch*)
|
||||
- borys_sh (*Ukrainian*)
|
||||
- ButterflyOfFire (*Arabic; French*)
|
||||
- Osoitz (*Basque*)
|
||||
- oɹʇuʞ (*Spanish, Argentina*)
|
||||
- Miguel Mayol (mitcoes) (*Spanish, Catalan*)
|
||||
- Danial Behzadi (danialbehzadi) (*Persian*)
|
||||
- yeft (*Chinese Traditional, Chinese Traditional, Hong Kong*)
|
||||
- koyu (*German*)
|
||||
- Jeroen (*Dutch*)
|
||||
- Muha Aliss (*Turkish*)
|
||||
- 唐宗勛 (*Chinese Simplified*)
|
||||
- Jeong Arm (*Korean; Esperanto; Japanese*)
|
||||
- Oguz Ersen (*Turkish*)
|
||||
- spla (*Catalan*)
|
||||
- Ramdziana F Y (*Indonesian*)
|
||||
- Aditoo17 (*Czech*)
|
||||
- Xosé M. (*Galician*)
|
||||
- Roboron (*Spanish*)
|
||||
- Alix Rossi (*Corsican; French*)
|
||||
- Maya Minatsuki (*Japanese*)
|
||||
- Masoud Abkenar (*Persian*)
|
||||
- Thai Localization (*Thai*)
|
||||
- Marek Ľach (*Slovak; Polish*)
|
||||
- d5Ziif3K (*Ukrainian*)
|
||||
- Koala Yeung (yookoala) (*Chinese Traditional, Hong Kong*)
|
||||
- Osoitz (*Basque*)
|
||||
- Peterandre (*Norwegian, Norwegian Nynorsk*)
|
||||
- tzium (*Sardinian*)
|
||||
- Iváns (Ivans_translator) (*Galician*)
|
||||
- Sasha Sorokin (Sasha-Sorokin) (*French, Catalan, Danish, German, Greek, Hungarian, Armenian, Korean, Russian, Albanian, Swedish, Ukrainian, Vietnamese, Galician*)
|
||||
- kamee (*Armenian*)
|
||||
- tolstoevsky (*Russian*)
|
||||
- enolp (*Asturian*)
|
||||
- FédiQuébec (manuelviens) (*French*)
|
||||
- lamnatos (*Greek*)
|
||||
- Emyn Nant Nefydd (*Welsh*)
|
||||
- Maya Minatsuki (mayaeh) (*Japanese*)
|
||||
- Masoud Abkenar (mabkenar) (*Persian*)
|
||||
- Alessandro Levati (Oct326) (*Italian*)
|
||||
- arshat (*Kazakh*)
|
||||
- Roboron (*Spanish*)
|
||||
- ariasuni (*French, Arabic, Czech, German, Greek, Hungarian, Slovenian, Ukrainian, Chinese Simplified, Portuguese, Brazilian, Persian, Norwegian Nynorsk, Esperanto, Breton, Corsican, Sardinian, Kabyle*)
|
||||
- Ali Demirtaş (alidemirtas) (*Turkish*)
|
||||
- Em St Cenydd (cancennau) (*Welsh*)
|
||||
- Marek Ľach (mareklach) (*Polish, Slovak*)
|
||||
- Muha Aliss (muhaaliss) (*Turkish*)
|
||||
- Jurica (ahjk) (*Croatian*)
|
||||
- Aditoo17 (*Czech*)
|
||||
- Diluns (*Occitan*)
|
||||
- atarashiako (*Chinese Simplified*)
|
||||
- 101010 (*Polish*)
|
||||
- Yi-Jyun Pan (*Chinese Traditional*)
|
||||
- silkevicious (*Italian*)
|
||||
- FédiQuébec (*French*)
|
||||
- Jaz-Michael King (*Welsh*)
|
||||
- christalleras (*Norwegian Nynorsk*)
|
||||
- tykayn (*French*)
|
||||
- Alessandro Levati (*Italian*)
|
||||
- carolinagiorno (*Portuguese, Brazilian*)
|
||||
- taoxvx (*Danish*)
|
||||
- sabri (*Spanish*)
|
||||
- Sasha Sorokin (*Russian*)
|
||||
- shioko (*Chinese Simplified*)
|
||||
- Evgeny Petrov (*Russian*)
|
||||
- ariasuni (*French; Esperanto*)
|
||||
- Tiago Epifânio (*Portuguese*)
|
||||
- dxwc (*Bengali*)
|
||||
- liffon (*Swedish*)
|
||||
- Vanege (*Esperanto*)
|
||||
- Johan Schiff (*Swedish*)
|
||||
- kat (*Ukrainian; Russian*)
|
||||
- oti4500 (*Hungarian; Ukrainian*)
|
||||
- Juan José Salvador Piedra (*Spanish*)
|
||||
- diazepan (*Spanish*)
|
||||
- SHeija (*Finnish*)
|
||||
- Jack R (*Spanish*)
|
||||
- gagik_ (*Armenian*)
|
||||
- vishnuvaratharajan (*Tamil*)
|
||||
- Marcin Mikołajczak (mkljczkk) (*Czech, Polish, Russian*)
|
||||
- regulartranslator (*Portuguese, Brazilian*)
|
||||
- Akarshan Biswas (biswasab) (*Bengali, Sanskrit*)
|
||||
- Yi-Jyun Pan (pan93412) (*Chinese Traditional*)
|
||||
- d5Ziif3K (*Ukrainian*)
|
||||
- GiorgioHerbie (*Italian*)
|
||||
- Rafael H L Moretti (Moretti) (*Portuguese, Brazilian*)
|
||||
- Saederup92 (*Danish*)
|
||||
- Stasiek Michalski (*Polish*)
|
||||
- Dewi (*Breton; French*)
|
||||
- cybergene (*Japanese*)
|
||||
- AW Unad (*Indonesian*)
|
||||
- Andrea Lo Iacono (*Italian*)
|
||||
- Ray (*Spanish*)
|
||||
- Unmual (*Spanish*)
|
||||
- Ryo (*Korean*)
|
||||
- juanda097 (*Spanish*)
|
||||
- Anunnakey (*Macedonian*)
|
||||
- Cutls (*Japanese*)
|
||||
- erikstl (*Esperanto*)
|
||||
- ruine (*Japanese*)
|
||||
- MadeInSteak (*Finnish*)
|
||||
- Sokratis Alichanidis (*Greek*)
|
||||
- dragnucs2 (*Arabic*)
|
||||
- frumble (*German*)
|
||||
- Rikard Linde (*Swedish*)
|
||||
- christalleras (*Norwegian Nynorsk*)
|
||||
- cybergene (cyber-gene) (*Japanese*)
|
||||
- Taloran (*Norwegian Nynorsk*)
|
||||
- ThibG (*French, Icelandic*)
|
||||
- xatier (*Chinese Traditional*)
|
||||
- otrapersona (*Spanish, Spanish, Mexico*)
|
||||
- atarashiako (*Chinese Simplified*)
|
||||
- 101010 (101010pl) (*Polish*)
|
||||
- silkevicious (*Italian*)
|
||||
- Floxu (fredrikdim1) (*Norwegian Nynorsk*)
|
||||
- Bertil Hedkvist (Berrahed) (*Swedish*)
|
||||
- William(ѕ)ⁿ (wmlgr) (*Spanish*)
|
||||
- norayr (*Armenian*)
|
||||
- Tiago Epifânio (tfve) (*Portuguese*)
|
||||
- Ryo (DrRyo) (*Korean*)
|
||||
- Mentor Gashi (mentorgashi.com) (*Albanian*)
|
||||
- Jaz-Michael King (jazmichaelking) (*Welsh*)
|
||||
- carolinagiorno (*Portuguese, Brazilian*)
|
||||
- Roby Thomas (roby.thomas) (*Malayalam*)
|
||||
- Bharat Kumar (Marwari) (*Hindi*)
|
||||
- ThonyVezbe (*Breton*)
|
||||
- dkdarshan760 (*Sanskrit*)
|
||||
- Tagomago (tagomago) (*French, Spanish*)
|
||||
- tykayn (*French*)
|
||||
- axi (*Finnish*)
|
||||
- Selyan Slimane AMIRI (slimane_AMIRI) (*Kabyle*)
|
||||
- Balázs Meskó (mesko.balazs) (*Hungarian*)
|
||||
- taoxvx (*Danish*)
|
||||
- Hrach Mkrtchyan (mhrach87) (*Armenian*)
|
||||
- sabri (thetomatoisavegetable) (*Spanish, Spanish, Argentina*)
|
||||
- Dewi (Unkorneg) (*French, Breton*)
|
||||
- Coelacanthus (*Chinese Simplified*)
|
||||
- syncopams (*Chinese Simplified, Chinese Traditional, Chinese Traditional, Hong Kong*)
|
||||
- SteinarK (*Norwegian Nynorsk*)
|
||||
- Sokratis Alichanidis (alichani) (*Greek*)
|
||||
- Mathias B. Vagnes (vagnes) (*Norwegian*)
|
||||
- dashersyed (*Urdu (Pakistan)*)
|
||||
- Acolyte (666noob404) (*Ukrainian*)
|
||||
- Conight Wang (xfddwhh) (*Chinese Simplified*)
|
||||
- liffon (*Swedish*)
|
||||
- Damjan Dimitrioski (gnud) (*Macedonian*)
|
||||
- PPNplus (*Thai*)
|
||||
- shioko (*Chinese Simplified*)
|
||||
- v4vachan (*Malayalam*)
|
||||
- Hakim Oubouali (zenata1) (*Standard Moroccan Tamazight*)
|
||||
- Evgeny Petrov (kondra007) (*Russian*)
|
||||
- Gwenn (Belvar) (*Breton*)
|
||||
- StanleyFrew (*French*)
|
||||
- Hayk Khachatryan (brutusromanus123) (*Armenian*)
|
||||
- jaranta (*Finnish*)
|
||||
- Felicia (midsommar) (*Swedish*)
|
||||
- Denys (dector) (*Ukrainian*)
|
||||
- Pukima (pukimaaa) (*German*)
|
||||
- Vanege (*Esperanto*)
|
||||
- Jess Rafn (therealyez) (*Danish*)
|
||||
- strubbl (*German*)
|
||||
- Stasiek Michalski (hellcp) (*Polish*)
|
||||
- dxwc (*Bengali*)
|
||||
- jmontane (*Catalan*)
|
||||
- Liboide (*Spanish*)
|
||||
- Johan Schiff (schyffel) (*Swedish*)
|
||||
- Arunmozhi (tecoholic) (*Tamil*)
|
||||
- kat (katktv) (*Russian, Ukrainian*)
|
||||
- Rikard Linde (rikardlinde) (*Swedish*)
|
||||
- oti4500 (*Hungarian, Ukrainian*)
|
||||
- Laura (selfisekai) (*Polish*)
|
||||
- Rachida S. (ZiriSut) (*Kabyle*)
|
||||
- diazepan (*Spanish, Spanish, Argentina*)
|
||||
- marzuquccen (*Kabyle*)
|
||||
- Juan José Salvador Piedra (JuanjoSalvador) (*Spanish*)
|
||||
- Tigran (tigransimonyan) (*Armenian*)
|
||||
- BurekzFinezt (*Serbian (Cyrillic)*)
|
||||
- SHeija (*Finnish*)
|
||||
- atriix (*Swedish*)
|
||||
- Jack R (isaac.97_WT) (*Spanish*)
|
||||
- antonyho (*Chinese Traditional, Hong Kong*)
|
||||
- andruhov (*Russian, Ukrainian*)
|
||||
- Aryamik Sharma (Aryamik) (*Swedish, Hindi*)
|
||||
- phena109 (*Chinese Traditional, Hong Kong*)
|
||||
- 森の子リスのミーコの大冒険 (Phroneris) (*Japanese*)
|
||||
- るいーね (ruine) (*Japanese*)
|
||||
- ahangarha (*Persian*)
|
||||
- Sam Tux (imahbub) (*Bengali*)
|
||||
- igordrozniak (*Polish*)
|
||||
- Unmual (*Spanish*)
|
||||
- Isaac Huang (caasih) (*Chinese Traditional*)
|
||||
- AW Unad (awcodify) (*Indonesian*)
|
||||
- Allen Zhong (AstroProfundis) (*Chinese Simplified*)
|
||||
- Cutls (cutls) (*Japanese*)
|
||||
- Ray (Ipsumry) (*Spanish*)
|
||||
- Falling Snowdin (tghgg) (*Vietnamese*)
|
||||
- coxde (*Chinese Simplified*)
|
||||
- Rasmus Lindroth (RasmusLindroth) (*Swedish*)
|
||||
- Andrea Lo Iacono (niels0n) (*Italian*)
|
||||
- Kinshuk Sunil (kinshuksunil) (*Hindi*)
|
||||
- Ullas Joseph (ullasjoseph) (*Malayalam*)
|
||||
- Goudarz Jafari (Goudarz) (*Persian*)
|
||||
- Yu-Pai Liu (tedliou) (*Chinese Traditional*)
|
||||
- Amarin Cemthong (acitmaster) (*Thai*)
|
||||
- juanda097 (juanda-097) (*Spanish*)
|
||||
- Anunnakey (*Macedonian*)
|
||||
- fragola (*Italian*)
|
||||
- erikstl (*Esperanto*)
|
||||
- twpenguin (*Chinese Traditional*)
|
||||
- bobchao (*Chinese Traditional*)
|
||||
- Esther (esthermations) (*Portuguese*)
|
||||
- MadeInSteak (*Finnish*)
|
||||
- Heimen Stoffels (vistausss) (*Dutch*)
|
||||
- Rajarshi Guha (rajarshiguha) (*Bengali*)
|
||||
- Andrew (iAndrew3) (*Romanian*)
|
||||
- Gopal Sharma (gopalvirat) (*Hindi*)
|
||||
- arethsu (*Swedish*)
|
||||
- EPEMA YT (*German*)
|
||||
- Rhys Harrison (*Esperanto*)
|
||||
- KEINOS (*Japanese*)
|
||||
- Tofiq Abdula (Xwla) (*Sorani (Kurdish)*)
|
||||
- Carlos Solís (csolisr) (*Esperanto*)
|
||||
- Parthan S Ramanujam (parthan) (*Tamil*)
|
||||
- Kasper Nymand (KasperNymand) (*Danish*)
|
||||
- TS (morte) (*Finnish*)
|
||||
- subram (*Turkish*)
|
||||
- SensDeViata (*Ukrainian*)
|
||||
- Ptrcmd (ptrcmd) (*Chinese Traditional*)
|
||||
- SergioFMiranda (*Portuguese, Brazilian*)
|
||||
- Scvoet (scvoet) (*Chinese Simplified*)
|
||||
- hiroTS (*Chinese Traditional*)
|
||||
- johne32rus23 (*Russian*)
|
||||
- AzureNya (*Chinese Simplified*)
|
||||
- OctolinGamer (octolingamer) (*Portuguese, Brazilian*)
|
||||
- Ram varma (ram4varma) (*Tamil*)
|
||||
- Hexandcube (hexandcube) (*Polish*)
|
||||
- 北䑓如法 (Nyoho) (*Japanese*)
|
||||
- frumble (*German*)
|
||||
- kekkepikkuni (*Tamil*)
|
||||
- Neo_Chen (NeoChen1024) (*Chinese Traditional*)
|
||||
- oorsutri (*Tamil*)
|
||||
- Rhys Harrison (rhedders) (*Esperanto*)
|
||||
- Nithin V (Nithin896) (*Tamil*)
|
||||
- Miro Rauhala (mirorauhala) (*Finnish*)
|
||||
- diorama (*Italian*)
|
||||
- AlexKoala (alexkoala) (*Korean*)
|
||||
- Aswin C (officialcjunior) (*Malayalam*)
|
||||
- Guillaume Turchini (orion78fr) (*French*)
|
||||
- Ganesh D (auntgd) (*Marathi*)
|
||||
- dragnucs2 (*Arabic*)
|
||||
- Ryan Ho (koungho) (*Chinese Traditional*)
|
||||
- Pedro Henrique (exploronauta) (*Portuguese, Brazilian*)
|
||||
- Tejas Harad (h_tejas) (*Marathi*)
|
||||
- Vasanthan (vasanthan) (*Tamil*)
|
||||
- 硫酸鶏 (acid_chicken) (*Japanese*)
|
||||
- clarmin b8 (clarminb8) (*Sorani (Kurdish)*)
|
||||
- manukp (*Malayalam*)
|
||||
- psymyn (*Hebrew*)
|
||||
- earth dweller (sanethoughtyt) (*Marathi*)
|
||||
- meijerivoi (toilet) (*Finnish*)
|
||||
- essaar (*Tamil*)
|
||||
- serubeena (*Swedish*)
|
||||
- Karol Kosek (krkkPL) (*Polish*)
|
||||
- Rintan (*Japanese*)
|
||||
- valarivan (*Tamil*)
|
||||
- Hernik (hernik27) (*Czech*)
|
||||
- Sebastián Andil (Selrond) (*Slovak*)
|
||||
- Hinaloe (hinaloe) (*Japanese*)
|
||||
- filippodb (*Italian*)
|
||||
- KEINOS (*Japanese*)
|
||||
- Balázs Meskó (meskobalazs) (*Hungarian*)
|
||||
- Bottle (suryasalem2010) (*Tamil*)
|
||||
- JzshAC (*Chinese Simplified*)
|
||||
- Rintan1 (*Japanese*)
|
||||
- Antillion (*Spanish*)
|
||||
- Wrya ali (John12) (*Sorani (Kurdish)*)
|
||||
- Khóo (khootiatling) (*Chinese Traditional*)
|
||||
- Steven Tappert (sammy8806) (*German*)
|
||||
- Antillion (antillion99) (*Spanish*)
|
||||
- Pukima (Pukimaa) (*German*)
|
||||
- Reg3xp (*Persian*)
|
||||
- hiphipvargas (*Portuguese*)
|
||||
- Ch. (*Korean*)
|
||||
- gowthamanb (*Tamil*)
|
||||
- Ch. (sftblw) (*Korean*)
|
||||
- Jeff Huang (s8321414) (*Chinese Traditional*)
|
||||
- Arttu Ylhävuori (arttu.ylhavuori) (*Finnish*)
|
||||
- tctovsli (*Norwegian Nynorsk*)
|
||||
- Timo Tijhof (Krinkle) (*Dutch*)
|
||||
- Yamagishi Kazutoshi (ykzts) (*Japanese, Icelandic, Sorani (Kurdish)*)
|
||||
- vjasiegd (*Polish*)
|
||||
- SamitiMed (*Thai*)
|
||||
- SamitiMed (samiti3d) (*Thai*)
|
||||
- Rekan Adl (rekan-adl1) (*Sorani (Kurdish)*)
|
||||
- umelard (*Hebrew*)
|
||||
- 硫酸鶏 (*Japanese*)
|
||||
- Adrián Lattes (*Spanish*)
|
||||
- Hinaloe (*Japanese*)
|
||||
- Renato "Lond" Cerqueira (*Portuguese, Brazilian*)
|
||||
- Antara2Cinta (Se7enTime) (*Indonesian*)
|
||||
- VSx86 (*Russian*)
|
||||
- Daniel Dimitrov (danny-dimitrov) (*Bulgarian*)
|
||||
- parnikkapore (*Thai*)
|
||||
- Marcin Mikołajczak (*Polish*)
|
||||
- 森の子リスのミーコの大冒険 (*Japanese*)
|
||||
- Marcepanek_ (*Polish*)
|
||||
- Sahak Petrosyan (*Armenian*)
|
||||
- Daniel Dimitrov (*Bulgarian*)
|
||||
- Hugh Liu (*Chinese Simplified*)
|
||||
- Rakino (*Chinese Simplified*)
|
||||
- mynameismonkey (*Welsh*)
|
||||
- Sherwan Othman (sherwanothman11) (*Sorani (Kurdish)*)
|
||||
- Yassine Aït-El-Mouden (yaitelmouden) (*Standard Moroccan Tamazight*)
|
||||
- SKELET (*Danish*)
|
||||
- Mo_der Steven (SakuraPuare) (*Chinese Simplified*)
|
||||
- Fei Yang (Fei1Yang) (*Chinese Traditional*)
|
||||
- ALEM FARID (faridatcemlulaqbayli) (*Kabyle*)
|
||||
- enipra (*Armenian*)
|
||||
- musix (*Persian*)
|
||||
- Renato "Lond" Cerqueira (renatolond) (*Portuguese, Brazilian*)
|
||||
- ギャラ (gyara) (*Japanese, Chinese Simplified*)
|
||||
- Hougo (hougo) (*French*)
|
||||
- ybardapurkar (*Marathi*)
|
||||
- Adrián Lattes (haztecaso) (*Spanish*)
|
||||
- TracyJacks (*Chinese Simplified*)
|
||||
- rasheedgm (*Kannada*)
|
||||
- GatoOscuro (*Spanish*)
|
||||
- mecqor labi (mecqorlabi) (*Persian*)
|
||||
- Belkacem Mohammed (belkacem77) (*Kabyle*)
|
||||
- Navjot Singh (nspeaks) (*Hindi*)
|
||||
- omquylzu (*Latvian*)
|
||||
- Ozai (*German*)
|
||||
- Sahak Petrosyan (petrosyan) (*Armenian*)
|
||||
- siamano (*Thai, Esperanto*)
|
||||
- Viorel-Cătălin Răpițeanu (rapiteanu) (*Romanian*)
|
||||
- Siddhartha Sarathi Basu (quinoa_biryani) (*Bengali*)
|
||||
- Pachara Chantawong (pachara2202) (*Thai*)
|
||||
- mkljczk (*Polish*)
|
||||
- Skew (noan.perrot) (*French*)
|
||||
- Zijian Zhao (jobs2512821228) (*Chinese Simplified*)
|
||||
- turtle836 (*German*)
|
||||
- Guru Prasath Anandapadmanaban (guruprasath) (*Tamil*)
|
||||
- Lamin (laminne) (*Japanese*)
|
||||
- Marcepanek_ (thekingmarcepan) (*Polish*)
|
||||
- Feruz Oripov (FeruzOripov) (*Russian*)
|
||||
- Yann Aguettaz (yann-a) (*French*)
|
||||
- Mick Onio (xgc.redes) (*Asturian*)
|
||||
- Tianqi Zhang (tina.zhang040609) (*Chinese Simplified*)
|
||||
- Malik Mann (dermalikmann) (*German*)
|
||||
- dadosch (*German*)
|
||||
- r3dsp1 (*Chinese Traditional, Hong Kong*)
|
||||
- padulafacundo (*Spanish*)
|
||||
- hg6 (*Hindi*)
|
||||
- Orlando Murcio (Atos20) (*Spanish, Mexico*)
|
||||
- piupiupiudiu (*Chinese Simplified*)
|
||||
- shdy (*German*)
|
||||
- Padraic Calpin (padraic-padraic) (*Slovenian*)
|
||||
- Ильзира Рахматуллина (rahmatullinailzira53) (*Tatar*)
|
||||
- cenegd (*Chinese Simplified*)
|
||||
- Hugh Liu (youloveonlymeh) (*Chinese Simplified*)
|
||||
- Pixelcode (realpixelcode) (*German*)
|
||||
- Yogesh K S (yogi) (*Kannada*)
|
||||
- Rakino (rakino) (*Chinese Simplified*)
|
||||
- Miquel Sabaté Solà (mssola) (*Catalan*)
|
||||
- AmazighNM (*Kabyle*)
|
||||
- Jothipazhani Nagarajan (jothipazhani.n) (*Tamil*)
|
||||
- Clash Clans (KURD12345) (*Sorani (Kurdish)*)
|
||||
- hallomaurits (*Dutch*)
|
||||
- alnd hezh (alndhezh) (*Sorani (Kurdish)*)
|
||||
- Solid Rhino (SolidRhino) (*Dutch*)
|
||||
- k_taka (peaceroad) (*Japanese*)
|
||||
- Hallo Abdullah (hallo_hamza12) (*Sorani (Kurdish)*)
|
||||
- hussama (*Portuguese, Brazilian*)
|
||||
- ThibG (*French*)
|
||||
- Sébastien Feugère (smonff) (*French*)
|
||||
- 林水溶 (shuiRong) (*Chinese Simplified*)
|
||||
- eichkat3r (*German*)
|
||||
- OminousCry (*Russian*)
|
||||
- SnDer (*Dutch*)
|
||||
- PifyZ (*French*)
|
||||
- eichkat3r (*German*)
|
||||
- Karol Kosek (*Polish*)
|
||||
- Akarshan Biswas (*Bengali*)
|
||||
- Tradjincal (*French*)
|
||||
- Steven Tappert (*German*)
|
||||
- sergioaraujo1 (*Portuguese, Brazilian*)
|
||||
- Tom_ (*Czech*)
|
||||
- Tagada (Tagadda) (*French*)
|
||||
- shafouz (*Portuguese, Brazilian*)
|
||||
- Kahina Mess (K_hina) (*Kabyle*)
|
||||
- Nathaël Noguès (NatNgs) (*French*)
|
||||
- Kk (kishorkumara3) (*Kannada*)
|
||||
- Swati Sani (swatisani) (*Urdu (Pakistan)*)
|
||||
- Shrinivasan T (tshrinivasan) (*Tamil*)
|
||||
- さっかりんにーさん (saccharin23) (*Japanese*)
|
||||
- 夜楓Yoka (Yoka2627) (*Chinese Simplified*)
|
||||
- Daniel M. (daniconil) (*Catalan*)
|
||||
- Vikatakavi (*Kannada*)
|
||||
- SusVersiva (*Catalan*)
|
||||
- Tradjincal (tradjincal) (*French*)
|
||||
- pullopen (*Chinese Simplified*)
|
||||
- Robin van der Vliet (RobinvanderVliet) (*Esperanto*)
|
||||
- Zinkokooo (*Basque*)
|
||||
- mmokhi (*Persian*)
|
||||
- fedot (*Russian*)
|
||||
- Livingston Samuel (livingston) (*Tamil*)
|
||||
- prabhjot (*Hindi*)
|
||||
- sergioaraujo1 (*Portuguese, Brazilian*)
|
||||
- CyberAmoeba (pseudoobscura) (*Chinese Simplified*)
|
||||
- tsundoker (*Malayalam*)
|
||||
- skaaarrr (*German*)
|
||||
- JackXu (*Chinese Simplified*)
|
||||
- Lukas Fülling (*German*)
|
||||
- Zoé Bőle (*German*)
|
||||
- Ricardo Colin (rysard) (*Spanish*)
|
||||
- mkljczk (mykylyjczyk) (*Polish*)
|
||||
- Philipp Fischbeck (PFischbeck) (*German*)
|
||||
- fedot (*Russian*)
|
||||
- Paz Galindo (paz.almendra.g) (*Spanish*)
|
||||
- GaggiX (*Italian*)
|
||||
- ralozkolya (*Georgian*)
|
||||
- Zoé Bőle (zoe1337) (*German*)
|
||||
- Lukas Fülling (lfuelling) (*German*)
|
||||
- JackXu (Merman-Jack) (*Chinese Simplified*)
|
||||
- Aymeric (AymBroussier) (*French*)
|
||||
- Anoop (anoopp) (*Malayalam*)
|
||||
- pezcurrel (*Italian*)
|
||||
- Dremski (*Bulgarian*)
|
||||
- tamaina (*Japanese*)
|
||||
- Xurxo Guerra (xguerrap) (*Galician*)
|
||||
- mashirozx (*Chinese Simplified*)
|
||||
- Albatroz Jeremias (albjeremias) (*Portuguese*)
|
||||
- Samir Tighzert (samir_t7) (*Kabyle*)
|
||||
- Apple (blackteaovo) (*Chinese Simplified*)
|
||||
- Nocta (*French*)
|
||||
- OpenAlgeria (*Arabic*)
|
||||
- tamaina (*Japanese*)
|
||||
- abidin toumi (Zet24) (*Arabic*)
|
||||
- xpac1985 (xpac) (*German*)
|
||||
- Kaede (kaedech) (*Japanese*)
|
||||
- ÀŘǾŚ PÀŚĦÀÍ (arospashai) (*Sorani (Kurdish)*)
|
||||
- Matias Lavik (matiaslavik) (*Norwegian Nynorsk*)
|
||||
- smedvedev (*Russian*)
|
||||
- mikel (mikelalas) (*Spanish*)
|
||||
- Doug (douglasalvespe) (*Portuguese, Brazilian*)
|
||||
- Trond Boksasp (boksasp) (*Norwegian*)
|
||||
- Fleva (*Sardinian*)
|
||||
- Mohammad Adnan Mahmood (adnanmig) (*Arabic*)
|
||||
- Sais Lakshmanan (Saislakshmanan) (*Tamil*)
|
||||
- Amith Raj Shetty (amithraj1989) (*Kannada*)
|
||||
- random_person (*Spanish*)
|
||||
- djoerd (*Dutch*)
|
||||
- Baban Abdulrahman (baban.abdulrehman) (*Sorani (Kurdish)*)
|
||||
- ebrezhoneg (*Breton*)
|
||||
- dashty (*Sorani (Kurdish)*)
|
||||
- Salh_haji6 (*Sorani (Kurdish)*)
|
||||
- Amir Kurdo (kuraking202) (*Sorani (Kurdish)*)
|
||||
- おさ (osapon) (*Japanese*)
|
||||
- Ranj A Abdulqadir (RanjAhmed) (*Sorani (Kurdish)*)
|
||||
- umonaca (*Chinese Simplified*)
|
||||
- Bartek Fijałkowski (brateq) (*Polish*)
|
||||
- tateisu (*Japanese*)
|
||||
- centumix (*Japanese*)
|
||||
- Jari Ronkainen (ronchaine) (*Finnish*)
|
||||
- Savarín Electrográfico Marmota Intergalactica (herrero.maty) (*Spanish*)
|
||||
- Torsten Högel (torstenhoegel) (*German*)
|
||||
- Abijeet Patro (Abijeet) (*Basque*)
|
||||
- Ács Zoltán (acszoltan111) (*Hungarian*)
|
||||
- Benjamin Cobb (benjamincobb) (*German*)
|
||||
- waweic (*German*)
|
||||
- Aries (orlea) (*Japanese*)
|
||||
- silverscat_3 (SilversCat) (*Japanese*)
|
||||
- kavitha129 (*Tamil*)
|
||||
- dcapillae (*Spanish*)
|
||||
- SamOak (*Portuguese, Brazilian*)
|
||||
- capiscuas (*Spanish*)
|
||||
- NeverMine17 (*Russian*)
|
||||
- Nithya Mary (nithyamary25) (*Tamil*)
|
||||
- t_aus_m (*German*)
|
||||
- dobrado (*Portuguese, Brazilian*)
|
||||
- Hannah (Aniqueper1) (*Chinese Simplified*)
|
||||
- Jiniux (*Italian*)
|
||||
- 于晚霞 (xissshawww) (*Chinese Simplified*)
|
||||
|
|
1
Aptfile
1
Aptfile
|
@ -5,7 +5,6 @@ libidn11
|
|||
libidn11-dev
|
||||
libpq-dev
|
||||
libprotobuf-dev
|
||||
libssl-dev
|
||||
libxdamage1
|
||||
libxfixes3
|
||||
protobuf-compiler
|
||||
|
|
261
CHANGELOG.md
261
CHANGELOG.md
|
@ -3,7 +3,238 @@ Changelog
|
|||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## Unreleased
|
||||
## [3.3.0] - 2020-12-27
|
||||
### Added
|
||||
|
||||
- **Add hotkeys for audio/video control in web UI** ([Gargron](https://github.com/tootsuite/mastodon/pull/15158), [Gargron](https://github.com/tootsuite/mastodon/pull/15198))
|
||||
- `Space` and `k` to toggle playback
|
||||
- `m` to toggle mute
|
||||
- `f` to toggle fullscreen
|
||||
- `j` and `l` to go back and forward by 10 seconds
|
||||
- `.` and `,` to go back and forward by a frame (video only)
|
||||
- Add expand/compress button on media modal in web UI ([mashirozx](https://github.com/tootsuite/mastodon/pull/15068), [mashirozx](https://github.com/tootsuite/mastodon/pull/15088), [mashirozx](https://github.com/tootsuite/mastodon/pull/15094))
|
||||
- Add border around 🕺 emoji in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/14769))
|
||||
- Add border around 🐞 emoji in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/14712))
|
||||
- Add home link to the getting started column when home isn't mounted ([ThibG](https://github.com/tootsuite/mastodon/pull/14707))
|
||||
- Add option to disable swiping motions across the web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/13885))
|
||||
- **Add pop-out player for audio/video in web UI** ([Gargron](https://github.com/tootsuite/mastodon/pull/14870), [Gargron](https://github.com/tootsuite/mastodon/pull/15157), [Gargron](https://github.com/tootsuite/mastodon/pull/14915), [noellabo](https://github.com/tootsuite/mastodon/pull/15309))
|
||||
- Continue watching/listening when you scroll away
|
||||
- Action bar to interact with/open toot from the pop-out player
|
||||
- Add unread notification markers in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/14818), [ThibG](https://github.com/tootsuite/mastodon/pull/14960), [ThibG](https://github.com/tootsuite/mastodon/pull/14954), [noellabo](https://github.com/tootsuite/mastodon/pull/14897), [noellabo](https://github.com/tootsuite/mastodon/pull/14907))
|
||||
- Add paragraph about browser add-ons when encountering errors in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/14801))
|
||||
- Add import and export for bookmarks ([ThibG](https://github.com/tootsuite/mastodon/pull/14956))
|
||||
- Add cache buster feature for media files ([Gargron](https://github.com/tootsuite/mastodon/pull/15155))
|
||||
- If you have a proxy cache in front of object storage, deleted files will persist until the cache expires
|
||||
- If enabled, cache buster will make a special request to the proxy to signal a cache reset
|
||||
- Add duration option to the mute function ([aquarla](https://github.com/tootsuite/mastodon/pull/13831))
|
||||
- Add replies policy option to the list function ([ThibG](https://github.com/tootsuite/mastodon/pull/9205), [trwnh](https://github.com/tootsuite/mastodon/pull/15304))
|
||||
- Add `og:published_time` OpenGraph tags on toots ([nornagon](https://github.com/tootsuite/mastodon/pull/14865))
|
||||
- **Add option to be notified when a followed user posts** ([Gargron](https://github.com/tootsuite/mastodon/pull/13546), [ThibG](https://github.com/tootsuite/mastodon/pull/14896), [Gargron](https://github.com/tootsuite/mastodon/pull/14822))
|
||||
- If you don't want to miss a toot, click the bell button!
|
||||
- Add client-side validation in password change forms ([ThibG](https://github.com/tootsuite/mastodon/pull/14564))
|
||||
- Add client-side validation in the registration form ([ThibG](https://github.com/tootsuite/mastodon/pull/14560), [ThibG](https://github.com/tootsuite/mastodon/pull/14599))
|
||||
- Add support for Gemini URLs ([joshleeb](https://github.com/tootsuite/mastodon/pull/15013))
|
||||
- Add app shortcuts to web app manifest ([mkljczk](https://github.com/tootsuite/mastodon/pull/15234))
|
||||
- Add WebAuthn as an alternative 2FA method ([santiagorodriguez96](https://github.com/tootsuite/mastodon/pull/14466), [jiikko](https://github.com/tootsuite/mastodon/pull/14806))
|
||||
- Add honeypot fields and minimum fill-out time for sign-up form ([ThibG](https://github.com/tootsuite/mastodon/pull/15276))
|
||||
- Add icon for mutual relationships in relationship manager ([noellabo](https://github.com/tootsuite/mastodon/pull/15149))
|
||||
- Add follow selected followers button in relationship manager ([noellabo](https://github.com/tootsuite/mastodon/pull/15148))
|
||||
- **Add subresource integrity for JS and CSS assets** ([Gargron](https://github.com/tootsuite/mastodon/pull/15096))
|
||||
- If you use a CDN for static assets (JavaScript, CSS, and so on), you have to trust that the CDN does not modify the assets maliciously
|
||||
- Subresource integrity compares server-generated asset digests with what's actually served from the CDN and prevents such attacks
|
||||
- Add `ku`, `sa`, `sc`, `zgh` to available locales ([ykzts](https://github.com/tootsuite/mastodon/pull/15138))
|
||||
- Add ability to force an account to mark media as sensitive ([noellabo](https://github.com/tootsuite/mastodon/pull/14361))
|
||||
- **Add ability to block access or limit sign-ups from chosen IPs** ([Gargron](https://github.com/tootsuite/mastodon/pull/14963), [ThibG](https://github.com/tootsuite/mastodon/pull/15263))
|
||||
- Add rules for IPs or CIDR ranges that automatically expire after a configurable amount of time
|
||||
- Choose the severity of the rule, either blocking all access or merely limiting sign-ups
|
||||
- **Add support for reversible suspensions through ActivityPub** ([Gargron](https://github.com/tootsuite/mastodon/pull/14989))
|
||||
- Servers can signal that one of their accounts has been suspended
|
||||
- During suspension, the account can only delete its own content
|
||||
- A reversal of the suspension can be signalled the same way
|
||||
- A local suspension always overrides a remote one
|
||||
- Add indication to admin UI of whether a report has been forwarded ([ThibG](https://github.com/tootsuite/mastodon/pull/13237))
|
||||
- Add display of reasons for joining of an account in admin UI ([mashirozx](https://github.com/tootsuite/mastodon/pull/15265))
|
||||
- Add option to obfuscate domain name in public list of domain blocks ([Gargron](https://github.com/tootsuite/mastodon/pull/15355))
|
||||
- Add option to make reasons for joining required on sign-up ([ThibG](https://github.com/tootsuite/mastodon/pull/15326), [ThibG](https://github.com/tootsuite/mastodon/pull/15358), [ThibG](https://github.com/tootsuite/mastodon/pull/15385), [ThibG](https://github.com/tootsuite/mastodon/pull/15405))
|
||||
- Add ActivityPub follower synchronization mechanism ([ThibG](https://github.com/tootsuite/mastodon/pull/14510), [ThibG](https://github.com/tootsuite/mastodon/pull/15026))
|
||||
- Add outbox attribute to instance actor ([ThibG](https://github.com/tootsuite/mastodon/pull/14721))
|
||||
- Add featured hashtags as an ActivityPub collection ([Gargron](https://github.com/tootsuite/mastodon/pull/11595), [noellabo](https://github.com/tootsuite/mastodon/pull/15277))
|
||||
- Add support for dereferencing objects through bearcaps ([Gargron](https://github.com/tootsuite/mastodon/pull/14683), [noellabo](https://github.com/tootsuite/mastodon/pull/14981))
|
||||
- Add `S3_READ_TIMEOUT` environment variable ([tateisu](https://github.com/tootsuite/mastodon/pull/14952))
|
||||
- Add `ALLOWED_PRIVATE_ADDRESSES` environment variable ([ThibG](https://github.com/tootsuite/mastodon/pull/14722))
|
||||
- Add `--fix-permissions` option to `tootctl media remove-orphans` ([Gargron](https://github.com/tootsuite/mastodon/pull/14383), [uist1idrju3i](https://github.com/tootsuite/mastodon/pull/14715))
|
||||
- Add `tootctl accounts merge` ([Gargron](https://github.com/tootsuite/mastodon/pull/15201), [ThibG](https://github.com/tootsuite/mastodon/pull/15264), [ThibG](https://github.com/tootsuite/mastodon/pull/15256))
|
||||
- Has someone changed their domain or subdomain thereby creating two accounts where there should be one?
|
||||
- This command will fix it on your end
|
||||
- Add `tootctl maintenance fix-duplicates` ([ThibG](https://github.com/tootsuite/mastodon/pull/14860), [Gargron](https://github.com/tootsuite/mastodon/pull/15223), [ThibG](https://github.com/tootsuite/mastodon/pull/15373))
|
||||
- Index corruption in the database?
|
||||
- This command is for you
|
||||
- **Add support for managing multiple stream subscriptions in a single connection** ([Gargron](https://github.com/tootsuite/mastodon/pull/14524), [Gargron](https://github.com/tootsuite/mastodon/pull/14566), [mfmfuyu](https://github.com/tootsuite/mastodon/pull/14859), [zunda](https://github.com/tootsuite/mastodon/pull/14608))
|
||||
- Previously, getting live updates for multiple timelines required opening a HTTP or WebSocket connection for each
|
||||
- More connections means more resource consumption on both ends, not to mention the (ever so slight) delay when establishing a new connection
|
||||
- Now, with just a single WebSocket connection you can subscribe and unsubscribe to and from multiple streams
|
||||
- Add support for limiting results by both `min_id` and `max_id` at the same time in REST API ([tateisu](https://github.com/tootsuite/mastodon/pull/14776))
|
||||
- Add `GET /api/v1/accounts/:id/featured_tags` to REST API ([noellabo](https://github.com/tootsuite/mastodon/pull/11817), [noellabo](https://github.com/tootsuite/mastodon/pull/15270))
|
||||
- Add stoplight for object storage failures, return HTTP 503 in REST API ([Gargron](https://github.com/tootsuite/mastodon/pull/13043))
|
||||
- Add optional `tootctl remove media` cronjob in Helm chart ([dunn](https://github.com/tootsuite/mastodon/pull/14396))
|
||||
- Add clean error message when `RAILS_ENV` is unset ([ThibG](https://github.com/tootsuite/mastodon/pull/15381))
|
||||
|
||||
### Changed
|
||||
|
||||
- **Change media modals look in web UI** ([Gargron](https://github.com/tootsuite/mastodon/pull/15217), [Gargron](https://github.com/tootsuite/mastodon/pull/15221), [Gargron](https://github.com/tootsuite/mastodon/pull/15284), [Gargron](https://github.com/tootsuite/mastodon/pull/15283), [Kjwon15](https://github.com/tootsuite/mastodon/pull/15308), [noellabo](https://github.com/tootsuite/mastodon/pull/15305), [ThibG](https://github.com/tootsuite/mastodon/pull/15417))
|
||||
- Background of the overlay matches the color of the image
|
||||
- Action bar to interact with or open the toot from the modal
|
||||
- Change order of announcements in admin UI to be newest-first ([ThibG](https://github.com/tootsuite/mastodon/pull/15091))
|
||||
- **Change account suspensions to be reversible by default** ([Gargron](https://github.com/tootsuite/mastodon/pull/14726), [ThibG](https://github.com/tootsuite/mastodon/pull/15152), [ThibG](https://github.com/tootsuite/mastodon/pull/15106), [ThibG](https://github.com/tootsuite/mastodon/pull/15100), [ThibG](https://github.com/tootsuite/mastodon/pull/15099), [noellabo](https://github.com/tootsuite/mastodon/pull/14855), [ThibG](https://github.com/tootsuite/mastodon/pull/15380), [Gargron](https://github.com/tootsuite/mastodon/pull/15420), [Gargron](https://github.com/tootsuite/mastodon/pull/15414))
|
||||
- Suspensions no longer equal deletions
|
||||
- A suspended account can be unsuspended with minimal consequences for 30 days
|
||||
- Immediate deletion of data is still available as an explicit option
|
||||
- Suspended accounts can request an archive of their data through the UI
|
||||
- Change REST API to return empty data for suspended accounts (14765)
|
||||
- Change web UI to show empty profile for suspended accounts ([Gargron](https://github.com/tootsuite/mastodon/pull/14766), [Gargron](https://github.com/tootsuite/mastodon/pull/15345))
|
||||
- Change featured hashtag suggestions to be recently used instead of most used ([abcang](https://github.com/tootsuite/mastodon/pull/14760))
|
||||
- Change direct toots to appear in the home feed again ([Gargron](https://github.com/tootsuite/mastodon/pull/14711), [ThibG](https://github.com/tootsuite/mastodon/pull/15182), [noellabo](https://github.com/tootsuite/mastodon/pull/14727))
|
||||
- Return to treating all toots the same instead of trying to retrofit direct visibility into an instant messaging model
|
||||
- Change email address validation to return more specific errors ([ThibG](https://github.com/tootsuite/mastodon/pull/14565))
|
||||
- Change HTTP signature requirements to include `Digest` header on `POST` requests ([ThibG](https://github.com/tootsuite/mastodon/pull/15069))
|
||||
- Change click area of video/audio player buttons to be bigger in web UI ([ariasuni](https://github.com/tootsuite/mastodon/pull/15049))
|
||||
- Change order of filters by alphabetic by "keyword or phrase" ([ariasuni](https://github.com/tootsuite/mastodon/pull/15050))
|
||||
- Change suspension of remote accounts to also undo outgoing follows ([ThibG](https://github.com/tootsuite/mastodon/pull/15188))
|
||||
- Change string "Home" to "Home and lists" in the filter creation screen ([ariasuni](https://github.com/tootsuite/mastodon/pull/15139))
|
||||
- Change string "Boost to original audience" to "Boost with original visibility" in web UI ([3n-k1](https://github.com/tootsuite/mastodon/pull/14598))
|
||||
- Change string "Show more" to "Show newer" and "Show older" on public pages ([ariasuni](https://github.com/tootsuite/mastodon/pull/15052))
|
||||
- Change order of announcements to be reverse chronological in web UI ([dariusk](https://github.com/tootsuite/mastodon/pull/15065), [dariusk](https://github.com/tootsuite/mastodon/pull/15070))
|
||||
- Change RTL detection to rely on unicode-bidi paragraph by paragraph in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/14573))
|
||||
- Change visibility icon next to timestamp to be clickable in web UI ([ariasuni](https://github.com/tootsuite/mastodon/pull/15053), [mayaeh](https://github.com/tootsuite/mastodon/pull/15055))
|
||||
- Change public thread view to hide "Show thread" link ([ThibG](https://github.com/tootsuite/mastodon/pull/15266))
|
||||
- Change number format on about page from full to shortened ([Gargron](https://github.com/tootsuite/mastodon/pull/15327))
|
||||
- Change how scheduled tasks run in multi-process environments ([noellabo](https://github.com/tootsuite/mastodon/pull/15314))
|
||||
- New dedicated queue `scheduler`
|
||||
- Runs by default when Sidekiq is executed with no options
|
||||
- Has to be added manually in a multi-process environment
|
||||
|
||||
### Removed
|
||||
|
||||
- Remove fade-in animation from modals in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/15199))
|
||||
- Remove auto-redirect to direct messages in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/15142))
|
||||
- Remove obsolete IndexedDB operations from web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/14730))
|
||||
- Remove dependency on unused and unmaintained http_parser.rb gem ([ThibG](https://github.com/tootsuite/mastodon/pull/14574))
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix layout on about page when contact account has a long username ([ThibG](https://github.com/tootsuite/mastodon/pull/15357))
|
||||
- Fix follow limit preventing re-following of a moved account ([Gargron](https://github.com/tootsuite/mastodon/pull/14207), [ThibG](https://github.com/tootsuite/mastodon/pull/15384))
|
||||
- **Fix deletes not reaching every server that interacted with toot** ([Gargron](https://github.com/tootsuite/mastodon/pull/15200))
|
||||
- Previously, delete of a toot would be primarily sent to the followers of its author, people mentioned in the toot, and people who reblogged the toot
|
||||
- Now, additionally, it is ensured that it is sent to people who replied to it, favourited it, and to the person it replies to even if that person is not mentioned
|
||||
- Fix resolving an account through its non-canonical form (i.e. alternate domain) ([ThibG](https://github.com/tootsuite/mastodon/pull/15187))
|
||||
- Fix sending redundant ActivityPub events when processing remote account deletion ([ThibG](https://github.com/tootsuite/mastodon/pull/15104))
|
||||
- Fix Move handler not being triggered when failing to fetch target account ([ThibG](https://github.com/tootsuite/mastodon/pull/15107))
|
||||
- Fix downloading remote media files when server returns empty filename ([ThibG](https://github.com/tootsuite/mastodon/pull/14867))
|
||||
- Fix account processing failing because of large collections ([ThibG](https://github.com/tootsuite/mastodon/pull/15027))
|
||||
- Fix not being able to unfavorite toots one has lost access to ([ThibG](https://github.com/tootsuite/mastodon/pull/15192))
|
||||
- Fix not being able to unbookmark toots one has lost access to ([ThibG](https://github.com/tootsuite/mastodon/pull/14604))
|
||||
- Fix possible casing inconsistencies in hashtag search ([ThibG](https://github.com/tootsuite/mastodon/pull/14906))
|
||||
- Fix updating account counters when association is not yet created ([Gargron](https://github.com/tootsuite/mastodon/pull/15108))
|
||||
- Fix cookies not having a SameSite attribute ([Gargron](https://github.com/tootsuite/mastodon/pull/15098))
|
||||
- Fix poll ending notifications being created for each vote ([ThibG](https://github.com/tootsuite/mastodon/pull/15071))
|
||||
- Fix multiple boosts of a same toot erroneously appearing in TL ([ThibG](https://github.com/tootsuite/mastodon/pull/14759))
|
||||
- Fix asset builds not picking up `CDN_HOST` change ([ThibG](https://github.com/tootsuite/mastodon/pull/14381))
|
||||
- Fix desktop notifications permission prompt in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/14985), [Gargron](https://github.com/tootsuite/mastodon/pull/15141), [ThibG](https://github.com/tootsuite/mastodon/pull/13543), [ThibG](https://github.com/tootsuite/mastodon/pull/15176))
|
||||
- Some time ago, browsers added a requirement that desktop notification prompts could only be displayed in response to a user-generated event (such as a click)
|
||||
- This means that for some time, users who haven't already given the permission before were not getting a prompt and as such were not receiving desktop notifications
|
||||
- Fix "Mark media as sensitive" string not supporting pluralizations in other languages in web UI ([ariasuni](https://github.com/tootsuite/mastodon/pull/15051))
|
||||
- Fix glitched image uploads when canvas read access is blocked in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/15180))
|
||||
- Fix some account gallery items having empty labels in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/15073))
|
||||
- Fix alt-key hotkeys activating while typing in a text field in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/14942))
|
||||
- Fix wrong seek bar width on media player in web UI ([mfmfuyu](https://github.com/tootsuite/mastodon/pull/15060))
|
||||
- Fix logging out on mobile in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/14901))
|
||||
- Fix wrong click area for GIFVs in media modal in web UI ([noellabo](https://github.com/tootsuite/mastodon/pull/14615))
|
||||
- Fix unreadable placeholder text color in high contrast theme in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/14803))
|
||||
- Fix scrolling issues when closing some dropdown menus in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/14606))
|
||||
- Fix notification filter bar incorrectly filtering gaps in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/14808))
|
||||
- Fix disabled boost icon being replaced by private boost icon on hover in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/14456))
|
||||
- Fix hashtag detection in compose form being different to server-side in web UI ([kedamaDQ](https://github.com/tootsuite/mastodon/pull/14484), [ThibG](https://github.com/tootsuite/mastodon/pull/14513))
|
||||
- Fix home last read marker mishandling gaps in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/14809))
|
||||
- Fix unnecessary re-rendering of various components when typing in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/15286))
|
||||
- Fix notifications being unnecessarily re-rendered in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/15312))
|
||||
- Fix column swiping animation logic in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/15301))
|
||||
- Fix inefficiency when fetching hashtag timeline ([noellabo](https://github.com/tootsuite/mastodon/pull/14861), [akihikodaki](https://github.com/tootsuite/mastodon/pull/14662))
|
||||
- Fix inefficiency when fetching bookmarks ([akihikodaki](https://github.com/tootsuite/mastodon/pull/14674))
|
||||
- Fix inefficiency when fetching favourites ([akihikodaki](https://github.com/tootsuite/mastodon/pull/14673))
|
||||
- Fix inefficiency when fetching media-only account timeline ([akihikodaki](https://github.com/tootsuite/mastodon/pull/14675))
|
||||
- Fix inefficieny when deleting accounts ([Gargron](https://github.com/tootsuite/mastodon/pull/15387), [ThibG](https://github.com/tootsuite/mastodon/pull/15409), [ThibG](https://github.com/tootsuite/mastodon/pull/15407), [ThibG](https://github.com/tootsuite/mastodon/pull/15408), [ThibG](https://github.com/tootsuite/mastodon/pull/15402), [ThibG](https://github.com/tootsuite/mastodon/pull/15416), [Gargron](https://github.com/tootsuite/mastodon/pull/15421))
|
||||
- Fix redundant query when processing batch actions on custom emojis ([niwatori24](https://github.com/tootsuite/mastodon/pull/14534))
|
||||
- Fix slow distinct queries where grouped queries are faster ([Gargron](https://github.com/tootsuite/mastodon/pull/15287))
|
||||
- Fix performance on instances list in admin UI ([Gargron](https://github.com/tootsuite/mastodon/pull/15282))
|
||||
- Fix server actor appearing in list of accounts in admin UI ([ThibG](https://github.com/tootsuite/mastodon/pull/14567))
|
||||
- Fix "bootstrap timeline accounts" toggle in site settings in admin UI ([ThibG](https://github.com/tootsuite/mastodon/pull/15325))
|
||||
- Fix PostgreSQL secret name for cronjob in Helm chart ([metal3d](https://github.com/tootsuite/mastodon/pull/15072))
|
||||
- Fix Procfile not being compatible with herokuish ([acuteaura](https://github.com/tootsuite/mastodon/pull/12685))
|
||||
- Fix installation of tini being split into multiple steps in Dockerfile ([ryncsn](https://github.com/tootsuite/mastodon/pull/14686))
|
||||
|
||||
### Security
|
||||
|
||||
- Fix streaming API allowing connections to persist after access token invalidation ([Gargron](https://github.com/tootsuite/mastodon/pull/15111))
|
||||
- Fix 2FA/sign-in token sessions being valid after password change ([Gargron](https://github.com/tootsuite/mastodon/pull/14802))
|
||||
- Fix resolving accounts sometimes creating duplicate records for a given ActivityPub identifier ([ThibG](https://github.com/tootsuite/mastodon/pull/15364))
|
||||
|
||||
## [3.2.2] - 2020-12-19
|
||||
### Added
|
||||
|
||||
- Add `tootctl maintenance fix-duplicates` ([ThibG](https://github.com/tootsuite/mastodon/pull/14860), [Gargron](https://github.com/tootsuite/mastodon/pull/15223))
|
||||
- Index corruption in the database?
|
||||
- This command is for you
|
||||
|
||||
### Removed
|
||||
|
||||
- Remove dependency on unused and unmaintained http_parser.rb gem ([ThibG](https://github.com/tootsuite/mastodon/pull/14574))
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix Move handler not being triggered when failing to fetch target account ([ThibG](https://github.com/tootsuite/mastodon/pull/15107))
|
||||
- Fix downloading remote media files when server returns empty filename ([ThibG](https://github.com/tootsuite/mastodon/pull/14867))
|
||||
- Fix possible casing inconsistencies in hashtag search ([ThibG](https://github.com/tootsuite/mastodon/pull/14906))
|
||||
- Fix updating account counters when association is not yet created ([Gargron](https://github.com/tootsuite/mastodon/pull/15108))
|
||||
- Fix account processing failing because of large collections ([ThibG](https://github.com/tootsuite/mastodon/pull/15027))
|
||||
- Fix resolving an account through its non-canonical form (i.e. alternate domain) ([ThibG](https://github.com/tootsuite/mastodon/pull/15187))
|
||||
- Fix slow distinct queries where grouped queries are faster ([Gargron](https://github.com/tootsuite/mastodon/pull/15287))
|
||||
|
||||
### Security
|
||||
|
||||
- Fix 2FA/sign-in token sessions being valid after password change ([Gargron](https://github.com/tootsuite/mastodon/pull/14802))
|
||||
- Fix resolving accounts sometimes creating duplicate records for a given ActivityPub identifier ([ThibG](https://github.com/tootsuite/mastodon/pull/15364))
|
||||
|
||||
## [3.2.1] - 2020-10-19
|
||||
### Added
|
||||
|
||||
- Add support for latest HTTP Signatures spec draft ([ThibG](https://github.com/tootsuite/mastodon/pull/14556))
|
||||
- Add support for inlined objects in ActivityPub `to`/`cc` ([ThibG](https://github.com/tootsuite/mastodon/pull/14514))
|
||||
|
||||
### Changed
|
||||
|
||||
- Change actors to not be served at all without authentication in limited federation mode ([ThibG](https://github.com/tootsuite/mastodon/pull/14800))
|
||||
- Previously, a bare version of an actor was served when not authenticated, i.e. username and public key
|
||||
- Because all actor fetch requests are signed using a separate system actor, that is no longer required
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix `tootctl media` commands not recognizing very large IDs ([ThibG](https://github.com/tootsuite/mastodon/pull/14536))
|
||||
- Fix crash when failing to load emoji picker in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/14525))
|
||||
- Fix contrast requirements in thumbnail color extraction ([ThibG](https://github.com/tootsuite/mastodon/pull/14464))
|
||||
- Fix audio/video player not using `CDN_HOST` on public pages ([ThibG](https://github.com/tootsuite/mastodon/pull/14486))
|
||||
- Fix private boost icon not being used on public pages ([OmmyZhang](https://github.com/tootsuite/mastodon/pull/14471))
|
||||
- Fix audio player on Safari in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/14485), [ThibG](https://github.com/tootsuite/mastodon/pull/14465))
|
||||
- Fix dereferencing remote statuses not using the correct account for signature when receiving a targeted inbox delivery ([ThibG](https://github.com/tootsuite/mastodon/pull/14656))
|
||||
- Fix nil error in `tootctl media remove` ([noellabo](https://github.com/tootsuite/mastodon/pull/14657))
|
||||
- Fix videos with near-60 fps being rejected ([Gargron](https://github.com/tootsuite/mastodon/pull/14684))
|
||||
- Fix reported statuses not being included in warning e-mail ([Gargron](https://github.com/tootsuite/mastodon/pull/14778))
|
||||
- Fix `Reject` activities of `Follow` objects not correctly destroying a follow relationship ([ThibG](https://github.com/tootsuite/mastodon/pull/14479))
|
||||
- Fix inefficiencies in fan-out-on-write service ([Gargron](https://github.com/tootsuite/mastodon/pull/14682), [noellabo](https://github.com/tootsuite/mastodon/pull/14709))
|
||||
- Fix timeout errors when trying to webfinger some IPv6 configurations ([Gargron](https://github.com/tootsuite/mastodon/pull/14919))
|
||||
- Fix files served as `application/octet-stream` being rejected without attempting mime type detection ([ThibG](https://github.com/tootsuite/mastodon/pull/14452))
|
||||
|
||||
## [3.2.0] - 2020-07-27
|
||||
### Added
|
||||
|
||||
- Add `SMTP_SSL` environment variable ([OmmyZhang](https://github.com/tootsuite/mastodon/pull/14309))
|
||||
|
@ -29,7 +260,7 @@ All notable changes to this project will be documented in this file.
|
|||
- New REST API: `POST /api/v1/accounts/:id/note` with `comment` param
|
||||
- The Relationship entity in REST API has a new `note` attribute
|
||||
- Add Helm chart ([dunn](https://github.com/tootsuite/mastodon/pull/14090), [dunn](https://github.com/tootsuite/mastodon/pull/14256), [dunn](https://github.com/tootsuite/mastodon/pull/14245))
|
||||
- **Add customizable thumbnails for audio and video attachments** ([Gargron](https://github.com/tootsuite/mastodon/pull/14145), [Gargron](https://github.com/tootsuite/mastodon/pull/14244), [Gargron](https://github.com/tootsuite/mastodon/pull/14273), [Gargron](https://github.com/tootsuite/mastodon/pull/14203), [ThibG](https://github.com/tootsuite/mastodon/pull/14255), [ThibG](https://github.com/tootsuite/mastodon/pull/14306))
|
||||
- **Add customizable thumbnails for audio and video attachments** ([Gargron](https://github.com/tootsuite/mastodon/pull/14145), [Gargron](https://github.com/tootsuite/mastodon/pull/14244), [Gargron](https://github.com/tootsuite/mastodon/pull/14273), [Gargron](https://github.com/tootsuite/mastodon/pull/14203), [ThibG](https://github.com/tootsuite/mastodon/pull/14255), [ThibG](https://github.com/tootsuite/mastodon/pull/14306), [noellabo](https://github.com/tootsuite/mastodon/pull/14358), [noellabo](https://github.com/tootsuite/mastodon/pull/14357))
|
||||
- Metadata (album, artist, etc) is no longer stripped from audio files
|
||||
- Album art is automatically extracted from audio files
|
||||
- Thumbnail can be manually uploaded for both audio and video attachments
|
||||
|
@ -37,6 +268,7 @@ All notable changes to this project will be documented in this file.
|
|||
- On `POST /api/v1/media` and `POST /api/v2/media`
|
||||
- And on `PUT /api/v1/media/:id`
|
||||
- ActivityPub representation of media attachments represents custom thumbnails with an `icon` attribute
|
||||
- The Media Attachment entity in REST API now has a `preview_remote_url` to its `preview_url`, equivalent to `remote_url` to its `url`
|
||||
- **Add color extraction for thumbnails** ([Gargron](https://github.com/tootsuite/mastodon/pull/14209), [ThibG](https://github.com/tootsuite/mastodon/pull/14264))
|
||||
- The `meta` attribute on the Media Attachment entity in REST API can now have a `colors` attribute which in turn contains three hex colors: `background`, `foreground`, and `accent`
|
||||
- The background color is chosen from the most dominant color around the edges of the thumbnail
|
||||
|
@ -48,6 +280,9 @@ All notable changes to this project will be documented in this file.
|
|||
- Add `tootctl email_domain_blocks` ([tateisu](https://github.com/tootsuite/mastodon/pull/13589), [Gargron](https://github.com/tootsuite/mastodon/pull/14147))
|
||||
- Add "Add new domain block" to header of federation page in admin UI ([ariasuni](https://github.com/tootsuite/mastodon/pull/13934))
|
||||
- Add ability to keep emoji picker open with ctrl+click in web UI ([bclindner](https://github.com/tootsuite/mastodon/pull/13896), [noellabo](https://github.com/tootsuite/mastodon/pull/14096))
|
||||
- Add custom icon for private boosts in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/14380))
|
||||
- Add support for Create and Update activities that don't inline objects in ActivityPub ([ThibG](https://github.com/tootsuite/mastodon/pull/14359))
|
||||
- Add support for Undo activities that don't inline activities in ActivityPub ([ThibG](https://github.com/tootsuite/mastodon/pull/14346))
|
||||
|
||||
### Changed
|
||||
|
||||
|
@ -59,9 +294,9 @@ All notable changes to this project will be documented in this file.
|
|||
- Some websites may not render OpenGraph tags into HTML if that's not the case
|
||||
- Change behaviour to carry blocks over when someone migrates their followers ([ThibG](https://github.com/tootsuite/mastodon/pull/14144))
|
||||
- Change volume control and download buttons in web UI ([Gargron](https://github.com/tootsuite/mastodon/pull/14122))
|
||||
- **Change design of audio players in web UI** ([Gargron](https://github.com/tootsuite/mastodon/pull/14095), [ThibG](https://github.com/tootsuite/mastodon/pull/14281), [Gargron](https://github.com/tootsuite/mastodon/pull/14282), [ThibG](https://github.com/tootsuite/mastodon/pull/14118), [Gargron](https://github.com/tootsuite/mastodon/pull/14199))
|
||||
- **Change design of audio players in web UI** ([Gargron](https://github.com/tootsuite/mastodon/pull/14095), [ThibG](https://github.com/tootsuite/mastodon/pull/14281), [Gargron](https://github.com/tootsuite/mastodon/pull/14282), [ThibG](https://github.com/tootsuite/mastodon/pull/14118), [Gargron](https://github.com/tootsuite/mastodon/pull/14199), [ThibG](https://github.com/tootsuite/mastodon/pull/14338))
|
||||
- Change reply filter to never filter own toots in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/14128))
|
||||
- Change boost button to no longer serve as visibility indicator in web UI ([noellabo](https://github.com/tootsuite/mastodon/pull/14132))
|
||||
- Change boost button to no longer serve as visibility indicator in web UI ([noellabo](https://github.com/tootsuite/mastodon/pull/14132), [ThibG](https://github.com/tootsuite/mastodon/pull/14373))
|
||||
- Change contrast of flash messages ([cchoi12](https://github.com/tootsuite/mastodon/pull/13892))
|
||||
- Change wording from "Hide media" to "Hide image/images" in web UI ([ariasuni](https://github.com/tootsuite/mastodon/pull/13834))
|
||||
- Change appearence of settings pages to be more consistent ([ariasuni](https://github.com/tootsuite/mastodon/pull/13938))
|
||||
|
@ -69,6 +304,7 @@ All notable changes to this project will be documented in this file.
|
|||
- Change how badly contrasting emoji are rendered in web UI ([leo60228](https://github.com/tootsuite/mastodon/pull/13773), [ThibG](https://github.com/tootsuite/mastodon/pull/13772), [mfmfuyu](https://github.com/tootsuite/mastodon/pull/14020), [ThibG](https://github.com/tootsuite/mastodon/pull/14015))
|
||||
- Change structure of unavailable content section on about page ([ariasuni](https://github.com/tootsuite/mastodon/pull/13930))
|
||||
- Change behaviour to accept ActivityPub activities relayed through group actor ([noellabo](https://github.com/tootsuite/mastodon/pull/14279))
|
||||
- Change amount of processing retries for ActivityPub activities ([noellabo](https://github.com/tootsuite/mastodon/pull/14355))
|
||||
|
||||
### Removed
|
||||
|
||||
|
@ -84,13 +320,18 @@ All notable changes to this project will be documented in this file.
|
|||
|
||||
### Fixed
|
||||
|
||||
- Fix `following` param not working when exact match is found in account search ([noellabo](https://github.com/tootsuite/mastodon/pull/14394))
|
||||
- Fix sometimes occuring duplicate mention notifications ([noellabo](https://github.com/tootsuite/mastodon/pull/14378))
|
||||
- Fix RSS feeds not being cachable ([ThibG](https://github.com/tootsuite/mastodon/pull/14368))
|
||||
- Fix lack of locking around processing of Announce activities in ActivityPub ([noellabo](https://github.com/tootsuite/mastodon/pull/14365))
|
||||
- Fix boosted toots from blocked account not being retroactively removed from TL ([ThibG](https://github.com/tootsuite/mastodon/pull/14339))
|
||||
- Fix large shortened numbers (like 1.2K) using incorrect pluralization ([Sasha-Sorokin](https://github.com/tootsuite/mastodon/pull/14061))
|
||||
- Fix streaming server trying to use empty password to connect to Redis when `REDIS_PASSWORD` is given but blank ([ThibG](https://github.com/tootsuite/mastodon/pull/14135))
|
||||
- Fix being unable to unboost posts when blocked by their author ([ThibG](https://github.com/tootsuite/mastodon/pull/14308))
|
||||
- Fix account domain block not properly unfollowing accounts from domain ([Gargron](https://github.com/tootsuite/mastodon/pull/14304))
|
||||
- Fix removing a domain allow wiping known accounts in open federation mode ([ThibG](https://github.com/tootsuite/mastodon/pull/14298))
|
||||
- Fix blocks and mutes pagination in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/14275))
|
||||
- Fix new posts pushing down origin of opened dropdown in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/14271))
|
||||
- Fix new posts pushing down origin of opened dropdown in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/14271), [ThibG](https://github.com/tootsuite/mastodon/pull/14348))
|
||||
- Fix timeline markers not being saved sometimes ([ThibG](https://github.com/tootsuite/mastodon/pull/13887), [ThibG](https://github.com/tootsuite/mastodon/pull/13889), [ThibG](https://github.com/tootsuite/mastodon/pull/14155))
|
||||
- Fix CSV uploads being rejected ([noellabo](https://github.com/tootsuite/mastodon/pull/13835))
|
||||
- Fix incompatibility with ElasticSearch 7.x ([noellabo](https://github.com/tootsuite/mastodon/pull/13828))
|
||||
|
@ -112,7 +353,7 @@ All notable changes to this project will be documented in this file.
|
|||
- Use circuit breakers to stop hitting unresponsive servers
|
||||
- Avoid hitting servers that are already known to be generally unavailable
|
||||
- Fix filters ignoring media descriptions ([BenLubar](https://github.com/tootsuite/mastodon/pull/13837))
|
||||
- Fix soem actions on custom emojis leading to cryptic errors in admin UI ([ThibG](https://github.com/tootsuite/mastodon/pull/13951))
|
||||
- Fix some actions on custom emojis leading to cryptic errors in admin UI ([ThibG](https://github.com/tootsuite/mastodon/pull/13951))
|
||||
- Fix ActivityPub serialization of replies when some of them are URIs ([ThibG](https://github.com/tootsuite/mastodon/pull/13957))
|
||||
- Fix `rake mastodon:setup` choking on environment variables containing `%` ([ThibG](https://github.com/tootsuite/mastodon/pull/13940))
|
||||
- Fix account redirect confirmation message talking about moved followers ([ThibG](https://github.com/tootsuite/mastodon/pull/13950))
|
||||
|
@ -132,7 +373,7 @@ All notable changes to this project will be documented in this file.
|
|||
- Fix unapproved users being able to view profiles when in limited-federation mode *and* requiring approval for sign-ups ([ThibG](https://github.com/tootsuite/mastodon/pull/14093))
|
||||
- Fix initial audio volume not corresponding to what's displayed in audio player in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/14057))
|
||||
- Fix timelines sometimes jumping when closing modals in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/14019))
|
||||
- Fix memory usage of downloading remote files ([Gargron](https://github.com/tootsuite/mastodon/pull/14184), [Gargron](https://github.com/tootsuite/mastodon/pull/14181))
|
||||
- Fix memory usage of downloading remote files ([Gargron](https://github.com/tootsuite/mastodon/pull/14184), [Gargron](https://github.com/tootsuite/mastodon/pull/14181), [noellabo](https://github.com/tootsuite/mastodon/pull/14356))
|
||||
- Don't read entire file (up to 40 MB) into memory
|
||||
- Read and write it to temp file in small chunks
|
||||
- Fix inconsistent account header padding in web UI ([trwnh](https://github.com/tootsuite/mastodon/pull/14179))
|
||||
|
@ -146,14 +387,14 @@ All notable changes to this project will be documented in this file.
|
|||
- Only then proceed to start removing their data (slow)
|
||||
- Clear out media attachments in a separate worker (slow)
|
||||
|
||||
## [v3.1.5] - 2020-07-07
|
||||
## [3.1.5] - 2020-07-07
|
||||
### Security
|
||||
|
||||
- Fix media attachment enumeration ([ThibG](https://github.com/tootsuite/mastodon/pull/14254))
|
||||
- Change rate limits for various paths ([Gargron](https://github.com/tootsuite/mastodon/pull/14253))
|
||||
- Fix other sessions not being logged out on password change ([Gargron](https://github.com/tootsuite/mastodon/pull/14252))
|
||||
|
||||
## [v3.1.4] - 2020-05-14
|
||||
## [3.1.4] - 2020-05-14
|
||||
### Added
|
||||
|
||||
- Add `vi` to available locales ([taicv](https://github.com/tootsuite/mastodon/pull/13542))
|
||||
|
@ -220,7 +461,7 @@ All notable changes to this project will be documented in this file.
|
|||
- For apps that self-register on behalf of every individual user (such as most mobile apps), this is a non-issue
|
||||
- The issue only affects developers of apps who are shared between multiple users, such as server-side apps like cross-posters
|
||||
|
||||
## [v3.1.3] - 2020-04-05
|
||||
## [3.1.3] - 2020-04-05
|
||||
### Added
|
||||
|
||||
- Add ability to filter audit log in admin UI ([Gargron](https://github.com/tootsuite/mastodon/pull/13381))
|
||||
|
|
23
Dockerfile
23
Dockerfile
|
@ -4,7 +4,7 @@ FROM ubuntu:20.04 as build-dep
|
|||
SHELL ["bash", "-c"]
|
||||
|
||||
# Install Node v12 (LTS)
|
||||
ENV NODE_VER="12.16.3"
|
||||
ENV NODE_VER="12.20.0"
|
||||
RUN ARCH= && \
|
||||
dpkgArch="$(dpkg --print-architecture)" && \
|
||||
case "${dpkgArch##*-}" in \
|
||||
|
@ -36,10 +36,11 @@ RUN apt update && \
|
|||
./autogen.sh && \
|
||||
./configure --prefix=/opt/jemalloc && \
|
||||
make -j$(nproc) > /dev/null && \
|
||||
make install_bin install_include install_lib
|
||||
make install_bin install_include install_lib && \
|
||||
cd .. && rm -rf jemalloc-$JE_VER $JE_VER.tar.gz
|
||||
|
||||
# Install Ruby
|
||||
ENV RUBY_VER="2.6.6"
|
||||
ENV RUBY_VER="2.7.2"
|
||||
ENV CPPFLAGS="-I/opt/jemalloc/include"
|
||||
ENV LDFLAGS="-L/opt/jemalloc/lib/"
|
||||
RUN apt update && \
|
||||
|
@ -56,7 +57,8 @@ RUN apt update && \
|
|||
--disable-install-doc && \
|
||||
ln -s /opt/jemalloc/lib/* /usr/lib/ && \
|
||||
make -j$(nproc) > /dev/null && \
|
||||
make install
|
||||
make install && \
|
||||
cd .. && rm -rf ruby-$RUBY_VER.tar.gz ruby-$RUBY_VER
|
||||
|
||||
ENV PATH="${PATH}:/opt/ruby/bin:/opt/node/bin"
|
||||
|
||||
|
@ -107,11 +109,14 @@ RUN apt -y --no-install-recommends install \
|
|||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Add tini
|
||||
ENV TINI_VERSION="0.18.0"
|
||||
ENV TINI_SUM="12d20136605531b09a2c2dac02ccee85e1b874eb322ef6baf7561cd93f93c855"
|
||||
ADD https://github.com/krallin/tini/releases/download/v${TINI_VERSION}/tini /tini
|
||||
RUN echo "$TINI_SUM tini" | sha256sum -c -
|
||||
RUN chmod +x /tini
|
||||
ENV TINI_VERSION="0.19.0"
|
||||
RUN dpkgArch="$(dpkg --print-architecture)" && \
|
||||
ARCH=$dpkgArch && \
|
||||
wget https://github.com/krallin/tini/releases/download/v${TINI_VERSION}/tini-$ARCH \
|
||||
https://github.com/krallin/tini/releases/download/v${TINI_VERSION}/tini-$ARCH.sha256sum && \
|
||||
cat tini-$ARCH.sha256sum | sha256sum -c - && \
|
||||
mv tini-$ARCH /tini && rm tini-$ARCH.sha256sum && \
|
||||
chmod +x /tini
|
||||
|
||||
# Copy over mastodon source, and dependencies from building, and set permissions
|
||||
COPY --chown=mastodon:mastodon . /opt/mastodon
|
||||
|
|
75
Gemfile
75
Gemfile
|
@ -5,22 +5,19 @@ ruby '>= 2.5.0', '< 3.0.0'
|
|||
|
||||
gem 'pkg-config', '~> 1.4'
|
||||
|
||||
gem 'puma', '~> 4.3'
|
||||
gem 'rails', '~> 5.2.4.3'
|
||||
gem 'puma', '~> 5.1'
|
||||
gem 'rails', '~> 5.2.4.4'
|
||||
gem 'sprockets', '~> 3.7.2'
|
||||
gem 'thor', '~> 0.20'
|
||||
gem 'thor', '~> 1.0'
|
||||
gem 'rack', '~> 2.2.3'
|
||||
|
||||
gem 'thwait', '~> 0.1.0'
|
||||
gem 'e2mmap', '~> 0.1.0'
|
||||
|
||||
gem 'hamlit-rails', '~> 0.2'
|
||||
gem 'pg', '~> 1.2'
|
||||
gem 'makara', '~> 0.4'
|
||||
gem 'pghero', '~> 2.5'
|
||||
gem 'pghero', '~> 2.7'
|
||||
gem 'dotenv-rails', '~> 2.7'
|
||||
|
||||
gem 'aws-sdk-s3', '~> 1.73', require: false
|
||||
gem 'aws-sdk-s3', '~> 1.87', require: false
|
||||
gem 'fog-core', '<= 2.1.0'
|
||||
gem 'fog-openstack', '~> 0.3', require: false
|
||||
gem 'paperclip', '~> 6.0'
|
||||
|
@ -30,12 +27,12 @@ gem 'blurhash', '~> 0.1'
|
|||
|
||||
gem 'active_model_serializers', '~> 0.10'
|
||||
gem 'addressable', '~> 2.7'
|
||||
gem 'bootsnap', '~> 1.4', require: false
|
||||
gem 'bootsnap', '~> 1.5', require: false
|
||||
gem 'browser'
|
||||
gem 'charlock_holmes', '~> 0.7.7'
|
||||
gem 'iso-639'
|
||||
gem 'chewy', '~> 5.1'
|
||||
gem 'cld3', '~> 3.3.0'
|
||||
gem 'cld3', '~> 3.4.1'
|
||||
gem 'devise', '~> 4.7'
|
||||
gem 'devise-two-factor', '~> 3.1'
|
||||
|
||||
|
@ -43,10 +40,11 @@ group :pam_authentication, optional: true do
|
|||
gem 'devise_pam_authenticatable2', '~> 9.2'
|
||||
end
|
||||
|
||||
gem 'net-ldap', '~> 0.16'
|
||||
gem 'omniauth-cas', '~> 1.1'
|
||||
gem 'net-ldap', '~> 0.17'
|
||||
gem 'omniauth-cas', '~> 2.0'
|
||||
gem 'omniauth-saml', '~> 1.10'
|
||||
gem 'omniauth', '~> 1.9'
|
||||
gem 'omniauth-rails_csrf_protection', '~> 0.1'
|
||||
|
||||
gem 'color_diff', '~> 0.1'
|
||||
gem 'discard', '~> 1.2'
|
||||
|
@ -54,14 +52,12 @@ gem 'doorkeeper', '~> 5.4'
|
|||
gem 'ed25519', '~> 1.2'
|
||||
gem 'fast_blank', '~> 1.0'
|
||||
gem 'fastimage'
|
||||
gem 'goldfinger', '~> 2.1'
|
||||
gem 'hiredis', '~> 0.6'
|
||||
gem 'redis-namespace', '~> 1.7'
|
||||
gem 'redis-namespace', '~> 1.8'
|
||||
gem 'health_check', git: 'https://github.com/ianheggie/health_check', ref: '0b799ead604f900ed50685e9b2d469cd2befba5b'
|
||||
gem 'htmlentities', '~> 4.3'
|
||||
gem 'http', '~> 4.4'
|
||||
gem 'http_accept_language', '~> 2.1'
|
||||
gem 'http_parser.rb', '~> 0.6', git: 'https://github.com/tmm1/http_parser.rb', ref: '54b17ba8c7d8d20a16dfc65d1775241833219cf2', submodules: true
|
||||
gem 'httplog', '~> 1.4.3'
|
||||
gem 'idn-ruby', require: 'idn'
|
||||
gem 'kaminari', '~> 1.2'
|
||||
|
@ -71,10 +67,10 @@ gem 'nilsimsa', git: 'https://github.com/witgo/nilsimsa', ref: 'fd184883048b922b
|
|||
gem 'nokogiri', '~> 1.10'
|
||||
gem 'nsa', '~> 0.2'
|
||||
gem 'oj', '~> 3.10'
|
||||
gem 'ox', '~> 2.13'
|
||||
gem 'ox', '~> 2.14'
|
||||
gem 'parslet'
|
||||
gem 'parallel', '~> 1.19'
|
||||
gem 'posix-spawn', git: 'https://github.com/rtomayko/posix-spawn', ref: '58465d2e213991f8afb13b984854a49fcdcc980c'
|
||||
gem 'parallel', '~> 1.20'
|
||||
gem 'posix-spawn'
|
||||
gem 'pundit', '~> 2.1'
|
||||
gem 'premailer-rails'
|
||||
gem 'rack-attack', '~> 6.3'
|
||||
|
@ -83,23 +79,25 @@ gem 'rails-i18n', '~> 5.1'
|
|||
gem 'rails-settings-cached', '~> 0.6'
|
||||
gem 'redis', '~> 4.2', require: ['redis', 'redis/connection/hiredis']
|
||||
gem 'mario-redis-lock', '~> 1.2', require: 'redis_lock'
|
||||
gem 'rqrcode', '~> 1.1'
|
||||
gem 'ruby-progressbar', '~> 1.10'
|
||||
gem 'rqrcode', '~> 1.2'
|
||||
gem 'ruby-progressbar', '~> 1.11'
|
||||
gem 'sanitize', '~> 5.2'
|
||||
gem 'sidekiq', '~> 6.0'
|
||||
gem 'scenic', '~> 1.5'
|
||||
gem 'sidekiq', '~> 6.1'
|
||||
gem 'sidekiq-scheduler', '~> 3.0'
|
||||
gem 'sidekiq-unique-jobs', '~> 6.0'
|
||||
gem 'sidekiq-bulk', '~>0.2.0'
|
||||
gem 'simple-navigation', '~> 4.1'
|
||||
gem 'simple_form', '~> 5.0'
|
||||
gem 'sprockets-rails', '~> 3.2', require: 'sprockets/railtie'
|
||||
gem 'stoplight', '~> 2.2.0'
|
||||
gem 'strong_migrations', '~> 0.6'
|
||||
gem 'tty-prompt', '~> 0.21', require: false
|
||||
gem 'stoplight', '~> 2.2.1'
|
||||
gem 'strong_migrations', '~> 0.7'
|
||||
gem 'tty-prompt', '~> 0.23', require: false
|
||||
gem 'twitter-text', '~> 1.14'
|
||||
gem 'tzinfo-data', '~> 1.2020'
|
||||
gem 'webpacker', '~> 5.1'
|
||||
gem 'webpacker', '~> 5.2'
|
||||
gem 'webpush'
|
||||
gem 'webauthn', '~> 3.0.0.alpha1'
|
||||
|
||||
gem 'json-ld'
|
||||
gem 'json-ld-preloaded', '~> 3.1'
|
||||
|
@ -119,35 +117,35 @@ group :production, :test do
|
|||
end
|
||||
|
||||
group :test do
|
||||
gem 'capybara', '~> 3.33'
|
||||
gem 'capybara', '~> 3.34'
|
||||
gem 'climate_control', '~> 0.2'
|
||||
gem 'faker', '~> 2.13'
|
||||
gem 'faker', '~> 2.15'
|
||||
gem 'microformats', '~> 4.2'
|
||||
gem 'rails-controller-testing', '~> 1.0'
|
||||
gem 'rspec-sidekiq', '~> 3.1'
|
||||
gem 'simplecov', '~> 0.18', require: false
|
||||
gem 'webmock', '~> 3.8'
|
||||
gem 'parallel_tests', '~> 3.0'
|
||||
gem 'simplecov', '~> 0.21', require: false
|
||||
gem 'webmock', '~> 3.11'
|
||||
gem 'parallel_tests', '~> 3.4'
|
||||
gem 'rspec_junit_formatter', '~> 0.4'
|
||||
end
|
||||
|
||||
group :development do
|
||||
gem 'active_record_query_trace', '~> 1.7'
|
||||
gem 'active_record_query_trace', '~> 1.8'
|
||||
gem 'annotate', '~> 3.1'
|
||||
gem 'better_errors', '~> 2.7'
|
||||
gem 'better_errors', '~> 2.9'
|
||||
gem 'binding_of_caller', '~> 0.7'
|
||||
gem 'bullet', '~> 6.1'
|
||||
gem 'letter_opener', '~> 1.7'
|
||||
gem 'letter_opener_web', '~> 1.4'
|
||||
gem 'memory_profiler'
|
||||
gem 'rubocop', '~> 0.86', require: false
|
||||
gem 'rubocop-rails', '~> 2.6', require: false
|
||||
gem 'brakeman', '~> 4.8', require: false
|
||||
gem 'rubocop', '~> 1.7', require: false
|
||||
gem 'rubocop-rails', '~> 2.9', require: false
|
||||
gem 'brakeman', '~> 4.10', require: false
|
||||
gem 'bundler-audit', '~> 0.7', require: false
|
||||
|
||||
gem 'capistrano', '~> 3.14'
|
||||
gem 'capistrano-rails', '~> 1.5'
|
||||
gem 'capistrano-rbenv', '~> 2.1'
|
||||
gem 'capistrano-rails', '~> 1.6'
|
||||
gem 'capistrano-rbenv', '~> 2.2'
|
||||
gem 'capistrano-yarn', '~> 2.0'
|
||||
|
||||
gem 'stackprof'
|
||||
|
@ -160,3 +158,6 @@ end
|
|||
|
||||
gem 'concurrent-ruby', require: false
|
||||
gem 'connection_pool', require: false
|
||||
|
||||
gem 'xorcist', '~> 1.1'
|
||||
gem 'pluck_each', '~> 0.1.3'
|
||||
|
|
469
Gemfile.lock
469
Gemfile.lock
|
@ -6,21 +6,6 @@ GIT
|
|||
health_check (4.0.0.pre)
|
||||
rails (>= 4.0)
|
||||
|
||||
GIT
|
||||
remote: https://github.com/rtomayko/posix-spawn
|
||||
revision: 58465d2e213991f8afb13b984854a49fcdcc980c
|
||||
ref: 58465d2e213991f8afb13b984854a49fcdcc980c
|
||||
specs:
|
||||
posix-spawn (0.3.13)
|
||||
|
||||
GIT
|
||||
remote: https://github.com/tmm1/http_parser.rb
|
||||
revision: 54b17ba8c7d8d20a16dfc65d1775241833219cf2
|
||||
ref: 54b17ba8c7d8d20a16dfc65d1775241833219cf2
|
||||
submodules: true
|
||||
specs:
|
||||
http_parser.rb (0.6.1)
|
||||
|
||||
GIT
|
||||
remote: https://github.com/witgo/nilsimsa
|
||||
revision: fd184883048b922b176939f851338d0a4971a532
|
||||
|
@ -31,49 +16,49 @@ GIT
|
|||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
actioncable (5.2.4.3)
|
||||
actionpack (= 5.2.4.3)
|
||||
actioncable (5.2.4.4)
|
||||
actionpack (= 5.2.4.4)
|
||||
nio4r (~> 2.0)
|
||||
websocket-driver (>= 0.6.1)
|
||||
actionmailer (5.2.4.3)
|
||||
actionpack (= 5.2.4.3)
|
||||
actionview (= 5.2.4.3)
|
||||
activejob (= 5.2.4.3)
|
||||
actionmailer (5.2.4.4)
|
||||
actionpack (= 5.2.4.4)
|
||||
actionview (= 5.2.4.4)
|
||||
activejob (= 5.2.4.4)
|
||||
mail (~> 2.5, >= 2.5.4)
|
||||
rails-dom-testing (~> 2.0)
|
||||
actionpack (5.2.4.3)
|
||||
actionview (= 5.2.4.3)
|
||||
activesupport (= 5.2.4.3)
|
||||
actionpack (5.2.4.4)
|
||||
actionview (= 5.2.4.4)
|
||||
activesupport (= 5.2.4.4)
|
||||
rack (~> 2.0, >= 2.0.8)
|
||||
rack-test (>= 0.6.3)
|
||||
rails-dom-testing (~> 2.0)
|
||||
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
||||
actionview (5.2.4.3)
|
||||
activesupport (= 5.2.4.3)
|
||||
actionview (5.2.4.4)
|
||||
activesupport (= 5.2.4.4)
|
||||
builder (~> 3.1)
|
||||
erubi (~> 1.4)
|
||||
rails-dom-testing (~> 2.0)
|
||||
rails-html-sanitizer (~> 1.0, >= 1.0.3)
|
||||
active_model_serializers (0.10.10)
|
||||
actionpack (>= 4.1, < 6.1)
|
||||
activemodel (>= 4.1, < 6.1)
|
||||
active_model_serializers (0.10.12)
|
||||
actionpack (>= 4.1, < 6.2)
|
||||
activemodel (>= 4.1, < 6.2)
|
||||
case_transform (>= 0.2)
|
||||
jsonapi-renderer (>= 0.1.1.beta1, < 0.3)
|
||||
active_record_query_trace (1.7)
|
||||
activejob (5.2.4.3)
|
||||
activesupport (= 5.2.4.3)
|
||||
active_record_query_trace (1.8)
|
||||
activejob (5.2.4.4)
|
||||
activesupport (= 5.2.4.4)
|
||||
globalid (>= 0.3.6)
|
||||
activemodel (5.2.4.3)
|
||||
activesupport (= 5.2.4.3)
|
||||
activerecord (5.2.4.3)
|
||||
activemodel (= 5.2.4.3)
|
||||
activesupport (= 5.2.4.3)
|
||||
activemodel (5.2.4.4)
|
||||
activesupport (= 5.2.4.4)
|
||||
activerecord (5.2.4.4)
|
||||
activemodel (= 5.2.4.4)
|
||||
activesupport (= 5.2.4.4)
|
||||
arel (>= 9.0)
|
||||
activestorage (5.2.4.3)
|
||||
actionpack (= 5.2.4.3)
|
||||
activerecord (= 5.2.4.3)
|
||||
activestorage (5.2.4.4)
|
||||
actionpack (= 5.2.4.4)
|
||||
activerecord (= 5.2.4.4)
|
||||
marcel (~> 0.3.1)
|
||||
activesupport (5.2.4.3)
|
||||
activesupport (5.2.4.4)
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
i18n (>= 0.7, < 2)
|
||||
minitest (~> 5.1)
|
||||
|
@ -82,6 +67,7 @@ GEM
|
|||
public_suffix (>= 2.0.2, < 5.0)
|
||||
airbrussh (1.4.0)
|
||||
sshkit (>= 1.6.1, != 1.7.0)
|
||||
android_key_attestation (0.3.0)
|
||||
annotate (3.1.1)
|
||||
activerecord (>= 3.2, < 7.0)
|
||||
rake (>= 10.4, < 14.0)
|
||||
|
@ -91,37 +77,39 @@ GEM
|
|||
encryptor (~> 3.0.0)
|
||||
av (0.9.0)
|
||||
cocaine (~> 0.5.3)
|
||||
awrence (1.1.1)
|
||||
aws-eventstream (1.1.0)
|
||||
aws-partitions (1.338.0)
|
||||
aws-sdk-core (3.103.0)
|
||||
aws-partitions (1.413.0)
|
||||
aws-sdk-core (3.110.0)
|
||||
aws-eventstream (~> 1, >= 1.0.2)
|
||||
aws-partitions (~> 1, >= 1.239.0)
|
||||
aws-sigv4 (~> 1.1)
|
||||
jmespath (~> 1.0)
|
||||
aws-sdk-kms (1.36.0)
|
||||
aws-sdk-core (~> 3, >= 3.99.0)
|
||||
aws-sdk-kms (1.40.0)
|
||||
aws-sdk-core (~> 3, >= 3.109.0)
|
||||
aws-sigv4 (~> 1.1)
|
||||
aws-sdk-s3 (1.73.0)
|
||||
aws-sdk-core (~> 3, >= 3.102.1)
|
||||
aws-sdk-s3 (1.87.0)
|
||||
aws-sdk-core (~> 3, >= 3.109.0)
|
||||
aws-sdk-kms (~> 1)
|
||||
aws-sigv4 (~> 1.1)
|
||||
aws-sigv4 (1.2.1)
|
||||
aws-sigv4 (1.2.2)
|
||||
aws-eventstream (~> 1, >= 1.0.2)
|
||||
bcrypt (3.1.13)
|
||||
better_errors (2.7.1)
|
||||
bcrypt (3.1.16)
|
||||
better_errors (2.9.1)
|
||||
coderay (>= 1.0.0)
|
||||
erubi (>= 1.0.0)
|
||||
rack (>= 0.9.0)
|
||||
bindata (2.4.8)
|
||||
binding_of_caller (0.8.0)
|
||||
debug_inspector (>= 0.0.1)
|
||||
blurhash (0.1.4)
|
||||
ffi (~> 1.10.0)
|
||||
bootsnap (1.4.6)
|
||||
bootsnap (1.5.1)
|
||||
msgpack (~> 1.0)
|
||||
brakeman (4.8.2)
|
||||
brakeman (4.10.1)
|
||||
browser (4.2.0)
|
||||
builder (3.2.4)
|
||||
bullet (6.1.0)
|
||||
bullet (6.1.2)
|
||||
activesupport (>= 3.0.0)
|
||||
uniform_notifier (~> 1.11)
|
||||
bundler-audit (0.7.0.1)
|
||||
|
@ -133,17 +121,17 @@ GEM
|
|||
i18n
|
||||
rake (>= 10.0.0)
|
||||
sshkit (>= 1.9.0)
|
||||
capistrano-bundler (1.6.0)
|
||||
capistrano-bundler (2.0.1)
|
||||
capistrano (~> 3.1)
|
||||
capistrano-rails (1.5.0)
|
||||
capistrano-rails (1.6.1)
|
||||
capistrano (~> 3.1)
|
||||
capistrano-bundler (~> 1.1)
|
||||
capistrano-rbenv (2.1.6)
|
||||
capistrano-bundler (>= 1.1, < 3)
|
||||
capistrano-rbenv (2.2.0)
|
||||
capistrano (~> 3.1)
|
||||
sshkit (~> 1.3)
|
||||
capistrano-yarn (2.0.2)
|
||||
capistrano (~> 3.0)
|
||||
capybara (3.33.0)
|
||||
capybara (3.34.0)
|
||||
addressable
|
||||
mini_mime (>= 0.1.3)
|
||||
nokogiri (~> 1.8)
|
||||
|
@ -153,28 +141,32 @@ GEM
|
|||
xpath (~> 3.2)
|
||||
case_transform (0.2)
|
||||
activesupport
|
||||
cbor (0.5.9.6)
|
||||
charlock_holmes (0.7.7)
|
||||
chewy (5.1.0)
|
||||
activesupport (>= 4.0)
|
||||
elasticsearch (>= 2.0.0)
|
||||
elasticsearch-dsl
|
||||
chunky_png (1.3.11)
|
||||
cld3 (3.3.0)
|
||||
ffi (>= 1.1.0, < 1.12.0)
|
||||
chunky_png (1.3.15)
|
||||
cld3 (3.4.1)
|
||||
ffi (>= 1.1.0, < 1.15.0)
|
||||
climate_control (0.2.0)
|
||||
cocaine (0.5.8)
|
||||
climate_control (>= 0.0.3, < 1.0)
|
||||
coderay (1.1.3)
|
||||
color_diff (0.1)
|
||||
concurrent-ruby (1.1.6)
|
||||
concurrent-ruby (1.1.7)
|
||||
connection_pool (2.2.3)
|
||||
crack (0.4.3)
|
||||
safe_yaml (~> 1.0.0)
|
||||
cose (1.0.0)
|
||||
cbor (~> 0.5.9)
|
||||
openssl-signature_algorithm (~> 0.4.0)
|
||||
crack (0.4.5)
|
||||
rexml
|
||||
crass (1.0.6)
|
||||
css_parser (1.7.1)
|
||||
addressable
|
||||
debug_inspector (0.0.3)
|
||||
devise (4.7.2)
|
||||
devise (4.7.3)
|
||||
bcrypt (~> 3.0)
|
||||
orm_adapter (~> 0.1)
|
||||
railties (>= 4.1.0)
|
||||
|
@ -192,39 +184,38 @@ GEM
|
|||
diff-lcs (1.4.4)
|
||||
discard (1.2.0)
|
||||
activerecord (>= 4.2, < 7)
|
||||
docile (1.3.2)
|
||||
docile (1.3.4)
|
||||
domain_name (0.5.20190701)
|
||||
unf (>= 0.0.5, < 1.0.0)
|
||||
doorkeeper (5.4.0)
|
||||
railties (>= 5)
|
||||
dotenv (2.7.5)
|
||||
dotenv-rails (2.7.5)
|
||||
dotenv (= 2.7.5)
|
||||
railties (>= 3.2, < 6.1)
|
||||
dotenv (2.7.6)
|
||||
dotenv-rails (2.7.6)
|
||||
dotenv (= 2.7.6)
|
||||
railties (>= 3.2)
|
||||
e2mmap (0.1.0)
|
||||
ed25519 (1.2.4)
|
||||
elasticsearch (7.8.0)
|
||||
elasticsearch-api (= 7.8.0)
|
||||
elasticsearch-transport (= 7.8.0)
|
||||
elasticsearch-api (7.8.0)
|
||||
elasticsearch (7.9.0)
|
||||
elasticsearch-api (= 7.9.0)
|
||||
elasticsearch-transport (= 7.9.0)
|
||||
elasticsearch-api (7.9.0)
|
||||
multi_json
|
||||
elasticsearch-dsl (0.1.9)
|
||||
elasticsearch-transport (7.8.0)
|
||||
elasticsearch-transport (7.9.0)
|
||||
faraday (~> 1)
|
||||
multi_json
|
||||
encryptor (3.0.0)
|
||||
equatable (0.6.1)
|
||||
erubi (1.9.0)
|
||||
erubi (1.10.0)
|
||||
et-orbi (1.2.4)
|
||||
tzinfo
|
||||
excon (0.75.0)
|
||||
excon (0.76.0)
|
||||
fabrication (2.21.1)
|
||||
faker (2.13.0)
|
||||
faker (2.15.1)
|
||||
i18n (>= 1.6, < 2)
|
||||
faraday (1.0.1)
|
||||
multipart-post (>= 1.2, < 3)
|
||||
fast_blank (1.0.0)
|
||||
fastimage (2.1.7)
|
||||
fastimage (2.2.1)
|
||||
ffi (1.10.0)
|
||||
ffi-compiler (1.0.1)
|
||||
ffi (>= 1.0.0)
|
||||
|
@ -242,20 +233,15 @@ GEM
|
|||
fog-json (>= 1.0)
|
||||
ipaddress (>= 0.8)
|
||||
formatador (0.2.5)
|
||||
fugit (1.3.6)
|
||||
fugit (1.3.9)
|
||||
et-orbi (~> 1.1, >= 1.1.8)
|
||||
raabro (~> 1.3)
|
||||
fuubar (2.5.0)
|
||||
fuubar (2.5.1)
|
||||
rspec-core (~> 3.0)
|
||||
ruby-progressbar (~> 1.4)
|
||||
globalid (0.4.2)
|
||||
activesupport (>= 4.2.0)
|
||||
goldfinger (2.1.1)
|
||||
addressable (~> 2.5)
|
||||
http (~> 4.0)
|
||||
nokogiri (~> 1.8)
|
||||
oj (~> 3.0)
|
||||
hamlit (2.11.0)
|
||||
hamlit (2.13.0)
|
||||
temple (>= 0.8.2)
|
||||
thor
|
||||
tilt
|
||||
|
@ -286,9 +272,9 @@ GEM
|
|||
httplog (1.4.3)
|
||||
rack (>= 1.0)
|
||||
rainbow (>= 2.0.0)
|
||||
i18n (1.8.3)
|
||||
i18n (1.8.5)
|
||||
concurrent-ruby (~> 1.0)
|
||||
i18n-tasks (0.9.31)
|
||||
i18n-tasks (0.9.33)
|
||||
activesupport (>= 4.0.2)
|
||||
ast (>= 2.1.0)
|
||||
erubi
|
||||
|
@ -304,18 +290,18 @@ GEM
|
|||
jmespath (1.4.0)
|
||||
json (2.3.1)
|
||||
json-canonicalization (0.2.0)
|
||||
json-ld (3.1.4)
|
||||
json-ld (3.1.7)
|
||||
htmlentities (~> 4.3)
|
||||
json-canonicalization (~> 0.2)
|
||||
link_header (~> 0.0, >= 0.0.8)
|
||||
multi_json (~> 1.14)
|
||||
rack (~> 2.0)
|
||||
rdf (~> 3.1)
|
||||
json-ld-preloaded (3.1.3)
|
||||
json-ld-preloaded (3.1.4)
|
||||
json-ld (~> 3.1)
|
||||
rdf (~> 3.1)
|
||||
jsonapi-renderer (0.2.2)
|
||||
jwt (2.2.1)
|
||||
jwt (2.2.2)
|
||||
kaminari (1.2.1)
|
||||
activesupport (>= 4.1.0)
|
||||
kaminari-actionview (= 1.2.1)
|
||||
|
@ -342,7 +328,7 @@ GEM
|
|||
activesupport (>= 4)
|
||||
railties (>= 4)
|
||||
request_store (~> 1.0)
|
||||
loofah (2.6.0)
|
||||
loofah (2.8.0)
|
||||
crass (~> 1.0.2)
|
||||
nokogiri (>= 1.5.9)
|
||||
mail (2.7.1)
|
||||
|
@ -353,9 +339,9 @@ GEM
|
|||
mimemagic (~> 0.3.2)
|
||||
mario-redis-lock (1.2.1)
|
||||
redis (>= 3.0.5)
|
||||
memory_profiler (0.9.14)
|
||||
memory_profiler (1.0.0)
|
||||
method_source (1.0.0)
|
||||
microformats (4.2.0)
|
||||
microformats (4.2.1)
|
||||
json (~> 2.2)
|
||||
nokogiri (~> 1.10)
|
||||
mime-types (3.3.1)
|
||||
|
@ -364,17 +350,16 @@ GEM
|
|||
mimemagic (0.3.5)
|
||||
mini_mime (1.0.2)
|
||||
mini_portile2 (2.4.0)
|
||||
minitest (5.14.1)
|
||||
minitest (5.14.2)
|
||||
msgpack (1.3.3)
|
||||
multi_json (1.14.1)
|
||||
multi_json (1.15.0)
|
||||
multipart-post (2.1.1)
|
||||
necromancer (0.5.1)
|
||||
net-ldap (0.16.2)
|
||||
net-ldap (0.17.0)
|
||||
net-scp (3.0.0)
|
||||
net-ssh (>= 2.6.5, < 7.0.0)
|
||||
net-ssh (6.1.0)
|
||||
nio4r (2.5.2)
|
||||
nokogiri (1.10.9)
|
||||
nio4r (2.5.4)
|
||||
nokogiri (1.10.10)
|
||||
mini_portile2 (~> 2.4.0)
|
||||
nokogumbo (2.0.2)
|
||||
nokogiri (~> 1.8, >= 1.8.4)
|
||||
|
@ -383,19 +368,24 @@ GEM
|
|||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
sidekiq (>= 3.5)
|
||||
statsd-ruby (~> 1.4, >= 1.4.0)
|
||||
oj (3.10.6)
|
||||
oj (3.10.18)
|
||||
omniauth (1.9.1)
|
||||
hashie (>= 3.4.6)
|
||||
rack (>= 1.6.2, < 3)
|
||||
omniauth-cas (1.1.1)
|
||||
omniauth-cas (2.0.0)
|
||||
addressable (~> 2.3)
|
||||
nokogiri (~> 1.5)
|
||||
omniauth (~> 1.2)
|
||||
omniauth-saml (1.10.2)
|
||||
omniauth-rails_csrf_protection (0.1.2)
|
||||
actionpack (>= 4.2)
|
||||
omniauth (>= 1.3.1)
|
||||
omniauth-saml (1.10.3)
|
||||
omniauth (~> 1.3, >= 1.3.2)
|
||||
ruby-saml (~> 1.9)
|
||||
openssl (2.2.0)
|
||||
openssl-signature_algorithm (0.4.0)
|
||||
orm_adapter (0.5.0)
|
||||
ox (2.13.2)
|
||||
ox (2.14.0)
|
||||
paperclip (6.0.0)
|
||||
activemodel (>= 4.2.0)
|
||||
activesupport (>= 4.2.0)
|
||||
|
@ -405,20 +395,23 @@ GEM
|
|||
paperclip-av-transcoder (0.6.4)
|
||||
av (~> 0.9.0)
|
||||
paperclip (>= 2.5.2)
|
||||
parallel (1.19.2)
|
||||
parallel_tests (3.0.0)
|
||||
parallel (1.20.1)
|
||||
parallel_tests (3.4.0)
|
||||
parallel
|
||||
parser (2.7.1.4)
|
||||
parser (3.0.0.0)
|
||||
ast (~> 2.4.1)
|
||||
parslet (2.0.0)
|
||||
pastel (0.7.4)
|
||||
equatable (~> 0.6)
|
||||
pastel (0.8.0)
|
||||
tty-color (~> 0.5)
|
||||
pg (1.2.3)
|
||||
pghero (2.5.1)
|
||||
pghero (2.7.3)
|
||||
activerecord (>= 5)
|
||||
pkg-config (1.4.1)
|
||||
premailer (1.11.1)
|
||||
pkg-config (1.4.4)
|
||||
pluck_each (0.1.3)
|
||||
activerecord (> 3.2.0)
|
||||
activesupport (> 3.0.0)
|
||||
posix-spawn (0.3.15)
|
||||
premailer (1.14.2)
|
||||
addressable
|
||||
css_parser (>= 1.6.0)
|
||||
htmlentities (>= 4.0.0)
|
||||
|
@ -434,12 +427,12 @@ GEM
|
|||
pry (~> 0.13.0)
|
||||
pry-rails (0.3.9)
|
||||
pry (>= 0.10.4)
|
||||
public_suffix (4.0.5)
|
||||
puma (4.3.5)
|
||||
public_suffix (4.0.6)
|
||||
puma (5.1.1)
|
||||
nio4r (~> 2.0)
|
||||
pundit (2.1.0)
|
||||
activesupport (>= 3.0.0)
|
||||
raabro (1.3.1)
|
||||
raabro (1.3.3)
|
||||
rack (2.2.3)
|
||||
rack-attack (6.3.1)
|
||||
rack (>= 1.0, < 3)
|
||||
|
@ -449,18 +442,18 @@ GEM
|
|||
rack
|
||||
rack-test (1.1.0)
|
||||
rack (>= 1.0, < 3)
|
||||
rails (5.2.4.3)
|
||||
actioncable (= 5.2.4.3)
|
||||
actionmailer (= 5.2.4.3)
|
||||
actionpack (= 5.2.4.3)
|
||||
actionview (= 5.2.4.3)
|
||||
activejob (= 5.2.4.3)
|
||||
activemodel (= 5.2.4.3)
|
||||
activerecord (= 5.2.4.3)
|
||||
activestorage (= 5.2.4.3)
|
||||
activesupport (= 5.2.4.3)
|
||||
rails (5.2.4.4)
|
||||
actioncable (= 5.2.4.4)
|
||||
actionmailer (= 5.2.4.4)
|
||||
actionpack (= 5.2.4.4)
|
||||
actionview (= 5.2.4.4)
|
||||
activejob (= 5.2.4.4)
|
||||
activemodel (= 5.2.4.4)
|
||||
activerecord (= 5.2.4.4)
|
||||
activestorage (= 5.2.4.4)
|
||||
activesupport (= 5.2.4.4)
|
||||
bundler (>= 1.3.0)
|
||||
railties (= 5.2.4.3)
|
||||
railties (= 5.2.4.4)
|
||||
sprockets-rails (>= 2.0.0)
|
||||
rails-controller-testing (1.0.5)
|
||||
actionpack (>= 5.0.1.rc1)
|
||||
|
@ -476,20 +469,20 @@ GEM
|
|||
railties (>= 5.0, < 6)
|
||||
rails-settings-cached (0.6.6)
|
||||
rails (>= 4.2.0)
|
||||
railties (5.2.4.3)
|
||||
actionpack (= 5.2.4.3)
|
||||
activesupport (= 5.2.4.3)
|
||||
railties (5.2.4.4)
|
||||
actionpack (= 5.2.4.4)
|
||||
activesupport (= 5.2.4.4)
|
||||
method_source
|
||||
rake (>= 0.8.7)
|
||||
thor (>= 0.19.0, < 2.0)
|
||||
rainbow (3.0.0)
|
||||
rake (13.0.1)
|
||||
rdf (3.1.4)
|
||||
rake (13.0.3)
|
||||
rdf (3.1.8)
|
||||
hamster (~> 3.0)
|
||||
link_header (~> 0.0, >= 0.0.8)
|
||||
rdf-normalize (0.4.0)
|
||||
rdf (~> 3.1)
|
||||
redis (4.2.1)
|
||||
redis (4.2.5)
|
||||
redis-actionpack (5.2.0)
|
||||
actionpack (>= 5, < 7)
|
||||
redis-rack (>= 2.1.0, < 3)
|
||||
|
@ -497,9 +490,9 @@ GEM
|
|||
redis-activesupport (5.2.0)
|
||||
activesupport (>= 3, < 7)
|
||||
redis-store (>= 1.3, < 2)
|
||||
redis-namespace (1.7.0)
|
||||
redis-namespace (1.8.0)
|
||||
redis (>= 3.0.4)
|
||||
redis-rack (2.1.2)
|
||||
redis-rack (2.1.3)
|
||||
rack (>= 2.0.8, < 3)
|
||||
redis-store (>= 1.2, < 2)
|
||||
redis-rails (5.0.2)
|
||||
|
@ -508,7 +501,7 @@ GEM
|
|||
redis-store (>= 1.2, < 2)
|
||||
redis-store (1.9.0)
|
||||
redis (>= 4, < 5)
|
||||
regexp_parser (1.7.1)
|
||||
regexp_parser (1.8.2)
|
||||
request_store (1.5.0)
|
||||
rack (>= 1.4)
|
||||
responders (3.0.1)
|
||||
|
@ -517,59 +510,64 @@ GEM
|
|||
rexml (3.2.4)
|
||||
rotp (2.1.2)
|
||||
rpam2 (4.0.2)
|
||||
rqrcode (1.1.2)
|
||||
rqrcode (1.2.0)
|
||||
chunky_png (~> 1.0)
|
||||
rqrcode_core (~> 0.1)
|
||||
rqrcode_core (0.1.2)
|
||||
rspec-core (3.9.2)
|
||||
rspec-support (~> 3.9.3)
|
||||
rspec-expectations (3.9.2)
|
||||
rqrcode_core (~> 0.2)
|
||||
rqrcode_core (0.2.0)
|
||||
rspec-core (3.10.1)
|
||||
rspec-support (~> 3.10.0)
|
||||
rspec-expectations (3.10.1)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.9.0)
|
||||
rspec-mocks (3.9.1)
|
||||
rspec-support (~> 3.10.0)
|
||||
rspec-mocks (3.10.1)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.9.0)
|
||||
rspec-rails (4.0.1)
|
||||
rspec-support (~> 3.10.0)
|
||||
rspec-rails (4.0.2)
|
||||
actionpack (>= 4.2)
|
||||
activesupport (>= 4.2)
|
||||
railties (>= 4.2)
|
||||
rspec-core (~> 3.9)
|
||||
rspec-expectations (~> 3.9)
|
||||
rspec-mocks (~> 3.9)
|
||||
rspec-support (~> 3.9)
|
||||
rspec-core (~> 3.10)
|
||||
rspec-expectations (~> 3.10)
|
||||
rspec-mocks (~> 3.10)
|
||||
rspec-support (~> 3.10)
|
||||
rspec-sidekiq (3.1.0)
|
||||
rspec-core (~> 3.0, >= 3.0.0)
|
||||
sidekiq (>= 2.4.0)
|
||||
rspec-support (3.9.3)
|
||||
rspec-support (3.10.1)
|
||||
rspec_junit_formatter (0.4.1)
|
||||
rspec-core (>= 2, < 4, != 2.12.0)
|
||||
rubocop (0.86.0)
|
||||
rubocop (1.7.0)
|
||||
parallel (~> 1.10)
|
||||
parser (>= 2.7.0.1)
|
||||
parser (>= 2.7.1.5)
|
||||
rainbow (>= 2.2.2, < 4.0)
|
||||
regexp_parser (>= 1.7)
|
||||
regexp_parser (>= 1.8, < 3.0)
|
||||
rexml
|
||||
rubocop-ast (>= 0.0.3, < 1.0)
|
||||
rubocop-ast (>= 1.2.0, < 2.0)
|
||||
ruby-progressbar (~> 1.7)
|
||||
unicode-display_width (>= 1.4.0, < 2.0)
|
||||
rubocop-ast (0.1.0)
|
||||
parser (>= 2.7.0.1)
|
||||
rubocop-rails (2.6.0)
|
||||
rubocop-ast (1.3.0)
|
||||
parser (>= 2.7.1.5)
|
||||
rubocop-rails (2.9.1)
|
||||
activesupport (>= 4.2.0)
|
||||
rack (>= 1.1)
|
||||
rubocop (>= 0.82.0)
|
||||
ruby-progressbar (1.10.1)
|
||||
rubocop (>= 0.90.0, < 2.0)
|
||||
ruby-progressbar (1.11.0)
|
||||
ruby-saml (1.11.0)
|
||||
nokogiri (>= 1.5.10)
|
||||
rufus-scheduler (3.6.0)
|
||||
fugit (~> 1.1, >= 1.1.6)
|
||||
safe_yaml (1.0.5)
|
||||
safety_net_attestation (0.4.0)
|
||||
jwt (~> 2.0)
|
||||
sanitize (5.2.1)
|
||||
crass (~> 1.0.2)
|
||||
nokogiri (>= 1.8.0)
|
||||
nokogumbo (~> 2.0)
|
||||
scenic (1.5.4)
|
||||
activerecord (>= 4.0.0)
|
||||
railties (>= 4.0.0)
|
||||
securecompare (1.0.0)
|
||||
semantic_range (2.3.0)
|
||||
sidekiq (6.1.0)
|
||||
sidekiq (6.1.2)
|
||||
connection_pool (>= 2.2.2)
|
||||
rack (~> 2.0)
|
||||
redis (>= 4.2.0)
|
||||
|
@ -582,74 +580,89 @@ GEM
|
|||
sidekiq (>= 3)
|
||||
thwait
|
||||
tilt (>= 1.4.0)
|
||||
sidekiq-unique-jobs (6.0.22)
|
||||
sidekiq-unique-jobs (6.0.25)
|
||||
concurrent-ruby (~> 1.0, >= 1.0.5)
|
||||
sidekiq (>= 4.0, < 7.0)
|
||||
thor (~> 0)
|
||||
thor (>= 0.20, < 2.0)
|
||||
simple-navigation (4.1.0)
|
||||
activesupport (>= 2.3.2)
|
||||
simple_form (5.0.2)
|
||||
simple_form (5.0.3)
|
||||
actionpack (>= 5.0)
|
||||
activemodel (>= 5.0)
|
||||
simplecov (0.18.5)
|
||||
simplecov (0.21.0)
|
||||
docile (~> 1.1)
|
||||
simplecov-html (~> 0.11)
|
||||
simplecov-html (0.12.2)
|
||||
simplecov_json_formatter (~> 0.1)
|
||||
simplecov-html (0.12.3)
|
||||
simplecov_json_formatter (0.1.2)
|
||||
sprockets (3.7.2)
|
||||
concurrent-ruby (~> 1.0)
|
||||
rack (> 1, < 3)
|
||||
sprockets-rails (3.2.1)
|
||||
sprockets-rails (3.2.2)
|
||||
actionpack (>= 4.0)
|
||||
activesupport (>= 4.0)
|
||||
sprockets (>= 3.0.0)
|
||||
sshkit (1.21.0)
|
||||
net-scp (>= 1.1.2)
|
||||
net-ssh (>= 2.8.0)
|
||||
stackprof (0.2.15)
|
||||
stackprof (0.2.16)
|
||||
statsd-ruby (1.4.0)
|
||||
stoplight (2.2.0)
|
||||
stoplight (2.2.1)
|
||||
streamio-ffmpeg (3.0.2)
|
||||
multi_json (~> 1.8)
|
||||
strong_migrations (0.6.8)
|
||||
strong_migrations (0.7.4)
|
||||
activerecord (>= 5)
|
||||
temple (0.8.2)
|
||||
terminal-table (1.8.0)
|
||||
terminal-table (2.0.0)
|
||||
unicode-display_width (~> 1.1, >= 1.1.1)
|
||||
terrapin (0.6.0)
|
||||
climate_control (>= 0.0.3, < 1.0)
|
||||
thor (0.20.3)
|
||||
thor (1.0.1)
|
||||
thread_safe (0.3.6)
|
||||
thwait (0.1.0)
|
||||
thwait (0.2.0)
|
||||
e2mmap
|
||||
tilt (2.0.10)
|
||||
tty-color (0.5.1)
|
||||
tpm-key_attestation (0.9.0)
|
||||
bindata (~> 2.4)
|
||||
openssl-signature_algorithm (~> 0.4.0)
|
||||
tty-color (0.6.0)
|
||||
tty-cursor (0.7.1)
|
||||
tty-prompt (0.21.0)
|
||||
necromancer (~> 0.5.0)
|
||||
pastel (~> 0.7.0)
|
||||
tty-reader (~> 0.7.0)
|
||||
tty-reader (0.7.0)
|
||||
tty-prompt (0.23.0)
|
||||
pastel (~> 0.8)
|
||||
tty-reader (~> 0.8)
|
||||
tty-reader (0.9.0)
|
||||
tty-cursor (~> 0.7)
|
||||
tty-screen (~> 0.7)
|
||||
wisper (~> 2.0.0)
|
||||
tty-screen (0.8.0)
|
||||
tty-screen (~> 0.8)
|
||||
wisper (~> 2.0)
|
||||
tty-screen (0.8.1)
|
||||
twitter-text (1.14.7)
|
||||
unf (~> 0.1.0)
|
||||
tzinfo (1.2.7)
|
||||
tzinfo (1.2.9)
|
||||
thread_safe (~> 0.1)
|
||||
tzinfo-data (1.2020.1)
|
||||
tzinfo-data (1.2020.6)
|
||||
tzinfo (>= 1.0.0)
|
||||
unf (0.1.4)
|
||||
unf_ext
|
||||
unf_ext (0.0.7.7)
|
||||
unicode-display_width (1.7.0)
|
||||
uniform_notifier (1.13.0)
|
||||
warden (1.2.8)
|
||||
rack (>= 2.0.6)
|
||||
webmock (3.8.3)
|
||||
warden (1.2.9)
|
||||
rack (>= 2.0.9)
|
||||
webauthn (3.0.0.alpha1)
|
||||
android_key_attestation (~> 0.3.0)
|
||||
awrence (~> 1.1)
|
||||
bindata (~> 2.4)
|
||||
cbor (~> 0.5.9)
|
||||
cose (~> 1.0)
|
||||
openssl (~> 2.0)
|
||||
safety_net_attestation (~> 0.4.0)
|
||||
securecompare (~> 1.0)
|
||||
tpm-key_attestation (~> 0.9.0)
|
||||
webmock (3.11.0)
|
||||
addressable (>= 2.3.6)
|
||||
crack (>= 0.3.2)
|
||||
hashdiff (>= 0.4.0, < 2.0.0)
|
||||
webpacker (5.1.1)
|
||||
webpacker (5.2.1)
|
||||
activesupport (>= 5.2)
|
||||
rack-proxy (>= 0.6.1)
|
||||
railties (>= 5.2)
|
||||
|
@ -657,10 +670,11 @@ GEM
|
|||
webpush (0.3.8)
|
||||
hkdf (~> 0.2)
|
||||
jwt (~> 2.0)
|
||||
websocket-driver (0.7.2)
|
||||
websocket-driver (0.7.3)
|
||||
websocket-extensions (>= 0.1.0)
|
||||
websocket-extensions (0.1.5)
|
||||
wisper (2.0.1)
|
||||
xorcist (1.1.2)
|
||||
xpath (3.2.0)
|
||||
nokogiri (~> 1.8)
|
||||
|
||||
|
@ -669,26 +683,26 @@ PLATFORMS
|
|||
|
||||
DEPENDENCIES
|
||||
active_model_serializers (~> 0.10)
|
||||
active_record_query_trace (~> 1.7)
|
||||
active_record_query_trace (~> 1.8)
|
||||
addressable (~> 2.7)
|
||||
annotate (~> 3.1)
|
||||
aws-sdk-s3 (~> 1.73)
|
||||
better_errors (~> 2.7)
|
||||
aws-sdk-s3 (~> 1.87)
|
||||
better_errors (~> 2.9)
|
||||
binding_of_caller (~> 0.7)
|
||||
blurhash (~> 0.1)
|
||||
bootsnap (~> 1.4)
|
||||
brakeman (~> 4.8)
|
||||
bootsnap (~> 1.5)
|
||||
brakeman (~> 4.10)
|
||||
browser
|
||||
bullet (~> 6.1)
|
||||
bundler-audit (~> 0.7)
|
||||
capistrano (~> 3.14)
|
||||
capistrano-rails (~> 1.5)
|
||||
capistrano-rbenv (~> 2.1)
|
||||
capistrano-rails (~> 1.6)
|
||||
capistrano-rbenv (~> 2.2)
|
||||
capistrano-yarn (~> 2.0)
|
||||
capybara (~> 3.33)
|
||||
capybara (~> 3.34)
|
||||
charlock_holmes (~> 0.7.7)
|
||||
chewy (~> 5.1)
|
||||
cld3 (~> 3.3.0)
|
||||
cld3 (~> 3.4.1)
|
||||
climate_control (~> 0.2)
|
||||
color_diff (~> 0.1)
|
||||
concurrent-ruby
|
||||
|
@ -699,23 +713,20 @@ DEPENDENCIES
|
|||
discard (~> 1.2)
|
||||
doorkeeper (~> 5.4)
|
||||
dotenv-rails (~> 2.7)
|
||||
e2mmap (~> 0.1.0)
|
||||
ed25519 (~> 1.2)
|
||||
fabrication (~> 2.21)
|
||||
faker (~> 2.13)
|
||||
faker (~> 2.15)
|
||||
fast_blank (~> 1.0)
|
||||
fastimage
|
||||
fog-core (<= 2.1.0)
|
||||
fog-openstack (~> 0.3)
|
||||
fuubar (~> 2.5)
|
||||
goldfinger (~> 2.1)
|
||||
hamlit-rails (~> 0.2)
|
||||
health_check!
|
||||
hiredis (~> 0.6)
|
||||
htmlentities (~> 4.3)
|
||||
http (~> 4.4)
|
||||
http_accept_language (~> 2.1)
|
||||
http_parser.rb (~> 0.6)!
|
||||
httplog (~> 1.4.3)
|
||||
i18n-tasks (~> 0.9)
|
||||
idn-ruby
|
||||
|
@ -732,67 +743,71 @@ DEPENDENCIES
|
|||
memory_profiler
|
||||
microformats (~> 4.2)
|
||||
mime-types (~> 3.3.1)
|
||||
net-ldap (~> 0.16)
|
||||
net-ldap (~> 0.17)
|
||||
nilsimsa!
|
||||
nokogiri (~> 1.10)
|
||||
nsa (~> 0.2)
|
||||
oj (~> 3.10)
|
||||
omniauth (~> 1.9)
|
||||
omniauth-cas (~> 1.1)
|
||||
omniauth-cas (~> 2.0)
|
||||
omniauth-rails_csrf_protection (~> 0.1)
|
||||
omniauth-saml (~> 1.10)
|
||||
ox (~> 2.13)
|
||||
ox (~> 2.14)
|
||||
paperclip (~> 6.0)
|
||||
paperclip-av-transcoder (~> 0.6)
|
||||
parallel (~> 1.19)
|
||||
parallel_tests (~> 3.0)
|
||||
parallel (~> 1.20)
|
||||
parallel_tests (~> 3.4)
|
||||
parslet
|
||||
pg (~> 1.2)
|
||||
pghero (~> 2.5)
|
||||
pghero (~> 2.7)
|
||||
pkg-config (~> 1.4)
|
||||
posix-spawn!
|
||||
pluck_each (~> 0.1.3)
|
||||
posix-spawn
|
||||
premailer-rails
|
||||
private_address_check (~> 0.5)
|
||||
pry-byebug (~> 3.9)
|
||||
pry-rails (~> 0.3)
|
||||
puma (~> 4.3)
|
||||
puma (~> 5.1)
|
||||
pundit (~> 2.1)
|
||||
rack (~> 2.2.3)
|
||||
rack-attack (~> 6.3)
|
||||
rack-cors (~> 1.1)
|
||||
rails (~> 5.2.4.3)
|
||||
rails (~> 5.2.4.4)
|
||||
rails-controller-testing (~> 1.0)
|
||||
rails-i18n (~> 5.1)
|
||||
rails-settings-cached (~> 0.6)
|
||||
rdf-normalize (~> 0.4)
|
||||
redis (~> 4.2)
|
||||
redis-namespace (~> 1.7)
|
||||
redis-namespace (~> 1.8)
|
||||
redis-rails (~> 5.0)
|
||||
rqrcode (~> 1.1)
|
||||
rqrcode (~> 1.2)
|
||||
rspec-rails (~> 4.0)
|
||||
rspec-sidekiq (~> 3.1)
|
||||
rspec_junit_formatter (~> 0.4)
|
||||
rubocop (~> 0.86)
|
||||
rubocop-rails (~> 2.6)
|
||||
ruby-progressbar (~> 1.10)
|
||||
rubocop (~> 1.7)
|
||||
rubocop-rails (~> 2.9)
|
||||
ruby-progressbar (~> 1.11)
|
||||
sanitize (~> 5.2)
|
||||
sidekiq (~> 6.0)
|
||||
scenic (~> 1.5)
|
||||
sidekiq (~> 6.1)
|
||||
sidekiq-bulk (~> 0.2.0)
|
||||
sidekiq-scheduler (~> 3.0)
|
||||
sidekiq-unique-jobs (~> 6.0)
|
||||
simple-navigation (~> 4.1)
|
||||
simple_form (~> 5.0)
|
||||
simplecov (~> 0.18)
|
||||
simplecov (~> 0.21)
|
||||
sprockets (~> 3.7.2)
|
||||
sprockets-rails (~> 3.2)
|
||||
stackprof
|
||||
stoplight (~> 2.2.0)
|
||||
stoplight (~> 2.2.1)
|
||||
streamio-ffmpeg (~> 3.0)
|
||||
strong_migrations (~> 0.6)
|
||||
thor (~> 0.20)
|
||||
thwait (~> 0.1.0)
|
||||
tty-prompt (~> 0.21)
|
||||
strong_migrations (~> 0.7)
|
||||
thor (~> 1.0)
|
||||
tty-prompt (~> 0.23)
|
||||
twitter-text (~> 1.14)
|
||||
tzinfo-data (~> 1.2020)
|
||||
webmock (~> 3.8)
|
||||
webpacker (~> 5.1)
|
||||
webauthn (~> 3.0.0.alpha1)
|
||||
webmock (~> 3.11)
|
||||
webpacker (~> 5.2)
|
||||
webpush
|
||||
xorcist (~> 1.1)
|
||||
|
|
2
Procfile
2
Procfile
|
@ -1,4 +1,4 @@
|
|||
web: if [ "$RUN_STREAMING" != "true" ]; then BIND=0.0.0.0 bundle exec puma -C config/puma.rb; else BIND=0.0.0.0 node ./streaming; fi
|
||||
web: bin/heroku-web
|
||||
worker: bundle exec sidekiq
|
||||
|
||||
# For the streaming API, you need a separate app that shares Postgres and Redis:
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AboutController < ApplicationController
|
||||
include RegistrationSpamConcern
|
||||
|
||||
layout 'public'
|
||||
|
||||
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: [:show, :more, :terms]
|
||||
before_action :set_expires_in, only: [:more, :terms]
|
||||
before_action :set_registration_form_time, only: :show
|
||||
|
||||
skip_before_action :require_functional!, only: [:more, :terms]
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ class AccountsController < ApplicationController
|
|||
include AccountControllerConcern
|
||||
include SignatureAuthentication
|
||||
|
||||
before_action :require_signature!, if: -> { request.format == :json && authorized_fetch_mode? }
|
||||
before_action :set_cache_headers
|
||||
before_action :set_body_classes
|
||||
|
||||
|
@ -28,8 +29,7 @@ class AccountsController < ApplicationController
|
|||
end
|
||||
|
||||
@pinned_statuses = cache_collection(@account.pinned_statuses, Status) if show_pinned_statuses?
|
||||
@statuses = filtered_status_page
|
||||
@statuses = cache_collection(@statuses, Status)
|
||||
@statuses = cached_filtered_status_page
|
||||
@rss_url = rss_url
|
||||
|
||||
unless @statuses.empty?
|
||||
|
@ -49,7 +49,7 @@ class AccountsController < ApplicationController
|
|||
|
||||
format.json do
|
||||
expires_in 3.minutes, public: !(authorized_fetch_mode? && signed_request_account.present?)
|
||||
render_with_cache json: @account, content_type: 'application/activity+json', serializer: ActivityPub::ActorSerializer, adapter: ActivityPub::Adapter, fields: restrict_fields_to
|
||||
render_with_cache json: @account, content_type: 'application/activity+json', serializer: ActivityPub::ActorSerializer, adapter: ActivityPub::Adapter
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -81,7 +81,7 @@ class AccountsController < ApplicationController
|
|||
end
|
||||
|
||||
def account_media_status_ids
|
||||
@account.media_attachments.attached.reorder(nil).select(:status_id).distinct
|
||||
@account.media_attachments.attached.reorder(nil).select(:status_id).group(:status_id)
|
||||
end
|
||||
|
||||
def no_replies_scope
|
||||
|
@ -102,6 +102,10 @@ class AccountsController < ApplicationController
|
|||
params[:username]
|
||||
end
|
||||
|
||||
def skip_temporary_suspension_response?
|
||||
request.format == :json
|
||||
end
|
||||
|
||||
def rss_url
|
||||
if tag_requested?
|
||||
short_account_tag_url(@account, params[:tag], format: 'rss')
|
||||
|
@ -142,19 +146,16 @@ class AccountsController < ApplicationController
|
|||
request.path.split('.').first.ends_with?(Addressable::URI.parse("/tagged/#{params[:tag]}").normalize)
|
||||
end
|
||||
|
||||
def filtered_status_page
|
||||
filtered_statuses.paginate_by_id(PAGE_SIZE, params_slice(:max_id, :min_id, :since_id))
|
||||
def cached_filtered_status_page
|
||||
cache_collection_paginated_by_id(
|
||||
filtered_statuses,
|
||||
Status,
|
||||
PAGE_SIZE,
|
||||
params_slice(:max_id, :min_id, :since_id)
|
||||
)
|
||||
end
|
||||
|
||||
def params_slice(*keys)
|
||||
params.slice(*keys).permit(*keys)
|
||||
end
|
||||
|
||||
def restrict_fields_to
|
||||
if signed_request_account.present? || public_fetch_mode?
|
||||
# Return all fields
|
||||
else
|
||||
%i(id type preferred_username inbox public_key endpoints)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -8,4 +8,8 @@ class ActivityPub::BaseController < Api::BaseController
|
|||
def set_cache_headers
|
||||
response.headers['Vary'] = 'Signature' if authorized_fetch_mode?
|
||||
end
|
||||
|
||||
def skip_temporary_suspension_response?
|
||||
false
|
||||
end
|
||||
end
|
||||
|
|
|
@ -12,7 +12,7 @@ class ActivityPub::CollectionsController < ActivityPub::BaseController
|
|||
|
||||
def show
|
||||
expires_in 3.minutes, public: public_fetch_mode?
|
||||
render_with_cache json: collection_presenter, content_type: 'application/activity+json', serializer: ActivityPub::CollectionSerializer, adapter: ActivityPub::Adapter, skip_activities: true
|
||||
render_with_cache json: collection_presenter, content_type: 'application/activity+json', serializer: ActivityPub::CollectionSerializer, adapter: ActivityPub::Adapter
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -20,17 +20,9 @@ class ActivityPub::CollectionsController < ActivityPub::BaseController
|
|||
def set_items
|
||||
case params[:id]
|
||||
when 'featured'
|
||||
@items = begin
|
||||
# Because in public fetch mode we cache the response, there would be no
|
||||
# benefit from performing the check below, since a blocked account or domain
|
||||
# would likely be served the cache from the reverse proxy anyway
|
||||
|
||||
if authorized_fetch_mode? && !signed_request_account.nil? && (@account.blocking?(signed_request_account) || (!signed_request_account.domain.nil? && @account.domain_blocking?(signed_request_account.domain)))
|
||||
[]
|
||||
else
|
||||
cache_collection(@account.pinned_statuses, Status)
|
||||
end
|
||||
end
|
||||
@items = for_signed_account { cache_collection(@account.pinned_statuses, Status) }
|
||||
when 'tags'
|
||||
@items = for_signed_account { @account.featured_tags }
|
||||
when 'devices'
|
||||
@items = @account.devices
|
||||
else
|
||||
|
@ -40,7 +32,7 @@ class ActivityPub::CollectionsController < ActivityPub::BaseController
|
|||
|
||||
def set_size
|
||||
case params[:id]
|
||||
when 'featured', 'devices'
|
||||
when 'featured', 'devices', 'tags'
|
||||
@size = @items.size
|
||||
else
|
||||
not_found
|
||||
|
@ -51,7 +43,7 @@ class ActivityPub::CollectionsController < ActivityPub::BaseController
|
|||
case params[:id]
|
||||
when 'featured'
|
||||
@type = :ordered
|
||||
when 'devices'
|
||||
when 'devices', 'tags'
|
||||
@type = :unordered
|
||||
else
|
||||
not_found
|
||||
|
@ -66,4 +58,16 @@ class ActivityPub::CollectionsController < ActivityPub::BaseController
|
|||
items: @items
|
||||
)
|
||||
end
|
||||
|
||||
def for_signed_account
|
||||
# Because in public fetch mode we cache the response, there would be no
|
||||
# benefit from performing the check below, since a blocked account or domain
|
||||
# would likely be served the cache from the reverse proxy anyway
|
||||
|
||||
if authorized_fetch_mode? && !signed_request_account.nil? && (@account.blocking?(signed_request_account) || (!signed_request_account.domain.nil? && @account.domain_blocking?(signed_request_account.domain)))
|
||||
[]
|
||||
else
|
||||
yield
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class ActivityPub::FollowersSynchronizationsController < ActivityPub::BaseController
|
||||
include SignatureVerification
|
||||
include AccountOwnedConcern
|
||||
|
||||
before_action :require_signature!
|
||||
before_action :set_items
|
||||
before_action :set_cache_headers
|
||||
|
||||
def show
|
||||
expires_in 0, public: false
|
||||
render json: collection_presenter,
|
||||
serializer: ActivityPub::CollectionSerializer,
|
||||
adapter: ActivityPub::Adapter,
|
||||
content_type: 'application/activity+json'
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def uri_prefix
|
||||
signed_request_account.uri[/http(s?):\/\/[^\/]+\//]
|
||||
end
|
||||
|
||||
def set_items
|
||||
@items = @account.followers.where(Account.arel_table[:uri].matches(uri_prefix + '%', false, true)).pluck(:uri)
|
||||
end
|
||||
|
||||
def collection_presenter
|
||||
ActivityPub::CollectionPresenter.new(
|
||||
id: account_followers_synchronization_url(@account),
|
||||
type: :ordered,
|
||||
items: @items
|
||||
)
|
||||
end
|
||||
end
|
|
@ -11,6 +11,7 @@ class ActivityPub::InboxesController < ActivityPub::BaseController
|
|||
|
||||
def create
|
||||
upgrade_account
|
||||
process_collection_synchronization
|
||||
process_payload
|
||||
head 202
|
||||
end
|
||||
|
@ -32,6 +33,10 @@ class ActivityPub::InboxesController < ActivityPub::BaseController
|
|||
params[:account_username].present?
|
||||
end
|
||||
|
||||
def skip_temporary_suspension_response?
|
||||
true
|
||||
end
|
||||
|
||||
def body
|
||||
return @body if defined?(@body)
|
||||
|
||||
|
@ -52,6 +57,19 @@ class ActivityPub::InboxesController < ActivityPub::BaseController
|
|||
DeliveryFailureTracker.reset!(signed_request_account.inbox_url)
|
||||
end
|
||||
|
||||
def process_collection_synchronization
|
||||
raw_params = request.headers['Collection-Synchronization']
|
||||
return if raw_params.blank? || ENV['DISABLE_FOLLOWERS_SYNCHRONIZATION'] == 'true'
|
||||
|
||||
# Re-using the syntax for signature parameters
|
||||
tree = SignatureParamsParser.new.parse(raw_params)
|
||||
params = SignatureParamsTransformer.new.apply(tree)
|
||||
|
||||
ActivityPub::PrepareFollowersSynchronizationService.new.call(signed_request_account, params)
|
||||
rescue Parslet::ParseFailed
|
||||
Rails.logger.warn 'Error parsing Collection-Synchronization header'
|
||||
end
|
||||
|
||||
def process_payload
|
||||
ActivityPub::ProcessingWorker.perform_async(signed_request_account.id, body, @account&.id)
|
||||
end
|
||||
|
|
|
@ -20,9 +20,9 @@ class ActivityPub::OutboxesController < ActivityPub::BaseController
|
|||
def outbox_presenter
|
||||
if page_requested?
|
||||
ActivityPub::CollectionPresenter.new(
|
||||
id: account_outbox_url(@account, page_params),
|
||||
id: outbox_url(page_params),
|
||||
type: :ordered,
|
||||
part_of: account_outbox_url(@account),
|
||||
part_of: outbox_url,
|
||||
prev: prev_page,
|
||||
next: next_page,
|
||||
items: @statuses
|
||||
|
@ -32,12 +32,20 @@ class ActivityPub::OutboxesController < ActivityPub::BaseController
|
|||
id: account_outbox_url(@account),
|
||||
type: :ordered,
|
||||
size: @account.statuses_count,
|
||||
first: account_outbox_url(@account, page: true),
|
||||
last: account_outbox_url(@account, page: true, min_id: 0)
|
||||
first: outbox_url(page: true),
|
||||
last: outbox_url(page: true, min_id: 0)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def outbox_url(**kwargs)
|
||||
if params[:account_username].present?
|
||||
account_outbox_url(@account, **kwargs)
|
||||
else
|
||||
instance_actor_outbox_url(**kwargs)
|
||||
end
|
||||
end
|
||||
|
||||
def next_page
|
||||
account_outbox_url(@account, page: true, max_id: @statuses.last.id) if @statuses.size == LIMIT
|
||||
end
|
||||
|
@ -49,9 +57,12 @@ class ActivityPub::OutboxesController < ActivityPub::BaseController
|
|||
def set_statuses
|
||||
return unless page_requested?
|
||||
|
||||
@statuses = @account.statuses.permitted_for(@account, signed_request_account)
|
||||
@statuses = @statuses.paginate_by_id(LIMIT, params_slice(:max_id, :min_id, :since_id))
|
||||
@statuses = cache_collection(@statuses, Status)
|
||||
@statuses = cache_collection_paginated_by_id(
|
||||
@account.statuses.permitted_for(@account, signed_request_account),
|
||||
Status,
|
||||
LIMIT,
|
||||
params_slice(:max_id, :min_id, :since_id)
|
||||
)
|
||||
end
|
||||
|
||||
def page_requested?
|
||||
|
@ -61,4 +72,8 @@ class ActivityPub::OutboxesController < ActivityPub::BaseController
|
|||
def page_params
|
||||
{ page: true, max_id: params[:max_id], min_id: params[:min_id] }.compact
|
||||
end
|
||||
|
||||
def set_account
|
||||
@account = params[:account_username].present? ? Account.find_local!(username_param) : Account.representative
|
||||
end
|
||||
end
|
||||
|
|
|
@ -31,7 +31,7 @@ class ActivityPub::RepliesController < ActivityPub::BaseController
|
|||
end
|
||||
|
||||
def set_replies
|
||||
@replies = only_other_accounts? ? Status.where.not(account_id: @account.id) : @account.statuses
|
||||
@replies = only_other_accounts? ? Status.where.not(account_id: @account.id).joins(:account).merge(Account.without_suspended) : @account.statuses
|
||||
@replies = @replies.where(in_reply_to_id: @status.id, visibility: [:public, :unlisted])
|
||||
@replies = @replies.paginate_by_min_id(DESCENDANTS_LIMIT, params[:min_id])
|
||||
end
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
module Admin
|
||||
class AccountsController < BaseController
|
||||
before_action :set_account, only: [:show, :redownload, :remove_avatar, :remove_header, :enable, :unsilence, :unsuspend, :memorialize, :approve, :reject]
|
||||
before_action :set_account, except: [:index]
|
||||
before_action :require_remote_account!, only: [:redownload]
|
||||
before_action :require_local_account!, only: [:enable, :memorialize, :approve, :reject]
|
||||
|
||||
|
@ -14,49 +14,65 @@ module Admin
|
|||
def show
|
||||
authorize @account, :show?
|
||||
|
||||
@deletion_request = @account.deletion_request
|
||||
@account_moderation_note = current_account.account_moderation_notes.new(target_account: @account)
|
||||
@moderation_notes = @account.targeted_moderation_notes.latest
|
||||
@warnings = @account.targeted_account_warnings.latest.custom
|
||||
@domain_block = DomainBlock.rule_for(@account.domain)
|
||||
end
|
||||
|
||||
def memorialize
|
||||
authorize @account, :memorialize?
|
||||
@account.memorialize!
|
||||
log_action :memorialize, @account
|
||||
redirect_to admin_account_path(@account.id)
|
||||
redirect_to admin_account_path(@account.id), notice: I18n.t('admin.accounts.memorialized_msg', username: @account.acct)
|
||||
end
|
||||
|
||||
def enable
|
||||
authorize @account.user, :enable?
|
||||
@account.user.enable!
|
||||
log_action :enable, @account.user
|
||||
redirect_to admin_account_path(@account.id)
|
||||
redirect_to admin_account_path(@account.id), notice: I18n.t('admin.accounts.enabled_msg', username: @account.acct)
|
||||
end
|
||||
|
||||
def approve
|
||||
authorize @account.user, :approve?
|
||||
@account.user.approve!
|
||||
redirect_to admin_pending_accounts_path
|
||||
redirect_to admin_pending_accounts_path, notice: I18n.t('admin.accounts.approved_msg', username: @account.acct)
|
||||
end
|
||||
|
||||
def reject
|
||||
authorize @account.user, :reject?
|
||||
SuspendAccountService.new.call(@account, reserve_email: false, reserve_username: false)
|
||||
redirect_to admin_pending_accounts_path
|
||||
DeleteAccountService.new.call(@account, reserve_email: false, reserve_username: false)
|
||||
redirect_to admin_pending_accounts_path, notice: I18n.t('admin.accounts.rejected_msg', username: @account.acct)
|
||||
end
|
||||
|
||||
def destroy
|
||||
authorize @account, :destroy?
|
||||
Admin::AccountDeletionWorker.perform_async(@account.id)
|
||||
redirect_to admin_account_path(@account.id), notice: I18n.t('admin.accounts.destroyed_msg', username: @account.acct)
|
||||
end
|
||||
|
||||
def unsensitive
|
||||
authorize @account, :unsensitive?
|
||||
@account.unsensitize!
|
||||
log_action :unsensitive, @account
|
||||
redirect_to admin_account_path(@account.id)
|
||||
end
|
||||
|
||||
def unsilence
|
||||
authorize @account, :unsilence?
|
||||
@account.unsilence!
|
||||
log_action :unsilence, @account
|
||||
redirect_to admin_account_path(@account.id)
|
||||
redirect_to admin_account_path(@account.id), notice: I18n.t('admin.accounts.unsilenced_msg', username: @account.acct)
|
||||
end
|
||||
|
||||
def unsuspend
|
||||
authorize @account, :unsuspend?
|
||||
@account.unsuspend!
|
||||
Admin::UnsuspensionWorker.perform_async(@account.id)
|
||||
log_action :unsuspend, @account
|
||||
redirect_to admin_account_path(@account.id)
|
||||
redirect_to admin_account_path(@account.id), notice: I18n.t('admin.accounts.unsuspended_msg', username: @account.acct)
|
||||
end
|
||||
|
||||
def redownload
|
||||
|
@ -65,7 +81,7 @@ module Admin
|
|||
@account.update!(last_webfingered_at: nil)
|
||||
ResolveAccountService.new.call(@account)
|
||||
|
||||
redirect_to admin_account_path(@account.id)
|
||||
redirect_to admin_account_path(@account.id), notice: I18n.t('admin.accounts.redownloaded_msg', username: @account.acct)
|
||||
end
|
||||
|
||||
def remove_avatar
|
||||
|
@ -76,7 +92,7 @@ module Admin
|
|||
|
||||
log_action :remove_avatar, @account.user
|
||||
|
||||
redirect_to admin_account_path(@account.id)
|
||||
redirect_to admin_account_path(@account.id), notice: I18n.t('admin.accounts.removed_avatar_msg', username: @account.acct)
|
||||
end
|
||||
|
||||
def remove_header
|
||||
|
@ -87,7 +103,7 @@ module Admin
|
|||
|
||||
log_action :remove_header, @account.user
|
||||
|
||||
redirect_to admin_account_path(@account.id)
|
||||
redirect_to admin_account_path(@account.id), notice: I18n.t('admin.accounts.removed_header_msg', username: @account.acct)
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
@ -71,7 +71,7 @@ class Admin::AnnouncementsController < Admin::BaseController
|
|||
private
|
||||
|
||||
def set_announcements
|
||||
@announcements = AnnouncementFilter.new(filter_params).results.page(params[:page])
|
||||
@announcements = AnnouncementFilter.new(filter_params).results.reverse_chronological.page(params[:page])
|
||||
end
|
||||
|
||||
def set_announcement
|
||||
|
|
|
@ -29,6 +29,7 @@ module Admin
|
|||
@domain_block = existing_domain_block
|
||||
@domain_block.update(resource_params)
|
||||
end
|
||||
|
||||
if @domain_block.save
|
||||
DomainBlockWorker.perform_async(@domain_block.id)
|
||||
log_action :create, @domain_block
|
||||
|
@ -40,7 +41,7 @@ module Admin
|
|||
end
|
||||
|
||||
def update
|
||||
authorize :domain_block, :create?
|
||||
authorize :domain_block, :update?
|
||||
|
||||
@domain_block.update(update_params)
|
||||
|
||||
|
@ -48,7 +49,7 @@ module Admin
|
|||
|
||||
if @domain_block.save
|
||||
DomainBlockWorker.perform_async(@domain_block.id, severity_changed)
|
||||
log_action :create, @domain_block
|
||||
log_action :update, @domain_block
|
||||
redirect_to admin_instances_path(limited: '1'), notice: I18n.t('admin.domain_blocks.created_msg')
|
||||
else
|
||||
render :edit
|
||||
|
@ -73,11 +74,11 @@ module Admin
|
|||
end
|
||||
|
||||
def update_params
|
||||
params.require(:domain_block).permit(:severity, :reject_media, :reject_reports, :private_comment, :public_comment)
|
||||
params.require(:domain_block).permit(:severity, :reject_media, :reject_reports, :private_comment, :public_comment, :obfuscate)
|
||||
end
|
||||
|
||||
def resource_params
|
||||
params.require(:domain_block).permit(:domain, :severity, :reject_media, :reject_reports, :private_comment, :public_comment)
|
||||
params.require(:domain_block).permit(:domain, :severity, :reject_media, :reject_reports, :private_comment, :public_comment, :obfuscate)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -27,7 +27,7 @@ module Admin
|
|||
ips = []
|
||||
|
||||
Resolv::DNS.open do |dns|
|
||||
dns.timeouts = 1
|
||||
dns.timeouts = 5
|
||||
|
||||
hostnames = dns.getresources(@email_domain_block.domain, Resolv::DNS::Resource::IN::MX).to_a.map { |e| e.exchange.to_s }
|
||||
|
||||
|
|
|
@ -2,65 +2,31 @@
|
|||
|
||||
module Admin
|
||||
class InstancesController < BaseController
|
||||
before_action :set_domain_block, only: :show
|
||||
before_action :set_domain_allow, only: :show
|
||||
before_action :set_instances, only: :index
|
||||
before_action :set_instance, only: :show
|
||||
|
||||
def index
|
||||
authorize :instance, :index?
|
||||
|
||||
@instances = ordered_instances
|
||||
end
|
||||
|
||||
def show
|
||||
authorize :instance, :show?
|
||||
|
||||
@following_count = Follow.where(account: Account.where(domain: params[:id])).count
|
||||
@followers_count = Follow.where(target_account: Account.where(domain: params[:id])).count
|
||||
@reports_count = Report.where(target_account: Account.where(domain: params[:id])).count
|
||||
@blocks_count = Block.where(target_account: Account.where(domain: params[:id])).count
|
||||
@available = DeliveryFailureTracker.available?(params[:id])
|
||||
@media_storage = MediaAttachment.where(account: Account.where(domain: params[:id])).sum(:file_file_size)
|
||||
@private_comment = @domain_block&.private_comment
|
||||
@public_comment = @domain_block&.public_comment
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_domain_block
|
||||
@domain_block = DomainBlock.rule_for(params[:id])
|
||||
end
|
||||
|
||||
def set_domain_allow
|
||||
@domain_allow = DomainAllow.rule_for(params[:id])
|
||||
end
|
||||
|
||||
def set_instance
|
||||
resource = Account.by_domain_accounts.find_by(domain: params[:id])
|
||||
resource ||= @domain_block
|
||||
resource ||= @domain_allow
|
||||
|
||||
if resource
|
||||
@instance = Instance.new(resource)
|
||||
else
|
||||
not_found
|
||||
@instance = Instance.find(params[:id])
|
||||
end
|
||||
|
||||
def set_instances
|
||||
@instances = filtered_instances.page(params[:page])
|
||||
end
|
||||
|
||||
def filtered_instances
|
||||
InstanceFilter.new(whitelist_mode? ? { allowed: true } : filter_params).results
|
||||
end
|
||||
|
||||
def paginated_instances
|
||||
filtered_instances.page(params[:page])
|
||||
end
|
||||
|
||||
helper_method :paginated_instances
|
||||
|
||||
def ordered_instances
|
||||
paginated_instances.map { |resource| Instance.new(resource) }
|
||||
end
|
||||
|
||||
def filter_params
|
||||
params.slice(*InstanceFilter::KEYS).permit(*InstanceFilter::KEYS)
|
||||
end
|
||||
|
|
56
app/controllers/admin/ip_blocks_controller.rb
Normal file
56
app/controllers/admin/ip_blocks_controller.rb
Normal file
|
@ -0,0 +1,56 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Admin
|
||||
class IpBlocksController < BaseController
|
||||
def index
|
||||
authorize :ip_block, :index?
|
||||
|
||||
@ip_blocks = IpBlock.page(params[:page])
|
||||
@form = Form::IpBlockBatch.new
|
||||
end
|
||||
|
||||
def new
|
||||
authorize :ip_block, :create?
|
||||
|
||||
@ip_block = IpBlock.new(ip: '', severity: :no_access, expires_in: 1.year)
|
||||
end
|
||||
|
||||
def create
|
||||
authorize :ip_block, :create?
|
||||
|
||||
@ip_block = IpBlock.new(resource_params)
|
||||
|
||||
if @ip_block.save
|
||||
log_action :create, @ip_block
|
||||
redirect_to admin_ip_blocks_path, notice: I18n.t('admin.ip_blocks.created_msg')
|
||||
else
|
||||
render :new
|
||||
end
|
||||
end
|
||||
|
||||
def batch
|
||||
@form = Form::IpBlockBatch.new(form_ip_block_batch_params.merge(current_account: current_account, action: action_from_button))
|
||||
@form.save
|
||||
rescue ActionController::ParameterMissing
|
||||
flash[:alert] = I18n.t('admin.ip_blocks.no_ip_block_selected')
|
||||
rescue Mastodon::NotPermittedError
|
||||
flash[:alert] = I18n.t('admin.custom_emojis.not_permitted')
|
||||
ensure
|
||||
redirect_to admin_ip_blocks_path
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def resource_params
|
||||
params.require(:ip_block).permit(:ip, :severity, :comment, :expires_in)
|
||||
end
|
||||
|
||||
def action_from_button
|
||||
'delete' if params[:delete]
|
||||
end
|
||||
|
||||
def form_ip_block_batch_params
|
||||
params.require(:form_ip_block_batch).permit(ip_block_ids: [])
|
||||
end
|
||||
end
|
||||
end
|
|
@ -14,7 +14,7 @@ module Admin
|
|||
@statuses = @account.statuses.where(visibility: [:public, :unlisted])
|
||||
|
||||
if params[:media]
|
||||
account_media_status_ids = @account.media_attachments.attached.reorder(nil).select(:status_id).distinct
|
||||
account_media_status_ids = @account.media_attachments.attached.reorder(nil).select(:status_id).group(:status_id)
|
||||
@statuses.merge!(Status.where(id: account_media_status_ids))
|
||||
end
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ class Api::BaseController < ApplicationController
|
|||
render json: { error: 'This action is not allowed' }, status: 403
|
||||
end
|
||||
|
||||
rescue_from Mastodon::RaceConditionError do
|
||||
rescue_from Mastodon::RaceConditionError, Seahorse::Client::NetworkingError, Stoplight::Error::RedLight do
|
||||
render json: { error: 'There was a temporary problem serving your request, please try again' }, status: 503
|
||||
end
|
||||
|
||||
|
@ -71,6 +71,7 @@ class Api::BaseController < ApplicationController
|
|||
|
||||
def limit_param(default_limit)
|
||||
return default_limit unless params[:limit]
|
||||
|
||||
[params[:limit].to_i.abs, default_limit * 2].min
|
||||
end
|
||||
|
||||
|
@ -95,14 +96,14 @@ class Api::BaseController < ApplicationController
|
|||
def require_user!
|
||||
if !current_user
|
||||
render json: { error: 'This method requires an authenticated user' }, status: 422
|
||||
elsif current_user.disabled?
|
||||
render json: { error: 'Your login is currently disabled' }, status: 403
|
||||
elsif !current_user.confirmed?
|
||||
render json: { error: 'Your login is missing a confirmed e-mail address' }, status: 403
|
||||
elsif !current_user.approved?
|
||||
render json: { error: 'Your login is currently pending approval' }, status: 403
|
||||
elsif !current_user.functional?
|
||||
render json: { error: 'Your login is currently disabled' }, status: 403
|
||||
else
|
||||
set_user_activity
|
||||
update_user_sign_in
|
||||
end
|
||||
end
|
||||
|
||||
|
|
22
app/controllers/api/v1/accounts/featured_tags_controller.rb
Normal file
22
app/controllers/api/v1/accounts/featured_tags_controller.rb
Normal file
|
@ -0,0 +1,22 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Accounts::FeaturedTagsController < Api::BaseController
|
||||
before_action :set_account
|
||||
before_action :set_featured_tags
|
||||
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
render json: @featured_tags, each_serializer: REST::FeaturedTagSerializer
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_account
|
||||
@account = Account.find(params[:account_id])
|
||||
end
|
||||
|
||||
def set_featured_tags
|
||||
@featured_tags = @account.suspended? ? [] : @account.featured_tags
|
||||
end
|
||||
end
|
|
@ -25,7 +25,7 @@ class Api::V1::Accounts::FollowerAccountsController < Api::BaseController
|
|||
end
|
||||
|
||||
def hide_results?
|
||||
(@account.hides_followers? && current_account&.id != @account.id) || (current_account && @account.blocking?(current_account))
|
||||
@account.suspended? || (@account.hides_followers? && current_account&.id != @account.id) || (current_account && @account.blocking?(current_account))
|
||||
end
|
||||
|
||||
def default_accounts
|
||||
|
|
|
@ -25,7 +25,7 @@ class Api::V1::Accounts::FollowingAccountsController < Api::BaseController
|
|||
end
|
||||
|
||||
def hide_results?
|
||||
(@account.hides_following? && current_account&.id != @account.id) || (current_account && @account.blocking?(current_account))
|
||||
@account.suspended? || (@account.hides_following? && current_account&.id != @account.id) || (current_account && @account.blocking?(current_account))
|
||||
end
|
||||
|
||||
def default_accounts
|
||||
|
|
|
@ -5,7 +5,7 @@ class Api::V1::Accounts::IdentityProofsController < Api::BaseController
|
|||
before_action :set_account
|
||||
|
||||
def index
|
||||
@proofs = @account.identity_proofs.active
|
||||
@proofs = @account.suspended? ? [] : @account.identity_proofs.active
|
||||
render json: @proofs, each_serializer: REST::IdentityProofSerializer
|
||||
end
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ class Api::V1::Accounts::ListsController < Api::BaseController
|
|||
before_action :set_account
|
||||
|
||||
def index
|
||||
@lists = @account.lists.where(account: current_account)
|
||||
@lists = @account.suspended? ? [] : @account.lists.where(account: current_account)
|
||||
render json: @lists, each_serializer: REST::ListSerializer
|
||||
end
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ class Api::V1::Accounts::RelationshipsController < Api::BaseController
|
|||
before_action :require_user!
|
||||
|
||||
def index
|
||||
accounts = Account.where(id: account_ids).select('id')
|
||||
accounts = Account.without_suspended.where(id: account_ids).select('id')
|
||||
# .where doesn't guarantee that our results are in the same order
|
||||
# we requested them, so return the "right" order to the requestor.
|
||||
@accounts = accounts.index_by(&:id).values_at(*account_ids).compact
|
||||
|
|
|
@ -18,14 +18,10 @@ class Api::V1::Accounts::StatusesController < Api::BaseController
|
|||
end
|
||||
|
||||
def load_statuses
|
||||
cached_account_statuses
|
||||
@account.suspended? ? [] : cached_account_statuses
|
||||
end
|
||||
|
||||
def cached_account_statuses
|
||||
cache_collection account_statuses, Status
|
||||
end
|
||||
|
||||
def account_statuses
|
||||
statuses = truthy_param?(:pinned) ? pinned_scope : permitted_account_statuses
|
||||
|
||||
statuses.merge!(only_media_scope) if truthy_param?(:only_media)
|
||||
|
@ -33,7 +29,12 @@ class Api::V1::Accounts::StatusesController < Api::BaseController
|
|||
statuses.merge!(no_reblogs_scope) if truthy_param?(:exclude_reblogs)
|
||||
statuses.merge!(hashtag_scope) if params[:tagged].present?
|
||||
|
||||
statuses.paginate_by_id(limit_param(DEFAULT_STATUSES_LIMIT), params_slice(:max_id, :since_id, :min_id))
|
||||
cache_collection_paginated_by_id(
|
||||
statuses,
|
||||
Status,
|
||||
limit_param(DEFAULT_STATUSES_LIMIT),
|
||||
params_slice(:max_id, :since_id, :min_id)
|
||||
)
|
||||
end
|
||||
|
||||
def permitted_account_statuses
|
||||
|
@ -41,17 +42,7 @@ class Api::V1::Accounts::StatusesController < Api::BaseController
|
|||
end
|
||||
|
||||
def only_media_scope
|
||||
Status.where(id: account_media_status_ids)
|
||||
end
|
||||
|
||||
def account_media_status_ids
|
||||
# `SELECT DISTINCT id, updated_at` is too slow, so pluck ids at first, and then select id, updated_at with ids.
|
||||
# Also, Avoid getting slow by not narrowing down by `statuses.account_id`.
|
||||
# When narrowing down by `statuses.account_id`, `index_statuses_20180106` will be used
|
||||
# and the table will be joined by `Merge Semi Join`, so the query will be slow.
|
||||
@account.statuses.joins(:media_attachments).merge(@account.media_attachments).permitted_for(@account, current_account)
|
||||
.paginate_by_max_id(limit_param(DEFAULT_STATUSES_LIMIT), params[:max_id], params[:since_id])
|
||||
.reorder(id: :desc).distinct(:id).pluck(:id)
|
||||
Status.joins(:media_attachments).merge(@account.media_attachments.reorder(nil)).group(:id)
|
||||
end
|
||||
|
||||
def pinned_scope
|
||||
|
|
|
@ -9,7 +9,6 @@ class Api::V1::AccountsController < Api::BaseController
|
|||
|
||||
before_action :require_user!, except: [:show, :create]
|
||||
before_action :set_account, except: [:create]
|
||||
before_action :check_account_suspension, only: [:show]
|
||||
before_action :check_enabled_registrations, only: [:create]
|
||||
|
||||
skip_before_action :require_authenticated_user!, only: :create
|
||||
|
@ -21,7 +20,7 @@ class Api::V1::AccountsController < Api::BaseController
|
|||
end
|
||||
|
||||
def create
|
||||
token = AppSignUpService.new.call(doorkeeper_token.application, account_params)
|
||||
token = AppSignUpService.new.call(doorkeeper_token.application, request.remote_ip, account_params)
|
||||
response = Doorkeeper::OAuth::TokenResponse.new(token)
|
||||
|
||||
headers.merge!(response.headers)
|
||||
|
@ -31,9 +30,8 @@ class Api::V1::AccountsController < Api::BaseController
|
|||
end
|
||||
|
||||
def follow
|
||||
FollowService.new.call(current_user.account, @account, reblogs: truthy_param?(:reblogs), with_rate_limit: true)
|
||||
|
||||
options = @account.locked? || current_user.account.silenced? ? {} : { following_map: { @account.id => { reblogs: truthy_param?(:reblogs) } }, 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, 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 } }
|
||||
|
||||
render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships(options)
|
||||
end
|
||||
|
@ -44,7 +42,7 @@ class Api::V1::AccountsController < Api::BaseController
|
|||
end
|
||||
|
||||
def mute
|
||||
MuteService.new.call(current_user.account, @account, notifications: truthy_param?(:notifications))
|
||||
MuteService.new.call(current_user.account, @account, notifications: truthy_param?(:notifications), duration: (params[:duration] || 0))
|
||||
render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships
|
||||
end
|
||||
|
||||
|
@ -73,10 +71,6 @@ class Api::V1::AccountsController < Api::BaseController
|
|||
AccountRelationshipsPresenter.new([@account.id], current_user.account_id, options)
|
||||
end
|
||||
|
||||
def check_account_suspension
|
||||
gone if @account.suspended?
|
||||
end
|
||||
|
||||
def account_params
|
||||
params.permit(:username, :email, :password, :agreement, :locale, :reason)
|
||||
end
|
||||
|
|
|
@ -22,6 +22,7 @@ class Api::V1::Admin::AccountsController < Api::BaseController
|
|||
active
|
||||
pending
|
||||
disabled
|
||||
sensitized
|
||||
silenced
|
||||
suspended
|
||||
username
|
||||
|
@ -58,7 +59,20 @@ class Api::V1::Admin::AccountsController < Api::BaseController
|
|||
|
||||
def reject
|
||||
authorize @account.user, :reject?
|
||||
SuspendAccountService.new.call(@account, reserve_email: false, reserve_username: false)
|
||||
DeleteAccountService.new.call(@account, reserve_email: false, reserve_username: false)
|
||||
render json: @account, serializer: REST::Admin::AccountSerializer
|
||||
end
|
||||
|
||||
def destroy
|
||||
authorize @account, :destroy?
|
||||
Admin::AccountDeletionWorker.perform_async(@account.id)
|
||||
render json: @account, serializer: REST::Admin::AccountSerializer
|
||||
end
|
||||
|
||||
def unsensitive
|
||||
authorize @account, :unsensitive?
|
||||
@account.unsensitize!
|
||||
log_action :unsensitive, @account
|
||||
render json: @account, serializer: REST::Admin::AccountSerializer
|
||||
end
|
||||
|
||||
|
@ -72,6 +86,7 @@ class Api::V1::Admin::AccountsController < Api::BaseController
|
|||
def unsuspend
|
||||
authorize @account, :unsuspend?
|
||||
@account.unsuspend!
|
||||
Admin::UnsuspensionWorker.perform_async(@account.id)
|
||||
log_action :unsuspend, @account
|
||||
render json: @account, serializer: REST::Admin::AccountSerializer
|
||||
end
|
||||
|
@ -79,7 +94,7 @@ class Api::V1::Admin::AccountsController < Api::BaseController
|
|||
private
|
||||
|
||||
def set_accounts
|
||||
@accounts = filtered_accounts.order(id: :desc).includes(user: [:invite_request, :invite]).paginate_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id))
|
||||
@accounts = filtered_accounts.order(id: :desc).includes(user: [:invite_request, :invite]).to_a_paginated_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id))
|
||||
end
|
||||
|
||||
def set_account
|
||||
|
|
|
@ -63,7 +63,7 @@ class Api::V1::Admin::ReportsController < Api::BaseController
|
|||
private
|
||||
|
||||
def set_reports
|
||||
@reports = filtered_reports.order(id: :desc).with_accounts.paginate_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id))
|
||||
@reports = filtered_reports.order(id: :desc).with_accounts.to_a_paginated_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id))
|
||||
end
|
||||
|
||||
def set_report
|
||||
|
|
|
@ -18,6 +18,8 @@ class Api::V1::BlocksController < Api::BaseController
|
|||
|
||||
def paginated_blocks
|
||||
@paginated_blocks ||= Block.eager_load(target_account: :account_stat)
|
||||
.joins(:target_account)
|
||||
.merge(Account.without_suspended)
|
||||
.where(account: current_account)
|
||||
.paginate_by_max_id(
|
||||
limit_param(DEFAULT_ACCOUNTS_LIMIT),
|
||||
|
|
|
@ -17,14 +17,11 @@ class Api::V1::BookmarksController < Api::BaseController
|
|||
end
|
||||
|
||||
def cached_bookmarks
|
||||
cache_collection(
|
||||
Status.reorder(nil).joins(:bookmarks).merge(results),
|
||||
Status
|
||||
)
|
||||
cache_collection(results.map(&:status), Status)
|
||||
end
|
||||
|
||||
def results
|
||||
@_results ||= account_bookmarks.paginate_by_id(
|
||||
@_results ||= account_bookmarks.eager_load(:status).to_a_paginated_by_id(
|
||||
limit_param(DEFAULT_STATUSES_LIMIT),
|
||||
params_slice(:max_id, :since_id, :min_id)
|
||||
)
|
||||
|
|
|
@ -32,7 +32,7 @@ class Api::V1::ConversationsController < Api::BaseController
|
|||
|
||||
def paginated_conversations
|
||||
AccountConversation.where(account: current_account)
|
||||
.paginate_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id))
|
||||
.to_a_paginated_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id))
|
||||
end
|
||||
|
||||
def insert_pagination_headers
|
||||
|
|
|
@ -26,7 +26,7 @@ class Api::V1::Crypto::EncryptedMessagesController < Api::BaseController
|
|||
end
|
||||
|
||||
def set_encrypted_messages
|
||||
@encrypted_messages = @current_device.encrypted_messages.paginate_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id))
|
||||
@encrypted_messages = @current_device.encrypted_messages.to_a_paginated_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id))
|
||||
end
|
||||
|
||||
def insert_pagination_headers
|
||||
|
|
|
@ -25,7 +25,7 @@ class Api::V1::EndorsementsController < Api::BaseController
|
|||
end
|
||||
|
||||
def endorsed_accounts
|
||||
current_account.endorsed_accounts.includes(:account_stat)
|
||||
current_account.endorsed_accounts.includes(:account_stat).without_suspended
|
||||
end
|
||||
|
||||
def insert_pagination_headers
|
||||
|
|
|
@ -17,14 +17,11 @@ class Api::V1::FavouritesController < Api::BaseController
|
|||
end
|
||||
|
||||
def cached_favourites
|
||||
cache_collection(
|
||||
Status.reorder(nil).joins(:favourites).merge(results),
|
||||
Status
|
||||
)
|
||||
cache_collection(results.map(&:status), Status)
|
||||
end
|
||||
|
||||
def results
|
||||
@_results ||= account_favourites.paginate_by_id(
|
||||
@_results ||= account_favourites.eager_load(:status).to_a_paginated_by_id(
|
||||
limit_param(DEFAULT_STATUSES_LIMIT),
|
||||
params_slice(:max_id, :since_id, :min_id)
|
||||
)
|
||||
|
|
|
@ -3,15 +3,15 @@
|
|||
class Api::V1::FeaturedTags::SuggestionsController < Api::BaseController
|
||||
before_action -> { doorkeeper_authorize! :read, :'read:accounts' }, only: :index
|
||||
before_action :require_user!
|
||||
before_action :set_most_used_tags, only: :index
|
||||
before_action :set_recently_used_tags, only: :index
|
||||
|
||||
def index
|
||||
render json: @most_used_tags, each_serializer: REST::TagSerializer
|
||||
render json: @recently_used_tags, each_serializer: REST::TagSerializer
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_most_used_tags
|
||||
@most_used_tags = Tag.most_used(current_account).where.not(id: current_account.featured_tags).limit(10)
|
||||
def set_recently_used_tags
|
||||
@recently_used_tags = Tag.recently_used(current_account).where.not(id: current_account.featured_tags).limit(10)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -13,7 +13,7 @@ class Api::V1::FollowRequestsController < Api::BaseController
|
|||
|
||||
def authorize
|
||||
AuthorizeFollowService.new.call(account, current_account)
|
||||
NotifyService.new.call(current_account, Follow.find_by(account: account, target_account: current_account))
|
||||
NotifyService.new.call(current_account, :follow, Follow.find_by(account: account, target_account: current_account))
|
||||
render json: account, serializer: REST::RelationshipSerializer, relationships: relationships
|
||||
end
|
||||
|
||||
|
@ -37,7 +37,7 @@ class Api::V1::FollowRequestsController < Api::BaseController
|
|||
end
|
||||
|
||||
def default_accounts
|
||||
Account.includes(:follow_requests, :account_stat).references(:follow_requests)
|
||||
Account.without_suspended.includes(:follow_requests, :account_stat).references(:follow_requests)
|
||||
end
|
||||
|
||||
def paginated_follow_requests
|
||||
|
|
|
@ -8,7 +8,7 @@ class Api::V1::Instances::PeersController < Api::BaseController
|
|||
|
||||
def index
|
||||
expires_in 1.day, public: true
|
||||
render_with_cache(expires_in: 1.day) { Account.remote.domains }
|
||||
render_with_cache(expires_in: 1.day) { Instance.where.not(domain: DomainBlock.select(:domain)).pluck(:domain) }
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
@ -37,9 +37,9 @@ class Api::V1::Lists::AccountsController < Api::BaseController
|
|||
|
||||
def load_accounts
|
||||
if unlimited?
|
||||
@list.accounts.includes(:account_stat).all
|
||||
@list.accounts.without_suspended.includes(:account_stat).all
|
||||
else
|
||||
@list.accounts.includes(:account_stat).paginate_by_max_id(limit_param(DEFAULT_ACCOUNTS_LIMIT), params[:max_id], params[:since_id])
|
||||
@list.accounts.without_suspended.includes(:account_stat).paginate_by_max_id(limit_param(DEFAULT_ACCOUNTS_LIMIT), params[:max_id], params[:since_id])
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -38,6 +38,6 @@ class Api::V1::ListsController < Api::BaseController
|
|||
end
|
||||
|
||||
def list_params
|
||||
params.permit(:title)
|
||||
params.permit(:title, :replies_policy)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,7 +7,7 @@ class Api::V1::MutesController < Api::BaseController
|
|||
|
||||
def index
|
||||
@accounts = load_accounts
|
||||
render json: @accounts, each_serializer: REST::AccountSerializer
|
||||
render json: @accounts, each_serializer: REST::MutedAccountSerializer
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -18,6 +18,8 @@ class Api::V1::MutesController < Api::BaseController
|
|||
|
||||
def paginated_mutes
|
||||
@paginated_mutes ||= Mute.eager_load(:target_account)
|
||||
.joins(:target_account)
|
||||
.merge(Account.without_suspended)
|
||||
.where(account: current_account)
|
||||
.paginate_by_max_id(
|
||||
limit_param(DEFAULT_ACCOUNTS_LIMIT),
|
||||
|
|
|
@ -14,7 +14,7 @@ class Api::V1::NotificationsController < Api::BaseController
|
|||
end
|
||||
|
||||
def show
|
||||
@notification = current_account.notifications.find(params[:id])
|
||||
@notification = current_account.notifications.without_suspended.find(params[:id])
|
||||
render json: @notification, serializer: REST::NotificationSerializer
|
||||
end
|
||||
|
||||
|
@ -31,18 +31,16 @@ class Api::V1::NotificationsController < Api::BaseController
|
|||
private
|
||||
|
||||
def load_notifications
|
||||
cache_collection paginated_notifications, Notification
|
||||
end
|
||||
|
||||
def paginated_notifications
|
||||
browserable_account_notifications.paginate_by_id(
|
||||
cache_collection_paginated_by_id(
|
||||
browserable_account_notifications,
|
||||
Notification,
|
||||
limit_param(DEFAULT_NOTIFICATIONS_LIMIT),
|
||||
params_slice(:max_id, :since_id, :min_id)
|
||||
)
|
||||
end
|
||||
|
||||
def browserable_account_notifications
|
||||
current_account.notifications.browserable(exclude_types, from_account)
|
||||
current_account.notifications.without_suspended.browserable(exclude_types, from_account)
|
||||
end
|
||||
|
||||
def target_statuses_from_notifications
|
||||
|
|
|
@ -52,6 +52,6 @@ class Api::V1::Push::SubscriptionsController < Api::BaseController
|
|||
def data_params
|
||||
return {} if params[:data].blank?
|
||||
|
||||
params.require(:data).permit(alerts: [:follow, :follow_request, :favourite, :reblog, :mention, :poll])
|
||||
params.require(:data).permit(alerts: [:follow, :follow_request, :favourite, :reblog, :mention, :poll, :status])
|
||||
end
|
||||
end
|
||||
|
|
|
@ -32,7 +32,7 @@ class Api::V1::ScheduledStatusesController < Api::BaseController
|
|||
private
|
||||
|
||||
def set_statuses
|
||||
@statuses = current_account.scheduled_statuses.paginate_by_id(limit_param(DEFAULT_STATUSES_LIMIT), params_slice(:max_id, :since_id, :min_id))
|
||||
@statuses = current_account.scheduled_statuses.to_a_paginated_by_id(limit_param(DEFAULT_STATUSES_LIMIT), params_slice(:max_id, :since_id, :min_id))
|
||||
end
|
||||
|
||||
def set_status
|
||||
|
|
|
@ -5,7 +5,7 @@ class Api::V1::Statuses::BookmarksController < Api::BaseController
|
|||
|
||||
before_action -> { doorkeeper_authorize! :write, :'write:bookmarks' }
|
||||
before_action :require_user!
|
||||
before_action :set_status
|
||||
before_action :set_status, only: [:create]
|
||||
|
||||
def create
|
||||
current_account.bookmarks.find_or_create_by!(account: current_account, status: @status)
|
||||
|
@ -13,10 +13,20 @@ class Api::V1::Statuses::BookmarksController < Api::BaseController
|
|||
end
|
||||
|
||||
def destroy
|
||||
bookmark = current_account.bookmarks.find_by(status: @status)
|
||||
bookmark = current_account.bookmarks.find_by(status_id: params[:status_id])
|
||||
|
||||
if bookmark
|
||||
@status = bookmark.status
|
||||
else
|
||||
@status = Status.find(params[:status_id])
|
||||
authorize @status, :show?
|
||||
end
|
||||
|
||||
bookmark&.destroy!
|
||||
|
||||
render json: @status, serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new([@status], current_account.id, bookmarks_map: { @status.id => false })
|
||||
rescue Mastodon::NotPermittedError
|
||||
not_found
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
@ -22,6 +22,7 @@ class Api::V1::Statuses::FavouritedByAccountsController < Api::BaseController
|
|||
|
||||
def default_accounts
|
||||
Account
|
||||
.without_suspended
|
||||
.includes(:favourites, :account_stat)
|
||||
.references(:favourites)
|
||||
.where(favourites: { status_id: @status.id })
|
||||
|
|
|
@ -5,7 +5,7 @@ class Api::V1::Statuses::FavouritesController < Api::BaseController
|
|||
|
||||
before_action -> { doorkeeper_authorize! :write, :'write:favourites' }
|
||||
before_action :require_user!
|
||||
before_action :set_status
|
||||
before_action :set_status, only: [:create]
|
||||
|
||||
def create
|
||||
FavouriteService.new.call(current_account, @status)
|
||||
|
@ -13,8 +13,19 @@ class Api::V1::Statuses::FavouritesController < Api::BaseController
|
|||
end
|
||||
|
||||
def destroy
|
||||
fav = current_account.favourites.find_by(status_id: params[:status_id])
|
||||
|
||||
if fav
|
||||
@status = fav.status
|
||||
UnfavouriteWorker.perform_async(current_account.id, @status.id)
|
||||
else
|
||||
@status = Status.find(params[:status_id])
|
||||
authorize @status, :show?
|
||||
end
|
||||
|
||||
render json: @status, serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new([@status], current_account.id, favourites_map: { @status.id => false })
|
||||
rescue Mastodon::NotPermittedError
|
||||
not_found
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
@ -21,7 +21,7 @@ class Api::V1::Statuses::RebloggedByAccountsController < Api::BaseController
|
|||
end
|
||||
|
||||
def default_accounts
|
||||
Account.includes(:statuses, :account_stat).references(:statuses)
|
||||
Account.without_suspended.includes(:statuses, :account_stat).references(:statuses)
|
||||
end
|
||||
|
||||
def paginated_statuses
|
||||
|
|
|
@ -59,6 +59,7 @@ class Api::V1::StatusesController < Api::BaseController
|
|||
|
||||
@status.discard
|
||||
RemovalWorker.perform_async(@status.id, redraft: true)
|
||||
@status.account.statuses_count = @status.account.statuses_count - 1
|
||||
|
||||
render json: @status, serializer: REST::StatusSerializer, source_requested: true
|
||||
end
|
||||
|
|
|
@ -16,30 +16,29 @@ class Api::V1::Timelines::PublicController < Api::BaseController
|
|||
end
|
||||
|
||||
def load_statuses
|
||||
cached_public_statuses
|
||||
cached_public_statuses_page
|
||||
end
|
||||
|
||||
def cached_public_statuses
|
||||
cache_collection public_statuses, Status
|
||||
def cached_public_statuses_page
|
||||
cache_collection(public_statuses, Status)
|
||||
end
|
||||
|
||||
def public_statuses
|
||||
statuses = public_timeline_statuses.paginate_by_id(
|
||||
public_feed.get(
|
||||
limit_param(DEFAULT_STATUSES_LIMIT),
|
||||
params_slice(:max_id, :since_id, :min_id)
|
||||
params[:max_id],
|
||||
params[:since_id],
|
||||
params[:min_id]
|
||||
)
|
||||
|
||||
if truthy_param?(:only_media)
|
||||
# `SELECT DISTINCT id, updated_at` is too slow, so pluck ids at first, and then select id, updated_at with ids.
|
||||
status_ids = statuses.joins(:media_attachments).distinct(:id).pluck(:id)
|
||||
statuses.where(id: status_ids)
|
||||
else
|
||||
statuses
|
||||
end
|
||||
end
|
||||
|
||||
def public_timeline_statuses
|
||||
Status.as_public_timeline(current_account, truthy_param?(:remote) ? :remote : truthy_param?(:local))
|
||||
def public_feed
|
||||
PublicFeed.new(
|
||||
current_account,
|
||||
local: truthy_param?(:local),
|
||||
remote: truthy_param?(:remote),
|
||||
only_media: truthy_param?(:only_media)
|
||||
)
|
||||
end
|
||||
|
||||
def insert_pagination_headers
|
||||
|
|
|
@ -20,30 +20,29 @@ class Api::V1::Timelines::TagController < Api::BaseController
|
|||
end
|
||||
|
||||
def cached_tagged_statuses
|
||||
cache_collection tagged_statuses, Status
|
||||
end
|
||||
|
||||
def tagged_statuses
|
||||
if @tag.nil?
|
||||
[]
|
||||
else
|
||||
statuses = tag_timeline_statuses.paginate_by_id(
|
||||
limit_param(DEFAULT_STATUSES_LIMIT),
|
||||
params_slice(:max_id, :since_id, :min_id)
|
||||
)
|
||||
|
||||
if truthy_param?(:only_media)
|
||||
# `SELECT DISTINCT id, updated_at` is too slow, so pluck ids at first, and then select id, updated_at with ids.
|
||||
status_ids = statuses.joins(:media_attachments).distinct(:id).pluck(:id)
|
||||
statuses.where(id: status_ids)
|
||||
else
|
||||
statuses
|
||||
end
|
||||
end
|
||||
@tag.nil? ? [] : cache_collection(tag_timeline_statuses, Status)
|
||||
end
|
||||
|
||||
def tag_timeline_statuses
|
||||
HashtagQueryService.new.call(@tag, params.slice(:any, :all, :none), current_account, truthy_param?(:local))
|
||||
tag_feed.get(
|
||||
limit_param(DEFAULT_STATUSES_LIMIT),
|
||||
params[:max_id],
|
||||
params[:since_id],
|
||||
params[:min_id]
|
||||
)
|
||||
end
|
||||
|
||||
def tag_feed
|
||||
TagFeed.new(
|
||||
@tag,
|
||||
current_account,
|
||||
any: params[:any],
|
||||
all: params[:all],
|
||||
none: params[:none],
|
||||
local: truthy_param?(:local),
|
||||
remote: truthy_param?(:remote),
|
||||
only_media: truthy_param?(:only_media)
|
||||
)
|
||||
end
|
||||
|
||||
def insert_pagination_headers
|
||||
|
|
|
@ -22,6 +22,7 @@ class Api::Web::PushSubscriptionsController < Api::Web::BaseController
|
|||
reblog: alerts_enabled,
|
||||
mention: alerts_enabled,
|
||||
poll: alerts_enabled,
|
||||
status: alerts_enabled,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -57,6 +58,6 @@ class Api::Web::PushSubscriptionsController < Api::Web::BaseController
|
|||
end
|
||||
|
||||
def data_params
|
||||
@data_params ||= params.require(:data).permit(alerts: [:follow, :follow_request, :favourite, :reblog, :mention, :poll])
|
||||
@data_params ||= params.require(:data).permit(alerts: [:follow, :follow_request, :favourite, :reblog, :mention, :poll, :status])
|
||||
end
|
||||
end
|
||||
|
|
|
@ -28,7 +28,7 @@ class ApplicationController < ActionController::Base
|
|||
rescue_from ActiveRecord::RecordNotFound, with: :not_found
|
||||
rescue_from Mastodon::NotPermittedError, with: :forbidden
|
||||
rescue_from HTTP::Error, OpenSSL::SSL::SSLError, with: :internal_server_error
|
||||
rescue_from Mastodon::RaceConditionError, with: :service_unavailable
|
||||
rescue_from Mastodon::RaceConditionError, Seahorse::Client::NetworkingError, Stoplight::Error::RedLight, with: :service_unavailable
|
||||
rescue_from Mastodon::RateLimitExceededError, with: :too_many_requests
|
||||
|
||||
before_action :store_current_location, except: :raise_not_found, unless: :devise_controller?
|
||||
|
@ -55,7 +55,7 @@ class ApplicationController < ActionController::Base
|
|||
end
|
||||
|
||||
def store_current_location
|
||||
store_location_for(:user, request.url) unless request.format == :json
|
||||
store_location_for(:user, request.url) unless [:json, :rss].include?(request.format&.to_sym)
|
||||
end
|
||||
|
||||
def require_admin!
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
class Auth::RegistrationsController < Devise::RegistrationsController
|
||||
include Devise::Controllers::Rememberable
|
||||
include RegistrationSpamConcern
|
||||
|
||||
layout :determine_layout
|
||||
|
||||
|
@ -13,6 +14,7 @@ class Auth::RegistrationsController < Devise::RegistrationsController
|
|||
before_action :set_body_classes, only: [:new, :create, :edit, :update]
|
||||
before_action :require_not_suspended!, only: [:update]
|
||||
before_action :set_cache_headers, only: [:edit, :update]
|
||||
before_action :set_registration_form_time, only: :new
|
||||
|
||||
skip_before_action :require_functional!, only: [:edit, :update]
|
||||
|
||||
|
@ -47,14 +49,15 @@ class Auth::RegistrationsController < Devise::RegistrationsController
|
|||
|
||||
resource.locale = I18n.locale
|
||||
resource.invite_code = params[:invite_code] if resource.invite_code.blank?
|
||||
resource.current_sign_in_ip = request.remote_ip
|
||||
resource.registration_form_time = session[:registration_form_time]
|
||||
resource.sign_up_ip = request.remote_ip
|
||||
|
||||
resource.build_account if resource.account.nil?
|
||||
end
|
||||
|
||||
def configure_sign_up_params
|
||||
devise_parameter_sanitizer.permit(:sign_up) do |u|
|
||||
u.permit({ account_attributes: [:username], invite_request_attributes: [:text] }, :email, :password, :password_confirmation, :invite_code, :agreement)
|
||||
u.permit({ account_attributes: [:username], invite_request_attributes: [:text] }, :email, :password, :password_confirmation, :invite_code, :agreement, :website, :confirm_password)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ class Auth::SessionsController < Devise::SessionsController
|
|||
|
||||
skip_before_action :require_no_authentication, only: [:create]
|
||||
skip_before_action :require_functional!
|
||||
skip_before_action :update_user_sign_in
|
||||
|
||||
include TwoFactorAuthenticationConcern
|
||||
include SignInTokenAuthenticationConcern
|
||||
|
@ -24,6 +25,7 @@ class Auth::SessionsController < Devise::SessionsController
|
|||
|
||||
def create
|
||||
super do |resource|
|
||||
resource.update_sign_in!(request, new_sign_in: true)
|
||||
remember_me(resource)
|
||||
flash.delete(:notice)
|
||||
end
|
||||
|
@ -37,11 +39,27 @@ class Auth::SessionsController < Devise::SessionsController
|
|||
store_location_for(:user, tmp_stored_location) if continue_after?
|
||||
end
|
||||
|
||||
def webauthn_options
|
||||
user = find_user
|
||||
|
||||
if user.webauthn_enabled?
|
||||
options_for_get = WebAuthn::Credential.options_for_get(
|
||||
allow: user.webauthn_credentials.pluck(:external_id)
|
||||
)
|
||||
|
||||
session[:webauthn_challenge] = options_for_get.challenge
|
||||
|
||||
render json: options_for_get, status: :ok
|
||||
else
|
||||
render json: { error: t('webauthn_credentials.not_enabled') }, status: :unauthorized
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def find_user
|
||||
if session[:attempt_user_id]
|
||||
User.find(session[:attempt_user_id])
|
||||
User.find_by(id: session[:attempt_user_id])
|
||||
else
|
||||
user = User.authenticate_with_ldap(user_params) if Devise.ldap_authentication
|
||||
user ||= User.authenticate_with_pam(user_params) if Devise.pam_authentication
|
||||
|
@ -51,7 +69,7 @@ class Auth::SessionsController < Devise::SessionsController
|
|||
end
|
||||
|
||||
def user_params
|
||||
params.require(:user).permit(:email, :password, :otp_attempt, :sign_in_token_attempt)
|
||||
params.require(:user).permit(:email, :password, :otp_attempt, :sign_in_token_attempt, credential: {})
|
||||
end
|
||||
|
||||
def after_sign_in_path_for(resource)
|
||||
|
@ -74,6 +92,7 @@ class Auth::SessionsController < Devise::SessionsController
|
|||
|
||||
def require_no_authentication
|
||||
super
|
||||
|
||||
# Delete flash message that isn't entirely useful and may be confusing in
|
||||
# most cases because /web doesn't display/clear flash messages.
|
||||
flash.delete(:alert) if flash[:alert] == I18n.t('devise.failure.already_authenticated')
|
||||
|
@ -91,13 +110,30 @@ class Auth::SessionsController < Devise::SessionsController
|
|||
|
||||
def home_paths(resource)
|
||||
paths = [about_path]
|
||||
|
||||
if single_user_mode? && resource.is_a?(User)
|
||||
paths << short_account_path(username: resource.account)
|
||||
end
|
||||
|
||||
paths
|
||||
end
|
||||
|
||||
def continue_after?
|
||||
truthy_param?(:continue)
|
||||
end
|
||||
|
||||
def restart_session
|
||||
clear_attempt_from_session
|
||||
redirect_to new_user_session_path, alert: I18n.t('devise.failure.timeout')
|
||||
end
|
||||
|
||||
def set_attempt_session(user)
|
||||
session[:attempt_user_id] = user.id
|
||||
session[:attempt_user_updated_at] = user.updated_at.to_s
|
||||
end
|
||||
|
||||
def clear_attempt_from_session
|
||||
session.delete(:attempt_user_id)
|
||||
session.delete(:attempt_user_updated_at)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -29,6 +29,24 @@ module AccountOwnedConcern
|
|||
end
|
||||
|
||||
def check_account_suspension
|
||||
expires_in(3.minutes, public: true) && gone if @account.suspended?
|
||||
if @account.suspended_permanently?
|
||||
permanent_suspension_response
|
||||
elsif @account.suspended? && !skip_temporary_suspension_response?
|
||||
temporary_suspension_response
|
||||
end
|
||||
end
|
||||
|
||||
def skip_temporary_suspension_response?
|
||||
false
|
||||
end
|
||||
|
||||
def permanent_suspension_response
|
||||
expires_in(3.minutes, public: true)
|
||||
gone
|
||||
end
|
||||
|
||||
def temporary_suspension_response
|
||||
expires_in(3.minutes, public: true)
|
||||
forbidden
|
||||
end
|
||||
end
|
||||
|
|
|
@ -47,4 +47,8 @@ module CacheConcern
|
|||
|
||||
raw.map { |item| cached_keys_with_value[item.id] || uncached[item.id] }.compact
|
||||
end
|
||||
|
||||
def cache_collection_paginated_by_id(raw, klass, limit, options)
|
||||
cache_collection raw.cache_ids.to_a_paginated_by_id(limit, options), klass
|
||||
end
|
||||
end
|
||||
|
|
|
@ -32,7 +32,6 @@ module ChallengableConcern
|
|||
if params.key?(:form_challenge)
|
||||
if challenge_passed?
|
||||
session[:challenge_passed_at] = Time.now.utc
|
||||
return
|
||||
else
|
||||
flash.now[:alert] = I18n.t('challenge.invalid_password')
|
||||
render_challenge
|
||||
|
|
|
@ -5,7 +5,6 @@ module ExportControllerConcern
|
|||
|
||||
included do
|
||||
before_action :authenticate_user!
|
||||
before_action :require_not_suspended!
|
||||
before_action :load_export
|
||||
|
||||
skip_before_action :require_functional!
|
||||
|
@ -30,8 +29,4 @@ module ExportControllerConcern
|
|||
def export_filename
|
||||
"#{controller_name}.csv"
|
||||
end
|
||||
|
||||
def require_not_suspended!
|
||||
forbidden if current_account.suspended?
|
||||
end
|
||||
end
|
||||
|
|
9
app/controllers/concerns/registration_spam_concern.rb
Normal file
9
app/controllers/concerns/registration_spam_concern.rb
Normal file
|
@ -0,0 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module RegistrationSpamConcern
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
def set_registration_form_time
|
||||
session[:registration_form_time] = Time.now.utc
|
||||
end
|
||||
end
|
|
@ -18,7 +18,9 @@ module SignInTokenAuthenticationConcern
|
|||
def authenticate_with_sign_in_token
|
||||
user = self.resource = find_user
|
||||
|
||||
if user_params[:sign_in_token_attempt].present? && session[:attempt_user_id]
|
||||
if user.present? && session[:attempt_user_id].present? && session[:attempt_user_updated_at] != user.updated_at.to_s
|
||||
restart_session
|
||||
elsif user_params.key?(:sign_in_token_attempt) && session[:attempt_user_id]
|
||||
authenticate_with_sign_in_token_attempt(user)
|
||||
elsif user.present? && user.external_or_valid_password?(user_params[:password])
|
||||
prompt_for_sign_in_token(user)
|
||||
|
@ -27,7 +29,7 @@ module SignInTokenAuthenticationConcern
|
|||
|
||||
def authenticate_with_sign_in_token_attempt(user)
|
||||
if valid_sign_in_token_attempt?(user)
|
||||
session.delete(:attempt_user_id)
|
||||
clear_attempt_from_session
|
||||
remember_me(user)
|
||||
sign_in(user)
|
||||
else
|
||||
|
@ -42,10 +44,10 @@ module SignInTokenAuthenticationConcern
|
|||
UserMailer.sign_in_token(user, request.remote_ip, request.user_agent, Time.now.utc.to_s).deliver_later!
|
||||
end
|
||||
|
||||
set_locale do
|
||||
session[:attempt_user_id] = user.id
|
||||
set_attempt_session(user)
|
||||
|
||||
@body_classes = 'lighter'
|
||||
render :sign_in_token
|
||||
end
|
||||
|
||||
set_locale { render :sign_in_token }
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,6 +7,44 @@ module SignatureVerification
|
|||
|
||||
include DomainControlHelper
|
||||
|
||||
EXPIRATION_WINDOW_LIMIT = 12.hours
|
||||
CLOCK_SKEW_MARGIN = 1.hour
|
||||
|
||||
class SignatureVerificationError < StandardError; end
|
||||
|
||||
class SignatureParamsParser < Parslet::Parser
|
||||
rule(:token) { match("[0-9a-zA-Z!#$%&'*+.^_`|~-]").repeat(1).as(:token) }
|
||||
rule(:quoted_string) { str('"') >> (qdtext | quoted_pair).repeat.as(:quoted_string) >> str('"') }
|
||||
# qdtext and quoted_pair are not exactly according to spec but meh
|
||||
rule(:qdtext) { match('[^\\\\"]') }
|
||||
rule(:quoted_pair) { str('\\') >> any }
|
||||
rule(:bws) { match('\s').repeat }
|
||||
rule(:param) { (token.as(:key) >> bws >> str('=') >> bws >> (token | quoted_string).as(:value)).as(:param) }
|
||||
rule(:comma) { bws >> str(',') >> bws }
|
||||
# Old versions of node-http-signature add an incorrect "Signature " prefix to the header
|
||||
rule(:buggy_prefix) { str('Signature ') }
|
||||
rule(:params) { buggy_prefix.maybe >> (param >> (comma >> param).repeat).as(:params) }
|
||||
root(:params)
|
||||
end
|
||||
|
||||
class SignatureParamsTransformer < Parslet::Transform
|
||||
rule(params: subtree(:p)) do
|
||||
(p.is_a?(Array) ? p : [p]).each_with_object({}) { |(key, val), h| h[key] = val }
|
||||
end
|
||||
|
||||
rule(param: { key: simple(:key), value: simple(:val) }) do
|
||||
[key, val]
|
||||
end
|
||||
|
||||
rule(quoted_string: simple(:string)) do
|
||||
string.to_s
|
||||
end
|
||||
|
||||
rule(token: simple(:string)) do
|
||||
string.to_s
|
||||
end
|
||||
end
|
||||
|
||||
def require_signature!
|
||||
render plain: signature_verification_failure_reason, status: signature_verification_failure_code unless signed_request_account
|
||||
end
|
||||
|
@ -24,72 +62,41 @@ module SignatureVerification
|
|||
end
|
||||
|
||||
def signature_key_id
|
||||
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
|
||||
|
||||
signature_params['keyId']
|
||||
rescue SignatureVerificationError
|
||||
nil
|
||||
end
|
||||
|
||||
def signed_request_account
|
||||
return @signed_request_account if defined?(@signed_request_account)
|
||||
|
||||
unless signed_request?
|
||||
@signature_verification_failure_reason = 'Request not signed'
|
||||
@signed_request_account = nil
|
||||
return
|
||||
end
|
||||
raise SignatureVerificationError, 'Request not signed' unless signed_request?
|
||||
raise SignatureVerificationError, 'Incompatible request signature. keyId and signature are required' if missing_required_signature_parameters?
|
||||
raise SignatureVerificationError, 'Unsupported signature algorithm (only rsa-sha256 and hs2019 are supported)' unless %w(rsa-sha256 hs2019).include?(signature_algorithm)
|
||||
raise SignatureVerificationError, 'Signed request date outside acceptable time window' unless matches_time_window?
|
||||
|
||||
if request.headers['Date'].present? && !matches_time_window?
|
||||
@signature_verification_failure_reason = 'Signed request date outside acceptable time window'
|
||||
@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)
|
||||
@signature_verification_failure_reason = 'Incompatible request signature'
|
||||
@signed_request_account = nil
|
||||
return
|
||||
end
|
||||
verify_signature_strength!
|
||||
verify_body_digest!
|
||||
|
||||
account = account_from_key_id(signature_params['keyId'])
|
||||
|
||||
if account.nil?
|
||||
@signature_verification_failure_reason = "Public key not found for key #{signature_params['keyId']}"
|
||||
@signed_request_account = nil
|
||||
return
|
||||
end
|
||||
raise SignatureVerificationError, "Public key not found for key #{signature_params['keyId']}" if account.nil?
|
||||
|
||||
signature = Base64.decode64(signature_params['signature'])
|
||||
compare_signed_string = build_signed_string(signature_params['headers'])
|
||||
compare_signed_string = build_signed_string
|
||||
|
||||
return account unless verify_signature(account, signature, compare_signed_string).nil?
|
||||
|
||||
account = stoplight_wrap_request { account.possibly_stale? ? account.refresh! : account_refresh_key(account) }
|
||||
|
||||
if account.nil?
|
||||
@signature_verification_failure_reason = "Public key not found for key #{signature_params['keyId']}"
|
||||
@signed_request_account = nil
|
||||
return
|
||||
end
|
||||
raise SignatureVerificationError, "Public key not found for key #{signature_params['keyId']}" if account.nil?
|
||||
|
||||
return account unless verify_signature(account, signature, compare_signed_string).nil?
|
||||
|
||||
@signature_verification_failure_reason = "Verification failed for #{account.username}@#{account.domain} #{account.uri}"
|
||||
@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
|
||||
rescue SignatureVerificationError => e
|
||||
@signature_verification_failure_reason = e.message
|
||||
@signed_request_account = nil
|
||||
end
|
||||
|
||||
|
@ -99,8 +106,42 @@ module SignatureVerification
|
|||
|
||||
private
|
||||
|
||||
def signature_params
|
||||
@signature_params ||= begin
|
||||
raw_signature = request.headers['Signature']
|
||||
tree = SignatureParamsParser.new.parse(raw_signature)
|
||||
SignatureParamsTransformer.new.apply(tree)
|
||||
end
|
||||
rescue Parslet::ParseFailed
|
||||
raise SignatureVerificationError, 'Error parsing signature parameters'
|
||||
end
|
||||
|
||||
def signature_algorithm
|
||||
signature_params.fetch('algorithm', 'hs2019')
|
||||
end
|
||||
|
||||
def signed_headers
|
||||
signature_params.fetch('headers', signature_algorithm == 'hs2019' ? '(created)' : 'date').downcase.split(' ')
|
||||
end
|
||||
|
||||
def verify_signature_strength!
|
||||
raise SignatureVerificationError, 'Mastodon requires the Date header or (created) pseudo-header to be signed' unless signed_headers.include?('date') || signed_headers.include?('(created)')
|
||||
raise SignatureVerificationError, 'Mastodon requires the Digest header or (request-target) pseudo-header to be signed' unless signed_headers.include?(Request::REQUEST_TARGET) || signed_headers.include?('digest')
|
||||
raise SignatureVerificationError, 'Mastodon requires the Host header to be signed when doing a GET request' if request.get? && !signed_headers.include?('host')
|
||||
raise SignatureVerificationError, 'Mastodon requires the Digest header to be signed when doing a POST request' if request.post? && !signed_headers.include?('digest')
|
||||
end
|
||||
|
||||
def verify_body_digest!
|
||||
return unless signed_headers.include?('digest')
|
||||
|
||||
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]
|
||||
end
|
||||
|
||||
def verify_signature(account, signature, compare_signed_string)
|
||||
if account.keypair.public_key.verify(OpenSSL::Digest::SHA256.new, 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
|
||||
end
|
||||
|
@ -108,14 +149,20 @@ module SignatureVerification
|
|||
nil
|
||||
end
|
||||
|
||||
def build_signed_string(signed_headers)
|
||||
signed_headers = 'date' if signed_headers.blank?
|
||||
|
||||
signed_headers.downcase.split(' ').map do |signed_header|
|
||||
def build_signed_string
|
||||
signed_headers.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}"
|
||||
elsif signed_header == '(created)'
|
||||
raise SignatureVerificationError, 'Invalid pseudo-header (created) for rsa-sha256' unless signature_algorithm == 'hs2019'
|
||||
raise SignatureVerificationError, 'Pseudo-header (created) used but corresponding argument missing' if signature_params['created'].blank?
|
||||
|
||||
"(created): #{signature_params['created']}"
|
||||
elsif signed_header == '(expires)'
|
||||
raise SignatureVerificationError, 'Invalid pseudo-header (expires) for rsa-sha256' unless signature_algorithm == 'hs2019'
|
||||
raise SignatureVerificationError, 'Pseudo-header (expires) used but corresponding argument missing' if signature_params['expires'].blank?
|
||||
|
||||
"(expires): #{signature_params['expires']}"
|
||||
else
|
||||
"#{signed_header}: #{request.headers[to_header_name(signed_header)]}"
|
||||
end
|
||||
|
@ -123,26 +170,40 @@ module SignatureVerification
|
|||
end
|
||||
|
||||
def matches_time_window?
|
||||
created_time = nil
|
||||
expires_time = nil
|
||||
|
||||
begin
|
||||
time_sent = Time.httpdate(request.headers['Date'])
|
||||
if signature_algorithm == 'hs2019' && signature_params['created'].present?
|
||||
created_time = Time.at(signature_params['created'].to_i).utc
|
||||
elsif request.headers['Date'].present?
|
||||
created_time = Time.httpdate(request.headers['Date']).utc
|
||||
end
|
||||
|
||||
expires_time = Time.at(signature_params['expires'].to_i).utc if signature_params['expires'].present?
|
||||
rescue ArgumentError
|
||||
return false
|
||||
end
|
||||
|
||||
(Time.now.utc - time_sent).abs <= 12.hours
|
||||
expires_time ||= created_time + 5.minutes unless created_time.nil?
|
||||
expires_time = [expires_time, created_time + EXPIRATION_WINDOW_LIMIT].min unless created_time.nil?
|
||||
|
||||
return false if created_time.present? && created_time > Time.now.utc + CLOCK_SKEW_MARGIN
|
||||
return false if expires_time.present? && Time.now.utc > expires_time + CLOCK_SKEW_MARGIN
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
def body_digest
|
||||
"SHA-256=#{Digest::SHA256.base64digest(request_body)}"
|
||||
@body_digest ||= 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?
|
||||
def missing_required_signature_parameters?
|
||||
signature_params['keyId'].blank? || signature_params['signature'].blank?
|
||||
end
|
||||
|
||||
def account_from_key_id(key_id)
|
||||
|
|
|
@ -8,7 +8,23 @@ module TwoFactorAuthenticationConcern
|
|||
end
|
||||
|
||||
def two_factor_enabled?
|
||||
find_user&.otp_required_for_login?
|
||||
find_user&.two_factor_enabled?
|
||||
end
|
||||
|
||||
def valid_webauthn_credential?(user, webauthn_credential)
|
||||
user_credential = user.webauthn_credentials.find_by!(external_id: webauthn_credential.id)
|
||||
|
||||
begin
|
||||
webauthn_credential.verify(
|
||||
session[:webauthn_challenge],
|
||||
public_key: user_credential.public_key,
|
||||
sign_count: user_credential.sign_count
|
||||
)
|
||||
|
||||
user_credential.update!(sign_count: webauthn_credential.sign_count)
|
||||
rescue WebAuthn::Error
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def valid_otp_attempt?(user)
|
||||
|
@ -21,16 +37,33 @@ module TwoFactorAuthenticationConcern
|
|||
def authenticate_with_two_factor
|
||||
user = self.resource = find_user
|
||||
|
||||
if user_params[:otp_attempt].present? && session[:attempt_user_id]
|
||||
authenticate_with_two_factor_attempt(user)
|
||||
if user.present? && session[:attempt_user_id].present? && session[:attempt_user_updated_at] != user.updated_at.to_s
|
||||
restart_session
|
||||
elsif user.webauthn_enabled? && user_params.key?(:credential) && session[:attempt_user_id]
|
||||
authenticate_with_two_factor_via_webauthn(user)
|
||||
elsif user_params.key?(:otp_attempt) && session[:attempt_user_id]
|
||||
authenticate_with_two_factor_via_otp(user)
|
||||
elsif user.present? && user.external_or_valid_password?(user_params[:password])
|
||||
prompt_for_two_factor(user)
|
||||
end
|
||||
end
|
||||
|
||||
def authenticate_with_two_factor_attempt(user)
|
||||
def authenticate_with_two_factor_via_webauthn(user)
|
||||
webauthn_credential = WebAuthn::Credential.from_get(user_params[:credential])
|
||||
|
||||
if valid_webauthn_credential?(user, webauthn_credential)
|
||||
clear_attempt_from_session
|
||||
remember_me(user)
|
||||
sign_in(user)
|
||||
render json: { redirect_path: root_path }, status: :ok
|
||||
else
|
||||
render json: { error: t('webauthn_credentials.invalid_credential') }, status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
def authenticate_with_two_factor_via_otp(user)
|
||||
if valid_otp_attempt?(user)
|
||||
session.delete(:attempt_user_id)
|
||||
clear_attempt_from_session
|
||||
remember_me(user)
|
||||
sign_in(user)
|
||||
else
|
||||
|
@ -40,10 +73,18 @@ module TwoFactorAuthenticationConcern
|
|||
end
|
||||
|
||||
def prompt_for_two_factor(user)
|
||||
set_locale do
|
||||
session[:attempt_user_id] = user.id
|
||||
set_attempt_session(user)
|
||||
|
||||
@body_classes = 'lighter'
|
||||
render :two_factor
|
||||
@webauthn_enabled = user.webauthn_enabled?
|
||||
@scheme_type = begin
|
||||
if user.webauthn_enabled? && user_params[:otp_attempt].blank?
|
||||
'webauthn'
|
||||
else
|
||||
'totp'
|
||||
end
|
||||
end
|
||||
|
||||
set_locale { render :two_factor }
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,14 +6,13 @@ module UserTrackingConcern
|
|||
UPDATE_SIGN_IN_HOURS = 24
|
||||
|
||||
included do
|
||||
before_action :set_user_activity
|
||||
before_action :update_user_sign_in
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_user_activity
|
||||
return unless user_needs_sign_in_update?
|
||||
current_user.update_tracked_fields!(request)
|
||||
def update_user_sign_in
|
||||
current_user.update_sign_in!(request) if user_needs_sign_in_update?
|
||||
end
|
||||
|
||||
def user_needs_sign_in_update?
|
||||
|
|
|
@ -9,7 +9,7 @@ class FiltersController < ApplicationController
|
|||
before_action :set_body_classes
|
||||
|
||||
def index
|
||||
@filters = current_account.custom_filters
|
||||
@filters = current_account.custom_filters.order(:phrase)
|
||||
end
|
||||
|
||||
def new
|
||||
|
|
|
@ -52,6 +52,14 @@ class FollowerAccountsController < ApplicationController
|
|||
account_followers_url(@account, page: page) unless page.nil?
|
||||
end
|
||||
|
||||
def next_page_url
|
||||
page_url(follows.next_page) if follows.respond_to?(:next_page)
|
||||
end
|
||||
|
||||
def prev_page_url
|
||||
page_url(follows.prev_page) if follows.respond_to?(:prev_page)
|
||||
end
|
||||
|
||||
def collection_presenter
|
||||
if page_requested?
|
||||
ActivityPub::CollectionPresenter.new(
|
||||
|
@ -60,8 +68,8 @@ class FollowerAccountsController < ApplicationController
|
|||
size: @account.followers_count,
|
||||
items: follows.map { |f| ActivityPub::TagManager.instance.uri_for(f.account) },
|
||||
part_of: account_followers_url(@account),
|
||||
next: page_url(follows.next_page),
|
||||
prev: page_url(follows.prev_page)
|
||||
next: next_page_url,
|
||||
prev: prev_page_url
|
||||
)
|
||||
else
|
||||
ActivityPub::CollectionPresenter.new(
|
||||
|
|
|
@ -52,6 +52,14 @@ class FollowingAccountsController < ApplicationController
|
|||
account_following_index_url(@account, page: page) unless page.nil?
|
||||
end
|
||||
|
||||
def next_page_url
|
||||
page_url(follows.next_page) if follows.respond_to?(:next_page)
|
||||
end
|
||||
|
||||
def prev_page_url
|
||||
page_url(follows.prev_page) if follows.respond_to?(:prev_page)
|
||||
end
|
||||
|
||||
def collection_presenter
|
||||
if page_requested?
|
||||
ActivityPub::CollectionPresenter.new(
|
||||
|
@ -60,8 +68,8 @@ class FollowingAccountsController < ApplicationController
|
|||
size: @account.following_count,
|
||||
items: follows.map { |f| ActivityPub::TagManager.instance.uri_for(f.target_account) },
|
||||
part_of: account_following_index_url(@account),
|
||||
next: page_url(follows.next_page),
|
||||
prev: page_url(follows.prev_page)
|
||||
next: next_page_url,
|
||||
prev: prev_page_url
|
||||
)
|
||||
else
|
||||
ActivityPub::CollectionPresenter.new(
|
||||
|
|
|
@ -17,6 +17,6 @@ class InstanceActorsController < ApplicationController
|
|||
end
|
||||
|
||||
def restrict_fields_to
|
||||
%i(id type preferred_username inbox public_key endpoints url manually_approves_followers)
|
||||
%i(id type preferred_username inbox outbox public_key endpoints url manually_approves_followers)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,6 +5,7 @@ class Oauth::AuthorizedApplicationsController < Doorkeeper::AuthorizedApplicatio
|
|||
|
||||
before_action :store_current_location
|
||||
before_action :authenticate_resource_owner!
|
||||
before_action :require_not_suspended!, only: :destroy
|
||||
before_action :set_body_classes
|
||||
|
||||
skip_before_action :require_functional!
|
||||
|
@ -25,4 +26,8 @@ class Oauth::AuthorizedApplicationsController < Doorkeeper::AuthorizedApplicatio
|
|||
def store_current_location
|
||||
store_location_for(:user, request.url)
|
||||
end
|
||||
|
||||
def require_not_suspended!
|
||||
forbidden if current_account.suspended?
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,6 +5,7 @@ class RelationshipsController < ApplicationController
|
|||
|
||||
before_action :authenticate_user!
|
||||
before_action :set_accounts, only: :show
|
||||
before_action :set_relationships, only: :show
|
||||
before_action :set_body_classes
|
||||
|
||||
helper_method :following_relationship?, :followed_by_relationship?, :mutual_relationship?
|
||||
|
@ -28,6 +29,10 @@ class RelationshipsController < ApplicationController
|
|||
@accounts = RelationshipFilter.new(current_account, filter_params).results.page(params[:page]).per(40)
|
||||
end
|
||||
|
||||
def set_relationships
|
||||
@relationships = AccountRelationshipsPresenter.new(@accounts.pluck(:id), current_user.account_id)
|
||||
end
|
||||
|
||||
def form_account_batch_params
|
||||
params.require(:form_account_batch).permit(:action, account_ids: [])
|
||||
end
|
||||
|
@ -49,7 +54,9 @@ class RelationshipsController < ApplicationController
|
|||
end
|
||||
|
||||
def action_from_button
|
||||
if params[:unfollow]
|
||||
if params[:follow]
|
||||
'follow'
|
||||
elsif params[:unfollow]
|
||||
'unfollow'
|
||||
elsif params[:remove_from_followers]
|
||||
'remove_from_followers'
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Settings::AliasesController < Settings::BaseController
|
||||
layout 'admin'
|
||||
skip_before_action :require_functional!
|
||||
|
||||
before_action :authenticate_user!
|
||||
before_action :require_not_suspended!
|
||||
before_action :set_aliases, except: :destroy
|
||||
before_action :set_alias, only: :destroy
|
||||
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Settings::ApplicationsController < Settings::BaseController
|
||||
layout 'admin'
|
||||
|
||||
before_action :authenticate_user!
|
||||
before_action :set_application, only: [:show, :update, :destroy, :regenerate]
|
||||
before_action :prepare_scopes, only: [:create, :update]
|
||||
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Settings::BaseController < ApplicationController
|
||||
layout 'admin'
|
||||
|
||||
before_action :authenticate_user!
|
||||
before_action :set_body_classes
|
||||
before_action :set_cache_headers
|
||||
|
||||
|
@ -13,4 +16,8 @@ class Settings::BaseController < ApplicationController
|
|||
def set_cache_headers
|
||||
response.headers['Cache-Control'] = 'no-cache, no-store, max-age=0, must-revalidate'
|
||||
end
|
||||
|
||||
def require_not_suspended!
|
||||
forbidden if current_account.suspended?
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,14 +1,11 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Settings::DeletesController < Settings::BaseController
|
||||
layout 'admin'
|
||||
|
||||
before_action :check_enabled_deletion
|
||||
before_action :authenticate_user!
|
||||
before_action :require_not_suspended!
|
||||
|
||||
skip_before_action :require_functional!
|
||||
|
||||
before_action :require_not_suspended!
|
||||
before_action :check_enabled_deletion
|
||||
|
||||
def show
|
||||
@confirmation = Form::DeleteConfirmation.new
|
||||
end
|
||||
|
@ -45,8 +42,8 @@ class Settings::DeletesController < Settings::BaseController
|
|||
end
|
||||
|
||||
def destroy_account!
|
||||
current_account.suspend!
|
||||
Admin::SuspensionWorker.perform_async(current_user.account_id, true)
|
||||
current_account.suspend!(origin: :local)
|
||||
AccountDeletionWorker.perform_async(current_user.account_id)
|
||||
sign_out
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
module Settings
|
||||
module Exports
|
||||
class BlockedAccountsController < ApplicationController
|
||||
class BlockedAccountsController < BaseController
|
||||
include ExportControllerConcern
|
||||
|
||||
def index
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
module Settings
|
||||
module Exports
|
||||
class BlockedDomainsController < ApplicationController
|
||||
class BlockedDomainsController < BaseController
|
||||
include ExportControllerConcern
|
||||
|
||||
def index
|
||||
|
|
19
app/controllers/settings/exports/bookmarks_controller.rb
Normal file
19
app/controllers/settings/exports/bookmarks_controller.rb
Normal file
|
@ -0,0 +1,19 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Settings
|
||||
module Exports
|
||||
class BookmarksController < BaseController
|
||||
include ExportControllerConcern
|
||||
|
||||
def index
|
||||
send_export_file
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def export_data
|
||||
@export.to_bookmarks_csv
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
module Settings
|
||||
module Exports
|
||||
class FollowingAccountsController < ApplicationController
|
||||
class FollowingAccountsController < BaseController
|
||||
include ExportControllerConcern
|
||||
|
||||
def index
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
module Settings
|
||||
module Exports
|
||||
class ListsController < ApplicationController
|
||||
class ListsController < BaseController
|
||||
include ExportControllerConcern
|
||||
|
||||
def index
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
module Settings
|
||||
module Exports
|
||||
class MutedAccountsController < ApplicationController
|
||||
class MutedAccountsController < BaseController
|
||||
include ExportControllerConcern
|
||||
|
||||
def index
|
||||
|
|
|
@ -3,11 +3,6 @@
|
|||
class Settings::ExportsController < Settings::BaseController
|
||||
include Authorization
|
||||
|
||||
layout 'admin'
|
||||
|
||||
before_action :authenticate_user!
|
||||
before_action :require_not_suspended!
|
||||
|
||||
skip_before_action :require_functional!
|
||||
|
||||
def show
|
||||
|
@ -16,8 +11,6 @@ class Settings::ExportsController < Settings::BaseController
|
|||
end
|
||||
|
||||
def create
|
||||
raise Mastodon::NotPermittedError unless user_signed_in?
|
||||
|
||||
backup = nil
|
||||
|
||||
RedisLock.acquire(lock_options) do |lock|
|
||||
|
@ -37,8 +30,4 @@ class Settings::ExportsController < Settings::BaseController
|
|||
def lock_options
|
||||
{ redis: Redis.current, key: "backup:#{current_user.id}" }
|
||||
end
|
||||
|
||||
def require_not_suspended!
|
||||
forbidden if current_account.suspended?
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,12 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Settings::FeaturedTagsController < Settings::BaseController
|
||||
layout 'admin'
|
||||
|
||||
before_action :authenticate_user!
|
||||
before_action :set_featured_tags, only: :index
|
||||
before_action :set_featured_tag, except: [:index, :create]
|
||||
before_action :set_most_used_tags, only: :index
|
||||
before_action :set_recently_used_tags, only: :index
|
||||
|
||||
def index
|
||||
@featured_tag = FeaturedTag.new
|
||||
|
@ -20,7 +17,7 @@ class Settings::FeaturedTagsController < Settings::BaseController
|
|||
redirect_to settings_featured_tags_path
|
||||
else
|
||||
set_featured_tags
|
||||
set_most_used_tags
|
||||
set_recently_used_tags
|
||||
|
||||
render :index
|
||||
end
|
||||
|
@ -41,8 +38,8 @@ class Settings::FeaturedTagsController < Settings::BaseController
|
|||
@featured_tags = current_account.featured_tags.order(statuses_count: :desc).reject(&:new_record?)
|
||||
end
|
||||
|
||||
def set_most_used_tags
|
||||
@most_used_tags = Tag.most_used(current_account).where.not(id: @featured_tags.map(&:id)).limit(10)
|
||||
def set_recently_used_tags
|
||||
@recently_used_tags = Tag.recently_used(current_account).where.not(id: @featured_tags.map(&:id)).limit(10)
|
||||
end
|
||||
|
||||
def featured_tag_params
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Settings::IdentityProofsController < Settings::BaseController
|
||||
layout 'admin'
|
||||
|
||||
before_action :authenticate_user!
|
||||
before_action :check_required_params, only: :new
|
||||
|
||||
def index
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Settings::ImportsController < Settings::BaseController
|
||||
layout 'admin'
|
||||
|
||||
before_action :authenticate_user!
|
||||
before_action :set_account
|
||||
|
||||
def show
|
||||
|
|
|
@ -1,13 +1,10 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Settings::Migration::RedirectsController < Settings::BaseController
|
||||
layout 'admin'
|
||||
|
||||
before_action :authenticate_user!
|
||||
before_action :require_not_suspended!
|
||||
|
||||
skip_before_action :require_functional!
|
||||
|
||||
before_action :require_not_suspended!
|
||||
|
||||
def new
|
||||
@redirect = Form::Redirect.new
|
||||
end
|
||||
|
@ -38,8 +35,4 @@ class Settings::Migration::RedirectsController < Settings::BaseController
|
|||
def resource_params
|
||||
params.require(:form_redirect).permit(:acct, :current_password, :current_username)
|
||||
end
|
||||
|
||||
def require_not_suspended!
|
||||
forbidden if current_account.suspended?
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,15 +1,12 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Settings::MigrationsController < Settings::BaseController
|
||||
layout 'admin'
|
||||
skip_before_action :require_functional!
|
||||
|
||||
before_action :authenticate_user!
|
||||
before_action :require_not_suspended!
|
||||
before_action :set_migrations
|
||||
before_action :set_cooldown
|
||||
|
||||
skip_before_action :require_functional!
|
||||
|
||||
def show
|
||||
@migration = current_account.migrations.build
|
||||
end
|
||||
|
@ -44,8 +41,4 @@ class Settings::MigrationsController < Settings::BaseController
|
|||
def on_cooldown?
|
||||
@cooldown.present?
|
||||
end
|
||||
|
||||
def require_not_suspended!
|
||||
forbidden if current_account.suspended?
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,14 +2,17 @@
|
|||
|
||||
module Settings
|
||||
class PicturesController < BaseController
|
||||
before_action :authenticate_user!
|
||||
before_action :set_account
|
||||
before_action :set_picture
|
||||
|
||||
def destroy
|
||||
if valid_picture?
|
||||
msg = I18n.t('generic.changes_saved_msg') if UpdateAccountService.new.call(@account, { @picture => nil, "#{@picture}_remote_url" => '' })
|
||||
redirect_to settings_profile_path, notice: msg, status: 303
|
||||
if UpdateAccountService.new.call(@account, { @picture => nil, "#{@picture}_remote_url" => '' })
|
||||
ActivityPub::UpdateDistributionWorker.perform_async(@account.id)
|
||||
redirect_to settings_profile_path, notice: I18n.t('generic.changes_saved_msg'), status: 303
|
||||
else
|
||||
redirect_to settings_profile_path
|
||||
end
|
||||
else
|
||||
bad_request
|
||||
end
|
||||
|
|
|
@ -1,10 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Settings::PreferencesController < Settings::BaseController
|
||||
layout 'admin'
|
||||
|
||||
before_action :authenticate_user!
|
||||
|
||||
def show; end
|
||||
|
||||
def update
|
||||
|
@ -47,6 +43,7 @@ class Settings::PreferencesController < Settings::BaseController
|
|||
:setting_display_media,
|
||||
:setting_expand_spoilers,
|
||||
:setting_reduce_motion,
|
||||
:setting_disable_swiping,
|
||||
:setting_system_font_ui,
|
||||
:setting_noindex,
|
||||
:setting_theme,
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Settings::ProfilesController < Settings::BaseController
|
||||
layout 'admin'
|
||||
|
||||
before_action :authenticate_user!
|
||||
before_action :set_account
|
||||
|
||||
def show
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue