Skip to content

Commit

Permalink
Install and configure Oban Web (#472)
Browse files Browse the repository at this point in the history
* Install and configure LiveView

* Add Oban Web mix dep

* Configure required JS and mount oban in /admin

* Add a resolver that limits Oban Web access to admins

* Oban improvements

- Only install in prod and dev
- Upgrade to 2.10 (RC for now)
- Use new resolver 'forbidden' feature
- Remove no-longer-needed plugins
- Only load ObanWeb code when installed

* Do not add dev by default to oban_envs

Otherwise this will not work for contributors by default.

If OBAN_LICENSE_KEY env var is set, it's OK to add :dev too.
Maybe :test as well?

Signed-off-by: Gerhard Lazu <[email protected]>

* Fix mix deps.get in dev & test

I had to remove oban_met & oban_web from mix.lock, otherwise getting
dependencies would fail with:

    ** (Mix) Unknown repository "oban", add new repositories with the `mix hex.repo add` task

Signed-off-by: Gerhard Lazu <[email protected]>

* Only build the production image if OBAN_LICENSE_KEY env var is set

Otherwise it will fail - think about forks & PRs coming from forks.

Signed-off-by: Gerhard Lazu <[email protected]>

* Add Oban key fingerprint & license key to our CI/CD pipeline

Signed-off-by: Gerhard Lazu <[email protected]>

---------

Co-authored-by: Gerhard Lazu <[email protected]>
  • Loading branch information
jerodsanto and gerhard authored Sep 28, 2023
1 parent ee0171f commit 840bd9d
Show file tree
Hide file tree
Showing 17 changed files with 154 additions and 36 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/dagger_on_fly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ jobs:
R2_API_HOST: "${{ secrets.R2_API_HOST }}"
R2_ACCESS_KEY_ID: "${{ secrets.R2_ACCESS_KEY_ID }}"
R2_SECRET_ACCESS_KEY: "${{ secrets.R2_SECRET_ACCESS_KEY }}"
OBAN_KEY_FINGERPRINT: "${{ secrets.OBAN_KEY_FINGERPRINT }}"
OBAN_LICENSE_KEY: "${{ secrets.OBAN_LICENSE_KEY }}"
run: |
echo "🤨 Can we ping ${FLY_DNS_SERVER:?must be set}?"
ping6 -c 3 "$FLY_DNS_SERVER"
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/dagger_on_github.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ jobs:
R2_API_HOST: "${{ secrets.R2_API_HOST }}"
R2_ACCESS_KEY_ID: "${{ secrets.R2_ACCESS_KEY_ID }}"
R2_SECRET_ACCESS_KEY: "${{ secrets.R2_SECRET_ACCESS_KEY }}"
OBAN_KEY_FINGERPRINT: "${{ secrets.OBAN_KEY_FINGERPRINT }}"
OBAN_LICENSE_KEY: "${{ secrets.OBAN_LICENSE_KEY }}"
run: |
cd magefiles
go run main.go -w ../ ci cd
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/dagger_on_k8s.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ jobs:
R2_API_HOST: "${{ secrets.R2_API_HOST }}"
R2_ACCESS_KEY_ID: "${{ secrets.R2_ACCESS_KEY_ID }}"
R2_SECRET_ACCESS_KEY: "${{ secrets.R2_SECRET_ACCESS_KEY }}"
OBAN_KEY_FINGERPRINT: "${{ secrets.OBAN_KEY_FINGERPRINT }}"
OBAN_LICENSE_KEY: "${{ secrets.OBAN_LICENSE_KEY }}"
run: |
cd magefiles
go run main.go -w ../ ci
Expand Down
8 changes: 8 additions & 0 deletions assets/admin/admin.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import "phoenix_html";
import {Socket} from "phoenix"
import {LiveSocket} from "phoenix_live_view"
import "regenerator-runtime/runtime";

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();

import benefitView from "views/benefitView";
import episodeView from "views/episodeView";
import metacastView from "views/metacastView";
Expand Down
1 change: 1 addition & 0 deletions assets/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"normalize.css": "^8.0.0",
"phoenix": "file:../deps/phoenix",
"phoenix_html": "file:../deps/phoenix_html",
"phoenix_live_view": "file:../deps/phoenix_live_view",
"popper.js": "^1.12.5",
"prismjs": "^1.15.0",
"siema": "^1.5.1",
Expand Down
7 changes: 5 additions & 2 deletions assets/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2862,10 +2862,13 @@ pegjs@^0.10.0:
integrity sha512-qI5+oFNEGi3L5HAxDwN2LA4Gg7irF70Zs25edhjld9QemOgp0CbvMtbFcMvFtEo1OityPrcCzkQFB8JP/hxgow==

"phoenix@file:../deps/phoenix":
version "1.6.14"
version "1.7.3"

"phoenix_html@file:../deps/phoenix_html":
version "3.2.0"
version "3.3.1"

"phoenix_live_view@file:../deps/phoenix_live_view":
version "0.19.3"

picocolors@^1.0.0:
version "1.0.0"
Expand Down
3 changes: 2 additions & 1 deletion config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ config :changelog, ChangelogWeb.Endpoint,
"PABstVJCyPEcRByCU8tmSZjv0UfoV+UeBlXNRigy4ba221RzqfN82qwsKvA5bJzi"
),
render_errors: [view: ChangelogWeb.ErrorView, accepts: ~w(html json), layout: false],
pubsub_server: Changelog.PubSub
pubsub_server: Changelog.PubSub,
live_view: [signing_salt: "+GzuQhsbhBJIqc4ctHLGjo+D2ZohVqNW"]

