diff --git a/.travis.yml b/.travis.yml index a993fe6b..bd635015 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,8 @@ language: elixir elixir: - - 1.10.4 + - 1.12.3 otp_release: - - 23.0.3 + - 24.0.2 services: - postgresql env: diff --git a/Procfile b/Procfile new file mode 100644 index 00000000..8e9edc36 --- /dev/null +++ b/Procfile @@ -0,0 +1 @@ +web: mix phx.server \ No newline at end of file diff --git a/assets/.babelrc b/assets/.babelrc deleted file mode 100644 index ce33b24d..00000000 --- a/assets/.babelrc +++ /dev/null @@ -1,5 +0,0 @@ -{ - "presets": [ - "@babel/preset-env" - ] -} diff --git a/assets/js/app.js b/assets/js/app.js index 34ea13ec..ed2dcae3 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -1,17 +1,21 @@ -// We need to import the CSS so that webpack will load it. -// The MiniCssExtractPlugin is used to separate it out into -// its own CSS file. -import css from '../css/app.css'; +// We import the CSS which is extracted to its own file by esbuild. +// Remove this line if you add a your own CSS build pipeline (e.g postcss). +import "../css/app.css" -// webpack automatically bundles all modules in your -// entry points. Those entry points can be configured -// in "webpack.config.js". -// -// Import dependencies -// -import 'phoenix_html'; +// Include phoenix_html to handle method=PUT/DELETE in forms and buttons. +import "phoenix_html" +// Establish Phoenix Socket and LiveView configuration. +import {Socket} from "phoenix" +import {LiveSocket} from "phoenix_live_view" -// Import local files -// -// Local files can be imported directly using relative paths, for example: -// import socket from "./socket" +let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content") +let liveSocket = new LiveSocket("/live", Socket, {params: {_csrf_token: csrfToken}}) + +// connect if there are any LiveViews on the page +liveSocket.connect() + +// expose liveSocket on window for web console debug logs and latency simulation: +// >> liveSocket.enableDebug() +// >> liveSocket.enableLatencySim(1000) // enabled for duration of browser session +// >> liveSocket.disableLatencySim() +window.liveSocket = liveSocket \ No newline at end of file diff --git a/assets/package.json b/assets/package.json deleted file mode 100644 index cd2bb9a7..00000000 --- a/assets/package.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "repository": {}, - "license": "MIT", - "scripts": { - "deploy": "webpack --mode production", - "watch": "webpack --mode development --watch" - }, - "dependencies": { - "phoenix": "file:../deps/phoenix", - "phoenix_html": "file:../deps/phoenix_html" - }, - "devDependencies": { - "@babel/core": "^7.0.0", - "@babel/preset-env": "^7.0.0", - "babel-loader": "^8.0.0", - "copy-webpack-plugin": "^5.1.1", - "css-loader": "^3.4.2", - "mini-css-extract-plugin": "^0.9.0", - "optimize-css-assets-webpack-plugin": "^5.0.1", - "terser-webpack-plugin": "^2.3.2", - "webpack": "4.41.5", - "webpack-cli": "^3.3.2" - } -} diff --git a/assets/webpack.config.js b/assets/webpack.config.js deleted file mode 100644 index c0fa8881..00000000 --- a/assets/webpack.config.js +++ /dev/null @@ -1,43 +0,0 @@ -/* global require, module, __dirname */ - -const path = require('path'); -const glob = require('glob'); -const MiniCssExtractPlugin = require('mini-css-extract-plugin'); -const TerserPlugin = require('terser-webpack-plugin'); -const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin'); -const CopyWebpackPlugin = require('copy-webpack-plugin'); - -module.exports = (env, options) => ({ - optimization: { - minimizer: [ - new TerserPlugin({ cache: true, parallel: true, sourceMap: false }), - new OptimizeCSSAssetsPlugin({}) - ] - }, - entry: { - './js/app.js': glob.sync('./vendor/**/*.js').concat(['./js/app.js']) - }, - output: { - filename: 'app.js', - path: path.resolve(__dirname, '../priv/static/js') - }, - module: { - rules: [ - { - test: /\.js$/, - exclude: /node_modules/, - use: { - loader: 'babel-loader' - } - }, - { - test: /\.css$/, - use: [MiniCssExtractPlugin.loader, 'css-loader'] - } - ] - }, - plugins: [ - new MiniCssExtractPlugin({ filename: '../css/app.css' }), - new CopyWebpackPlugin([{ from: 'static/', to: '../' }]) - ] -}); diff --git a/config/config.exs b/config/config.exs index 0132fd14..6deda052 100644 --- a/config/config.exs +++ b/config/config.exs @@ -32,3 +32,13 @@ config :joken, default_signer: System.get_env("SECRET_KEY_BASE") # 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" + +# https://gist.github.com/chrismccord/2ab350f154235ad4a4d0f4de6decba7b +# Configure esbuild (the version is required) +config :esbuild, + version: "0.12.18", + default: [ + args: ~w(js/app.js --bundle --target=es2016 --outdir=../priv/static/assets), + cd: Path.expand("../assets", __DIR__), + env: %{"NODE_PATH" => Path.expand("../deps", __DIR__)} + ] \ No newline at end of file diff --git a/config/dev.exs b/config/dev.exs index e45f2aa3..b60852cb 100644 --- a/config/dev.exs +++ b/config/dev.exs @@ -21,15 +21,9 @@ config :auth, AuthWeb.Endpoint, code_reloader: true, check_origin: false, watchers: [ - node: [ - "node_modules/webpack/bin/webpack.js", - "--mode", - "development", - "--watch-stdin", - cd: Path.expand("../assets", __DIR__) - ] + # Start the esbuild watcher by calling Esbuild.install_and_run(:default, args) + esbuild: {Esbuild, :install_and_run, [:default, ~w(--sourcemap=inline --watch)]} ] - # ## SSL Support # # In order to use HTTPS in development, a self-signed @@ -73,4 +67,4 @@ config :logger, :console, format: "[$level] $message\n" config :phoenix, :stacktrace_depth, 20 # Initialize plugs at runtime for faster development compilation -config :phoenix, :plug_init_mode, :runtime +config :phoenix, :plug_init_mode, :runtime \ No newline at end of file diff --git a/config/test.exs b/config/test.exs index e601d5ef..83ad1245 100644 --- a/config/test.exs +++ b/config/test.exs @@ -12,7 +12,12 @@ config :auth, Auth.Repo, # you can enable the server option below. config :auth, AuthWeb.Endpoint, http: [port: 4002], - server: false + server: true # https://elixirforum.com/t/wallaby-with-phoenix-1-16-rc0/42352/9 # Print only warnings and errors during test config :logger, level: :warn + +config :elixir_auth_google, + client_id: "631770888008-6n0oruvsm16kbkqg6u76p5cv5kfkcekt.apps.googleusercontent.com", + client_secret: "MHxv6-RGF5nheXnxh1b0LNDq", + httpoison_mock: true \ No newline at end of file diff --git a/coveralls.json b/coveralls.json index 6aadc599..b80b5c2c 100644 --- a/coveralls.json +++ b/coveralls.json @@ -6,6 +6,8 @@ "test/", "lib/auth/application.ex", "lib/auth_web.ex", - "lib/auth_web/views/error_helpers.ex" + "lib/auth_web/views/error_helpers.ex", + "lib/auth_web/channels/user_socket.ex", + "lib/auth_web/telemetry.ex" ] } diff --git a/elixir_buildpack.config b/elixir_buildpack.config index 488a1dc3..1d244656 100644 --- a/elixir_buildpack.config +++ b/elixir_buildpack.config @@ -1,8 +1,12 @@ # Elixir version -elixir_version=1.10.4 +elixir_version=1.12.3 # Erlang version # available versions https://github.com/HashNuke/heroku-buildpack-elixir-otp-builds/blob/master/otp-versions -erlang_version=23.0.3 +erlang_version=23.3.2 -# always_rebuild=true +always_rebuild=true + +# Invoke assets.deploy defined in your mix.exs to deploy assets with esbuild +# Note we nuke the esbuild executable from the image +hook_post_compile="eval mix assets.deploy && rm -f _build/esbuild" \ No newline at end of file diff --git a/lib/auth/application.ex b/lib/auth/application.ex index 9c41f202..37e90da2 100644 --- a/lib/auth/application.ex +++ b/lib/auth/application.ex @@ -5,16 +5,19 @@ defmodule Auth.Application do use Application + @impl true def start(_type, _args) do - # List all child processes to be supervised children = [ # Start the Ecto repository Auth.Repo, - # Start the endpoint when the application starts + # Start the Telemetry supervisor + AuthWeb.Telemetry, + # Start the PubSub system {Phoenix.PubSub, name: Auth.PubSub}, + # Start the Endpoint (http/https) AuthWeb.Endpoint - # Starts a worker by calling: Auth.Worker.start_link(arg) - # {Auth.Worker, arg}, + # Start a worker by calling: Auth.Worker.start_link(arg) + # {Auth.Worker, arg} ] # See https://hexdocs.pm/elixir/Supervisor.html @@ -25,12 +28,12 @@ defmodule Auth.Application do # Tell Phoenix to update the endpoint configuration # whenever the application is updated. - # Sadly this is untestable hence ignoring it. + # coveralls-ignore-start + @impl true def config_change(changed, _new, removed) do AuthWeb.Endpoint.config_change(changed, removed) :ok end - # coveralls-ignore-stop end diff --git a/lib/auth/mailer.ex b/lib/auth/mailer.ex new file mode 100644 index 00000000..0482ee68 --- /dev/null +++ b/lib/auth/mailer.ex @@ -0,0 +1,3 @@ +defmodule Auth.Mailer do + use Swoosh.Mailer, otp_app: :auth +end diff --git a/lib/auth_web.ex b/lib/auth_web.ex index 5c0cb570..46c539bf 100644 --- a/lib/auth_web.ex +++ b/lib/auth_web.ex @@ -34,22 +34,38 @@ defmodule AuthWeb do namespace: AuthWeb # Import convenience functions from controllers - import Phoenix.Controller, only: [get_flash: 1, get_flash: 2, view_module: 1] + import Phoenix.Controller, + only: [get_flash: 1, get_flash: 2, view_module: 1, view_template: 1] - # Use all HTML functionality (forms, tags, etc) - use Phoenix.HTML + # Include shared imports and aliases for views + unquote(view_helpers()) + end + end - import AuthWeb.ErrorHelpers - import AuthWeb.Gettext - alias AuthWeb.Router.Helpers, as: Routes + def live_view do + quote do + use Phoenix.LiveView, + layout: {AuthWeb.LayoutView, "live.html"} + + unquote(view_helpers()) + end + end + + def live_component do + quote do + use Phoenix.LiveComponent + + unquote(view_helpers()) end end def router do quote do use Phoenix.Router + import Plug.Conn import Phoenix.Controller + import Phoenix.LiveView.Router end end @@ -60,6 +76,23 @@ defmodule AuthWeb do end end + defp view_helpers do + quote do + # Use all HTML functionality (forms, tags, etc) + use Phoenix.HTML + + # Import LiveView and .heex helpers (live_render, live_patch, <.form>, etc) + import Phoenix.LiveView.Helpers + + # Import basic rendering functionality (render, render_layout, etc) + import Phoenix.View + + import AuthWeb.ErrorHelpers + import AuthWeb.Gettext + alias AuthWeb.Router.Helpers, as: Routes + end + end + @doc """ When used, dispatch to the appropriate controller/view/etc. """ diff --git a/lib/auth_web/controllers/auth_controller.ex b/lib/auth_web/controllers/auth_controller.ex index fd26085b..0fd62b4c 100644 --- a/lib/auth_web/controllers/auth_controller.ex +++ b/lib/auth_web/controllers/auth_controller.ex @@ -222,6 +222,7 @@ defmodule AuthWeb.AuthController do def google_handler(conn, %{"code" => code, "state" => state}) do {:ok, token} = ElixirAuthGoogle.get_token(code, conn) {:ok, profile} = ElixirAuthGoogle.get_user_profile(token.access_token) + # save profile to people: app_id = get_app_id(state) person = Person.create_google_person(Map.merge(profile, %{app_id: app_id})) diff --git a/lib/auth_web/endpoint.ex b/lib/auth_web/endpoint.ex index a665b17a..ebd30676 100644 --- a/lib/auth_web/endpoint.ex +++ b/lib/auth_web/endpoint.ex @@ -10,9 +10,11 @@ defmodule AuthWeb.Endpoint do signing_salt: "Fir8x4nA" ] - socket "/socket", AuthWeb.UserSocket, - websocket: true, - longpoll: false + socket "/live", Phoenix.LiveView.Socket, websocket: [connect_info: [session: @session_options]] + + # socket "/socket", AuthWeb.UserSocket, + # websocket: true, + # longpoll: false # Serve at "/" the static files from "priv/static" directory. # @@ -22,7 +24,7 @@ defmodule AuthWeb.Endpoint do at: "/", from: :auth, gzip: false, - only: ~w(css fonts images js favicon.ico robots.txt) + only: ~w(assets fonts images favicon.ico robots.txt) # Code reloading can be explicitly enabled under the # :code_reloader configuration of your endpoint. @@ -30,8 +32,13 @@ defmodule AuthWeb.Endpoint do socket "/phoenix/live_reload/socket", Phoenix.LiveReloader.Socket plug Phoenix.LiveReloader plug Phoenix.CodeReloader + plug Phoenix.Ecto.CheckRepoStatus, otp_app: :auth end + plug Phoenix.LiveDashboard.RequestLogger, + param_key: "request_logger", + cookie_key: "request_logger" + plug Plug.RequestId plug Plug.Telemetry, event_prefix: [:phoenix, :endpoint] @@ -44,4 +51,4 @@ defmodule AuthWeb.Endpoint do plug Plug.Head plug Plug.Session, @session_options plug AuthWeb.Router -end +end \ No newline at end of file diff --git a/lib/auth_web/router.ex b/lib/auth_web/router.ex index 25eff670..2bd45192 100644 --- a/lib/auth_web/router.ex +++ b/lib/auth_web/router.ex @@ -67,4 +67,29 @@ defmodule AuthWeb.Router do get "/approles/:client_id", ApiController, :approles get "/personroles/:person_id/:client_id", ApiController, :personroles end + + # Added in Phoenix 1.6 ... Nice-to-have. Not tested. + # coveralls-ignore-start + if Mix.env() in [:dev, :test] do + import Phoenix.LiveDashboard.Router + + scope "/" do + pipe_through :browser + live_dashboard "/dashboard", metrics: AuthWeb.Telemetry + end + end + # coveralls-ignore-stop + + + # Enables the Swoosh mailbox preview in development. + # + # Note that preview only shows emails that were sent by the same + # node running the Phoenix server. + if Mix.env() == :dev do + scope "/dev" do + pipe_through :browser + + forward "/mailbox", Plug.Swoosh.MailboxPreview + end + end end diff --git a/lib/auth_web/telemetry.ex b/lib/auth_web/telemetry.ex new file mode 100644 index 00000000..c5043f9a --- /dev/null +++ b/lib/auth_web/telemetry.ex @@ -0,0 +1,71 @@ +defmodule AuthWeb.Telemetry do + use Supervisor + import Telemetry.Metrics + + def start_link(arg) do + Supervisor.start_link(__MODULE__, arg, name: __MODULE__) + end + + @impl true + def init(_arg) do + children = [ + # Telemetry poller will execute the given period measurements + # every 10_000ms. Learn more here: https://hexdocs.pm/telemetry_metrics + {:telemetry_poller, measurements: periodic_measurements(), period: 10_000} + # Add reporters as children of your supervision tree. + # {Telemetry.Metrics.ConsoleReporter, metrics: metrics()} + ] + + Supervisor.init(children, strategy: :one_for_one) + end + + def metrics do + [ + # Phoenix Metrics + summary("phoenix.endpoint.stop.duration", + unit: {:native, :millisecond} + ), + summary("phoenix.router_dispatch.stop.duration", + tags: [:route], + unit: {:native, :millisecond} + ), + + # Database Metrics + summary("auth.repo.query.total_time", + unit: {:native, :millisecond}, + description: "The sum of the other measurements" + ), + summary("auth.repo.query.decode_time", + unit: {:native, :millisecond}, + description: "The time spent decoding the data received from the database" + ), + summary("auth.repo.query.query_time", + unit: {:native, :millisecond}, + description: "The time spent executing the query" + ), + summary("auth.repo.query.queue_time", + unit: {:native, :millisecond}, + description: "The time spent waiting for a database connection" + ), + summary("auth.repo.query.idle_time", + unit: {:native, :millisecond}, + description: + "The time the connection spent waiting before being checked out for the query" + ), + + # VM Metrics + summary("vm.memory.total", unit: {:byte, :kilobyte}), + summary("vm.total_run_queue_lengths.total"), + summary("vm.total_run_queue_lengths.cpu"), + summary("vm.total_run_queue_lengths.io") + ] + end + + defp periodic_measurements do + [ + # A module, function and arguments to be invoked periodically. + # This function must call :telemetry.execute/3 and a metric must be added above. + # {AuthWeb, :count_users, []} + ] + end +end diff --git a/lib/auth_web/templates/layout/app.html.eex b/lib/auth_web/templates/layout/app.html.eex index 93c27abe..a775c927 100644 --- a/lib/auth_web/templates/layout/app.html.eex +++ b/lib/auth_web/templates/layout/app.html.eex @@ -5,8 +5,8 @@
<%= get_flash(@conn, :error) %>
<%= @inner_content %> - +