Skip to content

Latest commit

 

History

History
executable file
·
467 lines (450 loc) · 21.4 KB

File metadata and controls

executable file
·
467 lines (450 loc) · 21.4 KB

あなたはフルスタックウェブエンジニアです。 下記の要件定義書を元にコードを書いてください。 返答はコードだけを書いてください。 コード内のエスケープは日本語で書いてください。

あなたはフルスタックウェブエンジニアです。 下記の要件定義書を読み、誤りがある点や曖昧な点を探して修正案を提示してください。 急いで回答する必要はありません。熟考し丁寧に回答してください。

開発背景

一人用のマイクロブログアプリケーションを開発する。

開発方針

  1. アジャイル開発を採用する。可能な限り早期にサービスを行いながら開発を続けていく
  2. オープンソースソフトウェアとしてセルフホストされることを想定して開発を行う

システム構成要件

  • Docker-Composeによって環境を構築する
  • NodeJSを使用する
    • バージョンは最新のLTSを使う
    • 軽量化の為にalpine版を使う
    • フロントエンドにはReactを使う
    • バックエンドにはExpressを使う
    • 装飾にはTailwindCSSを使う
    • environmentにより管理ユーザを設定する
  • PostgreSQLを使う
    • バージョン16を使う
    • environmentで管理ユーザを作成する
  • PGAdmin4を使う
    • バックエンドの管理用に同梱する
  • Valkeyを使う
    • バージョンは最新のLTSを使う

ロードマップ

バージョン0

  1. docker-composeが正常に機能するようにする
    • 各コンテナが正常に起動する
    • nodejsの各必要モジュールが正常にインストールされる
  2. 「エラー処理、検証要件」に記述したエラー、検証ページを作成する
    1. まずはHTMLだけのファイルによって行う
    2. 1が達成され次第、TailwindCSSによる簡易的な装飾を行う
    3. docker-composeの起動時にTailwindsCSSのビルドが行われるようにする
  3. 機能要件の「全体」及び「基本」の機能を実装する
    • TailwindCSSによる装飾を簡易的に行っていく
  4. 機能要件の「ログイン」機能を実装する
    • 必要になるAPIを実装する
    • Cookieの保管ができるようになる
  5. 機能要件の「日記」機能を実装する
    • 必要になるAPIを実装する
    • 新規投稿の作成ができる
    • 過去投稿の読み込みができる
    • Server-Sent Eventsによる新規投稿の読み込みができる

バージョン1

重要:【後日実装する予定の各機能を順次実装していく(バージョン0の進捗を確認して計画する)】

  1. postにタグが付与できるようにする機能を実装する
    • 必要になるAPIを実装する
  2. postの本文でMarkdown解釈ができるようにする
  3. postに画像を添付できるようにする
    • 必要になるAPIを実装する

機能要件

機能

セットアップ

  • 初回起動時にデータベース要件で定義した各テーブルとそのカラムを作成していく
  • 初回起動時にnodeのenvironmentに記述したuser_idとuser_passwordで管理ユーザを作成し、userテーブルの1行目に記述する

全体

  • SPAとして開発する
  • PCとモバイルに対応する
  • 画面の変更に対してURLを変更する
  • react-routerを使用して動作毎にブラウザバックできるようにする
  • ブラウザの設定に応じてライトテーマとダークテーマが切り替わる機能
後日実装するもの(順番はロードマップに記載する)
  • 投稿に画像を添付する機能
    • D&Dまたは端末のエクスプローラから選択すると画像がAPIを通してアップロードされる
    • アップロードされた画像を表示するURLが返却される
    • そのURLを投稿に添付する
    • URLに画像が含まれる場合、それを表示する

基本

  • ページは「日記」「ブログ」「検索」「ドライブ」「個人設定」の5つ
  • 「ドライブ」「個人設定」はログイン状態でのみ表示する
  • PC画面は左側に縦に並べたボタンを用意してページを移動する
  • モバイル画面はハンバーガーメニューによってページを移動する
  • 存在しないページにはHTTP404ページを表示する
