Flutter Web APP + Amazon S3 Bucket

# アホまぬけ登録画面
## 定数
```bash
BACKET_NAME=aho-maru.dashi-user && REGION=us-east-1
APP_NAME=aho_marudashi_app && APP_ROOT=~/app/flutter/$APP_NAME
```
* バケットウェブサイトエンドポイントURL
  ```bash
  BACKET_URL=http://$BACKET_NAME.s3-website-$REGION.amazonaws.com && echo $BACKET_NAME
  ```
## 通常開発時ルーチン
* ビルド＆デプロイコマンド
  ```bash
  cd $APP_ROOT && git add . && git commit -m "regular update" && git push -u origin main
  flutter build web && aws s3 sync build/web s3://$BACKET_NAME/ && aws s3 ls s3://$BACKET_NAME/
  ```
## 開発時コマンドサマリー
* 変更内容をリポジトリに登録
  ```bash
  cd $APP_ROOT && git add . && git commit -m "regular update" &&  git push -u origin main && flutter pub get
  ```
* 開発端末で実行
  ```bash
  flutter build web && flutter run -d chrome
  ```
* 変更内容をリポジトリに登録＆ビルド＆開発端末で実行
  ```bash
  cd $APP_ROOT && git add . && git commit -m "regular update" && git push -u origin main && flutter pub get && flutter build web && flutter run -d chrome
  ```
* ビルド＆本番S3上にデプロイ
  ```bash
  flutter pub get && flutter build web && aws s3 ls s3://$BACKET_NAME/ && aws s3 cp build/web/ s3://$BACKET_NAME/ --recursive
  ```

## Flutterインストール手順
* macOSの場合：ターミナル上での操作
* 参考資料：https://docs.flutter.dev/get-started/install/macos/mobile-ios?* tab=vscode
* Apple Siliconの場合のみ
  ```bash
  sudo softwareupdate --install-rosetta --agree-to-license
  ```
* ダウンロード
  * Intel Processor : 
    * https://storage.googleapis.com/flutter_infra_release/releases/stable/macos/flutter_macos_3.24.3-stable.zip
  * Apple Silicon : 
    * https://storage.googleapis.com/flutter_infra_release/releases/stable/macos/flutter_macos_3.24.3-stable.zip
* インストールコマンド
  ```bash
  unzip ~/development/flutter_macos_3.24.3-stable.zip -d ~/development
  touch ~/.zshenv
  echo 'export PATH=$HOME/development/flutter/bin:$PATH' > ~/.zshenv && cat ~/.zshenv
  ```
* VSCodeでの操作
  * press Command + Shift + P
  * Command Palette, type flutter
  * Select Flutter: New Project.
* XCodeインストール
  * https://itunes.apple.com/jp/app/xcode/id497799835?ls=1&mt=12
  * 「App Storeを開きますか？」のダイアログが出てくるので「App Storeを開く」をクリック
* Xcodeビルド
  ```bash
  sudo sh -c 'xcode-select -s /Applications/Xcode.app/Contents/Developer && xcodebuild -runFirstLaunch'
  sudo xcodebuild -license
  xcodebuild -downloadPlatform iOS
  ```
* iOSシミュレーターが必要な場合のみ
  ```bash
  open -a Simulator
  ```
  * Display Size	Menu command	Keyboard shortcut
  * Small	Window > Physical Size	Cmd + 1
  * Moderate	Window > Point Accurate	Cmd + 2
  * HD accurate	Window > Pixel Accurate	Cmd + 3
  * Fit to screen	Window > Fit Screen	Cmd + 4
* その他開発環境のインストール
  ```bash
  sudo gem install cocoapods
  export PATH=$HOME/.gem/bin:$PATH
  flutter doctor
  flutter doctor -v
  ```
* Flutterプロジェクトの作成
  ```bash
  flutter create $APP_NAME
  cd $APP_NAME
  ```