config :changelog,
buffer_token: SecretOrEnv.get("BUFFER_TOKEN"),
Expand Down
25 changes: 25 additions & 0 deletions lib/changelog_web.ex
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,28 @@ defmodule ChangelogWeb do
end
end

def live_view do
quote do
use Phoenix.LiveView,
layout: {ChangelogWeb.LayoutView, :live}

unquote(html_helpers())
end
end

defp html_helpers do
quote do
# HTML escaping functionality
import Phoenix.HTML
# Core UI components and translation
import HelloWeb.CoreComponents
import HelloWeb.Gettext

# Shortcut for generating JS commands
alias Phoenix.LiveView.JS
end
end

def public_view do
quote do
use Phoenix.View,
Expand All @@ -83,6 +105,7 @@ defmodule ChangelogWeb do

use Phoenix.HTML

import Phoenix.Component
import Phoenix.Controller,
only: [current_url: 1, get_flash: 1, get_flash: 2, view_module: 1]

Expand All @@ -102,6 +125,8 @@ defmodule ChangelogWeb do
def router do
quote do
use Phoenix.Router

import Phoenix.LiveView.Router
end
end

Expand Down
36 changes: 19 additions & 17 deletions lib/changelog_web/endpoint.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,24 @@ defmodule ChangelogWeb.Endpoint do
use Sentry.PlugCapture
use Phoenix.Endpoint, otp_app: :changelog

cookie_domain =
if Mix.env() == :prod do
".changelog.com"
else
System.get_env("HOST", "localhost")
end

@session_options [
store: :cookie,
key: "_changelog_key",
encryption_salt: System.get_env("ENCRYPTION_SALT") || "8675309",
max_age: 31_536_000,
signing_salt: System.get_env("SIGNING_SALT") || "8bAOekZm",
extra: "SameSite=Lax",
domain: cookie_domain
]

socket "/live", Phoenix.LiveView.Socket, websocket: [connect_info: [session: @session_options]]
socket "/socket", ChangelogWeb.UserSocket,
# or list of options
websocket: true
Expand Down Expand Up @@ -61,22 +79,6 @@ defmodule ChangelogWeb.Endpoint do

plug Plug.MethodOverride
plug Plug.Head

cookie_domain =
if Mix.env() == :prod do
".changelog.com"
else
System.get_env("HOST", "localhost")
end

plug Plug.Session,
store: :cookie,
key: "_changelog_key",
encryption_salt: System.get_env("ENCRYPTION_SALT") || "8675309",
max_age: 31_536_000,
signing_salt: System.get_env("SIGNING_SALT") || "8bAOekZm",
extra: "SameSite=Lax",
domain: cookie_domain

plug Plug.Session, @session_options
plug ChangelogWeb.Router
end
42 changes: 42 additions & 0 deletions lib/changelog_web/oban_web.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
defmodule ChangelogWeb.ObanWeb do
@moduledoc """
Simple macro to conditionally load Oban.Web only if already loaded. This
allows us to include it only in the production release and hence make it a lot
easier on potential open source contributors, avoiding the problem of sharing
the oban key and/or them hacking the code to get it working
Thanks to the team at Glific for showing us the way on this.
"""

defmacro __using__(_) do
if Code.ensure_loaded?(Oban.Web.Router) do
defmodule Resolver do
@behaviour Oban.Web.Resolver

@impl true
def resolve_user(conn) do
conn.assigns.current_user
end

@impl true
def resolve_access(user) do
if Changelog.Policies.AdminsOnly.index(user) do
:all
else
{:forbidden, "/in"}
end
end
end

quote do
import Oban.Web.Router

scope "/admin" do
pipe_through [:browser, :admin]

oban_dashboard("/oban", resolver: ChangelogWeb.ObanWeb.Resolver)
end
end
end
end
end
6 changes: 4 additions & 2 deletions lib/changelog_web/router.ex
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
defmodule ChangelogWeb.Router do
use ChangelogWeb, :router
use ChangelogWeb.ObanWeb

alias ChangelogWeb.Plug

Expand All @@ -17,14 +18,15 @@ defmodule ChangelogWeb.Router do

pipeline :admin do
plug Plug.AdminLayoutPlug
plug :protect_from_forgery
end