後日実装するもの(順番はロードマップに記載する)
  • Xのポストをインポートする機能
  • Misskeyの投稿をインポートする機能
  • ActivityPubに対応する機能

ログイン(/login)

  • ログイン画面URL(/admin/login)に直接アクセスすることによって表示される
  • ログイン画面でアカウントの作成を行える
  • ユーザー名とパスワードはデータベースのuserテーブルに保管する
  • パスワードはハッシュ化をして保管する
    • argon2アルゴリズムを使用する
    • このとき、30000回のストレッチングを実施する
    • ソルトを生成し、user_saltに平文で保存する
  • ログインは失敗するごとにuser_failcountを1加算する。
  • user_failcountが10になるとロックされ、user_updateatをロック時刻で更新する
  • ロックから24時間でロックは解除され、user_failcountは0に戻る。
  • ログインするとセッションIDをCookieに保存して状態を維持する

ページ:日記(/diary)

  • postテーブルの情報を時系列順に表示する
  • Server-Sent Eventsによって新規投稿を表示する
    • 10分ごとに再接続をサーバに求めることで切断対策を行う
    • ※WebSocketを使用しないのは、投稿者は管理者だけでそれ以外のユーザは閲覧者であるため
  • スクロールによって過去の投稿を読み込んでいくことができる
  • 投稿はカード状に表示する
  • 投稿日時をカードの上部に表示する
  • 本文をカードの下部に表示する
  • ログインしている場合、画面上部に投稿作成フォームが表示される
  • 投稿作成フォームに本文を書いて「投稿」を押すとAPIを通して投稿が作成される
後日実装するもの(順番はロードマップに記載する)
  • URLにリンクを付与する
  • URLはカードでリンク先のタイトルを表示する
  • MarkDown記法を解釈して表示する
  • 200字以上は非表示にして「表示」ボタンで開くようにする
  • 半角#と半角スペースの間の文字をタグとして登録する機能
    • 投稿内で重複してもよい
    • 使用していけない文字は半角の記号とする。例=>-=!#$%^&*()<>??:";'{}[]|/,.
    • 判定されたtag文字は、/api/post_tag/checkにリスト形式で送信してpost_tag_idを取得する
    • 取得したpost_tag_idはリスト形式のまま/api/post/createに送信する(通常の投稿動作に添付する、ということ)
  • /diary/{tag}でそのタグが付与された投稿だけが表示される機能
  • 投稿中のタグに/diary/{tag}のURLを付与する機能
  • /diary/post_idでその投稿だけを表示する機能
  • 投稿に画像を添付する機能

ページ:検索(/search)

  • 「作成中」という文字
後日実装するもの(順番はロードマップに記載する)
  • 検索欄に検索したいワードを打ち込み検索ボタンを押すとpostテーブルを検索して表示する
  • AND検索とOR検索に対応する
  • タグ検索に対応する
  • 日時を指定した検索に対応する

ページ:ブログ(/blog)

  • 「作成中」という文字
後日実装するもの(順番はロードマップに記載する)
  • blogテーブルの情報を時系列順に表示する
  • 表示は1ページに20件ずつとする
  • 画面下部のボタンでページが移動できる
  • 記事はカード形式になっている
  • カードの上部にはタイトルが書かれている
  • カードの下部には本文の冒頭140字が表示される
  • カードの左側には画像があれば画像を正方向にクロップしたものをサムネイルとして表示する
  • カードは記事URLがリンクされている
  • 記事は/blog/blog_idの形式で読み込まれる
  • 記事はMarkdownを解釈して表示される
  • 記事の先頭には見出しを対象にした目次が設置される
  • アクセス回数を計測する機能
  • 記事の下部には該当ページのタイトルとURLをクリップボードにコピーするボタンを設置する
  • ログイン状態で/blog/manageにアクセスするとブログの投稿と更新と削除ができる
    • ログインしていない状態でアクセスするとHTTP401ページを表示する
    • 上部にある作成ボタンを押すと作成画面がポップアップする
      • ブログの投稿はタイトル、本文とタグ(任意)を入力すると作成できる
      • 画像を添付することができる
      • 左側に入力テキスト、右側にMarkdown解釈後の最終的に表示されるテキストを表示する
    • 下部には表形式で投稿済みの記事が表示される
      • 通常記事と固定記事がタブで分かれるようになっている
      • 右から、blog_id、タイトル、投稿日時、更新日時、アクセス回数、更新ボタン、削除ボタン、設定ボタンがある
      • blog_idは該当する記事のURLがリンクされている
      • 更新ボタンを押すと、更新画面がポップアップする
        • 仕様は作成画面と同様だが、タイトルと本文がすでに入力されている
      • 削除ボタンを押すと、確認画面がポップアップされ、承認を押すとblog_attitudeが000000000に更新されて非公開化される
      • 設定ボタンを押すと、設定画面がポップアップする
        • 固定URLを入力して更新を押すと固定ページとすることができる
        • 固定ページになると/{固定URL}でアクセスできるようになる
        • 固定URLがdiary,blog,drive,search,settings,既存の固定URLと重複する場合はエラーを返す
        • 固定ページになると/blog/blog_idでアクセスできなくなる
        • 「日記」や「検索」といった左側のボタンが追加されて、直接記事にアクセスできるようになる
        • 固定URLに使用していけない文字は半角の記号とする。例=>-=!#$%^&*()<>??:";'{}[]|/,.