* パッケージを追加
  <details><summary>$APP_NAME/pubspec.yaml</summary>

  ```yaml
  name: $APP_NAME
  description: A new Flutter project.

  environment:
    sdk: '^3.3.0'

  dependencies:
    flutter:
      sdk: flutter
    path_provider: ^2.0.11
  ```
  </details>
* 実装：
  * $APP_NAME/lib/main.dart
  * $APP_NAME/test/widget_test.dart
* テスト
  ```bash
  flutter pub get
  flutter test
  ```
    <details><summary>実行結果</summary>
    
    ```bash
    00:04 +1: All tests passed!  
    ```
    </details>
* Web向けビルド
  ```bash
  flutter build web
  ```
    <details><summary>成功した時のメッセージ</summary>

    ```bash
    Compiling lib/main.dart for the Web...
    ```
    </details>
* Webブラウザで実行
  ```bash
  flutter run -d chrome
  ```
* 開発時gitコマンド
  * 初期化
    ```bash
    git init && git add . && git commit -m "regular update"
    git remote add origin https://github.com/aho-manuke/$APP_NAME.git
    git remote -v
    git config --global http.postBuffer 524288000
    git push -u origin main
    ```
  * 通常時
    ```bash
    git add . && git commit -m "regular update" && git push -u origin main
    ```

## 実装コード

## S3へのデプロイ
以下の条件で新規にAWS の S3バケットを作成するAWS CLIコマンド
* リージョン：us-east-1
* バケット名 : $BACKET_NAME
* パブリックアクセスをすべてブロック：OFF
* 静的ウェブサイトホスティング : 有効にする
* インデックスドキュメント : index.html
  * バケット作成コマンド
    ```bash
    # 定数設定
    echo $BACKET_NAME && echo $REGION
    # S3バケットを作成
    aws s3api create-bucket --bucket $BACKET_NAME --region $REGION
    # パブリックアクセスをすべてブロックをOFFにする
    aws s3api put-public-access-block --bucket $BACKET_NAME --public-access-block-configuration BlockPublicAcls=false,IgnorePublicAcls=false,BlockPublicPolicy=false,RestrictPublicBuckets=false
    # 静的ウェブサイトホスティングを有効にする
    aws s3 website s3://$BACKET_NAME/ --index-document index.html --error-document error.html
    ```
  <details><summary>実行結果</summary>

  ```json
  {
      "Location": "/$BACKET_NAME"
  }
  ```
</details>

* S3 Bucket Polcy : allow access http
  <details><summary>コマンド</summary>

    ```bash
    aws s3api put-bucket-policy --bucket $BACKET_NAME --policy '{
      "Version": "2012-10-17",
      "Statement": [
          {
              "Sid": "PublicReadGetObject",
              "Effect": "Allow",
              "Principal": "*",
              "Action": "s3:GetObject",
              "Resource": "arn:aws:s3:::'$BACKET_NAME'/*",
              "Condition": {
                  "Bool": {
                      "aws:SecureTransport": "false"
                  }
              }
          }
      ]
    }'
    ```
  </details>
* Backet Cross-Origine Resource Sharring(CROS)
  <details><summary>コマンド</summary>

    ```bash
    aws s3api put-bucket-cors --bucket $BACKET_NAME --cors-configuration  '{
      "CORSRules": [
        {
          "AllowedHeaders": [
            "*"
          ],
          "AllowedMethods": [
            "GET","PUT","POST","DELETE","HEAD"
          ],
          "AllowedOrigins": [
            "*"
          ],
          "ExposeHeaders": []
        }
      ]
    }'
    ```
  </details>

## S3バケットへのコンテンツのアップロード
* 単純コピーの場合
  ```bash
  cd $APP_ROOT && flutter build web
  aws s3 ls s3://$BACKET_NAME/
  aws s3 cp build/web/ s3://$BACKET_NAME/ --recursive
  ```
