Phoenixを使ってChatをHerokuにたててみた

sing-up! [https://chat.syui.cf](https://chat.syui.cf), [@syui@syui.cf](https://mstdn.syui.cf/@syui)

status : [https://cachet.mybluemix.net](https://cachet.mybluemix.net)

## introduction

この前、chat(チャット)立てたいという話を耳にしたので、1,2時間ほどでherokuのほうに立ててみました。ちょっと調べたりしました。


```sh
$ git clone https://github.com/dwyl/phoenix-chat-example

$ cd !$:t

$ heroku create $APP_NAME

$ rm -rf .git

$ heroku git:remote -a $APP_NAME 

$ echo "erlang_version=20.1
elixir_version=1.6.0
always_rebuild=false
runtime_path=/app
" >> elixir_buildpack.config

$ echo "web: MIX_ENV=prod POOL_SIZE=2 mix ecto.migrate && mix phx.server" >> Procfile

$ vim config/prod.exs
config :chat, ChatWeb.Endpoint,
  load_from_system_env: true,
  url: [scheme: "https", host: "syui-chat.herokuapp.com", port: 443],
  force_ssl: [rewrite_on: [:x_forwarded_proto]],
  cache_static_manifest: "priv/static/cache_manifest.json",
  secret_key_base: Map.fetch!(System.get_env(), "SECRET_KEY_BASE")

$ mix deps.get
$ mix phx.gen.secret
XXX
$ heroku config:set SECRET_KEY_BASE="XXX" -a $APP_NAME
$ heroku addons:create heroku-postgresql:hobby-dev -a $APP_NAME
$ heroku buildpacks:set https://github.com/HashNuke/heroku-buildpack-elixir.git -a $APP_NAME
$ heroku buildpacks:add --index 2 https://github.com/gjaldon/heroku-buildpack-phoenix-static.git -a $APP_NAME

$ echo mix.lock >> .gitignore
$ git add .
$ git commit -m "first"
$ git push heroku master
```


参考 :

https://github.com/dwyl/learn-heroku/blob/master/elixir-phoenix-app-deployment.md

https://hexdocs.pm/phoenix/heroku.html#making-our-project-ready-for-heroku

## phx:exchat

今度は、login機能をつけたり、notifyとしてmail通知を実装したりしてみました。

少し機能多めのchatでもと思ってherokuにたててみました。

これは、[tony612/exchat](https://github.com/tony612/exchat)を使いますが、login機能が付きます。あくまでexampleっぽいsrcになっていますね。(多分、example-chatの略, slackを参考)

iconの取得はmailからgravatar.comを使ってるんじゃないですかね(知りませんが)。コード読めという話ですね。

さて、ではrepoのcloneからいきましょー。今回もexample-chatみたいな感じのやつです。

```sh
$ git clone https://github.com/tony612/exchat

$ cd !$:t

$ cat ./elixir_buildpack.config
erlang_version=20.1
elixir_version=1.6.0
always_rebuild=true
config_vars_to_export=(DATABASE_URL JWT_SECRET)

$ cat ./Procfile
web: MIX_ENV=prod POOL_SIZE=2 mix ecto.migrate && mix phoenix.server


$ heroku buildpacks:set https://github.com/HashNuke/heroku-buildpack-elixir.git -a $APP_NAME
# npm i
$ heroku buildpacks:add --index 2 https://github.com/gjaldon/heroku-buildpack-phoenix-static.git -a $APP_NAME
```

見た感じ、front-endには、react, redux, bootstrapを使っていて、割としっかりしている印象です。

heroku config:setには、`DATABASE_URL`, `JWT_SECRET`, `SECRET_KEY_BASE`を入れてください。と言っても、DATABASE_URL, PORTはpostgresを入れれば勝手に生成されると思うので、JWTとKEYですね。値は、`$ mix phx.gen.sercet`の結果になります。

```sh
$ mix phx.gen.sercet
123
$ heroku config:set JWT_SECRET=123 -a $APP_NAME
$ heroku config:set SECRET_KEY_BASE=123 -a $APP_NAME
```

### notify:mail

通知の手段は色々あるんですけど、mailが簡単そうなので、まずは簡単そうなものから。

```elixir:mix.exs
applications: [:bamboo]

defp deps do
  [{:phoenix, "~> 1.3.3"},
   {:bamboo, "~> 1.0.0"},
   {:bamboo_smtp, "~> 1.5.0"}
  ]
```

```elixir:config/prod.ex

config :exchat, Exchat.Mailer,
       adapter: Bamboo.SMTPAdapter,
       server: "smtp.gmail.com",
       port: 587,
       username: System.get_env("GMAIL"),
       password: System.get_env("GMAIL_APP_PASS"),
       tls: :if_available, # can be `:always` or `:never`
       ssl: false, # can be `true`
       retries: 1
```

これに関しては、mailgunとかを使うのが通常なんだけど、demoなのでgmailで。

```elixir:lib/mailer.ex
defmodule Exchat.Mailer do
  use Bamboo.Mailer, otp_app: :exchat
end
```

```elixir:lib/mail.ex
defmodule Exchat.Email do
  use Bamboo.Phoenix, view: Exchat.EmailView

  def hello_email(email) do
    new_email
    |> to(email)
    |> from("support@xxx.herokuapp.com")
    |> subject("login")
    |> text_body(".")
  end
end
```

userがloginしたらmailを送信するようにする。これによって、管理者は送信メールが出るので、それを通知すればいいかな。

```elixir:web/controllers/api_auth.ex

  def login_by_email_pass(conn, email, pass, opts) do
    repo = Keyword.fetch!(opts, :repo)
    user = repo.get_by(User, email: email)

    cond do
      user && checkpw(pass, user.password_hash) ->
	Exchat.Email.hello_email(user.email) |> Exchat.Mailer.deliver_now
        {:ok, login(conn, user)}
      user ->
        {:error, :unauthorized, conn}
      true ->
        dummy_checkpw()
        {:error, :not_found, conn}
    end
  end
```

実際、loginしてみてmailをcheckしてみる。確認できたなら、コードを変更。

DMやmessageが来た時も通知したければこんな感じでやってくとよさそう。

というかメールの通知は色々とつらい。普通はやらないと思うので、[ravenx](https://github.com/acutario/ravenx) / slackとか使うのがいいんでしょうか。


### notify:mailgun

一応、[mailgun](https://www.mailgun.com/)の書き方も掲載しておきます。


mailgun: 使用する上での注意点としては、freeの場合は、sandboxでしかapi/v3からmailの送信はできません。しかも、認証済みのmail addressにしか送れず、よって、lib/mail.exを書き換えて、to:verify-email, from:sandbox.domain, text_body:emailというようにしなければなりません。つまり、login userに送信(通知)するのではなく誰かがloginした時に管理者に通知する役割しか担えませんので注意です。freeでない場合はapiから普通に送れますので大丈夫ですが。

```elixir
# In config/config.exs, or config.prod.exs, etc.
config :my_app, MyApp.Mailer,
  adapter: Bamboo.MailgunAdapter,
  api_key: "my_api_key",
  domain: "your.domain"
```

mailgun(MailgunAdapter)を使う場合のnew_emailの値はこちらを参考に。

```sh
$ curl -s --user 'api:YOUR_API_KEY' \
    https://api.mailgun.net/v3/YOUR_DOMAIN_NAME/messages \
    -F from='Excited User <mailgun@YOUR_DOMAIN_NAME>' \
    -F to=YOU@YOUR_DOMAIN_NAME \
    -F to=bar@example.com \
    -F subject='Hello' \
    -F text='Testing some Mailgun awesomeness!'
```

### frontend

今度は、exchatのfrontendの内容になります。

heorkuでは、通常、/app, /webにわけられdeployされます。

なので、phxのようなbackendの成否とreactのようなfrontendの成否は基本的に別々になっています。

つまり、phxのdeployは正常に成功したけど、webpackが失敗して、/webがうまく表示できないなんてこともありえます。

exchatは、backendをelixir(phoenix)で構成し、frontendは、react, reduxなどで構成しているようです。

ここで、frontend(js)のpreview, build, convertなどには、webpack, babelなどを使用しているようです。

buildされたものは、`./priv/static/app.js`に置かれるようですね。しかし、.gitignoreに書かれていますので、pushしません。

```sh
$ cat webpack.config.js

$ mix deps.get

$ npm i

$ webpack

$ cat ./priv/static/app.js

$ mix phx.server
```

layoutを変えるには、`./web/templates/layout`から`html:head`より上をいじることもできますが、基本的には、`./client/`以下を触ります。これらは、webpackを使ってbuildされます。

`./config/dev.exs`を見ることで、どのようにpreviewされるのかわかります。

riotを触ってた頃、たまたまwebpackも使ってた気がするんですけど、もう忘れてる。

なお、cloudflare, herokuとかを利用してる場合、cacheなどが効いてるので、今回生成されるようなapp.jsみたいな一箇所に詰め込まれたファイルというのは、特に、なかなか反映されないことがあります。これは、purgeをするとか、もしくはherokuapp.comで確認するとかしたほうがいいわけですが、それでも反映するまでにしばらく時間がかかることが多いです。browserのprivate windowを利用しても同じ。frontendはこのあたり、大変ですね。あくまで初見の印象ですが。

```sh
$ heroku plugins:install heroku-repo
$ heroku repo:purge_cache
```

previewは、`$ bash ./compile`したあとに、`$ mix phx.server`します。`-> localhost:4000`

色々なパッケージをupdateしてみました。

詳しくは[こちら](https://syui.gitlab.io/blog/post/2018-08-25-webpack/)の記事を読んでください。