Merge remote-tracking branch 'parent/main' into kb_migration

This commit is contained in:
KMY 2023-09-13 22:08:20 +09:00
commit fbb1a69a65
19 changed files with 317 additions and 70 deletions

View file

@ -39,47 +39,47 @@ GIT
GEM GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
actioncable (7.0.7.2) actioncable (7.0.8)
actionpack (= 7.0.7.2) actionpack (= 7.0.8)
activesupport (= 7.0.7.2) activesupport (= 7.0.8)
nio4r (~> 2.0) nio4r (~> 2.0)
websocket-driver (>= 0.6.1) websocket-driver (>= 0.6.1)
actionmailbox (7.0.7.2) actionmailbox (7.0.8)
actionpack (= 7.0.7.2) actionpack (= 7.0.8)
activejob (= 7.0.7.2) activejob (= 7.0.8)
activerecord (= 7.0.7.2) activerecord (= 7.0.8)
activestorage (= 7.0.7.2) activestorage (= 7.0.8)
activesupport (= 7.0.7.2) activesupport (= 7.0.8)
mail (>= 2.7.1) mail (>= 2.7.1)
net-imap net-imap
net-pop net-pop
net-smtp net-smtp
actionmailer (7.0.7.2) actionmailer (7.0.8)
actionpack (= 7.0.7.2) actionpack (= 7.0.8)
actionview (= 7.0.7.2) actionview (= 7.0.8)
activejob (= 7.0.7.2) activejob (= 7.0.8)
activesupport (= 7.0.7.2) activesupport (= 7.0.8)
mail (~> 2.5, >= 2.5.4) mail (~> 2.5, >= 2.5.4)
net-imap net-imap
net-pop net-pop
net-smtp net-smtp
rails-dom-testing (~> 2.0) rails-dom-testing (~> 2.0)
actionpack (7.0.7.2) actionpack (7.0.8)
actionview (= 7.0.7.2) actionview (= 7.0.8)
activesupport (= 7.0.7.2) activesupport (= 7.0.8)
rack (~> 2.0, >= 2.2.4) rack (~> 2.0, >= 2.2.4)
rack-test (>= 0.6.3) rack-test (>= 0.6.3)
rails-dom-testing (~> 2.0) rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.2.0) rails-html-sanitizer (~> 1.0, >= 1.2.0)
actiontext (7.0.7.2) actiontext (7.0.8)
actionpack (= 7.0.7.2) actionpack (= 7.0.8)
activerecord (= 7.0.7.2) activerecord (= 7.0.8)
activestorage (= 7.0.7.2) activestorage (= 7.0.8)
activesupport (= 7.0.7.2) activesupport (= 7.0.8)
globalid (>= 0.6.0) globalid (>= 0.6.0)
nokogiri (>= 1.8.5) nokogiri (>= 1.8.5)
actionview (7.0.7.2) actionview (7.0.8)
activesupport (= 7.0.7.2) activesupport (= 7.0.8)
builder (~> 3.1) builder (~> 3.1)
erubi (~> 1.4) erubi (~> 1.4)
rails-dom-testing (~> 2.0) rails-dom-testing (~> 2.0)
@ -89,22 +89,22 @@ GEM
activemodel (>= 4.1, < 7.1) activemodel (>= 4.1, < 7.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 (7.0.7.2) activejob (7.0.8)
activesupport (= 7.0.7.2) activesupport (= 7.0.8)
globalid (>= 0.3.6) globalid (>= 0.3.6)
activemodel (7.0.7.2) activemodel (7.0.8)
activesupport (= 7.0.7.2) activesupport (= 7.0.8)
activerecord (7.0.7.2) activerecord (7.0.8)
activemodel (= 7.0.7.2) activemodel (= 7.0.8)
activesupport (= 7.0.7.2) activesupport (= 7.0.8)
activestorage (7.0.7.2) activestorage (7.0.8)
actionpack (= 7.0.7.2) actionpack (= 7.0.8)
activejob (= 7.0.7.2) activejob (= 7.0.8)
activerecord (= 7.0.7.2) activerecord (= 7.0.8)
activesupport (= 7.0.7.2) activesupport (= 7.0.8)
marcel (~> 1.0) marcel (~> 1.0)
mini_mime (>= 1.1.0) mini_mime (>= 1.1.0)
activesupport (7.0.7.2) activesupport (7.0.8)
concurrent-ruby (~> 1.0, >= 1.0.2) concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 1.6, < 2) i18n (>= 1.6, < 2)
minitest (>= 5.1) minitest (>= 5.1)
@ -556,20 +556,20 @@ GEM
rack rack
rack-test (2.1.0) rack-test (2.1.0)
rack (>= 1.3) rack (>= 1.3)
rails (7.0.7.2) rails (7.0.8)
actioncable (= 7.0.7.2) actioncable (= 7.0.8)
actionmailbox (= 7.0.7.2) actionmailbox (= 7.0.8)
actionmailer (= 7.0.7.2) actionmailer (= 7.0.8)
actionpack (= 7.0.7.2) actionpack (= 7.0.8)
actiontext (= 7.0.7.2) actiontext (= 7.0.8)
actionview (= 7.0.7.2) actionview (= 7.0.8)
activejob (= 7.0.7.2) activejob (= 7.0.8)
activemodel (= 7.0.7.2) activemodel (= 7.0.8)
activerecord (= 7.0.7.2) activerecord (= 7.0.8)
activestorage (= 7.0.7.2) activestorage (= 7.0.8)
activesupport (= 7.0.7.2) activesupport (= 7.0.8)
bundler (>= 1.15.0) bundler (>= 1.15.0)
railties (= 7.0.7.2) railties (= 7.0.8)
rails-controller-testing (1.0.5) rails-controller-testing (1.0.5)
actionpack (>= 5.0.1.rc1) actionpack (>= 5.0.1.rc1)
actionview (>= 5.0.1.rc1) actionview (>= 5.0.1.rc1)
@ -584,9 +584,9 @@ GEM
rails-i18n (7.0.7) rails-i18n (7.0.7)
i18n (>= 0.7, < 2) i18n (>= 0.7, < 2)
railties (>= 6.0.0, < 8) railties (>= 6.0.0, < 8)
railties (7.0.7.2) railties (7.0.8)
actionpack (= 7.0.7.2) actionpack (= 7.0.8)
activesupport (= 7.0.7.2) activesupport (= 7.0.8)
method_source method_source
rake (>= 12.2) rake (>= 12.2)
thor (~> 1.0) thor (~> 1.0)
@ -747,7 +747,7 @@ GEM
unicode-display_width (>= 1.1.1, < 3) unicode-display_width (>= 1.1.1, < 3)
terrapin (0.6.0) terrapin (0.6.0)
climate_control (>= 0.0.3, < 1.0) climate_control (>= 0.0.3, < 1.0)
test-prof (1.2.2) test-prof (1.2.3)
thor (1.2.2) thor (1.2.2)
tilt (2.2.0) tilt (2.2.0)
timeout (0.4.0) timeout (0.4.0)

