.
21 |
22 |
--------------------------------------------------------------------------------
/apps/hany_ml/lib/hany_ml.ex:
--------------------------------------------------------------------------------
1 | defmodule HanyMl do
2 | alias HanyCluster.Rpc
3 |
4 | def run(image) do
5 | __MODULE__.__exec__(:run, [image])
6 | end
7 |
8 | def __exec__(func, args) do
9 | Rpc.call(__MODULE__.Local, func, args)
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/apps/hany_ml/lib/hany_ml/application.ex:
--------------------------------------------------------------------------------
1 | defmodule HanyMl.Application do
2 | use Application
3 |
4 | @impl true
5 | def start(_type, _args) do
6 | [HanyMl.Local]
7 | |> Supervisor.start_link(strategy: :one_for_one, name: __MODULE__)
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/apps/hany_ml/lib/hany_ml/local.ex:
--------------------------------------------------------------------------------
1 | defmodule HanyMl.Local do
2 | use GenServer
3 |
4 | def start_link(_) do
5 | GenServer.start_link(__MODULE__, nil, name: __MODULE__)
6 | end
7 |
8 | @impl true
9 | def init(_) do
10 | {:ok, resnet} = Bumblebee.load_model({:hf, "microsoft/resnet-50"})
11 | {:ok, featurizer} = Bumblebee.load_featurizer({:hf, "microsoft/resnet-50"})
12 | {:ok, Bumblebee.Vision.image_classification(resnet, featurizer)}
13 | end
14 |
15 | @impl true
16 | def handle_call({:run, image}, _from, serving) do
17 | image = image.data |> Nx.from_binary(:u8) |> Nx.reshape({image.height, image.width, 3})
18 | {:reply, Nx.Serving.run(serving, image), serving}
19 | end
20 |
21 | def run(image) do
22 | GenServer.call(__MODULE__, {:run, image})
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/apps/hany_ml/mix.exs:
--------------------------------------------------------------------------------
1 | defmodule HanyMl.MixProject do
2 | use Mix.Project
3 |
4 | def project() do
5 | [
6 | app: :hany_ml,
7 | version: "0.1.0",
8 | build_path: "../../_build",
9 | config_path: "../../config/config.exs",
10 | deps_path: "../../deps",
11 | lockfile: "../../mix.lock",
12 | elixir: "~> 1.14",
13 | start_permanent: Mix.env() == :prod,
14 | deps: deps(),
15 | aliases: aliases()
16 | ]
17 | end
18 |
19 | def application() do
20 | [
21 | extra_applications: [:logger],
22 | mod: {HanyMl.Application, []}
23 | ]
24 | end
25 |
26 | defp deps() do
27 | [
28 | {:hany_cluster, in_umbrella: true, runtime: true},
29 | {:bumblebee, "~> 0.1.2"},
30 | {:exla, ">= 0.0.0"},
31 | {:nx, "~> 0.4.1"}
32 | ]
33 | end
34 |
35 | defp aliases() do
36 | [
37 | setup: ["deps.get"],
38 | "release.setup": []
39 | ]
40 | end
41 | end
42 |
--------------------------------------------------------------------------------
/apps/hany_ml/test/test_helper.exs:
--------------------------------------------------------------------------------
1 | ExUnit.start()
2 |
--------------------------------------------------------------------------------
/apps/hany_web/.formatter.exs:
--------------------------------------------------------------------------------
1 | [
2 | import_deps: [:phoenix],
3 | inputs: ["*.{ex,exs}", "{config,lib,test}/**/*.{ex,exs}"]
4 | ]
5 |
--------------------------------------------------------------------------------
/apps/hany_web/.gitignore:
--------------------------------------------------------------------------------
1 | # The directory Mix will write compiled artifacts to.
2 | /_build/
3 |
4 | # If you run "mix test --cover", coverage assets end up here.
5 | /cover/
6 |
7 | # The directory Mix downloads your dependencies sources to.
8 | /deps/
9 |
10 | # Where 3rd-party dependencies like ExDoc output generated docs.
11 | /doc/
12 |
13 | # Ignore .fetch files in case you like to edit your project deps locally.
14 | /.fetch
15 |
16 | # If the VM crashes, it generates a dump, let's ignore it too.
17 | erl_crash.dump
18 |
19 | # Also ignore archive artifacts (built via "mix archive.build").
20 | *.ez
21 |
22 | # Ignore package tarball (built via "mix hex.build").
23 | hany_web-*.tar
24 |
25 | # Ignore assets that are produced by build tools.
26 | /priv/static/assets/
27 |
28 | # Ignore digested assets cache.
29 | /priv/static/cache_manifest.json
30 |
31 | # In case you use Node.js/npm, you want to ignore these.
32 | npm-debug.log
33 | /assets/node_modules/
34 |
35 |
--------------------------------------------------------------------------------
/apps/hany_web/README.md:
--------------------------------------------------------------------------------
1 | # HanyWeb
2 |
3 | To start your Phoenix server:
4 |
5 | * Install dependencies with `mix deps.get`
6 | * Create and migrate your database with `mix ecto.setup`
7 | * Start Phoenix endpoint with `mix phx.server`
8 |
9 | Now you can visit [`localhost:4000`](http://localhost:4000) from your browser.
10 |
11 | Ready to run in production? Please [check our deployment guides](https://hexdocs.pm/phoenix/deployment.html).
12 |
13 | ## Learn more
14 |
15 | * Official website: https://www.phoenixframework.org/
16 | * Guides: https://hexdocs.pm/phoenix/overview.html
17 | * Docs: https://hexdocs.pm/phoenix
18 | * Forum: https://elixirforum.com/c/phoenix-forum
19 | * Source: https://github.com/phoenixframework/phoenix
20 |
--------------------------------------------------------------------------------
/apps/hany_web/assets/css/app.css:
--------------------------------------------------------------------------------
1 | /* This file is for your main application CSS */
2 | @import "./phoenix.css";
3 |
4 | /* Alerts and form errors used by phx.new */
5 | .alert {
6 | padding: 15px;
7 | margin-bottom: 20px;
8 | border: 1px solid transparent;
9 | border-radius: 4px;
10 | }
11 | .alert-info {
12 | color: #31708f;
13 | background-color: #d9edf7;
14 | border-color: #bce8f1;
15 | }
16 | .alert-warning {
17 | color: #8a6d3b;
18 | background-color: #fcf8e3;
19 | border-color: #faebcc;
20 | }
21 | .alert-danger {
22 | color: #a94442;
23 | background-color: #f2dede;
24 | border-color: #ebccd1;
25 | }
26 | .alert p {
27 | margin-bottom: 0;
28 | }
29 | .alert:empty {
30 | display: none;
31 | }
32 | .invalid-feedback {
33 | color: #a94442;
34 | display: block;
35 | margin: -1rem 0 2rem;
36 | }
37 |
38 | /* LiveView specific classes for your customization */
39 | .phx-no-feedback.invalid-feedback,
40 | .phx-no-feedback .invalid-feedback {
41 | display: none;
42 | }
43 |
44 | .phx-click-loading {
45 | opacity: 0.5;
46 | transition: opacity 1s ease-out;
47 | }
48 |
49 | .phx-loading{
50 | cursor: wait;
51 | }
52 |
53 | .phx-modal {
54 | opacity: 1!important;
55 | position: fixed;
56 | z-index: 1;
57 | left: 0;
58 | top: 0;
59 | width: 100%;
60 | height: 100%;
61 | overflow: auto;
62 | background-color: rgba(0,0,0,0.4);
63 | }
64 |
65 | .phx-modal-content {
66 | background-color: #fefefe;
67 | margin: 15vh auto;
68 | padding: 20px;
69 | border: 1px solid #888;
70 | width: 80%;
71 | }
72 |
73 | .phx-modal-close {
74 | color: #aaa;
75 | float: right;
76 | font-size: 28px;
77 | font-weight: bold;
78 | }
79 |
80 | .phx-modal-close:hover,
81 | .phx-modal-close:focus {
82 | color: black;
83 | text-decoration: none;
84 | cursor: pointer;
85 | }
86 |
87 | .fade-in-scale {
88 | animation: 0.2s ease-in 0s normal forwards 1 fade-in-scale-keys;
89 | }
90 |
91 | .fade-out-scale {
92 | animation: 0.2s ease-out 0s normal forwards 1 fade-out-scale-keys;
93 | }
94 |
95 | .fade-in {
96 | animation: 0.2s ease-out 0s normal forwards 1 fade-in-keys;
97 | }
98 | .fade-out {
99 | animation: 0.2s ease-out 0s normal forwards 1 fade-out-keys;
100 | }
101 |
102 | @keyframes fade-in-scale-keys{
103 | 0% { scale: 0.95; opacity: 0; }
104 | 100% { scale: 1.0; opacity: 1; }
105 | }
106 |
107 | @keyframes fade-out-scale-keys{
108 | 0% { scale: 1.0; opacity: 1; }
109 | 100% { scale: 0.95; opacity: 0; }
110 | }
111 |
112 | @keyframes fade-in-keys{
113 | 0% { opacity: 0; }
114 | 100% { opacity: 1; }
115 | }
116 |
117 | @keyframes fade-out-keys{
118 | 0% { opacity: 1; }
119 | 100% { opacity: 0; }
120 | }
121 |
--------------------------------------------------------------------------------
/apps/hany_web/assets/css/phoenix.css:
--------------------------------------------------------------------------------
1 | /* Includes some default style for the starter application.
2 | * This can be safely deleted to start fresh.
3 | */
4 |
5 | /* Milligram v1.4.1 https://milligram.github.io
6 | * Copyright (c) 2020 CJ Patoilo Licensed under the MIT license
7 | */
8 |
9 | *,*:after,*:before{box-sizing:inherit}html{box-sizing:border-box;font-size:62.5%}body{color:#000000;font-family:'Helvetica Neue', 'Helvetica', 'Arial', sans-serif;font-size:1.6em;font-weight:300;letter-spacing:.01em;line-height:1.6}blockquote{border-left:0.3rem solid #d1d1d1;margin-left:0;margin-right:0;padding:1rem 1.5rem}blockquote *:last-child{margin-bottom:0}.button,button,input[type='button'],input[type='reset'],input[type='submit']{background-color:#0069d9;border:0.1rem solid #0069d9;border-radius:.4rem;color:#fff;cursor:pointer;display:inline-block;font-size:1.1rem;font-weight:700;height:3.8rem;letter-spacing:.1rem;line-height:3.8rem;padding:0 3.0rem;text-align:center;text-decoration:none;text-transform:uppercase;white-space:nowrap}.button:focus,.button:hover,button:focus,button:hover,input[type='button']:focus,input[type='button']:hover,input[type='reset']:focus,input[type='reset']:hover,input[type='submit']:focus,input[type='submit']:hover{background-color:#606c76;border-color:#606c76;color:#fff;outline:0}.button[disabled],button[disabled],input[type='button'][disabled],input[type='reset'][disabled],input[type='submit'][disabled]{cursor:default;opacity:.5}.button[disabled]:focus,.button[disabled]:hover,button[disabled]:focus,button[disabled]:hover,input[type='button'][disabled]:focus,input[type='button'][disabled]:hover,input[type='reset'][disabled]:focus,input[type='reset'][disabled]:hover,input[type='submit'][disabled]:focus,input[type='submit'][disabled]:hover{background-color:#0069d9;border-color:#0069d9}.button.button-outline,button.button-outline,input[type='button'].button-outline,input[type='reset'].button-outline,input[type='submit'].button-outline{background-color:transparent;color:#0069d9}.button.button-outline:focus,.button.button-outline:hover,button.button-outline:focus,button.button-outline:hover,input[type='button'].button-outline:focus,input[type='button'].button-outline:hover,input[type='reset'].button-outline:focus,input[type='reset'].button-outline:hover,input[type='submit'].button-outline:focus,input[type='submit'].button-outline:hover{background-color:transparent;border-color:#606c76;color:#606c76}.button.button-outline[disabled]:focus,.button.button-outline[disabled]:hover,button.button-outline[disabled]:focus,button.button-outline[disabled]:hover,input[type='button'].button-outline[disabled]:focus,input[type='button'].button-outline[disabled]:hover,input[type='reset'].button-outline[disabled]:focus,input[type='reset'].button-outline[disabled]:hover,input[type='submit'].button-outline[disabled]:focus,input[type='submit'].button-outline[disabled]:hover{border-color:inherit;color:#0069d9}.button.button-clear,button.button-clear,input[type='button'].button-clear,input[type='reset'].button-clear,input[type='submit'].button-clear{background-color:transparent;border-color:transparent;color:#0069d9}.button.button-clear:focus,.button.button-clear:hover,button.button-clear:focus,button.button-clear:hover,input[type='button'].button-clear:focus,input[type='button'].button-clear:hover,input[type='reset'].button-clear:focus,input[type='reset'].button-clear:hover,input[type='submit'].button-clear:focus,input[type='submit'].button-clear:hover{background-color:transparent;border-color:transparent;color:#606c76}.button.button-clear[disabled]:focus,.button.button-clear[disabled]:hover,button.button-clear[disabled]:focus,button.button-clear[disabled]:hover,input[type='button'].button-clear[disabled]:focus,input[type='button'].button-clear[disabled]:hover,input[type='reset'].button-clear[disabled]:focus,input[type='reset'].button-clear[disabled]:hover,input[type='submit'].button-clear[disabled]:focus,input[type='submit'].button-clear[disabled]:hover{color:#0069d9}code{background:#f4f5f6;border-radius:.4rem;font-size:86%;margin:0 .2rem;padding:.2rem .5rem;white-space:nowrap}pre{background:#f4f5f6;border-left:0.3rem solid #0069d9;overflow-y:hidden}pre>code{border-radius:0;display:block;padding:1rem 1.5rem;white-space:pre}hr{border:0;border-top:0.1rem solid #f4f5f6;margin:3.0rem 0}input[type='color'],input[type='date'],input[type='datetime'],input[type='datetime-local'],input[type='email'],input[type='month'],input[type='number'],input[type='password'],input[type='search'],input[type='tel'],input[type='text'],input[type='url'],input[type='week'],input:not([type]),textarea,select{-webkit-appearance:none;background-color:transparent;border:0.1rem solid #d1d1d1;border-radius:.4rem;box-shadow:none;box-sizing:inherit;height:3.8rem;padding:.6rem 1.0rem .7rem;width:100%}input[type='color']:focus,input[type='date']:focus,input[type='datetime']:focus,input[type='datetime-local']:focus,input[type='email']:focus,input[type='month']:focus,input[type='number']:focus,input[type='password']:focus,input[type='search']:focus,input[type='tel']:focus,input[type='text']:focus,input[type='url']:focus,input[type='week']:focus,input:not([type]):focus,textarea:focus,select:focus{border-color:#0069d9;outline:0}select{background:url('data:image/svg+xml;utf8,') center right no-repeat;padding-right:3.0rem}select:focus{background-image:url('data:image/svg+xml;utf8,')}select[multiple]{background:none;height:auto}textarea{min-height:6.5rem}label,legend{display:block;font-size:1.6rem;font-weight:700;margin-bottom:.5rem}fieldset{border-width:0;padding:0}input[type='checkbox'],input[type='radio']{display:inline}.label-inline{display:inline-block;font-weight:normal;margin-left:.5rem}.container{margin:0 auto;max-width:112.0rem;padding:0 2.0rem;position:relative;width:100%}.row{display:flex;flex-direction:column;padding:0;width:100%}.row.row-no-padding{padding:0}.row.row-no-padding>.column{padding:0}.row.row-wrap{flex-wrap:wrap}.row.row-top{align-items:flex-start}.row.row-bottom{align-items:flex-end}.row.row-center{align-items:center}.row.row-stretch{align-items:stretch}.row.row-baseline{align-items:baseline}.row .column{display:block;flex:1 1 auto;margin-left:0;max-width:100%;width:100%}.row .column.column-offset-10{margin-left:10%}.row .column.column-offset-20{margin-left:20%}.row .column.column-offset-25{margin-left:25%}.row .column.column-offset-33,.row .column.column-offset-34{margin-left:33.3333%}.row .column.column-offset-40{margin-left:40%}.row .column.column-offset-50{margin-left:50%}.row .column.column-offset-60{margin-left:60%}.row .column.column-offset-66,.row .column.column-offset-67{margin-left:66.6666%}.row .column.column-offset-75{margin-left:75%}.row .column.column-offset-80{margin-left:80%}.row .column.column-offset-90{margin-left:90%}.row .column.column-10{flex:0 0 10%;max-width:10%}.row .column.column-20{flex:0 0 20%;max-width:20%}.row .column.column-25{flex:0 0 25%;max-width:25%}.row .column.column-33,.row .column.column-34{flex:0 0 33.3333%;max-width:33.3333%}.row .column.column-40{flex:0 0 40%;max-width:40%}.row .column.column-50{flex:0 0 50%;max-width:50%}.row .column.column-60{flex:0 0 60%;max-width:60%}.row .column.column-66,.row .column.column-67{flex:0 0 66.6666%;max-width:66.6666%}.row .column.column-75{flex:0 0 75%;max-width:75%}.row .column.column-80{flex:0 0 80%;max-width:80%}.row .column.column-90{flex:0 0 90%;max-width:90%}.row .column .column-top{align-self:flex-start}.row .column .column-bottom{align-self:flex-end}.row .column .column-center{align-self:center}@media (min-width: 40rem){.row{flex-direction:row;margin-left:-1.0rem;width:calc(100% + 2.0rem)}.row .column{margin-bottom:inherit;padding:0 1.0rem}}a{color:#0069d9;text-decoration:none}a:focus,a:hover{color:#606c76}dl,ol,ul{list-style:none;margin-top:0;padding-left:0}dl dl,dl ol,dl ul,ol dl,ol ol,ol ul,ul dl,ul ol,ul ul{font-size:90%;margin:1.5rem 0 1.5rem 3.0rem}ol{list-style:decimal inside}ul{list-style:circle inside}.button,button,dd,dt,li{margin-bottom:1.0rem}fieldset,input,select,textarea{margin-bottom:1.5rem}blockquote,dl,figure,form,ol,p,pre,table,ul{margin-bottom:2.5rem}table{border-spacing:0;display:block;overflow-x:auto;text-align:left;width:100%}td,th{border-bottom:0.1rem solid #e1e1e1;padding:1.2rem 1.5rem}td:first-child,th:first-child{padding-left:0}td:last-child,th:last-child{padding-right:0}@media (min-width: 40rem){table{display:table;overflow-x:initial}}b,strong{font-weight:bold}p{margin-top:0}h1,h2,h3,h4,h5,h6{font-weight:300;letter-spacing:-.1rem;margin-bottom:2.0rem;margin-top:0}h1{font-size:4.6rem;line-height:1.2}h2{font-size:3.6rem;line-height:1.25}h3{font-size:2.8rem;line-height:1.3}h4{font-size:2.2rem;letter-spacing:-.08rem;line-height:1.35}h5{font-size:1.8rem;letter-spacing:-.05rem;line-height:1.5}h6{font-size:1.6rem;letter-spacing:0;line-height:1.4}img{max-width:100%}.clearfix:after{clear:both;content:' ';display:table}.float-left{float:left}.float-right{float:right}
10 |
11 | /* General style */
12 | h1{font-size: 3.6rem; line-height: 1.25}
13 | h2{font-size: 2.8rem; line-height: 1.3}
14 | h3{font-size: 2.2rem; letter-spacing: -.08rem; line-height: 1.35}
15 | h4{font-size: 1.8rem; letter-spacing: -.05rem; line-height: 1.5}
16 | h5{font-size: 1.6rem; letter-spacing: 0; line-height: 1.4}
17 | h6{font-size: 1.4rem; letter-spacing: 0; line-height: 1.2}
18 | pre{padding: 1em;}
19 |
20 | .container{
21 | margin: 0 auto;
22 | max-width: 80.0rem;
23 | padding: 0 2.0rem;
24 | position: relative;
25 | width: 100%
26 | }
27 | select {
28 | width: auto;
29 | }
30 |
31 | /* Phoenix promo and logo */
32 | .phx-hero {
33 | text-align: center;
34 | border-bottom: 1px solid #e3e3e3;
35 | background: #eee;
36 | border-radius: 6px;
37 | padding: 3em 3em 1em;
38 | margin-bottom: 3rem;
39 | font-weight: 200;
40 | font-size: 120%;
41 | }
42 | .phx-hero input {
43 | background: #ffffff;
44 | }
45 | .phx-logo {
46 | min-width: 300px;
47 | margin: 1rem;
48 | display: block;
49 | }
50 | .phx-logo img {
51 | width: auto;
52 | display: block;
53 | }
54 |
55 | /* Headers */
56 | header {
57 | width: 100%;
58 | background: #fdfdfd;
59 | border-bottom: 1px solid #eaeaea;
60 | margin-bottom: 2rem;
61 | }
62 | header section {
63 | align-items: center;
64 | display: flex;
65 | flex-direction: column;
66 | justify-content: space-between;
67 | }
68 | header section :first-child {
69 | order: 2;
70 | }
71 | header section :last-child {
72 | order: 1;
73 | }
74 | header nav ul,
75 | header nav li {
76 | margin: 0;
77 | padding: 0;
78 | display: block;
79 | text-align: right;
80 | white-space: nowrap;
81 | }
82 | header nav ul {
83 | margin: 1rem;
84 | margin-top: 0;
85 | }
86 | header nav a {
87 | display: block;
88 | }
89 |
90 | @media (min-width: 40.0rem) { /* Small devices (landscape phones, 576px and up) */
91 | header section {
92 | flex-direction: row;
93 | }
94 | header nav ul {
95 | margin: 1rem;
96 | }
97 | .phx-logo {
98 | flex-basis: 527px;
99 | margin: 2rem 1rem;
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/apps/hany_web/assets/js/app.js:
--------------------------------------------------------------------------------
1 | // We import the CSS which is extracted to its own file by esbuild.
2 | // Remove this line if you add a your own CSS build pipeline (e.g postcss).
3 | import "../css/app.css"
4 |
5 | // If you want to use Phoenix channels, run `mix help phx.gen.channel`
6 | // to get started and then uncomment the line below.
7 | // import "./user_socket.js"
8 |
9 | // You can include dependencies in two ways.
10 | //
11 | // The simplest option is to put them in assets/vendor and
12 | // import them using relative paths:
13 | //
14 | // import "../vendor/some-package.js"
15 | //
16 | // Alternatively, you can `npm install some-package --prefix assets` and import
17 | // them using a path starting with the package name:
18 | //
19 | // import "some-package"
20 | //
21 |
22 | // Include phoenix_html to handle method=PUT/DELETE in forms and buttons.
23 | import "phoenix_html"
24 | // Establish Phoenix Socket and LiveView configuration.
25 | import {Socket} from "phoenix"
26 | import {LiveSocket} from "phoenix_live_view"
27 | import topbar from "../vendor/topbar"
28 |
29 | let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content")
30 | let liveSocket = new LiveSocket("/live", Socket, {params: {_csrf_token: csrfToken}})
31 |
32 | // Show progress bar on live navigation and form submits
33 | topbar.config({barColors: {0: "#29d"}, shadowColor: "rgba(0, 0, 0, .3)"})
34 | window.addEventListener("phx:page-loading-start", info => topbar.show())
35 | window.addEventListener("phx:page-loading-stop", info => topbar.hide())
36 |
37 | // connect if there are any LiveViews on the page
38 | liveSocket.connect()
39 |
40 | // expose liveSocket on window for web console debug logs and latency simulation:
41 | // >> liveSocket.enableDebug()
42 | // >> liveSocket.enableLatencySim(1000) // enabled for duration of browser session
43 | // >> liveSocket.disableLatencySim()
44 | window.liveSocket = liveSocket
45 |
46 |
--------------------------------------------------------------------------------
/apps/hany_web/assets/vendor/topbar.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @license MIT
3 | * topbar 1.0.0, 2021-01-06
4 | * https://buunguyen.github.io/topbar
5 | * Copyright (c) 2021 Buu Nguyen
6 | */
7 | (function (window, document) {
8 | "use strict";
9 |
10 | // https://gist.github.com/paulirish/1579671
11 | (function () {
12 | var lastTime = 0;
13 | var vendors = ["ms", "moz", "webkit", "o"];
14 | for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
15 | window.requestAnimationFrame =
16 | window[vendors[x] + "RequestAnimationFrame"];
17 | window.cancelAnimationFrame =
18 | window[vendors[x] + "CancelAnimationFrame"] ||
19 | window[vendors[x] + "CancelRequestAnimationFrame"];
20 | }
21 | if (!window.requestAnimationFrame)
22 | window.requestAnimationFrame = function (callback, element) {
23 | var currTime = new Date().getTime();
24 | var timeToCall = Math.max(0, 16 - (currTime - lastTime));
25 | var id = window.setTimeout(function () {
26 | callback(currTime + timeToCall);
27 | }, timeToCall);
28 | lastTime = currTime + timeToCall;
29 | return id;
30 | };
31 | if (!window.cancelAnimationFrame)
32 | window.cancelAnimationFrame = function (id) {
33 | clearTimeout(id);
34 | };
35 | })();
36 |
37 | var canvas,
38 | progressTimerId,
39 | fadeTimerId,
40 | currentProgress,
41 | showing,
42 | addEvent = function (elem, type, handler) {
43 | if (elem.addEventListener) elem.addEventListener(type, handler, false);
44 | else if (elem.attachEvent) elem.attachEvent("on" + type, handler);
45 | else elem["on" + type] = handler;
46 | },
47 | options = {
48 | autoRun: true,
49 | barThickness: 3,
50 | barColors: {
51 | 0: "rgba(26, 188, 156, .9)",
52 | ".25": "rgba(52, 152, 219, .9)",
53 | ".50": "rgba(241, 196, 15, .9)",
54 | ".75": "rgba(230, 126, 34, .9)",
55 | "1.0": "rgba(211, 84, 0, .9)",
56 | },
57 | shadowBlur: 10,
58 | shadowColor: "rgba(0, 0, 0, .6)",
59 | className: null,
60 | },
61 | repaint = function () {
62 | canvas.width = window.innerWidth;
63 | canvas.height = options.barThickness * 5; // need space for shadow
64 |
65 | var ctx = canvas.getContext("2d");
66 | ctx.shadowBlur = options.shadowBlur;
67 | ctx.shadowColor = options.shadowColor;
68 |
69 | var lineGradient = ctx.createLinearGradient(0, 0, canvas.width, 0);
70 | for (var stop in options.barColors)
71 | lineGradient.addColorStop(stop, options.barColors[stop]);
72 | ctx.lineWidth = options.barThickness;
73 | ctx.beginPath();
74 | ctx.moveTo(0, options.barThickness / 2);
75 | ctx.lineTo(
76 | Math.ceil(currentProgress * canvas.width),
77 | options.barThickness / 2
78 | );
79 | ctx.strokeStyle = lineGradient;
80 | ctx.stroke();
81 | },
82 | createCanvas = function () {
83 | canvas = document.createElement("canvas");
84 | var style = canvas.style;
85 | style.position = "fixed";
86 | style.top = style.left = style.right = style.margin = style.padding = 0;
87 | style.zIndex = 100001;
88 | style.display = "none";
89 | if (options.className) canvas.classList.add(options.className);
90 | document.body.appendChild(canvas);
91 | addEvent(window, "resize", repaint);
92 | },
93 | topbar = {
94 | config: function (opts) {
95 | for (var key in opts)
96 | if (options.hasOwnProperty(key)) options[key] = opts[key];
97 | },
98 | show: function () {
99 | if (showing) return;
100 | showing = true;
101 | if (fadeTimerId !== null) window.cancelAnimationFrame(fadeTimerId);
102 | if (!canvas) createCanvas();
103 | canvas.style.opacity = 1;
104 | canvas.style.display = "block";
105 | topbar.progress(0);
106 | if (options.autoRun) {
107 | (function loop() {
108 | progressTimerId = window.requestAnimationFrame(loop);
109 | topbar.progress(
110 | "+" + 0.05 * Math.pow(1 - Math.sqrt(currentProgress), 2)
111 | );
112 | })();
113 | }
114 | },
115 | progress: function (to) {
116 | if (typeof to === "undefined") return currentProgress;
117 | if (typeof to === "string") {
118 | to =
119 | (to.indexOf("+") >= 0 || to.indexOf("-") >= 0
120 | ? currentProgress
121 | : 0) + parseFloat(to);
122 | }
123 | currentProgress = to > 1 ? 1 : to;
124 | repaint();
125 | return currentProgress;
126 | },
127 | hide: function () {
128 | if (!showing) return;
129 | showing = false;
130 | if (progressTimerId != null) {
131 | window.cancelAnimationFrame(progressTimerId);
132 | progressTimerId = null;
133 | }
134 | (function loop() {
135 | if (topbar.progress("+.1") >= 1) {
136 | canvas.style.opacity -= 0.05;
137 | if (canvas.style.opacity <= 0.05) {
138 | canvas.style.display = "none";
139 | fadeTimerId = null;
140 | return;
141 | }
142 | }
143 | fadeTimerId = window.requestAnimationFrame(loop);
144 | })();
145 | },
146 | };
147 |
148 | if (typeof module === "object" && typeof module.exports === "object") {
149 | module.exports = topbar;
150 | } else if (typeof define === "function" && define.amd) {
151 | define(function () {
152 | return topbar;
153 | });
154 | } else {
155 | this.topbar = topbar;
156 | }
157 | }.call(this, window, document));
158 |
--------------------------------------------------------------------------------
/apps/hany_web/lib/hany_web.ex:
--------------------------------------------------------------------------------
1 | defmodule HanyWeb do
2 | @moduledoc """
3 | The entrypoint for defining your web interface, such
4 | as controllers, views, channels and so on.
5 |
6 | This can be used in your application as:
7 |
8 | use HanyWeb, :controller
9 | use HanyWeb, :view
10 |
11 | The definitions below will be executed for every view,
12 | controller, etc, so keep them short and clean, focused
13 | on imports, uses and aliases.
14 |
15 | Do NOT define functions inside the quoted expressions
16 | below. Instead, define any helper function in modules
17 | and import those modules here.
18 | """
19 |
20 | def controller do
21 | quote do
22 | use Phoenix.Controller, namespace: HanyWeb
23 |
24 | import Plug.Conn
25 | alias HanyWeb.Router.Helpers, as: Routes
26 | end
27 | end
28 |
29 | def view do
30 | quote do
31 | use Phoenix.View,
32 | root: "lib/hany_web/templates",
33 | namespace: HanyWeb
34 |
35 | # Import convenience functions from controllers
36 | import Phoenix.Controller,
37 | only: [get_flash: 1, get_flash: 2, view_module: 1, view_template: 1]
38 |
39 | # Include shared imports and aliases for views
40 | unquote(view_helpers())
41 | end
42 | end
43 |
44 | def live_view do
45 | quote do
46 | use Phoenix.LiveView,
47 | layout: {HanyWeb.LayoutView, "live.html"}
48 |
49 | unquote(view_helpers())
50 | end
51 | end
52 |
53 | def live_component do
54 | quote do
55 | use Phoenix.LiveComponent
56 |
57 | unquote(view_helpers())
58 | end
59 | end
60 |
61 | def component do
62 | quote do
63 | use Phoenix.Component
64 |
65 | unquote(view_helpers())
66 | end
67 | end
68 |
69 | def router do
70 | quote do
71 | use Phoenix.Router
72 |
73 | import Plug.Conn
74 | import Phoenix.Controller
75 | import Phoenix.LiveView.Router
76 | end
77 | end
78 |
79 | def channel do
80 | quote do
81 | use Phoenix.Channel
82 | end
83 | end
84 |
85 | defp view_helpers do
86 | quote do
87 | # Use all HTML functionality (forms, tags, etc)
88 | use Phoenix.HTML
89 |
90 | # Import LiveView and .heex helpers (live_render, live_patch, <.form>, etc)
91 | import Phoenix.LiveView.Helpers
92 | import Phoenix.Component
93 |
94 | # Import basic rendering functionality (render, render_layout, etc)
95 | import Phoenix.View
96 |
97 | import HanyWeb.ErrorHelpers
98 | alias HanyWeb.Router.Helpers, as: Routes
99 | end
100 | end
101 |
102 | @doc """
103 | When used, dispatch to the appropriate controller/view/etc.
104 | """
105 | defmacro __using__(which) when is_atom(which) do
106 | apply(__MODULE__, which, [])
107 | end
108 | end
109 |
--------------------------------------------------------------------------------
/apps/hany_web/lib/hany_web/application.ex:
--------------------------------------------------------------------------------
1 | defmodule HanyWeb.Application do
2 | # See https://hexdocs.pm/elixir/Application.html
3 | # for more information on OTP Applications
4 | @moduledoc false
5 |
6 | use Application
7 |
8 | @impl true
9 | def start(_type, _args) do
10 | children = [
11 | # Start the Telemetry supervisor
12 | HanyWeb.Telemetry,
13 | # Start the Endpoint (http/https)
14 | HanyWeb.Endpoint
15 | # Start a worker by calling: HanyWeb.Worker.start_link(arg)
16 | # {HanyWeb.Worker, arg}
17 | ]
18 |
19 | # See https://hexdocs.pm/elixir/Supervisor.html
20 | # for other strategies and supported options
21 | opts = [strategy: :one_for_one, name: HanyWeb.Supervisor]
22 | Supervisor.start_link(children, opts)
23 | end
24 |
25 | # Tell Phoenix to update the endpoint configuration
26 | # whenever the application is updated.
27 | @impl true
28 | def config_change(changed, _new, removed) do
29 | HanyWeb.Endpoint.config_change(changed, removed)
30 | :ok
31 | end
32 | end
33 |
--------------------------------------------------------------------------------
/apps/hany_web/lib/hany_web/controllers/page_controller.ex:
--------------------------------------------------------------------------------
1 | defmodule HanyWeb.PageController do
2 | use HanyWeb, :controller
3 |
4 | def index(conn, _params) do
5 | render(conn, "index.html")
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/apps/hany_web/lib/hany_web/endpoint.ex:
--------------------------------------------------------------------------------
1 | defmodule HanyWeb.Endpoint do
2 | use Phoenix.Endpoint, otp_app: :hany_web
3 |
4 | # The session will be stored in the cookie and signed,
5 | # this means its contents can be read but not tampered with.
6 | # Set :encryption_salt if you would also like to encrypt it.
7 | @session_options [
8 | store: :cookie,
9 | key: "_hany_web_key",
10 | signing_salt: "q5ipgJRs"
11 | ]
12 |
13 | socket "/live", Phoenix.LiveView.Socket, websocket: [connect_info: [session: @session_options]]
14 |
15 | # Serve at "/" the static files from "priv/static" directory.
16 | #
17 | # You should set gzip to true if you are running phx.digest
18 | # when deploying your static files in production.
19 | plug Plug.Static,
20 | at: "/",
21 | from: :hany_web,
22 | gzip: false,
23 | only: ~w(assets fonts images favicon.ico robots.txt)
24 |
25 | # Code reloading can be explicitly enabled under the
26 | # :code_reloader configuration of your endpoint.
27 | if code_reloading? do
28 | socket "/phoenix/live_reload/socket", Phoenix.LiveReloader.Socket
29 | plug Phoenix.LiveReloader
30 | plug Phoenix.CodeReloader
31 | plug Phoenix.Ecto.CheckRepoStatus, otp_app: :hany_web
32 | end
33 |
34 | plug Phoenix.LiveDashboard.RequestLogger,
35 | param_key: "request_logger",
36 | cookie_key: "request_logger"
37 |
38 | plug Plug.RequestId
39 | plug Plug.Telemetry, event_prefix: [:phoenix, :endpoint]
40 |
41 | plug Plug.Parsers,
42 | parsers: [:urlencoded, :multipart, :json],
43 | pass: ["*/*"],
44 | json_decoder: Phoenix.json_library()
45 |
46 | plug Plug.MethodOverride
47 | plug Plug.Head
48 | plug Plug.Session, @session_options
49 | plug HanyWeb.Router
50 | end
51 |
--------------------------------------------------------------------------------
/apps/hany_web/lib/hany_web/router.ex:
--------------------------------------------------------------------------------
1 | defmodule HanyWeb.Router do
2 | use HanyWeb, :router
3 |
4 | pipeline :browser do
5 | plug :accepts, ["html"]
6 | plug :fetch_session
7 | plug :fetch_live_flash
8 | plug :put_root_layout, {HanyWeb.LayoutView, :root}
9 | plug :protect_from_forgery
10 | plug :put_secure_browser_headers
11 | end
12 |
13 | pipeline :api do
14 | plug :accepts, ["json"]
15 | end
16 |
17 | scope "/", HanyWeb do
18 | pipe_through :browser
19 |
20 | get "/", PageController, :index
21 | end
22 |
23 | # Other scopes may use custom stacks.
24 | # scope "/api", HanyWeb do
25 | # pipe_through :api
26 | # end
27 |
28 | # Enables LiveDashboard only for development
29 | #
30 | # If you want to use the LiveDashboard in production, you should put
31 | # it behind authentication and allow only admins to access it.
32 | # If your application does not have an admins-only section yet,
33 | # you can use Plug.BasicAuth to set up some basic authentication
34 | # as long as you are also using SSL (which you should anyway).
35 | if Mix.env() in [:dev, :test] do
36 | import Phoenix.LiveDashboard.Router
37 |
38 | scope "/" do
39 | pipe_through :browser
40 |
41 | live_dashboard "/dashboard", metrics: HanyWeb.Telemetry
42 | end
43 | end
44 | end
45 |
--------------------------------------------------------------------------------
/apps/hany_web/lib/hany_web/telemetry.ex:
--------------------------------------------------------------------------------
1 | defmodule HanyWeb.Telemetry do
2 | use Supervisor
3 | import Telemetry.Metrics
4 |
5 | def start_link(arg) do
6 | Supervisor.start_link(__MODULE__, arg, name: __MODULE__)
7 | end
8 |
9 | @impl true
10 | def init(_arg) do
11 | children = [
12 | # Telemetry poller will execute the given period measurements
13 | # every 10_000ms. Learn more here: https://hexdocs.pm/telemetry_metrics
14 | {:telemetry_poller, measurements: periodic_measurements(), period: 10_000}
15 | # Add reporters as children of your supervision tree.
16 | # {Telemetry.Metrics.ConsoleReporter, metrics: metrics()}
17 | ]
18 |
19 | Supervisor.init(children, strategy: :one_for_one)
20 | end
21 |
22 | def metrics do
23 | [
24 | # Phoenix Metrics
25 | summary("phoenix.endpoint.stop.duration",
26 | unit: {:native, :millisecond}
27 | ),
28 | summary("phoenix.router_dispatch.stop.duration",
29 | tags: [:route],
30 | unit: {:native, :millisecond}
31 | ),
32 |
33 | # Database Metrics
34 | summary("hany.repo.query.total_time",
35 | unit: {:native, :millisecond},
36 | description: "The sum of the other measurements"
37 | ),
38 | summary("hany.repo.query.decode_time",
39 | unit: {:native, :millisecond},
40 | description: "The time spent decoding the data received from the database"
41 | ),
42 | summary("hany.repo.query.query_time",
43 | unit: {:native, :millisecond},
44 | description: "The time spent executing the query"
45 | ),
46 | summary("hany.repo.query.queue_time",
47 | unit: {:native, :millisecond},
48 | description: "The time spent waiting for a database connection"
49 | ),
50 | summary("hany.repo.query.idle_time",
51 | unit: {:native, :millisecond},
52 | description:
53 | "The time the connection spent waiting before being checked out for the query"
54 | ),
55 |
56 | # VM Metrics
57 | summary("vm.memory.total", unit: {:byte, :kilobyte}),
58 | summary("vm.total_run_queue_lengths.total"),
59 | summary("vm.total_run_queue_lengths.cpu"),
60 | summary("vm.total_run_queue_lengths.io")
61 | ]
62 | end
63 |
64 | defp periodic_measurements do
65 | [
66 | # A module, function and arguments to be invoked periodically.
67 | # This function must call :telemetry.execute/3 and a metric must be added above.
68 | # {HanyWeb, :count_users, []}
69 | ]
70 | end
71 | end
72 |
--------------------------------------------------------------------------------
/apps/hany_web/lib/hany_web/templates/layout/app.html.heex:
--------------------------------------------------------------------------------
1 |
2 | <%= get_flash(@conn, :info) %>
3 | <%= get_flash(@conn, :error) %>
4 | <%= @inner_content %>
5 |
6 |
--------------------------------------------------------------------------------
/apps/hany_web/lib/hany_web/templates/layout/live.html.heex:
--------------------------------------------------------------------------------
1 |
2 | <%= live_flash(@flash, :info) %>
5 |
6 | <%= live_flash(@flash, :error) %>
9 |
10 | <%= @inner_content %>
11 |
12 |
--------------------------------------------------------------------------------
/apps/hany_web/lib/hany_web/templates/layout/root.html.heex:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | <%= live_title_tag assigns[:page_title] || "Hany", suffix: " · Phoenix Framework" %>
9 |
10 |
11 |
12 |
13 |
14 |
15 |
23 |
24 |
25 |
26 |
27 |
28 | <%= @inner_content %>
29 |
30 |
31 |
--------------------------------------------------------------------------------
/apps/hany_web/lib/hany_web/templates/page/index.html.heex:
--------------------------------------------------------------------------------
1 |
2 | Welcome to Phoenix!
3 | Peace of mind from prototype to production
4 |
5 |
6 |
7 |
8 | Resources
9 |
20 |
21 |
22 | Help
23 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/apps/hany_web/lib/hany_web/views/error_helpers.ex:
--------------------------------------------------------------------------------
1 | defmodule HanyWeb.ErrorHelpers do
2 | @moduledoc """
3 | Conveniences for translating and building error messages.
4 | """
5 |
6 | use Phoenix.HTML
7 |
8 | @doc """
9 | Generates tag for inlined form input errors.
10 | """
11 | def error_tag(form, field) do
12 | Enum.map(Keyword.get_values(form.errors, field), fn error ->
13 | content_tag(:span, translate_error(error),
14 | class: "invalid-feedback",
15 | phx_feedback_for: input_name(form, field)
16 | )
17 | end)
18 | end
19 |
20 | @doc """
21 | Translates an error message.
22 | """
23 | def translate_error({msg, opts}) do
24 | # Because the error messages we show in our forms and APIs
25 | # are defined inside Ecto, we need to translate them dynamically.
26 | Enum.reduce(opts, msg, fn {key, value}, acc ->
27 | String.replace(acc, "%{#{key}}", fn _ -> to_string(value) end)
28 | end)
29 | end
30 | end
31 |
--------------------------------------------------------------------------------
/apps/hany_web/lib/hany_web/views/error_view.ex:
--------------------------------------------------------------------------------
1 | defmodule HanyWeb.ErrorView do
2 | use HanyWeb, :view
3 |
4 | # If you want to customize a particular status code
5 | # for a certain format, you may uncomment below.
6 | # def render("500.html", _assigns) do
7 | # "Internal Server Error"
8 | # end
9 |
10 | # By default, Phoenix returns the status message from
11 | # the template name. For example, "404.html" becomes
12 | # "Not Found".
13 | def template_not_found(template, _assigns) do
14 | Phoenix.Controller.status_message_from_template(template)
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/apps/hany_web/lib/hany_web/views/layout_view.ex:
--------------------------------------------------------------------------------
1 | defmodule HanyWeb.LayoutView do
2 | use HanyWeb, :view
3 |
4 | # Phoenix LiveDashboard is available only in development by default,
5 | # so we instruct Elixir to not warn if the dashboard route is missing.
6 | @compile {:no_warn_undefined, {Routes, :live_dashboard_path, 2}}
7 | end
8 |
--------------------------------------------------------------------------------
/apps/hany_web/lib/hany_web/views/page_view.ex:
--------------------------------------------------------------------------------
1 | defmodule HanyWeb.PageView do
2 | use HanyWeb, :view
3 | end
4 |
--------------------------------------------------------------------------------
/apps/hany_web/mix.exs:
--------------------------------------------------------------------------------
1 | defmodule HanyWeb.MixProject do
2 | use Mix.Project
3 |
4 | def project do
5 | [
6 | app: :hany_web,
7 | version: "0.1.0",
8 | build_path: "../../_build",
9 | config_path: "../../config/config.exs",
10 | deps_path: "../../deps",
11 | lockfile: "../../mix.lock",
12 | elixir: "~> 1.12",
13 | elixirc_paths: elixirc_paths(Mix.env()),
14 | compilers: Mix.compilers(),
15 | start_permanent: Mix.env() == :prod,
16 | aliases: aliases(),
17 | deps: deps()
18 | ]
19 | end
20 |
21 | # Configuration for the OTP application.
22 | #
23 | # Type `mix help compile.app` for more information.
24 | def application do
25 | [
26 | mod: {HanyWeb.Application, []},
27 | extra_applications: [:logger, :runtime_tools]
28 | ]
29 | end
30 |
31 | # Specifies which paths to compile per environment.
32 | defp elixirc_paths(:test), do: ["lib", "test/support"]
33 | defp elixirc_paths(_), do: ["lib"]
34 |
35 | # Specifies your project dependencies.
36 | #
37 | # Type `mix help deps` for examples and options.
38 | defp deps do
39 | [
40 | {:hany, in_umbrella: true, runtime: false},
41 | {:hany_cluster, in_umbrella: true, runtime: true},
42 | {:phoenix, "~> 1.6.15"},
43 | {:phoenix_ecto, "~> 4.4"},
44 | {:phoenix_html, "~> 3.0"},
45 | {:phoenix_live_reload, "~> 1.2", only: :dev},
46 | {:phoenix_live_view, "~> 0.18.3"},
47 | {:floki, ">= 0.30.0", only: :test},
48 | {:phoenix_live_dashboard, "~> 0.7.2"},
49 | {:esbuild, "~> 0.4", runtime: Mix.env() == :dev},
50 | {:telemetry_metrics, "~> 0.6"},
51 | {:telemetry_poller, "~> 1.0"},
52 | {:jason, "~> 1.2"},
53 | {:plug_cowboy, "~> 2.5.2"}
54 | ]
55 | end
56 |
57 | # Aliases are shortcuts or tasks specific to the current project.
58 | #
59 | # See the documentation for `Mix` for more info on aliases.
60 | defp aliases do
61 | [
62 | setup: ["deps.get"],
63 | test: ["ecto.create --quiet", "ecto.migrate --quiet", "test"],
64 | "release.setup": ["esbuild default --minify", "phx.digest"]
65 | ]
66 | end
67 | end
68 |
--------------------------------------------------------------------------------
/apps/hany_web/priv/static/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rudebono/elixir-da-example/11af589bbdfc36acda91ad9738b3b00786488d8c/apps/hany_web/priv/static/favicon.ico
--------------------------------------------------------------------------------
/apps/hany_web/priv/static/images/phoenix.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rudebono/elixir-da-example/11af589bbdfc36acda91ad9738b3b00786488d8c/apps/hany_web/priv/static/images/phoenix.png
--------------------------------------------------------------------------------
/apps/hany_web/priv/static/robots.txt:
--------------------------------------------------------------------------------
1 | # See https://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file
2 | #
3 | # To ban all spiders from the entire site uncomment the next two lines:
4 | # User-agent: *
5 | # Disallow: /
6 |
--------------------------------------------------------------------------------
/apps/hany_web/test/hany_web/controllers/page_controller_test.exs:
--------------------------------------------------------------------------------
1 | defmodule HanyWeb.PageControllerTest do
2 | use HanyWeb.ConnCase
3 |
4 | test "GET /", %{conn: conn} do
5 | conn = get(conn, "/")
6 | assert html_response(conn, 200) =~ "Welcome to Phoenix!"
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/apps/hany_web/test/hany_web/views/error_view_test.exs:
--------------------------------------------------------------------------------
1 | defmodule HanyWeb.ErrorViewTest do
2 | use HanyWeb.ConnCase, async: true
3 |
4 | # Bring render/3 and render_to_string/3 for testing custom views
5 | import Phoenix.View
6 |
7 | test "renders 404.html" do
8 | assert render_to_string(HanyWeb.ErrorView, "404.html", []) == "Not Found"
9 | end
10 |
11 | test "renders 500.html" do
12 | assert render_to_string(HanyWeb.ErrorView, "500.html", []) == "Internal Server Error"
13 | end
14 | end
15 |
--------------------------------------------------------------------------------
/apps/hany_web/test/hany_web/views/layout_view_test.exs:
--------------------------------------------------------------------------------
1 | defmodule HanyWeb.LayoutViewTest do
2 | use HanyWeb.ConnCase, async: true
3 |
4 | # When testing helpers, you may want to import Phoenix.HTML and
5 | # use functions such as safe_to_string() to convert the helper
6 | # result into an HTML string.
7 | # import Phoenix.HTML
8 | end
9 |
--------------------------------------------------------------------------------
/apps/hany_web/test/hany_web/views/page_view_test.exs:
--------------------------------------------------------------------------------
1 | defmodule HanyWeb.PageViewTest do
2 | use HanyWeb.ConnCase, async: true
3 | end
4 |
--------------------------------------------------------------------------------
/apps/hany_web/test/support/conn_case.ex:
--------------------------------------------------------------------------------
1 | defmodule HanyWeb.ConnCase do
2 | @moduledoc """
3 | This module defines the test case to be used by
4 | tests that require setting up a connection.
5 |
6 | Such tests rely on `Phoenix.ConnTest` and also
7 | import other functionality to make it easier
8 | to build common data structures and query the data layer.
9 |
10 | Finally, if the test case interacts with the database,
11 | we enable the SQL sandbox, so changes done to the database
12 | are reverted at the end of every test. If you are using
13 | PostgreSQL, you can even run database tests asynchronously
14 | by setting `use HanyWeb.ConnCase, async: true`, although
15 | this option is not recommended for other databases.
16 | """
17 |
18 | use ExUnit.CaseTemplate
19 |
20 | using do
21 | quote do
22 | # Import conveniences for testing with connections
23 | import Plug.Conn
24 | import Phoenix.ConnTest
25 | import HanyWeb.ConnCase
26 |
27 | alias HanyWeb.Router.Helpers, as: Routes
28 |
29 | # The default endpoint for testing
30 | @endpoint HanyWeb.Endpoint
31 | end
32 | end
33 |
34 | setup tags do
35 | Hany.DataCase.setup_sandbox(tags)
36 | {:ok, conn: Phoenix.ConnTest.build_conn()}
37 | end
38 | end
39 |
--------------------------------------------------------------------------------
/apps/hany_web/test/test_helper.exs:
--------------------------------------------------------------------------------
1 | ExUnit.start()
2 | Ecto.Adapters.SQL.Sandbox.mode(Hany.Repo.Local, :manual)
3 |
--------------------------------------------------------------------------------
/config/config.exs:
--------------------------------------------------------------------------------
1 | import Config
2 |
3 | config :hany, ecto_repos: [Hany.Repo.Local]
4 |
5 | config :hany, Hany.Repo.Local, priv: "priv/repo"
6 |
7 | config :hany_web, ecto_repos: [Hany.Repo.Local], generators: [context_app: :hany]
8 |
9 | config :hany_web, HanyWeb.Endpoint,
10 | url: [host: "localhost"],
11 | render_errors: [view: HanyWeb.ErrorView, accepts: ~w(html json), layout: false],
12 | pubsub_server: Hany.PubSub,
13 | live_view: [signing_salt: "XzNU9OtT"]
14 |
15 | config :esbuild,
16 | version: "0.14.29",
17 | default: [
18 | args:
19 | ~w(js/app.js --bundle --target=es2017 --outdir=../priv/static/assets --external:/fonts/* --external:/images/*),
20 | cd: Path.expand("../apps/hany_web/assets", __DIR__),
21 | env: %{"NODE_PATH" => Path.expand("../deps", __DIR__)}
22 | ]
23 |
24 | config :logger, :console, format: "$time $metadata[$level] $message\n", metadata: [:request_id]
25 |
26 | config :phoenix, :json_library, Jason
27 |
28 | config :mime, :types, %{"text/plain" => ["livemd"]}
29 |
30 | config :livebook,
31 | app_service_name: nil,
32 | app_service_url: nil,
33 | authentication_mode: :token,
34 | feature_flags: [],
35 | force_ssl_host: nil,
36 | learn_notebooks: [],
37 | plugs: [],
38 | shutdown_callback: nil,
39 | storage: Livebook.Storage.Ets,
40 | update_instructions_url: nil,
41 | within_iframe: false,
42 | allowed_uri_schemes: [],
43 | iframe_port: 4003
44 |
45 | config :livebook, LivebookWeb.Endpoint,
46 | url: [host: "localhost"],
47 | pubsub_server: Livebook.PubSub,
48 | live_view: [signing_salt: "XzNU9OtT"]
49 |
50 | config :nx, default_backend: EXLA.Backend
51 |
52 | import_config "#{config_env()}.exs"
53 |
--------------------------------------------------------------------------------
/config/dev.exs:
--------------------------------------------------------------------------------
1 | import Config
2 |
3 | config :hany_cluster, strategy: HanyCluster.Strategy.Epmd
4 |
5 | config :hany, Hany.Repo.Local,
6 | username: "postgres",
7 | password: "postgres",
8 | hostname: "localhost",
9 | database: "hany_dev",
10 | stacktrace: true,
11 | show_sensitive_data_on_connection_error: true,
12 | pool_size: 10,
13 | port: 5432
14 |
15 | config :hany_web, HanyWeb.Endpoint,
16 | http: [ip: {127, 0, 0, 1}, port: 4000],
17 | check_origin: false,
18 | code_reloader: true,
19 | debug_errors: true,
20 | secret_key_base: "y6D26+l7RT5ZokEQjrYw3BZUnNSKYqRgXGv1eRLfBXtulKARLnQnVikk0lcFkrD3",
21 | watchers: [esbuild: {Esbuild, :install_and_run, [:default, ~w(--sourcemap=inline --watch)]}],
22 | live_reload: [
23 | patterns: [
24 | ~r"priv/static/.*(js|css|png|jpeg|jpg|gif|svg)$",
25 | ~r"lib/hany_web/(live|views)/.*(ex)$",
26 | ~r"lib/hany_web/templates/.*(eex)$"
27 | ]
28 | ]
29 |
30 | config :logger, :console, format: "[$level] $message\n"
31 |
32 | config :phoenix, :plug_init_mode, :runtime
33 | config :phoenix, :stacktrace_depth, 20
34 |
35 | config :livebook, LivebookWeb.Endpoint,
36 | http: [ip: {127, 0, 0, 1}, port: 4002],
37 | pubsub_server: Livebook.PubSub,
38 | server: false
39 |
--------------------------------------------------------------------------------
/config/prod.exs:
--------------------------------------------------------------------------------
1 | import Config
2 |
3 | config :hany_cluster, strategy: HanyCluster.Strategy.Gce
4 |
5 | config :hany_web, HanyWeb.Endpoint,
6 | url: [scheme: "https", host: "da.rudebono.dev", port: 443],
7 | cache_static_manifest: "priv/static/cache_manifest.json",
8 | check_origin: false,
9 | server: true
10 |
11 | config :logger, level: :info
12 |
13 | config :livebook, LivebookWeb.Endpoint,
14 | url: [scheme: "https", host: "livebook.rudebono.dev", port: 443, path: "/"],
15 | http: [ip: {0, 0, 0, 0, 0, 0, 0, 0}, port: 4002],
16 | check_origin: false,
17 | server: true
18 |
--------------------------------------------------------------------------------
/config/runtime.exs:
--------------------------------------------------------------------------------
1 | import Config
2 |
3 | if config_env() == :prod do
4 | Livebook.config_runtime()
5 |
6 | config :hany, Hany.Repo.Local,
7 | username: System.get_env("PG_USERNAME", "postgres"),
8 | password: System.get_env("PG_PASSWORD", "postgres"),
9 | hostname: System.get_env("PG_HOSATNAME", "localhost"),
10 | database: "hany_prod",
11 | port: String.to_integer(System.get_env("PG_PORT", "5432"))
12 |
13 | config :hany_web, HanyWeb.Endpoint,
14 | http: [
15 | ip: {0, 0, 0, 0, 0, 0, 0, 0},
16 | port: String.to_integer(System.get_env("HANYWEB_PORT", "4000"))
17 | ],
18 | secret_key_base:
19 | System.get_env(
20 | "SECRET_KEY_BASE",
21 | "y6D26+l7RT5ZokEQjrYw3BZUnNSKYqRgXGv1eRLfBXtulKARLnQnVikk0lcFkrD3"
22 | )
23 |
24 | config :livebook, LivebookWeb.Endpoint,
25 | http: [
26 | ip: {0, 0, 0, 0, 0, 0, 0, 0},
27 | port: String.to_integer(System.get_env("LIVEBOOK_PORT", "4002"))
28 | ],
29 | secret_key_base:
30 | System.get_env(
31 | "SECRET_KEY_BASE",
32 | "y6D26+l7RT5ZokEQjrYw3BZUnNSKYqRgXGv1eRLfBXtulKARLnQnVikk0lcFkrD3"
33 | )
34 | end
35 |
--------------------------------------------------------------------------------
/config/test.exs:
--------------------------------------------------------------------------------
1 | import Config
2 |
3 | config :hany_cluster, strategy: HanyCluster.Strategy.Epmd
4 |
5 | config :hany, Hany.Repo.Local,
6 | username: "postgres",
7 | password: "postgres",
8 | hostname: "localhost",
9 | database: "hany_test#{System.get_env("MIX_TEST_PARTITION")}",
10 | pool: Ecto.Adapters.SQL.Sandbox,
11 | pool_size: 10,
12 | port: 5432
13 |
14 | config :hany_web, HanyWeb.Endpoint,
15 | http: [ip: {127, 0, 0, 1}, port: 4001],
16 | secret_key_base: "G1W9Xa9RI016nDE8izj9Qr6GiLhUL8iVF69BLWuMXHwrvhkLL6bVzi9u+IRlOI4w",
17 | server: false
18 |
19 | config :logger, level: :warn
20 |
21 | config :phoenix, :plug_init_mode, :runtime
22 |
23 | config :livebook, LivebookWeb.Endpoint, server: false
24 |
25 | config :livebook, LivebookWeb.Endpoint,
26 | http: [ip: {127, 0, 0, 1}, port: 4002],
27 | pubsub_server: Livebook.PubSub,
28 | server: false
29 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3.8'
2 |
3 | services:
4 | postgres:
5 | image: postgres:13.10-alpine
6 | ports:
7 | - 5432:5432
8 | environment:
9 | POSTGRES_PASSWORD: postgres
10 |
--------------------------------------------------------------------------------
/mix.exs:
--------------------------------------------------------------------------------
1 | defmodule Hany.Umbrella.MixProject do
2 | use Mix.Project
3 |
4 | def project do
5 | [
6 | apps_path: "apps",
7 | version: "0.1.0",
8 | start_permanent: Mix.env() == :prod,
9 | deps: deps(),
10 | aliases: aliases(),
11 | releases: releases()
12 | ]
13 | end
14 |
15 | defp deps() do
16 | [
17 | {:livebook, "~> 0.8.1"},
18 | {:kino, "~> 0.8.0"}
19 | ]
20 | end
21 |
22 | defp aliases() do
23 | [
24 | setup: ["cmd mix setup"],
25 | "release.setup": ["cmd mix release.setup"],
26 | "ecto.reset": ["cmd --app hany mix ecto.reset"]
27 | ]
28 | end
29 |
30 | defp releases() do
31 | [
32 | hany: [
33 | include_executables_for: [:unix],
34 | applications: [
35 | livebook: :load,
36 | hany: :permanent,
37 | hany_cluster: :permanent,
38 | hany_ml: :load,
39 | hany_web: :load
40 | ]
41 | ],
42 | hany_web: [
43 | include_executables_for: [:unix],
44 | applications: [
45 | livebook: :load,
46 | hany: :load,
47 | hany_cluster: :permanent,
48 | hany_ml: :load,
49 | hany_web: :permanent
50 | ]
51 | ],
52 | hany_ml: [
53 | include_executables_for: [:unix],
54 | applications: [
55 | livebook: :load,
56 | hany: :load,
57 | hany_cluster: :permanent,
58 | hany_ml: :permanent,
59 | hany_web: :load
60 | ]
61 | ],
62 | hany_livebook: [
63 | include_executables_for: [:unix],
64 | applications: [
65 | livebook: :permanent,
66 | kino: :permanent,
67 | hany: :load,
68 | hany_cluster: :permanent,
69 | hany_ml: :load,
70 | hany_web: :load
71 | ]
72 | ]
73 | ]
74 | end
75 | end
76 |
--------------------------------------------------------------------------------
/mix.lock:
--------------------------------------------------------------------------------
1 | %{
2 | "aws_signature": {:hex, :aws_signature, "0.3.1", "67f369094cbd55ffa2bbd8cc713ede14b195fcfb45c86665cd7c5ad010276148", [:rebar3], [], "hexpm", "50fc4dc1d1f7c2d0a8c63f455b3c66ecd74c1cf4c915c768a636f9227704a674"},
3 | "axon": {:hex, :axon, "0.3.1", "d2f678871d439ff623c50eecb255e2e89de9e2cd5d4bb7a40d9a80f3a46b86d1", [:mix], [{:kino, "~> 0.7", [hex: :kino, repo: "hexpm", optional: true]}, {:nx, "~> 0.4.0", [hex: :nx, repo: "hexpm", optional: false]}, {:table_rex, "~> 3.1.1", [hex: :table_rex, repo: "hexpm", optional: true]}], "hexpm", "01e0c085a3f26d0cccd44e29a2f308c537ad71d573bb853130f1fd484caccd55"},
4 | "bumblebee": {:hex, :bumblebee, "0.1.2", "bdc596400b0039df80400c49a9a3c8f0df2000427006dbbc02ba8c308f2b3881", [:mix], [{:axon, "~> 0.3.1", [hex: :axon, repo: "hexpm", optional: false]}, {:castore, "~> 0.1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.4.0", [hex: :jason, repo: "hexpm", optional: false]}, {:nx, "~> 0.4.1", [hex: :nx, repo: "hexpm", optional: false]}, {:nx_image, "~> 0.1.0", [hex: :nx_image, repo: "hexpm", optional: false]}, {:progress_bar, "~> 2.0", [hex: :progress_bar, repo: "hexpm", optional: false]}, {:tokenizers, "~> 0.2.0", [hex: :tokenizers, repo: "hexpm", optional: false]}, {:unpickler, "~> 0.1.0", [hex: :unpickler, repo: "hexpm", optional: false]}], "hexpm", "b360f533e30ac3e0a397aeb33f7f7ae100d26d1efe28f91a5774034bac45f29e"},
5 | "castore": {:hex, :castore, "0.1.20", "62a0126cbb7cb3e259257827b9190f88316eb7aa3fdac01fd6f2dfd64e7f46e9", [:mix], [], "hexpm", "a020b7650529c986c454a4035b6b13a328e288466986307bea3aadb4c95ac98a"},
6 | "complex": {:hex, :complex, "0.4.3", "84db4aad241099a8785446ac6eacf498bf3a60634a0e45c7745d875714ddbf98", [:mix], [], "hexpm", "2ceda96ebddcc22697974f1a2666d4cc5dfdd34f8cd8c4f9dced037bcb41eeb5"},
7 | "connection": {:hex, :connection, "1.1.0", "ff2a49c4b75b6fb3e674bfc5536451607270aac754ffd1bdfe175abe4a6d7a68", [:mix], [], "hexpm", "722c1eb0a418fbe91ba7bd59a47e28008a189d47e37e0e7bb85585a016b2869c"},
8 | "cowboy": {:hex, :cowboy, "2.9.0", "865dd8b6607e14cf03282e10e934023a1bd8be6f6bacf921a7e2a96d800cd452", [:make, :rebar3], [{:cowlib, "2.11.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "2c729f934b4e1aa149aff882f57c6372c15399a20d54f65c8d67bef583021bde"},
9 | "cowboy_telemetry": {:hex, :cowboy_telemetry, "0.4.0", "f239f68b588efa7707abce16a84d0d2acf3a0f50571f8bb7f56a15865aae820c", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7d98bac1ee4565d31b62d59f8823dfd8356a169e7fcbb83831b8a5397404c9de"},
10 | "cowlib": {:hex, :cowlib, "2.11.0", "0b9ff9c346629256c42ebe1eeb769a83c6cb771a6ee5960bd110ab0b9b872063", [:make, :rebar3], [], "hexpm", "2b3e9da0b21c4565751a6d4901c20d1b4cc25cbb7fd50d91d2ab6dd287bc86a9"},
11 | "db_connection": {:hex, :db_connection, "2.4.3", "3b9aac9f27347ec65b271847e6baeb4443d8474289bd18c1d6f4de655b70c94d", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c127c15b0fa6cfb32eed07465e05da6c815b032508d4ed7c116122871df73c12"},
12 | "decimal": {:hex, :decimal, "2.0.0", "a78296e617b0f5dd4c6caf57c714431347912ffb1d0842e998e9792b5642d697", [:mix], [], "hexpm", "34666e9c55dea81013e77d9d87370fe6cb6291d1ef32f46a1600230b1d44f577"},
13 | "earmark_parser": {:hex, :earmark_parser, "1.4.29", "149d50dcb3a93d9f3d6f3ecf18c918fb5a2d3c001b5d3305c926cddfbd33355b", [:mix], [], "hexpm", "4902af1b3eb139016aed210888748db8070b8125c2342ce3dcae4f38dcc63503"},
14 | "ecto": {:hex, :ecto, "3.9.4", "3ee68e25dbe0c36f980f1ba5dd41ee0d3eb0873bccae8aeaf1a2647242bffa35", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "de5f988c142a3aa4ec18b85a4ec34a2390b65b24f02385c1144252ff6ff8ee75"},
15 | "ecto_sql": {:hex, :ecto_sql, "3.9.2", "34227501abe92dba10d9c3495ab6770e75e79b836d114c41108a4bf2ce200ad5", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.9.2", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "1eb5eeb4358fdbcd42eac11c1fbd87e3affd7904e639d77903c1358b2abd3f70"},
16 | "elixir_make": {:hex, :elixir_make, "0.7.5", "784cc00f5fa24239067cc04d449437dcc5f59353c44eb08f188b2b146568738a", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: true]}], "hexpm", "c3d63e8d5c92fa3880d89ecd41de59473fa2e83eeb68148155e25e8b95aa2887"},
17 | "esbuild": {:hex, :esbuild, "0.6.1", "a774bfa7b4512a1211bf15880b462be12a4c48ed753a170c68c63b2c95888150", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}], "hexpm", "569f7409fb5a932211573fc20e2a930a0d5cf3377c5b4f6506c651b1783a1678"},
18 | "exla": {:hex, :exla, "0.4.2", "7d5008c36c942de75efddffe4a4e6aac98da722261b7188b23b1363282a146a8", [:make, :mix], [{:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:nx, "~> 0.4.2", [hex: :nx, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:xla, "~> 0.4.0", [hex: :xla, repo: "hexpm", optional: false]}], "hexpm", "c7c5d70073c30ca4fee3981d992d27f2a2c1d8333b012ab8d0f7330c3624ee79"},
19 | "file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"},
20 | "floki": {:hex, :floki, "0.34.2", "5fad07ef153b3b8ec110b6b155ec3780c4b2c4906297d0b4be1a7162d04a7e02", [:mix], [], "hexpm", "26b9d50f0f01796bc6be611ca815c5e0de034d2128e39cc9702eee6b66a4d1c8"},
21 | "hpax": {:hex, :hpax, "0.1.2", "09a75600d9d8bbd064cdd741f21fc06fc1f4cf3d0fcc335e5aa19be1a7235c84", [:mix], [], "hexpm", "2c87843d5a23f5f16748ebe77969880e29809580efdaccd615cd3bed628a8c13"},
22 | "jason": {:hex, :jason, "1.4.0", "e855647bc964a44e2f67df589ccf49105ae039d4179db7f6271dfd3843dc27e6", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "79a3791085b2a0f743ca04cec0f7be26443738779d09302e01318f97bdb82121"},
23 | "livebook": {:hex, :livebook, "0.8.1", "bdbb69189fd04d487fe42f02740911c6d6d3d2e0b28d4e7e9ec484d24ceb581d", [:mix], [{:aws_signature, "0.3.1", [hex: :aws_signature, repo: "hexpm", optional: false]}, {:castore, "0.1.20", [hex: :castore, repo: "hexpm", optional: false]}, {:connection, "1.1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:earmark_parser, "1.4.29", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:ecto, "3.9.4", [hex: :ecto, repo: "hexpm", optional: false]}, {:jason, "1.4.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mint_web_socket, "1.0.2", [hex: :mint_web_socket, repo: "hexpm", optional: false]}, {:phoenix, "1.6.15", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_ecto, "4.4.0", [hex: :phoenix_ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "3.2.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_live_dashboard, "0.7.2", [hex: :phoenix_live_dashboard, repo: "hexpm", optional: false]}, {:phoenix_live_view, "0.18.3", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:plug_cowboy, "2.5.2", [hex: :plug_cowboy, repo: "hexpm", optional: false]}, {:protobuf, "0.8.0", [hex: :protobuf, repo: "hexpm", optional: false]}, {:telemetry_metrics, "0.6.1", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}, {:telemetry_poller, "1.0.0", [hex: :telemetry_poller, repo: "hexpm", optional: false]}], "hexpm", "b92fd0799dd15a425fc56e5abc391fae8825d476087b9972595e9d261158204e"},
24 | "mime": {:hex, :mime, "2.0.3", "3676436d3d1f7b81b5a2d2bd8405f412c677558c81b1c92be58c00562bb59095", [:mix], [], "hexpm", "27a30bf0db44d25eecba73755acf4068cbfe26a4372f9eb3e4ea3a45956bff6b"},
25 | "mint": {:hex, :mint, "1.4.2", "50330223429a6e1260b2ca5415f69b0ab086141bc76dc2fbf34d7c389a6675b2", [:mix], [{:castore, "~> 0.1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "ce75a5bbcc59b4d7d8d70f8b2fc284b1751ffb35c7b6a6302b5192f8ab4ddd80"},
26 | "mint_web_socket": {:hex, :mint_web_socket, "1.0.2", "0933a4c82f2376e35569b2255cdce94f2e3f993c0d5b04c360460cb8beda7154", [:mix], [{:mint, ">= 1.4.0", [hex: :mint, repo: "hexpm", optional: false]}], "hexpm", "067c5e15439be060f2ab57c468ee4ab29e39cb20b498ed990cb94f62db0efc3a"},
27 | "nx": {:hex, :nx, "0.4.2", "444e9cc1b1e95edf8c9d9d9f22635349a0cd60cb6a07d4954f3016b2d6d178d7", [:mix], [{:complex, "~> 0.4.3", [hex: :complex, repo: "hexpm", optional: false]}], "hexpm", "9d8f110cf733c4bbc86f0a5fe08f6537e106c39bbcb6dfabc7ef33f14f12edb3"},
28 | "nx_image": {:hex, :nx_image, "0.1.0", "ae10fa41fa95126f934d6160ef4320f7db583535fb868415f2562fe19969d245", [:mix], [{:nx, "~> 0.4", [hex: :nx, repo: "hexpm", optional: false]}], "hexpm", "60a2928164cdca540b4c180ff25579b97a5f2a650fc890d40db3e1a7798c93ad"},
29 | "phoenix": {:hex, :phoenix, "1.6.15", "0a1d96bbc10747fd83525370d691953cdb6f3ccbac61aa01b4acb012474b047d", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 1.0 or ~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.2", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "d70ab9fbf6b394755ea88b644d34d79d8b146e490973151f248cacd122d20672"},
30 | "phoenix_ecto": {:hex, :phoenix_ecto, "4.4.0", "0672ed4e4808b3fbed494dded89958e22fb882de47a97634c0b13e7b0b5f7720", [:mix], [{:ecto, "~> 3.3", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "09864e558ed31ee00bd48fcc1d4fc58ae9678c9e81649075431e69dbabb43cc1"},
31 | "phoenix_html": {:hex, :phoenix_html, "3.2.0", "1c1219d4b6cb22ac72f12f73dc5fad6c7563104d083f711c3fcd8551a1f4ae11", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "36ec97ba56d25c0136ef1992c37957e4246b649d620958a1f9fa86165f8bc54f"},
32 | "phoenix_live_dashboard": {:hex, :phoenix_live_dashboard, "0.7.2", "97cc4ff2dba1ebe504db72cb45098cb8e91f11160528b980bd282cc45c73b29c", [:mix], [{:ecto, "~> 3.6.2 or ~> 3.7", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_mysql_extras, "~> 0.5", [hex: :ecto_mysql_extras, repo: "hexpm", optional: true]}, {:ecto_psql_extras, "~> 0.7", [hex: :ecto_psql_extras, repo: "hexpm", optional: true]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.18.3", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6 or ~> 1.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "0e5fdf063c7a3b620c566a30fcf68b7ee02e5e46fe48ee46a6ec3ba382dc05b7"},
33 | "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.4.1", "2aff698f5e47369decde4357ba91fc9c37c6487a512b41732818f2204a8ef1d3", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "9bffb834e7ddf08467fe54ae58b5785507aaba6255568ae22b4d46e2bb3615ab"},
34 | "phoenix_live_view": {:hex, :phoenix_live_view, "0.18.3", "2e3d009422addf8b15c3dccc65ce53baccbe26f7cfd21d264680b5867789a9c1", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.1", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c8845177a866e017dcb7083365393c8f00ab061b8b6b2bda575891079dce81b2"},
35 | "phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.1", "ba04e489ef03763bf28a17eb2eaddc2c20c6d217e2150a61e3298b0f4c2012b5", [:mix], [], "hexpm", "81367c6d1eea5878ad726be80808eb5a787a23dee699f96e72b1109c57cdd8d9"},
36 | "phoenix_template": {:hex, :phoenix_template, "1.0.1", "85f79e3ad1b0180abb43f9725973e3b8c2c3354a87245f91431eec60553ed3ef", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "157dc078f6226334c91cb32c1865bf3911686f8bcd6bcff86736f6253e6993ee"},
37 | "phoenix_view": {:hex, :phoenix_view, "2.0.2", "6bd4d2fd595ef80d33b439ede6a19326b78f0f1d8d62b9a318e3d9c1af351098", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}], "hexpm", "a929e7230ea5c7ee0e149ffcf44ce7cf7f4b6d2bfe1752dd7c084cdff152d36f"},
38 | "plug": {:hex, :plug, "1.14.0", "ba4f558468f69cbd9f6b356d25443d0b796fbdc887e03fa89001384a9cac638f", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "bf020432c7d4feb7b3af16a0c2701455cbbbb95e5b6866132cb09eb0c29adc14"},
39 | "plug_cowboy": {:hex, :plug_cowboy, "2.5.2", "62894ccd601cf9597e2c23911ff12798a8a18d237e9739f58a6b04e4988899fe", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "ea6e87f774c8608d60c8d34022a7d073bd7680a0a013f049fc62bf35efea1044"},
40 | "plug_crypto": {:hex, :plug_crypto, "1.2.3", "8f77d13aeb32bfd9e654cb68f0af517b371fb34c56c9f2b58fe3df1235c1251a", [:mix], [], "hexpm", "b5672099c6ad5c202c45f5a403f21a3411247f164e4a8fab056e5cd8a290f4a2"},
41 | "postgrex": {:hex, :postgrex, "0.16.5", "fcc4035cc90e23933c5d69a9cd686e329469446ef7abba2cf70f08e2c4b69810", [:mix], [{:connection, "~> 1.1", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "edead639dc6e882618c01d8fc891214c481ab9a3788dfe38dd5e37fd1d5fb2e8"},
42 | "progress_bar": {:hex, :progress_bar, "2.0.1", "7b40200112ae533d5adceb80ff75fbe66dc753bca5f6c55c073bfc122d71896d", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "2519eb58a2f149a3a094e729378256d8cb6d96a259ec94841bd69fdc71f18f87"},
43 | "protobuf": {:hex, :protobuf, "0.8.0", "61b27d6fd50e7b1b2eb0ee17c1f639906121f4ef965ae0994644eb4c68d4647d", [:mix], [{:jason, "~> 1.2", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "3644ed846fd6f5e3b5c2cd617aa8344641e230edf812a45365fee7622bccd25a"},
44 | "ranch": {:hex, :ranch, "1.8.0", "8c7a100a139fd57f17327b6413e4167ac559fbc04ca7448e9be9057311597a1d", [:make, :rebar3], [], "hexpm", "49fbcfd3682fab1f5d109351b61257676da1a2fdbe295904176d5e521a2ddfe5"},
45 | "rustler_precompiled": {:hex, :rustler_precompiled, "0.6.1", "160b545bce8bf9a3f1b436b2c10f53574036a0db628e40f393328cbbe593602f", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:rustler, "~> 0.23", [hex: :rustler, repo: "hexpm", optional: true]}], "hexpm", "0dd269fa261c4e3df290b12031c575fff07a542749f7b0e8b744d72d66c43600"},
46 | "telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"},
47 | "telemetry_metrics": {:hex, :telemetry_metrics, "0.6.1", "315d9163a1d4660aedc3fee73f33f1d355dcc76c5c3ab3d59e76e3edf80eef1f", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7be9e0871c41732c233be71e4be11b96e56177bf15dde64a8ac9ce72ac9834c6"},
48 | "telemetry_poller": {:hex, :telemetry_poller, "1.0.0", "db91bb424e07f2bb6e73926fcafbfcbcb295f0193e0a00e825e589a0a47e8453", [:rebar3], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "b3a24eafd66c3f42da30fc3ca7dda1e9d546c12250a2d60d7b81d264fbec4f6e"},
49 | "tokenizers": {:hex, :tokenizers, "0.2.0", "3aa9811396680f849803f6a3978a310a653059613592710ce5f883d67ff17a33", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: false]}, {:rustler, ">= 0.0.0", [hex: :rustler, repo: "hexpm", optional: true]}, {:rustler_precompiled, "~> 0.5", [hex: :rustler_precompiled, repo: "hexpm", optional: false]}], "hexpm", "2496fd44cf96bcefc70e75cf7e34126de8b63ccb9ad35967d6d5d8661cbdb6b7"},
50 | "unpickler": {:hex, :unpickler, "0.1.0", "c2262c0819e6985b761e7107546cef96a485f401816be5304a65fdd200d5bd6a", [:mix], [], "hexpm", "e2b3f61e62406187ac52afead8a63bfb4e49394028993f3c4c42712743cab79e"},
51 | "xla": {:hex, :xla, "0.4.4", "c3a8ed1f579bda949df505e49ff65415c8281d991fbd6ae1d8f3c5d0fd155f54", [:make, :mix], [{:elixir_make, "~> 0.4", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "484f3f9011db3c9f1ff1e98eecefd382f3882a07ada540fd58803db1d2dab671"},
52 | }
53 |
--------------------------------------------------------------------------------