4. HTML問い合わせフォームの作成

作業の目的 [why]

お問い合わせフォームを作成します。

課題

  • JS SDKのバージョン指定を変数化する。
  • 入力フォームの非テーブル化

作業対象 [what]

  • S3サービス

標準時間

8分

前提条件

作業権限条件 [who]

本作業は、以下の作業権限を有する人が行います。

作業権限条件: S3への権限

S3に対してフル権限があること。

作業権限条件: Congnito Identityへの権限

Cognito Identityに対してRead権限があること。

作業環境条件 [where]

本作業は、以下の作業環境で行います。

作業環境条件1: OSとバージョン

Amazon Linuxの以下のバージョンで動作確認済

コマンド:

cat /etc/issue | head -1

結果(例):

Amazon Linux AMI release 2016.09

作業環境条件2: シェルとバージョン

bashの以下のバージョンで動作確認済

コマンド:

bash --version -v | head -1

結果(例):

GNU bash, バージョン 4.2.46(1)-release (x86_64-redhat-linux-gnu)

作業環境条件3: AWS CLIのバージョン

以下のバージョンで動作確認済

  • AWS CLI 1.11.117

コマンド:

aws --version

結果(例):

aws-cli/1.11.144 Python/2.7.12 Linux/4.4.11-23.53.amzn1.x86_64 botocore/1.7.2

バージョンが古い場合は最新版に更新しましょう。

コマンド:

sudo -H pip install -U awscli

開始条件

作業に必要なモノ・情報 [resource]

作業開始には、以下が全て揃っていることが必要です。

リソース1: コンテンツファイル用ディレクトリ

  • 今回は"${HOME}/tmp/conf-html-handson20170904"をコンテンツファイル用ディレクトリとします。

    ls ${HOME}/tmp/conf-html-handson20170904
    
  • 存在しない場合は作成します。

    mkdir -p ${HOME}/tmp/conf-html-handson20170904
    

リソース2: コンテンツ用S3バケット名

  • コンテンツ用S3バケットの名称です。
  • 今回は"handson20170904web-XXXXXXXXXXXX"とします。

リソース3: データ保存用S3バケット名

  • データ保存用S3バケットの名称です。
  • 今回は"handson20170904data-XXXXXXXXXXXX"とします。

リソース4: Cognito識別子プール名

  • 利用するCognito識別子プールのの名称です。
  • 今回は"handson20170904pool"とします。

作業開始 [when]

以下を全て満たしているとき、作業を開始します。

  • 開始の指示があった場合。

タスクの実施

0. パラメータの指定

まず変数の確認をします。

変数の確認:

cat << ETX

  0.a. AWS_DEFAULT_PROFILE: actual: ${AWS_DEFAULT_PROFILE}
                            expect: <S3のフル権限が許可されているプロファイル>
  0.b. AWS_DEFAULT_REGION:  actual: ${AWS_DEFAULT_REGION}
                            expect: ap-northeast-1

  0.1. DIR_CONTENTS:        actual: ${DIR_CONTENTS}
                            expect: ${HOME}/tmp/conf-html-handson20170904
  0.2. S3_BUCKET_NAME:      actual: ${S3_BUCKET_NAME}
                            expect: handson20170904web-XXXXXXXXXXXX
  0.3. S3_BUCKET_DATA:      actual: ${S3_BUCKET_DATA}
                            expect: handson20170904data-XXXXXXXXXXXX
  0.4. COGNITO_IDPOOL_NAME: actual: ${COGNITO_IDPOOL_NAME}
                            expect: handson20170904pool

ETX

変数が入っていない、適切でない場合は、それぞれの手順番号について作業を行います。

0.a. プロファイルの指定

プロファイルの一覧を確認します。

コマンド:

cat ~/.aws/credentials \
 | grep '\[' \
 | sed 's/\[//g' | sed 's/\]//g'

結果(例):

iamFull-prjz-mbpr13
<S3のフル権限が許可されているプロファイル>

変数の設定:

export AWS_DEFAULT_PROFILE='<S3のフル権限が許可されているプロファイル>'

0.b. リージョンの指定

変数の設定

export AWS_DEFAULT_REGION='ap-northeast-1'

0.1. コンテンツファイルディレクトリの指定

変数の設定:

DIR_CONTENTS="${HOME}/tmp/conf-html-handson20170904"

0.2. コンテンツ用S3バケット名の指定

コンテンツ用S3バケット名を指定します。

変数の設定:

S3_BUCKET_NAME='handson20170904web-XXXXXXXXXXXX'

0.3. データ保存用S3バケット名の指定

データ保存用S3バケット名を指定します。

変数の設定:

S3_BUCKET_DATA='handson20170904data-XXXXXXXXXXXX'

0.4. Cognito識別子プール名の指定

Cognito識別子プール名を指定します。

変数の設定:

COGNITO_IDPOOL_NAME='handson20170904pool'

再確認

設定されている変数の内容を再確認します。