View file

@ -0,0 +1,74 @@
# frozen_string_literal: true
class Api::V1::Admin::TagsController < Api::BaseController
include Authorization
before_action -> { authorize_if_got_token! :'admin:read' }, only: [:index, :show]
before_action -> { authorize_if_got_token! :'admin:write' }, only: :update
before_action :set_tags, only: :index
before_action :set_tag, except: :index
after_action :insert_pagination_headers, only: :index
after_action :verify_authorized
LIMIT = 100
PAGINATION_PARAMS = %i(limit).freeze
def index
authorize :tag, :index?
render json: @tags, each_serializer: REST::Admin::TagSerializer
end
def show
authorize @tag, :show?
render json: @tag, serializer: REST::Admin::TagSerializer
end
def update
authorize @tag, :update?
@tag.update!(tag_params.merge(reviewed_at: Time.now.utc))
render json: @tag, serializer: REST::Admin::TagSerializer
end
private
def set_tag
@tag = Tag.find(params[:id])
end
def set_tags
@tags = Tag.all.to_a_paginated_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id))
end
def tag_params
params.permit(:display_name, :trendable, :usable, :listable)
end
def insert_pagination_headers
set_pagination_headers(next_path, prev_path)
end
def next_path
api_v1_admin_tags_url(pagination_params(max_id: pagination_max_id)) if records_continue?
end
def prev_path
api_v1_admin_tags_url(pagination_params(min_id: pagination_since_id)) unless @tags.empty?
end
def pagination_max_id
@tags.last.id
end
def pagination_since_id
@tags.first.id
end
def records_continue?
@tags.size == limit_param(LIMIT)
end
def pagination_params(core_params)
params.slice(*PAGINATION_PARAMS).permit(*PAGINATION_PARAMS).merge(core_params)
end
end

