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