feat: Upgrade framework
This commit is contained in:
		
						commit
						42984b8c29
					
				
					 49 changed files with 27425 additions and 11583 deletions
				
			
		
							
								
								
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| 
						 | 
					@ -34,8 +34,8 @@ node_modules
 | 
				
			||||||
/priv/static/
 | 
					/priv/static/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Mnesia DBs
 | 
					# Mnesia DBs
 | 
				
			||||||
/apps/*/priv/mnesia/
 | 
					/apps/*/priv/mnesia*
 | 
				
			||||||
/priv/mnesia/
 | 
					/priv/mnesia*
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Lock file for Brew, since the versions aren't really stable & isolated anyway
 | 
					# Lock file for Brew, since the versions aren't really stable & isolated anyway
 | 
				
			||||||
Brewfile.lock.json
 | 
					Brewfile.lock.json
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -18,6 +18,7 @@ variables:
 | 
				
			||||||
.test_template: &test_template
 | 
					.test_template: &test_template
 | 
				
			||||||
  stage: test
 | 
					  stage: test
 | 
				
			||||||
  cache:
 | 
					  cache:
 | 
				
			||||||
 | 
					    key: $CI_JOB_NAME
 | 
				
			||||||
    paths:
 | 
					    paths:
 | 
				
			||||||
      - _build/
 | 
					      - _build/
 | 
				
			||||||
      - deps/
 | 
					      - deps/
 | 
				
			||||||
| 
						 | 
					@ -42,10 +43,6 @@ build_image_for_commit:
 | 
				
			||||||
  image: "docker:20.10"
 | 
					  image: "docker:20.10"
 | 
				
			||||||
  only:
 | 
					  only:
 | 
				
			||||||
    - master
 | 
					    - master
 | 
				
			||||||
  cache:
 | 
					 | 
				
			||||||
    paths:
 | 
					 | 
				
			||||||
      - _build/
 | 
					 | 
				
			||||||
      - deps/
 | 
					 | 
				
			||||||
  services:
 | 
					  services:
 | 
				
			||||||
    - name: docker:20.10-dind
 | 
					    - name: docker:20.10-dind
 | 
				
			||||||
  before_script:
 | 
					  before_script:
 | 
				
			||||||
| 
						 | 
					@ -58,13 +55,14 @@ build_image_for_commit:
 | 
				
			||||||
deploy_to_tags:
 | 
					deploy_to_tags:
 | 
				
			||||||
  stage: deploy_tags
 | 
					  stage: deploy_tags
 | 
				
			||||||
  needs: ['test', 'build_image_for_commit']
 | 
					  needs: ['test', 'build_image_for_commit']
 | 
				
			||||||
  image: "node:15.0"
 | 
					  image: "node:16"
 | 
				
			||||||
  only:
 | 
					  only:
 | 
				
			||||||
    - master
 | 
					    - master
 | 
				
			||||||
  cache:
 | 
					  cache:
 | 
				
			||||||
    key:
 | 
					    key:
 | 
				
			||||||
      files:
 | 
					      files:
 | 
				
			||||||
        - package-lock.json
 | 
					        - package-lock.json
 | 
				
			||||||
 | 
					        - package.json
 | 
				
			||||||
    paths:
 | 
					    paths:
 | 
				
			||||||
      - node_modules/
 | 
					      - node_modules/
 | 
				
			||||||
  script:
 | 
					  script:
 | 
				
			||||||
| 
						 | 
					@ -72,7 +70,10 @@ deploy_to_tags:
 | 
				
			||||||
    - export GIT_AUTHOR_EMAIL=$GITLAB_USER_EMAIL
 | 
					    - export GIT_AUTHOR_EMAIL=$GITLAB_USER_EMAIL
 | 
				
			||||||
    - export GIT_COMMITTER_NAME=$GITLAB_USER_NAME
 | 
					    - export GIT_COMMITTER_NAME=$GITLAB_USER_NAME
 | 
				
			||||||
    - export GIT_COMMITTER_EMAIL=$GITLAB_USER_EMAIL
 | 
					    - export GIT_COMMITTER_EMAIL=$GITLAB_USER_EMAIL
 | 
				
			||||||
    - npm install
 | 
					    - export GIT_BRANCH=$CI_COMMIT_REF_NAME
 | 
				
			||||||
 | 
					    - git config user.email $GITLAB_USER_EMAIL
 | 
				
			||||||
 | 
					    - git config user.name $GITLAB_USER_NAME
 | 
				
			||||||
 | 
					    - npm install --no-save
 | 
				
			||||||
    - npx semantic-release --repository-url=$CI_REPOSITORY_URL
 | 
					    - npx semantic-release --repository-url=$CI_REPOSITORY_URL
 | 
				
			||||||
    - script/generate-build-version
 | 
					    - script/generate-build-version
 | 
				
			||||||
  artifacts:
 | 
					  artifacts:
 | 
				
			||||||
| 
						 | 
					@ -107,3 +108,83 @@ deploy_commit_image_to_tag:
 | 
				
			||||||
    - docker tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA$IMAGE_TYPE $CI_REGISTRY_IMAGE:latest$IMAGE_TYPE
 | 
					    - docker tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA$IMAGE_TYPE $CI_REGISTRY_IMAGE:latest$IMAGE_TYPE
 | 
				
			||||||
    - docker push $CI_REGISTRY_IMAGE:$BUILD_VERSION$IMAGE_TYPE
 | 
					    - docker push $CI_REGISTRY_IMAGE:$BUILD_VERSION$IMAGE_TYPE
 | 
				
			||||||
    - docker push $CI_REGISTRY_IMAGE:latest$IMAGE_TYPE
 | 
					    - docker push $CI_REGISTRY_IMAGE:latest$IMAGE_TYPE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.dependabot_gitlab: &dependabot_gitlab
 | 
				
			||||||
 | 
					  image:
 | 
				
			||||||
 | 
					    name: docker.io/andrcuns/dependabot-gitlab:0.4.3
 | 
				
			||||||
 | 
					    entrypoint: [""]
 | 
				
			||||||
 | 
					  variables:
 | 
				
			||||||
 | 
					    GIT_STRATEGY: none
 | 
				
			||||||
 | 
					    RAILS_ENV: production
 | 
				
			||||||
 | 
					    SETTINGS__STANDALONE: "true"
 | 
				
			||||||
 | 
					    SETTINGS__GITLAB_URL: $CI_SERVER_URL
 | 
				
			||||||
 | 
					    SETTINGS__GITLAB_ACCESS_TOKEN: $GITLAB_TOKEN
 | 
				
			||||||
 | 
					  before_script:
 | 
				
			||||||
 | 
					    - cd /home/dependabot/app
 | 
				
			||||||
 | 
					  script:
 | 
				
			||||||
 | 
					    - bundle exec rake "dependabot:update[$CI_PROJECT_PATH,$PACKAGE_MANAGER,$DIRECTORY]"
 | 
				
			||||||
 | 
					  only:
 | 
				
			||||||
 | 
					    - schedules
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					npm-release-tools:
 | 
				
			||||||
 | 
					  extends: .dependabot_gitlab
 | 
				
			||||||
 | 
					  variables:
 | 
				
			||||||
 | 
					    DIRECTORY: "/"
 | 
				
			||||||
 | 
					    PACKAGE_MANAGER: npm
 | 
				
			||||||
 | 
					  only:
 | 
				
			||||||
 | 
					    variables:
 | 
				
			||||||
 | 
					      - $PACKAGE_MANAGER_SET =~ /(\bnpm|yarn\b)/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					npm-assets:
 | 
				
			||||||
 | 
					  extends: .dependabot_gitlab
 | 
				
			||||||
 | 
					  variables:
 | 
				
			||||||
 | 
					    DIRECTORY: "/apps/app/assets"
 | 
				
			||||||
 | 
					    PACKAGE_MANAGER: npm
 | 
				
			||||||
 | 
					  only:
 | 
				
			||||||
 | 
					    variables:
 | 
				
			||||||
 | 
					      - $PACKAGE_MANAGER_SET =~ /(\bnpm|yarn\b)/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					mix-admin:
 | 
				
			||||||
 | 
					  extends: .dependabot_gitlab
 | 
				
			||||||
 | 
					  variables:
 | 
				
			||||||
 | 
					    DIRECTORY: "/apps/admin"
 | 
				
			||||||
 | 
					    PACKAGE_MANAGER: mix
 | 
				
			||||||
 | 
					  only:
 | 
				
			||||||
 | 
					    variables:
 | 
				
			||||||
 | 
					      - $PACKAGE_MANAGER_SET =~ /\bmix\b/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					mix-app:
 | 
				
			||||||
 | 
					  extends: .dependabot_gitlab
 | 
				
			||||||
 | 
					  variables:
 | 
				
			||||||
 | 
					    DIRECTORY: "/apps/app"
 | 
				
			||||||
 | 
					    PACKAGE_MANAGER: mix
 | 
				
			||||||
 | 
					  only:
 | 
				
			||||||
 | 
					    variables:
 | 
				
			||||||
 | 
					      - $PACKAGE_MANAGER_SET =~ /\bmix\b/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					mix-core:
 | 
				
			||||||
 | 
					  extends: .dependabot_gitlab
 | 
				
			||||||
 | 
					  variables:
 | 
				
			||||||
 | 
					    DIRECTORY: "/apps/core"
 | 
				
			||||||
 | 
					    PACKAGE_MANAGER: mix
 | 
				
			||||||
 | 
					  only:
 | 
				
			||||||
 | 
					    variables:
 | 
				
			||||||
 | 
					      - $PACKAGE_MANAGER_SET =~ /\bmix\b/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					mix-content:
 | 
				
			||||||
 | 
					  extends: .dependabot_gitlab
 | 
				
			||||||
 | 
					  variables:
 | 
				
			||||||
 | 
					    DIRECTORY: "/apps/content"
 | 
				
			||||||
 | 
					    PACKAGE_MANAGER: mix
 | 
				
			||||||
 | 
					  only:
 | 
				
			||||||
 | 
					    variables:
 | 
				
			||||||
 | 
					      - $PACKAGE_MANAGER_SET =~ /\bmix\b/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					docker:
 | 
				
			||||||
 | 
					  extends: .dependabot_gitlab
 | 
				
			||||||
 | 
					  variables:
 | 
				
			||||||
 | 
					    DIRECTORY: "/"
 | 
				
			||||||
 | 
					    PACKAGE_MANAGER: docker
 | 
				
			||||||
 | 
					  only:
 | 
				
			||||||
 | 
					    variables:
 | 
				
			||||||
 | 
					      - $PACKAGE_MANAGER_SET =~ /\bdocker\b/
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										30
									
								
								.gitlab/dependabot.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								.gitlab/dependabot.yml
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,30 @@
 | 
				
			||||||
 | 
					version: 2
 | 
				
			||||||
 | 
					updates:
 | 
				
			||||||
 | 
					- package-ecosystem: docker
 | 
				
			||||||
 | 
					  directory: "/"
 | 
				
			||||||
 | 
					  schedule:
 | 
				
			||||||
 | 
					      interval: "daily"
 | 
				
			||||||
 | 
					- package-ecosystem: mix
 | 
				
			||||||
 | 
					  directory: "/apps/admin"
 | 
				
			||||||
 | 
					  schedule:
 | 
				
			||||||
 | 
					      interval: "daily"
 | 
				
			||||||
 | 
					- package-ecosystem: mix
 | 
				
			||||||
 | 
					  directory: "/apps/app"
 | 
				
			||||||
 | 
					  schedule:
 | 
				
			||||||
 | 
					      interval: "daily"
 | 
				
			||||||
 | 
					- package-ecosystem: mix
 | 
				
			||||||
 | 
					  directory: "/apps/content"
 | 
				
			||||||
 | 
					  schedule:
 | 
				
			||||||
 | 
					      interval: "daily"
 | 
				
			||||||
 | 
					- package-ecosystem: mix
 | 
				
			||||||
 | 
					  directory: "/apps/core"
 | 
				
			||||||
 | 
					  schedule:
 | 
				
			||||||
 | 
					      interval: "daily"
 | 
				
			||||||
 | 
					- package-ecosystem: npm
 | 
				
			||||||
 | 
					  directory: "/"
 | 
				
			||||||
 | 
					  schedule:
 | 
				
			||||||
 | 
					      interval: "daily"
 | 
				
			||||||
 | 
					- package-ecosystem: npm
 | 
				
			||||||
 | 
					  directory: "/apps/app/assets"
 | 
				
			||||||
 | 
					  schedule:
 | 
				
			||||||
 | 
					      interval: "daily"
 | 
				
			||||||
| 
						 | 
					@ -1,3 +1,3 @@
 | 
				
			||||||
erlang 23.0.2
 | 
					erlang 23.0.2
 | 
				
			||||||
elixir 1.10.3-otp-23
 | 
					elixir 1.12.1
 | 
				
			||||||