変数の確認:

cat << ETX

  0.a. AWS_DEFAULT_PROFILE: actual: ${AWS_DEFAULT_PROFILE}
                            expect: <S3のフル権限が許可されているプロファイル>
  0.b. AWS_DEFAULT_REGION:  actual: ${AWS_DEFAULT_REGION}
                            expect: ap-northeast-1

  0.1. DIR_CONTENTS:        actual: ${DIR_CONTENTS}
                            expect: ${HOME}/tmp/conf-html-handson20170904
  0.2. S3_BUCKET_NAME:      actual: ${S3_BUCKET_NAME}
                            expect: handson20170904web-XXXXXXXXXXXX
  0.3. S3_BUCKET_DATA:      actual: ${S3_BUCKET_DATA}
                            expect: handson20170904data-XXXXXXXXXXXX
  0.4. COGNITO_IDPOOL_NAME: actual: ${COGNITO_IDPOOL_NAME}
                            expect: handson20170904pool

ETX

1. 前処理

1.1. 処理対象の状態確認

主処理の実施は、以下の状態であることを前提とします。

前提と異なることが判明した場合、直ちに処理を中止します。

事前条件1: コンテンツファイル用ディレクトリが存在する。

「コンテンツファイル用ディレクトリが存在する。」ことを確認します。

コマンド:

ls -d ${DIR_CONTENTS}

結果(例):

${HOME}/tmp/conf-html-handson20170904

事前条件2: S3バケット"handson20170904web-XXXXXXXXXXXX"が存在する。

「S3バケット"handson20170904web-XXXXXXXXXXXX"が存在する。」ことを確認します。

コマンド:

aws s3api list-buckets \
  --query "Buckets[?Name == \`${S3_BUCKET_NAME}\`].Name"

結果(例):

[
  "handson20170904web-XXXXXXXXXXXX"
]

事前条件3: S3バケット"handson20170904data-XXXXXXXXXXXX"が存在する。

「S3バケット"handson20170904data-XXXXXXXXXXXX"が存在する。」ことを確認します。

コマンド:

aws s3api list-buckets \
  --query "Buckets[?Name == \`${S3_BUCKET_DATA}\`].Name"

結果(例):

[
  "handson20170904data-XXXXXXXXXXXX"
]

事前条件4: Cognito識別子プール"handson20170904pool"が存在する。

「Cognito識別子プール"handson20170904pool"が存在する。」ことを確認します。

コマンド:

aws cognito-identity list-identity-pools \
  --max-results 60 \
  --query "IdentityPools[?IdentityPoolName==\`${COGNITO_IDPOOL_NAME}\`].IdentityPoolName"

結果(例):

[
  "handson20170904pool"
]

1.2. 主処理に必要な情報の取得

IDプールIDの取得

コマンド:

COGNITO_IDPOOL_ID=$( \
  aws cognito-identity list-identity-pools \
    --max-results 60 \
    --query "IdentityPools[?IdentityPoolName==\`${COGNITO_IDPOOL_NAME}\`].IdentityPoolId" \
    --output text \
) \
  && echo ${COGNITO_IDPOOL_ID}

結果(例):

ap-northeast-1:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

問合せフォームの作成

変数の設定:

FILE_FORM="${DIR_CONTENTS}/contact.html"

変数の確認:

cat << ETX

    AWS_DEFAULT_REGION: ${AWS_DEFAULT_REGION}
    COGNITO_IDPOOL_ID:  ${COGNITO_IDPOOL_ID}
    S3_BUCKET_DATA:     ${S3_BUCKET_DATA}
    FILE_FORM:      ${FILE_FORM}

ETX

ファイルの作成:

cat << EOF > ${FILE_FORM}
<!DOCTYPE html>
<meta charset="UTF-8">
<script src="https://sdk.amazonaws.com/js/aws-sdk-2.108.0.min.js"></script>
<title>お問い合わせフォーム</title>
<script>
    var func = function(id) { return document.getElementById(id); };
    AWS.config.region = "${AWS_DEFAULT_REGION}";
    AWS.config.credentials = new AWS.CognitoIdentityCredentials({IdentityPoolId: "${COGNITO_IDPOOL_ID}"});
    AWS.config.credentials.get(function(err) {
        if (!err) {
            console.log("Cognito Identify Id: " + AWS.config.credentials.identityId);
        }
    });

    function uploadFile() {
      AWS.config.region = '${AWS_DEFAULT_REGION}';
      var s3BucketName = "${S3_BUCKET_DATA}";
      var now = new Date();
      var obj = {"title": func("title").value, "mail": func("mail").value ,"contents": func("contents").value, "date": now.toLocaleString()};
      var s3 = new AWS.S3({params: {Bucket: s3BucketName}});
      var blob = new Blob([JSON.stringify(obj, null, 2)], {type:'text/plain'});
      s3.putObject({Key: "uploads/" +now.getTime()+".txt", ContentType: "text/plain", Body: blob, ACL: "public-read"},
      function(err, data){
        if(data !== null){
          window.location.href = './thanks.html';
        }
        else{
          alert("Upload Failed" + err.message);
        }
      });
    }
