feat: Move main router and menu into app to allow easier development

This commit is contained in:
Robert Prehn 2020-09-30 15:10:17 -05:00
parent c7a0bf1b3d
commit cd2379d4e7
62 changed files with 5773 additions and 13953 deletions

View file

@ -7,7 +7,6 @@ defmodule Admin.Router do
plug :fetch_flash
plug :protect_from_forgery
plug :put_secure_browser_headers
plug :put_layout, {CoreWeb.LayoutView, :app}
end
pipeline :require_admin do
@ -18,5 +17,5 @@ defmodule Admin.Router do
plug :accepts, ["json"]
end
use Kaffy.Routes, scope: "/", pipe_through: [:require_admin]
use Admin.Routes
end

View file

@ -0,0 +1,7 @@
defmodule Admin.Routes do
defmacro __using__(_opts \\ []) do
quote do
use Kaffy.Routes, scope: "/admin", pipe_through: [:require_admin]
end
end
end

View file

@ -9,6 +9,10 @@
@import "blog";
@import "code";
body {
outline: 1px pink solid;
}
input[type="checkbox"]::after {
content: "";
color: currentColor;

View file

@ -1,31 +0,0 @@
/* This file is for your main application css. */
@import "./phoenix.css";
/* Alerts and form errors */
.alert {
padding: 15px;
margin-bottom: 20px;
border: 1px solid transparent;
border-radius: 4px;
}
.alert-info {
color: #31708f;
background-color: #d9edf7;
border-color: #bce8f1;
}
.alert-warning {
color: #8a6d3b;
background-color: #fcf8e3;
border-color: #faebcc;
}
.alert-danger {
color: #a94442;
background-color: #f2dede;
border-color: #ebccd1;
}
.alert p {
margin-bottom: 0;
}
.alert:empty {
display: none;
}

View file

@ -13,3 +13,45 @@ import "../css/app.css"
// import socket from "./socket"
//
import "phoenix_html"
import { ready } from "./utils"
function togglePasswordFieldVisibility()
{
const passwordFields = document.querySelectorAll('[name="user[password]"]')
passwordFields.forEach((el) => {
if (el.type == 'password')
{
el.type = 'text'
}
else
{
el.type = 'password'
}
})
}
const toggleSidebar = (event) => {
document.querySelectorAll('.sidebar').forEach((el) => {
el.classList.toggle('visible')
})
}
ready(() => {
document.getElementById('nav-toggle').onclick = function(){
document.getElementById("nav-content").classList.toggle("hidden");
}
document.querySelectorAll('.js-passwordRevealer').forEach((el) => {
el.addEventListener('click', togglePasswordFieldVisibility)
})
document.querySelectorAll('.js-SidebarOpener').forEach((el) => {
el.addEventListener('click', toggleSidebar)
})
document.querySelectorAll('.js-flash-closer').forEach((el) => {
el.addEventListener('click', () => {
el.closest('.js-flash').remove()
})
})
})

File diff suppressed because it is too large Load diff

View file

@ -4,11 +4,22 @@
"license": "MIT",
"scripts": {
"deploy": "webpack --mode production",
"watch": "webpack --mode development --watch"
"watch": "webpack --mode development --watch",
"preinstall": "npx npm-force-resolutions",
"profile": "webpack --mode development --plugin webpack/lib/debug/ProfilingPlugin"
},
"dependencies": {
"phoenix": "file:../../../deps/phoenix",
"phoenix_html": "file:../../../deps/phoenix_html"
"@fortawesome/fontawesome-free": "^5.14.0",
"autoprefixer": "^9.8.6",
"csswring": "^7.0.0",
"glob": "^7.1.6",
"gulp": "^4.0.2",
"phoenix": "file:/../../../deps/phoenix",
"phoenix_html": "file:/../../../deps/phoenix_html",
"postcss-color-function": "^4.1.0",
"simplemde": "^1.11.2",
"stylelint": "^13.6.1",
"tailwindcss": "^1.7.3"
},
"devDependencies": {
"@babel/core": "^7.0.0",
@ -16,12 +27,26 @@
"babel-loader": "^8.0.0",
"copy-webpack-plugin": "^5.1.1",
"css-loader": "^3.4.2",
"sass-loader": "^8.0.2",
"node-sass": "^4.13.1",
"extract-text-webpack-plugin": "^3.0.2",
"file-loader": "^6.0.0",
"image-webpack-loader": "^6.0.0",
"less": "^3.11.3",
"less-loader": "^6.2.0",
"mini-css-extract-plugin": "^0.9.0",
"node-sass": "^4.13.1",
"optimize-css-assets-webpack-plugin": "^5.0.1",
"postcss-css-variables": "^0.17.0",
"postcss-import": "^12.0.1",
"postcss-loader": "^3.0.0",
"sass-loader": "^8.0.2",
"style-loader": "^1.2.1",
"stylelint-config-standard": "^20.0.0",
"stylelint-order": "^4.1.0",
"terser-webpack-plugin": "^2.3.2",
"webpack": "4.41.5",
"webpack-cli": "^3.3.2"
},
"resolutions": {
"graceful-fs": "4.2.3"
}
}

View file

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 29 KiB

View file

Before

Width:  |  Height:  |  Size: 6.4 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

View file

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

View file

Before

Width:  |  Height:  |  Size: 8.1 KiB

After

Width:  |  Height:  |  Size: 8.1 KiB

View file

Before

Width:  |  Height:  |  Size: 7.2 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

View file

