diff --git a/backend/app/controllers/wiki_assets_controller.rb b/backend/app/controllers/wiki_assets_controller.rb new file mode 100644 index 0000000..dfb8cf8 --- /dev/null +++ b/backend/app/controllers/wiki_assets_controller.rb @@ -0,0 +1,38 @@ +require 'digest' + + +class WikiAssetsController < ApplicationController + def index + page_id = params[:wiki_page_id].to_i + page = WikiPage.find_by(id: page_id) + return head :not_found unless page + + render json: page.assets + end + + def create + return head :unauthorized unless current_user + return head :forbidden unless current_user.gte_member? + + wiki_page_id = params[:wiki_page_id].to_i + page = WikiPage.find_by(id: wiki_page_id) + return head :not_found unless page + + file = params[:file] + return head :bad_request if file.blank? + + page.with_lock do + no = page.next_asset_no + alt_text = params[:alt_text].presence + sha256 = Digest::SHA256.file(file.tempfile.path).digest + + asset = WikiAsset.new(wiki_page_id:, no:, alt_text:, sha256:, created_by_user: current_user) + asset.file.attach(file) + asset.save! + + page.update!(next_asset_no: no + 1) + end + + render json: asset + end +end diff --git a/backend/app/models/wiki_asset.rb b/backend/app/models/wiki_asset.rb new file mode 100644 index 0000000..4fb5d66 --- /dev/null +++ b/backend/app/models/wiki_asset.rb @@ -0,0 +1,10 @@ +class WikiAsset < ApplicationRecord + self.primary_key = :wiki_page_id, :no + + belongs_to :wiki_page + belongs_to :created_by_user, class_name: 'User' + + has_one_attached :file + + validates :file, presence: true +end diff --git a/backend/app/models/wiki_page.rb b/backend/app/models/wiki_page.rb index 1573127..b752599 100644 --- a/backend/app/models/wiki_page.rb +++ b/backend/app/models/wiki_page.rb @@ -15,6 +15,8 @@ class WikiPage < ApplicationRecord foreign_key: :redirect_page_id, dependent: :nullify + has_many :assets, class_name: 'WikiAsset', dependent: :destroy + belongs_to :tag_name validates :tag_name, presence: true diff --git a/backend/config/routes.rb b/backend/config/routes.rb index 57eb470..e53bef5 100644 --- a/backend/config/routes.rb +++ b/backend/config/routes.rb @@ -41,6 +41,8 @@ Rails.application.routes.draw do get :exists get :diff end + + resources :assets, controller: :wiki_assets, only: [:index, :create] end resources :posts, only: [:index, :show, :create, :update] do diff --git a/backend/db/migrate/20260323192300_create_wiki_assets.rb b/backend/db/migrate/20260323192300_create_wiki_assets.rb new file mode 100644 index 0000000..1a4e875 --- /dev/null +++ b/backend/db/migrate/20260323192300_create_wiki_assets.rb @@ -0,0 +1,16 @@ +class CreateWikiAssets < ActiveRecord::Migration[8.0] + def change + create_table :wiki_assets, primary_key: [:wiki_page_id, :no] do |t| + t.references :wiki_page, null: false, foreign_key: true, index: false + t.integer :no, null: false + t.string :alt_text + t.column :sha256, 'binary(32)', null: false + t.references :created_by_user, null: false, foreign_key: { to_table: :users } + t.timestamps + end + + add_index :wiki_assets, [:wiki_page_id, :sha256], unique: true + + add_column :wiki_pages, :next_asset_no, :integer, null: false, default: 1 + end +end diff --git a/backend/db/schema.rb b/backend/db/schema.rb index 6a2096b..7dbc388 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_03_17_015000) do +ActiveRecord::Schema[8.0].define(version: 2026_03_23_192300) 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 @@ -239,6 +239,18 @@ ActiveRecord::Schema[8.0].define(version: 2026_03_17_015000) do t.datetime "updated_at", null: false end + create_table "wiki_assets", primary_key: ["wiki_page_id", "no"], charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + t.bigint "wiki_page_id", null: false + t.integer "no", null: false + t.string "alt_text" + t.binary "sha256", limit: 32, null: false + t.bigint "created_by_user_id", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["created_by_user_id"], name: "index_wiki_assets_on_created_by_user_id" + t.index ["wiki_page_id", "sha256"], name: "index_wiki_assets_on_wiki_page_id_and_sha256", unique: true + end + create_table "wiki_lines", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.string "sha256", limit: 64, null: false t.text "body", null: false @@ -254,6 +266,7 @@ ActiveRecord::Schema[8.0].define(version: 2026_03_17_015000) do t.datetime "created_at", null: false t.datetime "updated_at", null: false t.datetime "discarded_at" + t.integer "next_asset_no", default: 1, null: false t.index ["created_user_id"], name: "index_wiki_pages_on_created_user_id" t.index ["discarded_at"], name: "index_wiki_pages_on_discarded_at" t.index ["tag_name_id"], name: "index_wiki_pages_on_tag_name_id", unique: true @@ -320,6 +333,8 @@ ActiveRecord::Schema[8.0].define(version: 2026_03_17_015000) do add_foreign_key "user_ips", "users" add_foreign_key "user_post_views", "posts" add_foreign_key "user_post_views", "users" + add_foreign_key "wiki_assets", "users", column: "created_by_user_id" + add_foreign_key "wiki_assets", "wiki_pages" add_foreign_key "wiki_pages", "tag_names" add_foreign_key "wiki_pages", "users", column: "created_user_id" add_foreign_key "wiki_pages", "users", column: "updated_user_id"