-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Basic infrastructure to support Feldspar
- Loading branch information
Showing
18 changed files
with
302 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
export const FeldsparApp = { | ||
mounted() { | ||
const iframe = this.getIframe(); | ||
if (iframe.contentDocument.readyState === "complete") { | ||
this.onFrameLoaded(); | ||
} | ||
else { | ||
iframe.contentDocument.addEventListener("load", ()=>{ this.onFrameLoaded() }); | ||
} | ||
}, | ||
|
||
getIframe() { | ||
return this.el.querySelector("iframe"); | ||
}, | ||
|
||
onFrameLoaded() { | ||
this.channel = new MessageChannel(); | ||
this.channel.port1.onmessage = (e) => { | ||
this.handleMessage(e); | ||
}; | ||
this.getIframe().contentWindow.postMessage("init", "*", [this.channel.port2]); | ||
}, | ||
|
||
handleMessage(e) { | ||
this.pushEvent("app_event", e.data); | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -71,13 +71,14 @@ config :core, Systems.Email.Mailer, | |
|
||
config :core, :apns_backend, Core.APNS.LoggingBackend | ||
|
||
config :core, | ||
:static_path, | ||
static_path = | ||
File.cwd!() | ||
|> Path.join("tmp") | ||
|> Path.join("uploads") | ||
|> tap(&File.mkdir_p!/1) | ||
|
||
config :core, :static_path, static_path | ||
|
||
config :core, | ||
:admins, | ||
["[email protected]"] | ||
|
@@ -96,6 +97,10 @@ config :ex_aws, | |
access_key_id: "my_access_key", | ||
secret_access_key: "a_super_secret" | ||
|
||
config :core, :feldspar, | ||
backend: Systems.Feldspar.LocalFS, | ||
local_fs_root_path: static_path | ||
|
||
try do | ||
import_config "dev.secret.exs" | ||
rescue | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
defmodule Systems.Feldspar.Public do | ||
import Systems.Feldspar.Internal, only: [get_backend: 0] | ||
|
||
def store(zip_file) do | ||
get_backend().store(zip_file) | ||
end | ||
|
||
def get_public_url(id) do | ||
get_backend().get_public_url(id) | ||
end | ||
|
||
def remove(id) do | ||
get_backend().remove(id) | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
defmodule Systems.Feldspar.Routes do | ||
defmacro routes() do | ||
quote do | ||
scope "/apps", Systems.Feldspar do | ||
pipe_through([:browser]) | ||
live("/:id", AppPage) | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
defmodule Systems.Feldspar.AppPage do | ||
use CoreWeb, :live_view | ||
alias CoreWeb.Endpoint | ||
|
||
@impl true | ||
def mount(%{"id" => app_id}, _session, socket) do | ||
app_url = Endpoint.static_url() <> Endpoint.static_path("/images/" <> app_id <> "/index.html") | ||
|
||
{:ok, assign(socket, app_url: app_url, error: nil)} | ||
end | ||
|
||
@impl true | ||
def handle_uri(socket), do: socket | ||
|
||
@impl true | ||
def render(assigns) do | ||
~H""" | ||
<div class="flex flex-col w-full h-screen"> | ||
<%!-- Ensure that updates don't alter the hierarchy in front of the iframe. | ||
Changing the preceding siblings of the iframe would result in a reload of the iframe | ||
due to Morphdom (https://github.com/patrick-steele-idem/morphdom/issues/200). | ||
--%> | ||
<div> | ||
<div :if={@error} class="bg-[#ff00ff] text-white p-8 text-xl"><%= @error %></div> | ||
</div> | ||
<div phx-update="ignore" id="web-app-frame" phx-hook="FeldsparApp"> | ||
<iframe src={@app_url} class="grow"></iframe> | ||
</div> | ||
</div> | ||
""" | ||
end | ||
|
||
@impl true | ||
def handle_event("app_event", params, socket) do | ||
{:noreply, assign(socket, :error, "Unsupported message: #{inspect(params)}")} | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
defmodule Systems.Feldspar.Internal do | ||
def get_backend do | ||
:core | ||
|> Application.fetch_env!(:feldspar) | ||
|> Access.fetch!(:backend) | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
defmodule Systems.Feldspar.LocalFS do | ||
def store(zip_file) do | ||
id = Ecto.UUID.generate() | ||
path = get_path(id) | ||
File.mkdir!(path) | ||
:zip.unzip(to_charlist(zip_file), cwd: to_charlist(path)) | ||
id | ||
end | ||
|
||
def storage_path(id) do | ||
get_path(id) | ||
end | ||
|
||
def get_public_url(id) do | ||
base_url = Application.get_env(__MODULE__, :base_url, "https://example.org") | ||
"#{base_url}/#{id}" | ||
end | ||
|
||
def remove(id) do | ||
with {:ok, _} <- File.rm_rf(get_path(id)) do | ||
:ok | ||
end | ||
end | ||
|
||
defp get_path(id) do | ||
Path.join(get_root_path(), id) | ||
end | ||
|
||
def get_root_path do | ||
:core | ||
|> Application.get_env(:feldspar, []) | ||
|> Access.fetch!(:local_fs_root_path) | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
defmodule Systems.Feldspar.Plug do | ||
@behaviour Plug | ||
|
||
defmacro setup() do | ||
quote do | ||
plug(Systems.Feldspar.Plug, at: "/web_apps") | ||
end | ||
end | ||
|
||
@impl true | ||
def init(opts) do | ||
opts | ||
# Ensure that init works, from will be set dynamically later on | ||
|> Keyword.put(:from, {nil, nil}) | ||
|> Plug.Static.init() | ||
end | ||
|
||
@impl true | ||
def call( | ||
conn, | ||
options | ||
) do | ||
call(Systems.Feldspar.Internal.get_backend(), conn, options) | ||
end | ||
|
||
def call(Systems.Feldspar.LocalFS, conn, options) do | ||
root_path = Systems.Feldspar.LocalFS.get_root_path() | ||
options = Map.put(options, :from, root_path) | ||
Plug.Static.call(conn, options) | ||
end | ||
|
||
def call(_, conn, _options) do | ||
# Other backends (they generate URL's to external systems) | ||
conn | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,7 +15,8 @@ defmodule Systems.Routes do | |
:lab, | ||
:data_donation, | ||
:benchmark, | ||
:budget | ||
:budget, | ||
:feldspar | ||
] | ||
end | ||
end | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
defmodule Systems.Feldspar.AppPageTest do | ||
use CoreWeb.ConnCase | ||
import Phoenix.ConnTest | ||
import Phoenix.LiveViewTest | ||
|
||
describe "render an app page" do | ||
test "renders page with iframe", %{conn: conn} do | ||
{:ok, _view, html} = live(conn, ~p"/apps/test") | ||
assert html =~ "<iframe" | ||
end | ||
end | ||
|
||
describe "handle app_event" do | ||
test "can receive random app_event data", %{conn: conn} do | ||
{:ok, view, _html} = live(conn, ~p"/apps/test") | ||
assert render_hook(view, :app_event, %{unexpected_key: "some data"}) =~ "Unsupported message:" | ||
end | ||
end | ||
end |
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Hello World! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
defmodule Systems.Feldspar.LocalFSTest do | ||
use ExUnit.Case, async: true | ||
|
||
alias Systems.Feldspar.LocalFS | ||
|
||
describe "store/1" do | ||
test "extracts zip and stores files on disk" do | ||
id = LocalFS.store(Path.join(__DIR__, "hello.zip")) | ||
path = LocalFS.storage_path(id) | ||
assert ["index.html"] == File.ls!(path) | ||
end | ||
|
||
# TODO: | ||
# filters files? .exe etc.? | ||
# runs virus scanner? clamav | ||
end | ||
|
||
describe "get_public_url/1" do | ||
test "returns URL" do | ||
id = Ecto.UUID.generate() | ||
url = LocalFS.get_public_url(id) | ||
uri = URI.parse(url) | ||
assert String.contains?(uri.path, id) | ||
end | ||
end | ||
|
||
describe "remove/1" do | ||
test "removes folder" do | ||
id = LocalFS.store(Path.join(__DIR__, "hello.zip")) | ||
path = LocalFS.storage_path(id) | ||
assert :ok == LocalFS.remove(id) | ||
refute File.exists?(path) | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
defmodule Systems.Feldspar.PlugTest do | ||
use ExUnit.Case | ||
use Plug.Test | ||
|
||
require Systems.Feldspar.Plug | ||
alias Systems.Feldspar.Plug | ||
|
||
setup do | ||
conf = Application.get_env(:core, :feldspar, []) | ||
|
||
on_exit(fn -> | ||
Application.put_env(:core, :feldspar, conf) | ||
end) | ||
|
||
folder_name = "temp_#{:crypto.strong_rand_bytes(16) |> Base.encode16()}" | ||
|
||
tmp_dir = | ||
System.tmp_dir() | ||
|> Path.join(folder_name) | ||
|
||
File.mkdir!(tmp_dir) | ||
|
||
on_exit(fn -> | ||
File.rm_rf!(tmp_dir) | ||
end) | ||
|
||
conf = | ||
conf | ||
|> Keyword.put(:backend, Systems.Feldspar.LocalFS) | ||
|> Keyword.put(:local_fs_root_path, tmp_dir) | ||
|
||
Application.put_env( | ||
:core, | ||
:feldspar, | ||
conf | ||
) | ||
|
||
{:ok, tmp_dir: tmp_dir, app_conf: conf} | ||
end | ||
|
||
test "call with LocalFS backend serves static content", %{tmp_dir: tmp_dir} do | ||
tmp_dir | ||
|> Path.join("plug_test.txt") | ||
|> File.write("hello world!") | ||
|
||
opts = Plug.init(at: "/web_apps") | ||
conn = Plug.call(conn(:get, "/web_apps/plug_test.txt"), opts) | ||
assert "hello world!" == conn.resp_body | ||
end | ||
|
||
test "call with other backends doesn't serve static content", %{app_conf: conf} do | ||
Application.put_env( | ||
:core, | ||
:feldspar, | ||
Keyword.put(conf, :backend, Systems.Feldspar.FakeBackend) | ||
) | ||
|
||
opts = Plug.init(at: "/web_apps") | ||
conn = Plug.call(conn(:get, "/web_apps/plug_test.txt"), opts) | ||
assert nil == conn.resp_body | ||
end | ||
end |