@ -5,6 +5,8 @@ const TerserPlugin = require('terser-webpack-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const nodeModulesPath = path.resolve(__dirname, 'node_modules')
module.exports = (env, options) => {
const devMode = options.mode !== 'production';
@ -15,17 +17,42 @@ module.exports = (env, options) => {
new OptimizeCSSAssetsPlugin({})
]
},
mode: options.mode,
devtool: devMode ? 'source-map' : undefined,
entry: {
'app': glob.sync('./vendor/**/*.js').concat(['./js/app.js'])
'app': glob.sync('./vendor/**/*.js').concat(['./js/app.js']),
'content-editor': ['./js/content-editor.js'],
'tailwind': ['./tailwind.config.js'],
},
output: {
filename: '[name].js',
path: path.resolve(__dirname, '../priv/static/js'),
publicPath: '/js/'
filename: 'js/[name].js',
path: path.resolve(__dirname, '../priv/static/')
},
devtool: devMode ? 'source-map' : undefined,
module: {
rules: [
// For images and fonts found in our scss files
{
test: /\.(jpg|jpeg|gif|png)$/,
use: [
'file-loader',
{
loader: 'image-webpack-loader',
options: {
disable: devMode,
},
},
],
},
{
test: /\.(woff2?|ttf|eot|svg)(\?[a-z0-9\=\.]+)?$/,
loader: 'file-loader',
options: {
publicPath: '/fonts',
outputPath: (url, resourcePath, context) => {
return `/fonts/${url}`;
},
}
},
{
test: /\.js$/,
exclude: /node_modules/,
@ -34,18 +61,45 @@ module.exports = (env, options) => {
}
},
{
test: /\.[s]?css$/,
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'sass-loader',
{loader: MiniCssExtractPlugin.loader, options: {sourceMap: true}},
{loader: 'css-loader', options: {sourceMap: true}},
{loader: 'postcss-loader', options: {sourceMap: true}},
],
}
},
]
},
plugins: [
new MiniCssExtractPlugin({ filename: '../css/app.css' }),
new CopyWebpackPlugin([{ from: 'static/', to: '../' }])
]
new MiniCssExtractPlugin({
filename: 'css/[name].css',
chunkFilename: '[id].css',
}),
new CopyWebpackPlugin([
{
from: path.resolve(__dirname, 'static'),
to: path.resolve(__dirname, '../priv/static'),
},
]),
],
resolve: {
alias: {
"../webfonts/fa-brands-400.eot": path.resolve(__dirname, "node_modules/@fortawesome/fontawesome-free/webfonts/fa-brands-400.eot"),
"../webfonts/fa-brands-400.woff2": path.resolve(__dirname, "node_modules/@fortawesome/fontawesome-free/webfonts/fa-brands-400.woff2"),
"../webfonts/fa-brands-400.woff": path.resolve(__dirname, "node_modules/@fortawesome/fontawesome-free/webfonts/fa-brands-400.woff"),
"../webfonts/fa-brands-400.ttf": path.resolve(__dirname, "node_modules/@fortawesome/fontawesome-free/webfonts/fa-brands-400.ttf"),
"../webfonts/fa-brands-400.svg": path.resolve(__dirname, "node_modules/@fortawesome/fontawesome-free/webfonts/fa-brands-400.svg"),
"../webfonts/fa-regular-400.eot": path.resolve(__dirname, "node_modules/@fortawesome/fontawesome-free/webfonts/fa-regular-400.eot"),
"../webfonts/fa-regular-400.woff2": path.resolve(__dirname, "node_modules/@fortawesome/fontawesome-free/webfonts/fa-regular-400.woff2"),
"../webfonts/fa-regular-400.woff": path.resolve(__dirname, "node_modules/@fortawesome/fontawesome-free/webfonts/fa-regular-400.woff"),
"../webfonts/fa-regular-400.ttf": path.resolve(__dirname, "node_modules/@fortawesome/fontawesome-free/webfonts/fa-regular-400.ttf"),
"../webfonts/fa-regular-400.svg": path.resolve(__dirname, "node_modules/@fortawesome/fontawesome-free/webfonts/fa-regular-400.svg"),
"../webfonts/fa-solid-900.eot": path.resolve(__dirname, "node_modules/@fortawesome/fontawesome-free/webfonts/fa-solid-900.eot"),
"../webfonts/fa-solid-900.woff2": path.resolve(__dirname, "node_modules/@fortawesome/fontawesome-free/webfonts/fa-solid-900.woff2"),
"../webfonts/fa-solid-900.woff": path.resolve(__dirname, "node_modules/@fortawesome/fontawesome-free/webfonts/fa-solid-900.woff"),
"../webfonts/fa-solid-900.ttf": path.resolve(__dirname, "node_modules/@fortawesome/fontawesome-free/webfonts/fa-solid-900.ttf"),
"../webfonts/fa-solid-900.svg": path.resolve(__dirname, "node_modules/@fortawesome/fontawesome-free/webfonts/fa-solid-900.svg"),
}
},
}
};

View file

@ -10,6 +10,8 @@ defmodule App.Application do
App.Repo,
# Start the Telemetry supervisor
AppWeb.Telemetry,
# Set up the pubsub server
{Phoenix.PubSub, name: App.PubSub},
# Start the Endpoint (http/https)
AppWeb.Endpoint
# Start a worker by calling: AppWeb.Worker.start_link(arg)

View file

@ -26,6 +26,12 @@ defmodule AppWeb.Endpoint do
gzip: false,
only: ~w(css fonts images js favicon.ico robots.txt)
plug Plug.Static,
at: "/kaffy",
from: :kaffy,
gzip: false,
only: ~w(assets)
# Code reloading can be explicitly enabled under the
# :code_reloader configuration of your endpoint.
if code_reloading? do
@ -50,5 +56,7 @@ defmodule AppWeb.Endpoint do
plug Plug.MethodOverride
plug Plug.Head
plug Plug.Session, @session_options
plug Pow.Plug.Session, otp_app: :auth_web
plug PowPersistentSession.Plug.Cookie
plug AppWeb.Router
end

View file

@ -1,5 +1,10 @@
defmodule AppWeb.Router do
use AppWeb, :router
use Pow.Phoenix.Router
use Pow.Extension.Phoenix.Router,
extensions: [PowResetPassword, PowEmailConfirmation]
alias AuthWeb.Plugs.{RequireAdmin}
pipeline :browser do
plug :accepts, ["html"]
@ -7,13 +12,21 @@ defmodule AppWeb.Router do
plug :fetch_flash
plug :protect_from_forgery
plug :put_secure_browser_headers
plug :put_layout, {CoreWeb.LayoutView, :app}
plug :put_layout, {AppWeb.LayoutView, :app}
end
pipeline :api do
plug :accepts, ["json"]
end
pipeline :require_admin do
plug(RequireAdmin)
end
pipeline :require_auth do
plug Pow.Plug.RequireAuthenticated, error_handler: Pow.Phoenix.PlugErrorHandler
end
# Other scopes may use custom stacks.
# scope "/api", AppWeb do
# pipe_through :api
@ -33,5 +46,17 @@ defmodule AppWeb.Router do
pipe_through :browser
live_dashboard "/dashboard", metrics: AppWeb.Telemetry
end
forward "/sent_emails", Bamboo.SentEmailViewerPlug
end
scope "/" do
pipe_through :browser
pow_routes()
pow_extension_routes()
end
use Admin.Routes
use Content.Routes
end

View file

@ -20,16 +20,16 @@
<% end %>
<%= if Pow.Plug.current_user(@conn) do %>
<li class="mr-3">
<%= link "Sign Out", to: AuthWeb.Router.Helpers.pow_session_path(%URI{path: "/auth"}, :delete), method: :delete, class: "inline-block text-white no-underline hover:text-white hover:text-underline py-2 px-4" %>
<%= link "Sign Out", to: Routes.pow_session_path(@conn, :delete), method: :delete, class: "inline-block text-white no-underline hover:text-white hover:text-underline py-2 px-4" %>
</li>
<% else %>
<li class="mr-3">
<%= link to: AuthWeb.Router.Helpers.pow_session_path(%URI{path: "/auth"}, :new), class: "inline-block text-white no-underline hover:text-white hover:text-underline py-2 px-4" do %>
<%= link to: Routes.pow_session_path(@conn, :new), class: "inline-block text-white no-underline hover:text-white hover:text-underline py-2 px-4" do %>
Log In
<% end %>
</li>
<li class="mr-3">
<%= link to: AuthWeb.Router.Helpers.pow_registration_path(%URI{path: "/auth"}, :new), class: "inline-block text-white no-underline hover:text-white hover:text-underline py-2 px-4" do %>
<%= link to: Routes.pow_registration_path(@conn, :new), class: "inline-block text-white no-underline hover:text-white hover:text-underline py-2 px-4" do %>
Sign Up
<% end %>
</li>

