このコミットが含まれているのは:
2026-07-17 21:06:26 +09:00
コミット 3820d3d4d5
21個のファイルの変更949行の追加1270行の削除
+117 -7
ファイルの表示
@@ -114,6 +114,37 @@ class PostsController < ApplicationController
render json: PostRepr.base(post, current_user)
end
def preview
return head :unauthorized unless current_user
return head :forbidden unless current_user.gte_member?
return render_bad_request('URL は必須です.') if params[:url].blank?
preview = PostImportPreviewer.new.preview_rows(
rows: [{
source_row: 1,
url: params[:url].to_s,
attributes: { },
provenance: { },
tag_sources: { } }],
fetch_metadata: true).first
return render_validation_error fields: preview[:validation_errors] if preview[:validation_errors].present?
render json: {
url: preview[:url],
title: preview[:attributes]['title'],
thumbnail_base: preview[:attributes]['thumbnail_base'],
tags: preview[:attributes]['tags'],
original_created_from: preview[:attributes]['original_created_from'],
original_created_before: preview[:attributes]['original_created_before'],
duration: preview[:attributes]['duration'],
video_ms: preview[:attributes]['video_ms'],
field_warnings: preview[:field_warnings],
base_warnings: preview[:base_warnings],
existing_post: compact_post(preview[:existing_post_id]) }
rescue ArgumentError => e
render_bad_request e.message
end
def show
post =
Post
@@ -142,17 +173,21 @@ class PostsController < ApplicationController
return head :unauthorized unless current_user
return head :forbidden unless current_user.gte_member?
if bool?(:dry)
preflight = PostCreatePreflight.new(
attributes: post_create_attributes,
thumbnail: params[:thumbnail]).run
return render json: preflight
end
post = PostCreator.new(actor: current_user,
attributes: { title: params[:title], url: params[:url],
thumbnail: params[:thumbnail], tags: params[:tags],
original_created_from: params[:original_created_from],
original_created_before: params[:original_created_before],
parent_post_ids: parse_parent_post_ids,
video_ms: params[:video_ms],
duration: params[:duration] }).create!
attributes: post_create_attributes.merge(
thumbnail: params[:thumbnail])).create!
post.reload
render json: PostRepr.base(post), status: :created
rescue PostCreatePreflight::ValidationFailed => e
render_validation_error fields: e.fields, base: e.base_errors
rescue Tag::NicoTagNormalisationError
render_validation_error fields: { tags: 'ニコニコ・タグは直接指定できません.' }
rescue Tag::DeprecatedTagNormalisationError
@@ -161,6 +196,8 @@ class PostsController < ApplicationController
render_validation_error fields: { tags: ['タグ区間の記法が不正です.'] }
rescue PostCreator::VideoMsParseError
render_validation_error fields: { video_ms: ['動画時間の記法が不正です.'] }
rescue Post::RemoteThumbnailFetchFailed
render_validation_error fields: { thumbnail_base: ['サムネイル画像の取得に失敗しました.'] }
rescue MiniMagick::Error
render_validation_error fields: { thumbnail: ['サムネイル画像の変換に失敗しました.'] }
rescue ArgumentError => e
@@ -169,6 +206,22 @@ class PostsController < ApplicationController
render_post_form_record_invalid e.record
end
def bulk
return head :unauthorized unless current_user
return head :forbidden unless current_user.gte_member?
return head :unsupported_media_type unless request.content_mime_type == Mime[:multipart_form]
result = PostBulkCreator.new(
actor: current_user,
posts: parse_bulk_posts_manifest,
thumbnails: parse_bulk_thumbnails).run
render json: result
rescue JSON::ParserError
render_bad_request 'posts manifest の JSON が不正です.'
rescue ArgumentError => e
render_validation_error base: [e.message]
end
def viewed
return head :unauthorized unless current_user
@@ -463,6 +516,63 @@ class PostsController < ApplicationController
}.uniq
end
def post_create_attributes
{ title: params[:title],
url: params[:url],
thumbnail_base: params[:thumbnail_base],
tags: params[:tags],
original_created_from: params[:original_created_from],
original_created_before: params[:original_created_before],
parent_post_ids: parse_parent_post_ids,
video_ms: params[:video_ms],
duration: params[:duration] }
end
def parse_bulk_posts_manifest
manifest = params[:posts]
raise ArgumentError, 'posts は必須です.' if manifest.blank?
posts = JSON.parse(manifest)
raise ArgumentError, 'posts は配列で指定してください.' unless posts.is_a?(Array)
raise ArgumentError, '投稿件数は 1 件以上必要です.' if posts.empty?
raise ArgumentError, '投稿件数が多すぎます.' if posts.length > 100
raise ArgumentError, 'posts 要素の形式が不正です.' unless posts.all? { _1.is_a?(Hash) }
posts
end
def parse_bulk_thumbnails
thumbnails = { }
post_count = Array(JSON.parse(params[:posts].presence || '[]')).length
params.to_unsafe_h.each do |key, value|
match = key.match(/\Athumbnails\[(\d+)\]\z/)
next if match.nil?
index = Integer(match[1], 10)
raise ArgumentError, 'thumbnail index が範囲外です.' if index.negative? || index >= post_count
thumbnails[index] = value
end
thumbnails
end
def compact_post post_id
return nil if post_id.blank?
post = Post.with_attached_thumbnail.find_by(id: post_id)
return nil if post.nil?
{ id: post.id,
title: post.title,
url: post.url,
thumbnail_url:
if post.thumbnail.attached?
rails_storage_proxy_url(post.thumbnail, only_path: false)
end }
end
def sync_parent_posts! post, parent_post_ids
if parent_post_ids.include?(post.id)
post.errors.add :parent_post_ids, '自分自身を親投稿にはできません.'