diff --git a/backend/app/models/post.rb b/backend/app/models/post.rb index 6dd565b..24af551 100644 --- a/backend/app/models/post.rb +++ b/backend/app/models/post.rb @@ -17,7 +17,12 @@ class Post < ApplicationRecord foreign_key: :target_post_id has_one_attached :thumbnail + before_validation :normalise_url + + validates :url, presence: true, uniqueness: true + validate :validate_original_created_range + validate :url_must_be_http_url def as_json options = { } super(options).merge({ thumbnail: thumbnail.attached? ? @@ -69,4 +74,33 @@ class Post < ApplicationRecord errors.add :original_created_before, 'オリジナルの作成日時の順番がをかしぃです.' end end + + def url_must_be_http_url + begin + u = URI.parse(url) + rescue URI::InvalidURIError + errors.add(:url, 'URL が不正です.') + return + end + + if !(u in URI::HTTP) || u.host.blank? + errors.add(:url, 'URL が不正です.') + return + end + end + + def normalise_url + return if url.blank? + + self.url = url.strip + + u = URI.parse(url) + return unless u in URI::HTTP + + u.host = u.host.downcase if u.host + u.path = u.path.sub(/\/\Z/, '') if u.path.present? + self.url = u.to_s + rescue URI::InvalidURIError + ; + end end diff --git a/backend/config/locales/en.yml b/backend/config/locales/en.yml index 6c349ae..ff6ebed 100644 --- a/backend/config/locales/en.yml +++ b/backend/config/locales/en.yml @@ -28,4 +28,8 @@ # enabled: "ON" en: - hello: "Hello world" + activerecord: + errors: + messages: + record_invalid: "Validation failed: %{errors}" + taken: 'イキスギ!' diff --git a/backend/db/migrate/20260118144400_add_unique_index_to_url_in_posts.rb b/backend/db/migrate/20260118144400_add_unique_index_to_url_in_posts.rb new file mode 100644 index 0000000..de807c3 --- /dev/null +++ b/backend/db/migrate/20260118144400_add_unique_index_to_url_in_posts.rb @@ -0,0 +1,6 @@ +class AddUniqueIndexToUrlInPosts < ActiveRecord::Migration[7.1] + def change + change_column :posts, :url, :string, limit: 768 + add_index :posts, :url, unique: true, name: 'index_posts_on_url' + end +end diff --git a/backend/db/schema.rb b/backend/db/schema.rb index b8312fb..a67d0dd 100644 --- a/backend/db/schema.rb +++ b/backend/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[8.0].define(version: 2026_01_12_111800) do +ActiveRecord::Schema[8.0].define(version: 2026_01_18_144400) do create_table "active_storage_attachments", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.string "name", null: false t.string "record_type", null: false @@ -85,7 +85,7 @@ ActiveRecord::Schema[8.0].define(version: 2026_01_12_111800) do create_table "posts", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.string "title" - t.string "url", limit: 2000, null: false + t.string "url", limit: 768, null: false t.string "thumbnail_base", limit: 2000 t.bigint "parent_id" t.bigint "uploaded_user_id" @@ -95,6 +95,7 @@ ActiveRecord::Schema[8.0].define(version: 2026_01_12_111800) do t.datetime "updated_at", null: false t.index ["parent_id"], name: "index_posts_on_parent_id" t.index ["uploaded_user_id"], name: "index_posts_on_uploaded_user_id" + t.index ["url"], name: "index_posts_on_url", unique: true end create_table "settings", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| diff --git a/backend/spec/requests/posts_spec.rb b/backend/spec/requests/posts_spec.rb index 4e0cf11..817eec0 100644 --- a/backend/spec/requests/posts_spec.rb +++ b/backend/spec/requests/posts_spec.rb @@ -33,14 +33,14 @@ RSpec.describe 'Posts API', type: :request do let!(:hit_post) do Post.create!(uploaded_user: user, title: "hello spec world", - url: 'https://example.com/spec').tap do |p| + url: 'https://example.com/spec2').tap do |p| PostTag.create!(post: p, tag:) end end let!(:miss_post) do Post.create!(uploaded_user: user, title: "unrelated title", - url: 'https://example.com/spec2').tap do |p| + url: 'https://example.com/spec3').tap do |p| PostTag.create!(post: p, tag: tag2) end end @@ -158,6 +158,36 @@ RSpec.describe 'Posts API', type: :request do expect(json['tags']).to be_an(Array) expect(json['tags'][0]).to have_key('name') end + + context 'when url is blank' do + it 'returns 422' do + sign_in_as(member) + + post '/posts', params: { + title: 'new post', + url: ' ', + tags: 'spec_tag', # 既存タグ名を投げる + thumbnail: dummy_upload + } + + expect(response).to have_http_status(:unprocessable_entity) + end + end + + context 'when url is invalid' do + it 'returns 422' do + sign_in_as(member) + + post '/posts', params: { + title: 'new post', + url: 'ぼざクリタグ広場', + tags: 'spec_tag', # 既存タグ名を投げる + thumbnail: dummy_upload + } + + expect(response).to have_http_status(:unprocessable_entity) + end + end end describe 'PUT /posts/:id' do diff --git a/backend/spec/requests/wiki_spec.rb b/backend/spec/requests/wiki_spec.rb index 6b5fe1f..4541501 100644 --- a/backend/spec/requests/wiki_spec.rb +++ b/backend/spec/requests/wiki_spec.rb @@ -8,7 +8,9 @@ RSpec.describe 'Wiki API', type: :request do let!(:tn) { TagName.create!(name: 'spec_wiki_title') } let!(:page) do - WikiPage.create!(tag_name: tn, created_user: user, updated_user: user) + WikiPage.create!(tag_name: tn, created_user: user, updated_user: user).tap do |p| + Wiki::Commit.content!(page: p, body: 'init', created_user: user, message: 'init') + end end describe 'GET /wiki' do