nodejs 14.5.0
 | 
					nodejs 14.5.0
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
FROM elixir:1.10.4-alpine AS elixir-builder
 | 
					FROM elixir:1.12.1-alpine AS elixir-builder
 | 
				
			||||||
 | 
					
 | 
				
			||||||
RUN mix local.hex --force \
 | 
					RUN mix local.hex --force \
 | 
				
			||||||
  && mix local.rebar --force
 | 
					  && mix local.rebar --force
 | 
				
			||||||
| 
						 | 
					@ -27,7 +27,7 @@ RUN mix deps.get
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Leave off here so that we can built assets and compile the elixir app in parallel
 | 
					# Leave off here so that we can built assets and compile the elixir app in parallel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
FROM node:15.0 AS asset-builder
 | 
					FROM node:16.3.0 AS asset-builder
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Build assets in a node container
 | 
					# Build assets in a node container
 | 
				
			||||||
ADD ./apps/app/assets/ /root/app/apps/app/assets/
 | 
					ADD ./apps/app/assets/ /root/app/apps/app/assets/
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,7 @@
 | 
				
			||||||
defmodule Legendary.Admin.MixProject do
 | 
					defmodule Legendary.Admin.MixProject do
 | 
				
			||||||
  use Mix.Project
 | 
					  use Mix.Project
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @version "2.10.0"
 | 
					  @version "2.11.2"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def project do
 | 
					  def project do
 | 
				
			||||||
    [
 | 
					    [
 | 
				
			||||||
| 
						 | 
					@ -50,7 +50,7 @@ defmodule Legendary.Admin.MixProject do
 | 
				
			||||||
      {:phoenix_ecto, "~> 4.0"},
 | 
					      {:phoenix_ecto, "~> 4.0"},
 | 
				
			||||||
      {:phoenix_html, "~> 2.11"},
 | 
					      {:phoenix_html, "~> 2.11"},
 | 
				
			||||||
      {:phoenix_live_reload, "~> 1.2", only: :dev},
 | 
					      {:phoenix_live_reload, "~> 1.2", only: :dev},
 | 
				
			||||||
      {:phoenix_live_dashboard, "~> 0.2.0"},
 | 
					      {:phoenix_live_dashboard, "~> 0.4.0"},
 | 
				
			||||||
      {:postgrex, ">= 0.0.0"},
 | 
					      {:postgrex, ">= 0.0.0"},
 | 
				
			||||||
      {:telemetry_metrics, "~> 0.4"},
 | 
					      {:telemetry_metrics, "~> 0.4"},
 | 
				
			||||||
      {:telemetry_poller, "~> 0.4"},
 | 
					      {:telemetry_poller, "~> 0.4"},
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,6 +14,7 @@ import "../css/app.css"
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
import "phoenix_html"
 | 
					import "phoenix_html"
 | 
				
			||||||
import "alpinejs"
 | 
					import "alpinejs"
 | 
				
			||||||
 | 
					import "./live"
 | 
				
			||||||
import { ready } from "./utils"
 | 
					import { ready } from "./utils"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function togglePasswordFieldVisibility()
 | 
					function togglePasswordFieldVisibility()
 | 
				
			||||||
| 
						 | 
					@ -38,7 +39,7 @@ const toggleSidebar = (event) => {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ready(() => {
 | 
					ready(() => {
 | 
				
			||||||
  document.getElementById('nav-toggle').onclick = function(){
 | 
					  (document.getElementById('nav-toggle') ||{}).onclick = function(){
 | 
				
			||||||
    document.getElementById("nav-content").classList.toggle("hidden");
 | 
					    document.getElementById("nav-content").classList.toggle("hidden");
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										23
									
								
								apps/app/assets/js/live.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								apps/app/assets/js/live.js
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,23 @@
 | 
				
			||||||
 | 
					import {Socket} from "phoenix"
 | 
				
			||||||
 | 
					import LiveSocket from "phoenix_live_view"
 | 
				
			||||||
 | 
					import topbar from "topbar"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					let liveSocket = new LiveSocket("/live", Socket, {params: {_csrf_token: csrfToken}})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Show progress bar on live navigation and form submits
 | 
				
			||||||
 | 
					topbar.config({barColors: {0: "#3B82F6"}, shadowColor: "rgba(0, 0, 0, .3)"})
 | 
				
			||||||
 | 
					window.addEventListener("phx:page-loading-start", info => topbar.show())
 | 
				
			||||||
 | 
					window.addEventListener("phx:page-loading-stop", info => topbar.hide())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Connect if there are any LiveViews on the page
 | 
				
			||||||
 | 
					liveSocket.connect()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Expose liveSocket on window for web console debug logs and latency simulation:
 | 
				
			||||||
 | 
					// >> liveSocket.enableDebug()
 | 
				
			||||||
 | 
					// >> liveSocket.enableLatencySim(1000)
 | 
				
			||||||
 | 
					// The latency simulator is enabled for the duration of the browser session.
 | 
				
			||||||
 | 
					// Call disableLatencySim() to disable:
 | 
				
			||||||
 | 
					// >> liveSocket.disableLatencySim()
 | 
				
			||||||
 | 
					window.liveSocket = liveSocket
 | 
				
			||||||
							
								
								
									
										23988
									
								
								apps/app/assets/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										23988
									
								
								apps/app/assets/package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							| 
						 | 
					@ -13,38 +13,38 @@
 | 
				
			||||||
    "autoprefixer": "^9.8.6",
 | 
					    "autoprefixer": "^9.8.6",
 | 
				
			||||||
    "csswring": "^7.0.0",
 | 
					    "csswring": "^7.0.0",
 | 
				
			||||||
    "glob": "^7.1.6",
 | 
					    "glob": "^7.1.6",
 | 
				
			||||||
    "gulp": "^4.0.2",
 | 
					 | 
				
			||||||
    "phoenix": "file:/../../../deps/phoenix",
 | 
					    "phoenix": "file:/../../../deps/phoenix",
 | 
				
			||||||
    "phoenix_html": "file:/../../../deps/phoenix_html",
 | 
					    "phoenix_html": "file:/../../../deps/phoenix_html",
 | 
				
			||||||
 | 
					    "phoenix_live_view": "file:../../../deps/phoenix_live_view",
 | 
				
			||||||
    "postcss-color-function": "^4.1.0",
 | 
					    "postcss-color-function": "^4.1.0",
 | 
				
			||||||
    "simplemde": "^1.11.2",
 | 
					    "simplemde": "^1.11.2",
 | 
				
			||||||
    "stylelint": "^13.6.1",
 | 
					    "tailwindcss": "^1.7.3",
 | 
				
			||||||
    "tailwindcss": "^1.7.3"
 | 
					    "topbar": "^1.0.1"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "devDependencies": {
 | 
					  "devDependencies": {
 | 
				
			||||||
    "@babel/core": "^7.0.0",
 | 
					    "@babel/core": "^7.0.0",
 | 
				
			||||||
    "@babel/preset-env": "^7.0.0",
 | 
					    "@babel/preset-env": "^7.0.0",
 | 
				
			||||||
    "babel-loader": "^8.0.0",
 | 
					    "babel-loader": "^8.0.0",
 | 
				
			||||||
    "copy-webpack-plugin": "^5.1.1",
 | 
					    "copy-webpack-plugin": "^9.0.1",
 | 
				
			||||||
    "css-loader": "^3.4.2",
 | 
					    "css-loader": "^3.4.2",
 | 
				
			||||||
    "extract-text-webpack-plugin": "^3.0.2",
 | 
					    "css-minimizer-webpack-plugin": "^3.0.2",
 | 
				
			||||||
    "file-loader": "^6.0.0",
 | 
					    "file-loader": "^6.0.0",
 | 
				
			||||||
    "image-webpack-loader": "^6.0.0",
 | 
					    "image-webpack-loader": "^6.0.0",
 | 
				
			||||||
    "less": "^3.11.3",
 | 
					    "less": "^3.11.3",
 | 
				
			||||||
    "less-loader": "^6.2.0",
 | 
					    "less-loader": "^6.2.0",
 | 
				
			||||||
    "mini-css-extract-plugin": "^0.9.0",
 | 
					    "mini-css-extract-plugin": "^1.6.2",
 | 
				
			||||||
    "node-sass": "^4.13.1",
 | 
					 | 
				
			||||||
    "optimize-css-assets-webpack-plugin": "^5.0.1",
 | 
					 | 
				
			||||||
    "postcss-css-variables": "^0.17.0",
 | 
					    "postcss-css-variables": "^0.17.0",
 | 
				
			||||||
    "postcss-import": "^12.0.1",
 | 
					    "postcss-import": "^12.0.1",
 | 
				
			||||||
    "postcss-loader": "^3.0.0",
 | 
					    "postcss-loader": "^6.1.0",
 | 
				
			||||||
 | 
					    "sass": "^1.35.1",
 | 
				
			||||||
    "sass-loader": "^8.0.2",
 | 
					    "sass-loader": "^8.0.2",
 | 
				
			||||||
    "style-loader": "^1.2.1",
 | 
					    "style-loader": "^1.2.1",
 | 
				
			||||||
    "stylelint-config-standard": "^20.0.0",
 | 
					    "stylelint": "^13.13.1",
 | 
				
			||||||
 | 
					    "stylelint-config-standard": "^22.0.0",
 | 
				
			||||||
    "stylelint-order": "^4.1.0",
 | 
					    "stylelint-order": "^4.1.0",
 | 
				
			||||||
    "terser-webpack-plugin": "^2.3.2",
 | 
					    "terser-webpack-plugin": "^2.3.2",
 | 
				
			||||||
    "webpack": "4.41.5",
 | 
					    "webpack": "^5.1.0",
 | 
				
			||||||
    "webpack-cli": "^3.3.2"
 | 
					    "webpack-cli": "^4.7.2"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "resolutions": {
 | 
					  "resolutions": {
 | 
				
			||||||
    "graceful-fs": "4.2.3"
 | 
					    "graceful-fs": "4.2.3"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,8 +1,19 @@
 | 
				
			||||||
module.exports = {
 | 
					module.exports = {
 | 
				
			||||||
  future: {
 | 
					  future: {
 | 
				
			||||||
    removeDeprecatedGapUtilities: true,
 | 
					    removeDeprecatedGapUtilities: true,
 | 
				
			||||||
 | 
					    purgeLayersByDefault: true,
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  purge: {
 | 
				
			||||||
 | 
					    enabled: true,
 | 
				
			||||||
 | 
					    layers: ['base', 'components', 'utilities'],
 | 
				
			||||||
 | 
					    content: [
 | 
				
			||||||
 | 
					      '../../../**/views/*.ex',
 | 
				
			||||||
 | 
					      '../../../**/*.html.eex',
 | 
				
			||||||
 | 
					      '../../../**/*.html.leex',
 | 
				
			||||||
 | 
					      '../../../**/*.html.heex',
 | 
				
			||||||
 | 
					      './js/**/*.js'
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  purge: [],
 | 
					 | 
				
			||||||
  theme: {
 | 
					  theme: {
 | 
				
			||||||
    extend: {},
 | 
					    extend: {},
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,7 +2,7 @@ const path = require('path');
 | 
				
			||||||
const glob = require('glob');
 | 
					const glob = require('glob');
 | 
				
			||||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
 | 
					const MiniCssExtractPlugin = require('mini-css-extract-plugin');
 | 
				
			||||||
const TerserPlugin = require('terser-webpack-plugin');
 | 
					const TerserPlugin = require('terser-webpack-plugin');
 | 
				
			||||||
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
 | 
					const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
 | 
				
			||||||
const CopyWebpackPlugin = require('copy-webpack-plugin');
 | 
					const CopyWebpackPlugin = require('copy-webpack-plugin');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const nodeModulesPath = path.resolve(__dirname, 'node_modules')
 | 
					const nodeModulesPath = path.resolve(__dirname, 'node_modules')
 | 
				
			||||||
| 
						 | 
					@ -14,7 +14,7 @@ module.exports = (env, options) => {
 | 
				
			||||||
    optimization: {
 | 
					    optimization: {
 | 
				
			||||||
      minimizer: [
 | 
					      minimizer: [
 | 
				
			||||||
        new TerserPlugin({ cache: true, parallel: true, sourceMap: devMode }),
 | 
					        new TerserPlugin({ cache: true, parallel: true, sourceMap: devMode }),
 | 
				
			||||||
        new OptimizeCSSAssetsPlugin({})
 | 
					        new CssMinimizerPlugin(),
 | 
				
			||||||
      ]
 | 
					      ]
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    mode: options.mode,
 | 
					    mode: options.mode,
 | 
				
			||||||
| 
						 | 
					@ -63,7 +63,7 @@ module.exports = (env, options) => {
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
          test: /\.css$/,
 | 
					          test: /\.css$/,
 | 
				
			||||||
          use: [
 | 
					          use: [
 | 
				
			||||||
            {loader: MiniCssExtractPlugin.loader, options: {sourceMap: true}},
 | 
					            {loader: MiniCssExtractPlugin.loader},
 | 
				
			||||||
            {loader: 'css-loader', options: {sourceMap: true}},
 | 
					            {loader: 'css-loader', options: {sourceMap: true}},
 | 
				
			||||||
            {loader: 'postcss-loader', options: {sourceMap: true}},
 | 
					            {loader: 'postcss-loader', options: {sourceMap: true}},
 | 
				
			||||||
          ],
 | 
					          ],
 | 
				
			||||||
| 
						 | 
					@ -75,12 +75,12 @@ module.exports = (env, options) => {
 | 
				
			||||||
        filename: 'css/[name].css',
 | 
					        filename: 'css/[name].css',
 | 
				
			||||||
        chunkFilename: '[id].css',
 | 
					        chunkFilename: '[id].css',
 | 
				
			||||||
      }),
 | 
					      }),
 | 
				
			||||||
      new CopyWebpackPlugin([
 | 
					      new CopyWebpackPlugin({patterns: [
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
          from: path.resolve(__dirname, 'static'),
 | 
					          from: path.resolve(__dirname, 'static'),
 | 
				
			||||||
          to: path.resolve(__dirname, '../priv/static'),
 | 
					          to: path.resolve(__dirname, '../priv/static'),
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
      ]),
 | 
					      ]}),
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
    resolve: {
 | 
					    resolve: {
 | 
				
			||||||
      alias: {
 | 
					      alias: {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -27,6 +27,27 @@ defmodule AppWeb do
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def live_view do
 | 
				
			||||||
 | 
					    quote do
 | 
				
			||||||
 | 
					      use Phoenix.LiveView,
 | 
				
			||||||
 | 
					        layout: {AppWeb.LayoutView, "live.html"}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      import AppWeb.LiveHelpers
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      unquote(view_helpers())
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def live_component do
 | 
				
			||||||
 | 
					    quote do
 | 
				
			||||||
 | 
					      use Phoenix.LiveComponent
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      import AppWeb.LiveHelpers
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      unquote(view_helpers())
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def view do
 | 
					  def view do
 | 
				
			||||||
    quote do
 | 
					    quote do
 | 
				
			||||||
      use Phoenix.View,
 | 
					      use Phoenix.View,
 | 
				
			||||||
| 
						 | 
					@ -48,6 +69,7 @@ defmodule AppWeb do
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      import Plug.Conn
 | 
					      import Plug.Conn
 | 
				
			||||||
      import Phoenix.Controller
 | 
					      import Phoenix.Controller
 | 
				
			||||||
 | 
					      import Phoenix.LiveView.Router
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -66,9 +88,10 @@ defmodule AppWeb do
 | 
				
			||||||
      # Import basic rendering functionality (render, render_layout, etc)
 | 
					      # Import basic rendering functionality (render, render_layout, etc)
 | 
				
			||||||
      import Phoenix.View
 | 
					      import Phoenix.View
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      import Legendary.CoreWeb.Helpers
 | 
					 | 
				
			||||||
      import AppWeb.ErrorHelpers
 | 
					      import AppWeb.ErrorHelpers
 | 
				
			||||||
      import AppWeb.Gettext
 | 
					      import AppWeb.Gettext
 | 
				
			||||||
 | 
					      import Legendary.CoreWeb.Helpers
 | 
				
			||||||
 | 
					      import Phoenix.LiveView.Helpers
 | 
				
			||||||
      alias AppWeb.Router.Helpers, as: Routes
 | 
					      alias AppWeb.Router.Helpers, as: Routes
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										0
									
								
								apps/app/lib/app_web/live/.keep
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								apps/app/lib/app_web/live/.keep
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										41
									
								
								apps/app/lib/app_web/live_helpers.ex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								apps/app/lib/app_web/live_helpers.ex
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,41 @@
 | 
				
			||||||
 | 
					defmodule AppWeb.LiveHelpers do
 | 
				
			||||||
 | 
					  @moduledoc """
 | 
				
			||||||
 | 
					  Commonly functions for LiveViews.
 | 
				
			||||||
 | 
					  """
 | 
				
			||||||
 | 
					  import Phoenix.LiveView
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  alias Legendary.Auth.User
 | 
				
			||||||
 | 
					  alias Pow.Store.CredentialsCache
 | 
				
			||||||
 | 
					  alias AppWeb.Pow.Routes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def assign_defaults(socket, session) do
 | 
				
			||||||
 | 
					    assign_new(socket, :current_user, fn -> get_user(socket, session) end)
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def require_auth(socket) do
 | 
				
			||||||
 | 
					    if socket.assigns.current_user do
 | 
				
			||||||
 | 
					      socket
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					      redirect(socket, to: Routes.after_sign_out_path(%Plug.Conn{}))
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  defp get_user(socket, session, config \\ [otp_app: :core])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  defp get_user(socket, %{"core_auth" => signed_token}, config) do
 | 
				
			||||||
 | 
					    {otp_app, _config} = Keyword.pop(config, :otp_app, :core)
 | 
				
			||||||
 | 
					    {store, store_config} = Pow.Plug.Base.store(Application.get_env(otp_app, :pow))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    conn = struct!(Plug.Conn, secret_key_base: socket.endpoint.config(:secret_key_base))
 | 
				
			||||||
 | 
					    salt = Atom.to_string(Pow.Plug.Session)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    with {:ok, token} <- Pow.Plug.verify_token(conn, salt, signed_token, config),
 | 
				
			||||||
 | 
					        {user, _metadata} <- store.get(store_config, token) do
 | 
				
			||||||
 | 
					      user
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					      _any -> nil
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  defp get_user(_, _, _), do: nil
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
| 
						 | 
					@ -9,10 +9,11 @@ defmodule AppWeb.Router do
 | 
				
			||||||
  pipeline :browser do
 | 
					  pipeline :browser do
 | 
				
			||||||
    plug :accepts, ["html"]
 | 
					    plug :accepts, ["html"]
 | 
				
			||||||
    plug :fetch_session
 | 
					    plug :fetch_session
 | 
				
			||||||
    plug :fetch_flash
 | 
					    plug :fetch_live_flash
 | 
				
			||||||
    plug :protect_from_forgery
 | 
					    plug :protect_from_forgery
 | 
				
			||||||
    plug :put_secure_browser_headers
 | 
					    plug :put_secure_browser_headers
 | 
				
			||||||
    plug :put_layout, {AppWeb.LayoutView, :app}
 | 
					    plug :put_layout, {AppWeb.LayoutView, :app}
 | 
				
			||||||
 | 
					    plug :put_root_layout, {AppWeb.LayoutView, :root}
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  pipeline :api do
 | 
					  pipeline :api do
 | 
				
			||||||
| 
						 | 
					@ -57,6 +58,7 @@ defmodule AppWeb.Router do
 | 
				
			||||||
    pow_extension_routes()
 | 
					    pow_extension_routes()
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  use Legendary.Core.Routes
 | 
				
			||||||
  use Legendary.Admin.Routes
 | 
					  use Legendary.Admin.Routes
 | 
				
			||||||
  use Legendary.Content.Routes
 | 
					  use Legendary.Content.Routes
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,22 @@
 | 
				
			||||||
 | 
					<div class="fixed w-full px-4 z-50 mt-20">
 | 
				
			||||||
 | 
					  <%= [info: "green", error: "red"] |> Enum.map(fn {level, color} ->  %>
 | 
				
			||||||
 | 
					    <%= if live_flash(@flash, level) do %>
 | 
				
			||||||
 | 
					      <div
 | 
				
			||||||
 | 
					        class="relative bg-<%= color %>-100 lg:w-1/2 w-full p-5 object-right rounded shadow-xl m-auto mb-5 js-flash"
 | 
				
			||||||
 | 
					        phx-click="lv:clear-flash"
 | 
				
			||||||
 | 
					        phx-value-key="<%= level %>"
 | 
				
			||||||
 | 
					      >
 | 
				
			||||||
 | 
					        <div class="flex justify-between text-<%= color %>-700">
 | 
				
			||||||
 | 
					          <div class="flex space-x-3">
 | 
				
			||||||
 | 
					            <div class="flex-1 leading-tight text-sm font-medium">
 | 
				
			||||||
 | 
					              <%= live_flash(@flash, level) %>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					          <div class="flex-none">
 | 
				
			||||||
 | 
					            ×
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    <% end %>
 | 
				
			||||||
 | 
					  <% end) %>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
| 
						 | 
					@ -1,3 +1,5 @@
 | 
				
			||||||
 | 
					<% conn_or_socket = if assigns[:conn], do: assigns[:conn], else: @socket %>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<nav class="flex items-center justify-between flex-wrap bg-grey-dark p-6 w-full z-10 bg-gray-800">
 | 
					<nav class="flex items-center justify-between flex-wrap bg-grey-dark p-6 w-full z-10 bg-gray-800">
 | 
				
			||||||
  <div class="flex items-center flex-no-shrink text-white mr-6">
 | 
					  <div class="flex items-center flex-no-shrink text-white mr-6">
 | 
				
			||||||
    <a class="text-white no-underline hover:text-white hover:no-underline" href="/">
 | 
					    <a class="text-white no-underline hover:text-white hover:no-underline" href="/">
 | 
				
			||||||
| 
						 | 
					@ -35,14 +37,14 @@
 | 
				
			||||||
          class: "inline-block text-white  no-underline hover:text-white hover:text-underline py-2 px-4"
 | 
					          class: "inline-block text-white  no-underline hover:text-white hover:text-underline py-2 px-4"
 | 
				
			||||||
        %>
 | 
					        %>
 | 
				
			||||||
      </li>
 | 
					      </li>
 | 
				
			||||||
      <%= if has_role?(@conn, :admin) do %>
 | 
					      <%= if has_role?(conn_or_socket, :admin) do %>
 | 
				
			||||||
        <li class="mr-3">
 | 
					        <li class="mr-3">
 | 
				
			||||||
          <a class="inline-block py-2 px-4 text-white no-underline" href="/admin">Admin</a>
 | 
					          <a class="inline-block py-2 px-4 text-white no-underline" href="/admin">Admin</a>
 | 
				
			||||||
        </li>
 | 
					        </li>
 | 
				
			||||||
      <% end %>
 | 
					      <% end %>
 | 
				
			||||||
      <%= if Pow.Plug.current_user(@conn) do %>
 | 
					      <%= if current_user(conn_or_socket) do %>
 | 
				
			||||||
        <li class="mr-3">
 | 
					        <li class="mr-3">
 | 
				
			||||||
          <%= 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" %>
 | 
					          <%= link "Sign Out", to: Routes.pow_session_path(conn_or_socket, :delete), method: :delete, class: "inline-block text-white  no-underline hover:text-white hover:text-underline py-2 px-4" %>
 | 
				
			||||||
        </li>
 | 
					        </li>
 | 
				
			||||||
      <% else %>
 | 
					      <% else %>
 | 
				
			||||||
      <% end %>
 | 
					      <% end %>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,24 +1,5 @@
 | 
				
			||||||
<!DOCTYPE html>
 | 
					<%= render "_menu.html", assigns %>
 | 
				
			||||||
<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">
 | 
					 | 
				
			||||||
        <%= render "_menu.html", assigns %>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <%= flash_block(@conn) %>
 | 
					<%= flash_block(@conn) %>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <%= @inner_content %>
 | 
					<%= @inner_content %>
 | 
				
			||||||
      </div>
 | 
					 | 
				
			||||||
    </main>
 | 
					 | 
				
			||||||
  </body>
 | 
					 | 
				
			||||||
</html>
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										6
									
								
								apps/app/lib/app_web/templates/layout/live.html.leex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								apps/app/lib/app_web/templates/layout/live.html.leex
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,6 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<%= render "_menu.html", assigns %>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<%= render "_live_flash_block.html", flash: @flash %>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<%= @inner_content %>
 | 
				
			||||||
							
								
								
									
										20
									
								
								apps/app/lib/app_web/templates/layout/root.html.leex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								apps/app/lib/app_web/templates/layout/root.html.leex
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,20 @@
 | 
				
			||||||
 | 
					<!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"/>
 | 
				
			||||||
 | 
					    <%= csrf_meta_tag() %>
 | 
				
			||||||
 | 
					    <%= live_title_tag title(@view_module, @view_template, assigns) %>
 | 
				
			||||||
 | 
					    <link phx-track-static rel="stylesheet" href="<%= Routes.static_path(@conn, "/css/app.css") %>"/>
 | 
				
			||||||
 | 
					    <script defer phx-track-static 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">
 | 
				
			||||||
 | 
					        <%= @inner_content %>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </main>
 | 
				
			||||||
 | 
					  </body>
 | 
				
			||||||
 | 
					</html>
 | 
				
			||||||
| 
						 | 
					@ -44,12 +44,14 @@ defmodule App.MixProject do
 | 
				
			||||||
      {:core, in_umbrella: true},
 | 
					      {:core, in_umbrella: true},
 | 
				
			||||||
      {:ecto_sql, "~> 3.4"},
 | 
					      {:ecto_sql, "~> 3.4"},
 | 
				
			||||||
      {:excoveralls, "~> 0.10", only: [:dev, :test]},
 | 
					      {:excoveralls, "~> 0.10", only: [:dev, :test]},
 | 
				
			||||||
 | 
					      {:floki, ">= 0.30.0"},
 | 
				
			||||||
      {:oban, "~> 2.1"},
 | 
					      {:oban, "~> 2.1"},
 | 
				
			||||||
      {:phoenix, "~> 1.5.8"},
 | 
					      {:phoenix, "~> 1.5.8"},
 | 
				
			||||||
      {:phoenix_ecto, "~> 4.0"},
 | 
					      {:phoenix_ecto, "~> 4.0"},
 | 
				
			||||||
      {:phoenix_html, "~> 2.11"},
 | 
					      {:phoenix_html, "~> 2.11"},
 | 
				
			||||||
      {:phoenix_live_reload, "~> 1.2", only: :dev},
 | 
					      {:phoenix_live_reload, "~> 1.2", only: :dev},
 | 
				
			||||||
      {:phoenix_live_dashboard, "~> 0.2.0"},
 | 
					      {:phoenix_live_dashboard, "~> 0.4.0"},
 | 
				
			||||||
 | 
					      {:phoenix_live_view, "~> 0.15.7", override: true},
 | 
				
			||||||
      {:postgrex, ">= 0.0.0"},
 | 
					      {:postgrex, ">= 0.0.0"},
 | 
				
			||||||
      {:telemetry_metrics, "~> 0.4"},
 | 
					      {:telemetry_metrics, "~> 0.4"},
 | 
				
			||||||
      {:telemetry_poller, "~> 0.4"},
 | 
					      {:telemetry_poller, "~> 0.4"},
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -42,7 +42,7 @@ defmodule Legendary.Content do
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      def process_content(text) do
 | 
					      def process_content(text) do
 | 
				
			||||||
        text
 | 
					        text
 | 
				
			||||||
        |> Earmark.as_html!(encode: false)
 | 
					        |> Earmark.as_html!()
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      # Include shared imports and aliases for views
 | 
					      # Include shared imports and aliases for views
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,8 +3,6 @@ defmodule Legendary.Content.FeedsController do
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  alias Legendary.Content.{Posts}
 | 
					  alias Legendary.Content.{Posts}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  plug :put_layout, false when action in [:preview]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  def index(conn, params) do
 | 
					  def index(conn, params) do
 | 
				
			||||||
    category = params |> Map.get("category")
 | 
					    category = params |> Map.get("category")
 | 
				
			||||||
    posts =  Posts.list_posts(params)
 | 
					    posts =  Posts.list_posts(params)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,9 +5,9 @@
 | 
				
			||||||
    <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
 | 
					    <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
 | 
				
			||||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
 | 
					    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
 | 
				
			||||||
    <title><%= title(@view_module, @view_template, assigns) %></title>
 | 
					    <title><%= title(@view_module, @view_template, assigns) %></title>
 | 
				
			||||||
    <link rel="stylesheet" href="<%= Routes.static_path(@conn, "/css/app.css") %>"/>
 | 
					    <link phx-track-static rel="stylesheet" href="<%= Routes.static_path(@conn, "/css/app.css") %>"/>
 | 
				
			||||||
    <%= feed_tag(@conn, @view_module, @view_template, assigns) %>
 | 
					    <%= feed_tag(@conn, @view_module, @view_template, assigns) %>
 | 
				
			||||||
    <script defer type="text/javascript" src="<%= Routes.static_path(@conn, "/js/app.js") %>"></script>
 | 
					    <script phx-track-static defer type="text/javascript" src="<%= Routes.static_path(@conn, "/js/app.js") %>"></script>
 | 
				
			||||||
  </head>
 | 
					  </head>
 | 
				
			||||||
  <body class="text-gray-800 antialiased">
 | 
					  <body class="text-gray-800 antialiased">
 | 
				
			||||||
    <main role="main">
 | 
					    <main role="main">
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,7 @@
 | 
				
			||||||
defmodule Legendary.Content.MixProject do
 | 
					defmodule Legendary.Content.MixProject do
 | 
				
			||||||
  use Mix.Project
 | 
					  use Mix.Project
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @version "2.10.0"
 | 
					  @version "2.11.2"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def project do
 | 
					  def project do
 | 
				
			||||||
    [
 | 
					    [
 | 
				
			||||||
| 
						 | 
					@ -42,9 +42,9 @@ defmodule Legendary.Content.MixProject do
 | 
				
			||||||
  defp deps do
 | 
					  defp deps do
 | 
				
			||||||
    [
 | 
					    [
 | 
				
			||||||
      {:core, in_umbrella: true},
 | 
					      {:core, in_umbrella: true},
 | 
				
			||||||
      {:earmark, "1.4.3"},
 | 
					      {:earmark, "1.4.15"},
 | 
				
			||||||
      {:excoveralls, "~> 0.10", only: [:dev, :test]},
 | 
					      {:excoveralls, "~> 0.10", only: [:dev, :test]},
 | 
				
			||||||
      {:floki, "~> 0.25.0"},
 | 
					      {:floki, ">= 0.30.0"},
 | 
				
			||||||
      {:gettext, "~> 0.11"},
 | 
					      {:gettext, "~> 0.11"},
 | 
				
			||||||
      {:jason, "~> 1.0"},
 | 
					      {:jason, "~> 1.0"},
 | 
				
			||||||
      {:mock, "~> 0.3.0", only: :test},
 | 
					      {:mock, "~> 0.3.0", only: :test},
 | 
				
			||||||
| 
						 | 
					@ -54,10 +54,10 @@ defmodule Legendary.Content.MixProject do
 | 
				
			||||||
      {:phoenix, "~> 1.5.8"},
 | 
					      {:phoenix, "~> 1.5.8"},
 | 
				
			||||||
      {:phoenix_ecto, "~> 4.0"},
 | 
					      {:phoenix_ecto, "~> 4.0"},
 | 
				
			||||||
      {:phoenix_html, "~> 2.11"},
 | 
					      {:phoenix_html, "~> 2.11"},
 | 
				
			||||||
      {:phoenix_html_sanitizer, "~> 1.0.0"},
 | 
					      {:phoenix_html_sanitizer, "~> 1.1.0"},
 | 
				
			||||||
      {:phoenix_live_reload, "~> 1.2", only: :dev},
 | 
					      {:phoenix_live_reload, "~> 1.2", only: :dev},
 | 
				
			||||||
      {:phoenix_live_dashboard, "~> 0.2.0"},
 | 
					      {:phoenix_live_dashboard, "~> 0.4.0"},
 | 
				
			||||||
      {:php_serializer, "~> 0.9.0"},
 | 
					      {:php_serializer, "~> 2.0.0"},
 | 
				
			||||||
      {:plug_cowboy, "~> 2.0"},
 | 
					      {:plug_cowboy, "~> 2.0"},
 | 
				
			||||||
      {:sitemap, "~> 1.1"},
 | 
					      {:sitemap, "~> 1.1"},
 | 
				
			||||||
      {:slugger, "~> 0.3"},
 | 
					      {:slugger, "~> 0.3"},
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										32
									
								
								apps/core/guides/features/feature-flags.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								apps/core/guides/features/feature-flags.md
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,32 @@
 | 
				
			||||||
 | 
					# Feature Flags
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Legendary comes with [Fun with Flags](https://github.com/tompave/fun_with_flags)
 | 
				
			||||||
 | 
					preconfigured for managing [feature flags](https://en.wikipedia.org/wiki/Feature_toggle).
 | 
				
			||||||
 | 
					This allows you to have more granular control over which users see which features
 | 
				
			||||||
 | 
					and when. For example, you can hide a feature which is not complete, or show it
 | 
				
			||||||
 | 
					to only a select group of testers.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Fun With Flags supports a variety of different feature gate types. From
 | 
				
			||||||
 | 
					the Fun With Flags docs:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* **Boolean**: globally on and off.
 | 
				
			||||||
 | 
					* **Actors**: on or off for specific structs or data. The `FunWithFlags.Actor` protocol can be implemented for types and structs that should have specific rules. For example, in web applications it's common to use a `%User{}` struct or equivalent as an actor, or perhaps the current country of the request.
 | 
				
			||||||
 | 
					* **Groups**: on or off for structs or data that belong to a category or satisfy a condition. The `FunWithFlags.Group` protocol can be implemented for types and structs that belong to groups for which a feature flag can be enabled or disabled. For example, one could implement the protocol for a `%User{}` struct to identify administrators.
 | 
				
			||||||
 | 
					* **%-of-Time**: globally on for a percentage of the time. It ignores actors and groups. Mutually exclusive with the %-of-actors gate.
 | 
				
			||||||
 | 
					* **%-of-Actors**: globally on for a percentage of the actors. It only applies when the flag is checked with a specific actor and is ignored when the flag is checked without actor arguments. Mutually exclusive with the %-of-time gate.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Since feature flags may be checked often (sometimes multiple times per request),
 | 
				
			||||||
 | 
					Fun With Flags uses a two-layer approach. Flags are cached in [ETS](https://erlang.org/doc/man/ets.html)
 | 
				
			||||||
 | 
					and also persisted to longer-term storage so that they are not lost when the app
 | 
				
			||||||
 | 
					restarts.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					By default, Legendary caches the flags for five minutes. We use Ecto for
 | 
				
			||||||
 | 
					persistence. We also use Phoenix PubSub to inform application nodes when a flag
 | 
				
			||||||
 | 
					has been updated. This configuration is a sensible default that we would not
 | 
				
			||||||
 | 
					expect you to need to change in most cases.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## UI
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					We integrate the Fun With Flags UI for managing flags. You can reach it through
 | 
				
			||||||
 | 
					a link in the admin.
 | 
				
			||||||
| 
						 | 
					@ -3,6 +3,12 @@ defmodule Legendary.Auth.UserAdmin do
 | 
				
			||||||
  alias Legendary.Auth.User
 | 
					  alias Legendary.Auth.User
 | 
				
			||||||
  alias Legendary.Core.Repo
 | 
					  alias Legendary.Core.Repo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def custom_links(_schema) do
 | 
				
			||||||
 | 
					    [
 | 
				
			||||||
 | 
					      %{name: "Feature Flags", url: "/admin/feature-flags", order: 2, location: :top, icon: "flag"},
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def create_changeset(schema, attrs) do
 | 
					  def create_changeset(schema, attrs) do
 | 
				
			||||||
    Legendary.Auth.User.admin_changeset(schema, attrs)
 | 
					    Legendary.Auth.User.admin_changeset(schema, attrs)
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										45
									
								
								apps/core/lib/auth_web/controller_callbacks.ex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								apps/core/lib/auth_web/controller_callbacks.ex
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,45 @@
 | 
				
			||||||
 | 
					defmodule AuthWeb.Pow.ControllerCallbacks do
 | 
				
			||||||
 | 
					  @moduledoc """
 | 
				
			||||||
 | 
					  Hook into Pow Controllers to provide additional framework feature. In particular,
 | 
				
			||||||
 | 
					  we disconnect any active live views when a user logs out. This will cause the
 | 
				
			||||||
 | 
					  live view to re-connect with the new session environment.
 | 
				
			||||||
 | 
					  """
 | 
				
			||||||
 | 
					  alias Pow.Extension.Phoenix.ControllerCallbacks
 | 
				
			||||||
 | 
					  alias Plug.Conn
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def before_respond(Pow.Phoenix.SessionController, :create, {:ok, conn}, config) do
 | 
				
			||||||
 | 
					    user = conn.assigns.current_user
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    conn =
 | 
				
			||||||
 | 
					      conn
 | 
				
			||||||
 | 
					      |> Conn.put_session(:current_user_id, user.id)
 | 
				
			||||||
 | 
					      |> Conn.put_session(:live_socket_id, "users_sockets:#{user.id}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ControllerCallbacks.before_respond(
 | 
				
			||||||
 | 
					      Pow.Phoenix.SessionController,
 | 
				
			||||||
 | 
					      :create,
 | 
				
			||||||
 | 
					      {:ok, conn},
 | 
				
			||||||
 | 
					      config
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def before_respond(Pow.Phoenix.SessionController, :delete, {:ok, conn}, config) do
 | 
				
			||||||
 | 
					    live_socket_id = Conn.get_session(conn, :live_socket_id)
 | 
				
			||||||
 | 
					    AppWeb.Endpoint.broadcast(live_socket_id, "disconnect", %{})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    conn =
 | 
				
			||||||
 | 
					      conn
 | 
				
			||||||
 | 
					      |> Conn.delete_session(:live_socket_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ControllerCallbacks.before_respond(
 | 
				
			||||||
 | 
					      Pow.Phoenix.SessionController,
 | 
				
			||||||
 | 
					      :delete,
 | 
				
			||||||
 | 
					      {:ok, conn},
 | 
				
			||||||
 | 
					      config
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  defdelegate before_respond(controller, action, results, config), to: ControllerCallbacks
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  defdelegate before_process(controller, action, results, config), to: ControllerCallbacks
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,12 @@
 | 
				
			||||||
defmodule Legendary.AuthWeb.Helpers do
 | 
					defmodule Legendary.AuthWeb.Helpers do
 | 
				
			||||||
  def has_role?(conn = %Plug.Conn{}, role) do
 | 
					  def current_user(socket = %Phoenix.LiveView.Socket{assigns: %{current_user: user}}), do: user
 | 
				
			||||||
    conn
 | 
					  def current_user(socket = %Phoenix.LiveView.Socket{assigns: %{__assigns__: %{current_user: user}}}), do: user
 | 
				
			||||||
    |> Pow.Plug.current_user()
 | 
					  def current_user(%Phoenix.LiveView.Socket{}), do: nil
 | 
				
			||||||
 | 
					  def current_user(conn), do: Pow.Plug.current_user(conn)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def has_role?(conn_or_socket, role) do
 | 
				
			||||||
 | 
					    conn_or_socket
 | 
				
			||||||
 | 
					    |> current_user()
 | 
				
			||||||
    |> Legendary.Auth.Roles.has_role?(role)
 | 
					    |> Legendary.Auth.Roles.has_role?(role)
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										11
									
								
								apps/core/lib/core_web/routes.ex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								apps/core/lib/core_web/routes.ex
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,11 @@
 | 
				
			||||||
 | 
					defmodule Legendary.Core.Routes do
 | 
				
			||||||
 | 
					  defmacro __using__(_opts \\ []) do
 | 
				
			||||||
 | 
					    quote do
 | 
				
			||||||
 | 
					      scope path: "/admin/feature-flags" do
 | 
				
			||||||
 | 
					        pipe_through :require_admin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        forward "/", FunWithFlags.UI.Router, namespace: "admin/feature-flags"
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
| 
						 | 
					@ -7,11 +7,8 @@ defmodule Legendary.CoreWeb.Helpers do
 | 
				
			||||||
  import Phoenix.Controller, only: [get_flash: 2]
 | 
					  import Phoenix.Controller, only: [get_flash: 2]
 | 
				
			||||||
  import Legendary.CoreWeb.ErrorHelpers
 | 
					  import Legendary.CoreWeb.ErrorHelpers
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def has_role?(conn = %Plug.Conn{}, role) do
 | 
					  defdelegate current_user(a), to: Legendary.AuthWeb.Helpers
 | 
				
			||||||
    conn
 | 
					  defdelegate has_role?(a, b), to: Legendary.AuthWeb.Helpers
 | 
				
			||||||
    |> Pow.Plug.current_user()
 | 
					 | 
				
			||||||
    |> Legendary.Auth.Roles.has_role?(role)
 | 
					 | 
				
			||||||
  end
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def changeset_error_block(changeset) do
 | 
					  def changeset_error_block(changeset) do
 | 
				
			||||||
    ~E"""
 | 
					    ~E"""
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,7 @@
 | 
				
			||||||
defmodule Legendary.Core.MixProject do
 | 
					defmodule Legendary.Core.MixProject do
 | 
				
			||||||
  use Mix.Project
 | 
					  use Mix.Project
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @version "2.10.0"
 | 
					  @version "2.11.2"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def project do
 | 
					  def project do
 | 
				
			||||||
    [
 | 
					    [
 | 
				
			||||||
| 
						 | 
					@ -58,6 +58,7 @@ defmodule Legendary.Core.MixProject do
 | 
				
			||||||
      "guides/features/content-management.md",
 | 
					      "guides/features/content-management.md",
 | 
				
			||||||
      "guides/features/devops-templates.md",
 | 
					      "guides/features/devops-templates.md",
 | 
				
			||||||
      "guides/features/email.md",
 | 
					      "guides/features/email.md",
 | 
				
			||||||
 | 
					      "guides/features/feature-flags.md",
 | 
				
			||||||
      "guides/features/i18n.md",
 | 
					      "guides/features/i18n.md",
 | 
				
			||||||
      "guides/features/tasks-and-scripts.md",
 | 
					      "guides/features/tasks-and-scripts.md",
 | 
				
			||||||
    ]
 | 
					    ]
 | 
				
			||||||
| 
						 | 
					@ -139,15 +140,17 @@ defmodule Legendary.Core.MixProject do
 | 
				
			||||||
      {:ex_cldr, "~> 2.13.0"},
 | 
					      {:ex_cldr, "~> 2.13.0"},
 | 
				
			||||||
      {:ex_doc, "~> 0.24", only: :dev, runtime: false},
 | 
					      {:ex_doc, "~> 0.24", only: :dev, runtime: false},
 | 
				
			||||||
      {:excoveralls, "~> 0.10", only: [:dev, :test]},
 | 
					      {:excoveralls, "~> 0.10", only: [:dev, :test]},
 | 
				
			||||||
 | 
					      {:fun_with_flags, "~> 1.6.0"},
 | 
				
			||||||
 | 
					      {:fun_with_flags_ui, "~> 0.7.2"},
 | 
				
			||||||
      {:phoenix, "~> 1.5.8"},
 | 
					      {:phoenix, "~> 1.5.8"},
 | 
				
			||||||
      {:phoenix_ecto, "~> 4.1"},
 | 
					      {:phoenix_ecto, "~> 4.1"},
 | 
				
			||||||
      {:ecto_sql, "~> 3.4"},
 | 
					      {:ecto_sql, "~> 3.4"},
 | 
				
			||||||
      {:ex_prompt, "~> 0.1.5"},
 | 
					      {:ex_prompt, "~> 0.2.0"},
 | 
				
			||||||
      {:linguist, "0.3.1"},
 | 
					      {:linguist, "0.3.1"},
 | 
				
			||||||
      {:postgrex, ">= 0.0.0"},
 | 
					      {:postgrex, ">= 0.0.0"},
 | 
				
			||||||
      {:phoenix_html, "~> 2.11"},
 | 
					      {:phoenix_html, "~> 2.11"},
 | 
				
			||||||
      {:phoenix_live_reload, "~> 1.2", only: :dev},
 | 
					      {:phoenix_live_reload, "~> 1.2", only: :dev},
 | 
				
			||||||
      {:phoenix_live_dashboard, "~> 0.2.0"},
 | 
					      {:phoenix_live_dashboard, "~> 0.4.0"},
 | 
				
			||||||
      {:phoenix_pubsub, "~> 2.0"},
 | 
					      {:phoenix_pubsub, "~> 2.0"},
 | 
				
			||||||
      {:pow, "~> 1.0.20"},
 | 
					      {:pow, "~> 1.0.20"},
 | 
				
			||||||
      {:telemetry_metrics, "~> 0.4"},
 | 
					      {:telemetry_metrics, "~> 0.4"},
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,23 @@
 | 
				
			||||||
 | 
					defmodule Legendary.Core.Repo.Migrations.AddFeatureFlagTable do
 | 
				
			||||||
 | 
					  use Ecto.Migration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def up do
 | 
				
			||||||
 | 
					    create table(:fun_with_flags_toggles, primary_key: false) do
 | 
				
			||||||
 | 
					      add :id, :bigserial, primary_key: true
 | 
				
			||||||
 | 
					      add :flag_name, :string, null: false
 | 
				
			||||||
 | 
					      add :gate_type, :string, null: false
 | 
				
			||||||
 | 
					      add :target, :string, null: false
 | 
				
			||||||
 | 
					      add :enabled, :boolean, null: false
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    create index(
 | 
				
			||||||
 | 
					      :fun_with_flags_toggles,
 | 
				
			||||||
 | 
					      [:flag_name, :gate_type, :target],
 | 
				
			||||||
 | 
					      [unique: true, name: "fwf_flag_name_gate_target_idx"]
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  def down do
 | 
				
			||||||
 | 
					    drop table(:fun_with_flags_toggles)
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
| 
						 | 
					@ -40,7 +40,7 @@ config :core, :pow,
 | 
				
			||||||
  user: Legendary.Auth.User,
 | 
					  user: Legendary.Auth.User,
 | 
				
			||||||
  repo: Legendary.Core.Repo,
 | 
					  repo: Legendary.Core.Repo,
 | 
				
			||||||
  extensions: [PowEmailConfirmation, PowPersistentSession, PowResetPassword],
 | 
					  extensions: [PowEmailConfirmation, PowPersistentSession, PowResetPassword],
 | 
				
			||||||
  controller_callbacks: Pow.Extension.Phoenix.ControllerCallbacks,
 | 
					  controller_callbacks: AuthWeb.Pow.ControllerCallbacks,
 | 
				
			||||||
  mailer_backend: Legendary.AuthWeb.Pow.Mailer,
 | 
					  mailer_backend: Legendary.AuthWeb.Pow.Mailer,
 | 
				
			||||||
  web_mailer_module: Legendary.AuthWeb,
 | 
					  web_mailer_module: Legendary.AuthWeb,
 | 
				
			||||||
  web_module: Legendary.AuthWeb,
 | 
					  web_module: Legendary.AuthWeb,
 | 
				
			||||||
| 
						 | 
					@ -74,7 +74,25 @@ config :app,
 | 
				
			||||||
  crontab: [
 | 
					  crontab: [
 | 
				
			||||||
  ]
 | 
					  ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
config :mnesia, dir: to_charlist(Path.expand("./priv/mnesia"))
 | 
					config :mnesia, dir: to_charlist(Path.expand("./priv/mnesia@#{Kernel.node}"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Feature flags
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					config :fun_with_flags, :cache,
 | 
				
			||||||
 | 
					  enabled: true,
 | 
				
			||||||
 | 
					  ttl: 300 # seconds
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					config :fun_with_flags, :persistence,
 | 
				
			||||||
 | 
					  adapter: FunWithFlags.Store.Persistent.Ecto,
 | 
				
			||||||
 | 
					  repo: Legendary.Core.Repo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					config :fun_with_flags, :cache_bust_notifications,
 | 
				
			||||||
 | 
					  enabled: true,
 | 
				
			||||||
 | 
					  adapter: FunWithFlags.Notifications.PhoenixPubSub,
 | 
				
			||||||
 | 
					  client: App.PubSub
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Notifications can also be disabled, which will also remove the Redis/Redix dependency
 | 
				
			||||||
 | 
					config :fun_with_flags, :cache_bust_notifications, [enabled: false]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import_config "email_styles.exs"
 | 
					import_config "email_styles.exs"
 | 
				
			||||||
import_config "admin.exs"
 | 
					import_config "admin.exs"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -15,7 +15,7 @@ use Mix.Config
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
|> Enum.map(fn {otp_app, module} ->
 | 
					|> Enum.map(fn {otp_app, module} ->
 | 
				
			||||||
  config otp_app, Module.concat(module, "Endpoint"),
 | 
					  config otp_app, Module.concat(module, "Endpoint"),
 | 
				
			||||||
    http: [port: 4000],
 | 
					    http: [port: String.to_integer(System.get_env("PORT") || "4000")],
 | 
				
			||||||
    debug_errors: true,
 | 
					    debug_errors: true,
 | 
				
			||||||
    code_reloader: true,
 | 
					    code_reloader: true,
 | 
				
			||||||
    check_origin: false,
 | 
					    check_origin: false,
 | 
				
			||||||
| 
						 | 
					@ -24,18 +24,19 @@ use Mix.Config
 | 
				
			||||||
        "node_modules/webpack/bin/webpack.js",
 | 
					        "node_modules/webpack/bin/webpack.js",
 | 
				
			||||||
        "--mode",
 | 
					        "--mode",
 | 
				
			||||||
        "development",
 | 
					        "development",
 | 
				
			||||||
        "--watch-stdin",
 | 
					        "--watch",
 | 
				
			||||||
 | 
					        "--watch-options-stdin",
 | 
				
			||||||
        cd: Path.expand("../apps/#{otp_app}/assets", __DIR__)
 | 
					        cd: Path.expand("../apps/#{otp_app}/assets", __DIR__)
 | 
				
			||||||
      ]
 | 
					      ]
 | 
				
			||||||
    ]
 | 
					    ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  config otp_app, Module.concat(module, "Endpoint"),
 | 
					  config otp_app, AppWeb.Endpoint,
 | 
				
			||||||
    live_reload: [
 | 
					    live_reload: [
 | 
				
			||||||
      patterns: [
 | 
					      patterns: [
 | 
				
			||||||
        ~r"priv/static/.*(js|css|png|jpeg|jpg|gif|svg)$",
 | 
					        ~r"priv/static/.*(js|css|png|jpeg|jpg|gif|svg)$",
 | 
				
			||||||
        ~r"priv/gettext/.*(po)$",
 | 
					        ~r"priv/gettext/.*(po)$",
 | 
				
			||||||
        ~r"lib/app/(live|views)/.*(ex)$",
 | 
					        ~r"(live|views)/.*(ex)$",
 | 
				
			||||||
        ~r"lib/app/templates/.*(eex)$"
 | 
					        ~r"templates/.*(eex)$"
 | 
				
			||||||
      ]
 | 
					      ]
 | 
				
			||||||
    ]
 | 
					    ]
 | 
				
			||||||
end)
 | 
					end)
 | 
				
			||||||
| 
						 | 
					@ -61,7 +62,7 @@ config :core, Legendary.CoreMailer, adapter: Bamboo.LocalAdapter
 | 
				
			||||||
config :libcluster,
 | 
					config :libcluster,
 | 
				
			||||||
  topologies: [
 | 
					  topologies: [
 | 
				
			||||||
    erlang_hosts: [
 | 
					    erlang_hosts: [
 | 
				
			||||||
      strategy: Elixir.Cluster.Strategy.ErlangHosts,
 | 
					      strategy: Elixir.Cluster.Strategy.Gossip,
 | 
				
			||||||
    ]
 | 
					    ]
 | 
				
			||||||
  ]
 | 
					  ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -73,7 +73,7 @@ config :core, Legendary.CoreMailer,
 | 
				
			||||||
  username: {:system, "SMTP_USERNAME"},
 | 
					  username: {:system, "SMTP_USERNAME"},
 | 
				
			||||||
  password: {:system, "SMTP_PASSWORD"},
 | 
					  password: {:system, "SMTP_PASSWORD"},
 | 
				
			||||||
  tls: :if_available,
 | 
					  tls: :if_available,
 | 
				
			||||||
  allowed_tls_versions: [:"tlsv1", :"tlsv1.1", :"tlsv1.2"],
 | 
					  allowed_tls_versions: [:tlsv1, :"tlsv1.1", :"tlsv1.2"],
 | 
				
			||||||
  ssl: false,
 | 
					  ssl: false,
 | 
				
			||||||
  retries: 1,
 | 
					  retries: 1,
 | 
				
			||||||
  no_mx_lookups: false,
 | 
					  no_mx_lookups: false,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										2
									
								
								mix.exs
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								mix.exs
									
									
									
									
									
								
							| 
						 | 
					@ -1,7 +1,7 @@
 | 
				
			||||||
defmodule Legendary.Mixfile do
 | 
					defmodule Legendary.Mixfile do
 | 
				
			||||||
  use Mix.Project
 | 
					  use Mix.Project
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @version "2.10.0"
 | 
					  @version "2.11.2"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def project do
 | 
					  def project do
 | 
				
			||||||
    [
 | 
					    [
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										22
									
								
								mix.lock
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								mix.lock
									
									
									
									
									
								
							| 
						 | 
					@ -16,25 +16,25 @@
 | 
				
			||||||
  "db_connection": {:hex, :db_connection, "2.2.2", "3bbca41b199e1598245b716248964926303b5d4609ff065125ce98bcd368939e", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}], "hexpm", "642af240d8a8affb93b4ba5a6fcd2bbcbdc327e1a524b825d383711536f8070c"},
 | 
					  "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"},
 | 
					  "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"},
 | 
					  "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"},
 | 
					  "earmark": {:hex, :earmark, "1.4.15", "2c7f924bf495ec1f65bd144b355d0949a05a254d0ec561740308a54946a67888", [:mix], [{:earmark_parser, ">= 1.4.13", [hex: :earmark_parser, repo: "hexpm", optional: false]}], "hexpm", "3b1209b85bc9f3586f370f7c363f6533788fb4e51db23aa79565875e7f9999ee"},
 | 
				
			||||||
  "earmark_parser": {:hex, :earmark_parser, "1.4.12", "b245e875ec0a311a342320da0551da407d9d2b65d98f7a9597ae078615af3449", [:mix], [], "hexpm", "711e2cc4d64abb7d566d43f54b78f7dc129308a63bc103fbd88550d2174b3160"},
 | 
					  "earmark_parser": {:hex, :earmark_parser, "1.4.13", "0c98163e7d04a15feb62000e1a891489feb29f3d10cb57d4f845c405852bbef8", [:mix], [], "hexpm", "d602c26af3a0af43d2f2645613f65841657ad6efc9f0e361c3b6c06b578214ba"},
 | 
				
			||||||
  "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": {: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"},
 | 
					  "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"},
 | 
					  "elixir_make": {:hex, :elixir_make, "0.6.0", "38349f3e29aff4864352084fc736fa7fa0f2995a819a737554f7ebd28b85aaab", [:mix], [], "hexpm", "d522695b93b7f0b4c0fcb2dfe73a6b905b1c301226a5a55cb42e5b14d509e050"},
 | 
				
			||||||
  "erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"},
 | 
					  "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_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_doc": {:hex, :ex_doc, "0.24.0", "2df14354835afaabdf87cb2971ea9485d8a36ff590e4b6c250b4f60c8fdf9143", [:mix], [{:earmark_parser, "~> 1.4.0", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "a0f4bcff21ceebea48414e49885d2a3e542200f76a2facf3f8faa54935eeb721"},
 | 
					  "ex_doc": {:hex, :ex_doc, "0.24.0", "2df14354835afaabdf87cb2971ea9485d8a36ff590e4b6c250b4f60c8fdf9143", [:mix], [{:earmark_parser, "~> 1.4.0", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "a0f4bcff21ceebea48414e49885d2a3e542200f76a2facf3f8faa54935eeb721"},
 | 
				
			||||||
  "ex_prompt": {:hex, :ex_prompt, "0.1.5", "b136642d0962f8ea37b3c9fa185ad1f42c71c3b9c6c3950f0358d7f3d2db2970", [:mix], [], "hexpm", "ad19a404708c9c7b05d36090b2d074ceafbed248a8de1a22d45a05ebe6994b83"},
 | 
					  "ex_prompt": {:hex, :ex_prompt, "0.2.0", "4030424e9a7710e1939d81eea4a82af2e0a1826065adb28d59bc01e919af4a60", [:mix], [], "hexpm", "220ac023d87d529457b87c9db4b40ce542bff93ae2de16c582808c6822dfe3e8"},
 | 
				
			||||||
  "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"},
 | 
					  "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"},
 | 
				
			||||||
  "file_system": {:hex, :file_system, "0.2.8", "f632bd287927a1eed2b718f22af727c5aeaccc9a98d8c2bd7bff709e851dc986", [:mix], [], "hexpm", "97a3b6f8d63ef53bd0113070102db2ce05352ecf0d25390eb8d747c2bde98bca"},
 | 
					  "file_system": {:hex, :file_system, "0.2.8", "f632bd287927a1eed2b718f22af727c5aeaccc9a98d8c2bd7bff709e851dc986", [:mix], [], "hexpm", "97a3b6f8d63ef53bd0113070102db2ce05352ecf0d25390eb8d747c2bde98bca"},
 | 
				
			||||||
  "floki": {:hex, :floki, "0.25.0", "b1c9ddf5f32a3a90b43b76f3386ca054325dc2478af020e87b5111c19f2284ac", [:mix], [{:html_entities, "~> 0.5.0", [hex: :html_entities, repo: "hexpm", optional: false]}], "hexpm", "631f4e627c46d5ecd347df5a2accdaf0621c77c3693c5b75a8ad58e84c61f242"},
 | 
					  "floki": {:hex, :floki, "0.31.0", "f05ee8a8e6a3ced4e62beeb2c79a63bc8e12ab98fbaaf6e6a3d9b76b1278e23f", [:mix], [{:html_entities, "~> 0.5.0", [hex: :html_entities, repo: "hexpm", optional: false]}], "hexpm", "b05afa372f5c345a5bf240ac25ea1f0f3d5fcfd7490ac0beeb4a203f9444891e"},
 | 
				
			||||||
  "gen_smtp": {:hex, :gen_smtp, "1.1.1", "bf9303c31735100631b1d708d629e4c65944319d1143b5c9952054f4a1311d85", [:rebar3], [{:hut, "1.3.0", [hex: :hut, repo: "hexpm", optional: false]}, {:ranch, ">= 1.7.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "51bc50cc017efd4a4248cbc39ea30fb60efa7d4a49688986fafad84434ff9ab7"},
 | 
					  "gen_smtp": {:hex, :gen_smtp, "1.1.1", "bf9303c31735100631b1d708d629e4c65944319d1143b5c9952054f4a1311d85", [:rebar3], [{:hut, "1.3.0", [hex: :hut, repo: "hexpm", optional: false]}, {:ranch, ">= 1.7.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "51bc50cc017efd4a4248cbc39ea30fb60efa7d4a49688986fafad84434ff9ab7"},
 | 
				
			||||||
  "gen_stage": {:hex, :gen_stage, "1.0.0", "51c8ae56ff54f9a2a604ca583798c210ad245f415115453b773b621c49776df5", [:mix], [], "hexpm", "1d9fc978db5305ac54e6f5fec7adf80cd893b1000cf78271564c516aa2af7706"},
 | 
					  "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"},
 | 
					  "gen_state_machine": {:hex, :gen_state_machine, "2.1.0", "a38b0e53fad812d29ec149f0d354da5d1bc0d7222c3711f3a0bd5aa608b42992", [:mix], [], "hexpm", "ae367038808db25cee2f2c4b8d0531522ea587c4995eb6f96ee73410a60fa06b"},
 | 
				
			||||||
  "gettext": {:hex, :gettext, "0.18.2", "7df3ea191bb56c0309c00a783334b288d08a879f53a7014341284635850a6e55", [:mix], [], "hexpm", "f9f537b13d4fdd30f3039d33cb80144c3aa1f8d9698e47d7bcbcc8df93b1f5c5"},
 | 
					  "gettext": {:hex, :gettext, "0.18.2", "7df3ea191bb56c0309c00a783334b288d08a879f53a7014341284635850a6e55", [:mix], [], "hexpm", "f9f537b13d4fdd30f3039d33cb80144c3aa1f8d9698e47d7bcbcc8df93b1f5c5"},
 | 
				
			||||||
  "hackney": {:hex, :hackney, "1.17.4", "99da4674592504d3fb0cfef0db84c3ba02b4508bae2dff8c0108baa0d6e0977c", [:rebar3], [{:certifi, "~>2.6.1", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~>6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~>1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.3.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~>1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "de16ff4996556c8548d512f4dbe22dd58a587bf3332e7fd362430a7ef3986b16"},
 | 
					  "hackney": {:hex, :hackney, "1.17.4", "99da4674592504d3fb0cfef0db84c3ba02b4508bae2dff8c0108baa0d6e0977c", [:rebar3], [{:certifi, "~>2.6.1", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~>6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~>1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.3.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~>1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "de16ff4996556c8548d512f4dbe22dd58a587bf3332e7fd362430a7ef3986b16"},
 | 
				
			||||||
  "html_entities": {:hex, :html_entities, "0.5.1", "1c9715058b42c35a2ab65edc5b36d0ea66dd083767bef6e3edb57870ef556549", [:mix], [], "hexpm", "30efab070904eb897ff05cd52fa61c1025d7f8ef3a9ca250bc4e6513d16c32de"},
 | 
					  "html_entities": {:hex, :html_entities, "0.5.2", "9e47e70598da7de2a9ff6af8758399251db6dbb7eebe2b013f2bbd2515895c3c", [:mix], [], "hexpm", "c53ba390403485615623b9531e97696f076ed415e8d8058b1dbaa28181f4fdcc"},
 | 
				
			||||||
  "html_sanitize_ex": {:hex, :html_sanitize_ex, "1.0.1", "2572e7122c78ab7e57b613e7c7f5e42bf9b3c25e430e32f23f1413d86db8a0af", [:mix], [{:mochiweb, "~> 2.12.2", [hex: :mochiweb, repo: "hexpm", optional: false]}], "hexpm", "c334e2835e094fb9c04658bd4cfc7533fa51a8f56f11343c57ab9cb2a01d8613"},
 | 
					  "html_sanitize_ex": {:hex, :html_sanitize_ex, "1.4.1", "e8a67da405fe9f0d1be121a40a60f70811192033a5b8d00a95dddd807f5e053e", [:mix], [{:mochiweb, "~> 2.15", [hex: :mochiweb, repo: "hexpm", optional: false]}], "hexpm", "68d92656f47cd73598c45ad2394561f025c8c65d146001b955fd7b517858962a"},
 | 
				
			||||||
  "hut": {:hex, :hut, "1.3.0", "71f2f054e657c03f959cf1acc43f436ea87580696528ca2a55c8afb1b06c85e7", [:"erlang.mk", :rebar, :rebar3], [], "hexpm", "7e15d28555d8a1f2b5a3a931ec120af0753e4853a4c66053db354f35bf9ab563"},
 | 
					  "hut": {:hex, :hut, "1.3.0", "71f2f054e657c03f959cf1acc43f436ea87580696528ca2a55c8afb1b06c85e7", [:"erlang.mk", :rebar, :rebar3], [], "hexpm", "7e15d28555d8a1f2b5a3a931ec120af0753e4853a4c66053db354f35bf9ab563"},
 | 
				
			||||||
  "idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"},
 | 
					  "idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"},
 | 
				
			||||||
  "jason": {:hex, :jason, "1.2.2", "ba43e3f2709fd1aa1dce90aaabfd039d000469c05c56f0b8e31978e03fa39052", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "18a228f5f0058ee183f29f9eae0805c6e59d61c3b006760668d8d18ff0d12179"},
 | 
					  "jason": {:hex, :jason, "1.2.2", "ba43e3f2709fd1aa1dce90aaabfd039d000469c05c56f0b8e31978e03fa39052", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "18a228f5f0058ee183f29f9eae0805c6e59d61c3b006760668d8d18ff0d12179"},
 | 
				
			||||||
| 
						 | 
					@ -49,7 +49,7 @@
 | 
				
			||||||
  "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"},
 | 
					  "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"},
 | 
				
			||||||
  "mime": {:hex, :mime, "1.6.0", "dabde576a497cef4bbdd60aceee8160e02a6c89250d6c0b29e56c0dfb00db3d2", [:mix], [], "hexpm", "31a1a8613f8321143dde1dafc36006a17d28d02bdfecb9e95a880fa7aabd19a7"},
 | 
					  "mime": {:hex, :mime, "1.6.0", "dabde576a497cef4bbdd60aceee8160e02a6c89250d6c0b29e56c0dfb00db3d2", [:mix], [], "hexpm", "31a1a8613f8321143dde1dafc36006a17d28d02bdfecb9e95a880fa7aabd19a7"},
 | 
				
			||||||
  "mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"},
 | 
					  "mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"},
 | 
				
			||||||
  "mochiweb": {:hex, :mochiweb, "2.12.2", "80804ad342afa3d7f3524040d4eed66ce74b17a555de454ac85b07c479928e46", [:make, :rebar], [], "hexpm", "d3e681d4054b74a96cf2efcd09e94157ab83a5f55ddc4ce69f90b8144673bd7a"},
 | 
					  "mochiweb": {:hex, :mochiweb, "2.20.1", "e4dbd0ed716f076366ecf62ada5755a844e1d95c781e8c77df1d4114be868cdf", [:rebar3], [], "hexpm", "d1aeee7870470d2fa9eae0b3d5ab6c33801aa2d82b10e9dade885c5c921b36aa"},
 | 
				
			||||||
  "mock": {:hex, :mock, "0.3.5", "feb81f52b8dcf0a0d65001d2fec459f6b6a8c22562d94a965862f6cc066b5431", [:mix], [{:meck, "~> 0.8.13", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm", "6fae404799408300f863550392635d8f7e3da6b71abdd5c393faf41b131c8728"},
 | 
					  "mock": {:hex, :mock, "0.3.5", "feb81f52b8dcf0a0d65001d2fec459f6b6a8c22562d94a965862f6cc066b5431", [:mix], [{:meck, "~> 0.8.13", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm", "6fae404799408300f863550392635d8f7e3da6b71abdd5c393faf41b131c8728"},
 | 
				
			||||||
  "neotomex": {:hex, :neotomex, "0.1.7", "64f76513653aa87ea7abdde0fd600e56955d838020a13d88f2bf334c88ac3e7a", [:mix], [], "hexpm", "4b87b8f614d1cd89dc8ba80ba0e559bedb3ebf6f6d74cd774fcfdd215e861445"},
 | 
					  "neotomex": {:hex, :neotomex, "0.1.7", "64f76513653aa87ea7abdde0fd600e56955d838020a13d88f2bf334c88ac3e7a", [:mix], [], "hexpm", "4b87b8f614d1cd89dc8ba80ba0e559bedb3ebf6f6d74cd774fcfdd215e861445"},
 | 
				
			||||||
  "nimble_parsec": {:hex, :nimble_parsec, "1.1.0", "3a6fca1550363552e54c216debb6a9e95bd8d32348938e13de5eda962c0d7f89", [:mix], [], "hexpm", "08eb32d66b706e913ff748f11694b17981c0b04a33ef470e33e11b3d3ac8f54b"},
 | 
					  "nimble_parsec": {:hex, :nimble_parsec, "1.1.0", "3a6fca1550363552e54c216debb6a9e95bd8d32348938e13de5eda962c0d7f89", [:mix], [], "hexpm", "08eb32d66b706e913ff748f11694b17981c0b04a33ef470e33e11b3d3ac8f54b"},
 | 
				
			||||||
| 
						 | 
					@ -58,10 +58,10 @@
 | 
				
			||||||
  "phoenix": {:hex, :phoenix, "1.5.9", "a6368d36cfd59d917b37c44386e01315bc89f7609a10a45a22f47c007edf2597", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_html, "~> 2.13 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.2", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.1.2 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7e4bce20a67c012f1fbb0af90e5da49fa7bf0d34e3a067795703b74aef75427d"},
 | 
					  "phoenix": {:hex, :phoenix, "1.5.9", "a6368d36cfd59d917b37c44386e01315bc89f7609a10a45a22f47c007edf2597", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_html, "~> 2.13 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.2", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.1.2 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7e4bce20a67c012f1fbb0af90e5da49fa7bf0d34e3a067795703b74aef75427d"},
 | 
				
			||||||
  "phoenix_ecto": {:hex, :phoenix_ecto, "4.1.0", "a044d0756d0464c5a541b4a0bf4bcaf89bffcaf92468862408290682c73ae50d", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.9", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "c5e666a341ff104d0399d8f0e4ff094559b2fde13a5985d4cb5023b2c2ac558b"},
 | 
					  "phoenix_ecto": {:hex, :phoenix_ecto, "4.1.0", "a044d0756d0464c5a541b4a0bf4bcaf89bffcaf92468862408290682c73ae50d", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.9", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "c5e666a341ff104d0399d8f0e4ff094559b2fde13a5985d4cb5023b2c2ac558b"},
 | 
				
			||||||
  "phoenix_html": {:hex, :phoenix_html, "2.14.3", "51f720d0d543e4e157ff06b65de38e13303d5778a7919bcc696599e5934271b8", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "efd697a7fff35a13eeeb6b43db884705cba353a1a41d127d118fda5f90c8e80f"},
 | 
					  "phoenix_html": {:hex, :phoenix_html, "2.14.3", "51f720d0d543e4e157ff06b65de38e13303d5778a7919bcc696599e5934271b8", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "efd697a7fff35a13eeeb6b43db884705cba353a1a41d127d118fda5f90c8e80f"},
 | 
				
			||||||
  "phoenix_html_sanitizer": {:hex, :phoenix_html_sanitizer, "1.0.2", "e2c8cfbc83660e362753de127cc957bec3442a8aecdf271fb65a684a906fccf5", [:mix], [{:html_sanitize_ex, "~> 1.0.0", [hex: :html_sanitize_ex, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}], "hexpm", "47aebb08fa954b7ad95f295fb701df9800ee3a489212119c9c6074a65e1e5a10"},
 | 
					  "phoenix_html_sanitizer": {:hex, :phoenix_html_sanitizer, "1.1.0", "ea9e1162217621208ba6b2951a24abe2c06b39347f65c22c31312f9f5ac0fa75", [:mix], [{:html_sanitize_ex, "~> 1.1", [hex: :html_sanitize_ex, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}], "hexpm", "089f28f0592d58f7cf1f032b89c13e873dc73c77a2ccf3386aee976c6ff077c9"},
 | 
				
			||||||
  "phoenix_live_dashboard": {:hex, :phoenix_live_dashboard, "0.2.6", "1b4e1b7d797386b7f9d70d2af931dc9843a5f2f2423609d22cef1eec4e4dba7d", [:mix], [{:phoenix_html, "~> 2.14.1 or ~> 2.15", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.13.1", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.4.0 or ~> 0.5.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "b20dcad98c4ca63d38a7f5e7a40936e1e8e9da983d3d722b88ae33afb866c9ca"},
 | 
					  "phoenix_live_dashboard": {:hex, :phoenix_live_dashboard, "0.4.0", "87990e68b60213d7487e65814046f9a2bed4a67886c943270125913499b3e5c3", [:mix], [{:ecto_psql_extras, "~> 0.4.1 or ~> 0.5", [hex: :ecto_psql_extras, repo: "hexpm", optional: true]}, {:phoenix_html, "~> 2.14.1 or ~> 2.15", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.15.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.4.0 or ~> 0.5.0 or ~> 0.6.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "8d52149e58188e9e4497cc0d8900ab94d9b66f96998ec38c47c7a4f8f4f50e57"},
 | 
				
			||||||
  "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.2.4", "940c0344b1d66a2e46eef02af3a70e0c5bb45a4db0bf47917add271b76cd3914", [: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", "38f9308357dea4cc77f247e216da99fcb0224e05ada1469167520bed4cb8cccd"},
 | 
					  "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.2.4", "940c0344b1d66a2e46eef02af3a70e0c5bb45a4db0bf47917add271b76cd3914", [: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", "38f9308357dea4cc77f247e216da99fcb0224e05ada1469167520bed4cb8cccd"},
 | 
				
			||||||
  "phoenix_live_view": {:hex, :phoenix_live_view, "0.13.3", "2186c55cc7c54ca45b97c6f28cfd267d1c61b5f205f3c83533704cd991bdfdec", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.4.17 or ~> 1.5.2", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14", [hex: :phoenix_html, repo: "hexpm", optional: false]}], "hexpm", "c6309a7da2e779cb9cdf2fb603d75f38f49ef324bedc7a81825998bd1744ff8a"},
 | 
					  "phoenix_live_view": {:hex, :phoenix_live_view, "0.15.7", "09720b8e5151b3ca8ef739cd7626d4feb987c69ba0b509c9bbdb861d5a365881", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.5.7", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 0.5", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "3a756cf662420272d0f1b3b908cce5222163b5a95aa9bab404f9d29aff53276e"},
 | 
				
			||||||
  "phoenix_pubsub": {:hex, :phoenix_pubsub, "2.0.0", "a1ae76717bb168cdeb10ec9d92d1480fec99e3080f011402c0a2d68d47395ffb", [:mix], [], "hexpm", "c52d948c4f261577b9c6fa804be91884b381a7f8f18450c5045975435350f771"},
 | 
					  "phoenix_pubsub": {:hex, :phoenix_pubsub, "2.0.0", "a1ae76717bb168cdeb10ec9d92d1480fec99e3080f011402c0a2d68d47395ffb", [:mix], [], "hexpm", "c52d948c4f261577b9c6fa804be91884b381a7f8f18450c5045975435350f771"},
 | 
				
			||||||
  "php_serializer": {:hex, :php_serializer, "0.9.2", "59c5fd6bd3096671fd89358fb8229341ac7423b50ad8d45a15213b02ea2edab2", [:mix], [], "hexpm", "34eb835a460944f7fc216773b363c02e7dcf8ac0390c9e9ccdbd92b31a7ca59a"},
 | 
					  "php_serializer": {:hex, :php_serializer, "0.9.2", "59c5fd6bd3096671fd89358fb8229341ac7423b50ad8d45a15213b02ea2edab2", [:mix], [], "hexpm", "34eb835a460944f7fc216773b363c02e7dcf8ac0390c9e9ccdbd92b31a7ca59a"},
 | 
				
			||||||
  "plug": {:hex, :plug, "1.11.1", "f2992bac66fdae679453c9e86134a4201f6f43a687d8ff1cd1b2862d53c80259", [:mix], [{:mime, "~> 1.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", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "23524e4fefbb587c11f0833b3910bfb414bf2e2534d61928e920f54e3a1b881f"},
 | 
					  "plug": {:hex, :plug, "1.11.1", "f2992bac66fdae679453c9e86134a4201f6f43a687d8ff1cd1b2862d53c80259", [:mix], [{:mime, "~> 1.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", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "23524e4fefbb587c11f0833b3910bfb414bf2e2534d61928e920f54e3a1b881f"},
 | 
				
			||||||
| 
						 | 
					@ -76,7 +76,7 @@
 | 
				
			||||||
  "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.6", "cf344f5692c82d2cd7554f5ec8fd961548d4fd09e7d22f5b62482e5aeaebd4b0", [:make, :mix, :rebar3], [], "hexpm", "bdb0d2471f453c88ff3908e7686f86f9be327d065cc1ec16fa4540197ea04680"},
 | 
					  "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.6", "cf344f5692c82d2cd7554f5ec8fd961548d4fd09e7d22f5b62482e5aeaebd4b0", [:make, :mix, :rebar3], [], "hexpm", "bdb0d2471f453c88ff3908e7686f86f9be327d065cc1ec16fa4540197ea04680"},
 | 
				
			||||||
  "swarm": {:hex, :swarm, "3.4.0", "64f8b30055d74640d2186c66354b33b999438692a91be275bb89cdc7e401f448", [:mix], [{:gen_state_machine, "~> 2.0", [hex: :gen_state_machine, repo: "hexpm", optional: false]}, {:libring, "~> 1.0", [hex: :libring, repo: "hexpm", optional: false]}], "hexpm", "94884f84783fc1ba027aba8fe8a7dae4aad78c98e9f9c76667ec3471585c08c6"},
 | 
					  "swarm": {:hex, :swarm, "3.4.0", "64f8b30055d74640d2186c66354b33b999438692a91be275bb89cdc7e401f448", [:mix], [{:gen_state_machine, "~> 2.0", [hex: :gen_state_machine, repo: "hexpm", optional: false]}, {:libring, "~> 1.0", [hex: :libring, repo: "hexpm", optional: false]}], "hexpm", "94884f84783fc1ba027aba8fe8a7dae4aad78c98e9f9c76667ec3471585c08c6"},
 | 
				
			||||||
  "telemetry": {:hex, :telemetry, "0.4.3", "a06428a514bdbc63293cd9a6263aad00ddeb66f608163bdec7c8995784080818", [:rebar3], [], "hexpm", "eb72b8365ffda5bed68a620d1da88525e326cb82a75ee61354fc24b844768041"},
 | 
					  "telemetry": {:hex, :telemetry, "0.4.3", "a06428a514bdbc63293cd9a6263aad00ddeb66f608163bdec7c8995784080818", [:rebar3], [], "hexpm", "eb72b8365ffda5bed68a620d1da88525e326cb82a75ee61354fc24b844768041"},
 | 
				
			||||||
  "telemetry_metrics": {:hex, :telemetry_metrics, "0.5.0", "1b796e74add83abf844e808564275dfb342bcc930b04c7577ab780e262b0d998", [:mix], [{:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "31225e6ce7a37a421a0a96ec55244386aec1c190b22578bd245188a4a33298fd"},
 | 
					  "telemetry_metrics": {:hex, :telemetry_metrics, "0.6.0", "da9d49ee7e6bb1c259d36ce6539cd45ae14d81247a2b0c90edf55e2b50507f7b", [:mix], [{:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "5cfe67ad464b243835512aa44321cee91faed6ea868d7fb761d7016e02915c3d"},
 | 
				
			||||||
  "telemetry_poller": {:hex, :telemetry_poller, "0.5.1", "21071cc2e536810bac5628b935521ff3e28f0303e770951158c73eaaa01e962a", [:rebar3], [{:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "4cab72069210bc6e7a080cec9afffad1b33370149ed5d379b81c7c5f0c663fd4"},
 | 
					  "telemetry_poller": {:hex, :telemetry_poller, "0.5.1", "21071cc2e536810bac5628b935521ff3e28f0303e770951158c73eaaa01e962a", [:rebar3], [{:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "4cab72069210bc6e7a080cec9afffad1b33370149ed5d379b81c7c5f0c663fd4"},
 | 
				
			||||||
  "timex": {:hex, :timex, "3.6.2", "845cdeb6119e2fef10751c0b247b6c59d86d78554c83f78db612e3290f819bc2", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 0.1.8 or ~> 0.5 or ~> 1.0.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "26030b46199d02a590be61c2394b37ea25a3664c02fafbeca0b24c972025d47a"},
 | 
					  "timex": {:hex, :timex, "3.6.2", "845cdeb6119e2fef10751c0b247b6c59d86d78554c83f78db612e3290f819bc2", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 0.1.8 or ~> 0.5 or ~> 1.0.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "26030b46199d02a590be61c2394b37ea25a3664c02fafbeca0b24c972025d47a"},
 | 
				
			||||||
  "tzdata": {:hex, :tzdata, "1.0.3", "73470ad29dde46e350c60a66e6b360d3b99d2d18b74c4c349dbebbc27a09a3eb", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "a6e1ee7003c4d04ecbd21dd3ec690d4c6662db5d3bbdd7262d53cdf5e7c746c1"},
 | 
					  "tzdata": {:hex, :tzdata, "1.0.3", "73470ad29dde46e350c60a66e6b360d3b99d2d18b74c4c349dbebbc27a09a3eb", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "a6e1ee7003c4d04ecbd21dd3ec690d4c6662db5d3bbdd7262d53cdf5e7c746c1"},
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										14332
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										14332
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										12
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								package.json
									
									
									
									
									
								
							| 
						 | 
					@ -1,6 +1,6 @@
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  "name": "@mythic-insight/legendary",
 | 
					  "name": "@mythic-insight/legendary",
 | 
				
			||||||
  "version": "2.10.0",
 | 
					  "version": "2.11.2",
 | 
				
			||||||
  "private": true,
 | 
					  "private": true,
 | 
				
			||||||
  "description": "The Legendary Phoenix Boilerplate.",
 | 
					  "description": "The Legendary Phoenix Boilerplate.",
 | 
				
			||||||
  "main": "index.js",
 | 
					  "main": "index.js",
 | 
				
			||||||
| 
						 | 
					@ -19,14 +19,16 @@
 | 
				
			||||||
  "homepage": "https://gitlab.com/mythic-insight/legendary#readme",
 | 
					  "homepage": "https://gitlab.com/mythic-insight/legendary#readme",
 | 
				
			||||||
  "devDependencies": {
 | 
					  "devDependencies": {
 | 
				
			||||||
    "@google/semantic-release-replace-plugin": "^1.0.2",
 | 
					    "@google/semantic-release-replace-plugin": "^1.0.2",
 | 
				
			||||||
    "@semantic-release/commit-analyzer": "^6.1.0",
 | 
					    "@semantic-release/commit-analyzer": "^8.0.1",
 | 
				
			||||||
    "@semantic-release/exec": "^5.0.0",
 | 
					    "@semantic-release/exec": "^5.0.0",
 | 
				
			||||||
    "@semantic-release/git": "^7.0.8",
 | 
					    "@semantic-release/git": "^9.0.0",
 | 
				
			||||||
    "@semantic-release/npm": "^7.0.9",
 | 
					    "@semantic-release/npm": "^7.1.3",
 | 
				
			||||||
    "dot": "^1.1.3",
 | 
					    "dot": "^1.1.3",
 | 
				
			||||||
    "semantic-release": "^15.14.0"
 | 
					    "http-proxy": "^1.18.1",
 | 
				
			||||||
 | 
					    "semantic-release": "^17.4.4"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "release": {
 | 
					  "release": {
 | 
				
			||||||
 | 
					    "branch": "master",
 | 
				
			||||||
    "plugins": [
 | 
					    "plugins": [
 | 
				
			||||||
      "@semantic-release/commit-analyzer"
 | 
					      "@semantic-release/commit-analyzer"
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
#!/bin/sh
 | 
					#!/bin/bash
 | 
				
			||||||
 | 
					
 | 
				
			||||||
set -x
 | 
					set -x
 | 
				
			||||||
set -e
 | 
					set -e
 | 
				
			||||||
| 
						 | 
					@ -6,10 +6,10 @@ set -e
 | 
				
			||||||
if ! brew -v &> /dev/null
 | 
					if ! brew -v &> /dev/null
 | 
				
			||||||
then
 | 
					then
 | 
				
			||||||
  echo "WARNING: Cannot find brew. Skipping brewfile installation."
 | 
					  echo "WARNING: Cannot find brew. Skipping brewfile installation."
 | 
				
			||||||
  export KERL_CONFIGURE_OPTIONS="--disable-hipe"
 | 
					  export KERL_CONFIGURE_OPTIONS="$KERL_CONFIGURE_OPTIONS --disable-hipe"
 | 
				
			||||||
else
 | 
					else
 | 
				
			||||||
  brew bundle
 | 
					  brew bundle
 | 
				
			||||||
  export KERL_CONFIGURE_OPTIONS="--disable-hipe --with-ssl=$(brew --prefix openssl)"
 | 
					  export KERL_CONFIGURE_OPTIONS="$KERL_CONFIGURE_OPTIONS --disable-hipe --with-ssl=$(brew --prefix openssl)"
 | 
				
			||||||
fi
 | 
					fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if ! which asdf &> /dev/null
 | 
					if ! which asdf &> /dev/null
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
#!/bin/sh
 | 
					#!/bin/bash
 | 
				
			||||||
 | 
					
 | 
				
			||||||
set -x
 | 
					set -x
 | 
				
			||||||
set -e
 | 
					set -e
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										5
									
								
								script/env-ci
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										5
									
								
								script/env-ci
									
									
									
									
									
										Executable file
									
								
							| 
						 | 
					@ -0,0 +1,5 @@
 | 
				
			||||||
 | 
					#!/usr/bin/env node
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const envCi = require('env-ci');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					console.log(envCi());
 | 
				
			||||||
							
								
								
									
										34
									
								
								script/proxy
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										34
									
								
								script/proxy
									
									
									
									
									
										Executable file
									
								
							| 
						 | 
					@ -0,0 +1,34 @@
 | 
				
			||||||
 | 
					#!/usr/bin/env node
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var http = require('http'),
 | 
				
			||||||
 | 
					    httpProxy = require('http-proxy');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Create a proxy server with custom application logic
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					var proxy = new httpProxy.createProxyServer({});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var proxyServer = http.createServer(function (req, res) {
 | 
				
			||||||
 | 
					  proxy.web(req, res, {
 | 
				
			||||||
 | 
					    target: {
 | 
				
			||||||
 | 
					      host: 'localhost',
 | 
				
			||||||
 | 
					      port: 4001
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Listen to the `upgrade` event and proxy the
 | 
				
			||||||
 | 
					// WebSocket requests as well.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					proxyServer.on('upgrade', function (req, socket, head) {
 | 
				
			||||||
 | 
					  proxy.ws(req, socket, head, {
 | 
				
			||||||
 | 
					    target: {
 | 
				
			||||||
 | 
					      host: 'localhost',
 | 
				
			||||||
 | 
					      port: 4001
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					console.log("listening on port 4000")
 | 
				
			||||||
 | 
					proxyServer.listen(4000);
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
#!/bin/sh
 | 
					#!/bin/bash
 | 
				
			||||||
 | 
					
 | 
				
			||||||
set -x
 | 
					set -x
 | 
				
			||||||
set -e
 | 
					set -e
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										33
									
								
								script/server-clustered
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										33
									
								
								script/server-clustered
									
									
									
									
									
										Executable file
									
								
							| 
						 | 
					@ -0,0 +1,33 @@
 | 
				
			||||||
 | 
					#!/usr/bin/env bash
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					set -x
 | 
				
			||||||
 | 
					set -e
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DIR_PATH=$(dirname $0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					$DIR_PATH/update
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					trap_with_arg() { # from https://stackoverflow.com/a/2183063/804678
 | 
				
			||||||
 | 
					  local func="$1"; shift
 | 
				
			||||||
 | 
					  for sig in "$@"; do
 | 
				
			||||||
 | 
					    trap "$func $sig" "$sig"
 | 
				
			||||||
 | 
					  done
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					stop() {
 | 
				
			||||||
 | 
					  trap - SIGINT EXIT
 | 
				
			||||||
 | 
					  printf '\n%s\n' "received $1, killing child processes"
 | 
				
			||||||
 | 
					  kill -s SIGINT 0
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					trap_with_arg 'stop' EXIT SIGINT SIGTERM SIGHUP
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CLUSTER_COOKIE=`openssl rand -hex 8`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					PORT=4002 elixir --sname two   --cookie $CLUSTER_COOKIE -S mix phx.server 2>/dev/null &
 | 
				
			||||||
 | 
					PORT=4003 elixir --sname three --cookie $CLUSTER_COOKIE -S mix phx.server 2>/dev/null &
 | 
				
			||||||
 | 
					PORT=4004 elixir --sname four  --cookie $CLUSTER_COOKIE -S mix phx.server 2>/dev/null &
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					$DIR_PATH/proxy &
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					PORT=4001 iex    --sname one --cookie $CLUSTER_COOKIE -S mix phx.server
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
#!/bin/sh
 | 
					#!/bin/bash
 | 
				
			||||||
 | 
					
 | 
				
			||||||
set -x
 | 
					set -x
 | 
				
			||||||
set -e
 | 
					set -e
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
#!/bin/sh
 | 
					#!/bin/bash
 | 
				
			||||||
 | 
					
 | 
				
			||||||
set -x
 | 
					set -x
 | 
				
			||||||
set -e
 | 
					set -e
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue