require 'open-uri' require 'nokogiri' class PostsController < ApplicationController # GET /posts def index limit = (params[:limit] || 20).to_i cursor = params[:cursor].presence q = filtered_posts.order(created_at: :desc) q = q.where('posts.created_at < ?', Time.iso8601(cursor)) if cursor posts = q.limit(limit + 1) next_cursor = nil if posts.size > limit next_cursor = posts.last.created_at.iso8601(6) posts = posts.first(limit) end render json: { posts: posts.as_json(include: { tags: { only: [:id, :name, :category, :post_count] } }), next_cursor: } end def random post = filtered_posts.order('RAND()').first return head :not_found unless post viewed = current_user&.viewed?(post) || false render json: (post .as_json(include: { tags: { only: [:id, :name, :category] } }) .merge(viewed: viewed)) end # GET /posts/1 def show post = Post.includes(:tags).find(params[:id]) return head :not_found unless post viewed = current_user&.viewed?(post) || false render json: (post .as_json(include: { tags: { only: [:id, :name, :category] } }) .merge(viewed: viewed)) end # POST /posts def create return head :unauthorized unless current_user return head :forbidden unless current_user.member? # TODO: URL が正規のものがチェック,不正ならエラー # TODO: title、URL は必須にする. # TODO: サイトに応じて thumbnail_base 設定 title = params[:title] url = params[:url] thumbnail = params[:thumbnail] tag_names = params[:tags].to_s.split(' ') post = Post.new(title:, url:, thumbnail_base: '', uploaded_user: current_user) post.thumbnail.attach(thumbnail) if post.save post.resized_thumbnail! post.tags = normalise_tags(tags_names) render json: post.as_json(include: { tags: { only: [:id, :name, :category] } }), status: :created else render json: { errors: post.errors.full_messages }, status: :unprocessable_entity end end def viewed return head :unauthorized unless current_user current_user.viewed_posts << Post.find(params[:id]) head :no_content end def unviewed return head :unauthorized unless current_user current_user.viewed_posts.delete(Post.find(params[:id])) head :no_content end # PATCH/PUT /posts/1 def update return head :unauthorized unless current_user return head :forbidden unless current_user.member? title = params[:title] tag_names = params[:tags].to_s.split(' ') post = Post.find(params[:id].to_i) tags = post.tags.where(category: 'nico').to_a + normalise_tags(tag_names) if post.update(title:, tags:) render json: post.as_json(include: { tags: { only: [:id, :name, :category] } }), status: :created else render json: post.errors, status: :unprocessable_entity end end # DELETE /posts/1 def destroy end private CATEGORY_PREFIXES = { 'gen:' => 'general', 'djk:' => 'deerjikist', 'meme:' => 'meme', 'chr:' => 'character', 'mtr:' => 'material', 'meta:' => 'meta' }.freeze def filtered_posts tag_names = params[:tags]&.split(' ') match_type = params[:match] tag_names.present? ? filter_posts_by_tags(tag_names, match_type) : Post.all end def filter_posts_by_tags tag_names, match_type posts = Post.joins(:tags) if match_type == 'any' posts = posts.where(tags: { name: tag_names }).distinct else tag_names.each do |tag| posts = posts.where(id: Post.joins(:tags).where(tags: { name: tag })) end end posts.distinct end def normalise_tags tag_names tags = tag_names.map do |name| pf, cat = CATEGORY_PREFIXES.find { |p, _| name.start_with?(p) } || ['', nil] name.delete_prefix!(pf) Tag.find_or_initialize_by(name:).tap do |tag| if cat && tag.category != cat tag.category = cat tag.save! end end end tags << Tag.tagme if tags.size < 20 && tags.none?(Tag.tagme) tags.uniq end end