はじめに
naritomoと申します。
本職はインフラエンジニアを行っています。
Laravelつぶやきサイトについてテスト公開してみたので、
ソースも公開したいと思います。
この手順で構築されるものはLaravel13 + Vite対応になります。
つぶやきアプリ
*開発中のため予告なしに内容の消去、公開停止を実施いたします。
いろいろアドバイスいただけると嬉しいです。
立ち上げ概要
tubuyakisaitoソースを入手し、
ローカル環境でデプロイするものとなります。
必要なソースは以下にまとめてあります。
dockerソース
つぶやきサイトソース
画面イメージ
参考書籍
事前準備
mac+DockerCompose+vscode+gitでの環境を構築してること。
*Windowsでもwsl2+Ubuntuで実施可能。
環境構築手順
ベースリポジトリをクローンする
git clone -b laravel13 https://github.com/naritomo08/laravel_docker.git laraveldocker
cd laraveldocker
rm -rf .git
git clone -b laravel13 https://github.com/naritomo08/laravel9tubu-public.git backend
cd backend
rm -rf .git
環境構築用のシェルスクリプトを実行する
cd ..
chmod u+x build_env.sh && ./build_env.sh
サイト設定を行う
PHPコンテナログイン
docker-compose exec app /bin/bash
Laravelキャッシュクリア
php artisan cache:clear
php artisan config:clear
php artisan route:clear
php artisan view:clear
DB初期化
php artisan migrate:fresh
DB初期化せずマイグレートのみする場合
php artisan migrate
Seeder作成管理者アカウント設定
php artisan db:seed --class=UsersSeeder
各種サイト確認する
laravel
利用規約・プライバシーポリシー・お問い合わせ
利用規約とプライバシーポリシーはMarkdownファイルで管理しています。画面表示時にMarkdownからHTMLへ変換し、変換後のHTMLをMarkdown内容のハッシュ付きキーでキャッシュします。
| 表示ページ | Markdownファイル |
|---|---|
/terms |
backend/resources/markdown/terms.md |
/privacy |
backend/resources/markdown/privacy.md |
お問い合わせはログイン後に表示される /contact から送信できます。ログイン中ユーザーのユーザー名とメールアドレスを固定表示し、問い合わせ内容を入力すると、管理者宛にメールがキューで送信されます。
管理者アドレスを変更したい場合は、backend/.env の ADMIN_EMAIL に送信先メールアドレスを指定してください。未設定時は admin@tubuyaki.com 宛に送信されます。
本文を変更する場合は、該当するMarkdownファイルを編集してビルドしてください。Markdownの内容が変わるとキャッシュキーも変わるため、通常はアプリケーションキャッシュの削除は不要です。
adminer(DB管理ツール)
- ログイン情報
- サーバ: db
- ユーザ名: phper
- パスワード: secret
- データベース: laravel_local
mailhog(メールサーバ)
コンテナ起動する方法
docker-compose.ymlが存在するフォルダーで以下のコマンドを実行する。
docker-compose up -d
コンテナ停止する方法
docker-compose.ymlが存在するフォルダーで以下のコマンドを実行する。
docker-compose stop
コンテナ削除する方法
docker-compose.ymlが存在するフォルダーで以下のコマンドを実行する。
docker-compose down
起動中のコンテナに入る
PHPコンテナ
docker-compose exec app /bin/bash
DBコンテナ
docker-compose exec db /bin/bash
その他
つぶやきサイトソース変更して反映したい際は以下コマンドを実施すること。
docker-compose build && docker-compose up -d
スケジュール実行の確認
スケジュール実行プロセスの確認:
docker-compose logs -f scheduler
キューワーカーの確認:
docker-compose logs -f queue
キューはRedisに保管されます。設定変更後に反映されない場合は、関連コンテナを再作成して設定キャッシュを削除してください。
docker-compose up -d app scheduler queue
docker-compose exec app php artisan config:clear
docker-compose exec queue php artisan queue:restart
手動で1回だけスケジューラ評価を走らせたい場合:
docker-compose exec app php artisan schedule:run
LaravelTest
PHPコンテナで動かす。
docker-compose exec app /bin/bash
php artisan optimize:clear
ユニットテスト
php artisan test
ブラウザテスト
php artisan dusk
php artisan testはテスト用設定.env.testingを使い、db.test上のlaravel_testデータベースに接続します。
テスト実行前にphp artisan optimize:clearを実行し、通常アプリの設定キャッシュが残っていない状態で動かしてください。
テストコマンドごとの実行対象
-
php artisan testtests/Unittests/Feature
-
php artisan dusktests/Browser
php artisan test で実行されるテスト(169テスト)
tests/Unit/ExampleTest.php
tests/Unit/Services/TweetImageServiceTest.php
tests/Unit/Services/TweetQueryServiceTest.php
tests/Unit/Services/TweetServiceTest.php
tests/Unit/Services/UserDeletionServiceTest.php
tests/Unit/View/Components/Tweet/FormattedContentTest.php
tests/Feature/AccountTest.php
tests/Feature/Admin/UserManagementTest.php
tests/Feature/Auth/AuthenticationTest.php
tests/Feature/Auth/GoogleAuthTest.php
tests/Feature/Auth/EmailVerificationTest.php
tests/Feature/Auth/PasswordConfirmationTest.php
tests/Feature/Auth/PasswordResetTest.php
tests/Feature/Auth/RegistrationTest.php
tests/Feature/Console/DeleteUnverifiedUsersTest.php
tests/Feature/Console/SendDailyTweetCountMailTest.php
tests/Feature/ContactTest.php
tests/Feature/ExampleTest.php
tests/Feature/LegalDocumentTest.php
tests/Feature/Tweet/ContentLengthTest.php
tests/Feature/Tweet/CreateTest.php
tests/Feature/Tweet/DeleteTest.php
tests/Feature/Tweet/LatestTest.php
tests/Feature/Tweet/LikeTest.php
tests/Feature/Tweet/ProtectionTest.php
tests/Feature/Tweet/ScheduledPostTest.php
tests/Feature/Tweet/SearchTest.php
tests/Feature/Tweet/SecretModeTest.php
tests/Feature/Tweet/UpdateTest.php
| ファイル | テスト概要 |
|---|---|
tests/Unit/ExampleTest.php |
true が true であることだけを確認するサンプルテスト。 |
tests/Unit/Services/TweetImageServiceTest.php |
つぶやき画像の保存・紐付けと、画像削除時のファイル・中間テーブル・画像レコード削除を検証。 |
tests/Unit/Services/TweetQueryServiceTest.php |
つぶやき一覧取得時のいいね状態・件数の一括付与、一覧・検索・新着取得・いいね状態取得で削除受付済みユーザーのいいねを除外して同じ件数になることを検証。 |
tests/Unit/Services/TweetServiceTest.php |
TweetService::checkOwnTweet の自分の投稿判定を検証。 |
tests/Unit/Services/UserDeletionServiceTest.php |
ユーザー削除時に本人のつぶやき、いいね、つぶやき画像が削除され、他ユーザーのデータが残ること、削除受付時に削除予定フラグを立てて削除Jobを一度だけ投入することを検証。 |
tests/Unit/View/Components/Tweet/FormattedContentTest.php |
ツイート本文表示コンポーネントのURLリンク化、改行反映、HTMLエスケープを検証。 |
tests/Feature/AccountTest.php |
アカウント設定リンク・画面のメール認証済みユーザー限定表示、プロフィール更新、メール変更時の再認証、パスワード更新、管理者本人の退会不可、通知メール設定、Google連携欄、本人統計、本人の予約投稿一覧の表示・動的取得・編集・削除、退会時に削除受付フラグを立てて削除Jobを投入しログアウトすることを検証。 |
tests/Feature/Admin/UserManagementTest.php |
管理者画面のメールアドレス表示、管理者自身の2FA有効時だけ可能な他ユーザーのメールアドレス変更と認証メール送信、自分自身とSeeder固定管理者のメール変更拒否、複数管理者の昇格/降格、自己権限変更拒否、Seeder固定管理者の維持・設定値反映・重複時の停止、管理者削除拒否、一般ユーザー削除受付時の削除Job投入、集計・ユーザー一覧の表示と動的取得、予約投稿一覧の表示・動的取得、管理者自身の2FA有効時だけ可能な管理者画面からの予約投稿削除、通知メール設定表示、Google連携表示、2FA有効状態表示、管理者ナビ・管理者画面アクセス監視、管理者ステータスAPIの管理者権限・2FA状態返却、管理者自身の2FA未設定時のユーザー関連操作拒否、管理者による他ユーザーの2FAリセット、自分自身とSeeder固定管理者の2FAリセット拒否、非管理者の操作拒否を検証。 |
tests/Feature/Auth/AuthenticationTest.php |
ログイン画面表示、正しい認証でログイン成功、2FA有効ユーザーが2FAチャレンジへ遷移すること、リカバリーコードで2FAログインを完了できること、2FAチャレンジのレート制限がユーザー単位で分離されること、2FA完了後も非管理者が古い管理画面遷移先へ戻されないこと、誤パスワードおよび削除受付済みユーザーのログイン失敗を検証。 |
tests/Feature/Auth/GoogleAuthTest.php |
Google連携、連携済みアカウントでのGoogleログイン、未連携メールでの拒否、連携解除、Google API失敗時のエラー表示を検証。 |
tests/Feature/Auth/EmailVerificationTest.php |
メール認証画面、認証状態API、未認証/認証済みユーザーの監視表示、未認証ユーザーでは投稿フォームを表示しないこと、登録直後とメール変更後で未認証通知の削除警告表示が切り替わること、署名付きURLでの認証成功、ログインなし・別ユーザー認証中・共有IPの連続アクセス・ホスト差異ありでも認証できること、無効な認証リンクでは認証されないことを検証。 |
tests/Feature/Auth/PasswordConfirmationTest.php |
パスワード確認画面表示、正しい/誤ったパスワードでの確認結果を検証。 |
tests/Feature/Auth/PasswordResetTest.php |
再設定リンク送信、再設定画面表示、トークンを使ったパスワード再設定を検証。 |
tests/Feature/Auth/RegistrationTest.php |
ユーザー登録画面表示、新規登録後の認証状態・遷移先、認証済みユーザーへの紹介メール送信を検証。 |
tests/Feature/Console/DeleteUnverifiedUsersTest.php |
登録後1時間を過ぎた未認証ユーザーの削除と、既存ユーザーのメール変更後アカウントが削除対象外であることを検証。 |
tests/Feature/Console/SendDailyTweetCountMailTest.php |
日次送付メールに各ユーザーのつぶやき数・いいね数が含まれること、未認証ユーザーや通知無効ユーザーへ送信されないことを検証。 |
tests/Feature/ContactTest.php |
問い合わせ画面のログイン必須、ログイン済みユーザー情報の固定表示、管理者アドレスへの問い合わせメールのキュー投入、バリデーション失敗時に送信されないことを検証。 |
tests/Feature/ExampleTest.php |
/tweet が 200 OK を返すことを確認する基本スモークテスト。 |
tests/Feature/LegalDocumentTest.php |
利用規約・プライバシーポリシーのMarkdown表示、Markdown内容のハッシュ付きキーによる法務文書HTMLキャッシュ利用、ゲスト画面・通常画面で共通リンクと問い合わせリンクが表示されることを検証。 |
tests/Feature/Tweet/ContentLengthTest.php |
つぶやき作成・編集で設定値に基づく最大文字数バリデーションが効くこと、投稿フォーム・編集フォームに最大文字数表示と動的カウント用の設定が出力されることを検証。 |
tests/Feature/Tweet/CreateTest.php |
Seeder固定管理者が作成したつぶやきはSeeder作成扱いになり、通常管理者が作成したつぶやきはSeeder作成扱いにならないことを検証。 |
tests/Feature/Tweet/DeleteTest.php |
ログインユーザーが投稿削除後に一覧へ遷移すること、検索画面から削除した場合は検索条件を維持して戻り通知が出ること、Seeder作成つぶやきはSeeder固定管理者本人以外の管理者が削除できないこと、既存のSeeder固定管理者つぶやきを削除保護対象に自動反映できることを検証。 |
tests/Feature/Tweet/LatestTest.php |
/tweet/latest の新着取得、after_id=0 時の返却件数上限、ユーザー名・画像更新時の差分HTML返却を検証。 |
tests/Feature/Tweet/LikeTest.php |
同一ユーザー・同一投稿へのいいね追加が競合しても正常に is_liked と like_count を返すことを検証。 |
tests/Feature/Tweet/ProtectionTest.php |
Seeder固定管理者だけが一般ユーザーのつぶやきを保護/解除できること、Seeder固定管理者のつぶやきは保護対象外であること、管理者自身の2FA未設定時はトップ画面に警告が表示され、他ユーザーのつぶやき保護・削除とメニュー表示が抑止されること、保護されたつぶやきはSeeder固定管理者以外が編集・削除できないこと、Seeder固定管理者は保護済みつぶやきを削除できるが編集できないこと、保護表記とメニュー表示を検証。 |
tests/Feature/Tweet/ScheduledPostTest.php |
予約投稿の作成、予約時刻までの一覧・検索・新着取得からの非表示、予約時刻後の表示、予約日時を表示時刻として扱うこと、予約日時順で並ぶことを検証。 |
tests/Feature/Tweet/SearchTest.php |
つぶやき検索画面のログイン必須、本文検索、空検索0件、ページネーション、空ページ時の最終ページ移動、ユーザー検索時は入力文字列では絞り込まず選択ユーザーで絞り込むこと、ユーザー選択候補の表示・JSON取得、登録直後の未認証ユーザー除外とメール変更後の未認証ユーザー表示、user:"" を通常キーワードとして扱うことを検証。 |
tests/Feature/Tweet/SecretModeTest.php |
シークレットモードのつぶやきが投稿者本人と管理者だけに表示されること、作成・編集時に設定が保存されること、検索・新着取得・いいね状態取得・いいね操作で第三者に参照されないことを検証。 |
tests/Feature/Tweet/UpdateTest.php |
つぶやき編集時の画像追加・削除、画像合計4枚までのバリデーション、検索画面から編集した場合は検索条件を維持して戻り通知が出ること、公開済み投稿は予約投稿へ戻せず、未公開の予約投稿だけ予約日時を編集できることを検証。 |
php artisan dusk で実行されるテスト(1テスト)
tests/Browser/LoginTest.php
| ファイル | テスト概要 |
|---|---|
tests/Browser/LoginTest.php |
ブラウザでログイン操作を行い、/tweet への遷移と表示文言を確認するE2Eテスト。 |
Google認証
通常登録したユーザーは、ログイン後の /account から Google アカウントを連携できます。連携後はログイン画面の Googleでログイン から同じアカウントへ入れます。
Google OAuth を使うには、Google Cloud Console で OAuth クライアントを作成し、以下を backend/.env に設定してください。
GOOGLE_CLIENT_ID=your-google-client-id
GOOGLE_CLIENT_SECRET=your-google-client-secret
GOOGLE_REDIRECT_URI=http://127.0.0.1:8080/auth/google/callback
Google 側の OAuth 設定にも、環境に対応した同じリダイレクト URI を登録します。
http://127.0.0.1:8080/auth/google/callback
本実装では、Google ログイン時に自動で新規登録や自動連携はしません。既存アカウントへ Google を紐付けたユーザーのみ、Google 認証でログインできます。
以下のページを参考にgoogle認証情報を入手してください。
2段階認証
2段階認証は Laravel Fortify を使用しています。メール認証とは別のログイン保護で、パスワード認証後に認証アプリの6桁コード、またはリカバリーコードを求めます。
初期状態では、既存ユーザーと新規登録ユーザーのどちらも2段階認証は無効です。ログイン済みかつメール認証済みユーザーが /account の「2段階認証」から任意で有効化します。
有効化の流れ:
アカウント設定
2段階認証の「有効化する」
現在のパスワード確認
QRコードを認証アプリで読み取り
6桁コードを入力して「有効化を完了する」
有効化後は、通常ログインと Google ログインのどちらでも /two-factor-challenge に遷移し、認証コードまたはリカバリーコードの入力が必要になります。
無効化やリカバリーコード再生成も /account の「2段階認証」から行います。無効化時も現在のパスワード確認を求めます。
利用者が認証アプリとリカバリーコードの両方を失った場合、管理者は /admin/users のユーザー一覧から対象ユーザーの2FAをリセットできます。リセットすると、対象ユーザーの2FAシークレット、リカバリーコード、確認日時が削除され、次回ログイン時は2FAなしでログインできます。
管理者画面でユーザー関連操作を行うには、管理者自身の2FA有効化が必要です。2FA未設定の管理者はユーザー一覧や予約投稿一覧の確認はできますが、メールアドレス変更、管理者権限変更、ユーザー削除、他ユーザーの2FAリセット、管理者画面からの予約投稿削除はできません。管理者が他ユーザーのつぶやきを保護・削除する場合も、管理者自身の2FA有効化が必要です。管理者が自分自身のメールアドレス変更や2FAリセットを管理者画面から行うこともできません。Seederで作成された固定管理者のメールアドレスと2FAは、他の管理者からも変更・リセットできません。
管理者が他ユーザーのメールアドレスを変更した場合、対象ユーザーのメール認証状態は未認証に戻り、新しいメールアドレス宛に認証メールが送信されます。
主な関連ファイル:
| ファイル | 内容 |
|---|---|
config/fortify.php |
Fortifyの2段階認証機能を有効化。QRコード確認必須、操作前パスワード確認必須。 |
app/Providers/RouteServiceProvider.php |
2FAチャレンジのレート制限を、ログイン途中のユーザーID単位で定義。 |
app/Providers/FortifyServiceProvider.php |
Fortifyの2FA画面とログイン完了レスポンスをアプリ側に紐付け。 |
app/Models/User.php |
TwoFactorAuthenticatable trait を追加し、2FA有効判定やQRコード生成を利用。 |
routes/auth.php |
2FAチャレンジ、有効化、確認、無効化、リカバリーコード再生成のルート。 |
routes/web.php |
管理者によるユーザー2FAリセットのルート。 |
resources/views/account/index.blade.php |
2FAの有効化、QRコード表示、確認、無効化UI。 |
resources/views/admin/users/_rows.blade.php |
管理者画面の2FA状態表示と2FAリセット操作。 |
resources/views/auth/two-factor-challenge.blade.php |
ログイン時の2FAコード入力画面。 |
管理者画面
管理者画面にアクセスしたい際は
初期値では以下のアカウントで入ることができます。
ユーザ:admin@tubuyaki.com
パスワード:test
トップ画面にリンクが乗り、各アカウント情報確認と削除が行えます。
管理者アカウント情報は backend/.env の以下の値で変更できます。
設定ファイル:
SEED_ADMIN_NAME=admin
SEED_ADMIN_EMAIL=admin@tubuyaki.com
SEED_ADMIN_PASSWORD=test
適用:
docker-compose build && docker-compose up -d
docker-compose exec app /bin/bash
php artisan db:seed --class=UsersSeeder
UsersSeeder を再実行すると、Seeder固定管理者として選ばれたユーザーの以下の項目が backend/.env の設定値で上書きされます。
- ユーザー名
- メールアドレス
- パスワード
- メール認証状態
- 管理者フラグ
- Seeder固定管理者フラグ
一方で、Google連携情報と2段階認証情報は UsersSeeder では更新・削除しません。既存のSeeder固定管理者でGoogle連携や2段階認証を有効化している場合、Seederを再実行してもそれらの設定は残ります。
UsersSeeder が上書き対象にする既存ユーザーは、必ず is_seed_admin = true のユーザーです。SEED_ADMIN_NAME や SEED_ADMIN_EMAIL が通常ユーザーと重複している場合、その通常ユーザーをSeeder固定管理者へ昇格せず、Seeder処理はエラーで停止します。
複数つぶやきをテスト的に入れたい場合
以下のファイルで60件の画像リンク付き書き込みを追加できます。
実施するたびに追記が可能。
設定ファイル:
database/seeeders/TweetsSeeder.php
適用(事前にUsersSeederを実施していること。):
docker-compose exec app /bin/bash
php artisan db:seed --class=TweetsSeeder
一般ユーザーをテスト的に入れたい場合
以下のファイルで一般ユーザーを追加できます。
作成されるユーザー:
ユーザ:test@example.com
パスワード:password
ユーザー名:test-user
設定ファイル:
database/seeders/TestUserSeeder.php
適用:
docker-compose exec app /bin/bash
php artisan db:seed --class=TestUserSeeder
一般ユーザーに60件のつぶやきを入れたい場合
以下のファイルで test-user に60件の画像リンク付き書き込みを追加できます。
test-user が存在しない場合は、先に一般ユーザーも作成されます。
設定ファイル:
database/seeders/TestUserTweetsSeeder.php
適用:
docker-compose exec app /bin/bash
php artisan db:seed --class=TestUserTweetsSeeder
全消しして管理者、一般ユーザー、つぶやきを入れる場合
docker-compose exec app /bin/bash
rm -rf storage/app/public/images/*
php artisan migrate:fresh
php artisan db:seed
一旦消して初期状態にしたい場合は以下のようにする。
UsersSeeder(管理者作成)は必ず適用すること。
docker-compose exec app /bin/bash
rm -rf storage/app/public/images/*
php artisan migrate:fresh
php artisan db:seed --class=UsersSeeder
つぶやき数通知メール時間変更
backend/.env の DAILY_TWEET_COUNT_MAIL_TIME を変更する。
時刻は HH:MM 形式で指定する。
DAILY_TWEET_COUNT_MAIL_TIME=07:00
適用:
docker-compose build && docker-compose up -d
つぶやき文字数の変更
backend/.env の TWEET_CONTENT_MAX_LENGTH を変更する。
文字数は 200 形式で指定する。
DB側が250文字までとしているため、それ以内で設定する。
TWEET_CONTENT_MAX_LENGTH=200
適用:
docker-compose build && docker-compose up -d
npm依存の脆弱性対応
フロントエンド依存の脆弱性は package-lock.json で固定されたバージョンに対して確認します。
Dockerfile の npm ci --no-audit --no-fund は監査を省略するだけで、脆弱性を自動修正しません。
ビルドの再現性を保つため、Docker build 中に npm audit fix を実行せず、手動で package-lock.json を更新してコミットしてください。
cd backend
# 脆弱性を確認
npm audit --audit-level=moderate
# 修正可能な依存を package-lock.json に反映
npm audit fix
# 修正後に脆弱性が残っていないか確認
npm audit --audit-level=moderate
# フロントエンドのビルド確認
npm run build
npm audit fix 後は、主に package-lock.json が更新されます。
npm audit fix --force はメジャーバージョン更新を含む可能性があるため、通常の npm audit fix で解消できない場合だけ、影響範囲を確認してから実行してください。
APP_KEY対応について
本ソースを外からアクセスできる環境に持っていくときは以下のコマンドでAPP_KEYの内容を控えて、.envに更新してください。
docker-compose exec app /bin/bash
php artisan key:generate
cat .env
→APP_KEYの行を控える。
exit
.env.exampleへ反映し、以下のコマンドで適用してください。
chmod u+x build_env.sh && ./build_env.sh
Laravel13 + Postgresql対応版
本アプリはMySQL対応ですが、以下のリポジトリでpostgresql対応にもなります。
Laravel9対応版
もともとこの記事でも提示してました、Laravel9対応版は以下を参照してください。
Laravel9 + Postgresql対応版
本アプリはMySQL対応ですが、以下のリポジトリでpostgresql対応にもなります。
ただし、上記のLaravel9対応版になります。
おわりに
これが他のLaravel9を扱う方の助けになればと思います。