ページ:ドライブ(/drive)

  • 「作成中」という文字
後日実装するもの(順番はロードマップに記載する)
  • ログイン状態でアクセスすることができる
  • ログインしていない状態でアクセスするとHTTP401ページを表示する
  • サーバにアップロードされた画像が表形式で表示される
    • 右から、file_id、投稿日時、更新ボタン、削除ボタンがある
    • 一番左には画像であれば正方形のサムネイルが表示される
    • 画像以外であれば灰色の四角が表示される
    • file_idは該当するファイルのURLがリンクされている
    • 更新ボタンを押すとファイルのD&Dまたは端末のエクスプローラから選択すると更新APIによってfile_idはそのままで置換される
    • 削除ボタンを押すと確認画面がポップアップされ、承認を押すとblog_attitudeが000000000に更新されて非公開化される

ページ:個人設定(/settings)

  • 「ログインしています」という文字を表示する
後日実装するもの(順番はロードマップに記載する)
  • userテーブルの各項目を編集できるようにする

API要件

  • 認証を、全てのエンドポイントについて実施する
  • 認証はexpress-sessionにより実施する
  • sessionidはValkeyにより保管などを行う

Server-Sent EventsのAPI

  • 接続のあったクライアントに対してpostテーブルの更新をpush通信で送信し続ける

postテーブルへのAPI

/api/post/create

  • 認証情報とpost_textの入力を受ける
  • 送信された以外の情報はサーバで処理する
  • post_idはマイクロ秒までのUNIX時間の後ろに6桁の乱数を結合して生成する
  • post_createat,post_updateatはUTC時刻のタイムスタンプを付与する
  • post_textはXSS攻撃への対策として、特別な記号文字(「<」、「>」、「&」等)を、HTMLエンティティ(「<」、「>」、「&」等)に置換する
  • 他の変数には'none_data'を代入する
  • postテーブルの各列にデータを入力していく
  • HTTP200を返答する
後日実装するもの(順番はロードマップに記載する)
  • 認証情報とpost_textの入力と共にpost_tag_idをリスト形式で受け取る
  • postテーブルのpost_tag列に収容する
  • posts-post_tags中間テーブルにリレーションする

blogテーブルへのAPI

/api/blog/create

  • 後日実装する
  • blog_textはXSS攻撃への対策として、特別な記号文字(「<」、「>」、「&」等)を、HTMLエンティティ(「<」、「>」、「&」等)に置換する
  • 認証情報とblog_textの入力と共にblog_tag_idをリスト形式で受け取る
  • blogテーブルのblog_tag列に収容する
  • blogs-blog_tags中間テーブルにリレーションする

/api/blog/read

  • 後日実装する

/api/blog/update

  • 後日実装する

/api/blog/delete

  • 後日実装する

driveテーブルへのAPI

/api/drive/create

  • file_idはマイクロ秒までのUNIX時間の後ろに6桁の乱数を結合して生成した数字に。先頭に「file_」を結合する

