What's?

Home / サーバーレス 問い合わせフォーム AWS s3/lambda/cognito/ses/slack

サーバーレス 問い合わせフォーム AWS s3/lambda/cognito/ses/slack
February 20, 2018

DEVELOPMENT

web
js
aws
cognito
ses
lambda
slack

webサーバを使用せず、s3 でwebを公開してる際のお問い合わせフォームの実装方法です。

前提

  • Aws s3にてwebサイト公開済み
  • slackアカウントを持っている
  • sesにて特定のdomainにて認証済み

※参考

処理の流れ

  • 問い合わせフォームからs3にjsonファイルがupされる(congnitoを使って)
  • s3にアップされると、lambda関数が処理実行
  • lambda関数がses(ユーザー用)とslack(管理用)に送信

やること

  • s3にbucket作成
  • cognito設定
  • slackの準備
  • sesの準備
  • lambda設定
  • フォーム実装

s3にbucket作成

s3にフォームからupされる専用バケットを作成します。 一応指定のサイト以外からアップされないように「CORS設定」をしておく(本番反映後設定したほうがローカルでの開発中に楽)

アクセス権限 -> CORSの設定

<!-- Sample policy -->
<CORSConfiguration>
  <CORSRule>
    <AllowedOrigin>開発中は * 公開後 公開ドメインに変えた方がいい</AllowedOrigin>
    <AllowedMethod>PUT</AllowedMethod>
    <MaxAgeSeconds>3000</MaxAgeSeconds>
    <AllowedHeader>*</AllowedHeader>
  </CORSRule>
</CORSConfiguration>

cognito設定

Cognitoについては下記を参考にすると理解できそうです。

https://dev.classmethod.jp/cloud/aws/what-is-the-cognito/

cognito IDプールを作成する

  • AWSコンソールより、Cognitoを選択し、「フェデレーテッドアイデンティティの管理」を洗濯、右上の「新しいIDプールの作成」を選択。
  • 「ID プール名」を、わかりやすい名前に
  • 「認証されていない ID に対してアクセスを有効にする」にチェックを入れる
  • 「プールを作成」する

次の画面に進むみ、詳細を表示 キャプチャ jsコードを控えておく

ポリシーを作成し、ロールにアタッチする

作成されたcognitoのロールに対して、s3バケットへのファイルup権限を追加します。

  • AWSコンソールより、IAMを選択し、ポリシーを選択。
  • ポリシーの作成をクリックし、JSONに切り替える
  • 下記をコピペ(指定したs3のarnに対してput権限を付与する)
  {
    "Version": "2012-10-17",
    "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:PutObject",
        "s3:PutObjectAcl"
      ],
      "Resource": [
        "arn:aws:s3:::先程作成したバケット名/*",
        "arn:aws:s3:::先程作成したバケット名/*"
      ]
    }
  ]
}
  • 作成をクリックし、次の画面で、ポリシー名にわかりやすい名前を入れて作成する

作成されたポリシーをロールにアタッチする

※ IDプール作成時にポリシー詳細にて設定したほうが楽かも

  • AWSコンソールより、IAMを選択し、ロールを選択。
  • 一覧より、作成された、「CognitoxxxxxxxxxxxxxUnauthRole」を選択。※Unauthの方
  • ポリシーのアタッチ
  • 上記作成したポリシーを検索欄より、検索し、チェックを入れる
  • ポリシーのアタッチ

※ この手順は、cognito IDプール作成じにポリシーの設定からやったほうが楽かもしれません。

一応、これでcognitoの準備はOKです。

slackの準備

とりあえずこちらの方法を参考にさせていただきました。

http://reiki4040.hatenablog.com/entry/2017/01/30/001634

webhookのURLだけ、コピーして控えておきます。

sesの準備

sesは、リージョンが限られているので、限られたリージョンの中から、選択する。

  • Aws コンソールより、sesを選択
  • 左メニューの「Email address」を選択し、通知元のEmailアドレスを指定する
  • Verify This Email Adressを選択する
  • 登録したアドレスに承認メールが届くので承認する

lambda設定

指定のS3バケットにデータがupされたら、upされたファイルの情報を元に、そのメールアドレスへ送付するし、管理側には、slackにて、通知する 最初に作成した、s3のバケットを使用します。

まずは、lambdaより、sesへのアクセスを行うのに、ロールの設定をします。

ロールを作成

  • IAMから、ポリシー、ポリシーの作成
  • Awsサービスをlambdaを選択し、次のステップ
  • ポリシータイプで、sesと検索し、「AmazonSESFullAccess」にチェックを入れて次のステップ
  • また、cloudwatchへの権限も追加するので「AWSOpsWorksCloudWatchLogs」と検索し、チェック
  • ロール名にわかりやすい名前を入れる

lambda関数を作成

Awsコンソールより、Lambdaを選択し、関数の作成をクリック

  • 名前 -> わかりやすい名前
  • ランタイム -> node6.10
  • ロール -> 既存のロールを選択
  • 既存のロール -> 先程作成したsesとcloudwatchのポリシーを持つロールを指定

これで関数の作成

  • 環境変数に「SLACKWEBHOOKURL」のkey名で、先程のslackのwebhookのURLを指定する