* ローカルディレクトリbuild/webの内容をS3バケットs3://$BACKET_NAME/に同期し、アップロードされたファイルのアクセス権限をパブリックリードに設定する場合
  ```bash
  cd $APP_ROOT && flutter build web
  aws s3api put-bucket-policy --bucket $BACKET_NAME --policy '{
      "Version": "2012-10-17",
      "Statement": [
          {
              "Sid": "PublicReadGetObject",
              "Effect": "Allow",
              "Principal": "*",
              "Action": "s3:GetObject",
              "Resource": "arn:aws:s3:::'$BACKET_NAME'/*"
          }
      ]
  }'
  aws s3 sync build/web s3://$BACKET_NAME/
  aws s3 ls s3://$BACKET_NAME/
  ```
  <details><summay>実行結果</summary>

  ```bash
  Warning: In index.html:37: Local variable for "serviceWorkerVersion" is deprecated. Use "{{flutter_service_worker_version}}" template token instead. See
  https://docs.flutter.dev/platform-integration/web/initialization for more details.
  Warning: In index.html:46: "FlutterLoader.loadEntrypoint" is deprecated. Use "FlutterLoader.load"
  instead. See https://docs.flutter.dev/platform-integration/web/initialization for more details.
  Compiling lib/main.dart for the Web...                             698ms
  ✓ Built build/web
  upload: build/web/assets/FontManifest.json to s3://$BACKET_NAME/assets/FontManifest.json
  ・・・省略・・・
  upload: build/web/.last_build_id to s3://$BACKET_NAME/.last_build_id
  upload: build/web/canvaskit/canvaskit.wasm to s3://$BACKET_NAME/canvaskit/canvaskit.wasm
  ```
  </details>

## 動作確認
* バケットウェブサイトエンドポイントURLを得るコマンド
  ```bash
  BACKET_URL=http://$BACKET_NAME.s3-website-$REGION.amazonaws.com
  echo $BACKET_URL
  ```

## `'YOUR_ACCESS_KEY'` と `'YOUR_SECRET_KEY'` の取得

AWSのアクセスキーとシークレットキーです。これらはAWSマネジメントコンソールから取得できます。以下の手順に従ってください。

### 手順
1. **AWSマネジメントコンソールにログイン**
   - [AWSマネジメントコンソール](https://aws.amazon.com/jp/console/)にアクセスし、ログインします。

2. **IAM（Identity and Access Management）サービスに移動**
   - コンソールの上部にある検索バーに「IAM」と入力し、IAMサービスに移動します。

3. **新しいユーザーを作成**
   - 左側のナビゲーションペインで「ユーザー」を選択し、「ユーザーを追加」ボタンをクリックします。
   - ユーザー名を入力し、「プログラムによるアクセス」にチェックを入れます。

4. **アクセス権限を設定**
   - 「次のステップ: アクセス権限」をクリックし、適切なアクセス権限をユーザーに付与します。
   - 「既存のポリシーを直接アタッチ」を選択し、「AmazonDynamoDBFullAccess」などの適切なポリシーを選択します。

5. **タグ（オプション）を設定**
   - 必要に応じてタグを追加します（オプション）。

6. **確認と作成**
   - 設定内容を確認し、「ユーザーを作成」ボタンをクリックします。

7. **アクセスキーとシークレットキーを取得**
   - ユーザーが作成されると、アクセスキーIDとシークレットアクセスキーが表示されます。
   - これらの情報を安全な場所に保存してください。シークレットキーはこの画面でしか表示されないため、必ず保存してください。

### 取得したキーをコードに設定
取得したアクセスキーとシークレットキーをコードに設定します。

```dart
final awsSigV

Client = AWSSigV4Client(
  'YOUR_ACCESS_KEY',  // 取得したアクセスキーID
  'YOUR_SECRET_KEY',  // 取得したシークレットアクセスキー
  'https://dynamodb.ap-northeast-1.amazonaws.com',
  region: 'ap-northeast-1',
);
```

これで、DynamoDBにアクセスするための認証情報が設定され、リクエストが成功するはずです。