Twitterクローンを作ります #4 ユーザー登録機能 (2)

前回MongoDBのセットアップを行ったので、ユーザー登録機能の続きを進めていきましょう。

# API側の実装

とりあえずAPI側でデータベースを利用しやすいように接続処理を作成しておきましょう。
下記のファイルを作成します。
DBの接続情報は後でGCPのSecretManagerかなにかで管理する予定ですが、一旦環境変数から取得することにしましょう。

```py:server/database.py
import os
import sys
from pymongo.mongo_client import MongoClient
from pymongo.server_api import ServerApi

CREDENTIAL = os.environ['MONGO_CREDENTIAL']

if not CREDENTIAL:
    print("MONGO_CREDENTIAL is not set")
    sys.exit(-1)

client = MongoClient(f"mongodb+srv://{CREDENTIAL}@toittacluster.YYYYYYY.mongodb.net/myFirstDatabase?retryWrites=true&w=majority", server_api=ServerApi('1'))
db = client['ToittaDB']
```

次にユーザークラスを作成していきます。

```py:server/models/user.py
from server.database import db

import hashlib

class User:
    _collection = db['users']

    def __init__(self,
                display_name: str,
                username: str,
                password: str,
                email: str):
        self.display_name = display_name
        self.username = username
        self.hashed_password = self.encrypt(password)
        self.email = email
    
    @staticmethod
    def encrypt(password: str):
        return hashlib.sha256(password.encode()).hexdigest()

    def create(self):
        return self._collection.insert_one(vars(self))
```

pymongoを隠蔽するのかしないのか決まってない感じになってしまっていますがひとまずこれで行きましょう。
API側を実装していきます。

```py:server/apis/users.py
from flask import jsonify, request, session
from server.models.user import User
from server.main import app


@app.route('/api/users', methods=['POST'])
def register():
    body = request.json
    if User._collection.find_one({'username': body['username']}):
        return jsonify({'error': { 'message': '登録済みのユーザー名です。' }}), 400
    if User._collection.find_one({'email': body['email']}):
        return jsonify({'error': { 'message': '登録済みのメールアドレスです。'}}), 400
    user = User(display_name=body['display_name'],
                username=body['username'],
                password=body['password'],
                email=body['email'])
    user.create()
    del body['password']
    user = vars(user)
    user['_id'] = str(user['_id'])
    session['user'] = user
    return jsonify(body)
```

ユーザー名とメールアドレスの重複は認めないようにしましょう。本来は入力されたメールアドレスにメールを送信して実際に存在するアドレスかを確認したほうが良いですが今回はよしとしましょう。
問題なければ登録されたユーザーでログインされたとみなし、セッションに保存しておきます。

サーバーの起動前に`$ export MONGO_CREDENTIAL=admin:XXXXXXX`のように接続情報を環境変数に設定しておいてください。

今度は画面側を修正していきます。

# 画面側の実装

APIの呼び出し部分までは以前に実装ずみなので、下記のように RegisterView.vue の script 部分を修正します。

```vue:client/src/views/RegisterView.vue

...

<script>
import { ref } from 'vue';
import { useRouter } from 'vue-router';
import UIkit from 'uikit';
import axios from 'axios';

export default {
    setup() {
        const router = useRouter();
        const displayName = ref("");
        const username = ref("");
        const password = ref("");
        const passwordConfirm = ref("");
        const email = ref("");
        const onClick = () => {
            var body = {
                display_name: displayName.value,
                username: username.value,
                password: password.value,
                email: email.value
            };
            axios.post('/api/users', body).then(() => {
                router.push('/');
            })
            .catch(error => {
                UIkit.notification(error.response.data.error.message, {status: 'danger'});
            });
        };
        return {
            displayName,
            username,
            password,
            passwordConfirm,
            email,
            onClick
        };
    },
}
</script>
```

画面からユーザー情報を入力すると、http://localhost:8080/ にリダイレクトされ、MongoDBにデータが保存されることが確認できると思います。

![スクリーンショット 2022-03-12 20.30.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2536525/94380572-e6b3-c2bd-ca77-652a3d4a68f8.png)


今回はここまでです。次回はログイン画面を作っていきましょう。