mirror of
https://github.com/mgerb/classic-wow-forums
synced 2026-01-10 09:02:50 +00:00
init
This commit is contained in:
18
.gitignore
vendored
Normal file
18
.gitignore
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
# App artifacts
|
||||
/_build
|
||||
/db
|
||||
/deps
|
||||
/*.ez
|
||||
|
||||
# Generated on crash by the VM
|
||||
erl_crash.dump
|
||||
|
||||
# Files matching config/*.secret.exs pattern contain sensitive
|
||||
# data and you should not commit them into version control.
|
||||
#
|
||||
# Alternatively, you may comment the line below and commit the
|
||||
# secrets files as long as you replace their contents by environment
|
||||
# variables.
|
||||
/config/*.secret.exs
|
||||
|
||||
uploads
|
||||
42
README.md
Normal file
42
README.md
Normal file
@@ -0,0 +1,42 @@
|
||||
# MyApp
|
||||
|
||||
## Generate a new app
|
||||
`mix phx.new myapp --module MyApp --no-brunch --no-html --database postgres`
|
||||
|
||||
## Start the server
|
||||
|
||||
* Install dependencies with `mix deps.get`
|
||||
* Create and migrate your database with `mix ecto.create && mix ecto.migrate`
|
||||
* Start Phoenix endpoint with `mix phx.server`
|
||||
|
||||
## Production
|
||||
Ready to run in production? Please [check our deployment guides](http://www.phoenixframework.org/docs/deployment).
|
||||
|
||||
# Ecto
|
||||
|
||||
## Create table
|
||||
`mix ecto.gen.migration create_user`
|
||||
`mix ecto.migrate`
|
||||
|
||||
## Prod
|
||||
Running app
|
||||
`PORT=80 MIX_ENV=prod mix phx.server`
|
||||
|
||||
- when creating prod database
|
||||
- `MIX_ENV=prod mix ecto.create`
|
||||
- `MIX_ENV=prod mix ecto.migrate`
|
||||
|
||||
## Installing Elixir on C9
|
||||
```
|
||||
# for some reason C9 complains this file is missing when it tries to remove couchdb
|
||||
sudo touch /etc/init.d/couchdb
|
||||
|
||||
sudo apt-get install inotify-tools
|
||||
wget https://packages.erlang-solutions.com/erlang-solutions_1.0_all.deb && sudo dpkg -i erlang-solutions_1.0_all.deb
|
||||
wget http://packages.erlang-solutions.com/ubuntu/erlang_solutions.asc
|
||||
sudo apt-key add erlang_solutions.asc
|
||||
sudo apt-get update
|
||||
sudo apt-get install esl-erlang
|
||||
sudo apt-get install elixir
|
||||
mix local.hex
|
||||
```
|
||||
29
config/config.exs
Normal file
29
config/config.exs
Normal file
@@ -0,0 +1,29 @@
|
||||
# This file is responsible for configuring your application
|
||||
# and its dependencies with the aid of the Mix.Config module.
|
||||
#
|
||||
# This configuration file is loaded before any dependency and
|
||||
# is restricted to this project.
|
||||
use Mix.Config
|
||||
|
||||
# General application configuration
|
||||
config :myapp,
|
||||
namespace: MyApp,
|
||||
ecto_repos: [MyApp.Repo]
|
||||
|
||||
# Configures the endpoint
|
||||
config :myapp, MyAppWeb.Endpoint,
|
||||
url: [host: "localhost"],
|
||||
secret_key_base: "HiI7i2OfG+hhJ4oa8grLHQohaDq3aIgVO/G11RMtAnRBKrmPKl5YE4yZBk2zTqz/",
|
||||
render_errors: [view: MyAppWeb.ErrorView, accepts: ~w(json)],
|
||||
pubsub: [name: MyApp.PubSub,
|
||||
adapter: Phoenix.PubSub.PG2]
|
||||
|
||||
# Configures Elixir's Logger
|
||||
config :logger, :console,
|
||||
format: "$time $metadata[$level] $message\n",
|
||||
metadata: [:request_id]
|
||||
|
||||
# Import environment specific config. This must remain at the bottom
|
||||
# of this file so it overrides the configuration defined above.
|
||||
import_config "#{Mix.env}.exs"
|
||||
import_config "config.secret.exs"
|
||||
6
config/config.secret.exs.template
Normal file
6
config/config.secret.exs.template
Normal file
@@ -0,0 +1,6 @@
|
||||
# secret dev configurations - api keys go here
|
||||
|
||||
config :myapp,
|
||||
bnet_client_id: "",
|
||||
bnet_client_secret: "",
|
||||
bnet_redirect_uri: ""
|
||||
52
config/dev.exs
Normal file
52
config/dev.exs
Normal file
@@ -0,0 +1,52 @@
|
||||
use Mix.Config
|
||||
|
||||
# For development, we disable any cache and enable
|
||||
# debugging and code reloading.
|
||||
#
|
||||
# The watchers configuration can be used to run external
|
||||
# watchers to your application. For example, we use it
|
||||
# with brunch.io to recompile .js and .css sources.
|
||||
config :myapp, MyAppWeb.Endpoint,
|
||||
http: [port: 8080],
|
||||
debug_errors: true,
|
||||
code_reloader: true,
|
||||
check_origin: false,
|
||||
watchers: []
|
||||
|
||||
# ## SSL Support
|
||||
#
|
||||
# In order to use HTTPS in development, a self-signed
|
||||
# certificate can be generated by running the following
|
||||
# command from your terminal:
|
||||
#
|
||||
# openssl req -new -newkey rsa:4096 -days 365 -nodes -x509 -subj "/C=US/ST=Denial/L=Springfield/O=Dis/CN=www.example.com" -keyout priv/server.key -out priv/server.pem
|
||||
#
|
||||
# The `http:` config above can be replaced with:
|
||||
#
|
||||
# https: [port: 4000, keyfile: "priv/server.key", certfile: "priv/server.pem"],
|
||||
#
|
||||
# If desired, both `http:` and `https:` keys can be
|
||||
# configured to run both http and https servers on
|
||||
# different ports.
|
||||
|
||||
# Do not include metadata nor timestamps in development logs
|
||||
config :logger, :console, format: "[$level] $message\n"
|
||||
|
||||
# Set a higher stacktrace during development. Avoid configuring such
|
||||
# in production as building large stacktraces may be expensive.
|
||||
config :phoenix, :stacktrace_depth, 20
|
||||
|
||||
# Configure your database
|
||||
config :myapp, MyApp.Repo,
|
||||
adapter: Ecto.Adapters.Postgres,
|
||||
username: "postgres",
|
||||
password: "postgres",
|
||||
database: "myapp_dev",
|
||||
hostname: "localhost",
|
||||
template: "template0",
|
||||
pool_size: 10
|
||||
|
||||
# Secret key. You can use `mix guardian.gen.secret` to get one
|
||||
config :myapp, MyApp.Guardian,
|
||||
issuer: "myapp",
|
||||
secret_key: "secret"
|
||||
64
config/prod.exs
Normal file
64
config/prod.exs
Normal file
@@ -0,0 +1,64 @@
|
||||
use Mix.Config
|
||||
|
||||
# For production, we often load configuration from external
|
||||
# sources, such as your system environment. For this reason,
|
||||
# you won't find the :http configuration below, but set inside
|
||||
# MyAppWeb.Endpoint.init/2 when load_from_system_env is
|
||||
# true. Any dynamic configuration should be done there.
|
||||
#
|
||||
# Don't forget to configure the url host to something meaningful,
|
||||
# Phoenix uses this information when generating URLs.
|
||||
#
|
||||
# Finally, we also include the path to a cache manifest
|
||||
# containing the digested version of static files. This
|
||||
# manifest is generated by the mix phx.digest task
|
||||
# which you typically run after static files are built.
|
||||
config :myapp, MyAppWeb.Endpoint,
|
||||
load_from_system_env: true,
|
||||
url: [host: "example.com", port: 80]
|
||||
# cache_static_manifest: "priv/static/cache_manifest.json"
|
||||
|
||||
# Do not print debug messages in production
|
||||
config :logger, level: :info
|
||||
|
||||
# ## SSL Support
|
||||
#
|
||||
# To get SSL working, you will need to add the `https` key
|
||||
# to the previous section and set your `:url` port to 443:
|
||||
#
|
||||
# config :myapp, MyAppWeb.Endpoint,
|
||||
# ...
|
||||
# url: [host: "example.com", port: 443],
|
||||
# https: [:inet6,
|
||||
# port: 443,
|
||||
# keyfile: System.get_env("SOME_APP_SSL_KEY_PATH"),
|
||||
# certfile: System.get_env("SOME_APP_SSL_CERT_PATH")]
|
||||
#
|
||||
# Where those two env variables return an absolute path to
|
||||
# the key and cert in disk or a relative path inside priv,
|
||||
# for example "priv/ssl/server.key".
|
||||
#
|
||||
# We also recommend setting `force_ssl`, ensuring no data is
|
||||
# ever sent via http, always redirecting to https:
|
||||
#
|
||||
# config :myapp, MyAppWeb.Endpoint,
|
||||
# force_ssl: [hsts: true]
|
||||
#
|
||||
# Check `Plug.SSL` for all available options in `force_ssl`.
|
||||
|
||||
# ## Using releases
|
||||
#
|
||||
# If you are doing OTP releases, you need to instruct Phoenix
|
||||
# to start the server for all endpoints:
|
||||
#
|
||||
# config :phoenix, :serve_endpoints, true
|
||||
#
|
||||
# Alternatively, you can configure exactly which server to
|
||||
# start per endpoint:
|
||||
#
|
||||
# config :myapp, MyAppWeb.Endpoint, server: true
|
||||
#
|
||||
|
||||
# Finally import the config/prod.secret.exs
|
||||
# which should be versioned separately.
|
||||
import_config "prod.secret.exs"
|
||||
26
config/prod.secret.exs.template
Normal file
26
config/prod.secret.exs.template
Normal file
@@ -0,0 +1,26 @@
|
||||
use Mix.Config
|
||||
|
||||
# In this file, we keep production configuration that
|
||||
# you'll likely want to automate and keep away from
|
||||
# your version control system.
|
||||
#
|
||||
# You should document the content of this
|
||||
# file or create a script for recreating it, since it's
|
||||
# kept out of version control and might be hard to recover
|
||||
# or recreate for your teammates (or yourself later on).
|
||||
config :myapp, MyAppWeb.Endpoint,
|
||||
secret_key_base: "Bv6Dz/0GVoxyCC8TKpgwX/7y8NHtsSEQB49UAzNQLzuAafmkOQR0wshC/7Wwyp3/"
|
||||
|
||||
# Configure your database
|
||||
config :myapp, MyApp.Repo,
|
||||
adapter: Ecto.Adapters.Postgres,
|
||||
username: "postgres",
|
||||
password: "postgres",
|
||||
database: "myapp_prod",
|
||||
template: "template0",
|
||||
pool_size: 15
|
||||
|
||||
# Secret key. You can use `mix guardian.gen.secret` to get one
|
||||
config :myapp, MyApp.Guardian,
|
||||
issuer: "myapp",
|
||||
secret_key: "secret"
|
||||
19
config/test.exs
Normal file
19
config/test.exs
Normal file
@@ -0,0 +1,19 @@
|
||||
use Mix.Config
|
||||
|
||||
# We don't run a server during test. If one is required,
|
||||
# you can enable the server option below.
|
||||
config :myapp, MyAppWeb.Endpoint,
|
||||
http: [port: 4001],
|
||||
server: false
|
||||
|
||||
# Print only warnings and errors during test
|
||||
config :logger, level: :warn
|
||||
|
||||
# Configure your database
|
||||
config :myapp, MyApp.Repo,
|
||||
adapter: Ecto.Adapters.Postgres,
|
||||
username: "postgres",
|
||||
password: "postgres",
|
||||
database: "myapp_test",
|
||||
hostname: "localhost",
|
||||
pool: Ecto.Adapters.SQL.Sandbox
|
||||
9
lib/myapp.ex
Normal file
9
lib/myapp.ex
Normal file
@@ -0,0 +1,9 @@
|
||||
defmodule MyApp do
|
||||
@moduledoc """
|
||||
MyApp keeps the contexts that define your domain
|
||||
and business logic.
|
||||
|
||||
Contexts are also responsible for managing your data, regardless
|
||||
if it comes from the database, an external API or others.
|
||||
"""
|
||||
end
|
||||
31
lib/myapp/application.ex
Normal file
31
lib/myapp/application.ex
Normal file
@@ -0,0 +1,31 @@
|
||||
defmodule MyApp.Application do
|
||||
use Application
|
||||
|
||||
# See https://hexdocs.pm/elixir/Application.html
|
||||
# for more information on OTP Applications
|
||||
def start(_type, _args) do
|
||||
import Supervisor.Spec
|
||||
|
||||
# Define workers and child supervisors to be supervised
|
||||
children = [
|
||||
# Start the Ecto repository
|
||||
supervisor(MyApp.Repo, []),
|
||||
# Start the endpoint when the application starts
|
||||
supervisor(MyAppWeb.Endpoint, []),
|
||||
# Start your own worker by calling: MyApp.Worker.start_link(arg1, arg2, arg3)
|
||||
# worker(MyApp.Worker, [arg1, arg2, arg3]),
|
||||
]
|
||||
|
||||
# See https://hexdocs.pm/elixir/Supervisor.html
|
||||
# for other strategies and supported options
|
||||
opts = [strategy: :one_for_one, name: MyApp.Supervisor]
|
||||
Supervisor.start_link(children, opts)
|
||||
end
|
||||
|
||||
# Tell Phoenix to update the endpoint configuration
|
||||
# whenever the application is updated.
|
||||
def config_change(changed, _new, removed) do
|
||||
MyAppWeb.Endpoint.config_change(changed, removed)
|
||||
:ok
|
||||
end
|
||||
end
|
||||
33
lib/myapp/auth/auth.ex
Normal file
33
lib/myapp/auth/auth.ex
Normal file
@@ -0,0 +1,33 @@
|
||||
defmodule MyApp.Guardian do
|
||||
use Guardian, otp_app: :myapp
|
||||
|
||||
def subject_for_token(resource, _claims) do
|
||||
# You can use any value for the subject of your token but
|
||||
# it should be useful in retrieving the resource later, see
|
||||
# how it being used on `resource_from_claims/1` function.
|
||||
# A unique `id` is a good subject, a non-unique email address
|
||||
# is a poor subject.
|
||||
sub = to_string(resource["id"])
|
||||
{:ok, sub}
|
||||
end
|
||||
|
||||
# def subject_for_token(_, _) do
|
||||
# {:error, :reason_for_error}
|
||||
# end
|
||||
|
||||
def resource_from_claims(claims) do
|
||||
# Here we'll look up our resource from the claims, the subject can be
|
||||
# found in the `"sub"` key. In `above subject_for_token/2` we returned
|
||||
# the resource id so here we'll rely on that to look it up.
|
||||
id = claims["sub"]
|
||||
# resource = MyApp.get_resource_by_id(id)
|
||||
IO.inspect(claims)
|
||||
resource = id
|
||||
{:ok, resource}
|
||||
end
|
||||
|
||||
# def resource_from_claims(_claims) do
|
||||
# {:error, :reason_for_error}
|
||||
# end
|
||||
|
||||
end
|
||||
10
lib/myapp/auth/handler.ex
Normal file
10
lib/myapp/auth/handler.ex
Normal file
@@ -0,0 +1,10 @@
|
||||
defmodule MyApp.Auth.ErrorHandler do
|
||||
import Plug.Conn
|
||||
alias MyAppWeb.Response
|
||||
|
||||
def auth_error(conn, {type, _reason}, _opts) do
|
||||
conn
|
||||
|> put_status(401)
|
||||
|> Response.json(to_string(type))
|
||||
end
|
||||
end
|
||||
9
lib/myapp/auth/pipeline.ex
Normal file
9
lib/myapp/auth/pipeline.ex
Normal file
@@ -0,0 +1,9 @@
|
||||
defmodule MyApp.Guardian.AuthPipeline.JSON do
|
||||
use Guardian.Plug.Pipeline, otp_app: :MyApp,
|
||||
module: MyApp.Guardian,
|
||||
error_handler: MyApp.Auth.ErrorHandler
|
||||
|
||||
plug Guardian.Plug.VerifyHeader, realm: "Bearer"
|
||||
plug Guardian.Plug.EnsureAuthenticated
|
||||
plug Guardian.Plug.LoadResource, allow_blank: true
|
||||
end
|
||||
50
lib/myapp/battle_net/auth.ex
Normal file
50
lib/myapp/battle_net/auth.ex
Normal file
@@ -0,0 +1,50 @@
|
||||
defmodule MyApp.BattleNet.Auth do
|
||||
alias MyApp.BattleNet.User
|
||||
alias MyApp.JWT
|
||||
|
||||
def token_uri, do: "https://us.battle.net/oauth/token"
|
||||
|
||||
def get_token(code) do
|
||||
client_id = Application.get_env(:myapp, :bnet_client_id)
|
||||
client_secret = Application.get_env(:myapp, :bnet_client_secret)
|
||||
redirect_uri = Application.get_env(:myapp, :bnet_redirect_uri)
|
||||
|
||||
req_options = [hackney: [basic_auth: {client_id, client_secret}]]
|
||||
|
||||
HTTPoison.request(:post, token_uri, get_req_body(code), [], req_options)
|
||||
|> parse_body
|
||||
|> parse_token
|
||||
|> validate_user
|
||||
|> generate_jwt
|
||||
end
|
||||
|
||||
defp parse_body({:error, err}), do: {:error, err}
|
||||
defp parse_body({:ok, %HTTPoison.Response{body: body}}), do: Poison.decode(body)
|
||||
|
||||
defp parse_token({:ok, %{"access_token" => token}}), do: {:ok, token}
|
||||
defp parse_token({:ok, %{"error" => err}}), do: {:error, err}
|
||||
defp parse_token({:error, err}), do: {:error, "Authentication error"}
|
||||
|
||||
defp validate_user({:error, err}), do: {:error, err}
|
||||
defp validate_user({:ok, token}), do: User.get_user(token)
|
||||
|
||||
defp generate_jwt({:error, err}), do: {:error, err}
|
||||
defp generate_jwt({:ok, user}) do
|
||||
case JWT.get_jwt(user, user) do
|
||||
{:ok, token} -> {:ok, Map.merge(user, %{"token" => token})}
|
||||
{:error, err} -> {:error, err}
|
||||
end
|
||||
end
|
||||
|
||||
defp get_req_body(code) do
|
||||
redirect_uri = Application.get_env(:myapp, :bnet_redirect_uri)
|
||||
{:form, [
|
||||
grant_type: "authorization_code",
|
||||
scope: "wow.profile",
|
||||
code: code,
|
||||
redirect_uri: redirect_uri,
|
||||
]}
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
16
lib/myapp/battle_net/user.ex
Normal file
16
lib/myapp/battle_net/user.ex
Normal file
@@ -0,0 +1,16 @@
|
||||
defmodule MyApp.BattleNet.User do
|
||||
defstruct id: nil, battletag: nil
|
||||
|
||||
def api_url, do: "https://us.api.battle.net"
|
||||
|
||||
def get_user(access_token) do
|
||||
case HTTPoison.get(resource_url("account/user", access_token)) do
|
||||
{:ok, %HTTPoison.Response{body: body}} -> {:ok, Poison.decode!(body, as: Battlenet.User)}
|
||||
{:error, err} -> {:error, err}
|
||||
end
|
||||
end
|
||||
|
||||
defp resource_url(path, access_token) do
|
||||
"#{api_url}/#{path}?access_token=#{access_token}"
|
||||
end
|
||||
end
|
||||
14
lib/myapp/jwt.ex
Normal file
14
lib/myapp/jwt.ex
Normal file
@@ -0,0 +1,14 @@
|
||||
defmodule MyApp.JWT do
|
||||
alias MyApp.Guardian
|
||||
|
||||
# ~1 year
|
||||
defp tokenTTL(), do: {52, :weeks}
|
||||
|
||||
def get_jwt(user, claims) do
|
||||
case Guardian.encode_and_sign(user, claims, ttl: tokenTTL()) do
|
||||
{:ok, token, _claims} -> {:ok, token}
|
||||
{:error, _token, _claims} -> {:error, "JWT error"}
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
11
lib/myapp/repo.ex
Normal file
11
lib/myapp/repo.ex
Normal file
@@ -0,0 +1,11 @@
|
||||
defmodule MyApp.Repo do
|
||||
use Ecto.Repo, otp_app: :myapp
|
||||
|
||||
@doc """
|
||||
Dynamically loads the repository url from the
|
||||
DATABASE_URL environment variable.
|
||||
"""
|
||||
def init(_, opts) do
|
||||
{:ok, Keyword.put(opts, :url, System.get_env("DATABASE_URL"))}
|
||||
end
|
||||
end
|
||||
64
lib/myapp_web.ex
Normal file
64
lib/myapp_web.ex
Normal file
@@ -0,0 +1,64 @@
|
||||
defmodule MyAppWeb do
|
||||
@moduledoc """
|
||||
The entrypoint for defining your web interface, such
|
||||
as controllers, views, channels and so on.
|
||||
|
||||
This can be used in your application as:
|
||||
|
||||
use MyAppWeb, :controller
|
||||
use MyAppWeb, :view
|
||||
|
||||
The definitions below will be executed for every view,
|
||||
controller, etc, so keep them short and clean, focused
|
||||
on imports, uses and aliases.
|
||||
|
||||
Do NOT define functions inside the quoted expressions
|
||||
below. Instead, define any helper function in modules
|
||||
and import those modules here.
|
||||
"""
|
||||
|
||||
def controller do
|
||||
quote do
|
||||
use Phoenix.Controller, namespace: MyAppWeb
|
||||
import Plug.Conn
|
||||
import MyAppWeb.Router.Helpers
|
||||
import MyAppWeb.Gettext
|
||||
end
|
||||
end
|
||||
|
||||
def view do
|
||||
quote do
|
||||
use Phoenix.View, root: "lib/myapp_web/templates",
|
||||
namespace: MyAppWeb
|
||||
|
||||
# Import convenience functions from controllers
|
||||
import Phoenix.Controller, only: [get_flash: 2, view_module: 1]
|
||||
|
||||
import MyAppWeb.Router.Helpers
|
||||
import MyAppWeb.ErrorHelpers
|
||||
import MyAppWeb.Gettext
|
||||
end
|
||||
end
|
||||
|
||||
def router do
|
||||
quote do
|
||||
use Phoenix.Router
|
||||
import Plug.Conn
|
||||
import Phoenix.Controller
|
||||
end
|
||||
end
|
||||
|
||||
def channel do
|
||||
quote do
|
||||
use Phoenix.Channel
|
||||
import MyAppWeb.Gettext
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
When used, dispatch to the appropriate controller/view/etc.
|
||||
"""
|
||||
defmacro __using__(which) when is_atom(which) do
|
||||
apply(__MODULE__, which, [])
|
||||
end
|
||||
end
|
||||
37
lib/myapp_web/channels/user_socket.ex
Normal file
37
lib/myapp_web/channels/user_socket.ex
Normal file
@@ -0,0 +1,37 @@
|
||||
defmodule MyAppWeb.UserSocket do
|
||||
use Phoenix.Socket
|
||||
|
||||
## Channels
|
||||
# channel "room:*", MyAppWeb.RoomChannel
|
||||
|
||||
## Transports
|
||||
transport :websocket, Phoenix.Transports.WebSocket
|
||||
# transport :longpoll, Phoenix.Transports.LongPoll
|
||||
|
||||
# Socket params are passed from the client and can
|
||||
# be used to verify and authenticate a user. After
|
||||
# verification, you can put default assigns into
|
||||
# the socket that will be set for all channels, ie
|
||||
#
|
||||
# {:ok, assign(socket, :user_id, verified_user_id)}
|
||||
#
|
||||
# To deny connection, return `:error`.
|
||||
#
|
||||
# See `Phoenix.Token` documentation for examples in
|
||||
# performing token verification on connect.
|
||||
def connect(_params, socket) do
|
||||
{:ok, socket}
|
||||
end
|
||||
|
||||
# Socket id's are topics that allow you to identify all sockets for a given user:
|
||||
#
|
||||
# def id(socket), do: "user_socket:#{socket.assigns.user_id}"
|
||||
#
|
||||
# Would allow you to broadcast a "disconnect" event and terminate
|
||||
# all active sockets and channels for a given user:
|
||||
#
|
||||
# MyAppWeb.Endpoint.broadcast("user_socket:#{user.id}", "disconnect", %{})
|
||||
#
|
||||
# Returning `nil` makes this socket anonymous.
|
||||
def id(_socket), do: nil
|
||||
end
|
||||
20
lib/myapp_web/controllers/battle_net_controller.ex
Normal file
20
lib/myapp_web/controllers/battle_net_controller.ex
Normal file
@@ -0,0 +1,20 @@
|
||||
defmodule MyAppWeb.BattleNetController do
|
||||
use MyAppWeb, :controller
|
||||
alias MyAppWeb.Response
|
||||
alias MyApp.BattleNet.Auth
|
||||
|
||||
# https://us.battle.net/oauth/authorize?redirect_uri=https://localhost/api/battlenet/authorize&scope=wow.profile&client_id=vxqv32fddxsy6cmk6259amtymbuzmfrq&response_type=code
|
||||
|
||||
def authorize(conn, %{"code" => code}) when not is_nil(code) do
|
||||
|
||||
{output, status} = case Auth.get_token(code) do
|
||||
{:ok, token} -> {token, 200}
|
||||
{:error, err} -> {err, 400}
|
||||
end
|
||||
|
||||
conn
|
||||
|>put_status(status)
|
||||
|>Response.json(output)
|
||||
end
|
||||
|
||||
end
|
||||
12
lib/myapp_web/controllers/user_controller.ex
Normal file
12
lib/myapp_web/controllers/user_controller.ex
Normal file
@@ -0,0 +1,12 @@
|
||||
defmodule MyAppWeb.UserController do
|
||||
use MyAppWeb, :controller
|
||||
alias MyAppWeb.Response
|
||||
|
||||
def index(conn, params) do
|
||||
IO.inspect(conn)
|
||||
IO.inspect(params)
|
||||
conn
|
||||
|> Response.json("Auth works!")
|
||||
end
|
||||
|
||||
end
|
||||
55
lib/myapp_web/endpoint.ex
Normal file
55
lib/myapp_web/endpoint.ex
Normal file
@@ -0,0 +1,55 @@
|
||||
defmodule MyAppWeb.Endpoint do
|
||||
use Phoenix.Endpoint, otp_app: :myapp
|
||||
|
||||
socket "/socket", MyAppWeb.UserSocket
|
||||
|
||||
# Serve at "/" the static files from "priv/static" directory.
|
||||
#
|
||||
# You should set gzip to true if you are running phoenix.digest
|
||||
# when deploying your static files in production.
|
||||
plug Plug.Static,
|
||||
at: "/", from: :myapp, gzip: false,
|
||||
only: ~w(css fonts images js favicon.ico robots.txt)
|
||||
|
||||
# Code reloading can be explicitly enabled under the
|
||||
# :code_reloader configuration of your endpoint.
|
||||
if code_reloading? do
|
||||
plug Phoenix.CodeReloader
|
||||
end
|
||||
|
||||
plug Plug.RequestId
|
||||
plug Plug.Logger
|
||||
|
||||
plug Plug.Parsers,
|
||||
parsers: [:urlencoded, :multipart, :json],
|
||||
pass: ["*/*"],
|
||||
json_decoder: Poison
|
||||
|
||||
plug Plug.MethodOverride
|
||||
plug Plug.Head
|
||||
|
||||
# The session will be stored in the cookie and signed,
|
||||
# this means its contents can be read but not tampered with.
|
||||
# Set :encryption_salt if you would also like to encrypt it.
|
||||
plug Plug.Session,
|
||||
store: :cookie,
|
||||
key: "_myapp_key",
|
||||
signing_salt: "WENlVlsb"
|
||||
|
||||
plug MyAppWeb.Router
|
||||
|
||||
@doc """
|
||||
Callback invoked for dynamically configuring the endpoint.
|
||||
|
||||
It receives the endpoint configuration and checks if
|
||||
configuration should be loaded from the system environment.
|
||||
"""
|
||||
def init(_key, config) do
|
||||
if config[:load_from_system_env] do
|
||||
port = System.get_env("PORT") || raise "expected the PORT environment variable to be set"
|
||||
{:ok, Keyword.put(config, :http, [:inet6, port: port])}
|
||||
else
|
||||
{:ok, config}
|
||||
end
|
||||
end
|
||||
end
|
||||
24
lib/myapp_web/gettext.ex
Normal file
24
lib/myapp_web/gettext.ex
Normal file
@@ -0,0 +1,24 @@
|
||||
defmodule MyAppWeb.Gettext do
|
||||
@moduledoc """
|
||||
A module providing Internationalization with a gettext-based API.
|
||||
|
||||
By using [Gettext](https://hexdocs.pm/gettext),
|
||||
your module gains a set of macros for translations, for example:
|
||||
|
||||
import MyAppWeb.Gettext
|
||||
|
||||
# Simple translation
|
||||
gettext "Here is the string to translate"
|
||||
|
||||
# Plural translation
|
||||
ngettext "Here is the string to translate",
|
||||
"Here are the strings to translate",
|
||||
3
|
||||
|
||||
# Domain-based translation
|
||||
dgettext "errors", "Here is the error message to translate"
|
||||
|
||||
See the [Gettext Docs](https://hexdocs.pm/gettext) for detailed usage.
|
||||
"""
|
||||
use Gettext, otp_app: :myapp
|
||||
end
|
||||
22
lib/myapp_web/response.ex
Normal file
22
lib/myapp_web/response.ex
Normal file
@@ -0,0 +1,22 @@
|
||||
defmodule MyAppWeb.Response do
|
||||
|
||||
# error handling based on http status
|
||||
def json(conn, data) do
|
||||
|
||||
output = cond do
|
||||
conn.status && conn.status >= 400 ->
|
||||
%{
|
||||
error: %{
|
||||
status: conn.status,
|
||||
message: data,
|
||||
}
|
||||
}
|
||||
|
||||
true -> %{
|
||||
data: data
|
||||
}
|
||||
end
|
||||
|
||||
Phoenix.Controller.json(conn, output)
|
||||
end
|
||||
end
|
||||
28
lib/myapp_web/router.ex
Normal file
28
lib/myapp_web/router.ex
Normal file
@@ -0,0 +1,28 @@
|
||||
defmodule MyAppWeb.Router do
|
||||
use MyAppWeb, :router
|
||||
alias MyApp.Guardian.AuthPipeline
|
||||
|
||||
pipeline :api do
|
||||
plug :accepts, ["json"]
|
||||
end
|
||||
|
||||
pipeline :api_auth do
|
||||
plug AuthPipeline.JSON
|
||||
end
|
||||
|
||||
# Other scopes may use custom stacks.
|
||||
scope "/api", MyAppWeb do
|
||||
pipe_through [:api]
|
||||
|
||||
scope "/battlenet" do
|
||||
get "/authorize", BattleNetController, :authorize
|
||||
end
|
||||
|
||||
scope "/user" do
|
||||
# authenticated routes
|
||||
pipe_through [:api_auth]
|
||||
get "/", UserController, :index
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
29
lib/myapp_web/views/error_helpers.ex
Normal file
29
lib/myapp_web/views/error_helpers.ex
Normal file
@@ -0,0 +1,29 @@
|
||||
defmodule MyAppWeb.ErrorHelpers do
|
||||
@moduledoc """
|
||||
Conveniences for translating and building error messages.
|
||||
"""
|
||||
|
||||
@doc """
|
||||
Translates an error message using gettext.
|
||||
"""
|
||||
def translate_error({msg, opts}) do
|
||||
# Because error messages were defined within Ecto, we must
|
||||
# call the Gettext module passing our Gettext backend. We
|
||||
# also use the "errors" domain as translations are placed
|
||||
# in the errors.po file.
|
||||
# Ecto will pass the :count keyword if the error message is
|
||||
# meant to be pluralized.
|
||||
# On your own code and templates, depending on whether you
|
||||
# need the message to be pluralized or not, this could be
|
||||
# written simply as:
|
||||
#
|
||||
# dngettext "errors", "1 file", "%{count} files", count
|
||||
# dgettext "errors", "is invalid"
|
||||
#
|
||||
if count = opts[:count] do
|
||||
Gettext.dngettext(MyAppWeb.Gettext, "errors", msg, msg, count, opts)
|
||||
else
|
||||
Gettext.dgettext(MyAppWeb.Gettext, "errors", msg, opts)
|
||||
end
|
||||
end
|
||||
end
|
||||
17
lib/myapp_web/views/error_view.ex
Normal file
17
lib/myapp_web/views/error_view.ex
Normal file
@@ -0,0 +1,17 @@
|
||||
defmodule MyAppWeb.ErrorView do
|
||||
use MyAppWeb, :view
|
||||
|
||||
def render("404.json", _assigns) do
|
||||
%{errors: %{detail: "Page not found"}}
|
||||
end
|
||||
|
||||
def render("500.json", _assigns) do
|
||||
%{errors: %{detail: "Internal server error"}}
|
||||
end
|
||||
|
||||
# In case no render clause matches or no
|
||||
# template is found, let's render it as 500
|
||||
def template_not_found(_template, assigns) do
|
||||
render "500.json", assigns
|
||||
end
|
||||
end
|
||||
62
mix.exs
Normal file
62
mix.exs
Normal file
@@ -0,0 +1,62 @@
|
||||
defmodule MyApp.Mixfile do
|
||||
use Mix.Project
|
||||
|
||||
def project do
|
||||
[
|
||||
app: :myapp,
|
||||
version: "0.0.1",
|
||||
elixir: "~> 1.4",
|
||||
elixirc_paths: elixirc_paths(Mix.env),
|
||||
compilers: [:phoenix, :gettext] ++ Mix.compilers,
|
||||
start_permanent: Mix.env == :prod,
|
||||
aliases: aliases(),
|
||||
deps: deps()
|
||||
]
|
||||
end
|
||||
|
||||
# Configuration for the OTP application.
|
||||
#
|
||||
# Type `mix help compile.app` for more information.
|
||||
def application do
|
||||
[
|
||||
mod: {MyApp.Application, []},
|
||||
extra_applications: [:logger, :runtime_tools]
|
||||
]
|
||||
end
|
||||
|
||||
# Specifies which paths to compile per environment.
|
||||
defp elixirc_paths(:test), do: ["lib", "test/support"]
|
||||
defp elixirc_paths(_), do: ["lib"]
|
||||
|
||||
# Specifies your project dependencies.
|
||||
#
|
||||
# Type `mix help deps` for examples and options.
|
||||
defp deps do
|
||||
[
|
||||
{:phoenix, "~> 1.3.0"},
|
||||
{:phoenix_pubsub, "~> 1.0"},
|
||||
{:phoenix_ecto, "~> 3.2"},
|
||||
{:postgrex, ">= 0.0.0"},
|
||||
{:gettext, "~> 0.11"},
|
||||
{:cowboy, "~> 1.0"},
|
||||
{:comeonin, "~> 4.0"},
|
||||
{:argon2_elixir, "~> 1.2"},
|
||||
{:guardian, "~> 1.0"},
|
||||
{:httpoison, "~> 0.13"},
|
||||
]
|
||||
end
|
||||
|
||||
# Aliases are shortcuts or tasks specific to the current project.
|
||||
# For example, to create, migrate and run the seeds file at once:
|
||||
#
|
||||
# $ mix ecto.setup
|
||||
#
|
||||
# See the documentation for `Mix` for more info on aliases.
|
||||
defp aliases do
|
||||
[
|
||||
"ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"],
|
||||
"ecto.reset": ["ecto.drop", "ecto.setup"],
|
||||
"test": ["ecto.create --quiet", "ecto.migrate", "test"]
|
||||
]
|
||||
end
|
||||
end
|
||||
31
mix.lock
Normal file
31
mix.lock
Normal file
@@ -0,0 +1,31 @@
|
||||
%{"argon2_elixir": {:hex, :argon2_elixir, "1.2.14", "0fc4bfbc1b7e459954987d3d2f3836befd72d63f3a355e3978f5005dd6e80816", [], [{:elixir_make, "~> 0.4", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"base64url": {:hex, :base64url, "0.0.1", "36a90125f5948e3afd7be97662a1504b934dd5dac78451ca6e9abf85a10286be", [], [], "hexpm"},
|
||||
"certifi": {:hex, :certifi, "2.0.0", "a0c0e475107135f76b8c1d5bc7efb33cd3815cb3cf3dea7aefdd174dabead064", [], [], "hexpm"},
|
||||
"comeonin": {:hex, :comeonin, "4.0.3", "4e257dcb748ed1ca2651b7ba24fdbd1bd24efd12482accf8079141e3fda23a10", [], [{:argon2_elixir, "~> 1.2", [hex: :argon2_elixir, repo: "hexpm", optional: true]}, {:bcrypt_elixir, "~> 0.12.1 or ~> 1.0", [hex: :bcrypt_elixir, repo: "hexpm", optional: true]}, {:pbkdf2_elixir, "~> 0.12", [hex: :pbkdf2_elixir, repo: "hexpm", optional: true]}], "hexpm"},
|
||||
"connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [], [], "hexpm"},
|
||||
"cowboy": {:hex, :cowboy, "1.1.2", "61ac29ea970389a88eca5a65601460162d370a70018afe6f949a29dca91f3bb0", [], [{:cowlib, "~> 1.0.2", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.3.2", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"cowlib": {:hex, :cowlib, "1.0.2", "9d769a1d062c9c3ac753096f868ca121e2730b9a377de23dec0f7e08b1df84ee", [], [], "hexpm"},
|
||||
"db_connection": {:hex, :db_connection, "1.1.2", "2865c2a4bae0714e2213a0ce60a1b12d76a6efba0c51fbda59c9ab8d1accc7a8", [], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}, {:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, repo: "hexpm", optional: true]}], "hexpm"},
|
||||
"decimal": {:hex, :decimal, "1.4.1", "ad9e501edf7322f122f7fc151cce7c2a0c9ada96f2b0155b8a09a795c2029770", [], [], "hexpm"},
|
||||
"ecto": {:hex, :ecto, "2.2.7", "2074106ff4a5cd9cb2b54b12ca087c4b659ddb3f6b50be4562883c1d763fb031", [], [{:db_connection, "~> 1.1", [hex: :db_connection, repo: "hexpm", optional: true]}, {:decimal, "~> 1.2", [hex: :decimal, repo: "hexpm", optional: false]}, {:mariaex, "~> 0.8.0", [hex: :mariaex, repo: "hexpm", optional: true]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: true]}, {:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.13.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, repo: "hexpm", optional: true]}], "hexpm"},
|
||||
"elixir_make": {:hex, :elixir_make, "0.4.0", "992f38fabe705bb45821a728f20914c554b276838433349d4f2341f7a687cddf", [], [], "hexpm"},
|
||||
"gettext": {:hex, :gettext, "0.14.0", "1a019a2e51d5ad3d126efe166dcdf6563768e5d06c32a99ad2281a1fa94b4c72", [], [], "hexpm"},
|
||||
"guardian": {:hex, :guardian, "1.0.0", "21bae2a8c0b4ed5943d9da0c6aeb16e52874c1f675de5d7920ae35471c6263f9", [], [{:jose, "~> 1.8", [hex: :jose, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.0 or ~> 1.2 or ~> 1.3", [hex: :phoenix, repo: "hexpm", optional: true]}, {:plug, "~> 1.3.3 or ~> 1.4", [hex: :plug, repo: "hexpm", optional: true]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: false]}, {:uuid, ">= 1.1.1", [hex: :uuid, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"hackney": {:hex, :hackney, "1.10.1", "c38d0ca52ea80254936a32c45bb7eb414e7a96a521b4ce76d00a69753b157f21", [], [{:certifi, "2.0.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "5.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "1.0.2", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.1", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"httpoison": {:hex, :httpoison, "0.13.0", "bfaf44d9f133a6599886720f3937a7699466d23bb0cd7a88b6ba011f53c6f562", [], [{:hackney, "~> 1.8", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"idna": {:hex, :idna, "5.1.0", "d72b4effeb324ad5da3cab1767cb16b17939004e789d8c0ad5b70f3cea20c89a", [], [{:unicode_util_compat, "0.3.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"jose": {:hex, :jose, "1.8.4", "7946d1e5c03a76ac9ef42a6e6a20001d35987afd68c2107bcd8f01a84e75aa73", [], [{:base64url, "~> 0.0.1", [hex: :base64url, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [], [], "hexpm"},
|
||||
"mime": {:hex, :mime, "1.2.0", "78adaa84832b3680de06f88f0997e3ead3b451a440d183d688085be2d709b534", [], [], "hexpm"},
|
||||
"mimerl": {:hex, :mimerl, "1.0.2", "993f9b0e084083405ed8252b99460c4f0563e41729ab42d9074fd5e52439be88", [], [], "hexpm"},
|
||||
"phoenix": {:hex, :phoenix, "1.3.0", "1c01124caa1b4a7af46f2050ff11b267baa3edb441b45dbf243e979cd4c5891b", [], [{:cowboy, "~> 1.0", [hex: :cowboy, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 1.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.3.3 or ~> 1.4", [hex: :plug, repo: "hexpm", optional: false]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"phoenix_ecto": {:hex, :phoenix_ecto, "3.3.0", "702f6e164512853d29f9d20763493f2b3bcfcb44f118af2bc37bb95d0801b480", [], [{:ecto, "~> 2.1", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.9", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"phoenix_pubsub": {:hex, :phoenix_pubsub, "1.0.2", "bfa7fd52788b5eaa09cb51ff9fcad1d9edfeb68251add458523f839392f034c1", [], [], "hexpm"},
|
||||
"plug": {:hex, :plug, "1.4.3", "236d77ce7bf3e3a2668dc0d32a9b6f1f9b1f05361019946aae49874904be4aed", [], [{:cowboy, "~> 1.0.1 or ~> 1.1", [hex: :cowboy, repo: "hexpm", optional: true]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [], [], "hexpm"},
|
||||
"poolboy": {:hex, :poolboy, "1.5.1", "6b46163901cfd0a1b43d692657ed9d7e599853b3b21b95ae5ae0a777cf9b6ca8", [], [], "hexpm"},
|
||||
"postgrex": {:hex, :postgrex, "0.13.3", "c277cfb2a9c5034d445a722494c13359e361d344ef6f25d604c2353185682bfc", [], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 1.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"ranch": {:hex, :ranch, "1.3.2", "e4965a144dc9fbe70e5c077c65e73c57165416a901bd02ea899cfd95aa890986", [], [], "hexpm"},
|
||||
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.1", "28a4d65b7f59893bc2c7de786dec1e1555bd742d336043fe644ae956c3497fbe", [], [], "hexpm"},
|
||||
"unicode_util_compat": {:hex, :unicode_util_compat, "0.3.1", "a1f612a7b512638634a603c8f401892afbf99b8ce93a45041f8aaca99cadb85e", [], [], "hexpm"},
|
||||
"uuid": {:hex, :uuid, "1.1.8", "e22fc04499de0de3ed1116b770c7737779f226ceefa0badb3592e64d5cfb4eb9", [], [], "hexpm"}}
|
||||
97
priv/gettext/en/LC_MESSAGES/errors.po
Normal file
97
priv/gettext/en/LC_MESSAGES/errors.po
Normal file
@@ -0,0 +1,97 @@
|
||||
## `msgid`s in this file come from POT (.pot) files.
|
||||
##
|
||||
## Do not add, change, or remove `msgid`s manually here as
|
||||
## they're tied to the ones in the corresponding POT file
|
||||
## (with the same domain).
|
||||
##
|
||||
## Use `mix gettext.extract --merge` or `mix gettext.merge`
|
||||
## to merge POT files into PO files.
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Language: en\n"
|
||||
|
||||
## From Ecto.Changeset.cast/4
|
||||
msgid "can't be blank"
|
||||
msgstr ""
|
||||
|
||||
## From Ecto.Changeset.unique_constraint/3
|
||||
msgid "has already been taken"
|
||||
msgstr ""
|
||||
|
||||
## From Ecto.Changeset.put_change/3
|
||||
msgid "is invalid"
|
||||
msgstr ""
|
||||
|
||||
## From Ecto.Changeset.validate_acceptance/3
|
||||
msgid "must be accepted"
|
||||
msgstr ""
|
||||
|
||||
## From Ecto.Changeset.validate_format/3
|
||||
msgid "has invalid format"
|
||||
msgstr ""
|
||||
|
||||
## From Ecto.Changeset.validate_subset/3
|
||||
msgid "has an invalid entry"
|
||||
msgstr ""
|
||||
|
||||
## From Ecto.Changeset.validate_exclusion/3
|
||||
msgid "is reserved"
|
||||
msgstr ""
|
||||
|
||||
## From Ecto.Changeset.validate_confirmation/3
|
||||
msgid "does not match confirmation"
|
||||
msgstr ""
|
||||
|
||||
## From Ecto.Changeset.no_assoc_constraint/3
|
||||
msgid "is still associated with this entry"
|
||||
msgstr ""
|
||||
|
||||
msgid "are still associated with this entry"
|
||||
msgstr ""
|
||||
|
||||
## From Ecto.Changeset.validate_length/3
|
||||
msgid "should be %{count} character(s)"
|
||||
msgid_plural "should be %{count} character(s)"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
msgid "should have %{count} item(s)"
|
||||
msgid_plural "should have %{count} item(s)"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
msgid "should be at least %{count} character(s)"
|
||||
msgid_plural "should be at least %{count} character(s)"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
msgid "should have at least %{count} item(s)"
|
||||
msgid_plural "should have at least %{count} item(s)"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
msgid "should be at most %{count} character(s)"
|
||||
msgid_plural "should be at most %{count} character(s)"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
msgid "should have at most %{count} item(s)"
|
||||
msgid_plural "should have at most %{count} item(s)"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
## From Ecto.Changeset.validate_number/3
|
||||
msgid "must be less than %{number}"
|
||||
msgstr ""
|
||||
|
||||
msgid "must be greater than %{number}"
|
||||
msgstr ""
|
||||
|
||||
msgid "must be less than or equal to %{number}"
|
||||
msgstr ""
|
||||
|
||||
msgid "must be greater than or equal to %{number}"
|
||||
msgstr ""
|
||||
|
||||
msgid "must be equal to %{number}"
|
||||
msgstr ""
|
||||
95
priv/gettext/errors.pot
Normal file
95
priv/gettext/errors.pot
Normal file
@@ -0,0 +1,95 @@
|
||||
## This file is a PO Template file.
|
||||
##
|
||||
## `msgid`s here are often extracted from source code.
|
||||
## Add new translations manually only if they're dynamic
|
||||
## translations that can't be statically extracted.
|
||||
##
|
||||
## Run `mix gettext.extract` to bring this file up to
|
||||
## date. Leave `msgstr`s empty as changing them here as no
|
||||
## effect: edit them in PO (`.po`) files instead.
|
||||
|
||||
## From Ecto.Changeset.cast/4
|
||||
msgid "can't be blank"
|
||||
msgstr ""
|
||||
|
||||
## From Ecto.Changeset.unique_constraint/3
|
||||
msgid "has already been taken"
|
||||
msgstr ""
|
||||
|
||||
## From Ecto.Changeset.put_change/3
|
||||
msgid "is invalid"
|
||||
msgstr ""
|
||||
|
||||
## From Ecto.Changeset.validate_acceptance/3
|
||||
msgid "must be accepted"
|
||||
msgstr ""
|
||||
|
||||
## From Ecto.Changeset.validate_format/3
|
||||
msgid "has invalid format"
|
||||
msgstr ""
|
||||
|
||||
## From Ecto.Changeset.validate_subset/3
|
||||
msgid "has an invalid entry"
|
||||
msgstr ""
|
||||
|
||||
## From Ecto.Changeset.validate_exclusion/3
|
||||
msgid "is reserved"
|
||||
msgstr ""
|
||||
|
||||
## From Ecto.Changeset.validate_confirmation/3
|
||||
msgid "does not match confirmation"
|
||||
msgstr ""
|
||||
|
||||
## From Ecto.Changeset.no_assoc_constraint/3
|
||||
msgid "is still associated with this entry"
|
||||
msgstr ""
|
||||
|
||||
msgid "are still associated with this entry"
|
||||
msgstr ""
|
||||
|
||||
## From Ecto.Changeset.validate_length/3
|
||||
msgid "should be %{count} character(s)"
|
||||
msgid_plural "should be %{count} character(s)"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
msgid "should have %{count} item(s)"
|
||||
msgid_plural "should have %{count} item(s)"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
msgid "should be at least %{count} character(s)"
|
||||
msgid_plural "should be at least %{count} character(s)"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
msgid "should have at least %{count} item(s)"
|
||||
msgid_plural "should have at least %{count} item(s)"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
msgid "should be at most %{count} character(s)"
|
||||
msgid_plural "should be at most %{count} character(s)"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
msgid "should have at most %{count} item(s)"
|
||||
msgid_plural "should have at most %{count} item(s)"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
## From Ecto.Changeset.validate_number/3
|
||||
msgid "must be less than %{number}"
|
||||
msgstr ""
|
||||
|
||||
msgid "must be greater than %{number}"
|
||||
msgstr ""
|
||||
|
||||
msgid "must be less than or equal to %{number}"
|
||||
msgstr ""
|
||||
|
||||
msgid "must be greater than or equal to %{number}"
|
||||
msgstr ""
|
||||
|
||||
msgid "must be equal to %{number}"
|
||||
msgstr ""
|
||||
11
priv/repo/seeds.exs
Normal file
11
priv/repo/seeds.exs
Normal file
@@ -0,0 +1,11 @@
|
||||
# Script for populating the database. You can run it as:
|
||||
#
|
||||
# mix run priv/repo/seeds.exs
|
||||
#
|
||||
# Inside the script, you can read and write to any of your
|
||||
# repositories directly:
|
||||
#
|
||||
# MyApp.Repo.insert!(%MyApp.SomeSchema{})
|
||||
#
|
||||
# We recommend using the bang functions (`insert!`, `update!`
|
||||
# and so on) as they will fail if something goes wrong.
|
||||
21
test/myapp_web/views/error_view_test.exs
Normal file
21
test/myapp_web/views/error_view_test.exs
Normal file
@@ -0,0 +1,21 @@
|
||||
defmodule MyAppWeb.ErrorViewTest do
|
||||
use MyAppWeb.ConnCase, async: true
|
||||
|
||||
# Bring render/3 and render_to_string/3 for testing custom views
|
||||
import Phoenix.View
|
||||
|
||||
test "renders 404.json" do
|
||||
assert render(MyAppWeb.ErrorView, "404.json", []) ==
|
||||
%{errors: %{detail: "Page not found"}}
|
||||
end
|
||||
|
||||
test "render 500.json" do
|
||||
assert render(MyAppWeb.ErrorView, "500.json", []) ==
|
||||
%{errors: %{detail: "Internal server error"}}
|
||||
end
|
||||
|
||||
test "render any other" do
|
||||
assert render(MyAppWeb.ErrorView, "505.json", []) ==
|
||||
%{errors: %{detail: "Internal server error"}}
|
||||
end
|
||||
end
|
||||
37
test/support/channel_case.ex
Normal file
37
test/support/channel_case.ex
Normal file
@@ -0,0 +1,37 @@
|
||||
defmodule MyAppWeb.ChannelCase do
|
||||
@moduledoc """
|
||||
This module defines the test case to be used by
|
||||
channel tests.
|
||||
|
||||
Such tests rely on `Phoenix.ChannelTest` and also
|
||||
import other functionality to make it easier
|
||||
to build common datastructures and query the data layer.
|
||||
|
||||
Finally, if the test case interacts with the database,
|
||||
it cannot be async. For this reason, every test runs
|
||||
inside a transaction which is reset at the beginning
|
||||
of the test unless the test case is marked as async.
|
||||
"""
|
||||
|
||||
use ExUnit.CaseTemplate
|
||||
|
||||
using do
|
||||
quote do
|
||||
# Import conveniences for testing with channels
|
||||
use Phoenix.ChannelTest
|
||||
|
||||
# The default endpoint for testing
|
||||
@endpoint MyAppWeb.Endpoint
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
setup tags do
|
||||
:ok = Ecto.Adapters.SQL.Sandbox.checkout(MyApp.Repo)
|
||||
unless tags[:async] do
|
||||
Ecto.Adapters.SQL.Sandbox.mode(MyApp.Repo, {:shared, self()})
|
||||
end
|
||||
:ok
|
||||
end
|
||||
|
||||
end
|
||||
38
test/support/conn_case.ex
Normal file
38
test/support/conn_case.ex
Normal file
@@ -0,0 +1,38 @@
|
||||
defmodule MyAppWeb.ConnCase do
|
||||
@moduledoc """
|
||||
This module defines the test case to be used by
|
||||
tests that require setting up a connection.
|
||||
|
||||
Such tests rely on `Phoenix.ConnTest` and also
|
||||
import other functionality to make it easier
|
||||
to build common datastructures and query the data layer.
|
||||
|
||||
Finally, if the test case interacts with the database,
|
||||
it cannot be async. For this reason, every test runs
|
||||
inside a transaction which is reset at the beginning
|
||||
of the test unless the test case is marked as async.
|
||||
"""
|
||||
|
||||
use ExUnit.CaseTemplate
|
||||
|
||||
using do
|
||||
quote do
|
||||
# Import conveniences for testing with connections
|
||||
use Phoenix.ConnTest
|
||||
import MyAppWeb.Router.Helpers
|
||||
|
||||
# The default endpoint for testing
|
||||
@endpoint MyAppWeb.Endpoint
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
setup tags do
|
||||
:ok = Ecto.Adapters.SQL.Sandbox.checkout(MyApp.Repo)
|
||||
unless tags[:async] do
|
||||
Ecto.Adapters.SQL.Sandbox.mode(MyApp.Repo, {:shared, self()})
|
||||
end
|
||||
{:ok, conn: Phoenix.ConnTest.build_conn()}
|
||||
end
|
||||
|
||||
end
|
||||
53
test/support/data_case.ex
Normal file
53
test/support/data_case.ex
Normal file
@@ -0,0 +1,53 @@
|
||||
defmodule MyApp.DataCase do
|
||||
@moduledoc """
|
||||
This module defines the setup for tests requiring
|
||||
access to the application's data layer.
|
||||
|
||||
You may define functions here to be used as helpers in
|
||||
your tests.
|
||||
|
||||
Finally, if the test case interacts with the database,
|
||||
it cannot be async. For this reason, every test runs
|
||||
inside a transaction which is reset at the beginning
|
||||
of the test unless the test case is marked as async.
|
||||
"""
|
||||
|
||||
use ExUnit.CaseTemplate
|
||||
|
||||
using do
|
||||
quote do
|
||||
alias MyApp.Repo
|
||||
|
||||
import Ecto
|
||||
import Ecto.Changeset
|
||||
import Ecto.Query
|
||||
import MyApp.DataCase
|
||||
end
|
||||
end
|
||||
|
||||
setup tags do
|
||||
:ok = Ecto.Adapters.SQL.Sandbox.checkout(MyApp.Repo)
|
||||
|
||||
unless tags[:async] do
|
||||
Ecto.Adapters.SQL.Sandbox.mode(MyApp.Repo, {:shared, self()})
|
||||
end
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
@doc """
|
||||
A helper that transform changeset errors to a map of messages.
|
||||
|
||||
assert {:error, changeset} = Accounts.create_user(%{password: "short"})
|
||||
assert "password is too short" in errors_on(changeset).password
|
||||
assert %{password: ["password is too short"]} = errors_on(changeset)
|
||||
|
||||
"""
|
||||
def errors_on(changeset) do
|
||||
Ecto.Changeset.traverse_errors(changeset, fn {message, opts} ->
|
||||
Enum.reduce(opts, message, fn {key, value}, acc ->
|
||||
String.replace(acc, "%{#{key}}", to_string(value))
|
||||
end)
|
||||
end)
|
||||
end
|
||||
end
|
||||
4
test/test_helper.exs
Normal file
4
test/test_helper.exs
Normal file
@@ -0,0 +1,4 @@
|
||||
ExUnit.start()
|
||||
|
||||
Ecto.Adapters.SQL.Sandbox.mode(MyApp.Repo, :manual)
|
||||
|
||||
Reference in New Issue
Block a user