/api/drive/read

  • 後日実装する

/api/drive/update

  • 後日実装する

/api/drive/delete

  • 後日実装する

userテーブルへのAPI

/api/user/create

  • user_id,user_passwordの入力を受ける
  • user_idが、userテーブルに重複が無いかを確認する
  • user_idが、30字以内の半角英数字と半角の記号_だけであることを確認する
  • user_passwordが、半角英数字、全角英語、半角の記号-=!#$%^&*()<>??:";'{}[]|/,.だけであることを確認する
  • user_passwordを30000回ストレッチングしてハッシュ化する
    • アルゴリズムはargon2を使用する
    • ソルトを生成する
  • user_saltにソルトを保存する
  • user_createat,user_updateatはUTC時刻のタイムスタンプを付与する
  • 他の変数には'none_data'を代入する
  • userテーブルの各列にデータを入力していく
  • HTTP200を返答する

/api/user/login

  • user_id,user_passwordの入力を受ける
  • user_passwordをハッシュ化する
    • user_saltのソルトを読み込む
    • アルゴリズムはargon2を使用する
    • 30000回ストレッチングを行う
  • userテーブルのuser_id行のuser_password列と合致するかを確認する
  • express-sessionを使用してセッション管理を実施する
    • メモリリークの対策としてValkeyにデータを保管する
    • ログインからログアウトまで再ログインは求めないようにする

/api/user/certification

  • express-sessionによる認証を実施する

/api/user/read

  • 後日実装する

/api/user/update

  • 後日実装する

/api/user/delete

  • 後日実装する

post_tagテーブルへのAPI

/api/post_tag/check

  • 認証情報とpost_tag_textの入力を受ける(リスト形式)
  • リスト形式の場合は一つずつ処理する
  • post_tag_idはマイクロ秒までのUNIX時間の後ろに6桁の乱数を結合して生成した数字に。先頭に「post_tag_」を結合する
  • post_tag_textは20字以内であることを確認する
  • post_tag_textに使用していけない文字である半角の記号がないことを確認する。例=>-=!#$%^&*()<>??:";'{}[]|/,.
  • post_tag_textがpost_tagテーブルで重複しないことを確認して書き込みを行う
  • 重複していた場合はそのpost_tag_idを取得する
  • 受け取ったpost_tag_textに対応するpost_tag_idをHTTP200と共に返却する。(リスト形式)

/api/post_tag/read

  • 認証情報とpost_tag_textの入力を受ける
  • post_tag_textをpost_tagテーブルで検索する
  • 存在していればpost_tag_idとHTTP200を返却する
  • 存在していなければHTTP400を返却する

blog_tagテーブルへのAPI

/api/blog_tag/check

  • 認証情報とblog_tag_textの入力を受ける(リスト形式)
  • リスト形式の場合は一つずつ処理する
  • blog_tag_idはマイクロ秒までのUNIX時間の後ろに6桁の乱数を結合して生成した数字に。先頭に「blog_tag_」を結合する
  • blog_tag_textは20字以内であることを確認する
  • blog_tag_textに使用していけない文字である半角の記号がないことを確認する。例=>-=!#$%^&*()<>??:";'{}[]|/,.
  • blog_tag_textがpost_tagテーブルで重複しないことを確認して書き込みを行う
  • 重複していた場合はそのblog_tag_idを取得する
  • 受け取ったblog_tag_textに対応するblog_tag_idをHTTP200と共に返却する。(リスト形式)

/api/blog_tag/read

  • 認証情報とblog_tag_textの入力を受ける
  • blog_tag_textをpost_tagテーブルで検索する
  • 存在していればblog_tag_idとHTTP200を返却する
  • 存在していなければHTTP400を返却する

UI要件

  • ボタンは全て両端を丸めたものを使用する
  • CSSデザインはTailwindCSSのデフォルトを使用する

データベース要件