'use strict'
const aws = require('aws-sdk');
const s3 = new aws.S3({ apiVersion: '2006-03-01' });

console.log('Loading function');

// Set for slack.
const https = require('https');
const url = require('url');
const slack_url = process.env.SLACK_WEBHOOK_URL;
const slack_req_opts = url.parse(slack_url);
slack_req_opts.method = 'POST';
slack_req_opts.headers = {'Content-Type': 'application/json'};

exports.handler = (event, context, callback) => {
  //console.log('Received event:', JSON.stringify(event, null, 2));

  // Get the object from the event and show its content type
  const bucket = event.Records[0].s3.bucket.name;
  const key = decodeURIComponent(event.Records[0].s3.object.key.replace(/\+/g, ' '));
  const params = {
    Bucket: bucket,
    Key: key,
  };
  s3.getObject(params, (err, data) => {
    if (err) {
      console.log(err);
      const message = `Error getting object ${key} from bucket ${bucket}. Make sure they exist and your bucket is in the same region as this function.`;
      console.log(message);
      callback(message);
    } else {
      const message = JSON.parse(data.Body);
      const body = [
        '[お名前] ' + message.name,
        '[メールアドレス] ' + message.email,
        '[日時] ' + message.date,
        '[お問い合わせ内容]' + "\n" + message.body,
      ].join("\n");

      // slack
      const req = https.request(slack_req_opts, function (res) {
        if (res.statusCode === 200) {
          context.succeed('posted to slack');
        } else {
          context.fail('status code: ' + res.statusCode);
        }
      });

      req.on('error', function(e) {
        console.log('problem with request: ' + e.message);
        context.fail(e.message);
      });

      req.write(JSON.stringify({text: "お問い合わせがありました\n\n" + body}));
      req.end();

      // ses
      const email = {
        Source: "xxxxxxxxxxxxxxxxxx",
        Destination: { ToAddresses: [message.email] },
        Message: {
          Body: { Text: { Data: message.name + "様\n\nテスト送信\n\n" + body} },
          Subject: { Data: "お問い合わせありがとうございます" },
        },
      };
      const ses = new aws.SES({ region: 'us-east-1' });
      ses.sendEmail(email, callback);
    }
  });
};

トリガーの設定

左側のリストからトリガーを追加します。

  • s3を選択
  • ページ下記にて、バケット名の指定
  • ObjectCreatedByPut を指定する

フォーム実装

例では、roit.jsを使っていますが、必要に応じて変更して下さい。

$ yarn add aws-sdk

コード

// Amazon Cognito 認証情報プロバイダーを初期化します
AWS.config.region = 'us-west-2';
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
  IdentityPoolId: 'xx-xxxx-xx:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
});
submit (event) {
  event.preventDefault()

  let s3BucketName = "バケット名"

  let email = this.refs.email.value
  let name = this.refs.name.value
  let body = this.refs.message.value
  let submit = this.refs.submit
  submit.disabled = true

  let now = new Date();
  let obj = {"name":name, "email":email , "body":body, "date": now.toLocaleString()}
  let s3 = new AWS.S3({params: {Bucket: s3BucketName}});
  let 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){
        // 成功処理
      }
      else{
        // 失敗処理
      }
    }
  );
}

webに公開後

s3のバケットのCORSの設定で、ドメインを指定して、指定ドメイン以外からfileをアップできないようにしておきましょう。

まとめ

cognito のIDプールを使用して、s3へファイルアップ、lambda関数より検知して、sesとslackにメッセージを送信することができました。 ec2等webサーバーを使っていればサーバ側で処理をして実装可能ですが、サーバーレスにして、この方法でも簡単に実装でき、トータル的な料金も、全然安いです。 ちょっとしたwebサイトなら本当にwebサーバーは不要になってきましたね。

ご意見ご指摘があったらお願いします。

うかい / 株式会社UKAI
うかい@代表取締役兼エンジニア株式会社UKAI
Nobuyuki Ukai

株式会社UKAI 代表取締役CEO。建築を専攻していたが、新卒でYahoo!Japanにエンジニアとして入社。その後、転職、脱サラ、フリーランスエンジニアを経て、田舎へ移住し、ゲストハウスの開業、法人成り。ゲストハウス2軒、焼肉店、カフェ、食品製造業を運営しています。詳細はこちらから

🙏よかったらシェアお願いします🙏
WRITTEN BY
うかい / 株式会社UKAI
うかい@代表取締役兼エンジニア株式会社UKAI

Nobuyuki Ukai

株式会社UKAI 代表取締役CEO。建築を専攻していたが、新卒でYahoo!Japanにエンジニアとして入社。その後、転職、脱サラ、フリーランスエンジニアを経て、田舎へ移住し、ゲストハウスの開業、法人成り。ゲストハウス2軒、焼肉店、カフェ、食品製造業を運営しています。詳細はこちらから

CONACT
入力して下さい
Slack からもどうぞ

※お気軽にどうぞ!(6月20日まで有効!お早めに)

COPYRIGHT © 2020 UKAI CO., LTD. ALL RIGHTS RESERVED.