pipeline :browser do
plug :accepts, ["html", "js"]
plug Plug.FlocOff
plug :fetch_session
plug Plug.Turbolinks
plug :fetch_flash
plug :fetch_live_flash
plug :put_secure_browser_headers
plug Plug.Authenticate, repo: Changelog.Repo
plug Plug.AllowFraming
Expand Down Expand Up @@ -57,7 +59,7 @@ defmodule ChangelogWeb.Router do
end

scope "/admin", ChangelogWeb.Admin, as: :admin do
pipe_through [:admin, :browser]
pipe_through [:browser, :admin]

get "/", PageController, :index
get "/downloads", PageController, :downloads
Expand Down
1 change: 1 addition & 0 deletions lib/changelog_web/templates/layout/admin.html.eex
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1">
<meta name="csrf-token" content={Plug.CSRFProtection.get_csrf_token()} />

<title><%= Meta.AdminTitle.get(:page, @conn) %></title>
<link rel="stylesheet" href="<%= url(~p"/css/admin.css") %>">
Expand Down
2 changes: 1 addition & 1 deletion magefiles/image/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ func (image *Image) WithAppDeps() *Image {
"echo", "Fetch app deps...",
}).
WithExec([]string{
"sh", "-c", "mix deps.get",
"sh", "-c", "mix deps.get --only $MIX_ENV",
}).
WithExec([]string{
"sh", "-c", "echo \"Ensure app deps are present & OK...\"",
Expand Down
21 changes: 20 additions & 1 deletion magefiles/image/production.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ import (
func (image *Image) Production() *Image {
productionImage := image.Runtime().
WithAppSrc().
WithAppDeps().
WithProdEnv().
WithObanRepo().
WithAppDeps().
WithAppCompiled().
WithAppStaticAssets().
WithAppLegacyAssets().
Expand All @@ -38,6 +39,11 @@ func (image *Image) Production() *Image {
// I don’t think this is sufferable much longer
// https://github.com/thechangelog/changelog.com/actions/runs/4430462525
func (image *Image) ProductionClean() *Image {
obanLicenseKey := os.Getenv("OBAN_LICENSE_KEY")
if obanLicenseKey == "" {
fmt.Printf("\n👮 Building the production image requires an OBAN_LICENSE_KEY\n")
return image
}
app := image.Production().container.
WithExec([]string{
"cp", "--recursive", "--preserve=mode,ownership,timestamps", "/app", "/app.prod",
Expand Down Expand Up @@ -158,6 +164,19 @@ func (image *Image) WithProdEnv() *Image {
return image
}

func (image *Image) WithObanRepo() *Image {
OBAN_KEY_FINGERPRINT := env.Get(image.ctx, image.dag.Host(), "OBAN_KEY_FINGERPRINT").Secret()
OBAN_LICENSE_KEY := env.Get(image.ctx, image.dag.Host(), "OBAN_LICENSE_KEY").Secret()
image.container = image.container.
WithSecretVariable("OBAN_KEY_FINGERPRINT", OBAN_KEY_FINGERPRINT).
WithSecretVariable("OBAN_LICENSE_KEY", OBAN_LICENSE_KEY).
WithExec([]string{
"sh", "-c", "mix hex.repo add oban https://getoban.pro/repo --fetch-public-key $OBAN_KEY_FINGERPRINT --auth-key $OBAN_LICENSE_KEY",
})

return image.OK()
}

func (image *Image) WithGitAuthor() *Image {
image.container = image.container.
WithNewFile("COMMIT_USER", dagger.ContainerWithNewFileOpts{
Expand Down
2 changes: 1 addition & 1 deletion magefiles/image/test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ func (image *Image) Test() *Image {
return image.
Runtime().
WithAppSrc().
WithAppDeps().
WithTestEnv().
WithAppDeps().
WithAppCompiled().
WithPostgreSQL("changelog_test").
WithTest().
Expand Down
7 changes: 7 additions & 0 deletions mix.exs
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
defmodule Changelog.Mixfile do
use Mix.Project

@oban_envs [:prod]
if System.get_env("OBAN_LICENSE_KEY") do
@oban_envs [:dev, :prod]
end

Code.compile_file("config/secret_or_env.exs")

def project do
Expand Down Expand Up @@ -40,9 +45,11 @@ defmodule Changelog.Mixfile do
{:ecto_sql, "~> 3.10"},
{:postgrex, ">= 0.0.0"},
{:phoenix_html, "~> 3.3"},
{:phoenix_live_view, "~> 0.19.0"},
{:phoenix_live_reload, "~> 1.2", only: :dev},
{:plug_cowboy, "~> 2.5"},
{:oban, "~> 2.15"},
{:oban_web, "~> 2.10.0-rc.2", repo: "oban", only: @oban_envs},
{:timex, "~> 3.0"},
{:scrivener_ecto, "~> 2.0"},
{:scrivener_html, "~> 1.8", github: "jerodsanto/scrivener_html", branch: "phx-1-7"},
Expand Down
Loading

0 comments on commit 840bd9d

Please sign in to comment.