Compare commits
20 commits
kb_develop
...
kb17.1
Author | SHA1 | Date | |
---|---|---|---|
|
b7c7b2afb2 | ||
|
3cba3a6af3 | ||
|
ac26f5f48a | ||
|
a4c43dcf18 | ||
|
d5ba371a5e | ||
|
d1208a2cf5 | ||
|
8f25192072 | ||
|
55e31110e3 | ||
|
e09adc6314 | ||
|
6515244a16 | ||
|
9ed1cb3c29 | ||
|
7afe02b36b | ||
|
4da06664a6 | ||
|
689d431dc6 | ||
|
4fa550d881 | ||
|
d4f0b01207 | ||
|
5fb4ae8edf | ||
|
e97f8d1b59 | ||
|
25d18d0bc8 | ||
|
fdca24ba56 |
|
@ -21,13 +21,12 @@ services:
|
||||||
ES_HOST: es
|
ES_HOST: es
|
||||||
ES_PORT: '9200'
|
ES_PORT: '9200'
|
||||||
LIBRE_TRANSLATE_ENDPOINT: http://libretranslate:5000
|
LIBRE_TRANSLATE_ENDPOINT: http://libretranslate:5000
|
||||||
LOCAL_DOMAIN: ${LOCAL_DOMAIN:-localhost:3000}
|
|
||||||
# Overrides default command so things don't shut down after the process ends.
|
# Overrides default command so things don't shut down after the process ends.
|
||||||
command: sleep infinity
|
command: sleep infinity
|
||||||
ports:
|
ports:
|
||||||
- '3000:3000'
|
- '127.0.0.1:3000:3000'
|
||||||
- '3035:3035'
|
- '127.0.0.1:3035:3035'
|
||||||
- '4000:4000'
|
- '127.0.0.1:4000:4000'
|
||||||
networks:
|
networks:
|
||||||
- external_network
|
- external_network
|
||||||
- internal_network
|
- internal_network
|
||||||
|
|
|
@ -20,9 +20,3 @@ postgres14
|
||||||
redis
|
redis
|
||||||
elasticsearch
|
elasticsearch
|
||||||
chart
|
chart
|
||||||
.yarn/
|
|
||||||
!.yarn/patches
|
|
||||||
!.yarn/plugins
|
|
||||||
!.yarn/releases
|
|
||||||
!.yarn/sdks
|
|
||||||
!.yarn/versions
|
|
||||||
|
|
|
@ -79,9 +79,6 @@ AWS_ACCESS_KEY_ID=
|
||||||
AWS_SECRET_ACCESS_KEY=
|
AWS_SECRET_ACCESS_KEY=
|
||||||
S3_ALIAS_HOST=files.example.com
|
S3_ALIAS_HOST=files.example.com
|
||||||
|
|
||||||
# Optional list of hosts that are allowed to serve media for your instance
|
|
||||||
# EXTRA_MEDIA_HOSTS=https://data.example1.com,https://data.example2.com
|
|
||||||
|
|
||||||
# IP and session retention
|
# IP and session retention
|
||||||
# -----------------------
|
# -----------------------
|
||||||
# Make sure to modify the scheduling of ip_cleanup_scheduler in config/sidekiq.yml
|
# Make sure to modify the scheduling of ip_cleanup_scheduler in config/sidekiq.yml
|
||||||
|
@ -89,27 +86,3 @@ S3_ALIAS_HOST=files.example.com
|
||||||
# -----------------------
|
# -----------------------
|
||||||
IP_RETENTION_PERIOD=31556952
|
IP_RETENTION_PERIOD=31556952
|
||||||
SESSION_RETENTION_PERIOD=31556952
|
SESSION_RETENTION_PERIOD=31556952
|
||||||
|
|
||||||
# Fetch All Replies Behavior
|
|
||||||
# --------------------------
|
|
||||||
# When a user expands a post (DetailedStatus view), fetch all of its replies
|
|
||||||
# (default: false)
|
|
||||||
FETCH_REPLIES_ENABLED=false
|
|
||||||
|
|
||||||
# Period to wait between fetching replies (in minutes)
|
|
||||||
FETCH_REPLIES_COOLDOWN_MINUTES=15
|
|
||||||
|
|
||||||
# Period to wait after a post is first created before fetching its replies (in minutes)
|
|
||||||
FETCH_REPLIES_INITIAL_WAIT_MINUTES=5
|
|
||||||
|
|
||||||
# Max number of replies to fetch - total, recursively through a whole reply tree
|
|
||||||
FETCH_REPLIES_MAX_GLOBAL=1000
|
|
||||||
|
|
||||||
# Max number of replies to fetch - for a single post
|
|
||||||
FETCH_REPLIES_MAX_SINGLE=500
|
|
||||||
|
|
||||||
# Max number of replies Collection pages to fetch - total
|
|
||||||
FETCH_REPLIES_MAX_PAGES=500
|
|
||||||
|
|
||||||
# Maximum allowed character count
|
|
||||||
MAX_CHARS=5555
|
|
||||||
|
|
13
.eslintignore
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
/build/**
|
||||||
|
/coverage/**
|
||||||
|
/db/**
|
||||||
|
/lib/**
|
||||||
|
/log/**
|
||||||
|
/node_modules/**
|
||||||
|
/nonobox/**
|
||||||
|
/public/**
|
||||||
|
!/public/embed.js
|
||||||
|
/spec/**
|
||||||
|
/tmp/**
|
||||||
|
/vendor/**
|
||||||
|
!.eslintrc.js
|
367
.eslintrc.js
Normal file
|
@ -0,0 +1,367 @@
|
||||||
|
// @ts-check
|
||||||
|
const { defineConfig } = require('eslint-define-config');
|
||||||
|
|
||||||
|
module.exports = defineConfig({
|
||||||
|
root: true,
|
||||||
|
|
||||||
|
extends: [
|
||||||
|
'eslint:recommended',
|
||||||
|
'plugin:react/recommended',
|
||||||
|
'plugin:react-hooks/recommended',
|
||||||
|
'plugin:jsx-a11y/recommended',
|
||||||
|
'plugin:import/recommended',
|
||||||
|
'plugin:promise/recommended',
|
||||||
|
'plugin:jsdoc/recommended',
|
||||||
|
],
|
||||||
|
|
||||||
|
env: {
|
||||||
|
browser: true,
|
||||||
|
node: true,
|
||||||
|
es6: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
parser: '@typescript-eslint/parser',
|
||||||
|
|
||||||
|
plugins: [
|
||||||
|
'react',
|
||||||
|
'jsx-a11y',
|
||||||
|
'import',
|
||||||
|
'promise',
|
||||||
|
'@typescript-eslint',
|
||||||
|
'formatjs',
|
||||||
|
],
|
||||||
|
|
||||||
|
parserOptions: {
|
||||||
|
sourceType: 'module',
|
||||||
|
ecmaFeatures: {
|
||||||
|
jsx: true,
|
||||||
|
},
|
||||||
|
ecmaVersion: 2021,
|
||||||
|
requireConfigFile: false,
|
||||||
|
babelOptions: {
|
||||||
|
configFile: false,
|
||||||
|
presets: ['@babel/react', '@babel/env'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
settings: {
|
||||||
|
react: {
|
||||||
|
version: 'detect',
|
||||||
|
},
|
||||||
|
'import/ignore': [
|
||||||
|
'node_modules',
|
||||||
|
'\\.(css|scss|json)$',
|
||||||
|
],
|
||||||
|
'import/resolver': {
|
||||||
|
typescript: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
rules: {
|
||||||
|
'consistent-return': 'error',
|
||||||
|
'dot-notation': 'error',
|
||||||
|
eqeqeq: ['error', 'always', { 'null': 'ignore' }],
|
||||||
|
'indent': ['error', 2],
|
||||||
|
'jsx-quotes': ['error', 'prefer-single'],
|
||||||
|
'semi': ['error', 'always'],
|
||||||
|
'no-catch-shadow': 'error',
|
||||||
|
'no-console': [
|
||||||
|
'warn',
|
||||||
|
{
|
||||||
|
allow: [
|
||||||
|
'error',
|
||||||
|
'warn',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'no-empty': ['error', { "allowEmptyCatch": true }],
|
||||||
|
'no-restricted-properties': [
|
||||||
|
'error',
|
||||||
|
{ property: 'substring', message: 'Use .slice instead of .substring.' },
|
||||||
|
{ property: 'substr', message: 'Use .slice instead of .substr.' },
|
||||||
|
],
|
||||||
|
'no-restricted-syntax': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
// eslint-disable-next-line no-restricted-syntax
|
||||||
|
selector: 'Literal[value=/•/], JSXText[value=/•/]',
|
||||||
|
// eslint-disable-next-line no-restricted-syntax
|
||||||
|
message: "Use '·' (middle dot) instead of '•' (bullet)",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'no-unused-expressions': 'error',
|
||||||
|
'no-unused-vars': 'off',
|
||||||
|
'@typescript-eslint/no-unused-vars': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
vars: 'all',
|
||||||
|
args: 'after-used',
|
||||||
|
destructuredArrayIgnorePattern: '^_',
|
||||||
|
ignoreRestSiblings: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'valid-typeof': 'error',
|
||||||
|
|
||||||
|
'react/jsx-filename-extension': ['error', { extensions: ['.jsx', 'tsx'] }],
|
||||||
|
'react/jsx-boolean-value': 'error',
|
||||||
|
'react/display-name': 'off',
|
||||||
|
'react/jsx-fragments': ['error', 'syntax'],
|
||||||
|
'react/jsx-equals-spacing': 'error',
|
||||||
|
'react/jsx-no-bind': 'error',
|
||||||
|
'react/jsx-no-useless-fragment': 'error',
|
||||||
|
'react/jsx-no-target-blank': ['error', { allowReferrer: true }],
|
||||||
|
'react/jsx-tag-spacing': 'error',
|
||||||
|
'react/jsx-uses-react': 'off', // not needed with new JSX transform
|
||||||
|
'react/jsx-wrap-multilines': 'error',
|
||||||
|
'react/react-in-jsx-scope': 'off', // not needed with new JSX transform
|
||||||
|
'react/self-closing-comp': 'error',
|
||||||
|
|
||||||
|
// recommended values found in https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/v6.8.0/src/index.js#L46
|
||||||
|
'jsx-a11y/click-events-have-key-events': 'off',
|
||||||
|
'jsx-a11y/label-has-associated-control': 'off',
|
||||||
|
'jsx-a11y/media-has-caption': 'off',
|
||||||
|
'jsx-a11y/no-autofocus': 'off',
|
||||||
|
// recommended rule is:
|
||||||
|
// 'jsx-a11y/no-interactive-element-to-noninteractive-role': [
|
||||||
|
// 'error',
|
||||||
|
// {
|
||||||
|
// tr: ['none', 'presentation'],
|
||||||
|
// canvas: ['img'],
|
||||||
|
// },
|
||||||
|
// ],
|
||||||
|
'jsx-a11y/no-interactive-element-to-noninteractive-role': 'off',
|
||||||
|
// recommended rule is:
|
||||||
|
// 'jsx-a11y/no-noninteractive-tabindex': [
|
||||||
|
// 'error',
|
||||||
|
// {
|
||||||
|
// tags: [],
|
||||||
|
// roles: ['tabpanel'],
|
||||||
|
// allowExpressionValues: true,
|
||||||
|
// },
|
||||||
|
// ],
|
||||||
|
'jsx-a11y/no-noninteractive-tabindex': 'off',
|
||||||
|
// recommended is full 'error'
|
||||||
|
'jsx-a11y/no-static-element-interactions': [
|
||||||
|
'warn',
|
||||||
|
{
|
||||||
|
handlers: [
|
||||||
|
'onClick',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
// See https://github.com/import-js/eslint-plugin-import/blob/v2.29.1/config/recommended.js
|
||||||
|
'import/extensions': [
|
||||||
|
'error',
|
||||||
|
'always',
|
||||||
|
{
|
||||||
|
js: 'never',
|
||||||
|
jsx: 'never',
|
||||||
|
mjs: 'never',
|
||||||
|
ts: 'never',
|
||||||
|
tsx: 'never',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'import/first': 'error',
|
||||||
|
'import/newline-after-import': 'error',
|
||||||
|
'import/no-anonymous-default-export': 'error',
|
||||||
|
'import/no-extraneous-dependencies': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
devDependencies: [
|
||||||
|
'.eslintrc.js',
|
||||||
|
'config/webpack/**',
|
||||||
|
'app/javascript/mastodon/performance.js',
|
||||||
|
'app/javascript/mastodon/test_setup.js',
|
||||||
|
'app/javascript/**/__tests__/**',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'import/no-amd': 'error',
|
||||||
|
'import/no-commonjs': 'error',
|
||||||
|
'import/no-import-module-exports': 'error',
|
||||||
|
'import/no-relative-packages': 'error',
|
||||||
|
'import/no-self-import': 'error',
|
||||||
|
'import/no-useless-path-segments': 'error',
|
||||||
|
'import/no-webpack-loader-syntax': 'error',
|
||||||
|
|
||||||
|
'import/order': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
alphabetize: { order: 'asc' },
|
||||||
|
'newlines-between': 'always',
|
||||||
|
groups: [
|
||||||
|
'builtin',
|
||||||
|
'external',
|
||||||
|
'internal',
|
||||||
|
'parent',
|
||||||
|
['index', 'sibling'],
|
||||||
|
'object',
|
||||||
|
],
|
||||||
|
pathGroups: [
|
||||||
|
// React core packages
|
||||||
|
{
|
||||||
|
pattern: '{react,react-dom,react-dom/client,prop-types}',
|
||||||
|
group: 'builtin',
|
||||||
|
position: 'after',
|
||||||
|
},
|
||||||
|
// I18n
|
||||||
|
{
|
||||||
|
pattern: '{react-intl,intl-messageformat}',
|
||||||
|
group: 'builtin',
|
||||||
|
position: 'after',
|
||||||
|
},
|
||||||
|
// Common React utilities
|
||||||
|
{
|
||||||
|
pattern: '{classnames,react-helmet,react-router,react-router-dom}',
|
||||||
|
group: 'external',
|
||||||
|
position: 'before',
|
||||||
|
},
|
||||||
|
// Immutable / Redux / data store
|
||||||
|
{
|
||||||
|
pattern: '{immutable,@reduxjs/toolkit,react-redux,react-immutable-proptypes,react-immutable-pure-component}',
|
||||||
|
group: 'external',
|
||||||
|
position: 'before',
|
||||||
|
},
|
||||||
|
// Internal packages
|
||||||
|
{
|
||||||
|
pattern: '{mastodon/**}',
|
||||||
|
group: 'internal',
|
||||||
|
position: 'after',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pathGroupsExcludedImportTypes: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
'promise/always-return': 'off',
|
||||||
|
'promise/catch-or-return': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
allowFinally: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'promise/no-callback-in-promise': 'off',
|
||||||
|
'promise/no-nesting': 'off',
|
||||||
|
'promise/no-promise-in-callback': 'off',
|
||||||
|
|
||||||
|
'formatjs/blocklist-elements': 'error',
|
||||||
|
'formatjs/enforce-default-message': ['error', 'literal'],
|
||||||
|
'formatjs/enforce-description': 'off', // description values not currently used
|
||||||
|
'formatjs/enforce-id': 'off', // Explicit IDs are used in the project
|
||||||
|
'formatjs/enforce-placeholders': 'off', // Issues in short_number.jsx
|
||||||
|
'formatjs/enforce-plural-rules': 'error',
|
||||||
|
'formatjs/no-camel-case': 'off', // disabledAccount is only non-conforming
|
||||||
|
'formatjs/no-complex-selectors': 'error',
|
||||||
|
'formatjs/no-emoji': 'error',
|
||||||
|
'formatjs/no-id': 'off', // IDs are used for translation keys
|
||||||
|
'formatjs/no-invalid-icu': 'error',
|
||||||
|
'formatjs/no-literal-string-in-jsx': 'off', // Should be looked at, but mainly flagging punctuation outside of strings
|
||||||
|
'formatjs/no-multiple-whitespaces': 'error',
|
||||||
|
'formatjs/no-offset': 'error',
|
||||||
|
'formatjs/no-useless-message': 'error',
|
||||||
|
'formatjs/prefer-formatted-message': 'error',
|
||||||
|
'formatjs/prefer-pound-in-plural': 'error',
|
||||||
|
|
||||||
|
'jsdoc/check-types': 'off',
|
||||||
|
'jsdoc/no-undefined-types': 'off',
|
||||||
|
'jsdoc/require-jsdoc': 'off',
|
||||||
|
'jsdoc/require-param-description': 'off',
|
||||||
|
'jsdoc/require-property-description': 'off',
|
||||||
|
'jsdoc/require-returns-description': 'off',
|
||||||
|
'jsdoc/require-returns': 'off',
|
||||||
|
},
|
||||||
|
|
||||||
|
overrides: [
|
||||||
|
{
|
||||||
|
files: [
|
||||||
|
'.eslintrc.js',
|
||||||
|
'*.config.js',
|
||||||
|
'.*rc.js',
|
||||||
|
'ide-helper.js',
|
||||||
|
'config/webpack/**/*',
|
||||||
|
'config/formatjs-formatter.js',
|
||||||
|
],
|
||||||
|
|
||||||
|
env: {
|
||||||
|
commonjs: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
parserOptions: {
|
||||||
|
sourceType: 'script',
|
||||||
|
},
|
||||||
|
|
||||||
|
rules: {
|
||||||
|
'import/no-commonjs': 'off',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: [
|
||||||
|
'**/*.ts',
|
||||||
|
'**/*.tsx',
|
||||||
|
],
|
||||||
|
|
||||||
|
extends: [
|
||||||
|
'eslint:recommended',
|
||||||
|
'plugin:@typescript-eslint/strict-type-checked',
|
||||||
|
'plugin:@typescript-eslint/stylistic-type-checked',
|
||||||
|
'plugin:react/recommended',
|
||||||
|
'plugin:react-hooks/recommended',
|
||||||
|
'plugin:jsx-a11y/recommended',
|
||||||
|
'plugin:import/recommended',
|
||||||
|
'plugin:import/typescript',
|
||||||
|
'plugin:promise/recommended',
|
||||||
|
'plugin:jsdoc/recommended-typescript',
|
||||||
|
],
|
||||||
|
|
||||||
|
parserOptions: {
|
||||||
|
projectService: true,
|
||||||
|
tsconfigRootDir: __dirname,
|
||||||
|
},
|
||||||
|
|
||||||
|
rules: {
|
||||||
|
// Disable formatting rules that have been enabled in the base config
|
||||||
|
'indent': 'off',
|
||||||
|
|
||||||
|
// This is not needed as we use noImplicitReturns, which handles this in addition to understanding types
|
||||||
|
'consistent-return': 'off',
|
||||||
|
|
||||||
|
'import/consistent-type-specifier-style': ['error', 'prefer-top-level'],
|
||||||
|
|
||||||
|
'@typescript-eslint/consistent-type-definitions': ['warn', 'interface'],
|
||||||
|
'@typescript-eslint/consistent-type-exports': 'error',
|
||||||
|
'@typescript-eslint/consistent-type-imports': 'error',
|
||||||
|
"@typescript-eslint/prefer-nullish-coalescing": ['error', { ignorePrimitives: { boolean: true } }],
|
||||||
|
"@typescript-eslint/no-restricted-imports": [
|
||||||
|
"warn",
|
||||||
|
{
|
||||||
|
"name": "react-redux",
|
||||||
|
"importNames": ["useSelector", "useDispatch"],
|
||||||
|
"message": "Use typed hooks `useAppDispatch` and `useAppSelector` instead."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"@typescript-eslint/restrict-template-expressions": ['warn', { allowNumber: true }],
|
||||||
|
'jsdoc/require-jsdoc': 'off',
|
||||||
|
|
||||||
|
// Those rules set stricter rules for TS files
|
||||||
|
// to enforce better practices when converting from JS
|
||||||
|
'import/no-default-export': 'warn',
|
||||||
|
'react/prefer-stateless-function': 'warn',
|
||||||
|
'react/function-component-definition': ['error', { namedComponents: 'arrow-function' }],
|
||||||
|
'react/jsx-uses-react': 'off', // not needed with new JSX transform
|
||||||
|
'react/react-in-jsx-scope': 'off', // not needed with new JSX transform
|
||||||
|
'react/prop-types': 'off',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
files: [
|
||||||
|
'**/__tests__/*.js',
|
||||||
|
'**/__tests__/*.jsx',
|
||||||
|
],
|
||||||
|
|
||||||
|
env: {
|
||||||
|
jest: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
],
|
||||||
|
});
|
10
.github/renovate.json5
vendored
|
@ -15,8 +15,6 @@
|
||||||
// to `null` after any other rule set it to something.
|
// to `null` after any other rule set it to something.
|
||||||
dependencyDashboardHeader: 'This issue lists Renovate updates and detected dependencies. Read the [Dependency Dashboard](https://docs.renovatebot.com/key-concepts/dashboard/) docs to learn more. Before approving any upgrade: read the description and comments in the [`renovate.json5` file](https://github.com/mastodon/mastodon/blob/main/.github/renovate.json5).',
|
dependencyDashboardHeader: 'This issue lists Renovate updates and detected dependencies. Read the [Dependency Dashboard](https://docs.renovatebot.com/key-concepts/dashboard/) docs to learn more. Before approving any upgrade: read the description and comments in the [`renovate.json5` file](https://github.com/mastodon/mastodon/blob/main/.github/renovate.json5).',
|
||||||
postUpdateOptions: ['yarnDedupeHighest'],
|
postUpdateOptions: ['yarnDedupeHighest'],
|
||||||
// The types are now included in recent versions,we ignore them here until we upgrade and remove the dependency
|
|
||||||
ignoreDeps: ['@types/emoji-mart'],
|
|
||||||
packageRules: [
|
packageRules: [
|
||||||
{
|
{
|
||||||
// Require Dependency Dashboard Approval for major version bumps of these node packages
|
// Require Dependency Dashboard Approval for major version bumps of these node packages
|
||||||
|
@ -99,13 +97,7 @@
|
||||||
{
|
{
|
||||||
// Group all eslint-related packages with `eslint` in the same PR
|
// Group all eslint-related packages with `eslint` in the same PR
|
||||||
matchManagers: ['npm'],
|
matchManagers: ['npm'],
|
||||||
matchPackageNames: [
|
matchPackageNames: ['eslint', 'eslint-*', '@typescript-eslint/*'],
|
||||||
'eslint',
|
|
||||||
'eslint-*',
|
|
||||||
'typescript-eslint',
|
|
||||||
'@eslint/*',
|
|
||||||
'globals',
|
|
||||||
],
|
|
||||||
matchUpdateTypes: ['patch', 'minor'],
|
matchUpdateTypes: ['patch', 'minor'],
|
||||||
groupName: 'eslint (non-major)',
|
groupName: 'eslint (non-major)',
|
||||||
},
|
},
|
||||||
|
|
4
.github/workflows/build-security.yml
vendored
|
@ -24,6 +24,8 @@ jobs:
|
||||||
uses: ./.github/workflows/build-container-image.yml
|
uses: ./.github/workflows/build-container-image.yml
|
||||||
with:
|
with:
|
||||||
file_to_build: Dockerfile
|
file_to_build: Dockerfile
|
||||||
|
platforms: linux/amd64,linux/arm64
|
||||||
|
use_native_arm64_builder: true
|
||||||
cache: false
|
cache: false
|
||||||
push_to_images: |
|
push_to_images: |
|
||||||
tootsuite/mastodon
|
tootsuite/mastodon
|
||||||
|
@ -44,6 +46,8 @@ jobs:
|
||||||
uses: ./.github/workflows/build-container-image.yml
|
uses: ./.github/workflows/build-container-image.yml
|
||||||
with:
|
with:
|
||||||
file_to_build: streaming/Dockerfile
|
file_to_build: streaming/Dockerfile
|
||||||
|
platforms: linux/amd64,linux/arm64
|
||||||
|
use_native_arm64_builder: true
|
||||||
cache: false
|
cache: false
|
||||||
push_to_images: |
|
push_to_images: |
|
||||||
tootsuite/mastodon-streaming
|
tootsuite/mastodon-streaming
|
||||||
|
|
2
.github/workflows/lint-haml.yml
vendored
|
@ -46,4 +46,4 @@ jobs:
|
||||||
- name: Run haml-lint
|
- name: Run haml-lint
|
||||||
run: |
|
run: |
|
||||||
echo "::add-matcher::.github/workflows/haml-lint-problem-matcher.json"
|
echo "::add-matcher::.github/workflows/haml-lint-problem-matcher.json"
|
||||||
bin/haml-lint --reporter github
|
bin/haml-lint --parallel --reporter github
|
||||||
|
|
6
.github/workflows/lint-js.yml
vendored
|
@ -14,7 +14,7 @@ on:
|
||||||
- 'tsconfig.json'
|
- 'tsconfig.json'
|
||||||
- '.nvmrc'
|
- '.nvmrc'
|
||||||
- '.prettier*'
|
- '.prettier*'
|
||||||
- 'eslint.config.mjs'
|
- '.eslint*'
|
||||||
- '**/*.js'
|
- '**/*.js'
|
||||||
- '**/*.jsx'
|
- '**/*.jsx'
|
||||||
- '**/*.ts'
|
- '**/*.ts'
|
||||||
|
@ -28,7 +28,7 @@ on:
|
||||||
- 'tsconfig.json'
|
- 'tsconfig.json'
|
||||||
- '.nvmrc'
|
- '.nvmrc'
|
||||||
- '.prettier*'
|
- '.prettier*'
|
||||||
- 'eslint.config.mjs'
|
- '.eslint*'
|
||||||
- '**/*.js'
|
- '**/*.js'
|
||||||
- '**/*.jsx'
|
- '**/*.jsx'
|
||||||
- '**/*.ts'
|
- '**/*.ts'
|
||||||
|
@ -47,7 +47,7 @@ jobs:
|
||||||
uses: ./.github/actions/setup-javascript
|
uses: ./.github/actions/setup-javascript
|
||||||
|
|
||||||
- name: ESLint
|
- name: ESLint
|
||||||
run: yarn workspaces foreach --all --parallel run lint:js --max-warnings 0
|
run: yarn lint:js --max-warnings 0
|
||||||
|
|
||||||
- name: Typecheck
|
- name: Typecheck
|
||||||
run: yarn typecheck
|
run: yarn typecheck
|
||||||
|
|
13
.github/workflows/test-migrations.yml
vendored
|
@ -67,6 +67,7 @@ jobs:
|
||||||
DB_HOST: localhost
|
DB_HOST: localhost
|
||||||
DB_USER: postgres
|
DB_USER: postgres
|
||||||
DB_PASS: postgres
|
DB_PASS: postgres
|
||||||
|
DISABLE_SIMPLECOV: true
|
||||||
RAILS_ENV: test
|
RAILS_ENV: test
|
||||||
BUNDLE_CLEAN: true
|
BUNDLE_CLEAN: true
|
||||||
BUNDLE_FROZEN: true
|
BUNDLE_FROZEN: true
|
||||||
|
@ -80,18 +81,6 @@ jobs:
|
||||||
- name: Set up Ruby environment
|
- name: Set up Ruby environment
|
||||||
uses: ./.github/actions/setup-ruby
|
uses: ./.github/actions/setup-ruby
|
||||||
|
|
||||||
- name: Ensure no errors with `db:prepare`
|
|
||||||
run: |
|
|
||||||
bin/rails db:drop
|
|
||||||
bin/rails db:prepare
|
|
||||||
bin/rails db:migrate
|
|
||||||
|
|
||||||
- name: Ensure no errors with `db:prepare` and SKIP_POST_DEPLOYMENT_MIGRATIONS
|
|
||||||
run: |
|
|
||||||
bin/rails db:drop
|
|
||||||
SKIP_POST_DEPLOYMENT_MIGRATIONS=true bin/rails db:prepare
|
|
||||||
bin/rails db:migrate
|
|
||||||
|
|
||||||
- name: Test "one step migration" flow
|
- name: Test "one step migration" flow
|
||||||
run: |
|
run: |
|
||||||
bin/rails db:drop
|
bin/rails db:drop
|
||||||
|
|
6
.github/workflows/test-ruby.yml
vendored
|
@ -110,7 +110,7 @@ jobs:
|
||||||
DB_HOST: localhost
|
DB_HOST: localhost
|
||||||
DB_USER: postgres
|
DB_USER: postgres
|
||||||
DB_PASS: postgres
|
DB_PASS: postgres
|
||||||
COVERAGE: ${{ matrix.ruby-version == '.ruby-version' }}
|
DISABLE_SIMPLECOV: ${{ matrix.ruby-version != '.ruby-version' }}
|
||||||
RAILS_ENV: test
|
RAILS_ENV: test
|
||||||
ALLOW_NOPAM: true
|
ALLOW_NOPAM: true
|
||||||
PAM_ENABLED: true
|
PAM_ENABLED: true
|
||||||
|
@ -212,7 +212,7 @@ jobs:
|
||||||
DB_HOST: localhost
|
DB_HOST: localhost
|
||||||
DB_USER: postgres
|
DB_USER: postgres
|
||||||
DB_PASS: postgres
|
DB_PASS: postgres
|
||||||
COVERAGE: ${{ matrix.ruby-version == '.ruby-version' }}
|
DISABLE_SIMPLECOV: ${{ matrix.ruby-version != '.ruby-version' }}
|
||||||
RAILS_ENV: test
|
RAILS_ENV: test
|
||||||
ALLOW_NOPAM: true
|
ALLOW_NOPAM: true
|
||||||
PAM_ENABLED: true
|
PAM_ENABLED: true
|
||||||
|
@ -299,6 +299,7 @@ jobs:
|
||||||
DB_HOST: localhost
|
DB_HOST: localhost
|
||||||
DB_USER: postgres
|
DB_USER: postgres
|
||||||
DB_PASS: postgres
|
DB_PASS: postgres
|
||||||
|
DISABLE_SIMPLECOV: true
|
||||||
RAILS_ENV: test
|
RAILS_ENV: test
|
||||||
BUNDLE_WITH: test
|
BUNDLE_WITH: test
|
||||||
ES_ENABLED: false
|
ES_ENABLED: false
|
||||||
|
@ -415,6 +416,7 @@ jobs:
|
||||||
DB_HOST: localhost
|
DB_HOST: localhost
|
||||||
DB_USER: postgres
|
DB_USER: postgres
|
||||||
DB_PASS: postgres
|
DB_PASS: postgres
|
||||||
|
DISABLE_SIMPLECOV: true
|
||||||
RAILS_ENV: test
|
RAILS_ENV: test
|
||||||
BUNDLE_WITH: test
|
BUNDLE_WITH: test
|
||||||
ES_ENABLED: true
|
ES_ENABLED: true
|
||||||
|
|
2
.nvmrc
|
@ -1 +1 @@
|
||||||
22.14
|
22.13
|
||||||
|
|
|
@ -63,7 +63,6 @@ docker-compose.override.yml
|
||||||
|
|
||||||
# Ignore emoji map file
|
# Ignore emoji map file
|
||||||
/app/javascript/mastodon/features/emoji/emoji_map.json
|
/app/javascript/mastodon/features/emoji/emoji_map.json
|
||||||
/app/javascript/mastodon/features/emoji/emoji_sheet.json
|
|
||||||
|
|
||||||
# Ignore locale files
|
# Ignore locale files
|
||||||
/app/javascript/mastodon/locales/*.json
|
/app/javascript/mastodon/locales/*.json
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
singleQuote: true,
|
singleQuote: true,
|
||||||
jsxSingleQuote: true
|
jsxSingleQuote: true
|
||||||
};
|
}
|
||||||
|
|
|
@ -18,7 +18,6 @@ inherit_from:
|
||||||
- .rubocop/rspec_rails.yml
|
- .rubocop/rspec_rails.yml
|
||||||
- .rubocop/rspec.yml
|
- .rubocop/rspec.yml
|
||||||
- .rubocop/style.yml
|
- .rubocop/style.yml
|
||||||
- .rubocop/i18n.yml
|
|
||||||
- .rubocop/custom.yml
|
- .rubocop/custom.yml
|
||||||
- .rubocop_todo.yml
|
- .rubocop_todo.yml
|
||||||
- .rubocop/strict.yml
|
- .rubocop/strict.yml
|
||||||
|
@ -27,10 +26,10 @@ inherit_mode:
|
||||||
merge:
|
merge:
|
||||||
- Exclude
|
- Exclude
|
||||||
|
|
||||||
plugins:
|
require:
|
||||||
- rubocop-capybara
|
|
||||||
- rubocop-i18n
|
|
||||||
- rubocop-performance
|
|
||||||
- rubocop-rails
|
- rubocop-rails
|
||||||
- rubocop-rspec
|
- rubocop-rspec
|
||||||
- rubocop-rspec_rails
|
- rubocop-rspec_rails
|
||||||
|
- rubocop-performance
|
||||||
|
- rubocop-capybara
|
||||||
|
- ./lib/linter/rubocop_middle_dot
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
I18n/RailsI18n:
|
|
||||||
Enabled: true
|
|
||||||
Exclude:
|
|
||||||
- 'config/**/*'
|
|
||||||
- 'db/**/*'
|
|
||||||
- 'lib/**/*'
|
|
||||||
- 'spec/**/*'
|
|
||||||
I18n/GetText:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
I18n/RailsI18n/DecorateStringFormattingUsingInterpolation:
|
|
||||||
Enabled: false
|
|
|
@ -2,9 +2,6 @@
|
||||||
Rails/BulkChangeTable:
|
Rails/BulkChangeTable:
|
||||||
Enabled: false # Conflicts with strong_migrations features
|
Enabled: false # Conflicts with strong_migrations features
|
||||||
|
|
||||||
Rails/Delegate:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
Rails/FilePath:
|
Rails/FilePath:
|
||||||
EnforcedStyle: arguments
|
EnforcedStyle: arguments
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# This configuration was generated by
|
# This configuration was generated by
|
||||||
# `rubocop --auto-gen-config --auto-gen-only-exclude --no-offense-counts --no-auto-gen-timestamp`
|
# `rubocop --auto-gen-config --auto-gen-only-exclude --no-offense-counts --no-auto-gen-timestamp`
|
||||||
# using RuboCop version 1.75.2.
|
# using RuboCop version 1.70.0.
|
||||||
# The point is for the user to remove these configuration records
|
# The point is for the user to remove these configuration records
|
||||||
# one by one as the offenses are removed from the code base.
|
# one by one as the offenses are removed from the code base.
|
||||||
# Note that changes in the inspected code, or installation of new
|
# Note that changes in the inspected code, or installation of new
|
||||||
|
@ -49,7 +49,7 @@ Style/FetchEnvVar:
|
||||||
- 'lib/tasks/repo.rake'
|
- 'lib/tasks/repo.rake'
|
||||||
|
|
||||||
# This cop supports safe autocorrection (--autocorrect).
|
# This cop supports safe autocorrection (--autocorrect).
|
||||||
# Configuration parameters: EnforcedStyle, MaxUnannotatedPlaceholdersAllowed, Mode, AllowedMethods, AllowedPatterns.
|
# Configuration parameters: EnforcedStyle, MaxUnannotatedPlaceholdersAllowed, AllowedMethods, AllowedPatterns.
|
||||||
# SupportedStyles: annotated, template, unannotated
|
# SupportedStyles: annotated, template, unannotated
|
||||||
# AllowedMethods: redirect
|
# AllowedMethods: redirect
|
||||||
Style/FormatStringToken:
|
Style/FormatStringToken:
|
||||||
|
@ -62,10 +62,22 @@ Style/FormatStringToken:
|
||||||
Style/GuardClause:
|
Style/GuardClause:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
|
||||||
|
# This cop supports unsafe autocorrection (--autocorrect-all).
|
||||||
|
Style/HashTransformValues:
|
||||||
|
Exclude:
|
||||||
|
- 'app/serializers/rest/web_push_subscription_serializer.rb'
|
||||||
|
- 'app/services/import_service.rb'
|
||||||
|
|
||||||
|
# This cop supports unsafe autocorrection (--autocorrect-all).
|
||||||
|
Style/MapToHash:
|
||||||
|
Exclude:
|
||||||
|
- 'app/models/status.rb'
|
||||||
|
|
||||||
# Configuration parameters: AllowedMethods.
|
# Configuration parameters: AllowedMethods.
|
||||||
# AllowedMethods: respond_to_missing?
|
# AllowedMethods: respond_to_missing?
|
||||||
Style/OptionalBooleanParameter:
|
Style/OptionalBooleanParameter:
|
||||||
Exclude:
|
Exclude:
|
||||||
|
- 'app/helpers/json_ld_helper.rb'
|
||||||
- 'app/lib/admin/system_check/message.rb'
|
- 'app/lib/admin/system_check/message.rb'
|
||||||
- 'app/lib/request.rb'
|
- 'app/lib/request.rb'
|
||||||
- 'app/lib/webfinger.rb'
|
- 'app/lib/webfinger.rb'
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
3.4.3
|
3.4.1
|
||||||
|
|
82
CHANGELOG.md
|
@ -2,88 +2,6 @@
|
||||||
|
|
||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
## [4.3.7] - 2025-04-02
|
|
||||||
|
|
||||||
### Add
|
|
||||||
|
|
||||||
- Add delay to profile updates to debounce them (#34137 by @ClearlyClaire)
|
|
||||||
- Add support for paginating partial collections in `SynchronizeFollowersService` (#34272 and #34277 by @ClearlyClaire)
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Change account suspensions to be federated to recently-followed accounts as well (#34294 by @ClearlyClaire)
|
|
||||||
- Change `AccountReachFinder` to consider statuses based on suspension date (#32805 and #34291 by @ClearlyClaire and @mjankowski)
|
|
||||||
- Change user archive signed URL TTL from 10 seconds to 1 hour (#34254 by @ClearlyClaire)
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Fix static version of animated PNG emojis not being properly extracted (#34337 by @ClearlyClaire)
|
|
||||||
- Fix filters not applying in detailed view, favourites and bookmarks (#34259 and #34260 by @ClearlyClaire)
|
|
||||||
- Fix handling of malformed/unusual HTML (#34201 by @ClearlyClaire)
|
|
||||||
- Fix `CacheBuster` being queued for missing media attachments (#34253 by @ClearlyClaire)
|
|
||||||
- Fix incorrect URL being used when cache busting (#34189 by @ClearlyClaire)
|
|
||||||
- Fix streaming server refusing unix socket path in `DATABASE_URL` (#34091 by @ClearlyClaire)
|
|
||||||
- Fix “x” hotkey not working on boosted filtered posts (#33758 by @ClearlyClaire)
|
|
||||||
|
|
||||||
## [4.3.6] - 2025-03-13
|
|
||||||
|
|
||||||
### Security
|
|
||||||
|
|
||||||
- Update dependency `omniauth-saml`
|
|
||||||
- Update dependency `rack`
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Fix Stoplight errors when using `REDIS_NAMESPACE` (#34126 by @ClearlyClaire)
|
|
||||||
|
|
||||||
## [4.3.5] - 2025-03-10
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Change hashtag suggestion to prefer personal history capitalization (#34070 by @ClearlyClaire)
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Fix processing errors for some HEIF images from iOS 18 (#34086 by @renchap)
|
|
||||||
- Fix streaming server not filtering unknown-language posts from public timelines (#33774 by @ClearlyClaire)
|
|
||||||
- Fix preview cards under Content Warnings not being shown in detailed statuses (#34068 by @ClearlyClaire)
|
|
||||||
- Fix username and display name being hidden on narrow screens in moderation interface (#33064 by @ClearlyClaire)
|
|
||||||
|
|
||||||
## [4.3.4] - 2025-02-27
|
|
||||||
|
|
||||||
### Security
|
|
||||||
|
|
||||||
- Update dependencies
|
|
||||||
- Change HTML sanitization to remove unusable and unused `embed` tag (#34021 by @ClearlyClaire, [GHSA-mq2m-hr29-8gqf](https://github.com/mastodon/mastodon/security/advisories/GHSA-mq2m-hr29-8gqf))
|
|
||||||
- Fix rate-limit on sign-up email verification ([GHSA-v39f-c9jj-8w7h](https://github.com/mastodon/mastodon/security/advisories/GHSA-v39f-c9jj-8w7h))
|
|
||||||
- Fix improper disclosure of domain blocks to unverified users ([GHSA-94h4-fj37-c825](https://github.com/mastodon/mastodon/security/advisories/GHSA-94h4-fj37-c825))
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Change preview cards to be shown when Content Warnings are expanded (#33827 by @ClearlyClaire)
|
|
||||||
- Change warnings against changing encryption secrets to be even more noticeable (#33631 by @ClearlyClaire)
|
|
||||||
- Change `mastodon:setup` to prevent overwriting already-configured servers (#33603, #33616, and #33684 by @ClearlyClaire and @mjankowski)
|
|
||||||
- Change notifications from moderators to not be filtered (#32974 and #33654 by @ClearlyClaire and @mjankowski)
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Fix `GET /api/v2/notifications/:id` and `POST /api/v2/notifications/:id/dismiss` for ungrouped notifications (#33990 by @ClearlyClaire)
|
|
||||||
- Fix issue with some versions of libvips on some systems (#33853 by @kleisauke)
|
|
||||||
- Fix handling of duplicate mentions in incoming status `Update` (#33911 by @ClearlyClaire)
|
|
||||||
- Fix inefficiencies in timeline generation (#33839 and #33842 by @ClearlyClaire)
|
|
||||||
- Fix emoji rewrite adding unnecessary curft to the DOM for most emoji (#33818 by @ClearlyClaire)
|
|
||||||
- Fix `tootctl feeds build` not building list timelines (#33783 by @ClearlyClaire)
|
|
||||||
- Fix flaky test in `/api/v2/notifications` tests (#33773 by @ClearlyClaire)
|
|
||||||
- Fix incorrect signature after HTTP redirect (#33757 and #33769 by @ClearlyClaire)
|
|
||||||
- Fix polls not being validated on edition (#33755 by @ClearlyClaire)
|
|
||||||
- Fix media preview height in compose form when 3 or more images are attached (#33571 by @ClearlyClaire)
|
|
||||||
- Fix preview card sizing in “Author attribution” in profile settings (#33482 by @ClearlyClaire)
|
|
||||||
- Fix processing of incoming notifications for unfilterable types (#33429 by @ClearlyClaire)
|
|
||||||
- Fix featured tags for remote accounts not being kept up to date (#33372, #33406, and #33425 by @ClearlyClaire and @mjankowski)
|
|
||||||
- Fix notification polling showing a loading bar in web UI (#32960 by @Gargron)
|
|
||||||
- Fix accounts table long display name (#29316 by @WebCoder49)
|
|
||||||
- Fix exclusive lists interfering with notifications (#28162 by @ShadowJonathan)
|
|
||||||
|
|
||||||
## [4.3.3] - 2025-01-16
|
## [4.3.3] - 2025-01-16
|
||||||
|
|
||||||
### Security
|
### Security
|
||||||
|
|
69
Dockerfile
|
@ -13,13 +13,13 @@ ARG BASE_REGISTRY="docker.io"
|
||||||
|
|
||||||
# Ruby image to use for base image, change with [--build-arg RUBY_VERSION="3.4.x"]
|
# Ruby image to use for base image, change with [--build-arg RUBY_VERSION="3.4.x"]
|
||||||
# renovate: datasource=docker depName=docker.io/ruby
|
# renovate: datasource=docker depName=docker.io/ruby
|
||||||
ARG RUBY_VERSION="3.4.2"
|
ARG RUBY_VERSION="3.4.1"
|
||||||
# # Node.js version to use in base image, change with [--build-arg NODE_MAJOR_VERSION="20"]
|
# # Node version to use in base image, change with [--build-arg NODE_MAJOR_VERSION="20"]
|
||||||
# renovate: datasource=node-version depName=node
|
# renovate: datasource=node-version depName=node
|
||||||
ARG NODE_MAJOR_VERSION="22"
|
ARG NODE_MAJOR_VERSION="22"
|
||||||
# Debian image to use for base image, change with [--build-arg DEBIAN_VERSION="bookworm"]
|
# Debian image to use for base image, change with [--build-arg DEBIAN_VERSION="bookworm"]
|
||||||
ARG DEBIAN_VERSION="bookworm"
|
ARG DEBIAN_VERSION="bookworm"
|
||||||
# Node.js image to use for base image based on combined variables (ex: 20-bookworm-slim)
|
# Node image to use for base image based on combined variables (ex: 20-bookworm-slim)
|
||||||
FROM ${BASE_REGISTRY}/node:${NODE_MAJOR_VERSION}-${DEBIAN_VERSION}-slim AS node
|
FROM ${BASE_REGISTRY}/node:${NODE_MAJOR_VERSION}-${DEBIAN_VERSION}-slim AS node
|
||||||
# Ruby image to use for base image based on combined variables (ex: 3.4.x-slim-bookworm)
|
# Ruby image to use for base image based on combined variables (ex: 3.4.x-slim-bookworm)
|
||||||
FROM ${BASE_REGISTRY}/ruby:${RUBY_VERSION}-slim-${DEBIAN_VERSION} AS ruby
|
FROM ${BASE_REGISTRY}/ruby:${RUBY_VERSION}-slim-${DEBIAN_VERSION} AS ruby
|
||||||
|
@ -61,7 +61,7 @@ ENV \
|
||||||
ENV \
|
ENV \
|
||||||
# Configure the IP to bind Mastodon to when serving traffic
|
# Configure the IP to bind Mastodon to when serving traffic
|
||||||
BIND="0.0.0.0" \
|
BIND="0.0.0.0" \
|
||||||
# Use production settings for Yarn, Node.js and related tools
|
# Use production settings for Yarn, Node and related nodejs based tools
|
||||||
NODE_ENV="production" \
|
NODE_ENV="production" \
|
||||||
# Use production settings for Ruby on Rails
|
# Use production settings for Ruby on Rails
|
||||||
RAILS_ENV="production" \
|
RAILS_ENV="production" \
|
||||||
|
@ -96,9 +96,6 @@ RUN \
|
||||||
# Set /opt/mastodon as working directory
|
# Set /opt/mastodon as working directory
|
||||||
WORKDIR /opt/mastodon
|
WORKDIR /opt/mastodon
|
||||||
|
|
||||||
# Add backport repository for some specific packages where we need the latest version
|
|
||||||
RUN echo 'deb http://deb.debian.org/debian bookworm-backports main' >> /etc/apt/sources.list
|
|
||||||
|
|
||||||
# hadolint ignore=DL3008,DL3005
|
# hadolint ignore=DL3008,DL3005
|
||||||
RUN \
|
RUN \
|
||||||
# Mount Apt cache and lib directories from Docker buildx caches
|
# Mount Apt cache and lib directories from Docker buildx caches
|
||||||
|
@ -128,6 +125,13 @@ RUN \
|
||||||
# Create temporary build layer from base image
|
# Create temporary build layer from base image
|
||||||
FROM ruby AS build
|
FROM ruby AS build
|
||||||
|
|
||||||
|
# Copy Node package configuration files into working directory
|
||||||
|
COPY package.json yarn.lock .yarnrc.yml /opt/mastodon/
|
||||||
|
COPY .yarn /opt/mastodon/.yarn
|
||||||
|
|
||||||
|
COPY --from=node /usr/local/bin /usr/local/bin
|
||||||
|
COPY --from=node /usr/local/lib /usr/local/lib
|
||||||
|
|
||||||
ARG TARGETPLATFORM
|
ARG TARGETPLATFORM
|
||||||
|
|
||||||
# hadolint ignore=DL3008
|
# hadolint ignore=DL3008
|
||||||
|
@ -161,7 +165,7 @@ RUN \
|
||||||
libexif-dev \
|
libexif-dev \
|
||||||
libexpat1-dev \
|
libexpat1-dev \
|
||||||
libgirepository1.0-dev \
|
libgirepository1.0-dev \
|
||||||
libheif-dev/bookworm-backports \
|
libheif-dev \
|
||||||
libimagequant-dev \
|
libimagequant-dev \
|
||||||
libjpeg62-turbo-dev \
|
libjpeg62-turbo-dev \
|
||||||
liblcms2-dev \
|
liblcms2-dev \
|
||||||
|
@ -181,12 +185,18 @@ RUN \
|
||||||
libx265-dev \
|
libx265-dev \
|
||||||
;
|
;
|
||||||
|
|
||||||
|
RUN \
|
||||||
|
# Configure Corepack
|
||||||
|
rm /usr/local/bin/yarn*; \
|
||||||
|
corepack enable; \
|
||||||
|
corepack prepare --activate;
|
||||||
|
|
||||||
# Create temporary libvips specific build layer from build layer
|
# Create temporary libvips specific build layer from build layer
|
||||||
FROM build AS libvips
|
FROM build AS libvips
|
||||||
|
|
||||||
# libvips version to compile, change with [--build-arg VIPS_VERSION="8.15.2"]
|
# libvips version to compile, change with [--build-arg VIPS_VERSION="8.15.2"]
|
||||||
# renovate: datasource=github-releases depName=libvips packageName=libvips/libvips
|
# renovate: datasource=github-releases depName=libvips packageName=libvips/libvips
|
||||||
ARG VIPS_VERSION=8.16.1
|
ARG VIPS_VERSION=8.16.0
|
||||||
# libvips download URL, change with [--build-arg VIPS_URL="https://github.com/libvips/libvips/releases/download"]
|
# libvips download URL, change with [--build-arg VIPS_URL="https://github.com/libvips/libvips/releases/download"]
|
||||||
ARG VIPS_URL=https://github.com/libvips/libvips/releases/download
|
ARG VIPS_URL=https://github.com/libvips/libvips/releases/download
|
||||||
|
|
||||||
|
@ -271,37 +281,38 @@ RUN \
|
||||||
# Download and install required Gems
|
# Download and install required Gems
|
||||||
bundle install -j"$(nproc)";
|
bundle install -j"$(nproc)";
|
||||||
|
|
||||||
# Create temporary assets build layer from build layer
|
# Create temporary node specific build layer from build layer
|
||||||
FROM build AS precompiler
|
FROM build AS yarn
|
||||||
|
|
||||||
ARG TARGETPLATFORM
|
ARG TARGETPLATFORM
|
||||||
|
|
||||||
# Copy Mastodon sources into layer
|
# Copy Node package configuration files into working directory
|
||||||
COPY . /opt/mastodon/
|
COPY package.json yarn.lock .yarnrc.yml /opt/mastodon/
|
||||||
|
COPY streaming/package.json /opt/mastodon/streaming/
|
||||||
# Copy Node.js binaries/libraries into layer
|
COPY .yarn /opt/mastodon/.yarn
|
||||||
COPY --from=node /usr/local/bin /usr/local/bin
|
|
||||||
COPY --from=node /usr/local/lib /usr/local/lib
|
|
||||||
|
|
||||||
RUN \
|
|
||||||
# Configure Corepack
|
|
||||||
rm /usr/local/bin/yarn*; \
|
|
||||||
corepack enable; \
|
|
||||||
corepack prepare --activate;
|
|
||||||
|
|
||||||
# hadolint ignore=DL3008
|
# hadolint ignore=DL3008
|
||||||
RUN \
|
RUN \
|
||||||
--mount=type=cache,id=corepack-cache-${TARGETPLATFORM},target=/usr/local/share/.cache/corepack,sharing=locked \
|
--mount=type=cache,id=corepack-cache-${TARGETPLATFORM},target=/usr/local/share/.cache/corepack,sharing=locked \
|
||||||
--mount=type=cache,id=yarn-cache-${TARGETPLATFORM},target=/usr/local/share/.cache/yarn,sharing=locked \
|
--mount=type=cache,id=yarn-cache-${TARGETPLATFORM},target=/usr/local/share/.cache/yarn,sharing=locked \
|
||||||
# Install Node.js packages
|
# Install Node packages
|
||||||
yarn workspaces focus --production @mastodon/mastodon;
|
yarn workspaces focus --production @mastodon/mastodon;
|
||||||
|
|
||||||
# Copy libvips components into layer for precompiler
|
# Create temporary assets build layer from build layer
|
||||||
COPY --from=libvips /usr/local/libvips/bin /usr/local/bin
|
FROM build AS precompiler
|
||||||
COPY --from=libvips /usr/local/libvips/lib /usr/local/lib
|
|
||||||
# Copy bundler packages into layer for precompiler
|
# Copy Mastodon sources into precompiler layer
|
||||||
|
COPY . /opt/mastodon/
|
||||||
|
|
||||||
|
# Copy bundler and node packages from build layer to container
|
||||||
|
COPY --from=yarn /opt/mastodon /opt/mastodon/
|
||||||
COPY --from=bundler /opt/mastodon /opt/mastodon/
|
COPY --from=bundler /opt/mastodon /opt/mastodon/
|
||||||
COPY --from=bundler /usr/local/bundle/ /usr/local/bundle/
|
COPY --from=bundler /usr/local/bundle/ /usr/local/bundle/
|
||||||
|
# Copy libvips components to layer for precompiler
|
||||||
|
COPY --from=libvips /usr/local/libvips/bin /usr/local/bin
|
||||||
|
COPY --from=libvips /usr/local/libvips/lib /usr/local/lib
|
||||||
|
|
||||||
|
ARG TARGETPLATFORM
|
||||||
|
|
||||||
RUN \
|
RUN \
|
||||||
ldconfig; \
|
ldconfig; \
|
||||||
|
@ -337,7 +348,7 @@ RUN \
|
||||||
# libvips components
|
# libvips components
|
||||||
libcgif0 \
|
libcgif0 \
|
||||||
libexif12 \
|
libexif12 \
|
||||||
libheif1/bookworm-backports \
|
libheif1 \
|
||||||
libimagequant0 \
|
libimagequant0 \
|
||||||
libjpeg62-turbo \
|
libjpeg62-turbo \
|
||||||
liblcms2-2 \
|
liblcms2-2 \
|
||||||
|
|
22
Gemfile
|
@ -14,7 +14,6 @@ gem 'haml-rails', '~>2.0'
|
||||||
gem 'pg', '~> 1.5'
|
gem 'pg', '~> 1.5'
|
||||||
gem 'pghero'
|
gem 'pghero'
|
||||||
|
|
||||||
gem 'aws-sdk-core', '< 3.216.0', require: false # TODO: https://github.com/mastodon/mastodon/pull/34173#issuecomment-2733378873
|
|
||||||
gem 'aws-sdk-s3', '~> 1.123', require: false
|
gem 'aws-sdk-s3', '~> 1.123', require: false
|
||||||
gem 'blurhash', '~> 0.1'
|
gem 'blurhash', '~> 0.1'
|
||||||
gem 'fog-core', '<= 2.6.0'
|
gem 'fog-core', '<= 2.6.0'
|
||||||
|
@ -40,7 +39,7 @@ gem 'net-ldap', '~> 0.18'
|
||||||
|
|
||||||
gem 'omniauth', '~> 2.0'
|
gem 'omniauth', '~> 2.0'
|
||||||
gem 'omniauth-cas', '~> 3.0.0.beta.1'
|
gem 'omniauth-cas', '~> 3.0.0.beta.1'
|
||||||
gem 'omniauth_openid_connect', '~> 0.8.0'
|
gem 'omniauth_openid_connect', '~> 0.6.1'
|
||||||
gem 'omniauth-rails_csrf_protection', '~> 1.0'
|
gem 'omniauth-rails_csrf_protection', '~> 1.0'
|
||||||
gem 'omniauth-saml', '~> 2.0'
|
gem 'omniauth-saml', '~> 2.0'
|
||||||
|
|
||||||
|
@ -62,7 +61,6 @@ gem 'inline_svg'
|
||||||
gem 'irb', '~> 1.8'
|
gem 'irb', '~> 1.8'
|
||||||
gem 'kaminari', '~> 1.2'
|
gem 'kaminari', '~> 1.2'
|
||||||
gem 'link_header', '~> 0.0'
|
gem 'link_header', '~> 0.0'
|
||||||
gem 'linzer', '~> 0.6.1'
|
|
||||||
gem 'mario-redis-lock', '~> 1.2', require: 'redis_lock'
|
gem 'mario-redis-lock', '~> 1.2', require: 'redis_lock'
|
||||||
gem 'mime-types', '~> 3.6.0', require: 'mime/types/columnar'
|
gem 'mime-types', '~> 3.6.0', require: 'mime/types/columnar'
|
||||||
gem 'mutex_m'
|
gem 'mutex_m'
|
||||||
|
@ -104,10 +102,10 @@ gem 'rdf-normalize', '~> 0.5'
|
||||||
|
|
||||||
gem 'prometheus_exporter', '~> 2.2', require: false
|
gem 'prometheus_exporter', '~> 2.2', require: false
|
||||||
|
|
||||||
gem 'opentelemetry-api', '~> 1.5.0'
|
gem 'opentelemetry-api', '~> 1.4.0'
|
||||||
|
|
||||||
group :opentelemetry do
|
group :opentelemetry do
|
||||||
gem 'opentelemetry-exporter-otlp', '~> 0.30.0', require: false
|
gem 'opentelemetry-exporter-otlp', '~> 0.29.0', require: false
|
||||||
gem 'opentelemetry-instrumentation-active_job', '~> 0.8.0', require: false
|
gem 'opentelemetry-instrumentation-active_job', '~> 0.8.0', require: false
|
||||||
gem 'opentelemetry-instrumentation-active_model_serializers', '~> 0.22.0', require: false
|
gem 'opentelemetry-instrumentation-active_model_serializers', '~> 0.22.0', require: false
|
||||||
gem 'opentelemetry-instrumentation-concurrent_ruby', '~> 0.22.0', require: false
|
gem 'opentelemetry-instrumentation-concurrent_ruby', '~> 0.22.0', require: false
|
||||||
|
@ -118,7 +116,7 @@ group :opentelemetry do
|
||||||
gem 'opentelemetry-instrumentation-net_http', '~> 0.23.0', require: false
|
gem 'opentelemetry-instrumentation-net_http', '~> 0.23.0', require: false
|
||||||
gem 'opentelemetry-instrumentation-pg', '~> 0.30.0', require: false
|
gem 'opentelemetry-instrumentation-pg', '~> 0.30.0', require: false
|
||||||
gem 'opentelemetry-instrumentation-rack', '~> 0.26.0', require: false
|
gem 'opentelemetry-instrumentation-rack', '~> 0.26.0', require: false
|
||||||
gem 'opentelemetry-instrumentation-rails', '~> 0.36.0', require: false
|
gem 'opentelemetry-instrumentation-rails', '~> 0.35.0', require: false
|
||||||
gem 'opentelemetry-instrumentation-redis', '~> 0.26.0', require: false
|
gem 'opentelemetry-instrumentation-redis', '~> 0.26.0', require: false
|
||||||
gem 'opentelemetry-instrumentation-sidekiq', '~> 0.26.0', require: false
|
gem 'opentelemetry-instrumentation-sidekiq', '~> 0.26.0', require: false
|
||||||
gem 'opentelemetry-sdk', '~> 1.4', require: false
|
gem 'opentelemetry-sdk', '~> 1.4', require: false
|
||||||
|
@ -147,6 +145,9 @@ group :test do
|
||||||
# Used to mock environment variables
|
# Used to mock environment variables
|
||||||
gem 'climate_control'
|
gem 'climate_control'
|
||||||
|
|
||||||
|
# Add back helpers functions removed in Rails 5.1
|
||||||
|
gem 'rails-controller-testing', '~> 1.0'
|
||||||
|
|
||||||
# Validate schemas in specs
|
# Validate schemas in specs
|
||||||
gem 'json-schema', '~> 5.0'
|
gem 'json-schema', '~> 5.0'
|
||||||
|
|
||||||
|
@ -155,7 +156,7 @@ group :test do
|
||||||
|
|
||||||
gem 'shoulda-matchers'
|
gem 'shoulda-matchers'
|
||||||
|
|
||||||
# Coverage formatter for RSpec
|
# Coverage formatter for RSpec test if DISABLE_SIMPLECOV is false
|
||||||
gem 'simplecov', '~> 0.22', require: false
|
gem 'simplecov', '~> 0.22', require: false
|
||||||
gem 'simplecov-lcov', '~> 0.8', require: false
|
gem 'simplecov-lcov', '~> 0.8', require: false
|
||||||
|
|
||||||
|
@ -167,14 +168,13 @@ group :development do
|
||||||
# Code linting CLI and plugins
|
# Code linting CLI and plugins
|
||||||
gem 'rubocop', require: false
|
gem 'rubocop', require: false
|
||||||
gem 'rubocop-capybara', require: false
|
gem 'rubocop-capybara', require: false
|
||||||
gem 'rubocop-i18n', require: false
|
|
||||||
gem 'rubocop-performance', require: false
|
gem 'rubocop-performance', require: false
|
||||||
gem 'rubocop-rails', require: false
|
gem 'rubocop-rails', require: false
|
||||||
gem 'rubocop-rspec', require: false
|
gem 'rubocop-rspec', require: false
|
||||||
gem 'rubocop-rspec_rails', require: false
|
gem 'rubocop-rspec_rails', require: false
|
||||||
|
|
||||||
# Annotates modules with schema
|
# Annotates modules with schema
|
||||||
gem 'annotaterb', '~> 4.13', require: false
|
gem 'annotaterb', '~> 4.13'
|
||||||
|
|
||||||
# Enhanced error message pages for development
|
# Enhanced error message pages for development
|
||||||
gem 'better_errors', '~> 2.9'
|
gem 'better_errors', '~> 2.9'
|
||||||
|
@ -197,7 +197,7 @@ end
|
||||||
|
|
||||||
group :development, :test do
|
group :development, :test do
|
||||||
# Interactive Debugging tools
|
# Interactive Debugging tools
|
||||||
gem 'debug', '~> 1.8', require: false
|
gem 'debug', '~> 1.8'
|
||||||
|
|
||||||
# Generate fake data values
|
# Generate fake data values
|
||||||
gem 'faker', '~> 3.2'
|
gem 'faker', '~> 3.2'
|
||||||
|
@ -209,7 +209,7 @@ group :development, :test do
|
||||||
gem 'memory_profiler', require: false
|
gem 'memory_profiler', require: false
|
||||||
gem 'ruby-prof', require: false
|
gem 'ruby-prof', require: false
|
||||||
gem 'stackprof', require: false
|
gem 'stackprof', require: false
|
||||||
gem 'test-prof', require: false
|
gem 'test-prof'
|
||||||
|
|
||||||
# RSpec runner for rails
|
# RSpec runner for rails
|
||||||
gem 'rspec-rails', '~> 7.0'
|
gem 'rspec-rails', '~> 7.0'
|
||||||
|
|
413
Gemfile.lock
|
@ -10,29 +10,29 @@ GIT
|
||||||
GEM
|
GEM
|
||||||
remote: https://rubygems.org/
|
remote: https://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
actioncable (8.0.2)
|
actioncable (8.0.1)
|
||||||
actionpack (= 8.0.2)
|
actionpack (= 8.0.1)
|
||||||
activesupport (= 8.0.2)
|
activesupport (= 8.0.1)
|
||||||
nio4r (~> 2.0)
|
nio4r (~> 2.0)
|
||||||
websocket-driver (>= 0.6.1)
|
websocket-driver (>= 0.6.1)
|
||||||
zeitwerk (~> 2.6)
|
zeitwerk (~> 2.6)
|
||||||
actionmailbox (8.0.2)
|
actionmailbox (8.0.1)
|
||||||
actionpack (= 8.0.2)
|
actionpack (= 8.0.1)
|
||||||
activejob (= 8.0.2)
|
activejob (= 8.0.1)
|
||||||
activerecord (= 8.0.2)
|
activerecord (= 8.0.1)
|
||||||
activestorage (= 8.0.2)
|
activestorage (= 8.0.1)
|
||||||
activesupport (= 8.0.2)
|
activesupport (= 8.0.1)
|
||||||
mail (>= 2.8.0)
|
mail (>= 2.8.0)
|
||||||
actionmailer (8.0.2)
|
actionmailer (8.0.1)
|
||||||
actionpack (= 8.0.2)
|
actionpack (= 8.0.1)
|
||||||
actionview (= 8.0.2)
|
actionview (= 8.0.1)
|
||||||
activejob (= 8.0.2)
|
activejob (= 8.0.1)
|
||||||
activesupport (= 8.0.2)
|
activesupport (= 8.0.1)
|
||||||
mail (>= 2.8.0)
|
mail (>= 2.8.0)
|
||||||
rails-dom-testing (~> 2.2)
|
rails-dom-testing (~> 2.2)
|
||||||
actionpack (8.0.2)
|
actionpack (8.0.1)
|
||||||
actionview (= 8.0.2)
|
actionview (= 8.0.1)
|
||||||
activesupport (= 8.0.2)
|
activesupport (= 8.0.1)
|
||||||
nokogiri (>= 1.8.5)
|
nokogiri (>= 1.8.5)
|
||||||
rack (>= 2.2.4)
|
rack (>= 2.2.4)
|
||||||
rack-session (>= 1.0.1)
|
rack-session (>= 1.0.1)
|
||||||
|
@ -40,15 +40,15 @@ GEM
|
||||||
rails-dom-testing (~> 2.2)
|
rails-dom-testing (~> 2.2)
|
||||||
rails-html-sanitizer (~> 1.6)
|
rails-html-sanitizer (~> 1.6)
|
||||||
useragent (~> 0.16)
|
useragent (~> 0.16)
|
||||||
actiontext (8.0.2)
|
actiontext (8.0.1)
|
||||||
actionpack (= 8.0.2)
|
actionpack (= 8.0.1)
|
||||||
activerecord (= 8.0.2)
|
activerecord (= 8.0.1)
|
||||||
activestorage (= 8.0.2)
|
activestorage (= 8.0.1)
|
||||||
activesupport (= 8.0.2)
|
activesupport (= 8.0.1)
|
||||||
globalid (>= 0.6.0)
|
globalid (>= 0.6.0)
|
||||||
nokogiri (>= 1.8.5)
|
nokogiri (>= 1.8.5)
|
||||||
actionview (8.0.2)
|
actionview (8.0.1)
|
||||||
activesupport (= 8.0.2)
|
activesupport (= 8.0.1)
|
||||||
builder (~> 3.1)
|
builder (~> 3.1)
|
||||||
erubi (~> 1.11)
|
erubi (~> 1.11)
|
||||||
rails-dom-testing (~> 2.2)
|
rails-dom-testing (~> 2.2)
|
||||||
|
@ -58,22 +58,22 @@ GEM
|
||||||
activemodel (>= 4.1)
|
activemodel (>= 4.1)
|
||||||
case_transform (>= 0.2)
|
case_transform (>= 0.2)
|
||||||
jsonapi-renderer (>= 0.1.1.beta1, < 0.3)
|
jsonapi-renderer (>= 0.1.1.beta1, < 0.3)
|
||||||
activejob (8.0.2)
|
activejob (8.0.1)
|
||||||
activesupport (= 8.0.2)
|
activesupport (= 8.0.1)
|
||||||
globalid (>= 0.3.6)
|
globalid (>= 0.3.6)
|
||||||
activemodel (8.0.2)
|
activemodel (8.0.1)
|
||||||
activesupport (= 8.0.2)
|
activesupport (= 8.0.1)
|
||||||
activerecord (8.0.2)
|
activerecord (8.0.1)
|
||||||
activemodel (= 8.0.2)
|
activemodel (= 8.0.1)
|
||||||
activesupport (= 8.0.2)
|
activesupport (= 8.0.1)
|
||||||
timeout (>= 0.4.0)
|
timeout (>= 0.4.0)
|
||||||
activestorage (8.0.2)
|
activestorage (8.0.1)
|
||||||
actionpack (= 8.0.2)
|
actionpack (= 8.0.1)
|
||||||
activejob (= 8.0.2)
|
activejob (= 8.0.1)
|
||||||
activerecord (= 8.0.2)
|
activerecord (= 8.0.1)
|
||||||
activesupport (= 8.0.2)
|
activesupport (= 8.0.1)
|
||||||
marcel (~> 1.0)
|
marcel (~> 1.0)
|
||||||
activesupport (8.0.2)
|
activesupport (8.0.1)
|
||||||
base64
|
base64
|
||||||
benchmark (>= 0.3)
|
benchmark (>= 0.3)
|
||||||
bigdecimal
|
bigdecimal
|
||||||
|
@ -90,12 +90,12 @@ GEM
|
||||||
public_suffix (>= 2.0.2, < 7.0)
|
public_suffix (>= 2.0.2, < 7.0)
|
||||||
aes_key_wrap (1.1.0)
|
aes_key_wrap (1.1.0)
|
||||||
android_key_attestation (0.3.0)
|
android_key_attestation (0.3.0)
|
||||||
annotaterb (4.14.0)
|
annotaterb (4.13.0)
|
||||||
ast (2.4.3)
|
ast (2.4.2)
|
||||||
attr_required (1.0.2)
|
attr_required (1.0.2)
|
||||||
aws-eventstream (1.3.2)
|
aws-eventstream (1.3.0)
|
||||||
aws-partitions (1.1087.0)
|
aws-partitions (1.1032.0)
|
||||||
aws-sdk-core (3.215.1)
|
aws-sdk-core (3.214.1)
|
||||||
aws-eventstream (~> 1, >= 1.3.0)
|
aws-eventstream (~> 1, >= 1.3.0)
|
||||||
aws-partitions (~> 1, >= 1.992.0)
|
aws-partitions (~> 1, >= 1.992.0)
|
||||||
aws-sigv4 (~> 1.9)
|
aws-sigv4 (~> 1.9)
|
||||||
|
@ -107,9 +107,9 @@ GEM
|
||||||
aws-sdk-core (~> 3, >= 3.210.0)
|
aws-sdk-core (~> 3, >= 3.210.0)
|
||||||
aws-sdk-kms (~> 1)
|
aws-sdk-kms (~> 1)
|
||||||
aws-sigv4 (~> 1.5)
|
aws-sigv4 (~> 1.5)
|
||||||
aws-sigv4 (1.11.0)
|
aws-sigv4 (1.10.1)
|
||||||
aws-eventstream (~> 1, >= 1.0.2)
|
aws-eventstream (~> 1, >= 1.0.2)
|
||||||
azure-blob (0.5.7)
|
azure-blob (0.5.4)
|
||||||
rexml
|
rexml
|
||||||
base64 (0.2.0)
|
base64 (0.2.0)
|
||||||
bcp47_spec (0.2.1)
|
bcp47_spec (0.2.1)
|
||||||
|
@ -120,13 +120,13 @@ GEM
|
||||||
rack (>= 0.9.0)
|
rack (>= 0.9.0)
|
||||||
rouge (>= 1.0.0)
|
rouge (>= 1.0.0)
|
||||||
bigdecimal (3.1.9)
|
bigdecimal (3.1.9)
|
||||||
bindata (2.5.1)
|
bindata (2.5.0)
|
||||||
binding_of_caller (1.0.1)
|
binding_of_caller (1.0.1)
|
||||||
debug_inspector (>= 1.2.0)
|
debug_inspector (>= 1.2.0)
|
||||||
blurhash (0.1.8)
|
blurhash (0.1.8)
|
||||||
bootsnap (1.18.4)
|
bootsnap (1.18.4)
|
||||||
msgpack (~> 1.2)
|
msgpack (~> 1.2)
|
||||||
brakeman (7.0.2)
|
brakeman (7.0.0)
|
||||||
racc
|
racc
|
||||||
browser (6.2.0)
|
browser (6.2.0)
|
||||||
brpoplpush-redis_script (0.1.3)
|
brpoplpush-redis_script (0.1.3)
|
||||||
|
@ -168,9 +168,9 @@ GEM
|
||||||
bigdecimal
|
bigdecimal
|
||||||
rexml
|
rexml
|
||||||
crass (1.0.6)
|
crass (1.0.6)
|
||||||
css_parser (1.21.1)
|
css_parser (1.21.0)
|
||||||
addressable
|
addressable
|
||||||
csv (3.3.4)
|
csv (3.3.2)
|
||||||
database_cleaner-active_record (2.2.0)
|
database_cleaner-active_record (2.2.0)
|
||||||
activerecord (>= 5.a)
|
activerecord (>= 5.a)
|
||||||
database_cleaner-core (~> 2.0.0)
|
database_cleaner-core (~> 2.0.0)
|
||||||
|
@ -194,14 +194,14 @@ GEM
|
||||||
devise_pam_authenticatable2 (9.2.0)
|
devise_pam_authenticatable2 (9.2.0)
|
||||||
devise (>= 4.0.0)
|
devise (>= 4.0.0)
|
||||||
rpam2 (~> 4.0)
|
rpam2 (~> 4.0)
|
||||||
diff-lcs (1.6.1)
|
diff-lcs (1.5.1)
|
||||||
discard (1.4.0)
|
discard (1.4.0)
|
||||||
activerecord (>= 4.2, < 9.0)
|
activerecord (>= 4.2, < 9.0)
|
||||||
docile (1.4.1)
|
docile (1.4.1)
|
||||||
domain_name (0.6.20240107)
|
domain_name (0.6.20240107)
|
||||||
doorkeeper (5.8.2)
|
doorkeeper (5.8.1)
|
||||||
railties (>= 5)
|
railties (>= 5)
|
||||||
dotenv (3.1.8)
|
dotenv (3.1.7)
|
||||||
drb (2.2.1)
|
drb (2.2.1)
|
||||||
elasticsearch (7.17.11)
|
elasticsearch (7.17.11)
|
||||||
elasticsearch-api (= 7.17.11)
|
elasticsearch-api (= 7.17.11)
|
||||||
|
@ -217,29 +217,24 @@ GEM
|
||||||
htmlentities (~> 4.3.3)
|
htmlentities (~> 4.3.3)
|
||||||
launchy (>= 2.1, < 4.0)
|
launchy (>= 2.1, < 4.0)
|
||||||
mail (~> 2.7)
|
mail (~> 2.7)
|
||||||
email_validator (2.2.4)
|
|
||||||
activemodel
|
|
||||||
erubi (1.13.1)
|
erubi (1.13.1)
|
||||||
et-orbi (1.2.11)
|
et-orbi (1.2.11)
|
||||||
tzinfo
|
tzinfo
|
||||||
excon (1.2.5)
|
excon (0.112.0)
|
||||||
logger
|
|
||||||
fabrication (2.31.0)
|
fabrication (2.31.0)
|
||||||
faker (3.5.1)
|
faker (3.5.1)
|
||||||
i18n (>= 1.8.11, < 2)
|
i18n (>= 1.8.11, < 2)
|
||||||
faraday (2.13.0)
|
faraday (2.12.2)
|
||||||
faraday-net_http (>= 2.0, < 3.5)
|
faraday-net_http (>= 2.0, < 3.5)
|
||||||
json
|
json
|
||||||
logger
|
logger
|
||||||
faraday-follow_redirects (0.3.0)
|
|
||||||
faraday (>= 1, < 3)
|
|
||||||
faraday-httpclient (2.0.1)
|
faraday-httpclient (2.0.1)
|
||||||
httpclient (>= 2.2)
|
httpclient (>= 2.2)
|
||||||
faraday-net_http (3.4.0)
|
faraday-net_http (3.4.0)
|
||||||
net-http (>= 0.5.0)
|
net-http (>= 0.5.0)
|
||||||
fast_blank (1.0.1)
|
fast_blank (1.0.1)
|
||||||
fastimage (2.4.0)
|
fastimage (2.4.0)
|
||||||
ffi (1.17.2)
|
ffi (1.17.1)
|
||||||
ffi-compiler (1.3.2)
|
ffi-compiler (1.3.2)
|
||||||
ffi (>= 1.15.5)
|
ffi (>= 1.15.5)
|
||||||
rake
|
rake
|
||||||
|
@ -249,15 +244,15 @@ GEM
|
||||||
flatware-rspec (2.3.4)
|
flatware-rspec (2.3.4)
|
||||||
flatware (= 2.3.4)
|
flatware (= 2.3.4)
|
||||||
rspec (>= 3.6)
|
rspec (>= 3.6)
|
||||||
fog-core (2.6.0)
|
fog-core (2.5.0)
|
||||||
builder
|
builder
|
||||||
excon (~> 1.0)
|
excon (~> 0.71)
|
||||||
formatador (>= 0.2, < 2.0)
|
formatador (>= 0.2, < 2.0)
|
||||||
mime-types
|
mime-types
|
||||||
fog-json (1.2.0)
|
fog-json (1.2.0)
|
||||||
fog-core
|
fog-core
|
||||||
multi_json (~> 1.10)
|
multi_json (~> 1.10)
|
||||||
fog-openstack (1.1.5)
|
fog-openstack (1.1.3)
|
||||||
fog-core (~> 2.1)
|
fog-core (~> 2.1)
|
||||||
fog-json (>= 1.0)
|
fog-json (>= 1.0)
|
||||||
formatador (1.1.0)
|
formatador (1.1.0)
|
||||||
|
@ -266,10 +261,8 @@ GEM
|
||||||
raabro (~> 1.4)
|
raabro (~> 1.4)
|
||||||
globalid (1.2.1)
|
globalid (1.2.1)
|
||||||
activesupport (>= 6.1)
|
activesupport (>= 6.1)
|
||||||
google-protobuf (4.30.2)
|
google-protobuf (3.25.5)
|
||||||
bigdecimal
|
googleapis-common-protos-types (1.15.0)
|
||||||
rake (>= 13)
|
|
||||||
googleapis-common-protos-types (1.19.0)
|
|
||||||
google-protobuf (>= 3.18, < 5.a)
|
google-protobuf (>= 3.18, < 5.a)
|
||||||
haml (6.3.0)
|
haml (6.3.0)
|
||||||
temple (>= 0.8.2)
|
temple (>= 0.8.2)
|
||||||
|
@ -280,7 +273,7 @@ GEM
|
||||||
activesupport (>= 5.1)
|
activesupport (>= 5.1)
|
||||||
haml (>= 4.0.6)
|
haml (>= 4.0.6)
|
||||||
railties (>= 5.1)
|
railties (>= 5.1)
|
||||||
haml_lint (0.62.0)
|
haml_lint (0.59.0)
|
||||||
haml (>= 5.0)
|
haml (>= 5.0)
|
||||||
parallel (~> 1.10)
|
parallel (~> 1.10)
|
||||||
rainbow
|
rainbow
|
||||||
|
@ -305,14 +298,13 @@ GEM
|
||||||
domain_name (~> 0.5)
|
domain_name (~> 0.5)
|
||||||
http-form_data (2.3.0)
|
http-form_data (2.3.0)
|
||||||
http_accept_language (2.1.1)
|
http_accept_language (2.1.1)
|
||||||
httpclient (2.9.0)
|
httpclient (2.8.3)
|
||||||
mutex_m
|
|
||||||
httplog (1.7.0)
|
httplog (1.7.0)
|
||||||
rack (>= 2.0)
|
rack (>= 2.0)
|
||||||
rainbow (>= 2.0.0)
|
rainbow (>= 2.0.0)
|
||||||
i18n (1.14.7)
|
i18n (1.14.7)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
i18n-tasks (1.0.15)
|
i18n-tasks (1.0.14)
|
||||||
activesupport (>= 4.0.2)
|
activesupport (>= 4.0.2)
|
||||||
ast (>= 2.1.0)
|
ast (>= 2.1.0)
|
||||||
erubi
|
erubi
|
||||||
|
@ -321,14 +313,13 @@ GEM
|
||||||
parser (>= 3.2.2.1)
|
parser (>= 3.2.2.1)
|
||||||
rails-i18n
|
rails-i18n
|
||||||
rainbow (>= 2.2.2, < 4.0)
|
rainbow (>= 2.2.2, < 4.0)
|
||||||
ruby-progressbar (~> 1.8, >= 1.8.1)
|
|
||||||
terminal-table (>= 1.5.1)
|
terminal-table (>= 1.5.1)
|
||||||
idn-ruby (0.1.5)
|
idn-ruby (0.1.5)
|
||||||
inline_svg (1.10.0)
|
inline_svg (1.10.0)
|
||||||
activesupport (>= 3.0)
|
activesupport (>= 3.0)
|
||||||
nokogiri (>= 1.6)
|
nokogiri (>= 1.6)
|
||||||
io-console (0.8.0)
|
io-console (0.8.0)
|
||||||
irb (1.15.2)
|
irb (1.15.1)
|
||||||
pp (>= 0.6.0)
|
pp (>= 0.6.0)
|
||||||
rdoc (>= 4.0.0)
|
rdoc (>= 4.0.0)
|
||||||
reline (>= 0.4.2)
|
reline (>= 0.4.2)
|
||||||
|
@ -337,15 +328,13 @@ GEM
|
||||||
azure-blob (~> 0.5.2)
|
azure-blob (~> 0.5.2)
|
||||||
hashie (~> 5.0)
|
hashie (~> 5.0)
|
||||||
jmespath (1.6.2)
|
jmespath (1.6.2)
|
||||||
json (2.10.2)
|
json (2.9.1)
|
||||||
json-canonicalization (1.0.0)
|
json-canonicalization (1.0.0)
|
||||||
json-jwt (1.16.7)
|
json-jwt (1.15.3.1)
|
||||||
activesupport (>= 4.2)
|
activesupport (>= 4.2)
|
||||||
aes_key_wrap
|
aes_key_wrap
|
||||||
base64
|
|
||||||
bindata
|
bindata
|
||||||
faraday (~> 2.0)
|
httpclient
|
||||||
faraday-follow_redirects
|
|
||||||
json-ld (3.3.2)
|
json-ld (3.3.2)
|
||||||
htmlentities (~> 4.3)
|
htmlentities (~> 4.3)
|
||||||
json-canonicalization (~> 1.0)
|
json-canonicalization (~> 1.0)
|
||||||
|
@ -361,7 +350,7 @@ GEM
|
||||||
addressable (~> 2.8)
|
addressable (~> 2.8)
|
||||||
bigdecimal (~> 3.1)
|
bigdecimal (~> 3.1)
|
||||||
jsonapi-renderer (0.2.2)
|
jsonapi-renderer (0.2.2)
|
||||||
jwt (2.10.1)
|
jwt (2.9.3)
|
||||||
base64
|
base64
|
||||||
kaminari (1.2.2)
|
kaminari (1.2.2)
|
||||||
activesupport (>= 4.1.0)
|
activesupport (>= 4.1.0)
|
||||||
|
@ -382,10 +371,9 @@ GEM
|
||||||
mime-types
|
mime-types
|
||||||
terrapin (>= 0.6.0, < 2.0)
|
terrapin (>= 0.6.0, < 2.0)
|
||||||
language_server-protocol (3.17.0.4)
|
language_server-protocol (3.17.0.4)
|
||||||
launchy (3.1.1)
|
launchy (3.0.1)
|
||||||
addressable (~> 2.8)
|
addressable (~> 2.8)
|
||||||
childprocess (~> 5.0)
|
childprocess (~> 5.0)
|
||||||
logger (~> 1.6)
|
|
||||||
letter_opener (1.10.0)
|
letter_opener (1.10.0)
|
||||||
launchy (>= 2.2, < 4)
|
launchy (>= 2.2, < 4)
|
||||||
letter_opener_web (3.0.0)
|
letter_opener_web (3.0.0)
|
||||||
|
@ -394,17 +382,10 @@ GEM
|
||||||
railties (>= 6.1)
|
railties (>= 6.1)
|
||||||
rexml
|
rexml
|
||||||
link_header (0.0.8)
|
link_header (0.0.8)
|
||||||
lint_roller (1.1.0)
|
llhttp-ffi (0.5.0)
|
||||||
linzer (0.6.5)
|
|
||||||
openssl (~> 3.0, >= 3.0.0)
|
|
||||||
rack (>= 2.2, < 4.0)
|
|
||||||
starry (~> 0.2)
|
|
||||||
stringio (~> 3.1, >= 3.1.2)
|
|
||||||
uri (~> 1.0, >= 1.0.2)
|
|
||||||
llhttp-ffi (0.5.1)
|
|
||||||
ffi-compiler (~> 1.0)
|
ffi-compiler (~> 1.0)
|
||||||
rake (~> 13.0)
|
rake (~> 13.0)
|
||||||
logger (1.7.0)
|
logger (1.6.5)
|
||||||
lograge (0.14.0)
|
lograge (0.14.0)
|
||||||
actionpack (>= 4)
|
actionpack (>= 4)
|
||||||
activesupport (>= 4)
|
activesupport (>= 4)
|
||||||
|
@ -423,14 +404,14 @@ GEM
|
||||||
redis (>= 3.0.5)
|
redis (>= 3.0.5)
|
||||||
matrix (0.4.2)
|
matrix (0.4.2)
|
||||||
memory_profiler (1.1.0)
|
memory_profiler (1.1.0)
|
||||||
mime-types (3.6.2)
|
mime-types (3.6.0)
|
||||||
logger
|
logger
|
||||||
mime-types-data (~> 3.2015)
|
mime-types-data (~> 3.2015)
|
||||||
mime-types-data (3.2025.0408)
|
mime-types-data (3.2025.0107)
|
||||||
mini_mime (1.1.5)
|
mini_mime (1.1.5)
|
||||||
mini_portile2 (2.8.8)
|
mini_portile2 (2.8.8)
|
||||||
minitest (5.25.5)
|
minitest (5.25.4)
|
||||||
msgpack (1.8.0)
|
msgpack (1.7.5)
|
||||||
multi_json (1.15.0)
|
multi_json (1.15.0)
|
||||||
mutex_m (0.3.0)
|
mutex_m (0.3.0)
|
||||||
net-http (0.6.0)
|
net-http (0.6.0)
|
||||||
|
@ -446,49 +427,47 @@ GEM
|
||||||
net-smtp (0.5.1)
|
net-smtp (0.5.1)
|
||||||
net-protocol
|
net-protocol
|
||||||
nio4r (2.7.4)
|
nio4r (2.7.4)
|
||||||
nokogiri (1.18.7)
|
nokogiri (1.18.3)
|
||||||
mini_portile2 (~> 2.8.2)
|
mini_portile2 (~> 2.8.2)
|
||||||
racc (~> 1.4)
|
racc (~> 1.4)
|
||||||
oj (3.16.10)
|
oj (3.16.9)
|
||||||
bigdecimal (>= 3.0)
|
bigdecimal (>= 3.0)
|
||||||
ostruct (>= 0.2)
|
ostruct (>= 0.2)
|
||||||
omniauth (2.1.3)
|
omniauth (2.1.2)
|
||||||
hashie (>= 3.4.6)
|
hashie (>= 3.4.6)
|
||||||
rack (>= 2.2.3)
|
rack (>= 2.2.3)
|
||||||
rack-protection
|
rack-protection
|
||||||
omniauth-cas (3.0.1)
|
omniauth-cas (3.0.0)
|
||||||
addressable (~> 2.8)
|
addressable (~> 2.8)
|
||||||
nokogiri (~> 1.12)
|
nokogiri (~> 1.12)
|
||||||
omniauth (~> 2.1)
|
omniauth (~> 2.1)
|
||||||
omniauth-rails_csrf_protection (1.0.2)
|
omniauth-rails_csrf_protection (1.0.2)
|
||||||
actionpack (>= 4.2)
|
actionpack (>= 4.2)
|
||||||
omniauth (~> 2.0)
|
omniauth (~> 2.0)
|
||||||
omniauth-saml (2.2.3)
|
omniauth-saml (2.2.1)
|
||||||
omniauth (~> 2.1)
|
omniauth (~> 2.1)
|
||||||
ruby-saml (~> 1.18)
|
ruby-saml (~> 1.17)
|
||||||
omniauth_openid_connect (0.8.0)
|
omniauth_openid_connect (0.6.1)
|
||||||
omniauth (>= 1.9, < 3)
|
omniauth (>= 1.9, < 3)
|
||||||
openid_connect (~> 2.2)
|
openid_connect (~> 1.1)
|
||||||
openid_connect (2.3.1)
|
openid_connect (1.4.2)
|
||||||
activemodel
|
activemodel
|
||||||
attr_required (>= 1.0.0)
|
attr_required (>= 1.0.0)
|
||||||
email_validator
|
json-jwt (>= 1.15.0)
|
||||||
faraday (~> 2.0)
|
net-smtp
|
||||||
faraday-follow_redirects
|
rack-oauth2 (~> 1.21)
|
||||||
json-jwt (>= 1.16)
|
swd (~> 1.3)
|
||||||
mail
|
|
||||||
rack-oauth2 (~> 2.2)
|
|
||||||
swd (~> 2.0)
|
|
||||||
tzinfo
|
tzinfo
|
||||||
|
validate_email
|
||||||
validate_url
|
validate_url
|
||||||
webfinger (~> 2.0)
|
webfinger (~> 1.2)
|
||||||
openssl (3.3.0)
|
openssl (3.2.1)
|
||||||
openssl-signature_algorithm (1.3.0)
|
openssl-signature_algorithm (1.3.0)
|
||||||
openssl (> 2.0)
|
openssl (> 2.0)
|
||||||
opentelemetry-api (1.5.0)
|
opentelemetry-api (1.4.0)
|
||||||
opentelemetry-common (0.22.0)
|
opentelemetry-common (0.21.0)
|
||||||
opentelemetry-api (~> 1.0)
|
opentelemetry-api (~> 1.0)
|
||||||
opentelemetry-exporter-otlp (0.30.0)
|
opentelemetry-exporter-otlp (0.29.1)
|
||||||
google-protobuf (>= 3.18)
|
google-protobuf (>= 3.18)
|
||||||
googleapis-common-protos-types (~> 1.3)
|
googleapis-common-protos-types (~> 1.3)
|
||||||
opentelemetry-api (~> 1.1)
|
opentelemetry-api (~> 1.1)
|
||||||
|
@ -501,7 +480,7 @@ GEM
|
||||||
opentelemetry-api (~> 1.0)
|
opentelemetry-api (~> 1.0)
|
||||||
opentelemetry-instrumentation-active_support (~> 0.7)
|
opentelemetry-instrumentation-active_support (~> 0.7)
|
||||||
opentelemetry-instrumentation-base (~> 0.23.0)
|
opentelemetry-instrumentation-base (~> 0.23.0)
|
||||||
opentelemetry-instrumentation-action_pack (0.12.0)
|
opentelemetry-instrumentation-action_pack (0.11.0)
|
||||||
opentelemetry-api (~> 1.0)
|
opentelemetry-api (~> 1.0)
|
||||||
opentelemetry-instrumentation-base (~> 0.23.0)
|
opentelemetry-instrumentation-base (~> 0.23.0)
|
||||||
opentelemetry-instrumentation-rack (~> 0.21)
|
opentelemetry-instrumentation-rack (~> 0.21)
|
||||||
|
@ -519,10 +498,6 @@ GEM
|
||||||
opentelemetry-instrumentation-active_record (0.9.0)
|
opentelemetry-instrumentation-active_record (0.9.0)
|
||||||
opentelemetry-api (~> 1.0)
|
opentelemetry-api (~> 1.0)
|
||||||
opentelemetry-instrumentation-base (~> 0.23.0)
|
opentelemetry-instrumentation-base (~> 0.23.0)
|
||||||
opentelemetry-instrumentation-active_storage (0.1.1)
|
|
||||||
opentelemetry-api (~> 1.0)
|
|
||||||
opentelemetry-instrumentation-active_support (~> 0.7)
|
|
||||||
opentelemetry-instrumentation-base (~> 0.23.0)
|
|
||||||
opentelemetry-instrumentation-active_support (0.8.0)
|
opentelemetry-instrumentation-active_support (0.8.0)
|
||||||
opentelemetry-api (~> 1.0)
|
opentelemetry-api (~> 1.0)
|
||||||
opentelemetry-instrumentation-base (~> 0.23.0)
|
opentelemetry-instrumentation-base (~> 0.23.0)
|
||||||
|
@ -555,45 +530,44 @@ GEM
|
||||||
opentelemetry-instrumentation-rack (0.26.0)
|
opentelemetry-instrumentation-rack (0.26.0)
|
||||||
opentelemetry-api (~> 1.0)
|
opentelemetry-api (~> 1.0)
|
||||||
opentelemetry-instrumentation-base (~> 0.23.0)
|
opentelemetry-instrumentation-base (~> 0.23.0)
|
||||||
opentelemetry-instrumentation-rails (0.36.0)
|
opentelemetry-instrumentation-rails (0.35.1)
|
||||||
opentelemetry-api (~> 1.0)
|
opentelemetry-api (~> 1.0)
|
||||||
opentelemetry-instrumentation-action_mailer (~> 0.4.0)
|
opentelemetry-instrumentation-action_mailer (~> 0.4.0)
|
||||||
opentelemetry-instrumentation-action_pack (~> 0.12.0)
|
opentelemetry-instrumentation-action_pack (~> 0.11.0)
|
||||||
opentelemetry-instrumentation-action_view (~> 0.9.0)
|
opentelemetry-instrumentation-action_view (~> 0.9.0)
|
||||||
opentelemetry-instrumentation-active_job (~> 0.8.0)
|
opentelemetry-instrumentation-active_job (~> 0.8.0)
|
||||||
opentelemetry-instrumentation-active_record (~> 0.9.0)
|
opentelemetry-instrumentation-active_record (~> 0.9.0)
|
||||||
opentelemetry-instrumentation-active_storage (~> 0.1.0)
|
|
||||||
opentelemetry-instrumentation-active_support (~> 0.8.0)
|
opentelemetry-instrumentation-active_support (~> 0.8.0)
|
||||||
opentelemetry-instrumentation-base (~> 0.23.0)
|
opentelemetry-instrumentation-base (~> 0.23.0)
|
||||||
opentelemetry-instrumentation-concurrent_ruby (~> 0.22.0)
|
opentelemetry-instrumentation-concurrent_ruby (~> 0.22.0)
|
||||||
opentelemetry-instrumentation-redis (0.26.1)
|
opentelemetry-instrumentation-redis (0.26.0)
|
||||||
opentelemetry-api (~> 1.0)
|
opentelemetry-api (~> 1.0)
|
||||||
opentelemetry-instrumentation-base (~> 0.23.0)
|
opentelemetry-instrumentation-base (~> 0.23.0)
|
||||||
opentelemetry-instrumentation-sidekiq (0.26.1)
|
opentelemetry-instrumentation-sidekiq (0.26.0)
|
||||||
opentelemetry-api (~> 1.0)
|
opentelemetry-api (~> 1.0)
|
||||||
opentelemetry-instrumentation-base (~> 0.23.0)
|
opentelemetry-instrumentation-base (~> 0.23.0)
|
||||||
opentelemetry-registry (0.4.0)
|
opentelemetry-registry (0.3.1)
|
||||||
opentelemetry-api (~> 1.1)
|
opentelemetry-api (~> 1.1)
|
||||||
opentelemetry-sdk (1.8.0)
|
opentelemetry-sdk (1.6.0)
|
||||||
opentelemetry-api (~> 1.1)
|
opentelemetry-api (~> 1.1)
|
||||||
opentelemetry-common (~> 0.20)
|
opentelemetry-common (~> 0.20)
|
||||||
opentelemetry-registry (~> 0.2)
|
opentelemetry-registry (~> 0.2)
|
||||||
opentelemetry-semantic_conventions
|
opentelemetry-semantic_conventions
|
||||||
opentelemetry-semantic_conventions (1.11.0)
|
opentelemetry-semantic_conventions (1.10.1)
|
||||||
opentelemetry-api (~> 1.0)
|
opentelemetry-api (~> 1.0)
|
||||||
orm_adapter (0.5.0)
|
orm_adapter (0.5.0)
|
||||||
ostruct (0.6.1)
|
ostruct (0.6.1)
|
||||||
ox (2.14.22)
|
ox (2.14.21)
|
||||||
bigdecimal (>= 3.0)
|
bigdecimal (>= 3.0)
|
||||||
parallel (1.27.0)
|
parallel (1.26.3)
|
||||||
parser (3.3.8.0)
|
parser (3.3.7.0)
|
||||||
ast (~> 2.4.1)
|
ast (~> 2.4.1)
|
||||||
racc
|
racc
|
||||||
parslet (2.0.0)
|
parslet (2.0.0)
|
||||||
pastel (0.8.0)
|
pastel (0.8.0)
|
||||||
tty-color (~> 0.5)
|
tty-color (~> 0.5)
|
||||||
pg (1.5.9)
|
pg (1.5.9)
|
||||||
pghero (3.6.2)
|
pghero (3.6.1)
|
||||||
activerecord (>= 6.1)
|
activerecord (>= 6.1)
|
||||||
pp (0.6.2)
|
pp (0.6.2)
|
||||||
prettyprint
|
prettyprint
|
||||||
|
@ -606,7 +580,6 @@ GEM
|
||||||
net-smtp
|
net-smtp
|
||||||
premailer (~> 1.7, >= 1.7.9)
|
premailer (~> 1.7, >= 1.7.9)
|
||||||
prettyprint (0.2.0)
|
prettyprint (0.2.0)
|
||||||
prism (1.4.0)
|
|
||||||
prometheus_exporter (2.2.0)
|
prometheus_exporter (2.2.0)
|
||||||
webrick
|
webrick
|
||||||
propshaft (1.1.0)
|
propshaft (1.1.0)
|
||||||
|
@ -618,22 +591,21 @@ GEM
|
||||||
date
|
date
|
||||||
stringio
|
stringio
|
||||||
public_suffix (6.0.1)
|
public_suffix (6.0.1)
|
||||||
puma (6.6.0)
|
puma (6.5.0)
|
||||||
nio4r (~> 2.0)
|
nio4r (~> 2.0)
|
||||||
pundit (2.5.0)
|
pundit (2.4.0)
|
||||||
activesupport (>= 3.0.0)
|
activesupport (>= 3.0.0)
|
||||||
raabro (1.4.0)
|
raabro (1.4.0)
|
||||||
racc (1.8.1)
|
racc (1.8.1)
|
||||||
rack (2.2.13)
|
rack (2.2.11)
|
||||||
rack-attack (6.7.0)
|
rack-attack (6.7.0)
|
||||||
rack (>= 1.0, < 4)
|
rack (>= 1.0, < 4)
|
||||||
rack-cors (2.0.2)
|
rack-cors (2.0.2)
|
||||||
rack (>= 2.0.0)
|
rack (>= 2.0.0)
|
||||||
rack-oauth2 (2.2.1)
|
rack-oauth2 (1.21.3)
|
||||||
activesupport
|
activesupport
|
||||||
attr_required
|
attr_required
|
||||||
faraday (~> 2.0)
|
httpclient
|
||||||
faraday-follow_redirects
|
|
||||||
json-jwt (>= 1.11.0)
|
json-jwt (>= 1.11.0)
|
||||||
rack (>= 2.1.0)
|
rack (>= 2.1.0)
|
||||||
rack-protection (3.2.0)
|
rack-protection (3.2.0)
|
||||||
|
@ -648,20 +620,24 @@ GEM
|
||||||
rackup (1.0.1)
|
rackup (1.0.1)
|
||||||
rack (< 3)
|
rack (< 3)
|
||||||
webrick
|
webrick
|
||||||
rails (8.0.2)
|
rails (8.0.1)
|
||||||
actioncable (= 8.0.2)
|
actioncable (= 8.0.1)
|
||||||
actionmailbox (= 8.0.2)
|
actionmailbox (= 8.0.1)
|
||||||
actionmailer (= 8.0.2)
|
actionmailer (= 8.0.1)
|
||||||
actionpack (= 8.0.2)
|
actionpack (= 8.0.1)
|
||||||
actiontext (= 8.0.2)
|
actiontext (= 8.0.1)
|
||||||
actionview (= 8.0.2)
|
actionview (= 8.0.1)
|
||||||
activejob (= 8.0.2)
|
activejob (= 8.0.1)
|
||||||
activemodel (= 8.0.2)
|
activemodel (= 8.0.1)
|
||||||
activerecord (= 8.0.2)
|
activerecord (= 8.0.1)
|
||||||
activestorage (= 8.0.2)
|
activestorage (= 8.0.1)
|
||||||
activesupport (= 8.0.2)
|
activesupport (= 8.0.1)
|
||||||
bundler (>= 1.15.0)
|
bundler (>= 1.15.0)
|
||||||
railties (= 8.0.2)
|
railties (= 8.0.1)
|
||||||
|
rails-controller-testing (1.0.5)
|
||||||
|
actionpack (>= 5.0.1.rc1)
|
||||||
|
actionview (>= 5.0.1.rc1)
|
||||||
|
activesupport (>= 5.0.1.rc1)
|
||||||
rails-dom-testing (2.2.0)
|
rails-dom-testing (2.2.0)
|
||||||
activesupport (>= 5.0.0)
|
activesupport (>= 5.0.0)
|
||||||
minitest
|
minitest
|
||||||
|
@ -672,9 +648,9 @@ GEM
|
||||||
rails-i18n (8.0.1)
|
rails-i18n (8.0.1)
|
||||||
i18n (>= 0.7, < 2)
|
i18n (>= 0.7, < 2)
|
||||||
railties (>= 8.0.0, < 9)
|
railties (>= 8.0.0, < 9)
|
||||||
railties (8.0.2)
|
railties (8.0.1)
|
||||||
actionpack (= 8.0.2)
|
actionpack (= 8.0.1)
|
||||||
activesupport (= 8.0.2)
|
activesupport (= 8.0.1)
|
||||||
irb (~> 1.13)
|
irb (~> 1.13)
|
||||||
rackup (>= 1.0.0)
|
rackup (>= 1.0.0)
|
||||||
rake (>= 12.2)
|
rake (>= 12.2)
|
||||||
|
@ -688,23 +664,23 @@ GEM
|
||||||
link_header (~> 0.0, >= 0.0.8)
|
link_header (~> 0.0, >= 0.0.8)
|
||||||
rdf-normalize (0.7.0)
|
rdf-normalize (0.7.0)
|
||||||
rdf (~> 3.3)
|
rdf (~> 3.3)
|
||||||
rdoc (6.13.1)
|
rdoc (6.11.0)
|
||||||
psych (>= 4.0.0)
|
psych (>= 4.0.0)
|
||||||
redcarpet (3.6.1)
|
redcarpet (3.6.0)
|
||||||
redis (4.8.1)
|
redis (4.8.1)
|
||||||
redis-namespace (1.11.0)
|
redis-namespace (1.11.0)
|
||||||
redis (>= 4)
|
redis (>= 4)
|
||||||
redlock (1.3.2)
|
redlock (1.3.2)
|
||||||
redis (>= 3.0.0, < 6.0)
|
redis (>= 3.0.0, < 6.0)
|
||||||
regexp_parser (2.10.0)
|
regexp_parser (2.10.0)
|
||||||
reline (0.6.1)
|
reline (0.6.0)
|
||||||
io-console (~> 0.5)
|
io-console (~> 0.5)
|
||||||
request_store (1.7.0)
|
request_store (1.7.0)
|
||||||
rack (>= 1.4)
|
rack (>= 1.4)
|
||||||
responders (3.1.1)
|
responders (3.1.1)
|
||||||
actionpack (>= 5.2)
|
actionpack (>= 5.2)
|
||||||
railties (>= 5.2)
|
railties (>= 5.2)
|
||||||
rexml (3.4.1)
|
rexml (3.4.0)
|
||||||
rotp (6.3.0)
|
rotp (6.3.0)
|
||||||
rouge (4.5.1)
|
rouge (4.5.1)
|
||||||
rpam2 (4.0.2)
|
rpam2 (4.0.2)
|
||||||
|
@ -716,7 +692,7 @@ GEM
|
||||||
rspec-core (~> 3.13.0)
|
rspec-core (~> 3.13.0)
|
||||||
rspec-expectations (~> 3.13.0)
|
rspec-expectations (~> 3.13.0)
|
||||||
rspec-mocks (~> 3.13.0)
|
rspec-mocks (~> 3.13.0)
|
||||||
rspec-core (3.13.3)
|
rspec-core (3.13.2)
|
||||||
rspec-support (~> 3.13.0)
|
rspec-support (~> 3.13.0)
|
||||||
rspec-expectations (3.13.3)
|
rspec-expectations (3.13.3)
|
||||||
diff-lcs (>= 1.2.0, < 2.0)
|
diff-lcs (>= 1.2.0, < 2.0)
|
||||||
|
@ -726,7 +702,7 @@ GEM
|
||||||
rspec-mocks (3.13.2)
|
rspec-mocks (3.13.2)
|
||||||
diff-lcs (>= 1.2.0, < 2.0)
|
diff-lcs (>= 1.2.0, < 2.0)
|
||||||
rspec-support (~> 3.13.0)
|
rspec-support (~> 3.13.0)
|
||||||
rspec-rails (7.1.1)
|
rspec-rails (7.1.0)
|
||||||
actionpack (>= 7.0)
|
actionpack (>= 7.0)
|
||||||
activesupport (>= 7.0)
|
activesupport (>= 7.0)
|
||||||
railties (>= 7.0)
|
railties (>= 7.0)
|
||||||
|
@ -734,52 +710,42 @@ GEM
|
||||||
rspec-expectations (~> 3.13)
|
rspec-expectations (~> 3.13)
|
||||||
rspec-mocks (~> 3.13)
|
rspec-mocks (~> 3.13)
|
||||||
rspec-support (~> 3.13)
|
rspec-support (~> 3.13)
|
||||||
rspec-sidekiq (5.1.0)
|
rspec-sidekiq (5.0.0)
|
||||||
rspec-core (~> 3.0)
|
rspec-core (~> 3.0)
|
||||||
rspec-expectations (~> 3.0)
|
rspec-expectations (~> 3.0)
|
||||||
rspec-mocks (~> 3.0)
|
rspec-mocks (~> 3.0)
|
||||||
sidekiq (>= 5, < 9)
|
sidekiq (>= 5, < 8)
|
||||||
rspec-support (3.13.2)
|
rspec-support (3.13.2)
|
||||||
rubocop (1.75.2)
|
rubocop (1.71.0)
|
||||||
json (~> 2.3)
|
json (~> 2.3)
|
||||||
language_server-protocol (~> 3.17.0.2)
|
language_server-protocol (>= 3.17.0)
|
||||||
lint_roller (~> 1.1.0)
|
|
||||||
parallel (~> 1.10)
|
parallel (~> 1.10)
|
||||||
parser (>= 3.3.0.2)
|
parser (>= 3.3.0.2)
|
||||||
rainbow (>= 2.2.2, < 4.0)
|
rainbow (>= 2.2.2, < 4.0)
|
||||||
regexp_parser (>= 2.9.3, < 3.0)
|
regexp_parser (>= 2.9.3, < 3.0)
|
||||||
rubocop-ast (>= 1.44.0, < 2.0)
|
rubocop-ast (>= 1.36.2, < 2.0)
|
||||||
ruby-progressbar (~> 1.7)
|
ruby-progressbar (~> 1.7)
|
||||||
unicode-display_width (>= 2.4.0, < 4.0)
|
unicode-display_width (>= 2.4.0, < 4.0)
|
||||||
rubocop-ast (1.44.1)
|
rubocop-ast (1.38.0)
|
||||||
parser (>= 3.3.7.2)
|
parser (>= 3.3.1.0)
|
||||||
prism (~> 1.4)
|
rubocop-capybara (2.21.0)
|
||||||
rubocop-capybara (2.22.1)
|
rubocop (~> 1.41)
|
||||||
lint_roller (~> 1.1)
|
rubocop-performance (1.23.1)
|
||||||
rubocop (~> 1.72, >= 1.72.1)
|
rubocop (>= 1.48.1, < 2.0)
|
||||||
rubocop-i18n (3.2.3)
|
rubocop-ast (>= 1.31.1, < 2.0)
|
||||||
lint_roller (~> 1.1)
|
rubocop-rails (2.29.1)
|
||||||
rubocop (>= 1.72.1)
|
|
||||||
rubocop-performance (1.25.0)
|
|
||||||
lint_roller (~> 1.1)
|
|
||||||
rubocop (>= 1.75.0, < 2.0)
|
|
||||||
rubocop-ast (>= 1.38.0, < 2.0)
|
|
||||||
rubocop-rails (2.31.0)
|
|
||||||
activesupport (>= 4.2.0)
|
activesupport (>= 4.2.0)
|
||||||
lint_roller (~> 1.1)
|
|
||||||
rack (>= 1.1)
|
rack (>= 1.1)
|
||||||
rubocop (>= 1.75.0, < 2.0)
|
rubocop (>= 1.52.0, < 2.0)
|
||||||
rubocop-ast (>= 1.38.0, < 2.0)
|
rubocop-ast (>= 1.31.1, < 2.0)
|
||||||
rubocop-rspec (3.5.0)
|
rubocop-rspec (3.4.0)
|
||||||
lint_roller (~> 1.1)
|
rubocop (~> 1.61)
|
||||||
rubocop (~> 1.72, >= 1.72.1)
|
rubocop-rspec_rails (2.30.0)
|
||||||
rubocop-rspec_rails (2.31.0)
|
rubocop (~> 1.61)
|
||||||
lint_roller (~> 1.1)
|
rubocop-rspec (~> 3, >= 3.0.1)
|
||||||
rubocop (~> 1.72, >= 1.72.1)
|
|
||||||
rubocop-rspec (~> 3.5)
|
|
||||||
ruby-prof (1.7.1)
|
ruby-prof (1.7.1)
|
||||||
ruby-progressbar (1.13.0)
|
ruby-progressbar (1.13.0)
|
||||||
ruby-saml (1.18.0)
|
ruby-saml (1.17.0)
|
||||||
nokogiri (>= 1.13.10)
|
nokogiri (>= 1.13.10)
|
||||||
rexml
|
rexml
|
||||||
ruby-vips (2.2.3)
|
ruby-vips (2.2.3)
|
||||||
|
@ -797,7 +763,7 @@ GEM
|
||||||
activerecord (>= 4.0.0)
|
activerecord (>= 4.0.0)
|
||||||
railties (>= 4.0.0)
|
railties (>= 4.0.0)
|
||||||
securerandom (0.4.1)
|
securerandom (0.4.1)
|
||||||
selenium-webdriver (4.31.0)
|
selenium-webdriver (4.28.0)
|
||||||
base64 (~> 0.2)
|
base64 (~> 0.2)
|
||||||
logger (~> 1.4)
|
logger (~> 1.4)
|
||||||
rexml (~> 3.2, >= 3.2.5)
|
rexml (~> 3.2, >= 3.2.5)
|
||||||
|
@ -835,29 +801,26 @@ GEM
|
||||||
simplecov-lcov (0.8.0)
|
simplecov-lcov (0.8.0)
|
||||||
simplecov_json_formatter (0.1.4)
|
simplecov_json_formatter (0.1.4)
|
||||||
stackprof (0.2.27)
|
stackprof (0.2.27)
|
||||||
starry (0.2.0)
|
stoplight (4.1.0)
|
||||||
base64
|
|
||||||
stoplight (4.1.1)
|
|
||||||
redlock (~> 1.0)
|
redlock (~> 1.0)
|
||||||
stringio (3.1.6)
|
stringio (3.1.2)
|
||||||
strong_migrations (2.3.0)
|
strong_migrations (2.1.0)
|
||||||
activerecord (>= 7)
|
activerecord (>= 6.1)
|
||||||
swd (2.0.3)
|
swd (1.3.0)
|
||||||
activesupport (>= 3)
|
activesupport (>= 3)
|
||||||
attr_required (>= 0.0.5)
|
attr_required (>= 0.0.5)
|
||||||
faraday (~> 2.0)
|
httpclient (>= 2.4)
|
||||||
faraday-follow_redirects
|
|
||||||
sysexits (1.2.0)
|
sysexits (1.2.0)
|
||||||
temple (0.10.3)
|
temple (0.10.3)
|
||||||
terminal-table (4.0.0)
|
terminal-table (3.0.2)
|
||||||
unicode-display_width (>= 1.1.1, < 4)
|
unicode-display_width (>= 1.1.1, < 3)
|
||||||
terrapin (1.1.0)
|
terrapin (1.0.1)
|
||||||
climate_control
|
climate_control
|
||||||
test-prof (1.4.4)
|
test-prof (1.4.4)
|
||||||
thor (1.3.2)
|
thor (1.3.2)
|
||||||
tilt (2.6.0)
|
tilt (2.5.0)
|
||||||
timeout (0.4.3)
|
timeout (0.4.3)
|
||||||
tpm-key_attestation (0.14.0)
|
tpm-key_attestation (0.12.1)
|
||||||
bindata (~> 2.4)
|
bindata (~> 2.4)
|
||||||
openssl (> 2.0)
|
openssl (> 2.0)
|
||||||
openssl-signature_algorithm (~> 1.0)
|
openssl-signature_algorithm (~> 1.0)
|
||||||
|
@ -876,34 +839,34 @@ GEM
|
||||||
unf (~> 0.1.0)
|
unf (~> 0.1.0)
|
||||||
tzinfo (2.0.6)
|
tzinfo (2.0.6)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
tzinfo-data (1.2025.2)
|
tzinfo-data (1.2025.1)
|
||||||
tzinfo (>= 1.0.0)
|
tzinfo (>= 1.0.0)
|
||||||
unf (0.1.4)
|
unf (0.1.4)
|
||||||
unf_ext
|
unf_ext
|
||||||
unf_ext (0.0.9.1)
|
unf_ext (0.0.9.1)
|
||||||
unicode-display_width (3.1.4)
|
unicode-display_width (2.6.0)
|
||||||
unicode-emoji (~> 4.0, >= 4.0.4)
|
uri (1.0.2)
|
||||||
unicode-emoji (4.0.4)
|
|
||||||
uri (1.0.3)
|
|
||||||
useragent (0.16.11)
|
useragent (0.16.11)
|
||||||
|
validate_email (0.1.6)
|
||||||
|
activemodel (>= 3.0)
|
||||||
|
mail (>= 2.2.5)
|
||||||
validate_url (1.0.15)
|
validate_url (1.0.15)
|
||||||
activemodel (>= 3.0.0)
|
activemodel (>= 3.0.0)
|
||||||
public_suffix
|
public_suffix
|
||||||
warden (1.2.9)
|
warden (1.2.9)
|
||||||
rack (>= 2.0.9)
|
rack (>= 2.0.9)
|
||||||
webauthn (3.4.0)
|
webauthn (3.2.2)
|
||||||
android_key_attestation (~> 0.3.0)
|
android_key_attestation (~> 0.3.0)
|
||||||
bindata (~> 2.4)
|
bindata (~> 2.4)
|
||||||
cbor (~> 0.5.9)
|
cbor (~> 0.5.9)
|
||||||
cose (~> 1.1)
|
cose (~> 1.1)
|
||||||
openssl (>= 2.2)
|
openssl (>= 2.2)
|
||||||
safety_net_attestation (~> 0.4.0)
|
safety_net_attestation (~> 0.4.0)
|
||||||
tpm-key_attestation (~> 0.14.0)
|
tpm-key_attestation (~> 0.12.0)
|
||||||
webfinger (2.1.3)
|
webfinger (1.2.0)
|
||||||
activesupport
|
activesupport
|
||||||
faraday (~> 2.0)
|
httpclient (>= 2.4)
|
||||||
faraday-follow_redirects
|
webmock (3.24.0)
|
||||||
webmock (3.25.1)
|
|
||||||
addressable (>= 2.8.0)
|
addressable (>= 2.8.0)
|
||||||
crack (>= 0.3.2)
|
crack (>= 0.3.2)
|
||||||
hashdiff (>= 0.4.0, < 2.0.0)
|
hashdiff (>= 0.4.0, < 2.0.0)
|
||||||
|
@ -922,7 +885,7 @@ GEM
|
||||||
xorcist (1.1.3)
|
xorcist (1.1.3)
|
||||||
xpath (3.2.0)
|
xpath (3.2.0)
|
||||||
nokogiri (~> 1.8)
|
nokogiri (~> 1.8)
|
||||||
zeitwerk (2.7.2)
|
zeitwerk (2.7.1)
|
||||||
|
|
||||||
PLATFORMS
|
PLATFORMS
|
||||||
ruby
|
ruby
|
||||||
|
@ -931,7 +894,6 @@ DEPENDENCIES
|
||||||
active_model_serializers (~> 0.10)
|
active_model_serializers (~> 0.10)
|
||||||
addressable (~> 2.8)
|
addressable (~> 2.8)
|
||||||
annotaterb (~> 4.13)
|
annotaterb (~> 4.13)
|
||||||
aws-sdk-core (< 3.216.0)
|
|
||||||
aws-sdk-s3 (~> 1.123)
|
aws-sdk-s3 (~> 1.123)
|
||||||
better_errors (~> 2.9)
|
better_errors (~> 2.9)
|
||||||
binding_of_caller (~> 1.0)
|
binding_of_caller (~> 1.0)
|
||||||
|
@ -988,7 +950,6 @@ DEPENDENCIES
|
||||||
letter_opener (~> 1.8)
|
letter_opener (~> 1.8)
|
||||||
letter_opener_web (~> 3.0)
|
letter_opener_web (~> 3.0)
|
||||||
link_header (~> 0.0)
|
link_header (~> 0.0)
|
||||||
linzer (~> 0.6.1)
|
|
||||||
lograge (~> 0.12)
|
lograge (~> 0.12)
|
||||||
mail (~> 2.8)
|
mail (~> 2.8)
|
||||||
mario-redis-lock (~> 1.2)
|
mario-redis-lock (~> 1.2)
|
||||||
|
@ -1003,9 +964,9 @@ DEPENDENCIES
|
||||||
omniauth-cas (~> 3.0.0.beta.1)
|
omniauth-cas (~> 3.0.0.beta.1)
|
||||||
omniauth-rails_csrf_protection (~> 1.0)
|
omniauth-rails_csrf_protection (~> 1.0)
|
||||||
omniauth-saml (~> 2.0)
|
omniauth-saml (~> 2.0)
|
||||||
omniauth_openid_connect (~> 0.8.0)
|
omniauth_openid_connect (~> 0.6.1)
|
||||||
opentelemetry-api (~> 1.5.0)
|
opentelemetry-api (~> 1.4.0)
|
||||||
opentelemetry-exporter-otlp (~> 0.30.0)
|
opentelemetry-exporter-otlp (~> 0.29.0)
|
||||||
opentelemetry-instrumentation-active_job (~> 0.8.0)
|
opentelemetry-instrumentation-active_job (~> 0.8.0)
|
||||||
opentelemetry-instrumentation-active_model_serializers (~> 0.22.0)
|
opentelemetry-instrumentation-active_model_serializers (~> 0.22.0)
|
||||||
opentelemetry-instrumentation-concurrent_ruby (~> 0.22.0)
|
opentelemetry-instrumentation-concurrent_ruby (~> 0.22.0)
|
||||||
|
@ -1016,7 +977,7 @@ DEPENDENCIES
|
||||||
opentelemetry-instrumentation-net_http (~> 0.23.0)
|
opentelemetry-instrumentation-net_http (~> 0.23.0)
|
||||||
opentelemetry-instrumentation-pg (~> 0.30.0)
|
opentelemetry-instrumentation-pg (~> 0.30.0)
|
||||||
opentelemetry-instrumentation-rack (~> 0.26.0)
|
opentelemetry-instrumentation-rack (~> 0.26.0)
|
||||||
opentelemetry-instrumentation-rails (~> 0.36.0)
|
opentelemetry-instrumentation-rails (~> 0.35.0)
|
||||||
opentelemetry-instrumentation-redis (~> 0.26.0)
|
opentelemetry-instrumentation-redis (~> 0.26.0)
|
||||||
opentelemetry-instrumentation-sidekiq (~> 0.26.0)
|
opentelemetry-instrumentation-sidekiq (~> 0.26.0)
|
||||||
opentelemetry-sdk (~> 1.4)
|
opentelemetry-sdk (~> 1.4)
|
||||||
|
@ -1035,6 +996,7 @@ DEPENDENCIES
|
||||||
rack-cors (~> 2.0)
|
rack-cors (~> 2.0)
|
||||||
rack-test (~> 2.1)
|
rack-test (~> 2.1)
|
||||||
rails (~> 8.0)
|
rails (~> 8.0)
|
||||||
|
rails-controller-testing (~> 1.0)
|
||||||
rails-i18n (~> 8.0)
|
rails-i18n (~> 8.0)
|
||||||
rdf-normalize (~> 0.5)
|
rdf-normalize (~> 0.5)
|
||||||
redcarpet (~> 3.6)
|
redcarpet (~> 3.6)
|
||||||
|
@ -1046,7 +1008,6 @@ DEPENDENCIES
|
||||||
rspec-sidekiq (~> 5.0)
|
rspec-sidekiq (~> 5.0)
|
||||||
rubocop
|
rubocop
|
||||||
rubocop-capybara
|
rubocop-capybara
|
||||||
rubocop-i18n
|
|
||||||
rubocop-performance
|
rubocop-performance
|
||||||
rubocop-rails
|
rubocop-rails
|
||||||
rubocop-rspec
|
rubocop-rspec
|
||||||
|
@ -1085,4 +1046,4 @@ RUBY VERSION
|
||||||
ruby 3.4.1p0
|
ruby 3.4.1p0
|
||||||
|
|
||||||
BUNDLED WITH
|
BUNDLED WITH
|
||||||
2.6.8
|
2.6.3
|
||||||
|
|
124
README.md
|
@ -1,27 +1,123 @@
|
||||||
NAS is an KMY & Mastodon Fork
|
#  kmyblue
|
||||||
|
|
||||||
The following are just a few of the most common features. There are many other minor changes to the specifications.
|
[](https://github.com/kmycode/mastodon/actions/workflows/test-ruby.yml)
|
||||||
|
|
||||||
Emoji reactions
|
! FOR ENGLISH USER ! We do not provide English documentation for kmyblue; we assume that you will use automatic translation software, such as Google, to translate the site.
|
||||||
|
|
||||||
Local Public (Does not appear on the federated timeline of remote servers, but does appear on followers' home timelines. This is different from local only)
|
kmyblueは、ActivityPubに接続するSNSの1つである[Mastodon](https://github.com/mastodon/mastodon)のフォークです。創作作家のためのMastodonを目指して開発しました。
|
||||||
|
|
||||||
Bookmark classification
|
kmyblueはフォーク名であり、同時に[サーバー名](https://kmy.blue)でもあります。以下は特に記述がない限り、フォークとしてのkmyblueをさします。
|
||||||
|
|
||||||
Set who can search your posts for each post (Searchability)
|
kmyblueは AGPL ライセンスで公開されているため、どなたでも自由にフォークし、このソースコードを元に自分でサーバーを立てて公開することができます。確かにサーバーkmyblueは創作作家向けの利用規約が設定されていますが、フォークとしてのkmyblueのルールは全くの別物です。いかなるコミュニティにも平等にお使いいただけます。
|
||||||
|
kmyblueは、閉鎖的なコミュニティ、あまり目立ちたくないコミュニティには特に強力な機能を提供します。kmyblueはプライバシーを考慮したうえで強力な独自機能を提供するため、汎用サーバーとして利用するにもある程度十分な機能が揃っています。
|
||||||
|
|
||||||
Quote posts, modest quotes (references)
|
テストコード、Lint どちらも動いています。
|
||||||
|
|
||||||
Record posts that meet certain conditions such as domains, accounts, and keywords (Subscriptions/Antennas)
|
### アジェンダ
|
||||||
|
|
||||||
Send posts to a designated set of followers (Circles) (different from direct messages)
|
- 利用方法
|
||||||
|
- kmyblueの開発方針
|
||||||
|
- kmyblueは何でないか
|
||||||
|
- kmyblueの独自機能
|
||||||
|
- 英語のサポートについて
|
||||||
|
|
||||||
Notification of new posts on lists
|
## 利用方法
|
||||||
|
|
||||||
Exclude posts from people you follow when filtering posts
|
### インストール方法
|
||||||
|
|
||||||
Hide number of followers and followings
|
[Wiki](https://github.com/kmycode/mastodon/wiki/Installation)を参照してください。
|
||||||
|
|
||||||
Automatically delete posts after a specified time has passed
|
### 開発への参加方法
|
||||||
|
|
||||||
Expanding moderation functions
|
CONTRIBUTING.mdを参照してください。
|
||||||
|
|
||||||
|
### テスト
|
||||||
|
|
||||||
|
```
|
||||||
|
# デバッグ実行(以下のいずれか)
|
||||||
|
foreman start
|
||||||
|
DB_USER=postgres DB_PASS=password foreman start
|
||||||
|
|
||||||
|
# 一部を除く全てのテストを行う
|
||||||
|
RAILS_ENV=test bundle exec rspec spec
|
||||||
|
|
||||||
|
# ElasticSearch連携テストを行う
|
||||||
|
新
|
||||||
|
RAILS_ENV=test ES_ENABLED=true bundle exec rspec --tag search
|
||||||
|
旧
|
||||||
|
RAILS_ENV=test ES_ENABLED=true RUN_SEARCH_SPECS=true bundle exec rspec spec/search
|
||||||
|
```
|
||||||
|
|
||||||
|
## kmyblueの開発方針
|
||||||
|
|
||||||
|
### 本家Mastodonへの積極的追従
|
||||||
|
|
||||||
|
kmyblueは、追加機能を控えめにする代わりに本家Mastodonに積極的に追従を行います。kmyblueの追加機能そのままに、Mastodonの新機能も利用できるよう調整を行います。
|
||||||
|
|
||||||
|
### ゆるやかな内輪での運用
|
||||||
|
|
||||||
|
kmyblueは同人向けサーバーとして出発したため、同人作家に需要のある「内輪ノリを外部にできるだけもらさない」という部分に特化しています。
|
||||||
|
|
||||||
|
「ローカル公開」は、投稿を見せたくない人に見つかりにくくする効果があります。「サークル」は、フォロワーの中でも特に見せたい人だけに見せる効果があります。
|
||||||
|
「検索許可」という独自の検索オプションを利用することで、公開投稿の一部だけを検索されにくくするだけでなく、非収載投稿が誰でも自由に検索できるようになります。
|
||||||
|
|
||||||
|
内輪とは自分のサーバーに限ったものではありません。内輪同士で複数のサーバーを運営するとき、お互いが深く繋がれる「フレンドサーバー」というシステムも用意しています。
|
||||||
|
|
||||||
|
### 少人数サーバーでの運用
|
||||||
|
|
||||||
|
kmyblueは、人の少ないサーバーでの運用を考慮して設計しています。そのため、Fedibirdにあるような、人の多いサーバー向けの機能はあまり作っていません。
|
||||||
|
|
||||||
|
サーバーの負荷については一部度外視している部分があります。たとえば絵文字リアクション機能はサーバーへ著しい負荷をかける場合があります。ただしkmyblueでは、絵文字リアクション機能そのものを無効にしたり、負荷の高いストリーミング処理を無効にする管理者オプションも存在します。
|
||||||
|
|
||||||
|
もちろん人の多いサーバーでの運用が不便になるような修正は行っていません。人数にかかわらず、そのままお使いいただけます。
|
||||||
|
|
||||||
|
### 比較的高い防御力
|
||||||
|
|
||||||
|
kmyblueでは、「Fediverseは将来的に荒むのではないか」「Fediverseは将来的にスパムに溢れるのではないか」を念頭に設計している部分があります。投稿だけでなく絵文字リアクションも対象にした防衛策があります。
|
||||||
|
|
||||||
|
管理者は「NGワード」「NGルール」機能の利用が可能です。設定を変更することで、一部のモデレーターもこの機能を利用できます。
|
||||||
|
利用者は、独自拡張されたフィルター機能、絵文字リアクションのブロックなどを利用できます。
|
||||||
|
|
||||||
|
ただし防御力の高さは自由を犠牲にします。例えばNGワードが多すぎると、他のサーバーからの投稿が制限され、かつそれに気づきにくくなります。
|
||||||
|
|
||||||
|
## kmyblueは何でないか
|
||||||
|
|
||||||
|
kmyblueは、企業・政府機関向けに開発されたものではありません。開発者はセキュリティに関する専門知識を有しておらず、高度なセキュリティを求められる機関向けのソフトウェアを制作する能力はありません。また、kmyblueのメンテナは現在1人のみであり、そのメンテナが飽きたら開発がストップするリスクも高いです。Mastodonのような高い信頼性・安全性を保証することはできないので、導入の際はご自身で安全を十分に確認してからお使いになることを強くおすすめします。
|
||||||
|
個人サーバーであっても、安定性を強く求める方にはおすすめできません。glitch-socがよりよい選択肢になるでしょう。
|
||||||
|
|
||||||
|
kmyblueは、Misskeyではありません。Misskeyは「楽しむ」をコンセプトにしていますが、kmyblueはMastodonの思想を受け継ぎ、炎上や喧騒を避けることのできる落ち着いた場所を目指しています。そのため、思想に合わない機能は実装しないか、大幅に弱体化しています。
|
||||||
|
|
||||||
|
kmyblueは、Fedibirdではありません。Fedibirdは大規模サーバー向けに設定していると思われる機能があり、例えば購読機能がその代表例です。Fedibirdの購読は擬似的なフォロー体験を与えるものですが、本物のフォローではないため、購読対象の投稿が配送されることを確約したものではありません。小規模サーバーだとかえって不便になる機能を、kmyblueは避けています。
|
||||||
|
|
||||||
|
## kmyblueの独自機能
|
||||||
|
|
||||||
|
以下に列挙したものはあくまで代表的なものです。これ以外にも、細かい仕様変更などが多数含まれます。
|
||||||
|
|
||||||
|
- 絵文字リアクション
|
||||||
|
- ローカル公開(Local Public)(リモートサーバーの連合タイムラインには流れませんが、フォロワーのホームタイムラインには流れます。**ローカル限定とは異なります**)
|
||||||
|
- ブックマークの分類
|
||||||
|
- 自分の投稿を検索できる人を投稿ごとに設定(検索許可・Searchability)
|
||||||
|
- 投稿の引用、ひかえめな引用(参照)
|
||||||
|
- ドメイン・アカウント・キーワードなど特定条件を満たした投稿を記録する機能(購読・アンテナ)
|
||||||
|
- フォロワーの一部を指名して投稿を送る機能(サークル)(ダイレクトメッセージとは異なります)
|
||||||
|
- リスト新着投稿の通知
|
||||||
|
- 投稿のフィルタリングにおいて、自分がフォローしている相手の投稿を除外
|
||||||
|
- フォロー・フォロワー数を隠す機能
|
||||||
|
- 指定した時間が経過したあとに投稿を自動削除する機能
|
||||||
|
- モデレーション機能の拡張
|
||||||
|
|
||||||
|
## 英語のサポートについて
|
||||||
|
|
||||||
|
kmyblueのメイン開発者である[雪あすか](https://kmy.blue/@askyq)は、英語の読み書きがほとんどできません。そのため、ドキュメントの英語化、海外向け公式アカウントの新設などを行う予定はありません。
|
||||||
|
|
||||||
|
要望やバグ報告はIssueに書いて構いませんが、Issue画面内の説明やテンプレートはすべて日本語になっています。投稿が難しければ、Discussionに投稿してください。こちらで必要と判断したものは、改めてIssueとして起票します。
|
||||||
|
|
||||||
|
そのほか開発者へ質問があれば、[@askyq@kmy.blue](https://kmy.blue/@askyq)へ英語のまま送ってください。
|
||||||
|
|
||||||
|
ただしkmyblueのドキュメント、[@askyq@kmy.blue](https://kmy.blue/@askyq)内のkmyblueフォークに関係する投稿を、許可なく翻訳して公開することは問題ありません。
|
||||||
|
|
||||||
|
## 開発者のアカウントについて
|
||||||
|
|
||||||
|
kmyblueのメイン開発者である[雪あすか](https://kmy.blue/@askyq)は、用途別にアカウントを分けるようなことはせず、すべての発言を1つのアカウントで行っています。そのため、kmyblueの開発だけでなく、成人向け同人作品の話も混ざっています。
|
||||||
|
|
||||||
|
このうち、公開範囲「公開」「ローカル公開」「非収載」であるkmyblueフォークの開発に関係する投稿に限り抽出し、翻訳の有無に関係なく公開することを許可します。これはkmyblueフォークの利用者にとって公共性の高いコンテンツであると思われます。これは、日本と欧米では一般的に考えられている児童ポルノの基準が異なり、欧米のサーバーの中にはこのアカウントをフォローしづらいものもあるという懸念を考慮したものです。
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class Admin::Announcements::DistributionsController < Admin::BaseController
|
|
||||||
before_action :set_announcement
|
|
||||||
|
|
||||||
def create
|
|
||||||
authorize @announcement, :distribute?
|
|
||||||
@announcement.touch(:notification_sent_at)
|
|
||||||
Admin::DistributeAnnouncementNotificationWorker.perform_async(@announcement.id)
|
|
||||||
redirect_to admin_announcements_path
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def set_announcement
|
|
||||||
@announcement = Announcement.find(params[:announcement_id])
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,16 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class Admin::Announcements::PreviewsController < Admin::BaseController
|
|
||||||
before_action :set_announcement
|
|
||||||
|
|
||||||
def show
|
|
||||||
authorize @announcement, :distribute?
|
|
||||||
@user_count = @announcement.scope_for_notification.count
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def set_announcement
|
|
||||||
@announcement = Announcement.find(params[:announcement_id])
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,17 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class Admin::Announcements::TestsController < Admin::BaseController
|
|
||||||
before_action :set_announcement
|
|
||||||
|
|
||||||
def create
|
|
||||||
authorize @announcement, :distribute?
|
|
||||||
UserMailer.announcement_published(current_user, @announcement).deliver_later!
|
|
||||||
redirect_to admin_announcements_path
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def set_announcement
|
|
||||||
@announcement = Announcement.find(params[:announcement_id])
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -7,12 +7,17 @@ module Admin
|
||||||
|
|
||||||
layout 'admin'
|
layout 'admin'
|
||||||
|
|
||||||
|
before_action :set_cache_headers
|
||||||
before_action :set_referrer_policy_header
|
before_action :set_referrer_policy_header
|
||||||
|
|
||||||
after_action :verify_authorized
|
after_action :verify_authorized
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def set_cache_headers
|
||||||
|
response.cache_control.replace(private: true, no_store: true)
|
||||||
|
end
|
||||||
|
|
||||||
def set_referrer_policy_header
|
def set_referrer_policy_header
|
||||||
response.headers['Referrer-Policy'] = 'same-origin'
|
response.headers['Referrer-Policy'] = 'same-origin'
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,20 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class Admin::Fasp::Debug::CallbacksController < Admin::BaseController
|
|
||||||
def index
|
|
||||||
authorize [:admin, :fasp, :provider], :update?
|
|
||||||
|
|
||||||
@callbacks = Fasp::DebugCallback
|
|
||||||
.includes(:fasp_provider)
|
|
||||||
.order(created_at: :desc)
|
|
||||||
end
|
|
||||||
|
|
||||||
def destroy
|
|
||||||
authorize [:admin, :fasp, :provider], :update?
|
|
||||||
|
|
||||||
callback = Fasp::DebugCallback.find(params[:id])
|
|
||||||
callback.destroy
|
|
||||||
|
|
||||||
redirect_to admin_fasp_debug_callbacks_path
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,19 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class Admin::Fasp::DebugCallsController < Admin::BaseController
|
|
||||||
before_action :set_provider
|
|
||||||
|
|
||||||
def create
|
|
||||||
authorize [:admin, @provider], :update?
|
|
||||||
|
|
||||||
@provider.perform_debug_call
|
|
||||||
|
|
||||||
redirect_to admin_fasp_providers_path
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def set_provider
|
|
||||||
@provider = Fasp::Provider.find(params[:provider_id])
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,47 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class Admin::Fasp::ProvidersController < Admin::BaseController
|
|
||||||
before_action :set_provider, only: [:show, :edit, :update, :destroy]
|
|
||||||
|
|
||||||
def index
|
|
||||||
authorize [:admin, :fasp, :provider], :index?
|
|
||||||
|
|
||||||
@providers = Fasp::Provider.order(confirmed: :asc, created_at: :desc)
|
|
||||||
end
|
|
||||||
|
|
||||||
def show
|
|
||||||
authorize [:admin, @provider], :show?
|
|
||||||
end
|
|
||||||
|
|
||||||
def edit
|
|
||||||
authorize [:admin, @provider], :update?
|
|
||||||
end
|
|
||||||
|
|
||||||
def update
|
|
||||||
authorize [:admin, @provider], :update?
|
|
||||||
|
|
||||||
if @provider.update(provider_params)
|
|
||||||
redirect_to admin_fasp_providers_path
|
|
||||||
else
|
|
||||||
render :edit
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def destroy
|
|
||||||
authorize [:admin, @provider], :destroy?
|
|
||||||
|
|
||||||
@provider.destroy
|
|
||||||
|
|
||||||
redirect_to admin_fasp_providers_path
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def provider_params
|
|
||||||
params.expect(fasp_provider: [capabilities_attributes: {}])
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_provider
|
|
||||||
@provider = Fasp::Provider.find(params[:id])
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,23 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class Admin::Fasp::RegistrationsController < Admin::BaseController
|
|
||||||
before_action :set_provider
|
|
||||||
|
|
||||||
def new
|
|
||||||
authorize [:admin, @provider], :create?
|
|
||||||
end
|
|
||||||
|
|
||||||
def create
|
|
||||||
authorize [:admin, @provider], :create?
|
|
||||||
|
|
||||||
@provider.update_info!(confirm: true)
|
|
||||||
|
|
||||||
redirect_to edit_admin_fasp_provider_path(@provider)
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def set_provider
|
|
||||||
@provider = Fasp::Provider.find(params[:provider_id])
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -6,7 +6,7 @@ module Admin
|
||||||
|
|
||||||
def index
|
def index
|
||||||
authorize :software_update, :index?
|
authorize :software_update, :index?
|
||||||
@software_updates = SoftwareUpdate.by_version.filter(&:pending?)
|
@software_updates = SoftwareUpdate.by_version
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
|
@ -23,7 +23,7 @@ class Admin::TermsOfService::DraftsController < Admin::BaseController
|
||||||
private
|
private
|
||||||
|
|
||||||
def set_terms_of_service
|
def set_terms_of_service
|
||||||
@terms_of_service = TermsOfService.draft.first || TermsOfService.new(text: current_terms_of_service&.text, effective_date: 10.days.from_now)
|
@terms_of_service = TermsOfService.draft.first || TermsOfService.new(text: current_terms_of_service&.text)
|
||||||
end
|
end
|
||||||
|
|
||||||
def current_terms_of_service
|
def current_terms_of_service
|
||||||
|
@ -32,6 +32,6 @@ class Admin::TermsOfService::DraftsController < Admin::BaseController
|
||||||
|
|
||||||
def resource_params
|
def resource_params
|
||||||
params
|
params
|
||||||
.expect(terms_of_service: [:text, :changelog, :effective_date])
|
.expect(terms_of_service: [:text, :changelog])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,6 +3,6 @@
|
||||||
class Admin::TermsOfServiceController < Admin::BaseController
|
class Admin::TermsOfServiceController < Admin::BaseController
|
||||||
def index
|
def index
|
||||||
authorize :terms_of_service, :index?
|
authorize :terms_of_service, :index?
|
||||||
@terms_of_service = TermsOfService.published.first
|
@terms_of_service = TermsOfService.live.first
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,81 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class Api::Fasp::BaseController < ApplicationController
|
|
||||||
class Error < ::StandardError; end
|
|
||||||
|
|
||||||
DIGEST_PATTERN = /sha-256=:(.*?):/
|
|
||||||
KEYID_PATTERN = /keyid="(.*?)"/
|
|
||||||
|
|
||||||
attr_reader :current_provider
|
|
||||||
|
|
||||||
skip_forgery_protection
|
|
||||||
|
|
||||||
before_action :check_fasp_enabled
|
|
||||||
before_action :require_authentication
|
|
||||||
after_action :sign_response
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def require_authentication
|
|
||||||
validate_content_digest!
|
|
||||||
validate_signature!
|
|
||||||
rescue Error, Linzer::Error, ActiveRecord::RecordNotFound => e
|
|
||||||
logger.debug("FASP Authentication error: #{e}")
|
|
||||||
authentication_error
|
|
||||||
end
|
|
||||||
|
|
||||||
def authentication_error
|
|
||||||
respond_to do |format|
|
|
||||||
format.json { head 401 }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def validate_content_digest!
|
|
||||||
content_digest_header = request.headers['content-digest']
|
|
||||||
raise Error, 'content-digest missing' if content_digest_header.blank?
|
|
||||||
|
|
||||||
digest_received = content_digest_header.match(DIGEST_PATTERN)[1]
|
|
||||||
|
|
||||||
digest_computed = OpenSSL::Digest.base64digest('sha256', request.body&.string || '')
|
|
||||||
|
|
||||||
raise Error, 'content-digest does not match' if digest_received != digest_computed
|
|
||||||
end
|
|
||||||
|
|
||||||
def validate_signature!
|
|
||||||
signature_input = request.headers['signature-input']&.encode('UTF-8')
|
|
||||||
raise Error, 'signature-input is missing' if signature_input.blank?
|
|
||||||
|
|
||||||
keyid = signature_input.match(KEYID_PATTERN)[1]
|
|
||||||
provider = Fasp::Provider.find(keyid)
|
|
||||||
linzer_request = Linzer.new_request(
|
|
||||||
request.method,
|
|
||||||
request.original_url,
|
|
||||||
{},
|
|
||||||
{
|
|
||||||
'content-digest' => request.headers['content-digest'],
|
|
||||||
'signature-input' => signature_input,
|
|
||||||
'signature' => request.headers['signature'],
|
|
||||||
}
|
|
||||||
)
|
|
||||||
message = Linzer::Message.new(linzer_request)
|
|
||||||
key = Linzer.new_ed25519_public_key(provider.provider_public_key_pem, keyid)
|
|
||||||
signature = Linzer::Signature.build(message.headers)
|
|
||||||
Linzer.verify(key, message, signature)
|
|
||||||
@current_provider = provider
|
|
||||||
end
|
|
||||||
|
|
||||||
def sign_response
|
|
||||||
response.headers['content-digest'] = "sha-256=:#{OpenSSL::Digest.base64digest('sha256', response.body || '')}:"
|
|
||||||
|
|
||||||
linzer_response = Linzer.new_response(response.body, response.status, { 'content-digest' => response.headers['content-digest'] })
|
|
||||||
message = Linzer::Message.new(linzer_response)
|
|
||||||
key = Linzer.new_ed25519_key(current_provider.server_private_key_pem)
|
|
||||||
signature = Linzer.sign(key, message, %w(@status content-digest))
|
|
||||||
|
|
||||||
response.headers.merge!(signature.to_h)
|
|
||||||
end
|
|
||||||
|
|
||||||
def check_fasp_enabled
|
|
||||||
raise ActionController::RoutingError unless Mastodon::Feature.fasp_enabled?
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,15 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class Api::Fasp::Debug::V0::Callback::ResponsesController < Api::Fasp::BaseController
|
|
||||||
def create
|
|
||||||
Fasp::DebugCallback.create(
|
|
||||||
fasp_provider: current_provider,
|
|
||||||
ip: request.remote_ip,
|
|
||||||
request_body: request.raw_post
|
|
||||||
)
|
|
||||||
|
|
||||||
respond_to do |format|
|
|
||||||
format.json { head 201 }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,26 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class Api::Fasp::RegistrationsController < Api::Fasp::BaseController
|
|
||||||
skip_before_action :require_authentication
|
|
||||||
|
|
||||||
def create
|
|
||||||
@current_provider = Fasp::Provider.create!(
|
|
||||||
name: params[:name],
|
|
||||||
base_url: params[:baseUrl],
|
|
||||||
remote_identifier: params[:serverId],
|
|
||||||
provider_public_key_base64: params[:publicKey]
|
|
||||||
)
|
|
||||||
|
|
||||||
render json: registration_confirmation
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def registration_confirmation
|
|
||||||
{
|
|
||||||
faspId: current_provider.id.to_s,
|
|
||||||
publicKey: current_provider.server_public_key_base64,
|
|
||||||
registrationCompletionUri: new_admin_fasp_provider_registration_url(current_provider),
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -14,7 +14,7 @@ class Api::V1::Accounts::CredentialsController < Api::BaseController
|
||||||
@account = current_account
|
@account = current_account
|
||||||
UpdateAccountService.new.call(@account, account_params, raise_error: true)
|
UpdateAccountService.new.call(@account, account_params, raise_error: true)
|
||||||
current_user.update(user_params) if user_params
|
current_user.update(user_params) if user_params
|
||||||
ActivityPub::UpdateDistributionWorker.perform_in(ActivityPub::UpdateDistributionWorker::DEBOUNCE_DELAY, @account.id)
|
ActivityPub::UpdateDistributionWorker.perform_async(@account.id)
|
||||||
render json: @account, serializer: REST::CredentialAccountSerializer
|
render json: @account, serializer: REST::CredentialAccountSerializer
|
||||||
rescue ActiveRecord::RecordInvalid => e
|
rescue ActiveRecord::RecordInvalid => e
|
||||||
render json: ValidationErrorFormatter.new(e).as_json, status: 422
|
render json: ValidationErrorFormatter.new(e).as_json, status: 422
|
||||||
|
|
|
@ -1,66 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class Api::V1::Accounts::EndorsementsController < Api::BaseController
|
|
||||||
include Authorization
|
|
||||||
|
|
||||||
before_action -> { authorize_if_got_token! :read, :'read:accounts' }, only: :index
|
|
||||||
before_action -> { doorkeeper_authorize! :write, :'write:accounts' }, except: :index
|
|
||||||
before_action :require_user!, except: :index
|
|
||||||
before_action :set_account
|
|
||||||
before_action :set_endorsed_accounts, only: :index
|
|
||||||
after_action :insert_pagination_headers, only: :index
|
|
||||||
|
|
||||||
def index
|
|
||||||
cache_if_unauthenticated!
|
|
||||||
render json: @endorsed_accounts, each_serializer: REST::AccountSerializer
|
|
||||||
end
|
|
||||||
|
|
||||||
def create
|
|
||||||
AccountPin.find_or_create_by!(account: current_account, target_account: @account)
|
|
||||||
render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships_presenter
|
|
||||||
end
|
|
||||||
|
|
||||||
def destroy
|
|
||||||
pin = AccountPin.find_by(account: current_account, target_account: @account)
|
|
||||||
pin&.destroy!
|
|
||||||
render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships_presenter
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def set_account
|
|
||||||
@account = Account.find(params[:account_id])
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_endorsed_accounts
|
|
||||||
@endorsed_accounts = @account.unavailable? ? [] : paginated_endorsed_accounts
|
|
||||||
end
|
|
||||||
|
|
||||||
def paginated_endorsed_accounts
|
|
||||||
@account.endorsed_accounts.without_suspended.includes(:account_stat, :user).paginate_by_max_id(
|
|
||||||
limit_param(DEFAULT_ACCOUNTS_LIMIT),
|
|
||||||
params[:max_id],
|
|
||||||
params[:since_id]
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def relationships_presenter
|
|
||||||
AccountRelationshipsPresenter.new([@account], current_user.account_id)
|
|
||||||
end
|
|
||||||
|
|
||||||
def next_path
|
|
||||||
api_v1_account_endorsements_url pagination_params(max_id: pagination_max_id) if records_continue?
|
|
||||||
end
|
|
||||||
|
|
||||||
def prev_path
|
|
||||||
api_v1_account_endorsements_url pagination_params(since_id: pagination_since_id) unless @endorsed_accounts.empty?
|
|
||||||
end
|
|
||||||
|
|
||||||
def pagination_collection
|
|
||||||
@endorsed_accounts
|
|
||||||
end
|
|
||||||
|
|
||||||
def records_continue?
|
|
||||||
@endorsed_accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -17,6 +17,6 @@ class Api::V1::Accounts::FeaturedTagsController < Api::BaseController
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_featured_tags
|
def set_featured_tags
|
||||||
@featured_tags = @account.unavailable? ? [] : @account.featured_tags
|
@featured_tags = @account.suspended? ? [] : @account.featured_tags
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,10 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class Api::V1::Accounts::IdentityProofsController < Api::BaseController
|
class Api::V1::Accounts::IdentityProofsController < Api::BaseController
|
||||||
include DeprecationConcern
|
|
||||||
|
|
||||||
deprecate_api '2022-03-30'
|
|
||||||
|
|
||||||
before_action :require_user!
|
before_action :require_user!
|
||||||
before_action :set_account
|
before_action :set_account
|
||||||
|
|
||||||
|
|
30
app/controllers/api/v1/accounts/pins_controller.rb
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class Api::V1::Accounts::PinsController < Api::BaseController
|
||||||
|
include Authorization
|
||||||
|
|
||||||
|
before_action -> { doorkeeper_authorize! :write, :'write:accounts' }
|
||||||
|
before_action :require_user!
|
||||||
|
before_action :set_account
|
||||||
|
|
||||||
|
def create
|
||||||
|
AccountPin.find_or_create_by!(account: current_account, target_account: @account)
|
||||||
|
render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships_presenter
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy
|
||||||
|
pin = AccountPin.find_by(account: current_account, target_account: @account)
|
||||||
|
pin&.destroy!
|
||||||
|
render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships_presenter
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def set_account
|
||||||
|
@account = Account.find(params[:account_id])
|
||||||
|
end
|
||||||
|
|
||||||
|
def relationships_presenter
|
||||||
|
AccountRelationshipsPresenter.new([@account], current_user.account_id)
|
||||||
|
end
|
||||||
|
end
|
|
@ -124,7 +124,7 @@ class Api::V1::AccountsController < Api::BaseController
|
||||||
end
|
end
|
||||||
|
|
||||||
def account_params
|
def account_params
|
||||||
params.permit(:username, :email, :password, :agreement, :locale, :reason, :time_zone, :invite_code, :date_of_birth)
|
params.permit(:username, :email, :password, :agreement, :locale, :reason, :time_zone, :invite_code)
|
||||||
end
|
end
|
||||||
|
|
||||||
def invite
|
def invite
|
||||||
|
|
|
@ -1,10 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class Api::V1::FiltersController < Api::BaseController
|
class Api::V1::FiltersController < Api::BaseController
|
||||||
include DeprecationConcern
|
|
||||||
|
|
||||||
deprecate_api '2022-11-14'
|
|
||||||
|
|
||||||
before_action -> { doorkeeper_authorize! :read, :'read:filters' }, only: [:index, :show]
|
before_action -> { doorkeeper_authorize! :read, :'read:filters' }, only: [:index, :show]
|
||||||
before_action -> { doorkeeper_authorize! :write, :'write:filters' }, except: [:index, :show]
|
before_action -> { doorkeeper_authorize! :write, :'write:filters' }, except: [:index, :show]
|
||||||
before_action :require_user!
|
before_action :require_user!
|
||||||
|
|
|
@ -5,18 +5,12 @@ class Api::V1::Instances::TermsOfServicesController < Api::V1::Instances::BaseCo
|
||||||
|
|
||||||
def show
|
def show
|
||||||
cache_even_if_authenticated!
|
cache_even_if_authenticated!
|
||||||
render json: @terms_of_service, serializer: REST::TermsOfServiceSerializer
|
render json: @terms_of_service, serializer: REST::PrivacyPolicySerializer
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def set_terms_of_service
|
def set_terms_of_service
|
||||||
@terms_of_service = begin
|
@terms_of_service = TermsOfService.live.first!
|
||||||
if params[:date].present?
|
|
||||||
TermsOfService.published.find_by!(effective_date: params[:date])
|
|
||||||
else
|
|
||||||
TermsOfService.live.first || TermsOfService.published.first! # For the case when none of the published terms have become effective yet
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,9 +1,15 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class Api::V1::InstancesController < Api::V2::InstancesController
|
class Api::V1::InstancesController < Api::BaseController
|
||||||
include DeprecationConcern
|
skip_before_action :require_authenticated_user!, unless: :limited_federation_mode?
|
||||||
|
skip_around_action :set_locale
|
||||||
|
|
||||||
deprecate_api '2022-11-14'
|
vary_by ''
|
||||||
|
|
||||||
|
# Override `current_user` to avoid reading session cookies unless in limited federation mode
|
||||||
|
def current_user
|
||||||
|
super if limited_federation_mode?
|
||||||
|
end
|
||||||
|
|
||||||
def show
|
def show
|
||||||
cache_even_if_authenticated!
|
cache_even_if_authenticated!
|
||||||
|
|
|
@ -7,6 +7,10 @@ class Api::V1::ListsController < Api::BaseController
|
||||||
before_action :require_user!
|
before_action :require_user!
|
||||||
before_action :set_list, except: [:index, :create]
|
before_action :set_list, except: [:index, :create]
|
||||||
|
|
||||||
|
rescue_from ArgumentError do |e|
|
||||||
|
render json: { error: e.to_s }, status: 422
|
||||||
|
end
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@lists = List.where(account: current_account).all
|
@lists = List.where(account: current_account).all
|
||||||
render json: @lists, each_serializer: REST::ListSerializer
|
render json: @lists, each_serializer: REST::ListSerializer
|
||||||
|
|
|
@ -3,8 +3,8 @@
|
||||||
class Api::V1::MediaController < Api::BaseController
|
class Api::V1::MediaController < Api::BaseController
|
||||||
before_action -> { doorkeeper_authorize! :write, :'write:media' }
|
before_action -> { doorkeeper_authorize! :write, :'write:media' }
|
||||||
before_action :require_user!
|
before_action :require_user!
|
||||||
before_action :set_media_attachment, except: [:create, :destroy]
|
before_action :set_media_attachment, except: [:create]
|
||||||
before_action :check_processing, except: [:create, :destroy]
|
before_action :check_processing, except: [:create]
|
||||||
|
|
||||||
def show
|
def show
|
||||||
render json: @media_attachment, serializer: REST::MediaAttachmentSerializer, status: status_code_for_media_attachment
|
render json: @media_attachment, serializer: REST::MediaAttachmentSerializer, status: status_code_for_media_attachment
|
||||||
|
@ -25,15 +25,6 @@ class Api::V1::MediaController < Api::BaseController
|
||||||
render json: @media_attachment, serializer: REST::MediaAttachmentSerializer, status: status_code_for_media_attachment
|
render json: @media_attachment, serializer: REST::MediaAttachmentSerializer, status: status_code_for_media_attachment
|
||||||
end
|
end
|
||||||
|
|
||||||
def destroy
|
|
||||||
@media_attachment = current_account.media_attachments.find(params[:id])
|
|
||||||
|
|
||||||
return render json: in_usage_error, status: 422 unless @media_attachment.status_id.nil?
|
|
||||||
|
|
||||||
@media_attachment.destroy
|
|
||||||
render_empty
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def status_code_for_media_attachment
|
def status_code_for_media_attachment
|
||||||
|
@ -63,8 +54,4 @@ class Api::V1::MediaController < Api::BaseController
|
||||||
def processing_error
|
def processing_error
|
||||||
{ error: 'Error processing thumbnail for uploaded media' }
|
{ error: 'Error processing thumbnail for uploaded media' }
|
||||||
end
|
end
|
||||||
|
|
||||||
def in_usage_error
|
|
||||||
{ error: 'Media attachment is currently used by a status' }
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -7,7 +7,7 @@ class Api::V1::Profile::AvatarsController < Api::BaseController
|
||||||
def destroy
|
def destroy
|
||||||
@account = current_account
|
@account = current_account
|
||||||
UpdateAccountService.new.call(@account, { avatar: nil }, raise_error: true)
|
UpdateAccountService.new.call(@account, { avatar: nil }, raise_error: true)
|
||||||
ActivityPub::UpdateDistributionWorker.perform_in(ActivityPub::UpdateDistributionWorker::DEBOUNCE_DELAY, @account.id)
|
ActivityPub::UpdateDistributionWorker.perform_async(@account.id)
|
||||||
render json: @account, serializer: REST::CredentialAccountSerializer
|
render json: @account, serializer: REST::CredentialAccountSerializer
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -7,7 +7,7 @@ class Api::V1::Profile::HeadersController < Api::BaseController
|
||||||
def destroy
|
def destroy
|
||||||
@account = current_account
|
@account = current_account
|
||||||
UpdateAccountService.new.call(@account, { header: nil }, raise_error: true)
|
UpdateAccountService.new.call(@account, { header: nil }, raise_error: true)
|
||||||
ActivityPub::UpdateDistributionWorker.perform_in(ActivityPub::UpdateDistributionWorker::DEBOUNCE_DELAY, @account.id)
|
ActivityPub::UpdateDistributionWorker.perform_async(@account.id)
|
||||||
render json: @account, serializer: REST::CredentialAccountSerializer
|
render json: @account, serializer: REST::CredentialAccountSerializer
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -67,8 +67,6 @@ class Api::V1::StatusesController < Api::BaseController
|
||||||
statuses = [@status] + @context.ancestors + @context.descendants + @context.references
|
statuses = [@status] + @context.ancestors + @context.descendants + @context.references
|
||||||
|
|
||||||
render json: @context, serializer: REST::ContextSerializer, relationships: StatusRelationshipsPresenter.new(statuses, current_user&.account_id)
|
render json: @context, serializer: REST::ContextSerializer, relationships: StatusRelationshipsPresenter.new(statuses, current_user&.account_id)
|
||||||
|
|
||||||
ActivityPub::FetchAllRepliesWorker.perform_async(@status.id) if !current_account.nil? && @status.should_fetch_replies?
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
|
@ -127,7 +125,7 @@ class Api::V1::StatusesController < Api::BaseController
|
||||||
@status.account.statuses_count = @status.account.statuses_count - 1
|
@status.account.statuses_count = @status.account.statuses_count - 1
|
||||||
json = render_to_body json: @status, serializer: REST::StatusSerializer, source_requested: true
|
json = render_to_body json: @status, serializer: REST::StatusSerializer, source_requested: true
|
||||||
|
|
||||||
RemovalWorker.perform_async(@status.id, { 'redraft' => !truthy_param?(:delete_media) })
|
RemovalWorker.perform_async(@status.id, { 'redraft' => true })
|
||||||
|
|
||||||
render json: json
|
render json: json
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,9 +2,6 @@
|
||||||
|
|
||||||
class Api::V1::SuggestionsController < Api::BaseController
|
class Api::V1::SuggestionsController < Api::BaseController
|
||||||
include Authorization
|
include Authorization
|
||||||
include DeprecationConcern
|
|
||||||
|
|
||||||
deprecate_api '2021-05-16', only: [:index]
|
|
||||||
|
|
||||||
before_action -> { doorkeeper_authorize! :read, :'read:accounts' }, only: :index
|
before_action -> { doorkeeper_authorize! :read, :'read:accounts' }, only: :index
|
||||||
before_action -> { doorkeeper_authorize! :write, :'write:accounts' }, except: :index
|
before_action -> { doorkeeper_authorize! :write, :'write:accounts' }, except: :index
|
||||||
|
|
|
@ -1,15 +1,11 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class Api::V1::Trends::TagsController < Api::BaseController
|
class Api::V1::Trends::TagsController < Api::BaseController
|
||||||
include DeprecationConcern
|
|
||||||
|
|
||||||
before_action :set_tags
|
before_action :set_tags
|
||||||
|
|
||||||
after_action :insert_pagination_headers
|
after_action :insert_pagination_headers
|
||||||
|
|
||||||
DEFAULT_TAGS_LIMIT = (ENV['MAX_TRENDING_TAGS'] || 10).to_i
|
DEFAULT_TAGS_LIMIT = 10
|
||||||
|
|
||||||
deprecate_api '2022-03-30', only: :index, if: -> { request.path == '/api/v1/trends' }
|
|
||||||
|
|
||||||
def index
|
def index
|
||||||
cache_if_unauthenticated!
|
cache_if_unauthenticated!
|
||||||
|
|
|
@ -1,16 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class Api::V2::InstancesController < Api::BaseController
|
class Api::V2::InstancesController < Api::V1::InstancesController
|
||||||
skip_before_action :require_authenticated_user!, unless: :limited_federation_mode?
|
|
||||||
skip_around_action :set_locale
|
|
||||||
|
|
||||||
vary_by ''
|
|
||||||
|
|
||||||
# Override `current_user` to avoid reading session cookies unless in limited federation mode
|
|
||||||
def current_user
|
|
||||||
super if limited_federation_mode?
|
|
||||||
end
|
|
||||||
|
|
||||||
def show
|
def show
|
||||||
cache_even_if_authenticated!
|
cache_even_if_authenticated!
|
||||||
render_with_cache json: InstancePresenter.new, serializer: REST::InstanceSerializer, root: 'instance'
|
render_with_cache json: InstancePresenter.new, serializer: REST::InstanceSerializer, root: 'instance'
|
||||||
|
|
|
@ -12,6 +12,7 @@ class Auth::RegistrationsController < Devise::RegistrationsController
|
||||||
before_action :set_sessions, only: [:edit, :update]
|
before_action :set_sessions, only: [:edit, :update]
|
||||||
before_action :set_strikes, only: [:edit, :update]
|
before_action :set_strikes, only: [:edit, :update]
|
||||||
before_action :require_not_suspended!, only: [:update]
|
before_action :require_not_suspended!, only: [:update]
|
||||||
|
before_action :set_cache_headers, only: [:edit, :update]
|
||||||
before_action :set_rules, only: :new
|
before_action :set_rules, only: :new
|
||||||
before_action :require_rules_acceptance!, only: :new
|
before_action :require_rules_acceptance!, only: :new
|
||||||
before_action :set_registration_form_time, only: :new
|
before_action :set_registration_form_time, only: :new
|
||||||
|
@ -62,7 +63,7 @@ class Auth::RegistrationsController < Devise::RegistrationsController
|
||||||
|
|
||||||
def configure_sign_up_params
|
def configure_sign_up_params
|
||||||
devise_parameter_sanitizer.permit(:sign_up) do |user_params|
|
devise_parameter_sanitizer.permit(:sign_up) do |user_params|
|
||||||
user_params.permit({ account_attributes: [:username, :display_name], invite_request_attributes: [:text] }, :email, :password, :password_confirmation, :invite_code, :agreement, :website, :confirm_password, :date_of_birth)
|
user_params.permit({ account_attributes: [:username, :display_name], invite_request_attributes: [:text] }, :email, :password, :password_confirmation, :invite_code, :agreement, :website, :confirm_password)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -138,6 +139,10 @@ class Auth::RegistrationsController < Devise::RegistrationsController
|
||||||
set_locale { render :rules }
|
set_locale { render :rules }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def set_cache_headers
|
||||||
|
response.cache_control.replace(private: true, no_store: true)
|
||||||
|
end
|
||||||
|
|
||||||
def is_flashing_format? # rubocop:disable Naming/PredicateName
|
def is_flashing_format? # rubocop:disable Naming/PredicateName
|
||||||
if params[:action] == 'create'
|
if params[:action] == 'create'
|
||||||
false # Disable flash messages for sign-up
|
false # Disable flash messages for sign-up
|
||||||
|
|
|
@ -174,7 +174,7 @@ class Auth::SessionsController < Devise::SessionsController
|
||||||
end
|
end
|
||||||
|
|
||||||
def disable_custom_css?
|
def disable_custom_css?
|
||||||
user_params[:disable_css].present? && user_params[:disable_css] == '1'
|
user_params[:disable_css].present? && user_params[:disable_css] != '0'
|
||||||
end
|
end
|
||||||
|
|
||||||
def disable_custom_css!(user)
|
def disable_custom_css!(user)
|
||||||
|
|
|
@ -9,15 +9,13 @@ class BackupsController < ApplicationController
|
||||||
before_action :authenticate_user!
|
before_action :authenticate_user!
|
||||||
before_action :set_backup
|
before_action :set_backup
|
||||||
|
|
||||||
BACKUP_LINK_TIMEOUT = 1.hour.freeze
|
|
||||||
|
|
||||||
def download
|
def download
|
||||||
case Paperclip::Attachment.default_options[:storage]
|
case Paperclip::Attachment.default_options[:storage]
|
||||||
when :s3, :azure
|
when :s3, :azure
|
||||||
redirect_to @backup.dump.expiring_url(BACKUP_LINK_TIMEOUT.to_i), allow_other_host: true
|
redirect_to @backup.dump.expiring_url(10), allow_other_host: true
|
||||||
when :fog
|
when :fog
|
||||||
if Paperclip::Attachment.default_options.dig(:fog_credentials, :openstack_temp_url_key).present?
|
if Paperclip::Attachment.default_options.dig(:fog_credentials, :openstack_temp_url_key).present?
|
||||||
redirect_to @backup.dump.expiring_url(BACKUP_LINK_TIMEOUT.from_now), allow_other_host: true
|
redirect_to @backup.dump.expiring_url(Time.now.utc + 10), allow_other_host: true
|
||||||
else
|
else
|
||||||
redirect_to full_asset_url(@backup.dump.url), allow_other_host: true
|
redirect_to full_asset_url(@backup.dump.url), allow_other_host: true
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
module DeprecationConcern
|
|
||||||
extend ActiveSupport::Concern
|
|
||||||
|
|
||||||
class_methods do
|
|
||||||
def deprecate_api(date, sunset: nil, **kwargs)
|
|
||||||
deprecation_timestamp = "@#{date.to_datetime.to_i}"
|
|
||||||
sunset = sunset&.to_date&.httpdate
|
|
||||||
|
|
||||||
before_action(**kwargs) do
|
|
||||||
response.headers['Deprecation'] = deprecation_timestamp
|
|
||||||
response.headers['Sunset'] = sunset if sunset
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -10,6 +10,8 @@ module SignatureVerification
|
||||||
EXPIRATION_WINDOW_LIMIT = 12.hours
|
EXPIRATION_WINDOW_LIMIT = 12.hours
|
||||||
CLOCK_SKEW_MARGIN = 1.hour
|
CLOCK_SKEW_MARGIN = 1.hour
|
||||||
|
|
||||||
|
class SignatureVerificationError < StandardError; end
|
||||||
|
|
||||||
def require_account_signature!
|
def require_account_signature!
|
||||||
render json: signature_verification_failure_reason, status: signature_verification_failure_code unless signed_request_account
|
render json: signature_verification_failure_reason, status: signature_verification_failure_code unless signed_request_account
|
||||||
end
|
end
|
||||||
|
@ -32,7 +34,7 @@ module SignatureVerification
|
||||||
|
|
||||||
def signature_key_id
|
def signature_key_id
|
||||||
signature_params['keyId']
|
signature_params['keyId']
|
||||||
rescue Mastodon::SignatureVerificationError
|
rescue SignatureVerificationError
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -43,17 +45,17 @@ module SignatureVerification
|
||||||
def signed_request_actor
|
def signed_request_actor
|
||||||
return @signed_request_actor if defined?(@signed_request_actor)
|
return @signed_request_actor if defined?(@signed_request_actor)
|
||||||
|
|
||||||
raise Mastodon::SignatureVerificationError, 'Request not signed' unless signed_request?
|
raise SignatureVerificationError, 'Request not signed' unless signed_request?
|
||||||
raise Mastodon::SignatureVerificationError, 'Incompatible request signature. keyId and signature are required' if missing_required_signature_parameters?
|
raise SignatureVerificationError, 'Incompatible request signature. keyId and signature are required' if missing_required_signature_parameters?
|
||||||
raise Mastodon::SignatureVerificationError, 'Unsupported signature algorithm (only rsa-sha256 and hs2019 are supported)' unless %w(rsa-sha256 hs2019).include?(signature_algorithm)
|
raise SignatureVerificationError, 'Unsupported signature algorithm (only rsa-sha256 and hs2019 are supported)' unless %w(rsa-sha256 hs2019).include?(signature_algorithm)
|
||||||
raise Mastodon::SignatureVerificationError, 'Signed request date outside acceptable time window' unless matches_time_window?
|
raise SignatureVerificationError, 'Signed request date outside acceptable time window' unless matches_time_window?
|
||||||
|
|
||||||
verify_signature_strength!
|
verify_signature_strength!
|
||||||
verify_body_digest!
|
verify_body_digest!
|
||||||
|
|
||||||
actor = actor_from_key_id(signature_params['keyId'])
|
actor = actor_from_key_id(signature_params['keyId'])
|
||||||
|
|
||||||
raise Mastodon::SignatureVerificationError, "Public key not found for key #{signature_params['keyId']}" if actor.nil?
|
raise SignatureVerificationError, "Public key not found for key #{signature_params['keyId']}" if actor.nil?
|
||||||
|
|
||||||
signature = Base64.decode64(signature_params['signature'])
|
signature = Base64.decode64(signature_params['signature'])
|
||||||
compare_signed_string = build_signed_string(include_query_string: true)
|
compare_signed_string = build_signed_string(include_query_string: true)
|
||||||
|
@ -66,7 +68,7 @@ module SignatureVerification
|
||||||
|
|
||||||
actor = stoplight_wrapper.run { actor_refresh_key!(actor) }
|
actor = stoplight_wrapper.run { actor_refresh_key!(actor) }
|
||||||
|
|
||||||
raise Mastodon::SignatureVerificationError, "Could not refresh public key #{signature_params['keyId']}" if actor.nil?
|
raise SignatureVerificationError, "Could not refresh public key #{signature_params['keyId']}" if actor.nil?
|
||||||
|
|
||||||
compare_signed_string = build_signed_string(include_query_string: true)
|
compare_signed_string = build_signed_string(include_query_string: true)
|
||||||
return actor unless verify_signature(actor, signature, compare_signed_string).nil?
|
return actor unless verify_signature(actor, signature, compare_signed_string).nil?
|
||||||
|
@ -76,7 +78,7 @@ module SignatureVerification
|
||||||
return actor unless verify_signature(actor, signature, compare_signed_string).nil?
|
return actor unless verify_signature(actor, signature, compare_signed_string).nil?
|
||||||
|
|
||||||
fail_with! "Verification failed for #{actor.to_log_human_identifier} #{actor.uri} using rsa-sha256 (RSASSA-PKCS1-v1_5 with SHA-256)", signed_string: compare_signed_string, signature: signature_params['signature']
|
fail_with! "Verification failed for #{actor.to_log_human_identifier} #{actor.uri} using rsa-sha256 (RSASSA-PKCS1-v1_5 with SHA-256)", signed_string: compare_signed_string, signature: signature_params['signature']
|
||||||
rescue Mastodon::SignatureVerificationError => e
|
rescue SignatureVerificationError => e
|
||||||
fail_with! e.message
|
fail_with! e.message
|
||||||
rescue *Mastodon::HTTP_CONNECTION_ERRORS => e
|
rescue *Mastodon::HTTP_CONNECTION_ERRORS => e
|
||||||
fail_with! "Failed to fetch remote data: #{e.message}"
|
fail_with! "Failed to fetch remote data: #{e.message}"
|
||||||
|
@ -102,7 +104,7 @@ module SignatureVerification
|
||||||
def signature_params
|
def signature_params
|
||||||
@signature_params ||= SignatureParser.parse(request.headers['Signature'])
|
@signature_params ||= SignatureParser.parse(request.headers['Signature'])
|
||||||
rescue SignatureParser::ParsingError
|
rescue SignatureParser::ParsingError
|
||||||
raise Mastodon::SignatureVerificationError, 'Error parsing signature parameters'
|
raise SignatureVerificationError, 'Error parsing signature parameters'
|
||||||
end
|
end
|
||||||
|
|
||||||
def signature_algorithm
|
def signature_algorithm
|
||||||
|
@ -114,31 +116,31 @@ module SignatureVerification
|
||||||
end
|
end
|
||||||
|
|
||||||
def verify_signature_strength!
|
def verify_signature_strength!
|
||||||
raise Mastodon::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 Date header or (created) pseudo-header to be signed' unless signed_headers.include?('date') || signed_headers.include?('(created)')
|
||||||
raise Mastodon::SignatureVerificationError, 'Mastodon requires the Digest header or (request-target) pseudo-header to be signed' unless signed_headers.include?(HttpSignatureDraft::REQUEST_TARGET) || signed_headers.include?('digest')
|
raise SignatureVerificationError, 'Mastodon requires the Digest header or (request-target) pseudo-header to be signed' unless signed_headers.include?(HttpSignatureDraft::REQUEST_TARGET) || signed_headers.include?('digest')
|
||||||
raise Mastodon::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 Host header to be signed when doing a GET request' if request.get? && !signed_headers.include?('host')
|
||||||
raise Mastodon::SignatureVerificationError, 'Mastodon requires the Digest header to be signed when doing a POST request' if request.post? && !signed_headers.include?('digest')
|
raise SignatureVerificationError, 'Mastodon requires the Digest header to be signed when doing a POST request' if request.post? && !signed_headers.include?('digest')
|
||||||
end
|
end
|
||||||
|
|
||||||
def verify_body_digest!
|
def verify_body_digest!
|
||||||
return unless signed_headers.include?('digest')
|
return unless signed_headers.include?('digest')
|
||||||
raise Mastodon::SignatureVerificationError, 'Digest header missing' unless request.headers.key?('Digest')
|
raise SignatureVerificationError, 'Digest header missing' unless request.headers.key?('Digest')
|
||||||
|
|
||||||
digests = request.headers['Digest'].split(',').map { |digest| digest.split('=', 2) }.map { |key, value| [key.downcase, value] }
|
digests = request.headers['Digest'].split(',').map { |digest| digest.split('=', 2) }.map { |key, value| [key.downcase, value] }
|
||||||
sha256 = digests.assoc('sha-256')
|
sha256 = digests.assoc('sha-256')
|
||||||
raise Mastodon::SignatureVerificationError, "Mastodon only supports SHA-256 in Digest header. Offered algorithms: #{digests.map(&:first).join(', ')}" if sha256.nil?
|
raise SignatureVerificationError, "Mastodon only supports SHA-256 in Digest header. Offered algorithms: #{digests.map(&:first).join(', ')}" if sha256.nil?
|
||||||
|
|
||||||
return if body_digest == sha256[1]
|
return if body_digest == sha256[1]
|
||||||
|
|
||||||
digest_size = begin
|
digest_size = begin
|
||||||
Base64.strict_decode64(sha256[1].strip).length
|
Base64.strict_decode64(sha256[1].strip).length
|
||||||
rescue ArgumentError
|
rescue ArgumentError
|
||||||
raise Mastodon::SignatureVerificationError, "Invalid Digest value. The provided Digest value is not a valid base64 string. Given digest: #{sha256[1]}"
|
raise SignatureVerificationError, "Invalid Digest value. The provided Digest value is not a valid base64 string. Given digest: #{sha256[1]}"
|
||||||
end
|
end
|
||||||
|
|
||||||
raise Mastodon::SignatureVerificationError, "Invalid Digest value. The provided Digest value is not a SHA-256 digest. Given digest: #{sha256[1]}" if digest_size != 32
|
raise SignatureVerificationError, "Invalid Digest value. The provided Digest value is not a SHA-256 digest. Given digest: #{sha256[1]}" if digest_size != 32
|
||||||
|
|
||||||
raise Mastodon::SignatureVerificationError, "Invalid Digest value. Computed SHA-256 digest: #{body_digest}; given: #{sha256[1]}"
|
raise SignatureVerificationError, "Invalid Digest value. Computed SHA-256 digest: #{body_digest}; given: #{sha256[1]}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def verify_signature(actor, signature, compare_signed_string)
|
def verify_signature(actor, signature, compare_signed_string)
|
||||||
|
@ -163,13 +165,13 @@ module SignatureVerification
|
||||||
"#{HttpSignatureDraft::REQUEST_TARGET}: #{request.method.downcase} #{request.path}"
|
"#{HttpSignatureDraft::REQUEST_TARGET}: #{request.method.downcase} #{request.path}"
|
||||||
end
|
end
|
||||||
when '(created)'
|
when '(created)'
|
||||||
raise Mastodon::SignatureVerificationError, 'Invalid pseudo-header (created) for rsa-sha256' unless signature_algorithm == 'hs2019'
|
raise SignatureVerificationError, 'Invalid pseudo-header (created) for rsa-sha256' unless signature_algorithm == 'hs2019'
|
||||||
raise Mastodon::SignatureVerificationError, 'Pseudo-header (created) used but corresponding argument missing' if signature_params['created'].blank?
|
raise SignatureVerificationError, 'Pseudo-header (created) used but corresponding argument missing' if signature_params['created'].blank?
|
||||||
|
|
||||||
"(created): #{signature_params['created']}"
|
"(created): #{signature_params['created']}"
|
||||||
when '(expires)'
|
when '(expires)'
|
||||||
raise Mastodon::SignatureVerificationError, 'Invalid pseudo-header (expires) for rsa-sha256' unless signature_algorithm == 'hs2019'
|
raise SignatureVerificationError, 'Invalid pseudo-header (expires) for rsa-sha256' unless signature_algorithm == 'hs2019'
|
||||||
raise Mastodon::SignatureVerificationError, 'Pseudo-header (expires) used but corresponding argument missing' if signature_params['expires'].blank?
|
raise SignatureVerificationError, 'Pseudo-header (expires) used but corresponding argument missing' if signature_params['expires'].blank?
|
||||||
|
|
||||||
"(expires): #{signature_params['expires']}"
|
"(expires): #{signature_params['expires']}"
|
||||||
else
|
else
|
||||||
|
@ -191,7 +193,7 @@ module SignatureVerification
|
||||||
|
|
||||||
expires_time = Time.at(signature_params['expires'].to_i).utc if signature_params['expires'].present?
|
expires_time = Time.at(signature_params['expires'].to_i).utc if signature_params['expires'].present?
|
||||||
rescue ArgumentError => e
|
rescue ArgumentError => e
|
||||||
raise Mastodon::SignatureVerificationError, "Invalid Date header: #{e.message}"
|
raise SignatureVerificationError, "Invalid Date header: #{e.message}"
|
||||||
end
|
end
|
||||||
|
|
||||||
expires_time ||= created_time + 5.minutes unless created_time.nil?
|
expires_time ||= created_time + 5.minutes unless created_time.nil?
|
||||||
|
@ -231,9 +233,9 @@ module SignatureVerification
|
||||||
account
|
account
|
||||||
end
|
end
|
||||||
rescue Mastodon::PrivateNetworkAddressError => e
|
rescue Mastodon::PrivateNetworkAddressError => e
|
||||||
raise Mastodon::SignatureVerificationError, "Requests to private network addresses are disallowed (tried to query #{e.host})"
|
raise SignatureVerificationError, "Requests to private network addresses are disallowed (tried to query #{e.host})"
|
||||||
rescue Mastodon::HostValidationError, ActivityPub::FetchRemoteActorService::Error, ActivityPub::FetchRemoteKeyService::Error, Webfinger::Error => e
|
rescue Mastodon::HostValidationError, ActivityPub::FetchRemoteActorService::Error, ActivityPub::FetchRemoteKeyService::Error, Webfinger::Error => e
|
||||||
raise Mastodon::SignatureVerificationError, e.message
|
raise SignatureVerificationError, e.message
|
||||||
end
|
end
|
||||||
|
|
||||||
def stoplight_wrapper
|
def stoplight_wrapper
|
||||||
|
@ -249,8 +251,8 @@ module SignatureVerification
|
||||||
|
|
||||||
ActivityPub::FetchRemoteActorService.new.call(actor.uri, only_key: true, suppress_errors: false)
|
ActivityPub::FetchRemoteActorService.new.call(actor.uri, only_key: true, suppress_errors: false)
|
||||||
rescue Mastodon::PrivateNetworkAddressError => e
|
rescue Mastodon::PrivateNetworkAddressError => e
|
||||||
raise Mastodon::SignatureVerificationError, "Requests to private network addresses are disallowed (tried to query #{e.host})"
|
raise SignatureVerificationError, "Requests to private network addresses are disallowed (tried to query #{e.host})"
|
||||||
rescue Mastodon::HostValidationError, ActivityPub::FetchRemoteActorService::Error, Webfinger::Error => e
|
rescue Mastodon::HostValidationError, ActivityPub::FetchRemoteActorService::Error, Webfinger::Error => e
|
||||||
raise Mastodon::SignatureVerificationError, e.message
|
raise SignatureVerificationError, e.message
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -46,6 +46,6 @@ module WebAppControllerConcern
|
||||||
protected
|
protected
|
||||||
|
|
||||||
def set_referer_header
|
def set_referer_header
|
||||||
response.set_header('Referrer-Policy', Setting.allow_referrer_origin ? 'strict-origin-when-cross-origin' : 'same-origin')
|
response.set_header('Referrer-Policy', Setting.allow_referrer_origin ? 'origin' : 'same-origin')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,4 +8,11 @@ class Disputes::BaseController < ApplicationController
|
||||||
skip_before_action :require_functional!
|
skip_before_action :require_functional!
|
||||||
|
|
||||||
before_action :authenticate_user!
|
before_action :authenticate_user!
|
||||||
|
before_action :set_cache_headers
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def set_cache_headers
|
||||||
|
response.cache_control.replace(private: true, no_store: true)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,6 +6,7 @@ class Filters::StatusesController < ApplicationController
|
||||||
before_action :authenticate_user!
|
before_action :authenticate_user!
|
||||||
before_action :set_filter
|
before_action :set_filter
|
||||||
before_action :set_status_filters
|
before_action :set_status_filters
|
||||||
|
before_action :set_cache_headers
|
||||||
|
|
||||||
PER_PAGE = 20
|
PER_PAGE = 20
|
||||||
|
|
||||||
|
@ -39,4 +40,8 @@ class Filters::StatusesController < ApplicationController
|
||||||
def action_from_button
|
def action_from_button
|
||||||
'remove' if params[:remove]
|
'remove' if params[:remove]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def set_cache_headers
|
||||||
|
response.cache_control.replace(private: true, no_store: true)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,6 +5,7 @@ class FiltersController < ApplicationController
|
||||||
|
|
||||||
before_action :authenticate_user!
|
before_action :authenticate_user!
|
||||||
before_action :set_filter, only: [:edit, :update, :destroy]
|
before_action :set_filter, only: [:edit, :update, :destroy]
|
||||||
|
before_action :set_cache_headers
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@filters = current_account.custom_filters.includes(:keywords, :statuses).order(:phrase)
|
@filters = current_account.custom_filters.includes(:keywords, :statuses).order(:phrase)
|
||||||
|
@ -49,4 +50,8 @@ class FiltersController < ApplicationController
|
||||||
def resource_params
|
def resource_params
|
||||||
params.expect(custom_filter: [:title, :expires_in, :filter_action, :exclude_follows, :exclude_localusers, :exclude_quote, :exclude_profile, context: [], keywords_attributes: [[:id, :keyword, :whole_word, :_destroy]]])
|
params.expect(custom_filter: [:title, :expires_in, :filter_action, :exclude_follows, :exclude_localusers, :exclude_quote, :exclude_profile, context: [], keywords_attributes: [[:id, :keyword, :whole_word, :_destroy]]])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def set_cache_headers
|
||||||
|
response.cache_control.replace(private: true, no_store: true)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,6 +6,7 @@ class InvitesController < ApplicationController
|
||||||
layout 'admin'
|
layout 'admin'
|
||||||
|
|
||||||
before_action :authenticate_user!
|
before_action :authenticate_user!
|
||||||
|
before_action :set_cache_headers
|
||||||
|
|
||||||
def index
|
def index
|
||||||
authorize :invite, :create?
|
authorize :invite, :create?
|
||||||
|
@ -44,4 +45,8 @@ class InvitesController < ApplicationController
|
||||||
def resource_params
|
def resource_params
|
||||||
params.expect(invite: [:max_uses, :expires_in, :autofollow, :comment])
|
params.expect(invite: [:max_uses, :expires_in, :autofollow, :comment])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def set_cache_headers
|
||||||
|
response.cache_control.replace(private: true, no_store: true)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,6 +5,7 @@ class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController
|
||||||
|
|
||||||
before_action :store_current_location
|
before_action :store_current_location
|
||||||
before_action :authenticate_resource_owner!
|
before_action :authenticate_resource_owner!
|
||||||
|
before_action :set_cache_headers
|
||||||
|
|
||||||
content_security_policy do |p|
|
content_security_policy do |p|
|
||||||
p.form_action(false)
|
p.form_action(false)
|
||||||
|
@ -31,4 +32,8 @@ class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController
|
||||||
def truthy_param?(key)
|
def truthy_param?(key)
|
||||||
ActiveModel::Type::Boolean.new.cast(params[key])
|
ActiveModel::Type::Boolean.new.cast(params[key])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def set_cache_headers
|
||||||
|
response.cache_control.replace(private: true, no_store: true)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,6 +6,7 @@ class Oauth::AuthorizedApplicationsController < Doorkeeper::AuthorizedApplicatio
|
||||||
before_action :store_current_location
|
before_action :store_current_location
|
||||||
before_action :authenticate_resource_owner!
|
before_action :authenticate_resource_owner!
|
||||||
before_action :require_not_suspended!, only: :destroy
|
before_action :require_not_suspended!, only: :destroy
|
||||||
|
before_action :set_cache_headers
|
||||||
|
|
||||||
before_action :set_last_used_at_by_app, only: :index, unless: -> { request.format == :json }
|
before_action :set_last_used_at_by_app, only: :index, unless: -> { request.format == :json }
|
||||||
|
|
||||||
|
@ -29,6 +30,10 @@ class Oauth::AuthorizedApplicationsController < Doorkeeper::AuthorizedApplicatio
|
||||||
forbidden if current_account.unavailable?
|
forbidden if current_account.unavailable?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def set_cache_headers
|
||||||
|
response.cache_control.replace(private: true, no_store: true)
|
||||||
|
end
|
||||||
|
|
||||||
def set_last_used_at_by_app
|
def set_last_used_at_by_app
|
||||||
@last_used_at_by_app = current_resource_owner.applications_last_used
|
@last_used_at_by_app = current_resource_owner.applications_last_used
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,6 +6,7 @@ class RelationshipsController < ApplicationController
|
||||||
before_action :authenticate_user!
|
before_action :authenticate_user!
|
||||||
before_action :set_accounts, only: :show
|
before_action :set_accounts, only: :show
|
||||||
before_action :set_relationships, only: :show
|
before_action :set_relationships, only: :show
|
||||||
|
before_action :set_cache_headers
|
||||||
|
|
||||||
helper_method :following_relationship?, :followed_by_relationship?, :mutual_relationship?
|
helper_method :following_relationship?, :followed_by_relationship?, :mutual_relationship?
|
||||||
|
|
||||||
|
@ -65,4 +66,8 @@ class RelationshipsController < ApplicationController
|
||||||
'remove_domains_from_followers'
|
'remove_domains_from_followers'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def set_cache_headers
|
||||||
|
response.cache_control.replace(private: true, no_store: true)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
class Settings::ApplicationsController < Settings::BaseController
|
class Settings::ApplicationsController < Settings::BaseController
|
||||||
before_action :set_application, only: [:show, :update, :destroy, :regenerate]
|
before_action :set_application, only: [:show, :update, :destroy, :regenerate]
|
||||||
|
before_action :prepare_scopes, only: [:create, :update]
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@applications = current_user.applications.order(id: :desc).page(params[:page])
|
@applications = current_user.applications.order(id: :desc).page(params[:page])
|
||||||
|
@ -59,6 +60,12 @@ class Settings::ApplicationsController < Settings::BaseController
|
||||||
end
|
end
|
||||||
|
|
||||||
def application_params
|
def application_params
|
||||||
params.expect(doorkeeper_application: [:name, :redirect_uri, :website, scopes: []])
|
params
|
||||||
|
.expect(doorkeeper_application: [:name, :redirect_uri, :scopes, :website])
|
||||||
|
end
|
||||||
|
|
||||||
|
def prepare_scopes
|
||||||
|
scopes = application_params.fetch(:doorkeeper_application, {}).fetch(:scopes, nil)
|
||||||
|
params[:doorkeeper_application][:scopes] = scopes.join(' ') if scopes.is_a? Array
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,9 +4,14 @@ class Settings::BaseController < ApplicationController
|
||||||
layout 'admin'
|
layout 'admin'
|
||||||
|
|
||||||
before_action :authenticate_user!
|
before_action :authenticate_user!
|
||||||
|
before_action :set_cache_headers
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def set_cache_headers
|
||||||
|
response.cache_control.replace(private: true, no_store: true)
|
||||||
|
end
|
||||||
|
|
||||||
def require_not_suspended!
|
def require_not_suspended!
|
||||||
forbidden if current_account.unavailable?
|
forbidden if current_account.unavailable?
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,7 +8,7 @@ module Settings
|
||||||
def destroy
|
def destroy
|
||||||
if valid_picture?
|
if valid_picture?
|
||||||
if UpdateAccountService.new.call(@account, { @picture => nil, "#{@picture}_remote_url" => '' })
|
if UpdateAccountService.new.call(@account, { @picture => nil, "#{@picture}_remote_url" => '' })
|
||||||
ActivityPub::UpdateDistributionWorker.perform_in(ActivityPub::UpdateDistributionWorker::DEBOUNCE_DELAY, @account.id)
|
ActivityPub::UpdateDistributionWorker.perform_async(@account.id)
|
||||||
redirect_to settings_profile_path, notice: I18n.t('generic.changes_saved_msg'), status: 303
|
redirect_to settings_profile_path, notice: I18n.t('generic.changes_saved_msg'), status: 303
|
||||||
else
|
else
|
||||||
redirect_to settings_profile_path
|
redirect_to settings_profile_path
|
||||||
|
|
|
@ -8,7 +8,7 @@ class Settings::PrivacyController < Settings::BaseController
|
||||||
def update
|
def update
|
||||||
if UpdateAccountService.new.call(@account, account_params.except(:settings))
|
if UpdateAccountService.new.call(@account, account_params.except(:settings))
|
||||||
current_user.update!(settings_attributes: account_params[:settings])
|
current_user.update!(settings_attributes: account_params[:settings])
|
||||||
ActivityPub::UpdateDistributionWorker.perform_in(ActivityPub::UpdateDistributionWorker::DEBOUNCE_DELAY, @account.id)
|
ActivityPub::UpdateDistributionWorker.perform_async(@account.id)
|
||||||
redirect_to settings_privacy_path, notice: I18n.t('generic.changes_saved_msg')
|
redirect_to settings_privacy_path, notice: I18n.t('generic.changes_saved_msg')
|
||||||
else
|
else
|
||||||
render :show
|
render :show
|
||||||
|
|
|
@ -9,7 +9,7 @@ class Settings::ProfilesController < Settings::BaseController
|
||||||
|
|
||||||
def update
|
def update
|
||||||
if UpdateAccountService.new.call(@account, account_params)
|
if UpdateAccountService.new.call(@account, account_params)
|
||||||
ActivityPub::UpdateDistributionWorker.perform_in(ActivityPub::UpdateDistributionWorker::DEBOUNCE_DELAY, @account.id)
|
ActivityPub::UpdateDistributionWorker.perform_async(@account.id)
|
||||||
redirect_to settings_profile_path, notice: I18n.t('generic.changes_saved_msg')
|
redirect_to settings_profile_path, notice: I18n.t('generic.changes_saved_msg')
|
||||||
else
|
else
|
||||||
@account.build_fields
|
@account.build_fields
|
||||||
|
|
|
@ -8,7 +8,7 @@ class Settings::VerificationsController < Settings::BaseController
|
||||||
|
|
||||||
def update
|
def update
|
||||||
if UpdateAccountService.new.call(@account, account_params)
|
if UpdateAccountService.new.call(@account, account_params)
|
||||||
ActivityPub::UpdateDistributionWorker.perform_in(ActivityPub::UpdateDistributionWorker::DEBOUNCE_DELAY, @account.id)
|
ActivityPub::UpdateDistributionWorker.perform_async(@account.id)
|
||||||
redirect_to settings_verification_path, notice: I18n.t('generic.changes_saved_msg')
|
redirect_to settings_verification_path, notice: I18n.t('generic.changes_saved_msg')
|
||||||
else
|
else
|
||||||
render :show
|
render :show
|
||||||
|
|
|
@ -4,6 +4,7 @@ class SeveredRelationshipsController < ApplicationController
|
||||||
layout 'admin'
|
layout 'admin'
|
||||||
|
|
||||||
before_action :authenticate_user!
|
before_action :authenticate_user!
|
||||||
|
before_action :set_cache_headers
|
||||||
|
|
||||||
before_action :set_event, only: [:following, :followers]
|
before_action :set_event, only: [:following, :followers]
|
||||||
|
|
||||||
|
@ -48,4 +49,8 @@ class SeveredRelationshipsController < ApplicationController
|
||||||
def acct(account)
|
def acct(account)
|
||||||
account.local? ? account.local_username_and_domain : account.acct
|
account.local? ? account.local_username_and_domain : account.acct
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def set_cache_headers
|
||||||
|
response.cache_control.replace(private: true, no_store: true)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,6 +5,7 @@ class StatusesCleanupController < ApplicationController
|
||||||
|
|
||||||
before_action :authenticate_user!
|
before_action :authenticate_user!
|
||||||
before_action :set_policy
|
before_action :set_policy
|
||||||
|
before_action :set_cache_headers
|
||||||
|
|
||||||
def show; end
|
def show; end
|
||||||
|
|
||||||
|
@ -29,4 +30,8 @@ class StatusesCleanupController < ApplicationController
|
||||||
def resource_params
|
def resource_params
|
||||||
params.expect(account_statuses_cleanup_policy: [:enabled, :min_status_age, :keep_direct, :keep_pinned, :keep_polls, :keep_media, :keep_self_fav, :keep_self_bookmark, :keep_self_emoji, :min_favs, :min_reblogs, :min_emojis])
|
params.expect(account_statuses_cleanup_policy: [:enabled, :min_status_age, :keep_direct, :keep_pinned, :keep_polls, :keep_media, :keep_self_fav, :keep_self_bookmark, :keep_self_emoji, :min_favs, :min_reblogs, :min_emojis])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def set_cache_headers
|
||||||
|
response.cache_control.replace(private: true, no_store: true)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,18 +2,11 @@
|
||||||
|
|
||||||
module Admin::Trends::StatusesHelper
|
module Admin::Trends::StatusesHelper
|
||||||
def one_line_preview(status)
|
def one_line_preview(status)
|
||||||
text = begin
|
text = if status.local?
|
||||||
if status.local?
|
|
||||||
status.text.split("\n").first
|
status.text.split("\n").first
|
||||||
else
|
else
|
||||||
Nokogiri::HTML5(status.text).css('html > body > *').first&.text
|
Nokogiri::HTML5(status.text).css('html > body > *').first&.text
|
||||||
end
|
end
|
||||||
rescue ArgumentError
|
|
||||||
# This can happen if one of the Nokogumbo limits is encountered
|
|
||||||
# Unfortunately, it does not use a more precise error class
|
|
||||||
# nor allows more graceful handling
|
|
||||||
''
|
|
||||||
end
|
|
||||||
|
|
||||||
return '' if text.blank?
|
return '' if text.blank?
|
||||||
|
|
||||||
|
|
|
@ -163,49 +163,24 @@ module JsonLdHelper
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Fetch the resource given by uri.
|
def fetch_resource(uri, id_is_known, on_behalf_of = nil, request_options: {})
|
||||||
# @param uri [String]
|
|
||||||
# @param id_is_known [Boolean]
|
|
||||||
# @param on_behalf_of [nil, Account]
|
|
||||||
# @param raise_on_error [Symbol<:all, :temporary, :none>] See {#fetch_resource_without_id_validation} for possible values
|
|
||||||
def fetch_resource(uri, id_is_known, on_behalf_of = nil, raise_on_error: :none, request_options: {})
|
|
||||||
unless id_is_known
|
unless id_is_known
|
||||||
json = fetch_resource_without_id_validation(uri, on_behalf_of, raise_on_error: raise_on_error)
|
json = fetch_resource_without_id_validation(uri, on_behalf_of)
|
||||||
|
|
||||||
return if !json.is_a?(Hash) || unsupported_uri_scheme?(json['id'])
|
return if !json.is_a?(Hash) || unsupported_uri_scheme?(json['id'])
|
||||||
|
|
||||||
uri = json['id']
|
uri = json['id']
|
||||||
end
|
end
|
||||||
|
|
||||||
json = fetch_resource_without_id_validation(uri, on_behalf_of, raise_on_error: raise_on_error, request_options: request_options)
|
json = fetch_resource_without_id_validation(uri, on_behalf_of, request_options: request_options)
|
||||||
json.present? && json['id'] == uri ? json : nil
|
json.present? && json['id'] == uri ? json : nil
|
||||||
end
|
end
|
||||||
|
|
||||||
# Fetch the resource given by uri
|
def fetch_resource_without_id_validation(uri, on_behalf_of = nil, raise_on_temporary_error = false, request_options: {})
|
||||||
#
|
|
||||||
# If an error is raised, it contains the response and can be captured for handling like
|
|
||||||
#
|
|
||||||
# begin
|
|
||||||
# fetch_resource_without_id_validation(uri, nil, true)
|
|
||||||
# rescue Mastodon::UnexpectedResponseError => e
|
|
||||||
# e.response
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
# @param uri [String]
|
|
||||||
# @param on_behalf_of [nil, Account]
|
|
||||||
# @param raise_on_error [Symbol<:all, :temporary, :none>]
|
|
||||||
# - +:all+ - raise if response code is not in the 2xx range
|
|
||||||
# - +:temporary+ - raise if the response code is not an "unsalvageable error" like a 404
|
|
||||||
# (see {#response_error_unsalvageable} )
|
|
||||||
# - +:none+ - do not raise, return +nil+
|
|
||||||
def fetch_resource_without_id_validation(uri, on_behalf_of = nil, raise_on_error: :none, request_options: {})
|
|
||||||
on_behalf_of ||= Account.representative
|
on_behalf_of ||= Account.representative
|
||||||
|
|
||||||
build_request(uri, on_behalf_of, options: request_options).perform do |response|
|
build_request(uri, on_behalf_of, options: request_options).perform do |response|
|
||||||
raise Mastodon::UnexpectedResponseError, response if !response_successful?(response) && (
|
raise Mastodon::UnexpectedResponseError, response unless response_successful?(response) || response_error_unsalvageable?(response) || !raise_on_temporary_error
|
||||||
raise_on_error == :all ||
|
|
||||||
(!response_error_unsalvageable?(response) && raise_on_error == :temporary)
|
|
||||||
)
|
|
||||||
|
|
||||||
body_to_json(response.body_with_limit) if response.code == 200 && valid_activitypub_content_type?(response)
|
body_to_json(response.body_with_limit) if response.code == 200 && valid_activitypub_content_type?(response)
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,31 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class DateOfBirthInput < SimpleForm::Inputs::Base
|
|
||||||
OPTIONS = [
|
|
||||||
{ autocomplete: 'bday-day', maxlength: 2, pattern: '[0-9]+', placeholder: 'DD' }.freeze,
|
|
||||||
{ autocomplete: 'bday-month', maxlength: 2, pattern: '[0-9]+', placeholder: 'MM' }.freeze,
|
|
||||||
{ autocomplete: 'bday-year', maxlength: 4, pattern: '[0-9]+', placeholder: 'YYYY' }.freeze,
|
|
||||||
].freeze
|
|
||||||
|
|
||||||
def input(wrapper_options = nil)
|
|
||||||
merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
|
|
||||||
merged_input_options[:inputmode] = 'numeric'
|
|
||||||
|
|
||||||
values = (object.public_send(attribute_name) || '').split('.')
|
|
||||||
|
|
||||||
safe_join(Array.new(3) do |index|
|
|
||||||
options = merged_input_options.merge(OPTIONS[index]).merge id: generate_id(index), 'aria-label': I18n.t("simple_form.labels.user.date_of_birth_#{index + 1}i"), value: values[index]
|
|
||||||
@builder.text_field("#{attribute_name}(#{index + 1}i)", options)
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
def label_target
|
|
||||||
"#{attribute_name}_1i"
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def generate_id(index)
|
|
||||||
"#{object_name}_#{attribute_name}_#{index + 1}i"
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,7 +1,7 @@
|
||||||
import './public-path';
|
import './public-path';
|
||||||
import { createRoot } from 'react-dom/client';
|
import { createRoot } from 'react-dom/client';
|
||||||
|
|
||||||
import { afterInitialRender } from 'mastodon/hooks/useRenderSignal';
|
import { afterInitialRender } from 'mastodon/../hooks/useRenderSignal';
|
||||||
|
|
||||||
import { start } from '../mastodon/common';
|
import { start } from '../mastodon/common';
|
||||||
import { Status } from '../mastodon/features/standalone/status';
|
import { Status } from '../mastodon/features/standalone/status';
|
||||||
|
|
|
@ -68,7 +68,7 @@ function loaded() {
|
||||||
|
|
||||||
if (id) message = localeData[id];
|
if (id) message = localeData[id];
|
||||||
|
|
||||||
message ??= defaultMessage as string;
|
if (!message) message = defaultMessage as string;
|
||||||
|
|
||||||
const messageFormat = new IntlMessageFormat(message, locale);
|
const messageFormat = new IntlMessageFormat(message, locale);
|
||||||
return messageFormat.format(values) as string;
|
return messageFormat.format(values) as string;
|
||||||
|
|
|
@ -14,9 +14,6 @@ const isHashtagClick = (element: HTMLAnchorElement) =>
|
||||||
element.textContent?.[0] === '#' ||
|
element.textContent?.[0] === '#' ||
|
||||||
element.previousSibling?.textContent?.endsWith('#');
|
element.previousSibling?.textContent?.endsWith('#');
|
||||||
|
|
||||||
const isFeaturedHashtagClick = (element: HTMLAnchorElement) =>
|
|
||||||
isHashtagClick(element) && element.href.includes('/tagged/');
|
|
||||||
|
|
||||||
export const useLinks = () => {
|
export const useLinks = () => {
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
@ -32,19 +29,6 @@ export const useLinks = () => {
|
||||||
[history],
|
[history],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleFeaturedHashtagClick = useCallback(
|
|
||||||
(element: HTMLAnchorElement) => {
|
|
||||||
const { textContent, href } = element;
|
|
||||||
|
|
||||||
if (!textContent) return;
|
|
||||||
|
|
||||||
const url = new URL(href);
|
|
||||||
|
|
||||||
history.push(url.pathname);
|
|
||||||
},
|
|
||||||
[history],
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleMentionClick = useCallback(
|
const handleMentionClick = useCallback(
|
||||||
async (element: HTMLAnchorElement) => {
|
async (element: HTMLAnchorElement) => {
|
||||||
const result = await dispatch(openURL({ url: element.href }));
|
const result = await dispatch(openURL({ url: element.href }));
|
||||||
|
@ -77,15 +61,12 @@ export const useLinks = () => {
|
||||||
if (isMentionClick(target)) {
|
if (isMentionClick(target)) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
void handleMentionClick(target);
|
void handleMentionClick(target);
|
||||||
} else if (isFeaturedHashtagClick(target)) {
|
|
||||||
e.preventDefault();
|
|
||||||
handleFeaturedHashtagClick(target);
|
|
||||||
} else if (isHashtagClick(target)) {
|
} else if (isHashtagClick(target)) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
handleHashtagClick(target);
|
handleHashtagClick(target);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[handleMentionClick, handleFeaturedHashtagClick, handleHashtagClick],
|
[handleMentionClick, handleHashtagClick],
|
||||||
);
|
);
|
||||||
|
|
||||||
return handleClick;
|
return handleClick;
|
BIN
app/javascript/icons/android-chrome-144x144.png
Normal file → Executable file
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 8.6 KiB |
BIN
app/javascript/icons/android-chrome-192x192.png
Normal file → Executable file
Before Width: | Height: | Size: 8.5 KiB After Width: | Height: | Size: 11 KiB |
BIN
app/javascript/icons/android-chrome-256x256.png
Normal file → Executable file
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 14 KiB |
BIN
app/javascript/icons/android-chrome-36x36.png
Normal file → Executable file
Before Width: | Height: | Size: 950 B After Width: | Height: | Size: 1.8 KiB |
BIN
app/javascript/icons/android-chrome-384x384.png
Normal file → Executable file
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 24 KiB |
BIN
app/javascript/icons/android-chrome-48x48.png
Normal file → Executable file
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 3.1 KiB |
BIN
app/javascript/icons/android-chrome-512x512.png
Normal file → Executable file
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 37 KiB |
BIN
app/javascript/icons/android-chrome-72x72.png
Normal file → Executable file
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 4.6 KiB |
BIN
app/javascript/icons/android-chrome-96x96.png
Normal file → Executable file
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 6 KiB |
BIN
app/javascript/icons/apple-touch-icon-1024x1024.png
Normal file → Executable file
Before Width: | Height: | Size: 76 KiB After Width: | Height: | Size: 112 KiB |
BIN
app/javascript/icons/apple-touch-icon-114x114.png
Normal file → Executable file
Before Width: | Height: | Size: 4 KiB After Width: | Height: | Size: 6.9 KiB |
BIN
app/javascript/icons/apple-touch-icon-120x120.png
Normal file → Executable file
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 7.3 KiB |
BIN
app/javascript/icons/apple-touch-icon-144x144.png
Normal file → Executable file
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 8.6 KiB |