</script>

<div>
  <div>
    <form>
      <table>
        <tr>
          <th>件名</th>
          <td><input id="title" type="text" name="title" maxlength="40" value="" /></td>
        </tr>
        <tr>
          <th>メールアドレス</th>
          <td><input id="mail" type="email" name="mail" size="20" maxlength="50" /></td>
        </tr>
        <tr>
          <th>お問い合わせ内容</th>
          <td><textarea id="contents" cols="70" rows="6" name="contents"></textarea></td>
        </tr>
      </table>
      <div>
        <input onClick="uploadFile();" type="button" value="送信" id="button" />
      </div>
    </form>
  </div>
</div>
EOF

cat ${FILE_FORM}

インデックスページの作成

変数の設定:

FILE_INDEX="${DIR_CONTENTS}/index.html"

変数の確認:

cat << ETX

    FILE_INDEX:      ${FILE_INDEX}

ETX

ファイルの作成:

cat << EOF > ${FILE_INDEX}
<!DOCTYPE html>
<meta charset="UTF-8">
<title>ようこそ</title>

<h1>ようこそ</h1>

<p><a href="./contact.html">お問い合わせ</a></p>
EOF

cat ${FILE_INDEX}

Thanksページの作成

変数の設定:

FILE_THANKS="${DIR_CONTENTS}/thanks.html"

変数の確認:

cat << ETX

    FILE_THANKS:      ${FILE_THANKS}

ETX

ファイルの作成:

cat << EOF > ${FILE_THANKS}
<!DOCTYPE html>
<meta http-equiv="refresh" content="5;URL=./index.html">
<meta charset="UTF-8">
<title>お問い合わせありがとうございました。</title>

<h1>お問い合わせありがとうございました。</h1>

<p><a href="./index.html">トップページに戻る</a></p>
EOF

cat ${FILE_THANKS}

2. 主処理

フォームの転送

変数の確認:

cat << ETX

  DIR_CONTENTS:   actual: ${DIR_CONTENTS}
                  expect: ${HOME}/tmp/conf-html-handson20170904

  S3_BUCKET_NAME: actual: ${S3_BUCKET_NAME}
                  expect: handson20170904web-XXXXXXXXXXXX

ETX

コマンド:

cd ${DIR_CONTENTS} \
  && aws s3 sync . s3://${S3_BUCKET_NAME}/

3. 後処理

3.1. 状態確認に必要な情報の取得

問い合わせフォームのURLの取得

変数の設定:

S3_BUCKET_REGION=$( \
  aws s3api get-bucket-location \
    --bucket ${S3_BUCKET_NAME} \
    --output text \
) \
  && echo ${S3_BUCKET_REGION}

結果(例):

ap-northeast-1

変数の設定:

URL_S3_BUCKET="http://${S3_BUCKET_NAME}.s3-website-${S3_BUCKET_REGION}.amazonaws.com" \
  && echo ${URL_S3_BUCKET}

結果(例)

http://handson20170904web-XXXXXXXXXXXX.s3-ap-northeast-1.amazonaws.com

変数の設定:

URL_CONTACT="${URL_S3_BUCKET}/contact.html" \
  && echo ${URL_CONTACT}

結果(例)

http://handson20170904web-XXXXXXXXXXXX.s3-ap-northeast-1.amazonaws.com/contact.html

ブラウザで上記のURLを開きます。

3.2. 完了条件の確認

  • コンタクトフォームの下記の項目に入力をします。
    • 件名: こんにちは
    • メールアドレス: info@example.jp
    • お問い合わせ内容: ご無沙汰しております。
  • "送信"(ボタン)を押します。
  • SNSに登録したメールアドレスに通知が来ます。
    • 題名: Amazon S3 Notification
    • 本文: JSON形式

3.3. 後処理に必要な作業

S3バケットの確認

コマンド:

aws s3 ls ${S3_BUCKET_DATA}/uploads/

結果(例):

2017-09-04 01:23:45        142 1504447230062.txt

問い合わせの取得

コマンド:

mkdir -p ${HOME}/tmp/${S3_BUCKET_NAME} \
  && cd ${HOME}/tmp/${S3_BUCKET_NAME}

コマンド:

aws s3 cp --recursive s3://${S3_BUCKET_DATA}/uploads . \

結果(例):

download: s3://handson20170904web-XXXXXXXXXXXX/uploads/1504447230062.txt to ./1504447230062.txt

コマンド:

cat *.txt

結果(例):

{
  "title": "こんにちは",
  "mail": "info@example.jp",
  "contents": "ご無沙汰しております。",
  "date": "2017/9/4 20:00:30"
}

問い合わせの消去

コマンド:

cd \
  && rm -r ${HOME}/tmp/${S3_BUCKET_NAME}

完了