postテーブル

  • post_id
    • numeric型
  • user_id
    • text型
  • post_text
    • text型
  • post_createat
    • timestamp型
  • post_updateat
    • timestamp型
  • post_tag
    • text型
    • リスト形式でそのまま収容する
  • post_file
    • text型
    • 添付ファイルのfile_idを記述する。半角コンマで区切る。
  • post_attitude
    • numeric型
    • 閲覧制限に将来使用する。1を代入する

blogテーブル

  • blog_id
    • numeric型
  • user_id
    • text型
  • blog_title
    • text型
  • blog_text
    • text型
  • blog_tag
    • text型
    • リスト形式でそのまま収容する
  • blog_createat
    • timestamp型
  • blog_updateat
    • timestamp型
  • blog_file
    • text型
    • 添付ファイルのfile_idを記述する。半角コンマで区切る。
  • blog_attitude
    • numeric型
    • 閲覧制限に将来使用する。1を代入する
  • blog_fixedurl
    • text型

driveテーブル

  • file_id
    • numeric型
  • user_id
    • text型
  • file_size
    • numeric型
  • file_format
    • character varying型
    • 拡張子を保存する
  • file_createat
    • timestamp型
  • file_updateat
    • timestamp型
  • file_attitude
    • numeric型
    • 閲覧制限に将来使用する。1を代入する

userテーブル

  • user_id
    • text型
    • ユーザが設定した任意の文字を収納する
    • 30字以内の半角英数字と半角の記号_だけ
  • user_password
    • text型
    • ハッシュ化して保存する
  • user_salt
    • text型
  • user_birth
    • date型
    • 将来使用する。1970年1月1日を代入する
  • user_icon
    • text型
    • 画像のfile_idを保管する
    • 将来使用する
  • user_mail
    • text型
    • メールアドレスを保存する
    • 将来使用する
  • user_attitude
    • numeric型
    • ロール管理に将来使用する。0を代入する
  • user_prof
    • text型
    • プロフィールに将来使用する。
  • user_createat
    • timestamp型
  • user_updateat
    • timestamp型
  • user_failcount
    • smallserial型
    • 総当たり攻撃に対抗するために使用する。
    • 10を超えるとログインの試みを24時間停止する
    • 10を超えた際にuser_updateatに現在時刻を入力して停止時間を判定する
  • user_token
    • text型
    • Botのために将来使用する。

post_tagテーブル

  • post_tag_id
    • text型
  • post_tag_text
    • text型

posts-post_tagsテーブル(中間テーブル)

  • post_id
    • numeric型
  • post_tag_id
    • text型

blog_tagテーブル

  • blog_tag_id
    • text型
  • blog_tag_text
    • text型

blogs-blog_tagsテーブル(中間テーブル)

  • blog_id
    • numeric型
  • blog_tag_id
    • text型

フォルダ構成要件

※一例として下記のように示すが、規模に応じて臨機応変に対応する。特にコードは可読性と拡張を優先して分割する

  • フロントエンドに関するデータはsrc/frontend/に保存する。

    • Reactのコンポーネントはcomponents/に保管する
  • バックエンドの処理はsrc/backend/に保存する

    • 特にAPIに関する処理はsrc/backend/apiの中に保管する
  • ユーザのデータはdrive/の中に保管する ./ ├ src/ ├ router/ ├ main.jsx ├ frontend/

      	├ tailwind.config.js
    
      ├ backend/
      	├ api/
      		├ api.js		
      ├ index.js
    

/components /drive /docker ├ docker-compose.yml/ ├ .env/ /node_modules /logs .dockerignore .gitignore package.json package-lock.json

エラー処理、検証要件

  • ページ:HTTP401
    • HTTPコード:401
    • URL:./401
    • 画面に表示する文章:「このページは管理者専用です。」
    • 下部にルートページへのリンクとナビゲーションボタンを設置する
  • ページ:HTTP404
    • HTTPコード:404
    • URL:./404
    • 画面に表示する文章:「このページは存在しません。誤りである場合は管理者へ連絡してください」
    • 下部にルートページへのリンクとナビゲーションボタンを設置する
  • ページ:test
    • HTTPコード:200
    • URL:./test
    • 画面に表示する文章:「サイトへの接続は正常です」
    • 下部にルートページへのリンクとナビゲーションボタンを設置する