View file

@ -1,3 +1,39 @@
defmodule AppWeb.LayoutView do
use AppWeb, :view
def title(view_module, template, assigns) do
delegate_with_default(view_module, :title, [view_module, template, assigns], I18n.t!("en", "site.title"))
end
def excerpt(view_module, template, assigns) do
delegate_with_default(view_module, :excerpt, [view_module, template, assigns], I18n.t!("en", "site.excerpt"))
end
def feed_tag(conn, view_module, view_template, assigns) do
delegate_with_default(view_module, :feed_tag, [conn, view_module, view_template, assigns], nil)
end
defp delegate_with_default(nil, _, _, default), do: default
defp delegate_with_default(view_module, function_name, args, default) do
sibling_layout = sibling_layout_view(view_module)
if function_exported?(sibling_layout, function_name, args |> Enum.count()) do
apply(sibling_layout, function_name, args)
else
default
end
end
defp sibling_layout_view(view_module) do
view_module
|> parent_module()
|> Module.concat("LayoutView")
end
defp parent_module(mod) do
[_|tail] = Module.split(mod) |> Enum.reverse()
tail
|> Enum.reverse()
|> Module.concat()
end
end

View file

@ -39,6 +39,8 @@ defmodule App.MixProject do
# Type `mix help deps` for examples and options.
defp deps do
[
{:admin, in_umbrella: true},
{:content, in_umbrella: true},
{:core, in_umbrella: true},
{:ecto_sql, "~> 3.4"},
{:excoveralls, "~> 0.10", only: [:dev, :test]},

View file

@ -1,8 +1,31 @@
defmodule App.LayoutViewTest do
use App.ConnCase, async: true
# When testing helpers, you may want to import Phoenix.HTML and
# use functions such as safe_to_string() to convert the helper
# result into an HTML string.
# import Phoenix.HTML
import AppWeb.LayoutView
describe "title/3" do
def default_title do
I18n.t! "en", "site.title"
end
test "for nil" do
assert title(nil, nil, nil) =~ default_title()
end
end
describe "excerpt/3" do
def default_excerpt do
I18n.t! "en", "site.excerpt"
end
test "for nil" do
assert excerpt(nil, nil, nil) =~ default_excerpt()
end
end
describe "feed_tag/4" do
test "for nil" do
assert feed_tag(nil, nil, nil, nil) == nil
end
end
end

View file

@ -10,7 +10,6 @@ defmodule AuthWeb.Router do
plug :fetch_flash
plug :protect_from_forgery
plug :put_secure_browser_headers
plug :put_layout, {CoreWeb.LayoutView, :app}
end
pipeline :api do

View file

@ -5,5 +5,4 @@ defmodule AuthWeb.EmailView do
pattern: "**/*"
import Phoenix.HTML, only: [raw: 1]
import CoreWeb.EmailHelpers
end

View file

@ -29,7 +29,7 @@ defmodule Content.MarkupField do
"""
end
def render_index(conn, resource, field, _opts) do
def render_index(_conn, resource, field, _opts) do
case Map.get(resource, field) do
nil ->
""

View file

@ -77,8 +77,14 @@ defmodule Content.PostsController do
e in Phoenix.Template.UndefinedError ->
case e do
%{template: ^path} ->
router =
case conn do
%{private: %{phoenix_router: router}} -> router
_ -> Content.Router
end
# The static page we're looking for is missing, so this is just a 404
raise Phoenix.Router.NoRouteError.exception(conn: conn, router: Content.Router)
raise Phoenix.Router.NoRouteError.exception(conn: conn, router: router)
_ ->
# We aren't missing the static page, we're missing a partial. This is probably
# a developer error, so bubble it up

View file

@ -8,57 +8,19 @@ defmodule Content.Router do
plug :fetch_flash
plug :protect_from_forgery
plug :put_secure_browser_headers
plug :put_layout, {CoreWeb.LayoutView, :app}
end
pipeline :api do
plug :accepts, ["json"]
end
pipeline :feed do
plug :accepts, ["rss"]
plug :fetch_session
plug :protect_from_forgery
plug :put_secure_browser_headers
pipeline :require_admin do
plug(RequireAdmin)
end
pipeline :require_auth do
plug Pow.Plug.RequireAuthenticated, error_handler: Pow.Phoenix.PlugErrorHandler
end
pipeline :require_admin do
plug(RequireAdmin)
end
pipeline :admin_layout do
plug :put_layout, {Content.LayoutView, :admin}
end
scope "/", Content do
pipe_through([:browser, :require_auth, :require_admin, :admin_layout])
put "/posts/preview", PostsController, :preview
post "/posts/preview", PostsController, :preview
end
scope "/", Content do
pipe_through :feed # Use the default browser stack
get "/category/:category/feed.rss", FeedsController, :index, as: :category_feed
get "/feed.rss", FeedsController, :index, as: :index_feed
end
scope "/", Content do
pipe_through :browser # Use the default browser stack
resources "/comments", CommentController, as: :comment, only: [:create, :delete, :update]
get "/page/:page", PostsController, :index_posts, as: :blog_page
get "/category/:category", PostsController, :index_posts, as: :category
get "/category/:category/page/:page", PostsController, :index, as: :category_page
post "/wp-login.php", PostPasswordController, :create
get "/", PostsController, :index
resources "/sitemap", SitemapController, only: [:index]
get "/:id", PostsController, :show
get "/:id/:page", PostsController, :show, as: :paged_post
end
use Content.Routes
end

View file

@ -0,0 +1,40 @@
defmodule Content.Routes do
defmacro __using__(_opts \\ []) do
quote do
pipeline :feed do
plug :accepts, ["rss"]
plug :fetch_session
plug :protect_from_forgery
plug :put_secure_browser_headers
end
scope "/", Content do
pipe_through([:browser, :require_auth, :require_admin])
put "/posts/preview", PostsController, :preview
post "/posts/preview", PostsController, :preview
end
scope "/", Content do
pipe_through :feed # Use the default browser stack
get "/category/:category/feed.rss", FeedsController, :index, as: :category_feed
get "/feed.rss", FeedsController, :index, as: :index_feed
end
scope "/", Content do
pipe_through :browser # Use the default browser stack
resources "/comments", CommentController, as: :comment, only: [:create, :delete, :update]
get "/page/:page", PostsController, :index_posts, as: :blog_page
get "/category/:category", PostsController, :index_posts, as: :category
get "/category/:category/page/:page", PostsController, :index, as: :category_page
post "/wp-login.php", PostPasswordController, :create
get "/", PostsController, :index
resources "/sitemap", SitemapController, only: [:index]
get "/:id", PostsController, :show
get "/:id/:page", PostsController, :show, as: :paged_post
end
end
end
end

View file

@ -0,0 +1,22 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title><%= title(@view_module, @view_template, assigns) %></title>
<link rel="stylesheet" href="<%= Routes.static_path(@conn, "/css/app.css") %>"/>
<%= feed_tag(@conn, @view_module, @view_template, assigns) %>
<script defer type="text/javascript" src="<%= Routes.static_path(@conn, "/js/app.js") %>"></script>
</head>
<body class="text-gray-800 antialiased">
<main role="main">
<!-- Page Contents -->
<div class="bg-gray-100 min-h-screen">
<%= flash_block(@conn) %>
<%= @inner_content %>
</div>
</main>
</body>
</html>

View file

@ -9,7 +9,7 @@
<div class="Article-content <%= if post.format, do: post.format.slug %> e-content">
<%= render "thumb.html", post: post, thumbs: @thumbs %>
<div class="Article-content-words">
<%= raw post |> Content.Post.content_page(1) |> Content.Post.before_more |> process_content |> () |> raw %>
<%= raw post |> Content.Post.content_page(1) |> Content.Post.before_more |> process_content |> raw %>
<%= if post.content =~ "<!--more-->" do %>
<p>
<%= link "Keep Reading", to: Routes.posts_path(@conn, :show, post) %>

View file

@ -20,8 +20,8 @@
</div>
</div>
<div class="w-full lg:w-8/12 px-4 ml-auto mr-auto text-center my-12">
<%= styled_button_link "Log In", to: AuthWeb.Router.Helpers.pow_session_path(%URI{path: "/auth"}, :new) %>
<%= styled_button_link "Sign Up", to: AuthWeb.Router.Helpers.pow_registration_path(%URI{path: "/auth"}, :new) %>
<%= styled_button_link "Log In", to: "/session/new" %>
<%= styled_button_link "Sign Up", to: "/registration/new" %>
</div>
</div>
</div>
@ -160,8 +160,8 @@
</h1>
</div>
<div class="w-full lg:w-8/12 px-4 ml-auto mr-auto text-center my-12">
<%= styled_button_link "Log In", to: AuthWeb.Router.Helpers.pow_session_path(%URI{path: "/auth"}, :new) %>
<%= styled_button_link "Sign Up", to: AuthWeb.Router.Helpers.pow_registration_path(%URI{path: "/auth"}, :new) %>
<%= styled_button_link "Log In", to: "/session/new" %>
<%= styled_button_link "Sign Up", to: "/registration/new" %>
</div>
</div>
</div>

View file

@ -1,5 +0,0 @@
{
"presets": [
"@babel/preset-env"
]
}

File diff suppressed because one or more lines are too long

View file

@ -1,57 +0,0 @@
// We need to import the CSS so that webpack will load it.
// The MiniCssExtractPlugin is used to separate it out into
// its own CSS file.
import "../css/app.css"
// webpack automatically bundles all modules in your
// entry points. Those entry points can be configured
// in "webpack.config.js".
//
// Import deps with the dep name or local files with a relative path, for example:
//
// import {Socket} from "phoenix"
// import socket from "./socket"
//
import "phoenix_html"
import { ready } from "./utils"
function togglePasswordFieldVisibility()
{
const passwordFields = document.querySelectorAll('[name="user[password]"]')
passwordFields.forEach((el) => {
if (el.type == 'password')
{
el.type = 'text'
}
else
{
el.type = 'password'
}
})
}
const toggleSidebar = (event) => {
document.querySelectorAll('.sidebar').forEach((el) => {
el.classList.toggle('visible')
})
}
ready(() => {
document.getElementById('nav-toggle').onclick = function(){
document.getElementById("nav-content").classList.toggle("hidden");
}
document.querySelectorAll('.js-passwordRevealer').forEach((el) => {
el.addEventListener('click', togglePasswordFieldVisibility)
})
document.querySelectorAll('.js-SidebarOpener').forEach((el) => {
el.addEventListener('click', toggleSidebar)
})
document.querySelectorAll('.js-flash-closer').forEach((el) => {
el.addEventListener('click', () => {
el.closest('.js-flash').remove()
})
})
})

View file

@ -1,63 +0,0 @@
// NOTE: The contents of this file will only be executed if
// you uncomment its entry in "assets/js/app.js".
// To use Phoenix channels, the first step is to import Socket,
// and connect at the socket path in "lib/web/endpoint.ex".
//
// Pass the token on params as below. Or remove it
// from the params if you are not using authentication.
import {Socket} from "phoenix"
let socket = new Socket("/socket", {params: {token: window.userToken}})
// When you connect, you'll often need to authenticate the client.
// For example, imagine you have an authentication plug, `MyAuth`,
// which authenticates the session and assigns a `:current_user`.
// If the current user exists you can assign the user's token in
// the connection for use in the layout.
//
// In your "lib/web/router.ex":
//
// pipeline :browser do
// ...
// plug MyAuth
// plug :put_user_token
// end
//
// defp put_user_token(conn, _) do
// if current_user = conn.assigns[:current_user] do
// token = Phoenix.Token.sign(conn, "user socket", current_user.id)
// assign(conn, :user_token, token)
// else
// conn
// end
// end
//
// Now you need to pass this token to JavaScript. You can do so
// inside a script tag in "lib/web/templates/layout/app.html.eex":
//
// <script>window.userToken = "<%= assigns[:user_token] %>";</script>
//
// You will need to verify the user token in the "connect/3" function
// in "lib/web/channels/user_socket.ex":
//
// def connect(%{"token" => token}, socket, _connect_info) do
// # max_age: 1209600 is equivalent to two weeks in seconds
// case Phoenix.Token.verify(socket, "user socket", token, max_age: 1209600) do
// {:ok, user_id} ->
// {:ok, assign(socket, :user, user_id)}
// {:error, reason} ->
// :error
// end
// end
//
// Finally, connect to the socket:
socket.connect()
// Now that you are connected, you can join channels with a topic:
let channel = socket.channel("topic:subtopic", {})
channel.join()
.receive("ok", resp => { console.log("Joined successfully", resp) })
.receive("error", resp => { console.log("Unable to join", resp) })
export default socket

File diff suppressed because it is too large Load diff

View file

@ -1,52 +0,0 @@
{
"repository": {},
"description": " ",
"license": "MIT",
"scripts": {
"deploy": "webpack --mode production",
"watch": "webpack --mode development --watch",
"preinstall": "npx npm-force-resolutions",
"profile": "webpack --mode development --plugin webpack/lib/debug/ProfilingPlugin"
},
"dependencies": {
"@fortawesome/fontawesome-free": "^5.14.0",
"autoprefixer": "^9.8.6",
"csswring": "^7.0.0",
"glob": "^7.1.6",
"gulp": "^4.0.2",
"phoenix": "file:../deps/phoenix",
"phoenix_html": "file:../deps/phoenix_html",
"postcss-color-function": "^4.1.0",
"simplemde": "^1.11.2",
"stylelint": "^13.6.1",
"tailwindcss": "^1.7.3"
},
"devDependencies": {
"@babel/core": "^7.0.0",
"@babel/preset-env": "^7.0.0",
"babel-loader": "^8.0.0",
"copy-webpack-plugin": "^5.1.1",
"css-loader": "^3.4.2",
"extract-text-webpack-plugin": "^3.0.2",
"file-loader": "^6.0.0",
"image-webpack-loader": "^6.0.0",
"less": "^3.11.3",
"less-loader": "^6.2.0",
"mini-css-extract-plugin": "^0.9.0",
"node-sass": "^4.13.1",
"optimize-css-assets-webpack-plugin": "^5.0.1",
"postcss-css-variables": "^0.17.0",
"postcss-import": "^12.0.1",
"postcss-loader": "^3.0.0",
"sass-loader": "^8.0.2",
"style-loader": "^1.2.1",
"stylelint-config-standard": "^20.0.0",
"stylelint-order": "^4.1.0",
"terser-webpack-plugin": "^2.3.2",
"webpack": "4.41.5",
"webpack-cli": "^3.3.2"
},
"resolutions": {
"graceful-fs": "4.2.3"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

View file

@ -1,5 +0,0 @@
# See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file
#
# To ban all spiders from the entire site uncomment the next two lines:
# User-agent: *
# Disallow: /

View file

@ -1,105 +0,0 @@
const path = require('path');
const glob = require('glob');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const TerserPlugin = require('terser-webpack-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const nodeModulesPath = path.resolve(__dirname, 'node_modules')
module.exports = (env, options) => {
const devMode = options.mode !== 'production';
return {
optimization: {
minimizer: [
new TerserPlugin({ cache: true, parallel: true, sourceMap: devMode }),
new OptimizeCSSAssetsPlugin({})
]
},
mode: options.mode,
devtool: devMode ? 'source-map' : undefined,
entry: {
'app': glob.sync('./vendor/**/*.js').concat(['./js/app.js']),
'content-editor': ['./js/content-editor.js'],
'tailwind': ['./tailwind.config.js'],
},
output: {
filename: 'js/[name].js',
path: path.resolve(__dirname, '../priv/static/')
},
module: {
rules: [
// For images and fonts found in our scss files
{
test: /\.(jpg|jpeg|gif|png)$/,
use: [
'file-loader',
{
loader: 'image-webpack-loader',
options: {
disable: devMode,
},
},
],
},
{
test: /\.(woff2?|ttf|eot|svg)(\?[a-z0-9\=\.]+)?$/,
loader: 'file-loader',
options: {
publicPath: '/fonts',
outputPath: (url, resourcePath, context) => {
return `/fonts/${url}`;
},
}
},
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader'
}
},
{
test: /\.css$/,
use: [
{loader: MiniCssExtractPlugin.loader, options: {sourceMap: true}},
{loader: 'css-loader', options: {sourceMap: true}},
{loader: 'postcss-loader', options: {sourceMap: true}},
],
},
]
},
plugins: [
new MiniCssExtractPlugin({
filename: 'css/[name].css',
chunkFilename: '[id].css',
}),
new CopyWebpackPlugin([
{
from: path.resolve(__dirname, 'static'),
to: path.resolve(__dirname, '../priv/static'),
},
]),
],
resolve: {
alias: {
"../webfonts/fa-brands-400.eot": path.resolve(__dirname, "node_modules/@fortawesome/fontawesome-free/webfonts/fa-brands-400.eot"),
"../webfonts/fa-brands-400.woff2": path.resolve(__dirname, "node_modules/@fortawesome/fontawesome-free/webfonts/fa-brands-400.woff2"),
"../webfonts/fa-brands-400.woff": path.resolve(__dirname, "node_modules/@fortawesome/fontawesome-free/webfonts/fa-brands-400.woff"),
"../webfonts/fa-brands-400.ttf": path.resolve(__dirname, "node_modules/@fortawesome/fontawesome-free/webfonts/fa-brands-400.ttf"),
"../webfonts/fa-brands-400.svg": path.resolve(__dirname, "node_modules/@fortawesome/fontawesome-free/webfonts/fa-brands-400.svg"),
"../webfonts/fa-regular-400.eot": path.resolve(__dirname, "node_modules/@fortawesome/fontawesome-free/webfonts/fa-regular-400.eot"),
"../webfonts/fa-regular-400.woff2": path.resolve(__dirname, "node_modules/@fortawesome/fontawesome-free/webfonts/fa-regular-400.woff2"),
"../webfonts/fa-regular-400.woff": path.resolve(__dirname, "node_modules/@fortawesome/fontawesome-free/webfonts/fa-regular-400.woff"),
"../webfonts/fa-regular-400.ttf": path.resolve(__dirname, "node_modules/@fortawesome/fontawesome-free/webfonts/fa-regular-400.ttf"),
"../webfonts/fa-regular-400.svg": path.resolve(__dirname, "node_modules/@fortawesome/fontawesome-free/webfonts/fa-regular-400.svg"),
"../webfonts/fa-solid-900.eot": path.resolve(__dirname, "node_modules/@fortawesome/fontawesome-free/webfonts/fa-solid-900.eot"),
"../webfonts/fa-solid-900.woff2": path.resolve(__dirname, "node_modules/@fortawesome/fontawesome-free/webfonts/fa-solid-900.woff2"),
"../webfonts/fa-solid-900.woff": path.resolve(__dirname, "node_modules/@fortawesome/fontawesome-free/webfonts/fa-solid-900.woff"),
"../webfonts/fa-solid-900.ttf": path.resolve(__dirname, "node_modules/@fortawesome/fontawesome-free/webfonts/fa-solid-900.ttf"),
"../webfonts/fa-solid-900.svg": path.resolve(__dirname, "node_modules/@fortawesome/fontawesome-free/webfonts/fa-solid-900.svg"),
}
},
}
};

View file

@ -11,7 +11,7 @@ config :core,
ecto_repos: [Core.Repo]
# Configures the endpoint
config :core, CoreWeb.Endpoint,
config :core, AppWeb.Endpoint,
url: [host: "localhost"],
secret_key_base: "kNJbLKCmuZYSK99S55+DmirA2TlmOxzs/xz3xnlXtOhQCoBMmYRabaRLTXkcsw5d",
render_errors: [view: CoreWeb.ErrorView, accepts: ~w(html json), layout: false],

View file

@ -15,20 +15,20 @@ config :core, Core.Repo,
# The watchers configuration can be used to run external
# watchers to your application. For example, we use it
# with webpack to recompile .js and .css sources.
config :core, CoreWeb.Endpoint,
http: [port: 4000],
debug_errors: true,
code_reloader: true,
check_origin: false,
watchers: [
node: [
"node_modules/webpack/bin/webpack.js",
"--mode",
"development",
"--watch-stdin",
cd: Path.expand("../assets", __DIR__)
]
]
# config :core, CoreWeb.Endpoint,
# http: [port: 4000],
# debug_errors: true,
# code_reloader: true,
# check_origin: false,
# watchers: [
# node: [
# "node_modules/webpack/bin/webpack.js",
# "--mode",
# "development",
# "--watch-stdin",
# cd: Path.expand("../assets", __DIR__)
# ]
# ]
# ## SSL Support
#

View file

@ -9,7 +9,7 @@ use Mix.Config
# manifest is generated by the `mix phx.digest` task,
# which you should run after static files are built and
# before starting your production server.
config :core, CoreWeb.Endpoint,
config :core, AppWeb.Endpoint,
url: [host: "example.com", port: 80],
cache_static_manifest: "priv/static/cache_manifest.json"

View file

@ -23,7 +23,7 @@ secret_key_base =
You can generate one by calling: mix phx.gen.secret
"""
config :core, CoreWeb.Endpoint,
config :core, AppWeb.Endpoint,
http: [
port: String.to_integer(System.get_env("PORT") || "4000"),
transport_options: [socket_opts: [:inet6]]

View file

@ -14,7 +14,7 @@ config :core, Core.Repo,
# We don't run a server during test. If one is required,
# you can enable the server option below.
config :core, CoreWeb.Endpoint,
config :core, AppWeb.Endpoint,
http: [port: 4002],
server: false

View file

@ -7,7 +7,6 @@ defmodule CoreWeb.Router do
plug :fetch_flash
plug :protect_from_forgery
plug :put_secure_browser_headers
plug :put_layout, {CoreWeb.LayoutView, :app}
end
pipeline :api do
@ -22,20 +21,4 @@ defmodule CoreWeb.Router do
live_dashboard "/dashboard", metrics: CoreWeb.Telemetry
end
end
if Mix.env == :dev do
# If using Phoenix
forward "/sent_emails", Bamboo.SentEmailViewerPlug
end
scope "/", Content do
pipe_through :browser
get "/", PostsController, :index
end
Application.get_env(:core, :router_forwards, [])
|> Enum.map(fn {router, path} ->
forward path, router
end)
end

View file

@ -49,10 +49,9 @@ defmodule CoreWeb.Helpers do
def styled_input(f, field, opts \\ [], options \\ nil, block_list \\ []) do
{content, _} = Keyword.pop(block_list, :do, "")
{type, rest_opts} = Keyword.pop(opts, :type, input_type(f, field))
{icon, rest_opts} = Keyword.pop(rest_opts, :icon, "")
{classes, rest_opts} = Keyword.pop(rest_opts, :class, default_classes_for_type(type))
{label_text, rest_opts} = Keyword.pop(rest_opts, :label)
{input_helper, rest_opts} = Keyword.pop(rest_opts, :input_helper, input_type(f, field))
{input_helper, _rest_opts} = Keyword.pop(rest_opts, :input_helper, input_type(f, field))
error_classes =
if Keyword.get_values(f.errors, field) |> Enum.any?() do
@ -112,11 +111,11 @@ defmodule CoreWeb.Helpers do
"""
end
defp do_styled_input_tag(type, input_helper, f, field, nil, opts, classes, error_classes) do
defp do_styled_input_tag(_type, input_helper, f, field, nil, opts, classes, error_classes) do
apply(Phoenix.HTML.Form, input_helper, [f, field, opts ++ [class: Enum.join([classes, error_classes], " ")]])
end
defp do_styled_input_tag(type, input_helper, f, field, options, opts, classes, error_classes) do
defp do_styled_input_tag(_type, input_helper, f, field, options, opts, classes, error_classes) do
apply(Phoenix.HTML.Form, input_helper, [f, field, options, opts ++ [class: Enum.join([classes, error_classes], " ")]])
end

View file

@ -1,39 +1,3 @@
defmodule CoreWeb.LayoutView do
use CoreWeb, :view
def title(view_module, template, assigns) do
delegate_with_default(view_module, :title, [view_module, template, assigns], I18n.t!("en", "site.title"))
end
def excerpt(view_module, template, assigns) do
delegate_with_default(view_module, :excerpt, [view_module, template, assigns], I18n.t!("en", "site.excerpt"))
end
def feed_tag(conn, view_module, view_template, assigns) do
delegate_with_default(view_module, :feed_tag, [conn, view_module, view_template, assigns], nil)
end
defp delegate_with_default(nil, _, _, default), do: default
defp delegate_with_default(view_module, function_name, args, default) do
sibling_layout = sibling_layout_view(view_module)
if function_exported?(sibling_layout, function_name, args |> Enum.count()) do
apply(sibling_layout, function_name, args)
else
default
end
end
defp sibling_layout_view(view_module) do
view_module
|> parent_module()
|> Module.concat("LayoutView")
end
defp parent_module(mod) do
[_|tail] = Module.split(mod) |> Enum.reverse()
tail
|> Enum.reverse()
|> Module.concat()
end
end

View file

@ -47,7 +47,7 @@ defmodule Core.MixProject do
{:phoenix, "~> 1.5.3"},
{:phoenix_ecto, "~> 4.1"},
{:ecto_sql, "~> 3.4"},
{:linguist, "0.3.0"},
{:linguist, "0.3.1"},
{:postgrex, ">= 0.0.0"},
{:phoenix_html, "~> 2.11"},
{:phoenix_live_reload, "~> 1.2", only: :dev},
@ -73,7 +73,7 @@ defmodule Core.MixProject do
"ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"],
"ecto.reset": ["ecto.drop", "ecto.setup"],
test: ["ecto.create --quiet", "ecto.migrate --quiet", "test"],
"npm.install": ["cmd npm install --prefix assets"],
"npm.install": [],
]
end
end

View file

@ -1,31 +1,3 @@
defmodule CoreWeb.LayoutViewTest do
use CoreWeb.ConnCase, async: true
import CoreWeb.LayoutView
describe "title/3" do
def default_title do
I18n.t! "en", "site.title"
end
test "for nil" do
assert title(nil, nil, nil) =~ default_title()
end
end
describe "excerpt/3" do
def default_excerpt do
I18n.t! "en", "site.excerpt"
end
test "for nil" do
assert excerpt(nil, nil, nil) =~ default_excerpt()
end
end
describe "feed_tag/4" do
test "for nil" do
assert feed_tag(nil, nil, nil, nil) == nil
end
end
end

View file

@ -29,7 +29,7 @@ config :app, AppWeb.Endpoint,
url: [host: "localhost"],
secret_key_base: "r2eN53mJ9RmlGz9ZQ7xf43P3Or59aaO9rdf5D3hRcsuiH44pGW9kPGfl5mt9N1Gi",
render_errors: [view: AppWeb.ErrorView, accepts: ~w(html json), layout: false],
pubsub_server: AppWeb.PubSub,
pubsub_server: App.PubSub,
live_view: [signing_salt: "g5ltUbnQ"]
# Configure Mix tasks and generators
@ -67,14 +67,7 @@ config :content, Content.Endpoint,
config :admin,
ecto_repos: [Admin.Repo]
config :core,
router_forwards: [
{Content.Router, "/pages"},
{AuthWeb.Router, "/auth"},
{Admin.Router, "/admin"},
{AppWeb.Router, "/app"},
],
email_from: "example@example.org"
config :core, email_from: "example@example.org"
config :content,
generators: [context_app: false]
@ -82,7 +75,7 @@ config :content,
config :content, Content.Endpoint, server: false
config :auth_web, AuthWeb.Endpoint, server: false
config :admin, Admin.Endpoint, server: false
config :app, AppWeb.Endpoint, server: false
config :app, CoreWeb.Endpoint, server: false
import_config "../apps/*/config/config.exs"

View file

@ -6,7 +6,7 @@ use Mix.Config
# The watchers configuration can be used to run external
# watchers to your application. For example, we use it
# with webpack to recompile .js and .css sources.
config :app, App.Endpoint,
config :app, AppWeb.Endpoint,
http: [port: 4000],
debug_errors: true,
code_reloader: true,
@ -46,7 +46,7 @@ config :app, App.Endpoint,
# different ports.
# Watch static and templates for browser reloading.
config :app, App.Endpoint,
config :app, AppWeb.Endpoint,
live_reload: [
patterns: [
~r"priv/static/.*(js|css|png|jpeg|jpg|gif|svg)$",

View file

@ -3,7 +3,7 @@
"bcrypt_elixir": {:hex, :bcrypt_elixir, "1.1.1", "6b5560e47a02196ce5f0ab3f1d8265db79a23868c137e973b27afef928ed8006", [:make, :mix], [{:elixir_make, "~> 0.4", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "10f658be786bd2daaadcd45cc5b598da01d5bbc313da4d0e3efb2d6a511d896d"},
"bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm", "7af5c7e09fe1d40f76c8e4f9dd2be7cebd83909f31fee7cd0e9eadc567da8353"},
"certifi": {:hex, :certifi, "2.5.2", "b7cfeae9d2ed395695dd8201c57a2d019c0c43ecaf8b8bcb9320b40d6662f340", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm", "3b3b5f36493004ac3455966991eaf6e768ce9884693d9968055aeeeb1e575040"},
"cldr_utils": {:hex, :cldr_utils, "2.9.1", "be714403abe1a7abed5ee4f7dd3823a9067f96ab4b0613a454177b51ca204236", [:mix], [{:decimal, "~> 1.6", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "6cba0a485f57feb773291ca1816469ddd887e22d73d9b12a1b207d82a67a4e71"},
"cldr_utils": {:hex, :cldr_utils, "2.11.0", "1822c5b246639aa106bb8aea674de183efe132f1bda23aa224000bfe6ee8e08d", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "79451992a1617f0b9c06c7ae1dd5ffcf3e6ae6a616bb14dea86f7ee142db71d0"},
"combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm", "1b1dbc1790073076580d0d1d64e42eae2366583e7aecd455d1215b0d16f2451b"},
"comeonin": {:hex, :comeonin, "4.1.2", "3eb5620fd8e35508991664b4c2b04dd41e52f1620b36957be837c1d7784b7592", [:mix], [{:argon2_elixir, "~> 1.2", [hex: :argon2_elixir, repo: "hexpm", optional: true]}, {:bcrypt_elixir, "~> 0.12.1 or ~> 1.0", [hex: :bcrypt_elixir, repo: "hexpm", optional: true]}, {:pbkdf2_elixir, "~> 0.12", [hex: :pbkdf2_elixir, repo: "hexpm", optional: true]}], "hexpm", "d8700a0ca4dbb616c22c9b3f6dd539d88deaafec3efe66869d6370c9a559b3e9"},
"connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], [], "hexpm", "4a0850c9be22a43af9920a71ab17c051f5f7d45c209e40269a1938832510e4d9"},
@ -13,10 +13,12 @@
"crontab": {:hex, :crontab, "1.1.10", "dc9bb1f4299138d47bce38341f5dcbee0aa6c205e864fba7bc847f3b5cb48241", [:mix], [{:ecto, "~> 1.0 or ~> 2.0 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm", "1347d889d1a0eda997990876b4894359e34bfbbd688acbb0ba28a2795ca40685"},
"db_connection": {:hex, :db_connection, "2.2.2", "3bbca41b199e1598245b716248964926303b5d4609ff065125ce98bcd368939e", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}], "hexpm", "642af240d8a8affb93b4ba5a6fcd2bbcbdc327e1a524b825d383711536f8070c"},
"decimal": {:hex, :decimal, "1.9.0", "83e8daf59631d632b171faabafb4a9f4242c514b0a06ba3df493951c08f64d07", [:mix], [], "hexpm", "b1f2343568eed6928f3e751cf2dffde95bfaa19dd95d09e8a9ea92ccfd6f7d85"},
"dialyxir": {:hex, :dialyxir, "1.0.0", "6a1fa629f7881a9f5aaf3a78f094b2a51a0357c843871b8bc98824e7342d00a5", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "aeb06588145fac14ca08d8061a142d52753dbc2cf7f0d00fc1013f53f8654654"},
"earmark": {:hex, :earmark, "1.4.3", "364ca2e9710f6bff494117dbbd53880d84bebb692dafc3a78eb50aa3183f2bfd", [:mix], [], "hexpm", "8cf8a291ebf1c7b9539e3cddb19e9cef066c2441b1640f13c34c1d3cfc825fec"},
"ecto": {:hex, :ecto, "3.4.6", "08f7afad3257d6eb8613309af31037e16c36808dfda5a3cd0cb4e9738db030e4", [: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", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "6f13a9e2a62e75c2dcfc7207bfc65645ab387af8360db4c89fee8b5a4bf3f70b"},
"ecto_sql": {:hex, :ecto_sql, "3.4.4", "d28bac2d420f708993baed522054870086fd45016a9d09bb2cd521b9c48d32ea", [:mix], [{:db_connection, "~> 2.2", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.4.3", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.3.0 or ~> 0.4.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.0", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "edb49af715dd72f213b66adfd0f668a43c17ed510b5d9ac7528569b23af57fe8"},
"elixir_make": {:hex, :elixir_make, "0.6.0", "38349f3e29aff4864352084fc736fa7fa0f2995a819a737554f7ebd28b85aaab", [:mix], [], "hexpm", "d522695b93b7f0b4c0fcb2dfe73a6b905b1c301226a5a55cb42e5b14d509e050"},
"erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"},
"ex_cldr": {:hex, :ex_cldr, "2.13.0", "742f14a4afcfea61a190d603d8e555d2c91d71e4e8fc2520d5dc35616969e225", [:mix], [{:cldr_utils, "~> 2.3", [hex: :cldr_utils, repo: "hexpm", optional: false]}, {:decimal, "~> 1.6", [hex: :decimal, repo: "hexpm", optional: false]}, {:gettext, "~> 0.13", [hex: :gettext, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:plug, "~> 1.4", [hex: :plug, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "5e4cf3e945ee60156a3342e2a762f69036ffbe1f80520cc88592d68f12c5db55"},
"ex_prompt": {:hex, :ex_prompt, "0.1.5", "b136642d0962f8ea37b3c9fa185ad1f42c71c3b9c6c3950f0358d7f3d2db2970", [:mix], [], "hexpm", "ad19a404708c9c7b05d36090b2d074ceafbed248a8de1a22d45a05ebe6994b83"},
"excoveralls": {:hex, :excoveralls, "0.13.0", "4e1b7cc4e0351d8d16e9be21b0345a7e165798ee5319c7800b9138ce17e0b38e", [:mix], [{:hackney, "~> 1.16", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "fe2a56c8909564e2e6764765878d7d5e141f2af3bc8ff3b018a68ee2a218fced"},
@ -24,7 +26,7 @@
"floki": {:hex, :floki, "0.25.0", "b1c9ddf5f32a3a90b43b76f3386ca054325dc2478af020e87b5111c19f2284ac", [:mix], [{:html_entities, "~> 0.5.0", [hex: :html_entities, repo: "hexpm", optional: false]}], "hexpm", "631f4e627c46d5ecd347df5a2accdaf0621c77c3693c5b75a8ad58e84c61f242"},
"gen_stage": {:hex, :gen_stage, "1.0.0", "51c8ae56ff54f9a2a604ca583798c210ad245f415115453b773b621c49776df5", [:mix], [], "hexpm", "1d9fc978db5305ac54e6f5fec7adf80cd893b1000cf78271564c516aa2af7706"},
"gen_state_machine": {:hex, :gen_state_machine, "2.1.0", "a38b0e53fad812d29ec149f0d354da5d1bc0d7222c3711f3a0bd5aa608b42992", [:mix], [], "hexpm", "ae367038808db25cee2f2c4b8d0531522ea587c4995eb6f96ee73410a60fa06b"},
"gettext": {:hex, :gettext, "0.18.0", "406d6b9e0e3278162c2ae1de0a60270452c553536772167e2d701f028116f870", [:mix], [], "hexpm", "c3f850be6367ebe1a08616c2158affe4a23231c70391050bf359d5f92f66a571"},
"gettext": {:hex, :gettext, "0.18.2", "7df3ea191bb56c0309c00a783334b288d08a879f53a7014341284635850a6e55", [:mix], [], "hexpm", "f9f537b13d4fdd30f3039d33cb80144c3aa1f8d9698e47d7bcbcc8df93b1f5c5"},
"hackney": {:hex, :hackney, "1.16.0", "5096ac8e823e3a441477b2d187e30dd3fff1a82991a806b2003845ce72ce2d84", [:rebar3], [{:certifi, "2.5.2", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.1", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.3.0", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.6", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm", "3bf0bebbd5d3092a3543b783bf065165fa5d3ad4b899b836810e513064134e18"},
"html_entities": {:hex, :html_entities, "0.5.1", "1c9715058b42c35a2ab65edc5b36d0ea66dd083767bef6e3edb57870ef556549", [:mix], [], "hexpm", "30efab070904eb897ff05cd52fa61c1025d7f8ef3a9ca250bc4e6513d16c32de"},
"html_sanitize_ex": {:hex, :html_sanitize_ex, "1.0.1", "2572e7122c78ab7e57b613e7c7f5e42bf9b3c25e430e32f23f1413d86db8a0af", [:mix], [{:mochiweb, "~> 2.12.2", [hex: :mochiweb, repo: "hexpm", optional: false]}], "hexpm", "c334e2835e094fb9c04658bd4cfc7533fa51a8f56f11343c57ab9cb2a01d8613"},
@ -32,7 +34,7 @@
"jason": {:hex, :jason, "1.2.2", "ba43e3f2709fd1aa1dce90aaabfd039d000469c05c56f0b8e31978e03fa39052", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "18a228f5f0058ee183f29f9eae0805c6e59d61c3b006760668d8d18ff0d12179"},
"kaffy": {:hex, :kaffy, "0.9.0", "bef34c9729f6a3af4d0dea8eede8bcb9e11371a83ac9a8b393991bce81839517", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.11", [hex: :phoenix_html, repo: "hexpm", optional: false]}], "hexpm", "d18ff57b8e68feb433aed11e71510cd357abc7034e75358af5deff7d0d4c6ed3"},
"libring": {:hex, :libring, "1.5.0", "44313eb6862f5c9168594a061e9d5f556a9819da7c6444706a9e2da533396d70", [:mix], [], "hexpm", "04e843d4fdcff49a62d8e03778d17c6cb2a03fe2d14020d3825a1761b55bd6cc"},
"linguist": {:hex, :linguist, "0.3.0", "2984dfce6720d1212ddd7bba82496f92627a39aecd4d32c7016ec00393e1f925", [:mix], [{:ex_cldr, "~> 2.0", [hex: :ex_cldr, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:yaml_elixir, "~> 2.0", [hex: :yaml_elixir, repo: "hexpm", optional: false]}], "hexpm", "1923876545db22b63334c9d203ef56397a2946daa018117767b068f856be41e4"},
"linguist": {:hex, :linguist, "0.3.1", "8ce81114691be8ef4a122e7f57bd1842bc96b1f5650b66b246d7035238cab69d", [:mix], [{:ex_cldr, "~> 2.0", [hex: :ex_cldr, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:yaml_elixir, "~> 2.0", [hex: :yaml_elixir, repo: "hexpm", optional: false]}], "hexpm", "5b06f97912e298f60dd00bc6a588b1fe1ec8838b268e4fdbf4443a25511f0614"},
"meck": {:hex, :meck, "0.8.13", "ffedb39f99b0b99703b8601c6f17c7f76313ee12de6b646e671e3188401f7866", [:rebar3], [], "hexpm", "d34f013c156db51ad57cc556891b9720e6a1c1df5fe2e15af999c84d6cebeb1a"},
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"},
"mime": {:hex, :mime, "1.4.0", "5066f14944b470286146047d2f73518cf5cca82f8e4815cf35d196b58cf07c47", [:mix], [], "hexpm", "75fa42c4228ea9a23f70f123c74ba7cece6a03b1fd474fe13f6a7a85c6ea4ff6"},
@ -69,5 +71,5 @@
"unicode_util_compat": {:hex, :unicode_util_compat, "0.5.0", "8516502659002cec19e244ebd90d312183064be95025a319a6c7e89f4bccd65b", [:rebar3], [], "hexpm", "d48d002e15f5cc105a696cf2f1bbb3fc72b4b770a184d8420c8db20da2674b38"},
"xml_builder": {:hex, :xml_builder, "2.1.2", "90cb9ad382958934c78c6ddfbe6d385a8ce147d84b61cbfa83ec93a169d0feab", [:mix], [], "hexpm", "b89046041da2fbc1d51d31493ba31b9d5fc6223c93384bf513a1a9e1df9ec081"},
"yamerl": {:hex, :yamerl, "0.8.0", "8214cfe16bbabe5d1d6c14a14aea11c784b9a21903dd6a7c74f8ce180adae5c7", [:rebar3], [], "hexpm", "010634477bf9c208a0767dcca89116c2442cf0b5e87f9c870f85cd1c3e0c2aab"},
"yaml_elixir": {:hex, :yaml_elixir, "2.4.0", "2f444abc3c994c902851fde56b6a9cb82895c291c05a0490a289035c2e62ae71", [:mix], [{:yamerl, "~> 0.7", [hex: :yamerl, repo: "hexpm", optional: false]}], "hexpm", "4e25a6d5c873e393689c6f1062c5ec90f6cd1be2527b073178ae37eae4c78bee"},
"yaml_elixir": {:hex, :yaml_elixir, "2.5.0", "45de762be6d75fa5a8b5f44ddff8c30f64c26526eab5b1d72e36d616007b7796", [:mix], [{:yamerl, "~> 0.7", [hex: :yamerl, repo: "hexpm", optional: false]}], "hexpm", "80fe4e43f05582f2a90f2dcd73fc6171fbd65f2e6836f71fe4ce2154ef358c36"},
}