View file

@ -11,7 +11,7 @@ module WebAppControllerConcern
end end
def skip_csrf_meta_tags? def skip_csrf_meta_tags?
!(ENV['OMNIAUTH_ONLY'] == 'true' && Devise.omniauth_providers.length == 1) && current_user.nil? !(ENV['ONE_CLICK_SSO_LOGIN'] == 'true' && ENV['OMNIAUTH_ONLY'] == 'true' && Devise.omniauth_providers.length == 1) && current_user.nil?
end end
def set_app_body_class def set_app_body_class

View file

@ -18,6 +18,7 @@ import {
importFetchedStatuses, importFetchedStatuses,
} from './importer'; } from './importer';
import { submitMarkers } from './markers'; import { submitMarkers } from './markers';
import { register as registerPushNotifications } from './push_notifications';
import { saveSettings } from './settings'; import { saveSettings } from './settings';
import { STATUS_EMOJI_REACTION_UPDATE } from './statuses'; import { STATUS_EMOJI_REACTION_UPDATE } from './statuses';
@ -305,6 +306,10 @@ export function requestBrowserPermission(callback = noOp) {
requestNotificationPermission((permission) => { requestNotificationPermission((permission) => {
dispatch(setBrowserPermission(permission)); dispatch(setBrowserPermission(permission));
callback(permission); callback(permission);
if (permission === 'granted') {
dispatch(registerPushNotifications());
}
}); });
}; };
} }

View file

