Compare commits

..

10 commits

61 changed files with 1937 additions and 1460 deletions

45
.dockerignore Normal file
View file

@ -0,0 +1,45 @@
# This file excludes paths from the Docker build context.
#
# By default, Docker's build context includes all files (and folders) in the
# current directory. Even if a file isn't copied into the container it is still sent to
# the Docker daemon.
#
# There are multiple reasons to exclude files from the build context:
#
# 1. Prevent nested folders from being copied into the container (ex: exclude
# /assets/node_modules when copying /assets)
# 2. Reduce the size of the build context and improve build time (ex. /build, /deps, /doc)
# 3. Avoid sending files containing sensitive information
#
# More information on using .dockerignore is available here:
# https://docs.docker.com/engine/reference/builder/#dockerignore-file
.dockerignore
# Ignore git, but keep git HEAD and refs to access current commit hash if needed:
#
# $ cat .git/HEAD | awk '{print ".git/"$2}' | xargs cat
# d0b8727759e1e0e7aa3d41707d12376e373d5ecc
.git
!.git/HEAD
!.git/refs
# Common development/test artifacts
/cover/
/doc/
/test/
/tmp/
.elixir_ls
# Mix artifacts
/_build/
/deps/
*.ez
# Generated on crash by the VM
erl_crash.dump
# Static artifacts - These should be fetched and built inside the Docker image
/assets/node_modules/
/priv/static/assets/
/priv/static/cache_manifest.json

1
.gitignore vendored
View file

@ -50,3 +50,4 @@ npm-debug.log
# dev # dev
TODO.md TODO.md
NOTES.md

View file

@ -1,31 +1,96 @@
# ./Dockerfile # Find eligible builder and runner images on Docker Hub. We use Ubuntu/Debian instead of
# Alpine to avoid DNS resolution issues in production.
#
# https://hub.docker.com/r/hexpm/elixir/tags?page=1&name=ubuntu
# https://hub.docker.com/_/ubuntu?tab=tags
#
#
# This file is based on these images:
#
# - https://hub.docker.com/r/hexpm/elixir/tags - for the build image
# - https://hub.docker.com/_/debian?tab=tags&page=1&name=bullseye-20230109-slim - for the release image
# - https://pkgs.org/ - resource for finding needed packages
# - Ex: hexpm/elixir:1.14.3-erlang-25.2.1-debian-bullseye-20230109-slim
#
ARG ELIXIR_VERSION=1.14.3
ARG OTP_VERSION=25.2.1
ARG DEBIAN_VERSION=bullseye-20230109-slim
# Extend from the official Elixir image ARG BUILDER_IMAGE="hexpm/elixir:${ELIXIR_VERSION}-erlang-${OTP_VERSION}-debian-${DEBIAN_VERSION}"
FROM elixir:1.13.4-otp-25-alpine ARG RUNNER_IMAGE="debian:${DEBIAN_VERSION}"
# # install the package postgresql-client to run pg_isready within entrypoint script FROM ${BUILDER_IMAGE} as builder
# RUN apt-get update && \
# apt-get install -y postgresql-client
# Create app directory and copy the Elixir project into it # install build dependencies
RUN mkdir /app RUN apt-get update -y && apt-get install -y build-essential git curl && \
COPY . /app curl -fsSL https://deb.nodesource.com/setup_19.x | bash - && \
apt-get install -y nodejs && apt-get clean && \
rm -f /var/lib/apt/lists/*_* && npm i -g npm
# prepare build dir
WORKDIR /app WORKDIR /app
# Install the build tools we'll need # install hex + rebar
RUN apk update && \ RUN mix local.hex --force && \
apk upgrade --no-cache && \ mix local.rebar --force
apk add --no-cache \
build-base && \
mix local.rebar --force && \
mix local.hex --force
# set build ENV
ENV MIX_ENV="prod"
# The environment to build with # install mix dependencies
ENV MIX_ENV=prod COPY mix.exs mix.lock ./
RUN mix deps.get --only $MIX_ENV
RUN mkdir config
# Get deps and compile # copy compile-time config files before we compile dependencies
RUN mix do deps.get, deps.compile, compile # to ensure any relevant config change will trigger the dependencies
# to be re-compiled.
COPY config/config.exs config/${MIX_ENV}.exs config/
RUN mix deps.compile
# Start command COPY priv priv
CMD = ["/app/entrypoint.sh"]
COPY lib lib
COPY assets assets
# install node modules
RUN npm --prefix assets install
# compile assets
RUN mix assets.deploy
# Compile the release
RUN mix compile
# Changes to config/runtime.exs don't require recompiling the code
COPY config/runtime.exs config/
COPY rel rel
RUN mix release
# start a new build stage so that the final image will only contain
# the compiled release and other runtime necessities
FROM ${RUNNER_IMAGE}
RUN apt-get update -y && apt-get install -y libstdc++6 openssl libncurses5 locales \
&& apt-get clean && rm -f /var/lib/apt/lists/*_*
# Set the locale
RUN sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen && locale-gen
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en
ENV LC_ALL en_US.UTF-8
WORKDIR "/app"
RUN chown nobody /app
# set runner ENV
ENV MIX_ENV="prod"
# Only copy the final release from the build stage
COPY --from=builder --chown=nobody:root /app/_build/${MIX_ENV}/rel/shift73k ./
USER nobody
CMD ["/app/bin/server"]

31
Dockerfile.bak Normal file
View file

@ -0,0 +1,31 @@
# ./Dockerfile
# Extend from the official Elixir image
FROM elixir:1.13.4-otp-25-alpine
# # install the package postgresql-client to run pg_isready within entrypoint script
# RUN apt-get update && \
# apt-get install -y postgresql-client
# Create app directory and copy the Elixir project into it
RUN mkdir /app
COPY . /app
WORKDIR /app
# Install the build tools we'll need
RUN apk update && \
apk upgrade --no-cache && \
apk add --no-cache \
build-base && \
mix local.rebar --force && \
mix local.hex --force
# The environment to build with
ENV MIX_ENV=prod
# Get deps and compile
RUN mix do deps.get, deps.compile, compile
# Start command
CMD = ["/app/entrypoint.sh"]

View file

@ -4,25 +4,86 @@ Calendaring app for shift-worker shift tracking, with support for CSV export and
Written in Elixir & Phoenix LiveView, with Bootstrap v5. Written in Elixir & Phoenix LiveView, with Bootstrap v5.
## Production
To run in production, you'll need to provide several environment variable values:
```bash
MIX_ENV=prod \
PHX_SERVER=true \
TZ=America/New_York \
DB_SOCK=[postgres unix socket path] \
DB_NAME=[postgres db name] \
DB_USER=[postgres db user] \
DB_PASS=[postgres db user password] \
SECRET_KEY_BASE=[phoenix secret key base] \
PHX_HOST=[server fqdn (e.g., shift.73k.us)] \
PORT=4000 \
SMTP_RELAY=[smtp server] \
SMTP_PORT=[smtp port] \
SMTP_USER=[smtp username] \
SMTP_PASS=[smtp user password] \
MAIL_REPLY_TO=reply@73k.us \
MAIL_FROM_FRIENDLY=Shift73k \
MAIL_FROM_ADDR=shift73k@73k.us \
ALLOW_REG=[open for registration? true/false] \
iex -S mix phx.server
```
### Rebuilding assets for production
```bash
# rebuild static assets:
MIX_ENV=prod mix phx.digest.clean --all
rm -rf ./priv/static/*
npm --prefix assets run build
MIX_ENV=prod mix phx.digest
# then do a new commit and push...
```
## TODO ## TODO
- [X] ~~*Proper modal to delete shifts?*~~ [2022-08-14] - [X] ~~*Proper modal to delete shifts?*~~ [2022-08-14]
- [ ] move runtime config out of compile-time config files, to move towards supporting releases - [X] ~~*move runtime config out of compile-time config files, to move towards supporting releases*~~ [2023-01-28]
- [ ] probably need to use `def get_app_config` style functions instead of `@module_var` module variables, ([see this](https://stephenbussey.com/2019/01/03/understanding-compile-time-dependencies-in-elixir-a-bug-hunt.html)) - [ ] bootstrap dark mode?
- [ ] Update tests, which are probably all way out of date. But I also don't care that much for this project... - [ ] update tests, which are way out of date? Also I don't care?
## Deploying ## Deploying with docker
I'm using a dumb & simple docker approach to deploying this now. Nothing automated, the basic steps are: The Dockerfile will enable building a new container. I do it all with docker compose, here's an example compose yml:
1. ensure latest assets are built, digested, and committed to the repo ```yaml
version: '3.9'
```shell services:
# rebuild static assets: shift73k:
rm -rf ./priv/static/* build:
npm --prefix assets run build context: ./shift73k # relative path from docker-compose.yml to shift73k repo
MIX_ENV=prod mix phx.digest network: host
# then do a new commit and push... container_name: www-shift73k
``` restart: unless-stopped
volumes:
2. on server, build a new container, and run it - /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
- /srv/dck/postgres/sock/postgres:/var/run/postgresql # if using unix socket
# env_file: ./shift73k.env # optionally, put your env vars in a separate file
environment:
- PHX_SERVER=true
- TZ=America/New_York
- DB_SOCK=/var/run/postgresql # if using unix socket instead of db url
- DB_NAME=[postgres db name] # if using unix socket instead of db url
- DB_USER=[postgres db user] # if using unix socket instead of db url
- DB_PASS=[postgres db user password] # if using unix socket instead of db url
- SECRET_KEY_BASE=[phoenix secret key base]
- PHX_HOST=[server fqdn (e.g., shift.73k.us)]
- PORT=4000
- SMTP_RELAY=[smtp server]
- SMTP_PORT=[smtp port]
- SMTP_USER=[smtp username]
- SMTP_PASS=[smtp user password]
- MAIL_REPLY_TO=reply@73k.us
- MAIL_FROM_FRIENDLY=Shift73k
- MAIL_FROM_ADDR=shift73k@73k.us
- ALLOW_REG=[open for registration? true/false]
ports:
- 4000:4000
```

2481
assets/package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -11,9 +11,11 @@
"devDependencies": { "devDependencies": {
"@types/node": "^18.6.5", "@types/node": "^18.6.5",
"@types/phoenix": "^1.5.4", "@types/phoenix": "^1.5.4",
"autoprefixer": "^10.4.13",
"cssnano": "^5.1.14",
"phoenix_live_view": "^0.18.11",
"sass": "^1.54.3", "sass": "^1.54.3",
"svg-sprite-generator": "^0.0.7", "vite": "^4.0.4"
"vite": "^3.0.0"
}, },
"dependencies": { "dependencies": {
"@fontsource/lato": "^4.5.9", "@fontsource/lato": "^4.5.9",
@ -22,7 +24,6 @@
"hamburgers": "^1.2.1", "hamburgers": "^1.2.1",
"phoenix": "^1.6.11", "phoenix": "^1.6.11",
"phoenix_html": "^3.2.0", "phoenix_html": "^3.2.0",
"phoenix_live_view": "^0.17.11",
"topbar": "^1.x" "topbar": "^1.x"
} }
} }

View file

@ -0,0 +1,5 @@
module.exports = {
plugins: [require("autoprefixer"),require('cssnano')({
preset: 'default',
})],
};

View file

@ -12,13 +12,6 @@ config :elixir, :time_zone_database, Tzdata.TimeZoneDatabase
config :shift73k, config :shift73k,
ecto_repos: [Shift73k.Repo] ecto_repos: [Shift73k.Repo]
# Custom application global variables
config :shift73k, :app_global_vars,
time_zone: "America/New_York",
mailer_reply_to: "reply_to@example.com",
mailer_from: "app_name@example.com",
allow_registration: :true
# Configures the endpoint # Configures the endpoint
config :shift73k, Shift73kWeb.Endpoint, config :shift73k, Shift73kWeb.Endpoint,
url: [host: "localhost"], url: [host: "localhost"],

View file

@ -72,6 +72,3 @@ config :phoenix, :stacktrace_depth, 20
# Initialize plugs at runtime for faster development compilation # Initialize plugs at runtime for faster development compilation
config :phoenix, :plug_init_mode, :runtime config :phoenix, :plug_init_mode, :runtime
# Import secret config
import_config "dev.secret.exs"

View file

@ -49,7 +49,3 @@ config :logger, level: :info
# force_ssl: [hsts: true] # force_ssl: [hsts: true]
# #
# Check `Plug.SSL` for all available options in `force_ssl`. # Check `Plug.SSL` for all available options in `force_ssl`.
# Finally import the config/prod.secret.exs which loads secrets
# and configuration from environment variables.
import_config "prod.secret.exs"

133
config/runtime.exs Normal file
View file

@ -0,0 +1,133 @@
import Config
# config/runtime.exs is executed for all environments, including
# during releases. It is executed after compilation and before the
# system starts, so it is typically used to load production configuration
# and secrets from environment variables or elsewhere. Do not define
# any compile-time configuration in here, as it won't be applied.
# The block below contains prod specific runtime configuration.
# ## Using releases
#
# If you use `mix release`, you need to explicitly enable the server
# by passing the PHX_SERVER=true when you start it:
#
# PHX_SERVER=true bin/shift73k start
#
# Alternatively, you can use `mix phx.gen.release` to generate a `bin/server`
# script that automatically sets the env var above.
if System.get_env("PHX_SERVER") do
config :shift73k, Shift73kWeb.Endpoint, server: true
end
if config_env() == :prod do
database_sock = System.get_env("DB_SOCK") || :false
database_name = System.get_env("DB_NAME") || :false
database_user = System.get_env("DB_USER") || :false
database_pass = System.get_env("DB_PASS") || :false
database_url = System.get_env("DATABASE_URL") || :false
if (!database_sock && !database_url) do
raise """
environment variable DATABASE_URL is missing.
can also configure with unix socket by providing
DB_SOCK, DB_NAME, DB_USER, and DB_PASS values.
"""
end
if (database_sock) do
if (!database_name) do
raise """
environment variable DB_NAME is missing.
"""
end
if (!database_user) do
raise """
environment variable DB_USER is missing.
"""
end
if (!database_pass) do
raise """
environment variable DB_PASS is missing.
"""
end
end
maybe_ipv6 = if System.get_env("ECTO_IPV6"), do: [:inet6], else: []
if (database_sock) do
config :shift73k, Shift73k.Repo,
username: database_user,
password: database_pass,
socket_dir: database_sock,
database: database_name,
pool_size: String.to_integer(System.get_env("POOL_SIZE") || "10"),
socket_options: maybe_ipv6
else
config :shift73k, Shift73k.Repo,
# ssl: true,
url: database_url,
pool_size: String.to_integer(System.get_env("POOL_SIZE") || "10"),
socket_options: maybe_ipv6
end
# The secret key base is used to sign/encrypt cookies and other secrets.
# A default value is used in config/dev.exs and config/test.exs but you
# want to use a different value for prod and you most likely don't want
# to check this value into version control, so we use an environment
# variable instead.
secret_key_base =
System.get_env("SECRET_KEY_BASE") ||
raise """
environment variable SECRET_KEY_BASE is missing.
You can generate one by calling: mix phx.gen.secret
"""
host = System.get_env("PHX_HOST") || "example.com"
port = String.to_integer(System.get_env("PORT") || "4000")
config :shift73k, Shift73kWeb.Endpoint,
url: [host: host, port: 443, scheme: "https"],
http: [ip: {0, 0, 0, 0}, port: port],
secret_key_base: secret_key_base
# ## Configuring the mailer
#
# In production you need to configure the mailer to use a different adapter.
# Also, you may need to configure the Swoosh API client of your choice if you
# are not using SMTP. Here is an example of the configuration:
#
# config :shift73k, Shift73k.Mailer,
# adapter: Swoosh.Adapters.Mailgun,
# api_key: System.get_env("MAILGUN_API_KEY"),
# domain: System.get_env("MAILGUN_DOMAIN")
#
# For this example you need include a HTTP client required by Swoosh API client.
# Swoosh supports Hackney and Finch out of the box:
#
# config :swoosh, :api_client, Swoosh.ApiClient.Hackney
#
# See https://hexdocs.pm/swoosh/Swoosh.html#module-installation for details.
# Swoosh mailer config
config :shift73k, Shift73k.Mailer,
adapter: Swoosh.Adapters.SMTP,
relay: System.get_env("SMTP_RELAY"),
port: System.get_env("SMTP_PORT"),
username: System.get_env("SMTP_USER"),
password: System.get_env("SMTP_PASS"),
ssl: false,
tls: :always,
auth: :always,
allowed_tls_versions: [:tlsv1, :"tlsv1.1", :"tlsv1.2"],
retries: 1,
no_mx_lookups: false
config :shift73k, :app_global_vars,
time_zone: System.get_env("TZ") || "America/New_York",
mailer_reply_to: System.get_env("MAIL_REPLY_TO") || "reply_to@example.com",
mailer_from: {
System.get_env("MAIL_FROM_FRIENDLY") || "Shift73k",
System.get_env("MAIL_FROM_ADDR") || "app_name@example.com"
},
allow_registration: System.get_env("ALLOW_REG") || :true
end

View file

@ -7,12 +7,32 @@ defmodule Shift73k do
if it comes from the database, an external API or others. if it comes from the database, an external API or others.
""" """
@app_vars Application.compile_env(:shift73k, :app_global_vars, time_zone: "America/New_York")
@app_time_zone @app_vars[:time_zone]
@weekdays [:monday, :tuesday, :wednesday, :thursday, :friday, :saturday, :sunday] @weekdays [:monday, :tuesday, :wednesday, :thursday, :friday, :saturday, :sunday]
def app_time_zone, do: @app_time_zone defp get_app_config_env do
Application.get_env(:shift73k, :app_global_vars,
time_zone: "America/New_York",
allow_registration: :true,
mailer_reply_to: "admin@example.com",
mailer_from: {"Shift73k", "shift73k@example.com"}
)
end
def weekdays, do: @weekdays def weekdays, do: @weekdays
def get_app_time_zone, do:
get_app_config_env() |> Keyword.fetch!(:time_zone) |> IO.inspect(label: "time_zone", pretty: :true)
def get_app_mailer_from, do:
get_app_config_env() |> Keyword.fetch!(:mailer_from) |> IO.inspect(label: "mailer_from", pretty: :true)
def get_app_mailer_reply_to, do:
get_app_config_env() |> Keyword.fetch!(:mailer_reply_to) |> IO.inspect(label: "mailer_reply_to", pretty: :true)
def get_app_allow_reg, do:
get_app_config_env() |> Keyword.fetch!(:allow_registration) |> get_app_allow_reg()
|> IO.inspect(label: "allow_registration", pretty: :true)
def get_app_allow_reg("false"), do: :false
def get_app_allow_reg(:false), do: :false
def get_app_allow_reg(_not_false), do: :true
end end

View file

@ -1,16 +1,13 @@
defmodule Shift73k.Mailer.UserEmail do defmodule Shift73k.Mailer.UserEmail do
import Swoosh.Email import Swoosh.Email
import Shift73k, only: [get_app_mailer_from: 0, get_app_mailer_reply_to: 0]
@mailer_vars Application.compile_env(:shift73k, :app_global_vars,
mailer_reply_to: "admin@example.com",
mailer_from: {"Shift73k", "shift73k@example.com"}
)
def compose(user_email, subject, body_text) do def compose(user_email, subject, body_text) do
new() new()
|> from(@mailer_vars[:mailer_from]) |> from(get_app_mailer_from())
|> to(user_email) |> to(user_email)
|> header("Reply-To", @mailer_vars[:mailer_reply_to]) |> header("Reply-To", get_app_mailer_reply_to())
|> subject(subject) |> subject(subject)
|> text_body(body_text) |> text_body(body_text)
end end

28
lib/shift73k/release.ex Normal file
View file

@ -0,0 +1,28 @@
defmodule Shift73k.Release do
@moduledoc """
Used for executing DB release tasks when run in production without Mix
installed.
"""
@app :shift73k
def migrate do
load_app()
for repo <- repos() do
{:ok, _, _} = Ecto.Migrator.with_repo(repo, &Ecto.Migrator.run(&1, :up, all: true))
end
end
def rollback(repo, version) do
load_app()
{:ok, _, _} = Ecto.Migrator.with_repo(repo, &Ecto.Migrator.run(&1, :down, to: version))
end
defp repos do
Application.fetch_env!(@app, :ecto_repos)
end
defp load_app do
Application.load(@app)
end
end

View file

@ -1,7 +1,7 @@
defmodule Shift73k.Shifts.Templates.ShiftTemplate do defmodule Shift73k.Shifts.Templates.ShiftTemplate do
use Ecto.Schema use Ecto.Schema
import Ecto.Changeset import Ecto.Changeset
import Shift73k, only: [app_time_zone: 0] import Shift73k, only: [get_app_time_zone: 0]
alias Shift73k.Shifts alias Shift73k.Shifts
alias Shift73k.Shifts.Templates.ShiftTemplate alias Shift73k.Shifts.Templates.ShiftTemplate
@ -12,7 +12,7 @@ defmodule Shift73k.Shifts.Templates.ShiftTemplate do
field :subject, :string field :subject, :string
field :description, :string field :description, :string
field :location, :string field :location, :string
field :time_zone, :string, default: app_time_zone() field :time_zone, :string, default: get_app_time_zone()
field :time_start, :time, default: ~T[09:00:00] field :time_start, :time, default: ~T[09:00:00]
field :time_end, :time, default: ~T[17:00:00] field :time_end, :time, default: ~T[17:00:00]

View file

@ -46,7 +46,7 @@ defmodule Shift73kWeb do
def live_view do def live_view do
quote do quote do
use Phoenix.LiveView, use Phoenix.LiveView,
layout: {Shift73kWeb.LayoutView, "live.html"} layout: {Shift73kWeb.LayoutView, :live}
unquote(view_helpers()) unquote(view_helpers())
import Shift73kWeb.LiveHelpers import Shift73kWeb.LiveHelpers
@ -100,7 +100,7 @@ defmodule Shift73kWeb do
use Phoenix.HTML use Phoenix.HTML
# Import LiveView helpers (live_render, live_component, live_patch, etc) # Import LiveView helpers (live_render, live_component, live_patch, etc)
import Phoenix.LiveView.Helpers import Phoenix.Component
# Import basic rendering functionality (render, render_layout, etc) # Import basic rendering functionality (render, render_layout, etc)
import Phoenix.View import Phoenix.View

View file

@ -21,27 +21,11 @@ defmodule Shift73kWeb.Endpoint do
# You should set gzip to true if you are running phx.digest # You should set gzip to true if you are running phx.digest
# when deploying your static files in production. # when deploying your static files in production.
# #
# file list generated by simple ls -1 assets/static/ - then copy/paste here # file list generated by simple ls -1 priv/static/ - then copy/paste here
plug Plug.Static, plug Plug.Static,
at: "/", at: "/",
from: :shift73k, from: :shift73k,
gzip: false, gzip: false
only: ~w(assets
android-chrome-192x192.png
android-chrome-512x512.png
apple-touch-icon.png
browserconfig.xml
favicon-16x16.png
favicon-32x32.png
favicon.ico
mstile-144x144.png
mstile-150x150.png
mstile-310x150.png
mstile-310x310.png
mstile-70x70.png
robots.txt
safari-pinned-tab.svg
site.webmanifest)
# For using vite.js in dev, we need to instruct Phoenix to serve files at assets/src over the usual endpoint. This is only necessary in development. # For using vite.js in dev, we need to instruct Phoenix to serve files at assets/src over the usual endpoint. This is only necessary in development.
if Mix.env() == :dev do if Mix.env() == :dev do

View file

@ -1,5 +1,6 @@
defmodule Shift73kWeb.LiveHelpers do defmodule Shift73kWeb.LiveHelpers do
import Phoenix.LiveView import Phoenix.LiveView
import Phoenix.Component
alias Shift73k.Accounts alias Shift73k.Accounts
alias Shift73k.Accounts.User alias Shift73k.Accounts.User

View file

@ -1,6 +1,6 @@
defmodule Shift73kWeb.ShiftAssignLive.Index do defmodule Shift73kWeb.ShiftAssignLive.Index do
use Shift73kWeb, :live_view use Shift73kWeb, :live_view
import Shift73k, only: [app_time_zone: 0] import Shift73k, only: [get_app_time_zone: 0]
alias Shift73k.Repo alias Shift73k.Repo
alias Shift73k.Shifts alias Shift73k.Shifts

View file

@ -127,7 +127,7 @@
disabled: @shift_template.id != @custom_shift.id, disabled: @shift_template.id != @custom_shift.id,
phx_debounce: 250, phx_debounce: 250,
list: "tz_list", list: "tz_list",
placeholder: "Default: #{app_time_zone()}" placeholder: "Default: #{get_app_time_zone()}"
%> %>
<datalist id="tz_list"> <datalist id="tz_list">
<%= for tz_name <- Tzdata.zone_list() do %> <%= for tz_name <- Tzdata.zone_list() do %>

View file

@ -33,7 +33,7 @@
<div class="inner-addon left-addon mb-3"> <div class="inner-addon left-addon mb-3">
<i class="bi bi-map icon is-left text-muted fs-5"></i> <i class="bi bi-map icon is-left text-muted fs-5"></i>
<%= text_input iimf, :time_zone, <%= text_input iimf, :time_zone,
value: Shift73k.app_time_zone(), value: Shift73k.get_app_time_zone(),
class: @tz_valid && "form-control" || "form-control is-invalid", class: @tz_valid && "form-control" || "form-control is-invalid",
phx_debounce: 250, phx_debounce: 250,
aria_describedby: "ics-import-tz-error", aria_describedby: "ics-import-tz-error",

View file

@ -1,6 +1,6 @@
defmodule Shift73kWeb.ShiftTemplateLive.FormComponent do defmodule Shift73kWeb.ShiftTemplateLive.FormComponent do
use Shift73kWeb, :live_component use Shift73kWeb, :live_component
import Shift73k, only: [app_time_zone: 0] import Shift73k, only: [get_app_time_zone: 0]
alias Shift73k.Shifts.Templates alias Shift73k.Shifts.Templates
alias Shift73k.Shifts.Templates.ShiftTemplate alias Shift73k.Shifts.Templates.ShiftTemplate

View file

@ -1,6 +1,6 @@
<div> <div>
<.form let={f} for={@changeset} phx-change="validate" phx-submit="save" phx-target={@myself} id="shift_template-form"> <.form :let={f} for={@changeset} phx-change="validate" phx-submit="save" phx-target={@myself} id="shift_template-form">
<div class="modal-body"> <div class="modal-body">
@ -88,7 +88,7 @@
class: input_class(f, :time_zone, "form-control"), class: input_class(f, :time_zone, "form-control"),
phx_debounce: 250, phx_debounce: 250,
list: "tz_list", list: "tz_list",
placeholder: "Default: #{app_time_zone()}" placeholder: "Default: #{get_app_time_zone()}"
%> %>
<datalist id="tz_list"> <datalist id="tz_list">
<%= for tz_name <- Tzdata.zone_list() do %> <%= for tz_name <- Tzdata.zone_list() do %>

View file

@ -6,7 +6,7 @@
</h2> </h2>
<p class="lead">Create an account to manage your work shifts with us.</p> <p class="lead">Create an account to manage your work shifts with us.</p>
<.form let={f} for={@changeset} phx-change="validate" phx-submit="save" novalidate id="reg_form"> <.form :let={f} for={@changeset} phx-change="validate" phx-submit="save" novalidate id="reg_form">
<%= label f, :email, class: "form-label" %> <%= label f, :email, class: "form-label" %>
<div class="inner-addon left-addon mb-3" phx-feedback-for={input_id(f, :email)}> <div class="inner-addon left-addon mb-3" phx-feedback-for={input_id(f, :email)}>

View file

@ -1,11 +1,11 @@
defmodule Shift73kWeb.UserLive.ResetPassword do defmodule Shift73kWeb.UserLive.ResetPassword do
use Shift73kWeb, :live_view use Shift73kWeb, :live_view
import Shift73k, only: [get_app_allow_reg: 0]
alias Shift73k.Accounts alias Shift73k.Accounts
alias Shift73k.Accounts.User alias Shift73k.Accounts.User
@app_vars Application.compile_env(:shift73k, :app_global_vars, allow_registration: :true)
@app_allow_registration @app_vars[:allow_registration]
@impl true @impl true
def mount(_params, session, socket) do def mount(_params, session, socket) do
@ -41,5 +41,5 @@ defmodule Shift73kWeb.UserLive.ResetPassword do
end end
end end
def allow_registration, do: @app_allow_registration def allow_registration, do: get_app_allow_reg()
end end

View file

@ -6,7 +6,7 @@
</h2> </h2>
<p class="lead">Hi <%= @user.email %> &mdash; tell us your new password, please.</p> <p class="lead">Hi <%= @user.email %> &mdash; tell us your new password, please.</p>
<.form let={f} for={@changeset} phx-change="validate" phx-submit="save" novalidate id="pw_reset_form"> <.form :let={f} for={@changeset} phx-change="validate" phx-submit="save" novalidate id="pw_reset_form">
<%= label f, :password, "New password", class: "form-label" %> <%= label f, :password, "New password", class: "form-label" %>
<div class="inner-addon left-addon mb-3" phx-feedback-for={input_id(f, :password)}> <div class="inner-addon left-addon mb-3" phx-feedback-for={input_id(f, :password)}>

View file

@ -198,7 +198,7 @@ defmodule Shift73kWeb.UserManagementLive.Index do
def dt_out(ndt) do def dt_out(ndt) do
ndt ndt
|> DateTime.from_naive!(Shift73k.app_time_zone()) |> DateTime.from_naive!(Shift73k.get_app_time_zone())
|> Calendar.strftime("%Y %b %-d, %-I:%M %p") |> Calendar.strftime("%Y %b %-d, %-I:%M %p")
end end
end end

View file

@ -243,7 +243,7 @@
<%# page buttons %> <%# page buttons %>
<%= for page_num <- generate_page_list(@page.page_number, @page.total_pages) do %> <%= for page_num <- generate_page_list(@page.page_number, @page.total_pages) do %>
<%= cond do %> <%= cond do %>
<%= page_num < 1 -> %> <% page_num < 1 -> %>
<li class="page-item disabled"> <li class="page-item disabled">
<span class="page-link" aria-hidden="true">&hellip;</span> <span class="page-link" aria-hidden="true">&hellip;</span>
<span class="visually-hidden" role="img" aria-label="ellipses">&hellip;</span> <span class="visually-hidden" role="img" aria-label="ellipses">&hellip;</span>

View file

@ -4,12 +4,11 @@ defmodule Shift73kWeb.EnsureAllowRegistrationPlug do
""" """
import Plug.Conn import Plug.Conn
import Phoenix.Controller import Phoenix.Controller
import Shift73k, only: [get_app_allow_reg: 0]
alias Shift73k.Repo alias Shift73k.Repo
alias Shift73k.Accounts.User alias Shift73k.Accounts.User
@app_vars Application.compile_env(:shift73k, :app_global_vars, allow_registration: :true)
@app_allow_registration @app_vars[:allow_registration]
@doc false @doc false
@spec init(any()) :: any() @spec init(any()) :: any()
@ -19,7 +18,7 @@ defmodule Shift73kWeb.EnsureAllowRegistrationPlug do
@spec call(Conn.t(), atom() | [atom()]) :: Conn.t() @spec call(Conn.t(), atom() | [atom()]) :: Conn.t()
def call(conn, _opts) do def call(conn, _opts) do
# If there aren't even any users, or registration is allowed # If there aren't even any users, or registration is allowed
if !Repo.exists?(User) || @app_allow_registration do if !Repo.exists?(User) || get_app_allow_reg() do
# We will allow registration # We will allow registration
conn conn
else else

View file

@ -12,13 +12,13 @@
<% end %> <% end %>
</li> </li>
<li> <li>
<%= link nav_link_opts(@conn, to: Routes.shift_index_path(@conn, :index), class: "dropdown-item") do %> <%= link nav_link_opts(@conn, to: Routes.shift_template_index_path(@conn, :index), class: "dropdown-item") do %>
<i class="bi bi-card-list me-1"></i> My Scheduled Shifts <i class="bi bi-clock-history me-1"></i> My Shift Templates
<% end %> <% end %>
</li> </li>
<li> <li>
<%= link nav_link_opts(@conn, to: Routes.shift_template_index_path(@conn, :index), class: "dropdown-item") do %> <%= link nav_link_opts(@conn, to: Routes.shift_index_path(@conn, :index), class: "dropdown-item") do %>
<i class="bi bi-clock-history me-1"></i> My Shift Templates <i class="bi bi-card-list me-1"></i> My Scheduled Shifts
<% end %> <% end %>
</li> </li>

View file

@ -5,7 +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"/>
<%= csrf_meta_tag() %> <%= csrf_meta_tag() %>
<%= live_title_tag assigns[:page_title] || "", suffix: assigns[:page_title] && " · Shift73k" || "Shift73k" %> <Phoenix.Component.live_title suffix=" · Shift73k">
<%= assigns[:page_title] || "Hi!" %>
</Phoenix.Component.live_title>
<%= render "_preamble.html", assigns %> <%= render "_preamble.html", assigns %>
<link rel="apple-touch-icon" sizes="180x180" href={Routes.static_path(@conn, "/apple-touch-icon.png")}> <link rel="apple-touch-icon" sizes="180x180" href={Routes.static_path(@conn, "/apple-touch-icon.png")}>
<link rel="icon" type="image/png" sizes="32x32" href={Routes.static_path(@conn, "/favicon-32x32.png")}> <link rel="icon" type="image/png" sizes="32x32" href={Routes.static_path(@conn, "/favicon-32x32.png")}>

View file

@ -1,11 +1,10 @@
defmodule Shift73kWeb.LayoutView do defmodule Shift73kWeb.LayoutView do
use Shift73kWeb, :view use Shift73kWeb, :view
import Shift73k, only: [get_app_allow_reg: 0]
alias Shift73k.Repo alias Shift73k.Repo
alias Shift73k.Accounts.User alias Shift73k.Accounts.User
alias Shift73kWeb.Roles alias Shift73kWeb.Roles
@app_vars Application.compile_env(:shift73k, :app_global_vars, allow_registration: :true)
@app_allow_registration @app_vars[:allow_registration]
# With a Vite.js-based workflow, we will import different asset files in development # With a Vite.js-based workflow, we will import different asset files in development
# and in production builds. Therefore, we will need a way to conditionally render # and in production builds. Therefore, we will need a way to conditionally render
@ -14,7 +13,7 @@ defmodule Shift73kWeb.LayoutView do
@env Mix.env() # remember value at compile time @env Mix.env() # remember value at compile time
def dev_env?, do: @env == :dev def dev_env?, do: @env == :dev
def allow_registration, do: @app_allow_registration def allow_registration, do: get_app_allow_reg()
def nav_link_opts(conn, opts) do def nav_link_opts(conn, opts) do
case Keyword.get(opts, :to) == Phoenix.Controller.current_path(conn) do case Keyword.get(opts, :to) == Phoenix.Controller.current_path(conn) do

View file

@ -1,9 +1,7 @@
defmodule Shift73kWeb.UserConfirmationView do defmodule Shift73kWeb.UserConfirmationView do
use Shift73kWeb, :view use Shift73kWeb, :view
import Shift73k, only: [get_app_allow_reg: 0]
alias Shift73k.Accounts.User alias Shift73k.Accounts.User
@app_vars Application.compile_env(:shift73k, :app_global_vars, allow_registration: :true) def allow_registration, do: get_app_allow_reg()
@app_allow_registration @app_vars[:allow_registration]
def allow_registration, do: @app_allow_registration
end end

View file

@ -1,9 +1,7 @@
defmodule Shift73kWeb.UserResetPasswordView do defmodule Shift73kWeb.UserResetPasswordView do
use Shift73kWeb, :view use Shift73kWeb, :view
import Shift73k, only: [get_app_allow_reg: 0]
alias Shift73k.Accounts.User alias Shift73k.Accounts.User
@app_vars Application.compile_env(:shift73k, :app_global_vars, allow_registration: :true) def allow_registration, do: get_app_allow_reg()
@app_allow_registration @app_vars[:allow_registration]
def allow_registration, do: @app_allow_registration
end end

View file

@ -1,9 +1,7 @@
defmodule Shift73kWeb.UserSessionView do defmodule Shift73kWeb.UserSessionView do
use Shift73kWeb, :view use Shift73kWeb, :view
import Shift73k, only: [get_app_allow_reg: 0]
alias Shift73k.Accounts.User alias Shift73k.Accounts.User
@app_vars Application.compile_env(:shift73k, :app_global_vars, allow_registration: :true) def allow_registration, do: get_app_allow_reg()
@app_allow_registration @app_vars[:allow_registration]
def allow_registration, do: @app_allow_registration
end end

View file

@ -4,7 +4,7 @@ defmodule Shift73k.MixProject do
def project do def project do
[ [
app: :shift73k, app: :shift73k,
version: "0.1.1", version: "0.2.1",
elixir: "~> 1.12", elixir: "~> 1.12",
elixirc_paths: elixirc_paths(Mix.env()), elixirc_paths: elixirc_paths(Mix.env()),
compilers: Mix.compilers(), compilers: Mix.compilers(),
@ -40,7 +40,7 @@ defmodule Shift73k.MixProject do
{:postgrex, ">= 0.0.0"}, {:postgrex, ">= 0.0.0"},
{:phoenix_html, "~> 3.0"}, {:phoenix_html, "~> 3.0"},
{:phoenix_live_reload, "~> 1.2", only: :dev}, {:phoenix_live_reload, "~> 1.2", only: :dev},
{:phoenix_live_view, "~> 0.17.5"}, {:phoenix_live_view, "~> 0.18"},
{:floki, ">= 0.30.0", only: :test}, {:floki, ">= 0.30.0", only: :test},
{:swoosh, "~> 1.7"}, {:swoosh, "~> 1.7"},
{:gen_smtp, "~> 1.2"}, {:gen_smtp, "~> 1.2"},
@ -52,7 +52,7 @@ defmodule Shift73k.MixProject do
{:tzdata, "~> 1.1"}, {:tzdata, "~> 1.1"},
{:nimble_csv, "~> 1.0"}, {:nimble_csv, "~> 1.0"},
{:icalendar, "~> 1.1"}, {:icalendar, "~> 1.1"},
{:httpoison, "~> 1.7"}, {:httpoison, "~> 2.0"},
# Additional packages # Additional packages
@ -71,7 +71,8 @@ defmodule Shift73k.MixProject do
setup: ["deps.get", "ecto.setup", "cmd npm install --prefix assets"], setup: ["deps.get", "ecto.setup", "cmd npm install --prefix assets"],
"ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"], "ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"],
"ecto.reset": ["ecto.drop", "ecto.setup"], "ecto.reset": ["ecto.drop", "ecto.setup"],
test: ["ecto.create --quiet", "ecto.migrate --quiet", "test"] test: ["ecto.create --quiet", "ecto.migrate --quiet", "test"],
"assets.deploy": ["cmd npm --prefix assets run build", "phx.digest"]
] ]
end end
end end

View file

@ -1,6 +1,7 @@
%{ %{
"bcrypt_elixir": {:hex, :bcrypt_elixir, "3.0.1", "9be815469e6bfefec40fa74658ecbbe6897acfb57614df1416eeccd4903f602c", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "486bb95efb645d1efc6794c1ddd776a186a9a713abf06f45708a6ce324fb96cf"}, "bcrypt_elixir": {:hex, :bcrypt_elixir, "3.0.1", "9be815469e6bfefec40fa74658ecbbe6897acfb57614df1416eeccd4903f602c", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "486bb95efb645d1efc6794c1ddd776a186a9a713abf06f45708a6ce324fb96cf"},
"bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm", "7af5c7e09fe1d40f76c8e4f9dd2be7cebd83909f31fee7cd0e9eadc567da8353"}, "bunt": {:hex, :bunt, "0.2.1", "e2d4792f7bc0ced7583ab54922808919518d0e57ee162901a16a1b6664ef3b14", [:mix], [], "hexpm", "a330bfb4245239787b15005e66ae6845c9cd524a288f0d141c148b02603777a5"},
"castore": {:hex, :castore, "1.0.0", "c25cd0794c054ebe6908a86820c8b92b5695814479ec95eeff35192720b71eec", [:mix], [], "hexpm", "577d0e855983a97ca1dfa33cbb8a3b6ece6767397ffb4861514343b078fc284b"},
"certifi": {:hex, :certifi, "2.9.0", "6f2a475689dd47f19fb74334859d460a2dc4e3252a3324bd2111b8f0429e7e21", [:rebar3], [], "hexpm", "266da46bdb06d6c6d35fde799bcb28d36d985d424ad7c08b5bb48f5b5cdd4641"}, "certifi": {:hex, :certifi, "2.9.0", "6f2a475689dd47f19fb74334859d460a2dc4e3252a3324bd2111b8f0429e7e21", [:rebar3], [], "hexpm", "266da46bdb06d6c6d35fde799bcb28d36d985d424ad7c08b5bb48f5b5cdd4641"},
"combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm", "1b1dbc1790073076580d0d1d64e42eae2366583e7aecd455d1215b0d16f2451b"}, "combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm", "1b1dbc1790073076580d0d1d64e42eae2366583e7aecd455d1215b0d16f2451b"},
"comeonin": {:hex, :comeonin, "5.3.3", "2c564dac95a35650e9b6acfe6d2952083d8a08e4a89b93a481acb552b325892e", [:mix], [], "hexpm", "3e38c9c2cb080828116597ca8807bb482618a315bfafd98c90bc22a821cc84df"}, "comeonin": {:hex, :comeonin, "5.3.3", "2c564dac95a35650e9b6acfe6d2952083d8a08e4a89b93a481acb552b325892e", [:mix], [], "hexpm", "3e38c9c2cb080828116597ca8807bb482618a315bfafd98c90bc22a821cc84df"},
@ -8,44 +9,45 @@
"cowboy": {:hex, :cowboy, "2.9.0", "865dd8b6607e14cf03282e10e934023a1bd8be6f6bacf921a7e2a96d800cd452", [:make, :rebar3], [{:cowlib, "2.11.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "2c729f934b4e1aa149aff882f57c6372c15399a20d54f65c8d67bef583021bde"}, "cowboy": {:hex, :cowboy, "2.9.0", "865dd8b6607e14cf03282e10e934023a1bd8be6f6bacf921a7e2a96d800cd452", [:make, :rebar3], [{:cowlib, "2.11.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "2c729f934b4e1aa149aff882f57c6372c15399a20d54f65c8d67bef583021bde"},
"cowboy_telemetry": {:hex, :cowboy_telemetry, "0.4.0", "f239f68b588efa7707abce16a84d0d2acf3a0f50571f8bb7f56a15865aae820c", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7d98bac1ee4565d31b62d59f8823dfd8356a169e7fcbb83831b8a5397404c9de"}, "cowboy_telemetry": {:hex, :cowboy_telemetry, "0.4.0", "f239f68b588efa7707abce16a84d0d2acf3a0f50571f8bb7f56a15865aae820c", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7d98bac1ee4565d31b62d59f8823dfd8356a169e7fcbb83831b8a5397404c9de"},
"cowlib": {:hex, :cowlib, "2.11.0", "0b9ff9c346629256c42ebe1eeb769a83c6cb771a6ee5960bd110ab0b9b872063", [:make, :rebar3], [], "hexpm", "2b3e9da0b21c4565751a6d4901c20d1b4cc25cbb7fd50d91d2ab6dd287bc86a9"}, "cowlib": {:hex, :cowlib, "2.11.0", "0b9ff9c346629256c42ebe1eeb769a83c6cb771a6ee5960bd110ab0b9b872063", [:make, :rebar3], [], "hexpm", "2b3e9da0b21c4565751a6d4901c20d1b4cc25cbb7fd50d91d2ab6dd287bc86a9"},
"credo": {:hex, :credo, "1.6.6", "f51f8d45db1af3b2e2f7bee3e6d3c871737bda4a91bff00c5eec276517d1a19c", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "625520ce0984ee0f9f1f198165cd46fa73c1e59a17ebc520038b8fce056a5bdc"}, "credo": {:hex, :credo, "1.6.7", "323f5734350fd23a456f2688b9430e7d517afb313fbd38671b8a4449798a7854", [:mix], [{:bunt, "~> 0.2.1", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "41e110bfb007f7eda7f897c10bf019ceab9a0b269ce79f015d54b0dcf4fc7dd3"},
"db_connection": {:hex, :db_connection, "2.4.2", "f92e79aff2375299a16bcb069a14ee8615c3414863a6fef93156aee8e86c2ff3", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "4fe53ca91b99f55ea249693a0229356a08f4d1a7931d8ffa79289b145fe83668"}, "db_connection": {:hex, :db_connection, "2.4.3", "3b9aac9f27347ec65b271847e6baeb4443d8474289bd18c1d6f4de655b70c94d", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c127c15b0fa6cfb32eed07465e05da6c815b032508d4ed7c116122871df73c12"},
"decimal": {:hex, :decimal, "2.0.0", "a78296e617b0f5dd4c6caf57c714431347912ffb1d0842e998e9792b5642d697", [:mix], [], "hexpm", "34666e9c55dea81013e77d9d87370fe6cb6291d1ef32f46a1600230b1d44f577"}, "decimal": {:hex, :decimal, "2.0.0", "a78296e617b0f5dd4c6caf57c714431347912ffb1d0842e998e9792b5642d697", [:mix], [], "hexpm", "34666e9c55dea81013e77d9d87370fe6cb6291d1ef32f46a1600230b1d44f577"},
"ecto": {:hex, :ecto, "3.8.4", "e06b8b87e62b27fea17fd2ff6041572ddd10339fd16cdf58446e402c6c90a74b", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "f9244288b8d42db40515463a008cf3f4e0e564bb9c249fe87bf28a6d79fe82d4"}, "ecto": {:hex, :ecto, "3.9.4", "3ee68e25dbe0c36f980f1ba5dd41ee0d3eb0873bccae8aeaf1a2647242bffa35", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "de5f988c142a3aa4ec18b85a4ec34a2390b65b24f02385c1144252ff6ff8ee75"},
"ecto_sql": {:hex, :ecto_sql, "3.8.3", "a7d22c624202546a39d615ed7a6b784580391e65723f2d24f65941b4dd73d471", [:mix], [{:db_connection, "~> 2.5 or ~> 2.4.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.8.4", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0 or ~> 0.16.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "348cb17fb9e6daf6f251a87049eafcb57805e2892e5e6a0f5dea0985d367329b"}, "ecto_sql": {:hex, :ecto_sql, "3.9.2", "34227501abe92dba10d9c3495ab6770e75e79b836d114c41108a4bf2ce200ad5", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.9.2", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "1eb5eeb4358fdbcd42eac11c1fbd87e3affd7904e639d77903c1358b2abd3f70"},
"elixir_make": {:hex, :elixir_make, "0.6.3", "bc07d53221216838d79e03a8019d0839786703129599e9619f4ab74c8c096eac", [:mix], [], "hexpm", "f5cbd651c5678bcaabdbb7857658ee106b12509cd976c2c2fca99688e1daf716"}, "elixir_make": {:hex, :elixir_make, "0.6.3", "bc07d53221216838d79e03a8019d0839786703129599e9619f4ab74c8c096eac", [:mix], [], "hexpm", "f5cbd651c5678bcaabdbb7857658ee106b12509cd976c2c2fca99688e1daf716"},
"file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"}, "file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"},
"floki": {:hex, :floki, "0.33.1", "f20f1eb471e726342b45ccb68edb9486729e7df94da403936ea94a794f072781", [:mix], [{:html_entities, "~> 0.5.0", [hex: :html_entities, repo: "hexpm", optional: false]}], "hexpm", "461035fd125f13fdf30f243c85a0b1e50afbec876cbf1ceefe6fddd2e6d712c6"}, "floki": {:hex, :floki, "0.34.0", "002d0cc194b48794d74711731db004fafeb328fe676976f160685262d43706a8", [:mix], [], "hexpm", "9c3a9f43f40dde00332a589bd9d389b90c1f518aef500364d00636acc5ebc99c"},
"gen_smtp": {:hex, :gen_smtp, "1.2.0", "9cfc75c72a8821588b9b9fe947ae5ab2aed95a052b81237e0928633a13276fd3", [:rebar3], [{:ranch, ">= 1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "5ee0375680bca8f20c4d85f58c2894441443a743355430ff33a783fe03296779"}, "gen_smtp": {:hex, :gen_smtp, "1.2.0", "9cfc75c72a8821588b9b9fe947ae5ab2aed95a052b81237e0928633a13276fd3", [:rebar3], [{:ranch, ">= 1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "5ee0375680bca8f20c4d85f58c2894441443a743355430ff33a783fe03296779"},
"gettext": {:hex, :gettext, "0.20.0", "75ad71de05f2ef56991dbae224d35c68b098dd0e26918def5bb45591d5c8d429", [:mix], [], "hexpm", "1c03b177435e93a47441d7f681a7040bd2a816ece9e2666d1c9001035121eb3d"}, "gettext": {:hex, :gettext, "0.20.0", "75ad71de05f2ef56991dbae224d35c68b098dd0e26918def5bb45591d5c8d429", [:mix], [], "hexpm", "1c03b177435e93a47441d7f681a7040bd2a816ece9e2666d1c9001035121eb3d"},
"hackney": {:hex, :hackney, "1.18.1", "f48bf88f521f2a229fc7bae88cf4f85adc9cd9bcf23b5dc8eb6a1788c662c4f6", [:rebar3], [{:certifi, "~>2.9.0", [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", "a4ecdaff44297e9b5894ae499e9a070ea1888c84afdd1fd9b7b2bc384950128e"}, "hackney": {:hex, :hackney, "1.18.1", "f48bf88f521f2a229fc7bae88cf4f85adc9cd9bcf23b5dc8eb6a1788c662c4f6", [:rebar3], [{:certifi, "~>2.9.0", [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", "a4ecdaff44297e9b5894ae499e9a070ea1888c84afdd1fd9b7b2bc384950128e"},
"html_entities": {:hex, :html_entities, "0.5.2", "9e47e70598da7de2a9ff6af8758399251db6dbb7eebe2b013f2bbd2515895c3c", [:mix], [], "hexpm", "c53ba390403485615623b9531e97696f076ed415e8d8058b1dbaa28181f4fdcc"}, "html_entities": {:hex, :html_entities, "0.5.2", "9e47e70598da7de2a9ff6af8758399251db6dbb7eebe2b013f2bbd2515895c3c", [:mix], [], "hexpm", "c53ba390403485615623b9531e97696f076ed415e8d8058b1dbaa28181f4fdcc"},
"httpoison": {:hex, :httpoison, "1.8.2", "9eb9c63ae289296a544842ef816a85d881d4a31f518a0fec089aaa744beae290", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "2bb350d26972e30c96e2ca74a1aaf8293d61d0742ff17f01e0279fef11599921"}, "httpoison": {:hex, :httpoison, "2.0.0", "d38b091f5e481e45cc700aba8121ce49b66d348122a097c9fbc2dc6876d88090", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "f1253bf455be73a4c3f6ae3407e7e3cf6fc91934093e056d737a0566126e2930"},
"icalendar": {:hex, :icalendar, "1.1.2", "5d0afff5d0143c5bd43f18ae32a777bf0fb9a724543ab05229a460d368f0a5e7", [:mix], [{:timex, "~> 3.4", [hex: :timex, repo: "hexpm", optional: false]}], "hexpm", "2060f8e353fdf3047e95a3f012583dc3c0bbd7ca1010e32ed9e9fc5760ad4292"}, "icalendar": {:hex, :icalendar, "1.1.2", "5d0afff5d0143c5bd43f18ae32a777bf0fb9a724543ab05229a460d368f0a5e7", [:mix], [{:timex, "~> 3.4", [hex: :timex, repo: "hexpm", optional: false]}], "hexpm", "2060f8e353fdf3047e95a3f012583dc3c0bbd7ca1010e32ed9e9fc5760ad4292"},
"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.3.0", "fa6b82a934feb176263ad2df0dbd91bf633d4a46ebfdffea0c8ae82953714946", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "53fc1f51255390e0ec7e50f9cb41e751c260d065dcba2bf0d08dc51a4002c2ac"}, "jason": {:hex, :jason, "1.4.0", "e855647bc964a44e2f67df589ccf49105ae039d4179db7f6271dfd3843dc27e6", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "79a3791085b2a0f743ca04cec0f7be26443738779d09302e01318f97bdb82121"},
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"}, "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"},
"mime": {:hex, :mime, "2.0.3", "3676436d3d1f7b81b5a2d2bd8405f412c677558c81b1c92be58c00562bb59095", [:mix], [], "hexpm", "27a30bf0db44d25eecba73755acf4068cbfe26a4372f9eb3e4ea3a45956bff6b"}, "mime": {:hex, :mime, "2.0.3", "3676436d3d1f7b81b5a2d2bd8405f412c677558c81b1c92be58c00562bb59095", [:mix], [], "hexpm", "27a30bf0db44d25eecba73755acf4068cbfe26a4372f9eb3e4ea3a45956bff6b"},
"mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"}, "mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"},
"nimble_csv": {:hex, :nimble_csv, "1.2.0", "4e26385d260c61eba9d4412c71cea34421f296d5353f914afe3f2e71cce97722", [:mix], [], "hexpm", "d0628117fcc2148178b034044c55359b26966c6eaa8e2ce15777be3bbc91b12a"}, "nimble_csv": {:hex, :nimble_csv, "1.2.0", "4e26385d260c61eba9d4412c71cea34421f296d5353f914afe3f2e71cce97722", [:mix], [], "hexpm", "d0628117fcc2148178b034044c55359b26966c6eaa8e2ce15777be3bbc91b12a"},
"parse_trans": {:hex, :parse_trans, "3.3.1", "16328ab840cc09919bd10dab29e431da3af9e9e7e7e6f0089dd5a2d2820011d8", [:rebar3], [], "hexpm", "07cd9577885f56362d414e8c4c4e6bdf10d43a8767abb92d24cbe8b24c54888b"}, "parse_trans": {:hex, :parse_trans, "3.3.1", "16328ab840cc09919bd10dab29e431da3af9e9e7e7e6f0089dd5a2d2820011d8", [:rebar3], [], "hexpm", "07cd9577885f56362d414e8c4c4e6bdf10d43a8767abb92d24cbe8b24c54888b"},
"phoenix": {:hex, :phoenix, "1.6.11", "29f3c0fd12fa1fc4d4b05e341578e55bc78d96ea83a022587a7e276884d397e4", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 1.0", [hex: :phoenix_view, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.2", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "1664e34f80c25ea4918fbadd957f491225ef601c0e00b4e644b1a772864bfbc2"}, "phoenix": {:hex, :phoenix, "1.6.15", "0a1d96bbc10747fd83525370d691953cdb6f3ccbac61aa01b4acb012474b047d", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 1.0 or ~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.2", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "d70ab9fbf6b394755ea88b644d34d79d8b146e490973151f248cacd122d20672"},
"phoenix_ecto": {:hex, :phoenix_ecto, "4.4.0", "0672ed4e4808b3fbed494dded89958e22fb882de47a97634c0b13e7b0b5f7720", [:mix], [{:ecto, "~> 3.3", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "09864e558ed31ee00bd48fcc1d4fc58ae9678c9e81649075431e69dbabb43cc1"}, "phoenix_ecto": {:hex, :phoenix_ecto, "4.4.0", "0672ed4e4808b3fbed494dded89958e22fb882de47a97634c0b13e7b0b5f7720", [:mix], [{:ecto, "~> 3.3", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "09864e558ed31ee00bd48fcc1d4fc58ae9678c9e81649075431e69dbabb43cc1"},
"phoenix_html": {:hex, :phoenix_html, "3.2.0", "1c1219d4b6cb22ac72f12f73dc5fad6c7563104d083f711c3fcd8551a1f4ae11", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "36ec97ba56d25c0136ef1992c37957e4246b649d620958a1f9fa86165f8bc54f"}, "phoenix_html": {:hex, :phoenix_html, "3.2.0", "1c1219d4b6cb22ac72f12f73dc5fad6c7563104d083f711c3fcd8551a1f4ae11", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "36ec97ba56d25c0136ef1992c37957e4246b649d620958a1f9fa86165f8bc54f"},
"phoenix_live_reload": {:hex, :phoenix_live_reload, "1.3.3", "3a53772a6118d5679bf50fc1670505a290e32a1d195df9e069d8c53ab040c054", [: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", "766796676e5f558dbae5d1bdb066849673e956005e3730dfd5affd7a6da4abac"}, "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.4.1", "2aff698f5e47369decde4357ba91fc9c37c6487a512b41732818f2204a8ef1d3", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "9bffb834e7ddf08467fe54ae58b5785507aaba6255568ae22b4d46e2bb3615ab"},
"phoenix_live_view": {:hex, :phoenix_live_view, "0.17.11", "205f6aa5405648c76f2abcd57716f42fc07d8f21dd8ea7b262dd12b324b50c95", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.1", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7177791944b7f90ed18f5935a6a5f07f760b36f7b3bdfb9d28c57440a3c43f99"}, "phoenix_live_view": {:hex, :phoenix_live_view, "0.18.11", "c50eac83dae6b5488859180422dfb27b2c609de87f4aa5b9c926ecd0501cd44f", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.1", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "76c99a0ffb47cd95bf06a917e74f282a603f3e77b00375f3c2dd95110971b102"},
"phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.1", "ba04e489ef03763bf28a17eb2eaddc2c20c6d217e2150a61e3298b0f4c2012b5", [:mix], [], "hexpm", "81367c6d1eea5878ad726be80808eb5a787a23dee699f96e72b1109c57cdd8d9"}, "phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.1", "ba04e489ef03763bf28a17eb2eaddc2c20c6d217e2150a61e3298b0f4c2012b5", [:mix], [], "hexpm", "81367c6d1eea5878ad726be80808eb5a787a23dee699f96e72b1109c57cdd8d9"},
"phoenix_view": {:hex, :phoenix_view, "1.1.2", "1b82764a065fb41051637872c7bd07ed2fdb6f5c3bd89684d4dca6e10115c95a", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "7ae90ad27b09091266f6adbb61e1d2516a7c3d7062c6789d46a7554ec40f3a56"}, "phoenix_template": {:hex, :phoenix_template, "1.0.0", "c57bc5044f25f007dc86ab21895688c098a9f846a8dda6bc40e2d0ddc146e38f", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "1b066f99a26fd22064c12b2600a9a6e56700f591bf7b20b418054ea38b4d4357"},
"plug": {:hex, :plug, "1.13.6", "187beb6b67c6cec50503e940f0434ea4692b19384d47e5fdfd701e93cadb4cc2", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "02b9c6b9955bce92c829f31d6284bf53c591ca63c4fb9ff81dfd0418667a34ff"}, "phoenix_view": {:hex, :phoenix_view, "2.0.2", "6bd4d2fd595ef80d33b439ede6a19326b78f0f1d8d62b9a318e3d9c1af351098", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}], "hexpm", "a929e7230ea5c7ee0e149ffcf44ce7cf7f4b6d2bfe1752dd7c084cdff152d36f"},
"plug_cowboy": {:hex, :plug_cowboy, "2.5.2", "62894ccd601cf9597e2c23911ff12798a8a18d237e9739f58a6b04e4988899fe", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "ea6e87f774c8608d60c8d34022a7d073bd7680a0a013f049fc62bf35efea1044"}, "plug": {:hex, :plug, "1.14.0", "ba4f558468f69cbd9f6b356d25443d0b796fbdc887e03fa89001384a9cac638f", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "bf020432c7d4feb7b3af16a0c2701455cbbbb95e5b6866132cb09eb0c29adc14"},
"plug_crypto": {:hex, :plug_crypto, "1.2.2", "05654514ac717ff3a1843204b424477d9e60c143406aa94daf2274fdd280794d", [:mix], [], "hexpm", "87631c7ad914a5a445f0a3809f99b079113ae4ed4b867348dd9eec288cecb6db"}, "plug_cowboy": {:hex, :plug_cowboy, "2.6.0", "d1cf12ff96a1ca4f52207c5271a6c351a4733f413803488d75b70ccf44aebec2", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "073cf20b753ce6682ed72905cd62a2d4bd9bad1bf9f7feb02a1b8e525bd94fa6"},
"postgrex": {:hex, :postgrex, "0.16.4", "26d998467b4a22252285e728a29d341e08403d084e44674784975bb1cd00d2cb", [:mix], [{:connection, "~> 1.1", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "3234d1a70cb7b1e0c95d2e242785ec2a7a94a092bbcef4472320b950cfd64c5f"}, "plug_crypto": {:hex, :plug_crypto, "1.2.3", "8f77d13aeb32bfd9e654cb68f0af517b371fb34c56c9f2b58fe3df1235c1251a", [:mix], [], "hexpm", "b5672099c6ad5c202c45f5a403f21a3411247f164e4a8fab056e5cd8a290f4a2"},
"postgrex": {:hex, :postgrex, "0.16.5", "fcc4035cc90e23933c5d69a9cd686e329469446ef7abba2cf70f08e2c4b69810", [:mix], [{:connection, "~> 1.1", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "edead639dc6e882618c01d8fc891214c481ab9a3788dfe38dd5e37fd1d5fb2e8"},
"ranch": {:hex, :ranch, "1.8.0", "8c7a100a139fd57f17327b6413e4167ac559fbc04ca7448e9be9057311597a1d", [:make, :rebar3], [], "hexpm", "49fbcfd3682fab1f5d109351b61257676da1a2fdbe295904176d5e521a2ddfe5"}, "ranch": {:hex, :ranch, "1.8.0", "8c7a100a139fd57f17327b6413e4167ac559fbc04ca7448e9be9057311597a1d", [:make, :rebar3], [], "hexpm", "49fbcfd3682fab1f5d109351b61257676da1a2fdbe295904176d5e521a2ddfe5"},
"scrivener": {:hex, :scrivener, "2.7.2", "1d913c965ec352650a7f864ad7fd8d80462f76a32f33d57d1e48bc5e9d40aba2", [:mix], [], "hexpm", "7866a0ec4d40274efbee1db8bead13a995ea4926ecd8203345af8f90d2b620d9"}, "scrivener": {:hex, :scrivener, "2.7.2", "1d913c965ec352650a7f864ad7fd8d80462f76a32f33d57d1e48bc5e9d40aba2", [:mix], [], "hexpm", "7866a0ec4d40274efbee1db8bead13a995ea4926ecd8203345af8f90d2b620d9"},
"scrivener_ecto": {:hex, :scrivener_ecto, "2.7.0", "cf64b8cb8a96cd131cdbcecf64e7fd395e21aaa1cb0236c42a7c2e34b0dca580", [:mix], [{:ecto, "~> 3.3", [hex: :ecto, repo: "hexpm", optional: false]}, {:scrivener, "~> 2.4", [hex: :scrivener, repo: "hexpm", optional: false]}], "hexpm", "e809f171687806b0031129034352f5ae44849720c48dd839200adeaf0ac3e260"}, "scrivener_ecto": {:hex, :scrivener_ecto, "2.7.0", "cf64b8cb8a96cd131cdbcecf64e7fd395e21aaa1cb0236c42a7c2e34b0dca580", [:mix], [{:ecto, "~> 3.3", [hex: :ecto, repo: "hexpm", optional: false]}, {:scrivener, "~> 2.4", [hex: :scrivener, repo: "hexpm", optional: false]}], "hexpm", "e809f171687806b0031129034352f5ae44849720c48dd839200adeaf0ac3e260"},
"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"},
"swoosh": {:hex, :swoosh, "1.7.4", "f967d9b2659e81bab241b96267aae1001d35c2beea2df9c03dcf47b007bf566f", [:mix], [{:cowboy, "~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:ex_aws, "~> 2.1", [hex: :ex_aws, repo: "hexpm", optional: true]}, {:finch, "~> 0.6", [hex: :finch, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.13 or ~> 1.0", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mail, "~> 0.2", [hex: :mail, repo: "hexpm", optional: true]}, {:mime, "~> 1.1 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_cowboy, ">= 1.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "1553d994b4cf069162965e63de1e1c53d8236e127118d21e56ce2abeaa3f25b4"}, "swoosh": {:hex, :swoosh, "1.9.1", "0a5d7bf9954eb41d7e55525bc0940379982b090abbaef67cd8e1fd2ed7f8ca1a", [:mix], [{:cowboy, "~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:ex_aws, "~> 2.1", [hex: :ex_aws, repo: "hexpm", optional: true]}, {:finch, "~> 0.6", [hex: :finch, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.13 or ~> 1.0", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mail, "~> 0.2", [hex: :mail, repo: "hexpm", optional: true]}, {:mime, "~> 1.1 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_cowboy, ">= 1.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "76dffff3ffcab80f249d5937a592eaef7cc49ac6f4cdd27e622868326ed6371e"},
"telemetry": {:hex, :telemetry, "1.1.0", "a589817034a27eab11144ad24d5c0f9fab1f58173274b1e9bae7074af9cbee51", [:rebar3], [], "hexpm", "b727b2a1f75614774cff2d7565b64d0dfa5bd52ba517f16543e6fc7efcc0df48"}, "telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"},
"telemetry_metrics": {:hex, :telemetry_metrics, "0.6.1", "315d9163a1d4660aedc3fee73f33f1d355dcc76c5c3ab3d59e76e3edf80eef1f", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7be9e0871c41732c233be71e4be11b96e56177bf15dde64a8ac9ce72ac9834c6"}, "telemetry_metrics": {:hex, :telemetry_metrics, "0.6.1", "315d9163a1d4660aedc3fee73f33f1d355dcc76c5c3ab3d59e76e3edf80eef1f", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7be9e0871c41732c233be71e4be11b96e56177bf15dde64a8ac9ce72ac9834c6"},
"telemetry_poller": {:hex, :telemetry_poller, "1.0.0", "db91bb424e07f2bb6e73926fcafbfcbcb295f0193e0a00e825e589a0a47e8453", [:rebar3], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "b3a24eafd66c3f42da30fc3ca7dda1e9d546c12250a2d60d7b81d264fbec4f6e"}, "telemetry_poller": {:hex, :telemetry_poller, "1.0.0", "db91bb424e07f2bb6e73926fcafbfcbcb295f0193e0a00e825e589a0a47e8453", [:rebar3], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "b3a24eafd66c3f42da30fc3ca7dda1e9d546c12250a2d60d7b81d264fbec4f6e"},
"timex": {:hex, :timex, "3.7.9", "790cdfc4acfce434e442f98c02ea6d84d0239073bfd668968f82ac63e9a6788d", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 1.1", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "64691582e5bb87130f721fc709acfb70f24405833998fabf35be968984860ce1"}, "timex": {:hex, :timex, "3.7.9", "790cdfc4acfce434e442f98c02ea6d84d0239073bfd668968f82ac63e9a6788d", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 1.1", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "64691582e5bb87130f721fc709acfb70f24405833998fabf35be968984860ce1"},

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

3
rel/overlays/bin/migrate Executable file
View file

@ -0,0 +1,3 @@
#!/bin/sh
cd -P -- "$(dirname -- "$0")"
exec ./shift73k eval Shift73k.Release.migrate

1
rel/overlays/bin/migrate.bat Executable file
View file

@ -0,0 +1 @@
call "%~dp0\shift73k" eval Shift73k.Release.migrate

3
rel/overlays/bin/server Executable file
View file

@ -0,0 +1,3 @@
#!/bin/sh
cd -P -- "$(dirname -- "$0")"
PHX_SERVER=true exec ./shift73k start

2
rel/overlays/bin/server.bat Executable file
View file

@ -0,0 +1,2 @@
set PHX_SERVER=true
call "%~dp0\shift73k" start