@ -33,7 +33,7 @@ function main() {
console.error(err); console.error(err);
} }
if (registration) { if (registration && 'Notification' in window && Notification.permission === 'granted') {
const registerPushNotifications = await import('mastodon/actions/push_notifications'); const registerPushNotifications = await import('mastodon/actions/push_notifications');
store.dispatch(registerPushNotifications.register()); store.dispatch(registerPushNotifications.register());

View file

@ -73,6 +73,7 @@ export default function relationships(state = initialState, action) {
case ACCOUNT_UNMUTE_SUCCESS: case ACCOUNT_UNMUTE_SUCCESS:
case ACCOUNT_PIN_SUCCESS: case ACCOUNT_PIN_SUCCESS:
case ACCOUNT_UNPIN_SUCCESS: case ACCOUNT_UNPIN_SUCCESS:
return normalizeRelationship(state, action.relationship);
case RELATIONSHIPS_FETCH_SUCCESS: case RELATIONSHIPS_FETCH_SUCCESS:
return normalizeRelationships(state, action.relationships); return normalizeRelationships(state, action.relationships);
case submitAccountNote.fulfilled: case submitAccountNote.fulfilled:

View file

@ -5,7 +5,7 @@ import { showAlertForError } from '../../actions/alerts';
const defaultFailSuffix = 'FAIL'; const defaultFailSuffix = 'FAIL';
export const errorsMiddleware: Middleware<Record<string, never>, RootState> = export const errorsMiddleware: Middleware<unknown, RootState> =
({ dispatch }) => ({ dispatch }) =>
(next) => (next) =>
(action: AnyAction & { skipAlert?: boolean; skipNotFound?: boolean }) => { (action: AnyAction & { skipAlert?: boolean; skipNotFound?: boolean }) => {

View file

@ -15,7 +15,7 @@ const defaultTypeSuffixes: Config['promiseTypeSuffixes'] = [
export const loadingBarMiddleware = ( export const loadingBarMiddleware = (
config: Config = {}, config: Config = {},
): Middleware<Record<string, never>, RootState> => { ): Middleware<unknown, RootState> => {
const promiseTypeSuffixes = config.promiseTypeSuffixes ?? defaultTypeSuffixes; const promiseTypeSuffixes = config.promiseTypeSuffixes ?? defaultTypeSuffixes;
return ({ dispatch }) => return ({ dispatch }) =>

View file

@ -34,10 +34,7 @@ const play = (audio: HTMLAudioElement) => {
void audio.play(); void audio.play();
}; };
export const soundsMiddleware = (): Middleware< export const soundsMiddleware = (): Middleware<unknown, RootState> => {
Record<string, never>,
RootState
> => {
const soundCache: Record<string, HTMLAudioElement> = {}; const soundCache: Record<string, HTMLAudioElement> = {};
void ready(() => { void ready(() => {

View file

@ -12,5 +12,4 @@ export const createAppAsyncThunk = createAsyncThunk.withTypes<{
state: RootState; state: RootState;
dispatch: AppDispatch; dispatch: AppDispatch;
rejectValue: string; rejectValue: string;
extra: { s: string; n: number };
}>(); }>();

View file

@ -5274,6 +5274,11 @@ a.status-card {
font-weight: 700; font-weight: 700;
color: $primary-text-color; color: $primary-text-color;
} }
span {
overflow: inherit;
text-overflow: inherit;
}
} }
} }
} }

View file

@ -20,6 +20,7 @@
# #
class Tag < ApplicationRecord class Tag < ApplicationRecord
include Paginable
has_and_belongs_to_many :statuses has_and_belongs_to_many :statuses
has_and_belongs_to_many :accounts has_and_belongs_to_many :accounts

View file

@ -125,6 +125,6 @@ class InitialStateSerializer < ActiveModel::Serializer
end end
def sso_redirect def sso_redirect
"/auth/auth/#{Devise.omniauth_providers[0]}" if ENV['OMNIAUTH_ONLY'] == 'true' && Devise.omniauth_providers.length == 1 "/auth/auth/#{Devise.omniauth_providers[0]}" if ENV['ONE_CLICK_SSO_LOGIN'] == 'true' && ENV['OMNIAUTH_ONLY'] == 'true' && Devise.omniauth_providers.length == 1
end end
end end

View file

@ -1,7 +1,7 @@
# frozen_string_literal: true # frozen_string_literal: true
class REST::Admin::TagSerializer < REST::TagSerializer class REST::Admin::TagSerializer < REST::TagSerializer
attributes :id, :trendable, :usable, :requires_review attributes :id, :trendable, :usable, :requires_review, :listable
def id def id
object.id.to_s object.id.to_s

View file

@ -19,6 +19,22 @@ media_host ||= host_to_url(ENV['AZURE_ALIAS_HOST'])
media_host ||= host_to_url(ENV['S3_HOSTNAME']) if ENV['S3_ENABLED'] == 'true' media_host ||= host_to_url(ENV['S3_HOSTNAME']) if ENV['S3_ENABLED'] == 'true'
media_host ||= assets_host media_host ||= assets_host
def sso_host
return unless ENV['ONE_CLICK_SSO_LOGIN'] == 'true'
return unless ENV['OMNIAUTH_ONLY'] == 'true'
return unless Devise.omniauth_providers.length == 1
provider = Devise.omniauth_configs[Devise.omniauth_providers[0]]
@sso_host ||= begin
# using CAS
provider.cas_url if ENV['CAS_ENABLED'] == 'true'
# using SAML
provider.options[:idp_sso_target_url] if ENV['SAML_ENABLED'] == 'true'
# or using OIDC
ENV['OIDC_AUTH_ENDPOINT'] || (OpenIDConnect::Discovery::Provider::Config.discover!(ENV['OIDC_ISSUER']).authorization_endpoint if ENV['OIDC_ENABLED'] == 'true')
end
end
Rails.application.config.content_security_policy do |p| Rails.application.config.content_security_policy do |p|
p.base_uri :none p.base_uri :none
p.default_src :none p.default_src :none
@ -29,7 +45,13 @@ Rails.application.config.content_security_policy do |p|
p.media_src :self, :https, :data, assets_host p.media_src :self, :https, :data, assets_host
p.frame_src :self, :https p.frame_src :self, :https
p.manifest_src :self, assets_host p.manifest_src :self, assets_host
if sso_host.present?
p.form_action :self, sso_host
else
p.form_action :self p.form_action :self
end
p.child_src :self, :blob, assets_host p.child_src :self, :blob, assets_host
p.worker_src :self, :blob, assets_host p.worker_src :self, :blob, assets_host

View file

@ -314,6 +314,8 @@ namespace :api, format: false do
post :test post :test
end end
end end
resources :tags, only: [:index, :show, :update]
end end
end end

View file

@ -16,7 +16,7 @@ module Mastodon::CLI
option :concurrency, type: :numeric, default: 5, aliases: [:c], desc: 'Workload will be split between this number of threads' option :concurrency, type: :numeric, default: 5, aliases: [:c], desc: 'Workload will be split between this number of threads'
option :batch_size, type: :numeric, default: 100, aliases: [:b], desc: 'Number of records in each batch' option :batch_size, type: :numeric, default: 100, aliases: [:b], desc: 'Number of records in each batch'
option :only, type: :array, enum: %w(instances accounts tags statuses), desc: 'Only process these indices' option :only, type: :array, enum: %w(instances accounts tags statuses public_statuses), desc: 'Only process these indices'
option :import, type: :boolean, default: true, desc: 'Import data from the database to the index' option :import, type: :boolean, default: true, desc: 'Import data from the database to the index'
option :clean, type: :boolean, default: true, desc: 'Remove outdated documents from the index' option :clean, type: :boolean, default: true, desc: 'Remove outdated documents from the index'
option :reset_chewy, type: :boolean, default: false, desc: "Reset Chewy's internal index" option :reset_chewy, type: :boolean, default: false, desc: "Reset Chewy's internal index"

View file

@ -0,0 +1,141 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe 'Tags' do
let(:role) { UserRole.find_by(name: 'Admin') }
let(:user) { Fabricate(:user, role: role) }
let(:scopes) { 'admin:read admin:write' }
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
let(:tag) { Fabricate(:tag) }
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
describe 'GET /api/v1/admin/tags' do
subject do
get '/api/v1/admin/tags', headers: headers, params: params
end
let(:params) { {} }
it_behaves_like 'forbidden for wrong scope', 'write:statuses'
it_behaves_like 'forbidden for wrong role', ''
it 'returns http success' do
subject
expect(response).to have_http_status(200)
end
context 'when there are no tags' do
it 'returns an empty list' do
subject
expect(body_as_json).to be_empty
end
end
context 'when there are tagss' do
let!(:tags) do
[
Fabricate(:tag),
Fabricate(:tag),
Fabricate(:tag),
Fabricate(:tag),
]
end
it 'returns the expected tags' do
subject
tags.each do |tag|
expect(body_as_json.find { |item| item[:id] == tag.id.to_s && item[:name] == tag.name }).to_not be_nil
end
end
context 'with limit param' do
let(:params) { { limit: 2 } }
it 'returns only the requested number of tags' do
subject
expect(body_as_json.size).to eq(params[:limit])
end
end
end
end
describe 'GET /api/v1/admin/tags/:id' do
subject do
get "/api/v1/admin/tags/#{tag.id}", headers: headers
end
let!(:tag) { Fabricate(:tag) }
it_behaves_like 'forbidden for wrong scope', 'write:statuses'
it_behaves_like 'forbidden for wrong role', ''
it 'returns http success' do
subject
expect(response).to have_http_status(200)
end
it 'returns expected tag content' do
subject
expect(body_as_json[:id].to_i).to eq(tag.id)
expect(body_as_json[:name]).to eq(tag.name)
end
context 'when the requested tag does not exist' do
it 'returns http not found' do
get '/api/v1/admin/tags/-1', headers: headers
expect(response).to have_http_status(404)
end
end
end
describe 'PUT /api/v1/admin/tags/:id' do
subject do
put "/api/v1/admin/tags/#{tag.id}", headers: headers, params: params
end
let!(:tag) { Fabricate(:tag) }
let(:params) { { display_name: tag.name.upcase } }
it_behaves_like 'forbidden for wrong scope', 'write:statuses'
it_behaves_like 'forbidden for wrong scope', 'admin:read'
it_behaves_like 'forbidden for wrong role', ''
it 'returns http success' do
subject
expect(response).to have_http_status(200)
end
it 'returns updated tag' do
subject
expect(body_as_json[:id].to_i).to eq(tag.id)
expect(body_as_json[:name]).to eq(tag.name.upcase)
end
context 'when the updated display name is invalid' do
let(:params) { { display_name: tag.name + tag.id.to_s } }
it 'returns http unprocessable content' do
subject
expect(response).to have_http_status(422)
end
end
context 'when the requested tag does not exist' do
it 'returns http not found' do
get '/api/v1/admin/tags/-1', headers: headers
expect(response).to have_http_status(404)
end
end
end
end

View file

@ -12379,9 +12379,9 @@ uuid@^8.3.2:
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
uuid@^9.0.0: uuid@^9.0.0:
version "9.0.0" version "9.0.1"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.0.tgz#592f550650024a38ceb0c562f2f6aa435761efb5" resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30"
integrity sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg== integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==
v8-compile-cache@^2.1.1: v8-compile-cache@^2.1.1:
version "2.3.0" version "2.3.0"