diff --git a/.drone.yml b/.drone.yml index 80e0b68..990a8bf 100644 --- a/.drone.yml +++ b/.drone.yml @@ -4,39 +4,69 @@ name: check services: - name: postgres - image: docker.io/library/postgres:17.5 + image: docker.io/library/postgres:17.2 environment: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres - - name: docker - image: docker:dind - privileged: true - volumes: - - name: dockersock - path: /var/run trigger: event: - push steps: - - name: build & publish container? - image: docker:dind - volumes: - - name: dockersock - path: /var/run + - name: lint + image: docker.io/library/elixir:1.18.3-otp-27 commands: - - sleep 5 # give docker time to start - - docker ps -a - - docker build --tag mitgliederverwaltung . + # Install hex package manager + - mix local.hex --force + # Fetch dependencies + - mix deps.get + # Check for compilation errors & warnings + - mix compile --warnings-as-errors + # Check formatting + - mix format --check-formatted + # Security checks + - mix sobelow --config + # Check dependencies for known vulnerabilities + - mix deps.audit + # Check for dependencies that are not maintained anymore + - mix hex.audit + # Provide hints for improving code quality + - mix credo -volumes: - - name: cache - host: - path: /tmp/drone_cache + - name: wait_for_postgres + image: docker.io/library/postgres:17.2 + commands: + # Wait for postgres to become available + - | + for i in {1..20}; do + if pg_isready -h postgres -U postgres; then + exit 0 + else + true + fi + sleep 2 + done + echo "Postgres did not become available, aborting." + exit 1 - - name: dockersock - temp: {} + - name: test + image: docker.io/library/elixir:1.18.3-otp-27 + environment: + MIX_ENV: test + TEST_POSTGRES_HOST: postgres + commands: + # Install hex package manager + - mix local.hex --force + # Fetch dependencies + - mix deps.get + # Run tests + - mix test + + - name: build & publish container? + image: docker.io/library/elixir:1.18.3-otp-27 + commands: + - docker build --tag mitgliederverwaltung . --- kind: pipeline @@ -56,13 +86,13 @@ environment: steps: - name: renovate - image: renovate/renovate:41.37 + image: renovate/renovate:40.22 environment: RENOVATE_CONFIG_FILE: "renovate_backend_config.js" RENOVATE_TOKEN: from_secret: RENOVATE_TOKEN - GITHUB_COM_TOKEN: - from_secret: GITHUB_COM_TOKEN + #GITHUB_COM_TOKEN: + # from_secret: GITHUB_COM_TOKEN commands: # https://github.com/renovatebot/renovate/discussions/15049 - unset GIT_COMMITTER_NAME GIT_COMMITTER_EMAIL GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL diff --git a/.formatter.exs b/.formatter.exs index 11132c0..75c5c0c 100644 --- a/.formatter.exs +++ b/.formatter.exs @@ -1,9 +1,7 @@ [ import_deps: [ - :ash_authentication_phoenix, :ash_admin, :ash_postgres, - :ash_authentication, :ash_phoenix, :ash, :reactor, diff --git a/.gitignore b/.gitignore index eef8464..247777e 100644 --- a/.gitignore +++ b/.gitignore @@ -35,7 +35,3 @@ mv-*.tar npm-debug.log /assets/node_modules/ -.cursor - -# Ignore the .env file with env variables -.env diff --git a/.igniter.exs b/.igniter.exs deleted file mode 100644 index bdc3383..0000000 --- a/.igniter.exs +++ /dev/null @@ -1,10 +0,0 @@ -# This is a configuration file for igniter. -# For option documentation, see https://hexdocs.pm/igniter/Igniter.Project.IgniterConfig.html -# To keep it up to date, use `mix igniter.setup` -[ - module_location: :outside_matching_folder, - extensions: [{Igniter.Extensions.Phoenix, []}], - deps_location: :last_list_literal, - source_folders: ["lib", "test/support"], - dont_move_files: [~r"lib/mix"] -] diff --git a/.tool-versions b/.tool-versions index 682ce54..cbe11b5 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,3 +1,3 @@ elixir 1.18.3-otp-27 -erlang 27.3.4 -just 1.42.2 +erlang 27.3 +just 1.40.0 diff --git a/Justfile b/Justfile index 26db3bc..b3541fd 100644 --- a/Justfile +++ b/Justfile @@ -1,6 +1,4 @@ -set dotenv-load := true - -run: install-dependencies start-database migrate-database seed-database +run: install-dependencies start-database migrate-database mix phx.server install-dependencies: @@ -11,20 +9,12 @@ migrate-database: reset-database: mix ash.reset - MIX_ENV=test mix ash.reset - -seed-database: - mix run priv/repo/seeds.exs start-database: docker compose up -d ci-dev: lint audit test -gettext: - mix gettext.extract - mix gettext.merge priv/gettext - lint: mix format --check-formatted mix compile --warnings-as-errors @@ -35,7 +25,7 @@ audit: mix deps.audit mix hex.audit -test: install-dependencies start-database +test: mix test format: @@ -46,35 +36,4 @@ build-docker-container: # This is meant for debugging the container build process only. run-docker-container: build-docker-container - docker run -e "SECRET_KEY_BASE=ahK8BeiDaibaige1ahkooS0chie9lo7the7uuzar0eeBeeCh2iereteshee2Oosu" -e='DATABASE_URL=postgres://postgres@localhost:5432/mv_dev' -e='PORT=4040' -e='PHX_HOST=localhost' --network=host mitgliederverwaltung - -# Usage: -# just regen-migrations migration_name [commit_hash] -# If commit_hash is given, rollback & delete the migrations from that commit. -# Otherwise, rollback & delete all untracked migrations. -regen-migrations migration_name commit_hash='': - #!/usr/bin/env bash - set -euo pipefail - # Pick migrations either from the given commit or untracked files - if [ -n "{{commit_hash}}" ]; then - echo "→ Rolling back migrations from commit {{commit_hash}}" - MIG_FILES=$(git show --name-only --pretty=format: "{{commit_hash}}" \ - | grep -E "^priv/repo/migrations/|^priv/resource_snapshots") - else - echo "→ Rolling back all untracked migrations" - MIG_FILES=$(git ls-files --others priv/repo/migrations) - fi - - # Roll back in Ash - COUNT=$(echo "$MIG_FILES" | wc -l) - mix ash_postgres.rollback -n "$COUNT" - - # Remove the migration files - echo removing $MIG_FILES - echo "$MIG_FILES" | xargs rm -f - - # Also clean up any untracked resource snapshots - git ls-files --others priv/resource_snapshots | xargs rm -f - - # Generate a fresh migration - mix ash.codegen --name "{{migration_name}}" + docker run -e "SECRET_KEY_BASE=ahK8BeiDaibaige1ahkooS0chie9lo7the7uuzar0eeBeeCh2iereteshee2Oosu" -e='DATABASE_URL=postgres://postgres@localhost:5432/mv_dev' -e='PORT=4040' -e='PHX_HOST=localhost' --network=host mitgliederverwaltung \ No newline at end of file diff --git a/assets/tailwind.config.js b/assets/tailwind.config.js index 873d6d6..c16fe48 100644 --- a/assets/tailwind.config.js +++ b/assets/tailwind.config.js @@ -7,7 +7,6 @@ const path = require("path") module.exports = { content: [ - "../deps/ash_authentication_phoenix/**/*.*ex", "./js/**/*.js", "../lib/mv_web.ex", "../lib/mv_web/**/*.*ex" diff --git a/config/config.exs b/config/config.exs index 43c8cf8..a43af46 100644 --- a/config/config.exs +++ b/config/config.exs @@ -49,7 +49,7 @@ config :spark, config :mv, ecto_repos: [Mv.Repo], generators: [timestamp_type: :utc_datetime], - ash_domains: [Mv.Membership, Mv.Accounts] + ash_domains: [Mv.Membership] # Configures the endpoint config :mv, MvWeb.Endpoint, diff --git a/config/dev.exs b/config/dev.exs index 17b4ce1..b7f9ad7 100644 --- a/config/dev.exs +++ b/config/dev.exs @@ -84,19 +84,3 @@ config :phoenix_live_view, # Disable swoosh api client as it is only required for production adapters. config :swoosh, :api_client, false - -config :mv, :secret_key_base, "ryn7D6ssmIHQFWIks2sFiTGATgwwAR1+3bN8p7fy6qVtB8qnxOuk1uyAwHz1Q8WB" - -# Signing Secret for Authentication -config :mv, :token_signing_secret, "IwUwi65TrEeExwBXXFPGm2I7889NsL" - -config :mv, :rauthy, - client_id: "mv", - base_url: "http://localhost:8080/auth/v1", - client_secret: System.get_env("OIDC_CLIENT_SECRET"), - redirect_uri: "http://localhost:4000/auth/user/rauthy/callback" - -# AshAuthentication development configuration -config :mv, :session_identifier, :jti - -config :mv, :require_token_presence_for_authentication, true diff --git a/config/runtime.exs b/config/runtime.exs index e8ab249..e591590 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -53,13 +53,6 @@ if config_env() == :prod do config :mv, :dns_cluster_query, System.get_env("DNS_CLUSTER_QUERY") - config :mv, :rauthy, redirect_uri: "http://localhost:4000/auth/user/rauthy/callback" - - # AshAuthentication production configuration - config :mv, :session_identifier, :jti - - config :mv, :require_token_presence_for_authentication, true - config :mv, MvWeb.Endpoint, url: [host: host, port: 443, scheme: "https"], http: [ diff --git a/config/test.exs b/config/test.exs index bcb55eb..00a6a7f 100644 --- a/config/test.exs +++ b/config/test.exs @@ -9,7 +9,6 @@ config :mv, Mv.Repo, username: "postgres", password: "postgres", hostname: System.get_env("TEST_POSTGRES_HOST", "localhost"), - port: System.get_env("TEST_POSTGRES_PORT", "5000"), database: "mv_test#{System.get_env("MIX_TEST_PARTITION")}", pool: Ecto.Adapters.SQL.Sandbox, pool_size: System.schedulers_online() * 2 @@ -36,12 +35,3 @@ config :phoenix, :plug_init_mode, :runtime # Enable helpful, but potentially expensive runtime checks config :phoenix_live_view, enable_expensive_runtime_checks: true - -# Token signing secret for AshAuthentication tests -config :mv, :token_signing_secret, "test_secret_key_for_ash_authentication_tokens" - -# AshAuthentication test-specific configuration -# In Tests we don't need token presence, but in other envs its recommended -config :mv, :session_identifier, :unsafe - -config :mv, :require_token_presence_for_authentication, false diff --git a/docker-compose.yml b/docker-compose.yml index fabe6b1..0ac02ca 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,13 +1,8 @@ version: "3.5" -networks: - local: - rauthy-dev: - driver: bridge - services: db: - image: postgres:17.5-alpine + image: postgres:17.2-alpine environment: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres @@ -23,46 +18,8 @@ services: networks: - local - mailcrab: - image: marlonb/mailcrab:latest - ports: - - "1080:1080" - networks: - - rauthy-dev - - - rauthy: - container_name: rauthy-dev - image: ghcr.io/sebadob/rauthy:0.31.2 - environment: - - LOCAL_TEST=true - - SMTP_URL=mailcrab - - SMTP_PORT=1025 - - SMTP_DANGER_INSECURE=true - - LISTEN_SCHEME=http - - PUB_URL=localhost:8080 - - BOOTSTRAP_ADMIN_PASSWORD_PLAIN=RauthyTest12345 - #- HIQLITE=false - #- PG_HOST=db - #- PG_PORT=5432 - #- PG_USER=postgres - #- PG_PASSWORD=postgres - #- PG_DB_NAME=mv_dev - ports: - - "8080:8080" - depends_on: - - mailcrab - - db - networks: - - rauthy-dev - - local - volumes: - - type: volume - source: rauthy-data - target: /app/data +networks: + local: volumes: - postgres-data: - rauthy-data: - - + postgres-data: \ No newline at end of file diff --git a/lib/accounts/accounts.ex b/lib/accounts/accounts.ex deleted file mode 100644 index 55e8a4b..0000000 --- a/lib/accounts/accounts.ex +++ /dev/null @@ -1,18 +0,0 @@ -defmodule Mv.Accounts do - @moduledoc """ - AshAuthentication specific domain to handle Authentication for users. - """ - use Ash.Domain, - extensions: [AshPhoenix] - - resources do - resource Mv.Accounts.User do - define :create_user, action: :create - define :list_users, action: :read - define :update_user, action: :update - define :destroy_user, action: :destroy - end - - resource Mv.Accounts.Token - end -end diff --git a/lib/accounts/token.ex b/lib/accounts/token.ex deleted file mode 100644 index ab9c3a7..0000000 --- a/lib/accounts/token.ex +++ /dev/null @@ -1,14 +0,0 @@ -defmodule Mv.Accounts.Token do - @moduledoc """ - AshAuthentication specific ressource - """ - use Ash.Resource, - data_layer: AshPostgres.DataLayer, - extensions: [AshAuthentication.TokenResource], - domain: Mv.Accounts - - postgres do - table "tokens" - repo Mv.Repo - end -end diff --git a/lib/accounts/user.ex b/lib/accounts/user.ex deleted file mode 100644 index 0de4a38..0000000 --- a/lib/accounts/user.ex +++ /dev/null @@ -1,127 +0,0 @@ -defmodule Mv.Accounts.User do - @moduledoc """ - The ressource for keeping user-specific data related to the login process. It is used by AshAuthentication to handle the Authentication strategies like SSO. - """ - use Ash.Resource, - domain: Mv.Accounts, - data_layer: AshPostgres.DataLayer, - extensions: [AshAuthentication] - - # authorizers: [Ash.Policy.Authorizer] - - postgres do - table "users" - repo Mv.Repo - end - - @doc """ - AshAuthentication specific: Defines the strategies we want to use for authentication. - Currently password and SSO with Rauthy as OIDC provider - """ - authentication do - session_identifier Application.compile_env(:mv, :session_identifier, :jti) - - tokens do - enabled? true - token_resource Mv.Accounts.Token - - require_token_presence_for_authentication? Application.compile_env( - :mv, - :require_token_presence_for_authentication, - false - ) - - store_all_tokens? true - - # signing_algorithm "EdDSA" -> https://git.local-it.org/local-it/mitgliederverwaltung/issues/87 - - signing_secret fn _, _ -> - {:ok, Application.get_env(:mv, :token_signing_secret)} - end - end - - strategies do - oidc :rauthy do - client_id Mv.Secrets - base_url Mv.Secrets - redirect_uri Mv.Secrets - client_secret Mv.Secrets - auth_method :client_secret_jwt - code_verifier true - - # id_token_signed_response_alg "EdDSA" #-> https://git.local-it.org/local-it/mitgliederverwaltung/issues/87 - end - - password :password do - identity_field :email - hash_provider AshAuthentication.BcryptProvider - confirmation_required? false - end - end - end - - actions do - defaults [:read, :create, :destroy, :update] - - read :get_by_subject do - description "Get a user by the subject claim in a JWT" - argument :subject, :string, allow_nil?: false - get? true - prepare AshAuthentication.Preparations.FilterBySubject - end - - read :sign_in_with_rauthy do - argument :user_info, :map, allow_nil?: false - argument :oauth_tokens, :map, allow_nil?: false - prepare AshAuthentication.Strategy.OAuth2.SignInPreparation - - filter expr(email == get_path(^arg(:user_info), [:email])) - end - - create :register_with_rauthy do - argument :user_info, :map, allow_nil?: false - argument :oauth_tokens, :map, allow_nil?: false - upsert? true - upsert_identity :unique_email - - change AshAuthentication.GenerateTokenChange - - change fn changeset, _ctx -> - user_info = Ash.Changeset.get_argument(changeset, :user_info) - - changeset - |> Ash.Changeset.change_attribute(:email, user_info["preferred_username"]) - |> Ash.Changeset.change_attribute(:oidc_id, user_info["id"]) - end - end - end - - attributes do - uuid_primary_key :id - - attribute :email, :ci_string, allow_nil?: false, public?: true - attribute :hashed_password, :string, sensitive?: true, allow_nil?: true - attribute :oidc_id, :string, allow_nil?: true - end - - relationships do - belongs_to :member, Mv.Membership.Member - end - - identities do - identity :unique_email, [:email] - identity :unique_oidc_id, [:oidc_id] - end - - # You can customize this if you wish, but this is a safe default that - # only allows user data to be interacted with via AshAuthentication. - # policies do - # bypass AshAuthentication.Checks.AshAuthenticationInteraction do - # authorize_if(always()) - # end - - # policy always() do - # forbid_if(always()) - # end - # end -end diff --git a/lib/accounts/user_identity.exs b/lib/accounts/user_identity.exs deleted file mode 100644 index fd8d2c9..0000000 --- a/lib/accounts/user_identity.exs +++ /dev/null @@ -1,18 +0,0 @@ -defmodule Mv.Accounts.UserIdentity do - @moduledoc """ - AshAuthentication specific ressource - """ - use Ash.Resource, - data_layer: AshPostgres.DataLayer, - extensions: [AshAuthentication.UserIdentity], - domain: Mv.Accounts - - postgres do - table "user_identities" - repo Mv.Repo - end - - user_identity do - user_resource Mv.Accounts.User - end -end diff --git a/lib/membership/email.ex b/lib/membership/email.ex deleted file mode 100644 index c611742..0000000 --- a/lib/membership/email.ex +++ /dev/null @@ -1,37 +0,0 @@ -defmodule Mv.Membership.Email do - @match_pattern ~S/^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$/ - @match_regex Regex.compile!(@match_pattern) - @min_length 5 - @max_length 254 - - use Ash.Type.NewType, - subtype_of: :string, - constraints: [ - match: @match_pattern, - trim?: true, - min_length: @min_length, - max_length: @max_length - ] - - @impl true - def cast_input(value, _) when is_binary(value) do - value = String.trim(value) - - cond do - String.length(value) < @min_length -> - :error - - String.length(value) > @max_length -> - :error - - !Regex.match?(@match_regex, value) -> - :error - - true -> - {:ok, value} - end - end - - @impl true - def cast_input(_, _), do: :error -end diff --git a/lib/membership/member.ex b/lib/membership/member.ex index 583f173..0538f45 100644 --- a/lib/membership/member.ex +++ b/lib/membership/member.ex @@ -14,23 +14,6 @@ defmodule Mv.Membership.Member do create :create_member do primary? true argument :properties, {:array, :map} - - accept [ - :first_name, - :last_name, - :email, - :birth_date, - :paid, - :phone_number, - :join_date, - :exit_date, - :notes, - :city, - :street, - :house_number, - :postal_code - ] - change manage_relationship(:properties, type: :create) end @@ -38,134 +21,12 @@ defmodule Mv.Membership.Member do primary? true require_atomic? false argument :properties, {:array, :map} - - accept [ - :first_name, - :last_name, - :email, - :birth_date, - :paid, - :phone_number, - :join_date, - :exit_date, - :notes, - :city, - :street, - :house_number, - :postal_code - ] - change manage_relationship(:properties, on_match: :update, on_no_match: :create) end end - validations do - # Required fields are covered by allow_nil? false - - # First name and last name must not be empty - validate present(:first_name) - validate present(:last_name) - validate present(:email) - - # Birth date not in the future - validate compare(:birth_date, less_than_or_equal_to: &Date.utc_today/0), - where: [present(:birth_date)], - message: "cannot be in the future" - - # Join date not in the future - validate compare(:join_date, less_than_or_equal_to: &Date.utc_today/0), - where: [present(:join_date)], - message: "cannot be in the future" - - # Exit date not before join date - validate compare(:exit_date, greater_than: :join_date), - where: [present([:join_date, :exit_date])], - message: "cannot be before join date" - - # Phone number format (only if set) - validate match(:phone_number, ~r/^\+?[0-9\- ]{6,20}$/), - where: [present(:phone_number)], - message: "is not a valid phone number" - - # Postal code format (only if set) - validate match(:postal_code, ~r/^\d{5}$/), - where: [present(:postal_code)], - message: "must consist of 5 digits" - - # Email validation with EctoCommons.EmailValidator - validate fn changeset, _ -> - email = Ash.Changeset.get_attribute(changeset, :email) - - changeset2 = - {%{}, %{email: :string}} - |> Ecto.Changeset.cast(%{email: email}, [:email]) - |> EctoCommons.EmailValidator.validate_email(:email, checks: [:html_input, :pow]) - - if changeset2.valid? do - :ok - else - {:error, field: :email, message: "is not a valid email"} - end - end - end - attributes do uuid_v7_primary_key :id - - attribute :first_name, :string do - allow_nil? false - constraints min_length: 1 - end - - attribute :last_name, :string do - allow_nil? false - constraints min_length: 1 - end - - attribute :email, :string do - allow_nil? false - constraints min_length: 5, max_length: 254 - end - - attribute :birth_date, :date do - allow_nil? true - end - - attribute :paid, :boolean do - allow_nil? true - end - - attribute :phone_number, :string do - allow_nil? true - end - - attribute :join_date, :date do - allow_nil? true - end - - attribute :exit_date, :date do - allow_nil? true - end - - attribute :notes, :string do - allow_nil? true - end - - attribute :city, :string do - allow_nil? true - end - - attribute :street, :string do - allow_nil? true - end - - attribute :house_number, :string do - allow_nil? true - end - - attribute :postal_code, :string do - allow_nil? true - end end relationships do diff --git a/lib/membership/property.ex b/lib/membership/property.ex index 2c432a8..433fc63 100644 --- a/lib/membership/property.ex +++ b/lib/membership/property.ex @@ -6,10 +6,6 @@ defmodule Mv.Membership.Property do postgres do table "properties" repo Mv.Repo - - references do - reference :member, on_delete: :delete - end end actions do @@ -20,17 +16,8 @@ defmodule Mv.Membership.Property do attributes do uuid_primary_key :id - attribute :value, :union, - constraints: [ - storage: :type_and_value, - types: [ - boolean: [type: :boolean], - date: [type: :date], - integer: [type: :integer], - string: [type: :string], - email: [type: Mv.Membership.Email] - ] - ] + attribute :value, :string, + description: "Speichert den Wert, Typ-Interpretation per property_type.typ" end relationships do @@ -38,8 +25,4 @@ defmodule Mv.Membership.Property do belongs_to :property_type, Mv.Membership.PropertyType end - - calculations do - calculate :value_to_string, :string, expr(value[:value] <> "") - end end diff --git a/lib/membership/property_type.ex b/lib/membership/property_type.ex index 7444c13..560f679 100644 --- a/lib/membership/property_type.ex +++ b/lib/membership/property_type.ex @@ -10,7 +10,7 @@ defmodule Mv.Membership.PropertyType do actions do defaults [:create, :read, :update, :destroy] - default_accept [:name, :value_type, :description, :immutable, :required] + default_accept [:name, :type, :description, :immutable, :required] end attributes do @@ -18,10 +18,9 @@ defmodule Mv.Membership.PropertyType do attribute :name, :string, allow_nil?: false, public?: true - attribute :value_type, :atom, - constraints: [one_of: [:string, :integer, :boolean, :date, :email]], + attribute :type, :string, allow_nil?: false, - description: "Defines the datatype `Property.value` is interpreted as" + description: "Definies the datatype `Property.value` is interpreted as" attribute :description, :string, allow_nil?: true, public?: true diff --git a/lib/mv/accounts/user/senders/send_new_user_confirmation_email.ex b/lib/mv/accounts/user/senders/send_new_user_confirmation_email.ex deleted file mode 100644 index 9e34f29..0000000 --- a/lib/mv/accounts/user/senders/send_new_user_confirmation_email.ex +++ /dev/null @@ -1,32 +0,0 @@ -defmodule Mv.Accounts.User.Senders.SendNewUserConfirmationEmail do - @moduledoc """ - Sends an email for a new user to confirm their email address. - """ - - use AshAuthentication.Sender - use MvWeb, :verified_routes - - import Swoosh.Email - - alias Mv.Mailer - - @impl true - def send(user, token, _) do - new() - # Replace with email from env - |> from({"noreply", "noreply@example.com"}) - |> to(to_string(user.email)) - |> subject("Confirm your email address") - |> html_body(body(token: token)) - |> Mailer.deliver!() - end - - defp body(params) do - url = url(~p"/confirm_new_user/#{params[:token]}") - - """ -

Click this link to confirm your email:

-

#{url}

- """ - end -end diff --git a/lib/mv/accounts/user/senders/send_password_reset_email.ex b/lib/mv/accounts/user/senders/send_password_reset_email.ex deleted file mode 100644 index 7c33d2e..0000000 --- a/lib/mv/accounts/user/senders/send_password_reset_email.ex +++ /dev/null @@ -1,32 +0,0 @@ -defmodule Mv.Accounts.User.Senders.SendPasswordResetEmail do - @moduledoc """ - Sends a password reset email - """ - - use AshAuthentication.Sender - use MvWeb, :verified_routes - - import Swoosh.Email - - alias Mv.Mailer - - @impl true - def send(user, token, _) do - new() - # Replace with email from env - |> from({"noreply", "noreply@example.com"}) - |> to(to_string(user.email)) - |> subject("Reset your password") - |> html_body(body(token: token)) - |> Mailer.deliver!() - end - - defp body(params) do - url = url(~p"/password-reset/#{params[:token]}") - - """ -

Click this link to reset your password:

-

#{url}

- """ - end -end diff --git a/lib/mv/application.ex b/lib/mv/application.ex index e0bf462..2a6eaa3 100644 --- a/lib/mv/application.ex +++ b/lib/mv/application.ex @@ -14,7 +14,6 @@ defmodule Mv.Application do {Phoenix.PubSub, name: Mv.PubSub}, # Start the Finch HTTP client for sending emails {Finch, name: Mv.Finch}, - {AshAuthentication.Supervisor, otp_app: :my}, # Start a worker by calling: Mv.Worker.start_link(arg) # {Mv.Worker, arg}, # Start to serve requests, typically the last entry diff --git a/lib/mv/repo.ex b/lib/mv/repo.ex index a8d696a..490750e 100644 --- a/lib/mv/repo.ex +++ b/lib/mv/repo.ex @@ -5,7 +5,7 @@ defmodule Mv.Repo do @impl true def installed_extensions do # Add extensions here, and the migration generator will install them. - ["ash-functions", "citext"] + ["ash-functions"] end # Don't open unnecessary transactions diff --git a/lib/mv/secrets.ex b/lib/mv/secrets.ex deleted file mode 100644 index 6a88eee..0000000 --- a/lib/mv/secrets.ex +++ /dev/null @@ -1,46 +0,0 @@ -defmodule Mv.Secrets do - use AshAuthentication.Secret - - def secret_for( - [:authentication, :strategies, :rauthy, :client_id], - Mv.Accounts.User, - _opts, - _meth - ) do - get_config(:client_id) - end - - def secret_for( - [:authentication, :strategies, :rauthy, :redirect_uri], - Mv.Accounts.User, - _opts, - _meth - ) do - get_config(:redirect_uri) - end - - def secret_for( - [:authentication, :strategies, :rauthy, :client_secret], - Mv.Accounts.User, - _opts, - _meth - ) do - get_config(:client_secret) - end - - def secret_for( - [:authentication, :strategies, :rauthy, :base_url], - Mv.Accounts.User, - _opts, - _meth - ) do - get_config(:base_url) - end - - defp get_config(key) do - :mv - |> Application.fetch_env!(:rauthy) - |> Keyword.fetch!(key) - |> then(&{:ok, &1}) - end -end diff --git a/lib/mv_web.ex b/lib/mv_web.ex index 4254449..de7184d 100644 --- a/lib/mv_web.ex +++ b/lib/mv_web.ex @@ -55,7 +55,6 @@ defmodule MvWeb do use Phoenix.LiveView, layout: {MvWeb.Layouts, :app} - on_mount MvWeb.LiveHelpers unquote(html_helpers()) end end diff --git a/lib/mv_web/auth_overrides.ex b/lib/mv_web/auth_overrides.ex deleted file mode 100644 index bec3354..0000000 --- a/lib/mv_web/auth_overrides.ex +++ /dev/null @@ -1,20 +0,0 @@ -defmodule MvWeb.AuthOverrides do - use AshAuthentication.Phoenix.Overrides - - # configure your UI overrides here - - # First argument to `override` is the component name you are overriding. - # The body contains any number of configurations you wish to override - # Below are some examples - - # For a complete reference, see https://hexdocs.pm/ash_authentication_phoenix/ui-overrides.html - - # override AshAuthentication.Phoenix.Components.Banner do - # set :image_url, "https://media.giphy.com/media/g7GKcSzwQfugw/giphy.gif" - # set :text_class, "bg-red-500" - # end - - # override AshAuthentication.Phoenix.Components.SignIn do - # set :show_banner, false - # end -end diff --git a/lib/mv_web/components/core_components.ex b/lib/mv_web/components/core_components.ex index c35f1ce..1e9b835 100644 --- a/lib/mv_web/components/core_components.ex +++ b/lib/mv_web/components/core_components.ex @@ -673,28 +673,4 @@ defmodule MvWeb.CoreComponents do def translate_errors(errors, field) when is_list(errors) do for {^field, {msg, opts}} <- errors, do: translate_error({msg, opts}) end - - @doc """ - Renders a list of items with name and value pairs. - - ## Examples - <.generic_list items={[ - {item.name, item.value}, - {other.name, other.value} - ]} /> - """ - attr :items, :list, required: true, doc: "List of {name, value} tuples" - - def generic_list(assigns) do - ~H""" -
-
-
-
{name}
-
{value}
-
-
-
- """ - end end diff --git a/lib/mv_web/components/layouts/app.html.heex b/lib/mv_web/components/layouts/app.html.heex index 54258db..3b3b607 100644 --- a/lib/mv_web/components/layouts/app.html.heex +++ b/lib/mv_web/components/layouts/app.html.heex @@ -9,13 +9,6 @@

-
- - -
@elixirphoenix diff --git a/lib/mv_web/controllers/auth_controller.ex b/lib/mv_web/controllers/auth_controller.ex deleted file mode 100644 index a8375d1..0000000 --- a/lib/mv_web/controllers/auth_controller.ex +++ /dev/null @@ -1,59 +0,0 @@ -require Logger - -defmodule MvWeb.AuthController do - use MvWeb, :controller - use AshAuthentication.Phoenix.Controller - - def success(conn, activity, user, _token) do - return_to = get_session(conn, :return_to) || ~p"/" - - message = - case activity do - {:confirm_new_user, :confirm} -> gettext("Your email address has now been confirmed") - {:password, :reset} -> gettext("Your password has successfully been reset") - _ -> gettext("You are now signed in") - end - - conn - |> delete_session(:return_to) - |> store_in_session(user) - # If your resource has a different name, update the assign name here (i.e :current_admin) - |> assign(:current_user, user) - |> put_flash(:info, message) - |> redirect(to: return_to) - end - - def failure(conn, activity, reason) do - Logger.error(%{conn: conn, reason: reason}) - - message = - case {activity, reason} do - {_, - %AshAuthentication.Errors.AuthenticationFailed{ - caused_by: %Ash.Error.Forbidden{ - errors: [%AshAuthentication.Errors.CannotConfirmUnconfirmedUser{}] - } - }} -> - gettext(""" - You have already signed in another way, but have not confirmed your account. - You can confirm your account using the link we sent to you, or by resetting your password. - """) - - _ -> - gettext("Incorrect email or password") - end - - conn - |> put_flash(:error, message) - |> redirect(to: ~p"/sign-in") - end - - def sign_out(conn, _params) do - return_to = get_session(conn, :return_to) || ~p"/" - - conn - |> clear_session(:mv) - |> put_flash(:info, gettext("You are now signed out")) - |> redirect(to: return_to) - end -end diff --git a/lib/mv_web/controllers/page_html/home.html.heex b/lib/mv_web/controllers/page_html/home.html.heex index f13765e..d72b03c 100644 --- a/lib/mv_web/controllers/page_html/home.html.heex +++ b/lib/mv_web/controllers/page_html/home.html.heex @@ -1,55 +1,222 @@ - - - -
-
-
-

- Demo -

-
-
-
-
-
-
-
-
-
diff --git a/lib/mv_web/endpoint.ex b/lib/mv_web/endpoint.ex index 090e54c..47bcb23 100644 --- a/lib/mv_web/endpoint.ex +++ b/lib/mv_web/endpoint.ex @@ -25,10 +25,6 @@ defmodule MvWeb.Endpoint do gzip: false, only: MvWeb.static_paths() - if Code.ensure_loaded?(Tidewave) do - plug Tidewave - end - # Code reloading can be explicitly enabled under the # :code_reloader configuration of your endpoint. if code_reloading? do diff --git a/lib/mv_web/live_helpers.ex b/lib/mv_web/live_helpers.ex deleted file mode 100644 index 03d7d45..0000000 --- a/lib/mv_web/live_helpers.ex +++ /dev/null @@ -1,7 +0,0 @@ -defmodule MvWeb.LiveHelpers do - def on_mount(:default, _params, session, socket) do - locale = session["locale"] || "en" - Gettext.put_locale(locale) - {:cont, socket} - end -end diff --git a/lib/mv_web/live_user_auth.ex b/lib/mv_web/live_user_auth.ex deleted file mode 100644 index 67bef70..0000000 --- a/lib/mv_web/live_user_auth.ex +++ /dev/null @@ -1,46 +0,0 @@ -defmodule MvWeb.LiveUserAuth do - @moduledoc """ - Helpers for authenticating users in LiveViews. - """ - - import Phoenix.Component - use MvWeb, :verified_routes - - # This is used for nested liveviews to fetch the current user. - # To use, place the following at the top of that liveview: - # on_mount {MvWeb.LiveUserAuth, :current_user} - def on_mount(:current_user, _params, session, socket) do - return_to = session[:return_to] - - socket = - socket - |> assign(:return_to, return_to) - |> AshAuthentication.Phoenix.LiveSession.assign_new_resources(session) - - {:cont, session, socket} - end - - def on_mount(:live_user_optional, _params, _session, socket) do - if socket.assigns[:current_user] do - {:cont, socket} - else - {:cont, assign(socket, :current_user, nil)} - end - end - - def on_mount(:live_user_required, _params, _session, socket) do - if socket.assigns[:current_user] do - {:cont, socket} - else - {:halt, Phoenix.LiveView.redirect(socket, to: ~p"/sign-in")} - end - end - - def on_mount(:live_no_user, _params, _session, socket) do - if socket.assigns[:current_user] do - {:halt, Phoenix.LiveView.redirect(socket, to: ~p"/")} - else - {:cont, assign(socket, :current_user, nil)} - end - end -end diff --git a/lib/mv_web/locale_controller.ex b/lib/mv_web/locale_controller.ex deleted file mode 100644 index 3c8056f..0000000 --- a/lib/mv_web/locale_controller.ex +++ /dev/null @@ -1,18 +0,0 @@ -defmodule MvWeb.LocaleController do - use MvWeb, :controller - - def set_locale(conn, %{"locale" => locale}) do - conn - |> put_session(:locale, locale) - |> redirect(to: get_referer(conn) || "/") - end - - defp get_referer(conn) do - conn.req_headers - |> Enum.find(fn {k, _v} -> k == "referer" end) - |> case do - {_, v} -> URI.parse(v).path - _ -> nil - end - end -end diff --git a/lib/mv_web/member_live/form_component.ex b/lib/mv_web/member_live/form_component.ex index 5535d1a..b91d3c0 100644 --- a/lib/mv_web/member_live/form_component.ex +++ b/lib/mv_web/member_live/form_component.ex @@ -9,11 +9,7 @@ defmodule MvWeb.MemberLive.FormComponent do Enum.map(property_types, fn pt -> %{ "property_type_id" => pt.id, - "value" => %{ - "type" => pt.value_type, - "value" => nil, - "_union_type" => Atom.to_string(pt.value_type) - } + "value" => nil } end) @@ -26,9 +22,7 @@ defmodule MvWeb.MemberLive.FormComponent do
<.header> {@title} - <:subtitle> - {gettext("Use this form to manage member records and their properties.")} - + <:subtitle>Use this form to manage member records and their properties. <.simple_form @@ -38,32 +32,9 @@ defmodule MvWeb.MemberLive.FormComponent do phx-change="validate" phx-submit="save" > - <.input field={@form[:first_name]} label={gettext("First Name")} required /> - <.input field={@form[:last_name]} label={gettext("Last Name")} required /> - <.input field={@form[:email]} label={gettext("Email")} required type="email" /> - <.input field={@form[:birth_date]} label={gettext("Birth Date")} type="date" /> - <.input field={@form[:paid]} label={gettext("Paid")} type="checkbox" /> - <.input field={@form[:phone_number]} label={gettext("Phone Number")} /> - <.input field={@form[:join_date]} label={gettext("Join Date")} type="date" /> - <.input field={@form[:exit_date]} label={gettext("Exit Date")} type="date" /> - <.input field={@form[:notes]} label={gettext("Notes")} /> - <.input field={@form[:city]} label={gettext("City")} /> - <.input field={@form[:street]} label={gettext("Street")} /> - <.input field={@form[:house_number]} label={gettext("House Number")} /> - <.input field={@form[:postal_code]} label={gettext("Postal Code")} /> - -

{gettext("Custom Properties")}

<.inputs_for :let={f_property} field={@form[:properties]}> <% type = Enum.find(@property_types, &(&1.id == f_property[:property_type_id].value)) %> - <.inputs_for :let={value_form} field={f_property[:value]}> - <% input_type = - cond do - type && type.value_type == :boolean -> "checkbox" - type && type.value_type == :date -> :date - true -> :text - end %> - <.input field={value_form[:value]} label={type && type.name} type={input_type} /> - + <.input field={f_property[:value]} label={type && type.name} /> <:actions> - <.button phx-disable-with={gettext("Saving...")}>{gettext("Save Member")} + <.button phx-disable-with="Saving...">Save Member
@@ -97,16 +68,9 @@ defmodule MvWeb.MemberLive.FormComponent do {:ok, member} -> notify_parent({:saved, member}) - action = - case socket.assigns.form.source.type do - :create -> gettext("create") - :update -> gettext("update") - other -> to_string(other) - end - socket = socket - |> put_flash(:info, gettext("Member %{action} successfully", action: action)) + |> put_flash(:info, "Member #{socket.assigns.form.source.type}d successfully") |> push_patch(to: socket.assigns.patch) {:noreply, socket} @@ -131,27 +95,12 @@ defmodule MvWeb.MemberLive.FormComponent do not Enum.member?(existing_properties, Map.get(i, "property_type_id")) end - params = %{ - "properties" => - Enum.map(member.properties, fn prop -> - %{ - "property_type_id" => prop.property_type_id, - "value" => %{ - "_union_type" => Atom.to_string(prop.value.type), - "type" => prop.value.type, - "value" => prop.value.value - } - } - end) - } - form = AshPhoenix.Form.for_update( member, :update_member, api: Mv.Membership, as: "member", - params: params, forms: [auto?: true] ) diff --git a/lib/mv_web/member_live/index.ex b/lib/mv_web/member_live/index.ex index 452ebab..4e37429 100644 --- a/lib/mv_web/member_live/index.ex +++ b/lib/mv_web/member_live/index.ex @@ -5,10 +5,10 @@ defmodule MvWeb.MemberLive.Index do def render(assigns) do ~H""" <.header> - {gettext("Listing Members")} + Listing Members <:actions> <.link patch={~p"/members/new"}> - <.button>{gettext("New Member")} + <.button>New Member @@ -18,27 +18,22 @@ defmodule MvWeb.MemberLive.Index do rows={@streams.members} row_click={fn {_id, member} -> JS.navigate(~p"/members/#{member}") end} > - - <:col :let={{_id, member}} label={gettext("First Name")}>{member.first_name} - <:col :let={{_id, member}} label={gettext("Last Name")}>{member.last_name} - <:col :let={{_id, member}} label={gettext("Email")}>{member.email} - <:col :let={{_id, member}} label={gettext("City")}>{member.city} - <:col :let={{_id, member}} label={gettext("Join Date")}>{member.join_date} + <:col :let={{_id, member}} label="Id">{member.id} <:action :let={{_id, member}}>
- <.link navigate={~p"/members/#{member}"}>{gettext("Show")} + <.link navigate={~p"/members/#{member}"}>Show
- <.link patch={~p"/members/#{member}/edit"}>{gettext("Edit")} + <.link patch={~p"/members/#{member}/edit"}>Edit <:action :let={{id, member}}> <.link phx-click={JS.push("delete", value: %{id: member.id}) |> hide("##{id}")} - data-confirm={gettext("Are you sure?")} + data-confirm="Are you sure?" > - {gettext("Delete")} + Delete @@ -73,19 +68,19 @@ defmodule MvWeb.MemberLive.Index do defp apply_action(socket, :edit, %{"id" => id}) do socket - |> assign(:page_title, gettext("Edit Member")) + |> assign(:page_title, "Edit Member") |> assign(:member, Ash.get!(Mv.Membership.Member, id)) end defp apply_action(socket, :new, _params) do socket - |> assign(:page_title, gettext("New Member")) + |> assign(:page_title, "New Member") |> assign(:member, nil) end defp apply_action(socket, :index, _params) do socket - |> assign(:page_title, gettext("Listing Members")) + |> assign(:page_title, "Listing Members") |> assign(:member, nil) end diff --git a/lib/mv_web/member_live/show.ex b/lib/mv_web/member_live/show.ex index 612abd6..47e0f92 100644 --- a/lib/mv_web/member_live/show.ex +++ b/lib/mv_web/member_live/show.ex @@ -1,55 +1,25 @@ defmodule MvWeb.MemberLive.Show do use MvWeb, :live_view - import Ash.Query @impl true def render(assigns) do ~H""" <.header> - {@member.first_name} {@member.last_name} - <:subtitle>{gettext("This is a member record from your database.")} + Member {@member.id} + <:subtitle>This is a member record from your database. <:actions> <.link patch={~p"/members/#{@member}/show/edit"} phx-click={JS.push_focus()}> - <.button>{gettext("Edit member")} + <.button>Edit member <.list> - <:item title={gettext("Id")}>{@member.id} - <:item title={gettext("First Name")}>{@member.first_name} - <:item title={gettext("Last Name")}>{@member.last_name} - <:item title={gettext("Email")}>{@member.email} - <:item title={gettext("Birth Date")}>{@member.birth_date} - <:item title={gettext("Paid")}> - {if @member.paid, do: gettext("Yes"), else: gettext("No")} - - <:item title={gettext("Phone Number")}>{@member.phone_number} - <:item title={gettext("Join Date")}>{@member.join_date} - <:item title={gettext("Exit Date")}>{@member.exit_date} - <:item title={gettext("Notes")}>{@member.notes} - <:item title={gettext("City")}>{@member.city} - <:item title={gettext("Street")}>{@member.street} - <:item title={gettext("House Number")}>{@member.house_number} - <:item title={gettext("Postal Code")}>{@member.postal_code} + <:item title="Id">{@member.id} -

{gettext("Custom Properties")}

- <.generic_list items={ - Enum.map(@member.properties, fn p -> - { - # name - p.property_type && p.property_type.name, - # value - case p.value do - %{value: v} -> v - v -> v - end - } - end) - } /> - <.back navigate={~p"/members"}>{gettext("Back to members")} + <.back navigate={~p"/members"}>Back to members <.modal :if={@live_action == :edit} @@ -76,19 +46,12 @@ defmodule MvWeb.MemberLive.Show do @impl true def handle_params(%{"id" => id}, _, socket) do - query = - Mv.Membership.Member - |> filter(id == ^id) - |> load(properties: [:property_type]) - - member = Ash.read_one!(query) - {:noreply, socket |> assign(:page_title, page_title(socket.assigns.live_action)) - |> assign(:member, member)} + |> assign(:member, Ash.get!(Mv.Membership.Member, id))} end - defp page_title(:show), do: gettext("Show Member") - defp page_title(:edit), do: gettext("Edit Member") + defp page_title(:show), do: "Show Member" + defp page_title(:edit), do: "Edit Member" end diff --git a/lib/mv_web/router.ex b/lib/mv_web/router.ex index 595a2da..3e9bdca 100644 --- a/lib/mv_web/router.ex +++ b/lib/mv_web/router.ex @@ -1,10 +1,6 @@ defmodule MvWeb.Router do use MvWeb, :router - use AshAuthentication.Phoenix.Router - - import AshAuthentication.Plug.Helpers - pipeline :browser do plug :accepts, ["html"] plug :fetch_session @@ -12,92 +8,33 @@ defmodule MvWeb.Router do plug :put_root_layout, html: {MvWeb.Layouts, :root} plug :protect_from_forgery plug :put_secure_browser_headers - plug :load_from_session - plug :set_locale end pipeline :api do plug :accepts, ["json"] - plug :load_from_bearer - plug :set_actor, :user end scope "/", MvWeb do pipe_through :browser - ash_authentication_live_session :authenticated_routes do - # in each liveview, add one of the following at the top of the module: - # - # If an authenticated user must be present: - # on_mount {MvWeb.LiveUserAuth, :live_user_required} - # - # If an authenticated user *may* be present: - # on_mount {MvWeb.LiveUserAuth, :live_user_optional} - # - # If an authenticated user must *not* be present: - # on_mount {MvWeb.LiveUserAuth, :live_no_user} - end - end + get "/", PageController, :home + live "/members", MemberLive.Index, :index + live "/members/new", MemberLive.Index, :new + live "/members/:id/edit", MemberLive.Index, :edit + live "/members/:id", MemberLive.Show, :show + live "/members/:id/show/edit", MemberLive.Show, :edit - scope "/", MvWeb do - pipe_through :browser + live "/property_types", PropertyTypeLive.Index, :index + live "/property_types/new", PropertyTypeLive.Index, :new + live "/property_types/:id/edit", PropertyTypeLive.Index, :edit + live "/property_types/:id", PropertyTypeLive.Show, :show + live "/property_types/:id/show/edit", PropertyTypeLive.Show, :edit - @doc """ - AshAuthentication-specific: We define that all routes can only be accessed when the user is signed in. - """ - ash_authentication_live_session :authentication_required, - on_mount: {MvWeb.LiveUserAuth, :live_user_required} do - get "/", PageController, :home - - live "/members", MemberLive.Index, :index - live "/members/new", MemberLive.Index, :new - live "/members/:id/edit", MemberLive.Index, :edit - live "/members/:id", MemberLive.Show, :show - live "/members/:id/show/edit", MemberLive.Show, :edit - - live "/property_types", PropertyTypeLive.Index, :index - live "/property_types/new", PropertyTypeLive.Index, :new - live "/property_types/:id/edit", PropertyTypeLive.Index, :edit - live "/property_types/:id", PropertyTypeLive.Show, :show - live "/property_types/:id/show/edit", PropertyTypeLive.Show, :edit - - live "/properties", PropertyLive.Index, :index - live "/properties/new", PropertyLive.Index, :new - live "/properties/:id/edit", PropertyLive.Index, :edit - live "/properties/:id", PropertyLive.Show, :show - live "/properties/:id/show/edit", PropertyLive.Show, :edit - - post "/set_locale", LocaleController, :set_locale - end - - # ASHAUTHENTICATION GENERATED AUTH ROUTES - auth_routes AuthController, Mv.Accounts.User, path: "/auth" - sign_out_route AuthController - - # Remove these if you'd like to use your own authentication views - sign_in_route register_path: "/register", - reset_path: "/reset", - auth_routes_prefix: "/auth", - on_mount: [{MvWeb.LiveUserAuth, :live_no_user}], - overrides: [MvWeb.AuthOverrides, AshAuthentication.Phoenix.Overrides.Default], - gettext_backend: {MvWeb.Gettext, "default"} - - # Remove this if you do not want to use the reset password feature - reset_route auth_routes_prefix: "/auth", - overrides: [MvWeb.AuthOverrides, AshAuthentication.Phoenix.Overrides.Default], - gettext_backend: {MvWeb.Gettext, "default"} - - # Remove this if you do not use the confirmation strategy - confirm_route Mv.Accounts.User, :confirm_new_user, - auth_routes_prefix: "/auth", - overrides: [MvWeb.AuthOverrides, AshAuthentication.Phoenix.Overrides.Default], - gettext_backend: {MvWeb.Gettext, "default"} - - # Remove this if you do not use the magic link strategy. - # magic_sign_in_route(Mv.Accounts.User, :magic_link, - # auth_routes_prefix: "/auth", - # overrides: [MvWeb.AuthOverrides, AshAuthentication.Phoenix.Overrides.Default] - # ) + live "/properties", PropertyLive.Index, :index + live "/properties/new", PropertyLive.Index, :new + live "/properties/:id/edit", PropertyLive.Index, :edit + live "/properties/:id", PropertyLive.Show, :show + live "/properties/:id/show/edit", PropertyLive.Show, :edit end # Other scopes may use custom stacks. @@ -131,10 +68,4 @@ defmodule MvWeb.Router do ash_admin "/" end end - - defp set_locale(conn, _opts) do - locale = get_session(conn, :locale) || "en" - Gettext.put_locale(MvWeb.Gettext, locale) - conn - end end diff --git a/mix.exs b/mix.exs index 2284006..687a111 100644 --- a/mix.exs +++ b/mix.exs @@ -33,17 +33,13 @@ defmodule Mv.MixProject do # Type `mix help deps` for examples and options. defp deps do [ - {:tidewave, "~> 0.2", only: [:dev]}, {:sourceror, "~> 1.8", only: [:dev, :test]}, - {:live_debugger, "~> 0.3", only: [:dev]}, + {:live_debugger, "~> 0.1", only: [:dev]}, {:ash_admin, "~> 0.13"}, {:ash_postgres, "~> 2.0"}, {:ash_phoenix, "~> 2.0"}, {:ash, "~> 3.0"}, - {:bcrypt_elixir, "~> 3.0"}, - {:ash_authentication, "~> 4.9"}, - {:ash_authentication_phoenix, "~> 2.10"}, - {:igniter, "~> 0.6", only: [:dev, :test]}, + {:igniter, "~> 0.5", only: [:dev, :test]}, {:phoenix, "~> 1.7.20"}, {:phoenix_ecto, "~> 4.5"}, {:ecto_sql, "~> 3.10"}, @@ -53,27 +49,26 @@ defmodule Mv.MixProject do {:phoenix_live_view, "~> 1.0.0"}, {:floki, ">= 0.30.0", only: :test}, {:phoenix_live_dashboard, "~> 0.8.3"}, - {:esbuild, "~> 0.10", runtime: Mix.env() == :dev}, + {:esbuild, "~> 0.8", runtime: Mix.env() == :dev}, {:tailwind, "~> 0.2", runtime: Mix.env() == :dev}, {:heroicons, github: "tailwindlabs/heroicons", - tag: "v2.2.0", + tag: "v2.1.1", sparse: "optimized", app: false, compile: false, depth: 1}, {:swoosh, "~> 1.5"}, - {:finch, "~> 0.20"}, + {:finch, "~> 0.13"}, {:telemetry_metrics, "~> 1.0"}, {:telemetry_poller, "~> 1.0"}, {:gettext, "~> 0.26"}, {:jason, "~> 1.2"}, - {:dns_cluster, "~> 0.2.0"}, + {:dns_cluster, "~> 0.1.1"}, {:bandit, "~> 1.5"}, {:mix_audit, "~> 2.1", only: [:dev, :test], runtime: false}, - {:sobelow, "~> 0.14", only: [:dev, :test], runtime: false}, - {:credo, "~> 1.7", only: [:dev, :test], runtime: false}, - {:ecto_commons, "~> 0.3"} + {:sobelow, "~> 0.13", only: [:dev, :test], runtime: false}, + {:credo, "~> 1.7", only: [:dev, :test], runtime: false} ] end @@ -95,8 +90,7 @@ defmodule Mv.MixProject do "tailwind mv --minify", "esbuild mv --minify", "phx.digest" - ], - "phx.routes": ["phx.routes", "ash_authentication.phoenix.routes"] + ] ] end end diff --git a/mix.lock b/mix.lock index 6aaec65..b7190ef 100644 --- a/mix.lock +++ b/mix.lock @@ -1,87 +1,71 @@ %{ - "ash": {:hex, :ash, "3.5.26", "f2e884623bfc39e0228a4fee0c41fbdb90195c6b1e1618a0a97f03f2dfbb1c4f", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:ecto, "~> 3.7", [hex: :ecto, repo: "hexpm", optional: false]}, {:ets, "~> 0.8", [hex: :ets, repo: "hexpm", optional: false]}, {:igniter, ">= 0.6.4 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: false]}, {:picosat_elixir, "~> 0.2", [hex: :picosat_elixir, repo: "hexpm", optional: true]}, {:plug, ">= 0.0.0", [hex: :plug, repo: "hexpm", optional: true]}, {:reactor, "~> 0.11", [hex: :reactor, repo: "hexpm", optional: false]}, {:simple_sat, ">= 0.1.1 and < 1.0.0-0", [hex: :simple_sat, repo: "hexpm", optional: true]}, {:spark, ">= 2.2.65 and < 3.0.0-0", [hex: :spark, repo: "hexpm", optional: false]}, {:splode, ">= 0.2.6 and < 1.0.0-0", [hex: :splode, repo: "hexpm", optional: false]}, {:stream_data, "~> 1.0", [hex: :stream_data, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.1", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "a9f5edbb7d838e2053e84f62c428650dadbff3ea4ec40ef68f167483eb7e9012"}, - "ash_admin": {:hex, :ash_admin, "0.13.11", "00bf3228b09ed6137e49a68374262f1de2cd5e1ea43ac2a6e2666cce71b7032e", [:mix], [{:ash, ">= 3.4.63 and < 4.0.0-0", [hex: :ash, repo: "hexpm", optional: false]}, {:ash_phoenix, ">= 2.1.8 and < 3.0.0-0", [hex: :ash_phoenix, repo: "hexpm", optional: false]}, {:gettext, "~> 0.26", [hex: :gettext, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.7", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 4.1", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 1.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: false]}], "hexpm", "ed9e8833affb80454ba04c51a70ad96da95bb9d24429cf4f9d7cd538306c6256"}, - "ash_authentication": {:hex, :ash_authentication, "4.9.6", "c333fa8c2a61a64f70be1c69b8479967b3bce448e6420088821c0634dfdace81", [:mix], [{:argon2_elixir, "~> 4.0", [hex: :argon2_elixir, repo: "hexpm", optional: true]}, {:ash, ">= 3.4.29 and < 4.0.0-0", [hex: :ash, repo: "hexpm", optional: false]}, {:ash_postgres, ">= 2.6.8 and < 3.0.0-0", [hex: :ash_postgres, repo: "hexpm", optional: true]}, {:assent, "~> 0.2.13", [hex: :assent, repo: "hexpm", optional: false]}, {:bcrypt_elixir, "~> 3.0", [hex: :bcrypt_elixir, repo: "hexpm", optional: false]}, {:castore, "~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:finch, "~> 0.19", [hex: :finch, repo: "hexpm", optional: false]}, {:igniter, "~> 0.4", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:joken, "~> 2.5", [hex: :joken, repo: "hexpm", optional: false]}, {:plug, "~> 1.13", [hex: :plug, repo: "hexpm", optional: false]}, {:spark, "~> 2.0", [hex: :spark, repo: "hexpm", optional: false]}, {:splode, "~> 0.2", [hex: :splode, repo: "hexpm", optional: false]}], "hexpm", "805ca73cc6723c60f8cb988a7c1b883f0d0db317e77007e52b30508e0cc32674"}, - "ash_authentication_phoenix": {:hex, :ash_authentication_phoenix, "2.10.3", "b3c32e51a77eefc02c155eccdd17f1b697da3314fb40102854dcdd79288325b7", [:mix], [{:ash, "~> 3.0", [hex: :ash, repo: "hexpm", optional: false]}, {:ash_authentication, ">= 4.9.1 and < 5.0.0-0", [hex: :ash_authentication, repo: "hexpm", optional: false]}, {:ash_phoenix, "~> 2.0", [hex: :ash_phoenix, repo: "hexpm", optional: false]}, {:bcrypt_elixir, "~> 3.0", [hex: :bcrypt_elixir, repo: "hexpm", optional: false]}, {:gettext, "~> 0.26", [hex: :gettext, repo: "hexpm", optional: true]}, {:igniter, ">= 0.5.25 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.6", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_html_helpers, "~> 1.0", [hex: :phoenix_html_helpers, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 1.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: false]}, {:slugify, "~> 1.3", [hex: :slugify, repo: "hexpm", optional: false]}], "hexpm", "89be0de638123193933a54ae15b9d1c670bb4010775c38b2b22a99180ecc1ac3"}, - "ash_phoenix": {:hex, :ash_phoenix, "2.3.10", "c038cbcd0550a4a26d7ee2d936d2886415dfa69fc5952f45b0e3737c3293a4d3", [:mix], [{:ash, ">= 3.5.13 and < 4.0.0-0", [hex: :ash, repo: "hexpm", optional: false]}, {:igniter, "~> 0.6", [hex: :igniter, repo: "hexpm", optional: true]}, {:inertia, "~> 2.3", [hex: :inertia, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.5.6 or ~> 1.6", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.20.3 or ~> 1.0-rc.1", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:spark, ">= 2.2.29 and < 3.0.0-0", [hex: :spark, repo: "hexpm", optional: false]}], "hexpm", "c4b7d86e1636c82c6f6a89983af17f19e55f25b066120193d9d3524d2013456d"}, - "ash_postgres": {:hex, :ash_postgres, "2.6.10", "d39ede943fe26dbd69c7511797adcff7f5e4c85e11990e23ec90b9a52a464bf6", [:mix], [{:ash, ">= 3.5.13 and < 4.0.0-0", [hex: :ash, repo: "hexpm", optional: false]}, {:ash_sql, ">= 0.2.72 and < 1.0.0-0", [hex: :ash_sql, repo: "hexpm", optional: false]}, {:ecto, "~> 3.13", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "~> 3.13", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:igniter, "~> 0.6", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, ">= 0.0.0", [hex: :postgrex, repo: "hexpm", optional: false]}], "hexpm", "bcce5bf8c9913be1eb6c2b0be0709decfc79eac1148c3c37d28eca4fda753ebe"}, - "ash_sql": {:hex, :ash_sql, "0.2.85", "96a35d197f5ff846c17aca9225d4baafa0fda6c804f1e46a79345d237b5e5c5f", [:mix], [{:ash, ">= 3.5.25 and < 4.0.0-0", [hex: :ash, repo: "hexpm", optional: false]}, {:ecto, "~> 3.9", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "~> 3.9", [hex: :ecto_sql, repo: "hexpm", optional: false]}], "hexpm", "62e7f8b79bcb04d82654ba519008b80bd21bd177ec646e9fffb87ec34285722b"}, - "assent": {:hex, :assent, "0.2.13", "11226365d2d8661d23e9a2cf94d3255e81054ff9d88ac877f28bfdf38fa4ef31", [:mix], [{:certifi, ">= 0.0.0", [hex: :certifi, repo: "hexpm", optional: true]}, {:finch, "~> 0.15", [hex: :finch, repo: "hexpm", optional: true]}, {:jose, "~> 1.8", [hex: :jose, repo: "hexpm", optional: true]}, {:mint, "~> 1.0", [hex: :mint, repo: "hexpm", optional: true]}, {:req, "~> 0.4", [hex: :req, repo: "hexpm", optional: true]}, {:ssl_verify_fun, ">= 0.0.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: true]}], "hexpm", "bf9f351b01dd6bceea1d1f157f05438f6765ce606e6eb8d29296003d29bf6eab"}, - "bandit": {:hex, :bandit, "1.7.0", "d1564f30553c97d3e25f9623144bb8df11f3787a26733f00b21699a128105c0c", [:mix], [{:hpax, "~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}, {:plug, "~> 1.18", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:thousand_island, "~> 1.0", [hex: :thousand_island, repo: "hexpm", optional: false]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "3e2f7a98c7a11f48d9d8c037f7177cd39778e74d55c7af06fe6227c742a8168a"}, - "bcrypt_elixir": {:hex, :bcrypt_elixir, "3.3.2", "d50091e3c9492d73e17fc1e1619a9b09d6a5ef99160eb4d736926fd475a16ca3", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "471be5151874ae7931911057d1467d908955f93554f7a6cd1b7d804cac8cef53"}, + "ash": {:hex, :ash, "3.5.6", "2f187150110b4c280c8551ad411f56d95862fcb37c067a0b8b94eb682bcc43e8", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:ecto, "~> 3.7", [hex: :ecto, repo: "hexpm", optional: false]}, {:ets, "~> 0.8", [hex: :ets, repo: "hexpm", optional: false]}, {:igniter, ">= 0.5.24 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: false]}, {:owl, "~> 0.11", [hex: :owl, repo: "hexpm", optional: false]}, {:picosat_elixir, "~> 0.2", [hex: :picosat_elixir, repo: "hexpm", optional: true]}, {:plug, ">= 0.0.0", [hex: :plug, repo: "hexpm", optional: true]}, {:reactor, "~> 0.11", [hex: :reactor, repo: "hexpm", optional: false]}, {:simple_sat, ">= 0.1.1 and < 1.0.0-0", [hex: :simple_sat, repo: "hexpm", optional: true]}, {:spark, ">= 2.2.29 and < 3.0.0-0", [hex: :spark, repo: "hexpm", optional: false]}, {:splode, ">= 0.2.6 and < 1.0.0-0", [hex: :splode, repo: "hexpm", optional: false]}, {:stream_data, "~> 1.0", [hex: :stream_data, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.1", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "d0d9aeb5aacfdc12253fae1e7e4720991868c5f69632c2766afb03b2b1830f55"}, + "ash_admin": {:hex, :ash_admin, "0.13.4", "101bc40e299441a65d5c9e911f3801b6ab23eca2e53bb778ed0c6586993cc453", [:mix], [{:ash, ">= 3.4.63 and < 4.0.0-0", [hex: :ash, repo: "hexpm", optional: false]}, {:ash_phoenix, ">= 2.1.8 and < 3.0.0-0", [hex: :ash_phoenix, repo: "hexpm", optional: false]}, {:gettext, "~> 0.26", [hex: :gettext, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.7", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 4.1", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 1.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: false]}], "hexpm", "d546e2f0d87a745c2156c65960f7a7a8b89abd238be5bfabac2176e814846415"}, + "ash_phoenix": {:hex, :ash_phoenix, "2.2.0", "aee367f4b3e4c7cfb6a4f1bc219409e0d40961aa9eee5da2113572b66d9f620d", [:mix], [{:ash, ">= 3.4.31 and < 4.0.0-0", [hex: :ash, repo: "hexpm", optional: false]}, {:igniter, ">= 0.4.3 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.5.6 or ~> 1.6", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.20.3 or ~> 1.0-rc.1", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:spark, ">= 2.2.29 and < 3.0.0-0", [hex: :spark, repo: "hexpm", optional: false]}], "hexpm", "ac9d58609b4232094c4789c6bd5f9039d1caa14ca6a893d6ab6ac1aee984e122"}, + "ash_postgres": {:hex, :ash_postgres, "2.5.16", "9fc82621aea3c4777f9a322be8cdce10488f0eed50e7d75465285c131c30ec6b", [:mix], [{:ash, ">= 3.4.69 and < 4.0.0-0", [hex: :ash, repo: "hexpm", optional: false]}, {:ash_sql, ">= 0.2.68 and < 1.0.0-0", [hex: :ash_sql, repo: "hexpm", optional: false]}, {:ecto, ">= 3.12.1 and < 4.0.0-0", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "~> 3.12", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:igniter, ">= 0.5.16 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, ">= 0.0.0", [hex: :postgrex, repo: "hexpm", optional: false]}], "hexpm", "d9f328683ea861707f19e89c16c2c4f3527431c50071b2aea4bac6a822f4f448"}, + "ash_sql": {:hex, :ash_sql, "0.2.71", "40cabdd0c7af2eaa0096b2b0eae886085fed1e3b326e20434274120e11dec2c5", [:mix], [{:ash, "~> 3.5", [hex: :ash, repo: "hexpm", optional: false]}, {:ecto, "~> 3.9", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "~> 3.9", [hex: :ecto_sql, repo: "hexpm", optional: false]}], "hexpm", "6e22da3d020aecaca9858f430828c12988c3418d252fa39be3f43fde9fd4224d"}, + "bandit": {:hex, :bandit, "1.6.8", "be6fcbe01a74e6cba42ae35f4085acaeae9b2d8d360c0908d0b9addbc2811e47", [:mix], [{:hpax, "~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:thousand_island, "~> 1.0", [hex: :thousand_island, repo: "hexpm", optional: false]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "4fc08c8d4733735d175a007ecb25895e84d09292b0180a2e9f16948182c88b6e"}, "bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"}, - "castore": {:hex, :castore, "1.0.14", "4582dd7d630b48cf5e1ca8d3d42494db51e406b7ba704e81fbd401866366896a", [:mix], [], "hexpm", "7bc1b65249d31701393edaaac18ec8398d8974d52c647b7904d01b964137b9f4"}, - "circular_buffer": {:hex, :circular_buffer, "1.0.0", "25c004da0cba7bd8bc1bdabded4f9a902d095e20600fd15faf1f2ffbaea18a07", [:mix], [], "hexpm", "c829ec31c13c7bafd1f546677263dff5bfb006e929f25635878ac3cfba8749e5"}, - "comeonin": {:hex, :comeonin, "5.5.1", "5113e5f3800799787de08a6e0db307133850e635d34e9fab23c70b6501669510", [:mix], [], "hexpm", "65aac8f19938145377cee73973f192c5645873dcf550a8a6b18187d17c13ccdb"}, + "castore": {:hex, :castore, "1.0.12", "053f0e32700cbec356280c0e835df425a3be4bc1e0627b714330ad9d0f05497f", [:mix], [], "hexpm", "3dca286b2186055ba0c9449b4e95b97bf1b57b47c1f2644555879e659960c224"}, "credo": {:hex, :credo, "1.7.12", "9e3c20463de4b5f3f23721527fcaf16722ec815e70ff6c60b86412c695d426c1", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "8493d45c656c5427d9c729235b99d498bd133421f3e0a683e5c1b561471291e5"}, - "db_connection": {:hex, :db_connection, "2.8.0", "64fd82cfa6d8e25ec6660cea73e92a4cbc6a18b31343910427b702838c4b33b2", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "008399dae5eee1bf5caa6e86d204dcb44242c82b1ed5e22c881f2c34da201b15"}, + "db_connection": {:hex, :db_connection, "2.7.0", "b99faa9291bb09892c7da373bb82cba59aefa9b36300f6145c5f201c7adf48ec", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "dcf08f31b2701f857dfc787fbad78223d61a32204f217f15e881dd93e4bdd3ff"}, "decimal": {:hex, :decimal, "2.3.0", "3ad6255aa77b4a3c4f818171b12d237500e63525c2fd056699967a3e7ea20f62", [:mix], [], "hexpm", "a4d66355cb29cb47c3cf30e71329e58361cfcb37c34235ef3bf1d7bf3773aeac"}, - "dns_cluster": {:hex, :dns_cluster, "0.2.0", "aa8eb46e3bd0326bd67b84790c561733b25c5ba2fe3c7e36f28e88f384ebcb33", [:mix], [], "hexpm", "ba6f1893411c69c01b9e8e8f772062535a4cf70f3f35bcc964a324078d8c8240"}, - "ecto": {:hex, :ecto, "3.13.2", "7d0c0863f3fc8d71d17fc3ad3b9424beae13f02712ad84191a826c7169484f01", [:mix], [{:decimal, "~> 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", "669d9291370513ff56e7b7e7081b7af3283d02e046cf3d403053c557894a0b3e"}, - "ecto_commons": {:hex, :ecto_commons, "0.3.6", "7b1d9e59396cf8c8cbe5a26d50d03f9b6d0fe6c640210dd503622f276f1e59bb", [:mix], [{:burnex, "~> 3.0", [hex: :burnex, repo: "hexpm", optional: true]}, {:ecto, "~> 3.4", [hex: :ecto, repo: "hexpm", optional: false]}, {:ex_phone_number, "~> 0.2", [hex: :ex_phone_number, repo: "hexpm", optional: false]}, {:luhn, "~> 0.3.0", [hex: :luhn, repo: "hexpm", optional: false]}], "hexpm", "3f12981a1e398f206c5d2014e7b732b7ec91b110b9cb84875cb5b28fc75d7a0a"}, - "ecto_sql": {:hex, :ecto_sql, "3.13.2", "a07d2461d84107b3d037097c822ffdd36ed69d1cf7c0f70e12a3d1decf04e2e1", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.13.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.7", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.19 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", "539274ab0ecf1a0078a6a72ef3465629e4d6018a3028095dc90f60a19c371717"}, - "elixir_make": {:hex, :elixir_make, "0.9.0", "6484b3cd8c0cee58f09f05ecaf1a140a8c97670671a6a0e7ab4dc326c3109726", [:mix], [], "hexpm", "db23d4fd8b757462ad02f8aa73431a426fe6671c80b200d9710caf3d1dd0ffdb"}, - "esbuild": {:hex, :esbuild, "0.10.0", "b0aa3388a1c23e727c5a3e7427c932d89ee791746b0081bbe56103e9ef3d291f", [:mix], [{:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "468489cda427b974a7cc9f03ace55368a83e1a7be12fba7e30969af78e5f8c70"}, + "dns_cluster": {:hex, :dns_cluster, "0.1.3", "0bc20a2c88ed6cc494f2964075c359f8c2d00e1bf25518a6a6c7fd277c9b0c66", [:mix], [], "hexpm", "46cb7c4a1b3e52c7ad4cbe33ca5079fbde4840dedeafca2baf77996c2da1bc33"}, + "ecto": {:hex, :ecto, "3.12.5", "4a312960ce612e17337e7cefcf9be45b95a3be6b36b6f94dfb3d8c361d631866", [:mix], [{:decimal, "~> 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", "6eb18e80bef8bb57e17f5a7f068a1719fbda384d40fc37acb8eb8aeca493b6ea"}, + "ecto_sql": {:hex, :ecto_sql, "3.12.1", "c0d0d60e85d9ff4631f12bafa454bc392ce8b9ec83531a412c12a0d415a3a4d0", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.12", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.7", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.19 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", "aff5b958a899762c5f09028c847569f7dfb9cc9d63bdb8133bff8a5546de6bf5"}, + "esbuild": {:hex, :esbuild, "0.9.0", "f043eeaca4932ca8e16e5429aebd90f7766f31ac160a25cbd9befe84f2bc068f", [:mix], [{:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "b415027f71d5ab57ef2be844b2a10d0c1b5a492d431727f43937adce22ba45ae"}, "ets": {:hex, :ets, "0.9.0", "79c6a6c205436780486f72d84230c6cba2f8a9920456750ddd1e47389107d5fd", [:mix], [], "hexpm", "2861fdfb04bcaeff370f1a5904eec864f0a56dcfebe5921ea9aadf2a481c822b"}, - "ex_phone_number": {:hex, :ex_phone_number, "0.4.5", "2065cc48c3e9d1ed9821f50877c32f2f6898362cb990f44147ca217c5d1374ed", [:mix], [{:sweet_xml, "~> 0.7", [hex: :sweet_xml, repo: "hexpm", optional: false]}], "hexpm", "67163f8706f8cbfef1b1f4b9230c461f19786d0d79fd0b22cbeeefc6f0b99d4a"}, "expo": {:hex, :expo, "1.1.0", "f7b9ed7fb5745ebe1eeedf3d6f29226c5dd52897ac67c0f8af62a07e661e5c75", [:mix], [], "hexpm", "fbadf93f4700fb44c331362177bdca9eeb8097e8b0ef525c9cc501cb9917c960"}, "file_system": {:hex, :file_system, "1.1.0", "08d232062284546c6c34426997dd7ef6ec9f8bbd090eb91780283c9016840e8f", [:mix], [], "hexpm", "bfcf81244f416871f2a2e15c1b515287faa5db9c6bcf290222206d120b3d43f6"}, - "finch": {:hex, :finch, "0.20.0", "5330aefb6b010f424dcbbc4615d914e9e3deae40095e73ab0c1bb0968933cadf", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.6.2 or ~> 1.7", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 1.1", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "2658131a74d051aabfcba936093c903b8e89da9a1b63e430bee62045fa9b2ee2"}, - "floki": {:hex, :floki, "0.38.0", "62b642386fa3f2f90713f6e231da0fa3256e41ef1089f83b6ceac7a3fd3abf33", [:mix], [], "hexpm", "a5943ee91e93fb2d635b612caf5508e36d37548e84928463ef9dd986f0d1abd9"}, + "finch": {:hex, :finch, "0.19.0", "c644641491ea854fc5c1bbaef36bfc764e3f08e7185e1f084e35e0672241b76d", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.6.2 or ~> 1.7", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 1.1", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "fc5324ce209125d1e2fa0fcd2634601c52a787aff1cd33ee833664a5af4ea2b6"}, + "floki": {:hex, :floki, "0.37.0", "b83e0280bbc6372f2a403b2848013650b16640cd2470aea6701f0632223d719e", [:mix], [], "hexpm", "516a0c15a69f78c47dc8e0b9b3724b29608aa6619379f91b1ffa47109b5d0dd3"}, "gettext": {:hex, :gettext, "0.26.2", "5978aa7b21fada6deabf1f6341ddba50bc69c999e812211903b169799208f2a8", [:mix], [{:expo, "~> 0.5.1 or ~> 1.0", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "aa978504bcf76511efdc22d580ba08e2279caab1066b76bb9aa81c4a1e0a32a5"}, "glob_ex": {:hex, :glob_ex, "0.1.11", "cb50d3f1ef53f6ca04d6252c7fde09fd7a1cf63387714fe96f340a1349e62c93", [:mix], [], "hexpm", "342729363056e3145e61766b416769984c329e4378f1d558b63e341020525de4"}, - "heroicons": {:git, "https://github.com/tailwindlabs/heroicons.git", "0435d4ca364a608cc75e2f8683d374e55abbae26", [tag: "v2.2.0", sparse: "optimized", depth: 1]}, - "hpax": {:hex, :hpax, "1.0.3", "ed67ef51ad4df91e75cc6a1494f851850c0bd98ebc0be6e81b026e765ee535aa", [:mix], [], "hexpm", "8eab6e1cfa8d5918c2ce4ba43588e894af35dbd8e91e6e55c817bca5847df34a"}, - "igniter": {:hex, :igniter, "0.6.19", "d87703b36890bc4278341d966a7ed8e10604a18610a4331ac10c75d1af48fff4", [:mix], [{:glob_ex, "~> 0.1.7", [hex: :glob_ex, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:owl, "~> 0.11", [hex: :owl, repo: "hexpm", optional: false]}, {:phx_new, "~> 1.7", [hex: :phx_new, repo: "hexpm", optional: true]}, {:req, "~> 0.5", [hex: :req, repo: "hexpm", optional: false]}, {:rewrite, ">= 1.1.1 and < 2.0.0-0", [hex: :rewrite, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.4", [hex: :sourceror, repo: "hexpm", optional: false]}, {:spitfire, ">= 0.1.3 and < 1.0.0-0", [hex: :spitfire, repo: "hexpm", optional: false]}], "hexpm", "c2070b3fdbd238fc0a0bfbc1f125b5c0f79a1fe2f5b3c7b43cd33de696783663"}, + "heroicons": {:git, "https://github.com/tailwindlabs/heroicons.git", "88ab3a0d790e6a47404cba02800a6b25d2afae50", [tag: "v2.1.1", sparse: "optimized", depth: 1]}, + "hpax": {:hex, :hpax, "1.0.2", "762df951b0c399ff67cc57c3995ec3cf46d696e41f0bba17da0518d94acd4aac", [:mix], [], "hexpm", "2f09b4c1074e0abd846747329eaa26d535be0eb3d189fa69d812bfb8bfefd32f"}, + "igniter": {:hex, :igniter, "0.5.46", "e3ad5b07a194b6e550ddd303bac45a126a65c6157c8acb664b22011cac8e34fd", [:mix], [{:glob_ex, "~> 0.1.7", [hex: :glob_ex, repo: "hexpm", optional: false]}, {:inflex, "~> 2.0", [hex: :inflex, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:owl, "~> 0.11", [hex: :owl, repo: "hexpm", optional: false]}, {:phx_new, "~> 1.7", [hex: :phx_new, repo: "hexpm", optional: true]}, {:req, "~> 0.5", [hex: :req, repo: "hexpm", optional: false]}, {:rewrite, ">= 1.1.1 and < 2.0.0-0", [hex: :rewrite, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.4", [hex: :sourceror, repo: "hexpm", optional: false]}, {:spitfire, ">= 0.1.3 and < 1.0.0-0", [hex: :spitfire, repo: "hexpm", optional: false]}], "hexpm", "64c59b696b678b2b83e2ee923f5254ac6479aff6c65dd513383bc0e4cdaeeeb7"}, "inflex": {:hex, :inflex, "2.1.0", "a365cf0821a9dacb65067abd95008ca1b0bb7dcdd85ae59965deef2aa062924c", [:mix], [], "hexpm", "14c17d05db4ee9b6d319b0bff1bdf22aa389a25398d1952c7a0b5f3d93162dd8"}, "iterex": {:hex, :iterex, "0.1.2", "58f9b9b9a22a55cbfc7b5234a9c9c63eaac26d276b3db80936c0e1c60355a5a6", [:mix], [], "hexpm", "2e103b8bcc81757a9af121f6dc0df312c9a17220f302b1193ef720460d03029d"}, "jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"}, - "joken": {:hex, :joken, "2.6.2", "5daaf82259ca603af4f0b065475099ada1b2b849ff140ccd37f4b6828ca6892a", [:mix], [{:jose, "~> 1.11.10", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm", "5134b5b0a6e37494e46dbf9e4dad53808e5e787904b7c73972651b51cce3d72b"}, - "jose": {:hex, :jose, "1.11.10", "a903f5227417bd2a08c8a00a0cbcc458118be84480955e8d251297a425723f83", [:mix, :rebar3], [], "hexpm", "0d6cd36ff8ba174db29148fc112b5842186b68a90ce9fc2b3ec3afe76593e614"}, "libgraph": {:hex, :libgraph, "0.16.0", "3936f3eca6ef826e08880230f806bfea13193e49bf153f93edcf0239d4fd1d07", [:mix], [], "hexpm", "41ca92240e8a4138c30a7e06466acc709b0cbb795c643e9e17174a178982d6bf"}, - "live_debugger": {:hex, :live_debugger, "0.3.1", "4b4d36481c3b0a49ec082c8268d37974ece34d2091ac323ccc0c906eb0c0d032", [:mix], [{:igniter, ">= 0.5.40 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: true]}, {:phoenix_live_view, "~> 0.20.4 or ~> 1.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}], "hexpm", "e50836495134b0dde98dc96919340749130ecb83618ea99d63a3a58ed1dcc27d"}, - "luhn": {:hex, :luhn, "0.3.3", "5aa0c6a32c2db4b9db9f9b883ba8301c1ae169d57199b9e6cb1ba2707bc51d96", [:mix], [], "hexpm", "3e823a913a25aab51352c727f135278d22954874d5f0835be81ed4fec3daf78d"}, - "mime": {:hex, :mime, "2.0.7", "b8d739037be7cd402aee1ba0306edfdef982687ee7e9859bee6198c1e7e2f128", [:mix], [], "hexpm", "6171188e399ee16023ffc5b76ce445eb6d9672e2e241d2df6050f3c771e80ccd"}, + "live_debugger": {:hex, :live_debugger, "0.1.5", "e7324a186071ac19885945e4ca7f3257ee07ed8c4ac5862305cab1fd595073aa", [:mix], [{:igniter, ">= 0.5.40 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: true]}, {:phoenix_live_view, "~> 0.20 or ~> 1.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}], "hexpm", "dc9416960bfe12873bc37707d4669797850f4e8ca4fe192f3195330e1c623634"}, + "mime": {:hex, :mime, "2.0.6", "8f18486773d9b15f95f4f4f1e39b710045fa1de891fada4516559967276e4dc2", [:mix], [], "hexpm", "c9945363a6b26d747389aac3643f8e0e09d30499a138ad64fe8fd1d13d9b153e"}, "mint": {:hex, :mint, "1.7.1", "113fdb2b2f3b59e47c7955971854641c61f378549d73e829e1768de90fc1abf1", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0 or ~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "fceba0a4d0f24301ddee3024ae116df1c3f4bb7a563a731f45fdfeb9d39a231b"}, - "mix_audit": {:hex, :mix_audit, "2.1.5", "c0f77cee6b4ef9d97e37772359a187a166c7a1e0e08b50edf5bf6959dfe5a016", [:make, :mix], [{:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:yaml_elixir, "~> 2.11", [hex: :yaml_elixir, repo: "hexpm", optional: false]}], "hexpm", "87f9298e21da32f697af535475860dc1d3617a010e0b418d2ec6142bc8b42d69"}, + "mix_audit": {:hex, :mix_audit, "2.1.4", "0a23d5b07350cdd69001c13882a4f5fb9f90fbd4cbf2ebc190a2ee0d187ea3e9", [:make, :mix], [{:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:yaml_elixir, "~> 2.11", [hex: :yaml_elixir, repo: "hexpm", optional: false]}], "hexpm", "fd807653cc8c1cada2911129c7eb9e985e3cc76ebf26f4dd628bb25bbcaa7099"}, "nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"}, "nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"}, "owl": {:hex, :owl, "0.12.2", "65906b525e5c3ef51bab6cba7687152be017aebe1da077bb719a5ee9f7e60762", [:mix], [{:ucwidth, "~> 0.2", [hex: :ucwidth, repo: "hexpm", optional: true]}], "hexpm", "6398efa9e1fea70a04d24231e10dcd66c1ac1aa2da418d20ef5357ec61de2880"}, - "phoenix": {:hex, :phoenix, "1.7.21", "14ca4f1071a5f65121217d6b57ac5712d1857e40a0833aff7a691b7870fc9a3b", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, 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]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.7", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.5.3", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "336dce4f86cba56fed312a7d280bf2282c720abb6074bdb1b61ec8095bdd0bc9"}, - "phoenix_ecto": {:hex, :phoenix_ecto, "4.6.5", "c4ef322acd15a574a8b1a08eff0ee0a85e73096b53ce1403b6563709f15e1cea", [:mix], [{:ecto, "~> 3.5", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.1", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm", "26ec3208eef407f31b748cadd044045c6fd485fbff168e35963d2f9dfff28d4b"}, + "phoenix": {:hex, :phoenix, "1.7.20", "6bababaf27d59f5628f9b608de902a021be2cecefb8231e1dbdc0a2e2e480e9b", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, 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]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.7", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.5.3", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "6be2ab98302e8784a31829e0d50d8bdfa81a23cd912c395bafd8b8bfb5a086c2"}, + "phoenix_ecto": {:hex, :phoenix_ecto, "4.6.3", "f686701b0499a07f2e3b122d84d52ff8a31f5def386e03706c916f6feddf69ef", [:mix], [{:ecto, "~> 3.5", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.1", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm", "909502956916a657a197f94cc1206d9a65247538de8a5e186f7537c895d95764"}, "phoenix_html": {:hex, :phoenix_html, "4.2.1", "35279e2a39140068fc03f8874408d58eef734e488fc142153f055c5454fd1c08", [:mix], [], "hexpm", "cff108100ae2715dd959ae8f2a8cef8e20b593f8dfd031c9cba92702cf23e053"}, - "phoenix_html_helpers": {:hex, :phoenix_html_helpers, "1.0.1", "7eed85c52eff80a179391036931791ee5d2f713d76a81d0d2c6ebafe1e11e5ec", [:mix], [{:phoenix_html, "~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "cffd2385d1fa4f78b04432df69ab8da63dc5cf63e07b713a4dcf36a3740e3090"}, - "phoenix_live_dashboard": {:hex, :phoenix_live_dashboard, "0.8.7", "405880012cb4b706f26dd1c6349125bfc903fb9e44d1ea668adaf4e04d4884b7", [:mix], [{:ecto, "~> 3.6.2 or ~> 3.7", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_mysql_extras, "~> 0.5", [hex: :ecto_mysql_extras, repo: "hexpm", optional: true]}, {:ecto_psql_extras, "~> 0.7", [hex: :ecto_psql_extras, repo: "hexpm", optional: true]}, {:ecto_sqlite3_extras, "~> 1.1.7 or ~> 1.2.0", [hex: :ecto_sqlite3_extras, repo: "hexpm", optional: true]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.19 or ~> 1.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6 or ~> 1.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "3a8625cab39ec261d48a13b7468dc619c0ede099601b084e343968309bd4d7d7"}, - "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.6.0", "2791fac0e2776b640192308cc90c0dbcf67843ad51387ed4ecae2038263d708d", [:mix], [{:file_system, "~> 0.2.10 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "b3a1fa036d7eb2f956774eda7a7638cf5123f8f2175aca6d6420a7f95e598e1c"}, - "phoenix_live_view": {:hex, :phoenix_live_view, "1.0.17", "beeb16d83a7d3760f7ad463df94e83b087577665d2acc0bf2987cd7d9778068f", [:mix], [{:floki, "~> 0.36", [hex: :floki, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0 or ~> 1.8.0-rc", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3 or ~> 4.0", [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]}, {:plug, "~> 1.15", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "a4ca05c1eb6922c4d07a508a75bfa12c45e5f4d8f77ae83283465f02c53741e1"}, + "phoenix_live_dashboard": {:hex, :phoenix_live_dashboard, "0.8.6", "7b1f0327f54c9eb69845fd09a77accf922f488c549a7e7b8618775eb603a62c7", [:mix], [{:ecto, "~> 3.6.2 or ~> 3.7", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_mysql_extras, "~> 0.5", [hex: :ecto_mysql_extras, repo: "hexpm", optional: true]}, {:ecto_psql_extras, "~> 0.7", [hex: :ecto_psql_extras, repo: "hexpm", optional: true]}, {:ecto_sqlite3_extras, "~> 1.1.7 or ~> 1.2.0", [hex: :ecto_sqlite3_extras, repo: "hexpm", optional: true]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.19 or ~> 1.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6 or ~> 1.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "1681ab813ec26ca6915beb3414aa138f298e17721dc6a2bde9e6eb8a62360ff6"}, + "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.5.3", "f2161c207fda0e4fb55165f650f7f8db23f02b29e3bff00ff7ef161d6ac1f09d", [:mix], [{:file_system, "~> 0.3 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "b4ec9cd73cb01ff1bd1cac92e045d13e7030330b74164297d1aee3907b54803c"}, + "phoenix_live_view": {:hex, :phoenix_live_view, "1.0.5", "f072166f87c44ffaf2b47b65c5ced8c375797830e517bfcf0a006fe7eb113911", [:mix], [{:floki, "~> 0.36", [hex: :floki, repo: "hexpm", optional: true]}, {: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.3 or ~> 4.0", [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]}, {:plug, "~> 1.15", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "94abbc84df8a93a64514fc41528695d7326b6f3095e906b32f264ec4280811f3"}, "phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.3", "3168d78ba41835aecad272d5e8cd51aa87a7ac9eb836eabc42f6e57538e3731d", [:mix], [], "hexpm", "bba06bc1dcfd8cb086759f0edc94a8ba2bc8896d5331a1e2c2902bf8e36ee502"}, "phoenix_template": {:hex, :phoenix_template, "1.0.4", "e2092c132f3b5e5b2d49c96695342eb36d0ed514c5b252a77048d5969330d639", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "2c0c81f0e5c6753faf5cca2f229c9709919aba34fab866d3bc05060c9c444206"}, "phoenix_view": {:hex, :phoenix_view, "2.0.4", "b45c9d9cf15b3a1af5fb555c674b525391b6a1fe975f040fb4d913397b31abf4", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}], "hexpm", "4e992022ce14f31fe57335db27a28154afcc94e9983266835bb3040243eb620b"}, - "plug": {:hex, :plug, "1.18.1", "5067f26f7745b7e31bc3368bc1a2b818b9779faa959b49c934c17730efc911cf", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "57a57db70df2b422b564437d2d33cf8d33cd16339c1edb190cd11b1a3a546cc2"}, - "plug_crypto": {:hex, :plug_crypto, "2.1.1", "19bda8184399cb24afa10be734f84a16ea0a2bc65054e23a62bb10f06bc89491", [:mix], [], "hexpm", "6470bce6ffe41c8bd497612ffde1a7e4af67f36a15eea5f921af71cf3e11247c"}, + "plug": {:hex, :plug, "1.17.0", "a0832e7af4ae0f4819e0c08dd2e7482364937aea6a8a997a679f2cbb7e026b2e", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "f6692046652a69a00a5a21d0b7e11fcf401064839d59d6b8787f23af55b1e6bc"}, + "plug_crypto": {:hex, :plug_crypto, "2.1.0", "f44309c2b06d249c27c8d3f65cfe08158ade08418cf540fd4f72d4d6863abb7b", [:mix], [], "hexpm", "131216a4b030b8f8ce0f26038bc4421ae60e4bb95c5cf5395e1421437824c4fa"}, "postgrex": {:hex, :postgrex, "0.20.0", "363ed03ab4757f6bc47942eff7720640795eb557e1935951c1626f0d303a3aed", [:mix], [{: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", "d36ef8b36f323d29505314f704e21a1a038e2dc387c6409ee0cd24144e187c0f"}, - "reactor": {:hex, :reactor, "0.15.6", "d717f9add549b25a089a94c90197718d2d838e35d81dd776b1d81587d4cf2aaa", [:mix], [{:igniter, "~> 0.4", [hex: :igniter, repo: "hexpm", optional: true]}, {:iterex, "~> 0.1", [hex: :iterex, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:libgraph, "~> 0.16", [hex: :libgraph, repo: "hexpm", optional: false]}, {:spark, "~> 2.0", [hex: :spark, repo: "hexpm", optional: false]}, {:splode, "~> 0.2", [hex: :splode, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.2", [hex: :telemetry, repo: "hexpm", optional: false]}, {:yaml_elixir, "~> 2.11", [hex: :yaml_elixir, repo: "hexpm", optional: false]}, {:ymlr, "~> 5.0", [hex: :ymlr, repo: "hexpm", optional: false]}], "hexpm", "74db98165e3644d86e0f723672d91ceca4339eaa935bcad7e78bf146a46d77b9"}, - "req": {:hex, :req, "0.5.15", "662020efb6ea60b9f0e0fac9be88cd7558b53fe51155a2d9899de594f9906ba9", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 2.0.6 or ~> 2.1", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "a6513a35fad65467893ced9785457e91693352c70b58bbc045b47e5eb2ef0c53"}, + "reactor": {:hex, :reactor, "0.15.2", "8c1b3fe0527b7a92b0b22c3f33f2e66858dd069bf1dd51d1031f63cd8cbd1fd5", [:mix], [{:igniter, "~> 0.4", [hex: :igniter, repo: "hexpm", optional: true]}, {:iterex, "~> 0.1", [hex: :iterex, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:libgraph, "~> 0.16", [hex: :libgraph, repo: "hexpm", optional: false]}, {:spark, "~> 2.0", [hex: :spark, repo: "hexpm", optional: false]}, {:splode, "~> 0.2", [hex: :splode, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.2", [hex: :telemetry, repo: "hexpm", optional: false]}, {:yaml_elixir, "~> 2.11", [hex: :yaml_elixir, repo: "hexpm", optional: false]}, {:ymlr, "~> 5.0", [hex: :ymlr, repo: "hexpm", optional: false]}], "hexpm", "091435a1fa0cab9bc2ed3934b203a0fd190f62e8b6aca63741f9242b8c7631ac"}, + "req": {:hex, :req, "0.5.10", "a3a063eab8b7510785a467f03d30a8d95f66f5c3d9495be3474b61459c54376c", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 2.0.6 or ~> 2.1", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "8a604815743f8a2d3b5de0659fa3137fa4b1cffd636ecb69b30b2b9b2c2559be"}, "rewrite": {:hex, :rewrite, "1.1.2", "f5a5d10f5fed1491a6ff48e078d4585882695962ccc9e6c779bae025d1f92eda", [:mix], [{:glob_ex, "~> 0.1", [hex: :glob_ex, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.0", [hex: :sourceror, repo: "hexpm", optional: false]}, {:text_diff, "~> 0.1", [hex: :text_diff, repo: "hexpm", optional: false]}], "hexpm", "7f8b94b1e3528d0a47b3e8b7bfeca559d2948a65fa7418a9ad7d7712703d39d4"}, - "slugify": {:hex, :slugify, "1.3.1", "0d3b8b7e5c1eeaa960e44dce94382bee34a39b3ea239293e457a9c5b47cc6fd3", [:mix], [], "hexpm", "cb090bbeb056b312da3125e681d98933a360a70d327820e4b7f91645c4d8be76"}, - "sobelow": {:hex, :sobelow, "0.14.0", "dd82aae8f72503f924fe9dd97ffe4ca694d2f17ec463dcfd365987c9752af6ee", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "7ecf91e298acfd9b24f5d761f19e8f6e6ac585b9387fb6301023f1f2cd5eed5f"}, - "sourceror": {:hex, :sourceror, "1.10.0", "38397dedbbc286966ec48c7af13e228b171332be1ad731974438c77791945ce9", [:mix], [], "hexpm", "29dbdfc92e04569c9d8e6efdc422fc1d815f4bd0055dc7c51b8800fb75c4b3f1"}, - "spark": {:hex, :spark, "2.2.67", "67626cb9f59ea4b1c5aa85d4afdd025e0740cbd49ed82665d0a40ff007d7fd4b", [:mix], [{:igniter, ">= 0.3.64 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: true]}, {:sourceror, "~> 1.2", [hex: :sourceror, repo: "hexpm", optional: true]}], "hexpm", "c8575402e3afc66871362e821bece890536d16319cdb758c5fb2d1250182e46f"}, - "spitfire": {:hex, :spitfire, "0.2.1", "29e154873f05444669c7453d3d931820822cbca5170e88f0f8faa1de74a79b47", [:mix], [], "hexpm", "6eeed75054a38341b2e1814d41bb0a250564092358de2669fdb57ff88141d91b"}, + "sobelow": {:hex, :sobelow, "0.13.0", "218afe9075904793f5c64b8837cc356e493d88fddde126a463839351870b8d1e", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "cd6e9026b85fc35d7529da14f95e85a078d9dd1907a9097b3ba6ac7ebbe34a0d"}, + "sourceror": {:hex, :sourceror, "1.9.0", "3bf5fe2d017aaabe3866d8a6da097dd7c331e0d2d54e59e21c2b066d47f1e08e", [:mix], [], "hexpm", "d20a9dd5efe162f0d75a307146faa2e17b823ea4f134f662358d70f0332fed82"}, + "spark": {:hex, :spark, "2.2.52", "50094275c9bbafa8e5e9eed0ab61983ee209a500e7044914ccf88e9921ae5082", [:mix], [{:igniter, ">= 0.3.64 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: true]}, {:sourceror, "~> 1.2", [hex: :sourceror, repo: "hexpm", optional: true]}], "hexpm", "f8de8c298bbbf7abd2a80d0ecabcefef65941f397cdbe94ce6165a121b09084f"}, + "spitfire": {:hex, :spitfire, "0.2.0", "0de1f519a23f65bde40d316adad53c07a9563f25cc68915d639d8a509a0aad8a", [:mix], [], "hexpm", "743daaee2d81a0d8095431729f478ce49b47ea8943c7d770de86704975cb7775"}, "splode": {:hex, :splode, "0.2.9", "3a2776e187c82f42f5226b33b1220ccbff74f4bcc523dd4039c804caaa3ffdc7", [:mix], [], "hexpm", "8002b00c6e24f8bd1bcced3fbaa5c33346048047bb7e13d2f3ad428babbd95c3"}, "stream_data": {:hex, :stream_data, "1.2.0", "58dd3f9e88afe27dc38bef26fce0c84a9e7a96772b2925c7b32cd2435697a52b", [:mix], [], "hexpm", "eb5c546ee3466920314643edf68943a5b14b32d1da9fe01698dc92b73f89a9ed"}, - "sweet_xml": {:hex, :sweet_xml, "0.7.5", "803a563113981aaac202a1dbd39771562d0ad31004ddbfc9b5090bdcd5605277", [:mix], [], "hexpm", "193b28a9b12891cae351d81a0cead165ffe67df1b73fe5866d10629f4faefb12"}, - "swoosh": {:hex, :swoosh, "1.19.3", "02ad4455939f502386e4e1443d4de94c514995fd0e51b3cafffd6bd270ffe81c", [:mix], [{:bandit, ">= 1.0.0", [hex: :bandit, repo: "hexpm", optional: true]}, {: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]}, {:mua, "~> 0.2.3", [hex: :mua, repo: "hexpm", optional: true]}, {:multipart, "~> 0.4", [hex: :multipart, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: true]}, {:plug_cowboy, ">= 1.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:req, "~> 0.5.10 or ~> 0.6 or ~> 1.0", [hex: :req, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "04a10f8496786b744b84130e3510eb53ca51e769c39511b65023bdf4136b732f"}, + "swoosh": {:hex, :swoosh, "1.18.2", "41279e8449b65d14b571b66afe9ab352c3b0179291af8e5f4ad9207f489ad11a", [:mix], [{:bandit, ">= 1.0.0", [hex: :bandit, repo: "hexpm", optional: true]}, {: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]}, {:mua, "~> 0.2.3", [hex: :mua, repo: "hexpm", optional: true]}, {:multipart, "~> 0.4", [hex: :multipart, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: true]}, {:plug_cowboy, ">= 1.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:req, "~> 0.5 or ~> 1.0", [hex: :req, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "032fcb2179f6d4e3b90030514ddc8d3946d8b046be939d121db480ca78adbc38"}, "tailwind": {:hex, :tailwind, "0.3.1", "a89d2835c580748c7a975ad7dd3f2ea5e63216dc16d44f9df492fbd12c094bed", [:mix], [], "hexpm", "98a45febdf4a87bc26682e1171acdedd6317d0919953c353fcd1b4f9f4b676a2"}, "telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"}, "telemetry_metrics": {:hex, :telemetry_metrics, "1.1.0", "5bd5f3b5637e0abea0426b947e3ce5dd304f8b3bc6617039e2b5a008adc02f8f", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "e7b79e8ddfde70adb6db8a6623d1778ec66401f366e9a8f5dd0955c56bc8ce67"}, - "telemetry_poller": {:hex, :telemetry_poller, "1.3.0", "d5c46420126b5ac2d72bc6580fb4f537d35e851cc0f8dbd571acf6d6e10f5ec7", [:rebar3], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "51f18bed7128544a50f75897db9974436ea9bfba560420b646af27a9a9b35211"}, + "telemetry_poller": {:hex, :telemetry_poller, "1.1.0", "58fa7c216257291caaf8d05678c8d01bd45f4bdbc1286838a28c4bb62ef32999", [:rebar3], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "9eb9d9cbfd81cbd7cdd24682f8711b6e2b691289a0de6826e58452f28c103c8f"}, "text_diff": {:hex, :text_diff, "0.1.0", "1caf3175e11a53a9a139bc9339bd607c47b9e376b073d4571c031913317fecaa", [:mix], [], "hexpm", "d1ffaaecab338e49357b6daa82e435f877e0649041ace7755583a0ea3362dbd7"}, - "thousand_island": {:hex, :thousand_island, "1.3.14", "ad45ebed2577b5437582bcc79c5eccd1e2a8c326abf6a3464ab6c06e2055a34a", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "d0d24a929d31cdd1d7903a4fe7f2409afeedff092d277be604966cd6aa4307ef"}, - "tidewave": {:hex, :tidewave, "0.2.0", "e98378803e535d3035138e4b354dcfca26b7f862fd44cffef5aa697b814c0b0b", [:mix], [{:circular_buffer, "~> 0.4 or ~> 1.0", [hex: :circular_buffer, repo: "hexpm", optional: false]}, {:igniter, ">= 0.5.47 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:plug, "~> 1.17", [hex: :plug, repo: "hexpm", optional: false]}, {:req, "~> 0.5", [hex: :req, repo: "hexpm", optional: false]}], "hexpm", "6ad11829f4600cd69955ffc66935e6456b775fea095172147244ba6f65986735"}, + "thousand_island": {:hex, :thousand_island, "1.3.11", "b68f3e91f74d564ae20b70d981bbf7097dde084343c14ae8a33e5b5fbb3d6f37", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "555c18c62027f45d9c80df389c3d01d86ba11014652c00be26e33b1b64e98d29"}, "websock": {:hex, :websock, "0.5.3", "2f69a6ebe810328555b6fe5c831a851f485e303a7c8ce6c5f675abeb20ebdadc", [:mix], [], "hexpm", "6105453d7fac22c712ad66fab1d45abdf049868f253cf719b625151460b8b453"}, "websock_adapter": {:hex, :websock_adapter, "0.5.8", "3b97dc94e407e2d1fc666b2fb9acf6be81a1798a2602294aac000260a7c4a47d", [:mix], [{:bandit, ">= 0.6.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "315b9a1865552212b5f35140ad194e67ce31af45bcee443d4ecb96b5fd3f3782"}, "yamerl": {:hex, :yamerl, "0.10.0", "4ff81fee2f1f6a46f1700c0d880b24d193ddb74bd14ef42cb0bcf46e81ef2f8e", [:rebar3], [], "hexpm", "346adb2963f1051dc837a2364e4acf6eb7d80097c0f53cbdc3046ec8ec4b4e6e"}, "yaml_elixir": {:hex, :yaml_elixir, "2.11.0", "9e9ccd134e861c66b84825a3542a1c22ba33f338d82c07282f4f1f52d847bd50", [:mix], [{:yamerl, "~> 0.10", [hex: :yamerl, repo: "hexpm", optional: false]}], "hexpm", "53cc28357ee7eb952344995787f4bb8cc3cecbf189652236e9b163e8ce1bc242"}, - "ymlr": {:hex, :ymlr, "5.1.4", "b924d61e1fc1ec371cde6ab3ccd9311110b1e052fc5c2460fb322e8380e7712a", [:mix], [], "hexpm", "75f16cf0709fbd911b30311a0359a7aa4b5476346c01882addefd5f2b1cfaa51"}, + "ymlr": {:hex, :ymlr, "5.1.3", "a8061add5a378e20272a31905be70209a5680fdbe0ad51f40cb1af4bdd0a010b", [:mix], [], "hexpm", "8663444fa85101a117887c170204d4c5a2182567e5f84767f0071cf15f2efb1e"}, } diff --git a/priv/gettext/de/LC_MESSAGES/default.po b/priv/gettext/de/LC_MESSAGES/default.po deleted file mode 100644 index 7e6d755..0000000 --- a/priv/gettext/de/LC_MESSAGES/default.po +++ /dev/null @@ -1,274 +0,0 @@ -## `msgid`s in this file come from POT (.pot) files. -## -## Do not add, change, or remove `msgid`s manually here as -## they're tied to the ones in the corresponding POT file -## (with the same domain). -## -## Use `mix gettext.extract --merge` or `mix gettext.merge` -## to merge POT files into PO files. -msgid "" -msgstr "" -"Language: en\n" - -#: lib/mv_web/components/core_components.ex:482 -#, elixir-autogen, elixir-format -msgid "Actions" -msgstr "" - -#: lib/mv_web/member_live/index.ex:39 -#, elixir-autogen, elixir-format -msgid "Are you sure?" -msgstr "Bist du sicher?" - -#: lib/mv_web/components/core_components.ex:160 -#, elixir-autogen, elixir-format -msgid "Attempting to reconnect" -msgstr "Verbindung wird wiederhergestellt" - -#: lib/mv_web/member_live/form_component.ex:50 -#: lib/mv_web/member_live/index.ex:25 -#: lib/mv_web/member_live/show.ex:32 -#, elixir-autogen, elixir-format -msgid "City" -msgstr "Stadt" - -#: lib/mv_web/member_live/index.ex:41 -#, elixir-autogen, elixir-format -msgid "Delete" -msgstr "Löschen" - -#: lib/mv_web/member_live/index.ex:33 -#, elixir-autogen, elixir-format -msgid "Edit" -msgstr "Bearbeiten" - -#: lib/mv_web/member_live/index.ex:76 -#: lib/mv_web/member_live/show.ex:93 -#, elixir-autogen, elixir-format -msgid "Edit Member" -msgstr "Mitglied bearbeiten" - -#: lib/mv_web/member_live/form_component.ex:43 -#: lib/mv_web/member_live/index.ex:24 -#: lib/mv_web/member_live/show.ex:23 -#, elixir-autogen, elixir-format -msgid "Email" -msgstr "E-Mail" - -#: lib/mv_web/components/core_components.ex:151 -#, elixir-autogen, elixir-format -msgid "Error!" -msgstr "Fehler!" - -#: lib/mv_web/member_live/form_component.ex:41 -#: lib/mv_web/member_live/index.ex:22 -#: lib/mv_web/member_live/show.ex:21 -#, elixir-autogen, elixir-format -msgid "First Name" -msgstr "Vorname" - -#: lib/mv_web/components/core_components.ex:172 -#, elixir-autogen, elixir-format -msgid "Hang in there while we get back on track" -msgstr "Bitte warten, wir stellen die Verbindung wieder her." - -#: lib/mv_web/member_live/form_component.ex:47 -#: lib/mv_web/member_live/index.ex:26 -#: lib/mv_web/member_live/show.ex:29 -#, elixir-autogen, elixir-format -msgid "Join Date" -msgstr "Beitrittsdatum" - -#: lib/mv_web/member_live/form_component.ex:42 -#: lib/mv_web/member_live/index.ex:23 -#: lib/mv_web/member_live/show.ex:22 -#, elixir-autogen, elixir-format -msgid "Last Name" -msgstr "Nachname" - -#: lib/mv_web/member_live/index.ex:8 -#: lib/mv_web/member_live/index.ex:88 -#, elixir-autogen, elixir-format -msgid "Listing Members" -msgstr "Mitglieder" - -#: lib/mv_web/member_live/index.ex:11 -#: lib/mv_web/member_live/index.ex:82 -#, elixir-autogen, elixir-format -msgid "New Member" -msgstr "Neues Mitglied" - -#: lib/mv_web/member_live/index.ex:30 -#, elixir-autogen, elixir-format -msgid "Show" -msgstr "Anzeigen" - -#: lib/mv_web/components/core_components.ex:167 -#, elixir-autogen, elixir-format -msgid "Something went wrong!" -msgstr "Etwas ist schiefgelaufen!" - -#: lib/mv_web/components/core_components.ex:150 -#, elixir-autogen, elixir-format -msgid "Success!" -msgstr "Erfolg!" - -#: lib/mv_web/components/core_components.ex:155 -#, elixir-autogen, elixir-format -msgid "We can't find the internet" -msgstr "Keine Internetverbindung gefunden" - -#: lib/mv_web/components/core_components.ex:76 -#: lib/mv_web/components/core_components.ex:130 -#, elixir-autogen, elixir-format -msgid "close" -msgstr "schließen" - -#: lib/mv_web/member_live/form_component.ex:44 -#: lib/mv_web/member_live/show.ex:24 -#, elixir-autogen, elixir-format -msgid "Birth Date" -msgstr "Geburtsdatum" - -#: lib/mv_web/member_live/form_component.ex:55 -#: lib/mv_web/member_live/show.ex:38 -#, elixir-autogen, elixir-format -msgid "Custom Properties" -msgstr "Eigene Eigenschaften" - -#: lib/mv_web/member_live/form_component.ex:48 -#: lib/mv_web/member_live/show.ex:30 -#, elixir-autogen, elixir-format -msgid "Exit Date" -msgstr "Austrittsdatum" - -#: lib/mv_web/member_live/form_component.ex:52 -#: lib/mv_web/member_live/show.ex:34 -#, elixir-autogen, elixir-format -msgid "House Number" -msgstr "Hausnummer" - -#: lib/mv_web/member_live/form_component.ex:49 -#: lib/mv_web/member_live/show.ex:31 -#, elixir-autogen, elixir-format -msgid "Notes" -msgstr "Notizen" - -#: lib/mv_web/member_live/form_component.ex:45 -#: lib/mv_web/member_live/show.ex:25 -#, elixir-autogen, elixir-format -msgid "Paid" -msgstr "Bezahlt" - -#: lib/mv_web/member_live/form_component.ex:46 -#: lib/mv_web/member_live/show.ex:28 -#, elixir-autogen, elixir-format -msgid "Phone Number" -msgstr "Telefonnummer" - -#: lib/mv_web/member_live/form_component.ex:53 -#: lib/mv_web/member_live/show.ex:35 -#, elixir-autogen, elixir-format -msgid "Postal Code" -msgstr "Postleitzahl" - -#: lib/mv_web/member_live/form_component.ex:75 -#, elixir-autogen, elixir-format -msgid "Save Member" -msgstr "Mitglied speichern" - -#: lib/mv_web/member_live/form_component.ex:75 -#, elixir-autogen, elixir-format -msgid "Saving..." -msgstr "Speichern..." - -#: lib/mv_web/member_live/form_component.ex:51 -#: lib/mv_web/member_live/show.ex:33 -#, elixir-autogen, elixir-format -msgid "Street" -msgstr "Straße" - -#: lib/mv_web/member_live/form_component.ex:30 -#, elixir-autogen, elixir-format -msgid "Use this form to manage member records and their properties." -msgstr "Dieses Formular dient zur Verwaltung von Mitgliedern und deren Eigenschaften." - -#: lib/mv_web/member_live/show.ex:52 -#, elixir-autogen, elixir-format -msgid "Back to members" -msgstr "Zurück zur Mitgliederliste" - -#: lib/mv_web/member_live/show.ex:14 -#, elixir-autogen, elixir-format -msgid "Edit member" -msgstr "Mitglied bearbeiten" - -#: lib/mv_web/member_live/show.ex:20 -#, elixir-autogen, elixir-format -msgid "Id" -msgstr "ID" - -#: lib/mv_web/member_live/show.ex:26 -#, elixir-autogen, elixir-format -msgid "No" -msgstr "Nein" - -#: lib/mv_web/member_live/show.ex:92 -#, elixir-autogen, elixir-format, fuzzy -msgid "Show Member" -msgstr "Mitglied anzeigen" - -#: lib/mv_web/member_live/show.ex:10 -#, elixir-autogen, elixir-format -msgid "This is a member record from your database." -msgstr "Dies ist ein Mitglied aus deiner Datenbank." - -#: lib/mv_web/member_live/show.ex:26 -#, elixir-autogen, elixir-format -msgid "Yes" -msgstr "Ja" - -#: lib/mv_web/member_live/form_component.ex:102 -#, elixir-autogen, elixir-format -msgid "create" -msgstr "erstellt" - -#: lib/mv_web/member_live/form_component.ex:103 -#, elixir-autogen, elixir-format -msgid "update" -msgstr "aktualisiert" - -#: lib/mv_web/controllers/auth_controller.ex:43 -#, elixir-autogen, elixir-format -msgid "Incorrect email or password" -msgstr "" - -#: lib/mv_web/member_live/form_component.ex:109 -#, elixir-autogen, elixir-format -msgid "Member %{action} successfully" -msgstr "Mitglied %{action} erfolgreich" - -#: lib/mv_web/controllers/auth_controller.ex:14 -#, elixir-autogen, elixir-format -msgid "You are now signed in" -msgstr "" - -#: lib/mv_web/controllers/auth_controller.ex:56 -#, elixir-autogen, elixir-format -msgid "You are now signed out" -msgstr "" - -#: lib/mv_web/controllers/auth_controller.ex:36 -#, elixir-autogen, elixir-format -msgid "You have already signed in another way, but have not confirmed your account.\nYou can confirm your account using the link we sent to you, or by resetting your password.\n" -msgstr "" - -#: lib/mv_web/controllers/auth_controller.ex:12 -#, elixir-autogen, elixir-format -msgid "Your email address has now been confirmed" -msgstr "" - -#: lib/mv_web/controllers/auth_controller.ex:13 -#, elixir-autogen, elixir-format -msgid "Your password has successfully been reset" -msgstr "" diff --git a/priv/gettext/de/LC_MESSAGES/errors.po b/priv/gettext/de/LC_MESSAGES/errors.po deleted file mode 100644 index 844c4f5..0000000 --- a/priv/gettext/de/LC_MESSAGES/errors.po +++ /dev/null @@ -1,112 +0,0 @@ -## `msgid`s in this file come from POT (.pot) files. -## -## Do not add, change, or remove `msgid`s manually here as -## they're tied to the ones in the corresponding POT file -## (with the same domain). -## -## Use `mix gettext.extract --merge` or `mix gettext.merge` -## to merge POT files into PO files. -msgid "" -msgstr "" -"Language: en\n" - -## From Ecto.Changeset.cast/4 -msgid "can't be blank" -msgstr "" - -## From Ecto.Changeset.unique_constraint/3 -msgid "has already been taken" -msgstr "" - -## From Ecto.Changeset.put_change/3 -msgid "is invalid" -msgstr "" - -## From Ecto.Changeset.validate_acceptance/3 -msgid "must be accepted" -msgstr "" - -## From Ecto.Changeset.validate_format/3 -msgid "has invalid format" -msgstr "" - -## From Ecto.Changeset.validate_subset/3 -msgid "has an invalid entry" -msgstr "" - -## From Ecto.Changeset.validate_exclusion/3 -msgid "is reserved" -msgstr "" - -## From Ecto.Changeset.validate_confirmation/3 -msgid "does not match confirmation" -msgstr "" - -## From Ecto.Changeset.no_assoc_constraint/3 -msgid "is still associated with this entry" -msgstr "" - -msgid "are still associated with this entry" -msgstr "" - -## From Ecto.Changeset.validate_length/3 -msgid "should have %{count} item(s)" -msgid_plural "should have %{count} item(s)" -msgstr[0] "" -msgstr[1] "" - -msgid "should be %{count} character(s)" -msgid_plural "should be %{count} character(s)" -msgstr[0] "" -msgstr[1] "" - -msgid "should be %{count} byte(s)" -msgid_plural "should be %{count} byte(s)" -msgstr[0] "" -msgstr[1] "" - -msgid "should have at least %{count} item(s)" -msgid_plural "should have at least %{count} item(s)" -msgstr[0] "" -msgstr[1] "" - -msgid "should be at least %{count} character(s)" -msgid_plural "should be at least %{count} character(s)" -msgstr[0] "" -msgstr[1] "" - -msgid "should be at least %{count} byte(s)" -msgid_plural "should be at least %{count} byte(s)" -msgstr[0] "" -msgstr[1] "" - -msgid "should have at most %{count} item(s)" -msgid_plural "should have at most %{count} item(s)" -msgstr[0] "" -msgstr[1] "" - -msgid "should be at most %{count} character(s)" -msgid_plural "should be at most %{count} character(s)" -msgstr[0] "" -msgstr[1] "" - -msgid "should be at most %{count} byte(s)" -msgid_plural "should be at most %{count} byte(s)" -msgstr[0] "" -msgstr[1] "" - -## From Ecto.Changeset.validate_number/3 -msgid "must be less than %{number}" -msgstr "" - -msgid "must be greater than %{number}" -msgstr "" - -msgid "must be less than or equal to %{number}" -msgstr "" - -msgid "must be greater than or equal to %{number}" -msgstr "" - -msgid "must be equal to %{number}" -msgstr "" diff --git a/priv/gettext/default.pot b/priv/gettext/default.pot deleted file mode 100644 index aad2ae9..0000000 --- a/priv/gettext/default.pot +++ /dev/null @@ -1,275 +0,0 @@ -## This file is a PO Template file. -## -## "msgid"s here are often extracted from source code. -## Add new messages manually only if they're dynamic -## messages that can't be statically extracted. -## -## Run "mix gettext.extract" to bring this file up to -## date. Leave "msgstr"s empty as changing them here has no -## effect: edit them in PO (.po) files instead. -# -msgid "" -msgstr "" - -#: lib/mv_web/components/core_components.ex:482 -#, elixir-autogen, elixir-format -msgid "Actions" -msgstr "" - -#: lib/mv_web/member_live/index.ex:39 -#, elixir-autogen, elixir-format -msgid "Are you sure?" -msgstr "" - -#: lib/mv_web/components/core_components.ex:160 -#, elixir-autogen, elixir-format -msgid "Attempting to reconnect" -msgstr "" - -#: lib/mv_web/member_live/form_component.ex:50 -#: lib/mv_web/member_live/index.ex:25 -#: lib/mv_web/member_live/show.ex:32 -#, elixir-autogen, elixir-format -msgid "City" -msgstr "" - -#: lib/mv_web/member_live/index.ex:41 -#, elixir-autogen, elixir-format -msgid "Delete" -msgstr "" - -#: lib/mv_web/member_live/index.ex:33 -#, elixir-autogen, elixir-format -msgid "Edit" -msgstr "" - -#: lib/mv_web/member_live/index.ex:76 -#: lib/mv_web/member_live/show.ex:93 -#, elixir-autogen, elixir-format -msgid "Edit Member" -msgstr "" - -#: lib/mv_web/member_live/form_component.ex:43 -#: lib/mv_web/member_live/index.ex:24 -#: lib/mv_web/member_live/show.ex:23 -#, elixir-autogen, elixir-format -msgid "Email" -msgstr "" - -#: lib/mv_web/components/core_components.ex:151 -#, elixir-autogen, elixir-format -msgid "Error!" -msgstr "" - -#: lib/mv_web/member_live/form_component.ex:41 -#: lib/mv_web/member_live/index.ex:22 -#: lib/mv_web/member_live/show.ex:21 -#, elixir-autogen, elixir-format -msgid "First Name" -msgstr "" - -#: lib/mv_web/components/core_components.ex:172 -#, elixir-autogen, elixir-format -msgid "Hang in there while we get back on track" -msgstr "" - -#: lib/mv_web/member_live/form_component.ex:47 -#: lib/mv_web/member_live/index.ex:26 -#: lib/mv_web/member_live/show.ex:29 -#, elixir-autogen, elixir-format -msgid "Join Date" -msgstr "" - -#: lib/mv_web/member_live/form_component.ex:42 -#: lib/mv_web/member_live/index.ex:23 -#: lib/mv_web/member_live/show.ex:22 -#, elixir-autogen, elixir-format -msgid "Last Name" -msgstr "" - -#: lib/mv_web/member_live/index.ex:8 -#: lib/mv_web/member_live/index.ex:88 -#, elixir-autogen, elixir-format -msgid "Listing Members" -msgstr "" - -#: lib/mv_web/member_live/index.ex:11 -#: lib/mv_web/member_live/index.ex:82 -#, elixir-autogen, elixir-format -msgid "New Member" -msgstr "" - -#: lib/mv_web/member_live/index.ex:30 -#, elixir-autogen, elixir-format -msgid "Show" -msgstr "" - -#: lib/mv_web/components/core_components.ex:167 -#, elixir-autogen, elixir-format -msgid "Something went wrong!" -msgstr "" - -#: lib/mv_web/components/core_components.ex:150 -#, elixir-autogen, elixir-format -msgid "Success!" -msgstr "" - -#: lib/mv_web/components/core_components.ex:155 -#, elixir-autogen, elixir-format -msgid "We can't find the internet" -msgstr "" - -#: lib/mv_web/components/core_components.ex:76 -#: lib/mv_web/components/core_components.ex:130 -#, elixir-autogen, elixir-format -msgid "close" -msgstr "" - -#: lib/mv_web/member_live/form_component.ex:44 -#: lib/mv_web/member_live/show.ex:24 -#, elixir-autogen, elixir-format -msgid "Birth Date" -msgstr "" - -#: lib/mv_web/member_live/form_component.ex:55 -#: lib/mv_web/member_live/show.ex:38 -#, elixir-autogen, elixir-format -msgid "Custom Properties" -msgstr "" - -#: lib/mv_web/member_live/form_component.ex:48 -#: lib/mv_web/member_live/show.ex:30 -#, elixir-autogen, elixir-format -msgid "Exit Date" -msgstr "" - -#: lib/mv_web/member_live/form_component.ex:52 -#: lib/mv_web/member_live/show.ex:34 -#, elixir-autogen, elixir-format -msgid "House Number" -msgstr "" - -#: lib/mv_web/member_live/form_component.ex:49 -#: lib/mv_web/member_live/show.ex:31 -#, elixir-autogen, elixir-format -msgid "Notes" -msgstr "" - -#: lib/mv_web/member_live/form_component.ex:45 -#: lib/mv_web/member_live/show.ex:25 -#, elixir-autogen, elixir-format -msgid "Paid" -msgstr "" - -#: lib/mv_web/member_live/form_component.ex:46 -#: lib/mv_web/member_live/show.ex:28 -#, elixir-autogen, elixir-format -msgid "Phone Number" -msgstr "" - -#: lib/mv_web/member_live/form_component.ex:53 -#: lib/mv_web/member_live/show.ex:35 -#, elixir-autogen, elixir-format -msgid "Postal Code" -msgstr "" - -#: lib/mv_web/member_live/form_component.ex:75 -#, elixir-autogen, elixir-format -msgid "Save Member" -msgstr "" - -#: lib/mv_web/member_live/form_component.ex:75 -#, elixir-autogen, elixir-format -msgid "Saving..." -msgstr "" - -#: lib/mv_web/member_live/form_component.ex:51 -#: lib/mv_web/member_live/show.ex:33 -#, elixir-autogen, elixir-format -msgid "Street" -msgstr "" - -#: lib/mv_web/member_live/form_component.ex:30 -#, elixir-autogen, elixir-format -msgid "Use this form to manage member records and their properties." -msgstr "" - -#: lib/mv_web/member_live/show.ex:52 -#, elixir-autogen, elixir-format -msgid "Back to members" -msgstr "" - -#: lib/mv_web/member_live/show.ex:14 -#, elixir-autogen, elixir-format -msgid "Edit member" -msgstr "" - -#: lib/mv_web/member_live/show.ex:20 -#, elixir-autogen, elixir-format -msgid "Id" -msgstr "" - -#: lib/mv_web/member_live/show.ex:26 -#, elixir-autogen, elixir-format -msgid "No" -msgstr "" - -#: lib/mv_web/member_live/show.ex:92 -#, elixir-autogen, elixir-format -msgid "Show Member" -msgstr "" - -#: lib/mv_web/member_live/show.ex:10 -#, elixir-autogen, elixir-format -msgid "This is a member record from your database." -msgstr "" - -#: lib/mv_web/member_live/show.ex:26 -#, elixir-autogen, elixir-format -msgid "Yes" -msgstr "" - -#: lib/mv_web/member_live/form_component.ex:102 -#, elixir-autogen, elixir-format -msgid "create" -msgstr "" - -#: lib/mv_web/member_live/form_component.ex:103 -#, elixir-autogen, elixir-format -msgid "update" -msgstr "" - -#: lib/mv_web/controllers/auth_controller.ex:43 -#, elixir-autogen, elixir-format -msgid "Incorrect email or password" -msgstr "" - -#: lib/mv_web/member_live/form_component.ex:109 -#, elixir-autogen, elixir-format -msgid "Member %{action} successfully" -msgstr "" - -#: lib/mv_web/controllers/auth_controller.ex:14 -#, elixir-autogen, elixir-format -msgid "You are now signed in" -msgstr "" - -#: lib/mv_web/controllers/auth_controller.ex:56 -#, elixir-autogen, elixir-format -msgid "You are now signed out" -msgstr "" - -#: lib/mv_web/controllers/auth_controller.ex:36 -#, elixir-autogen, elixir-format -msgid "You have already signed in another way, but have not confirmed your account.\nYou can confirm your account using the link we sent to you, or by resetting your password.\n" -msgstr "" - -#: lib/mv_web/controllers/auth_controller.ex:12 -#, elixir-autogen, elixir-format -msgid "Your email address has now been confirmed" -msgstr "" - -#: lib/mv_web/controllers/auth_controller.ex:13 -#, elixir-autogen, elixir-format -msgid "Your password has successfully been reset" -msgstr "" diff --git a/priv/gettext/en/LC_MESSAGES/default.po b/priv/gettext/en/LC_MESSAGES/default.po deleted file mode 100644 index 3317236..0000000 --- a/priv/gettext/en/LC_MESSAGES/default.po +++ /dev/null @@ -1,275 +0,0 @@ -## "msgid"s in this file come from POT (.pot) files. -### -### Do not add, change, or remove "msgid"s manually here as -### they're tied to the ones in the corresponding POT file -### (with the same domain). -### -### Use "mix gettext.extract --merge" or "mix gettext.merge" -### to merge POT files into PO files. -msgid "" -msgstr "" -"Language: en\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" - -#: lib/mv_web/components/core_components.ex:482 -#, elixir-autogen, elixir-format -msgid "Actions" -msgstr "" - -#: lib/mv_web/member_live/index.ex:39 -#, elixir-autogen, elixir-format -msgid "Are you sure?" -msgstr "" - -#: lib/mv_web/components/core_components.ex:160 -#, elixir-autogen, elixir-format -msgid "Attempting to reconnect" -msgstr "" - -#: lib/mv_web/member_live/form_component.ex:50 -#: lib/mv_web/member_live/index.ex:25 -#: lib/mv_web/member_live/show.ex:32 -#, elixir-autogen, elixir-format -msgid "City" -msgstr "" - -#: lib/mv_web/member_live/index.ex:41 -#, elixir-autogen, elixir-format -msgid "Delete" -msgstr "" - -#: lib/mv_web/member_live/index.ex:33 -#, elixir-autogen, elixir-format -msgid "Edit" -msgstr "" - -#: lib/mv_web/member_live/index.ex:76 -#: lib/mv_web/member_live/show.ex:93 -#, elixir-autogen, elixir-format -msgid "Edit Member" -msgstr "" - -#: lib/mv_web/member_live/form_component.ex:43 -#: lib/mv_web/member_live/index.ex:24 -#: lib/mv_web/member_live/show.ex:23 -#, elixir-autogen, elixir-format -msgid "Email" -msgstr "" - -#: lib/mv_web/components/core_components.ex:151 -#, elixir-autogen, elixir-format -msgid "Error!" -msgstr "" - -#: lib/mv_web/member_live/form_component.ex:41 -#: lib/mv_web/member_live/index.ex:22 -#: lib/mv_web/member_live/show.ex:21 -#, elixir-autogen, elixir-format -msgid "First Name" -msgstr "" - -#: lib/mv_web/components/core_components.ex:172 -#, elixir-autogen, elixir-format -msgid "Hang in there while we get back on track" -msgstr "" - -#: lib/mv_web/member_live/form_component.ex:47 -#: lib/mv_web/member_live/index.ex:26 -#: lib/mv_web/member_live/show.ex:29 -#, elixir-autogen, elixir-format -msgid "Join Date" -msgstr "" - -#: lib/mv_web/member_live/form_component.ex:42 -#: lib/mv_web/member_live/index.ex:23 -#: lib/mv_web/member_live/show.ex:22 -#, elixir-autogen, elixir-format -msgid "Last Name" -msgstr "" - -#: lib/mv_web/member_live/index.ex:8 -#: lib/mv_web/member_live/index.ex:88 -#, elixir-autogen, elixir-format -msgid "Listing Members" -msgstr "" - -#: lib/mv_web/member_live/index.ex:11 -#: lib/mv_web/member_live/index.ex:82 -#, elixir-autogen, elixir-format -msgid "New Member" -msgstr "" - -#: lib/mv_web/member_live/index.ex:30 -#, elixir-autogen, elixir-format -msgid "Show" -msgstr "" - -#: lib/mv_web/components/core_components.ex:167 -#, elixir-autogen, elixir-format -msgid "Something went wrong!" -msgstr "" - -#: lib/mv_web/components/core_components.ex:150 -#, elixir-autogen, elixir-format -msgid "Success!" -msgstr "" - -#: lib/mv_web/components/core_components.ex:155 -#, elixir-autogen, elixir-format -msgid "We can't find the internet" -msgstr "" - -#: lib/mv_web/components/core_components.ex:76 -#: lib/mv_web/components/core_components.ex:130 -#, elixir-autogen, elixir-format -msgid "close" -msgstr "" - -#: lib/mv_web/member_live/form_component.ex:44 -#: lib/mv_web/member_live/show.ex:24 -#, elixir-autogen, elixir-format -msgid "Birth Date" -msgstr "" - -#: lib/mv_web/member_live/form_component.ex:55 -#: lib/mv_web/member_live/show.ex:38 -#, elixir-autogen, elixir-format -msgid "Custom Properties" -msgstr "" - -#: lib/mv_web/member_live/form_component.ex:48 -#: lib/mv_web/member_live/show.ex:30 -#, elixir-autogen, elixir-format -msgid "Exit Date" -msgstr "" - -#: lib/mv_web/member_live/form_component.ex:52 -#: lib/mv_web/member_live/show.ex:34 -#, elixir-autogen, elixir-format -msgid "House Number" -msgstr "" - -#: lib/mv_web/member_live/form_component.ex:49 -#: lib/mv_web/member_live/show.ex:31 -#, elixir-autogen, elixir-format -msgid "Notes" -msgstr "" - -#: lib/mv_web/member_live/form_component.ex:45 -#: lib/mv_web/member_live/show.ex:25 -#, elixir-autogen, elixir-format -msgid "Paid" -msgstr "" - -#: lib/mv_web/member_live/form_component.ex:46 -#: lib/mv_web/member_live/show.ex:28 -#, elixir-autogen, elixir-format -msgid "Phone Number" -msgstr "" - -#: lib/mv_web/member_live/form_component.ex:53 -#: lib/mv_web/member_live/show.ex:35 -#, elixir-autogen, elixir-format -msgid "Postal Code" -msgstr "" - -#: lib/mv_web/member_live/form_component.ex:75 -#, elixir-autogen, elixir-format, fuzzy -msgid "Save Member" -msgstr "" - -#: lib/mv_web/member_live/form_component.ex:75 -#, elixir-autogen, elixir-format -msgid "Saving..." -msgstr "" - -#: lib/mv_web/member_live/form_component.ex:51 -#: lib/mv_web/member_live/show.ex:33 -#, elixir-autogen, elixir-format -msgid "Street" -msgstr "" - -#: lib/mv_web/member_live/form_component.ex:30 -#, elixir-autogen, elixir-format -msgid "Use this form to manage member records and their properties." -msgstr "" - -#: lib/mv_web/member_live/show.ex:52 -#, elixir-autogen, elixir-format -msgid "Back to members" -msgstr "" - -#: lib/mv_web/member_live/show.ex:14 -#, elixir-autogen, elixir-format, fuzzy -msgid "Edit member" -msgstr "" - -#: lib/mv_web/member_live/show.ex:20 -#, elixir-autogen, elixir-format -msgid "Id" -msgstr "" - -#: lib/mv_web/member_live/show.ex:26 -#, elixir-autogen, elixir-format -msgid "No" -msgstr "" - -#: lib/mv_web/member_live/show.ex:92 -#, elixir-autogen, elixir-format, fuzzy -msgid "Show Member" -msgstr "" - -#: lib/mv_web/member_live/show.ex:10 -#, elixir-autogen, elixir-format -msgid "This is a member record from your database." -msgstr "" - -#: lib/mv_web/member_live/show.ex:26 -#, elixir-autogen, elixir-format -msgid "Yes" -msgstr "" - -#: lib/mv_web/member_live/form_component.ex:102 -#, elixir-autogen, elixir-format -msgid "create" -msgstr "" - -#: lib/mv_web/member_live/form_component.ex:103 -#, elixir-autogen, elixir-format -msgid "update" -msgstr "" - -#: lib/mv_web/controllers/auth_controller.ex:43 -#, elixir-autogen, elixir-format -msgid "Incorrect email or password" -msgstr "" - -#: lib/mv_web/member_live/form_component.ex:109 -#, elixir-autogen, elixir-format -msgid "Member %{action} successfully" -msgstr "" - -#: lib/mv_web/controllers/auth_controller.ex:14 -#, elixir-autogen, elixir-format -msgid "You are now signed in" -msgstr "" - -#: lib/mv_web/controllers/auth_controller.ex:56 -#, elixir-autogen, elixir-format -msgid "You are now signed out" -msgstr "" - -#: lib/mv_web/controllers/auth_controller.ex:36 -#, elixir-autogen, elixir-format -msgid "You have already signed in another way, but have not confirmed your account.\nYou can confirm your account using the link we sent to you, or by resetting your password.\n" -msgstr "" - -#: lib/mv_web/controllers/auth_controller.ex:12 -#, elixir-autogen, elixir-format -msgid "Your email address has now been confirmed" -msgstr "" - -#: lib/mv_web/controllers/auth_controller.ex:13 -#, elixir-autogen, elixir-format -msgid "Your password has successfully been reset" -msgstr "" diff --git a/priv/repo/migrations/20250528163901_initial_migration.exs b/priv/repo/migrations/20250514151922_initial_migration.exs similarity index 96% rename from priv/repo/migrations/20250528163901_initial_migration.exs rename to priv/repo/migrations/20250514151922_initial_migration.exs index bf36e43..64915c2 100644 --- a/priv/repo/migrations/20250528163901_initial_migration.exs +++ b/priv/repo/migrations/20250514151922_initial_migration.exs @@ -11,7 +11,7 @@ defmodule Mv.Repo.Migrations.InitialMigration do create table(:property_types, primary_key: false) do add :id, :uuid, null: false, default: fragment("gen_random_uuid()"), primary_key: true add :name, :text, null: false - add :value_type, :text, null: false + add :type, :text, null: false add :description, :text add :immutable, :boolean, null: false, default: false add :required, :boolean, null: false, default: false @@ -21,7 +21,7 @@ defmodule Mv.Repo.Migrations.InitialMigration do create table(:properties, primary_key: false) do add :id, :uuid, null: false, default: fragment("gen_random_uuid()"), primary_key: true - add :value, :map + add :value, :text add :member_id, :uuid add :property_type_id, :uuid end diff --git a/priv/repo/migrations/20250617090641_member_fields.exs b/priv/repo/migrations/20250617090641_member_fields.exs deleted file mode 100644 index 36a80eb..0000000 --- a/priv/repo/migrations/20250617090641_member_fields.exs +++ /dev/null @@ -1,45 +0,0 @@ -defmodule Mv.Repo.Migrations.MemberFields do - @moduledoc """ - Updates resources based on their most recent snapshots. - - This file was autogenerated with `mix ash_postgres.generate_migrations` - """ - - use Ecto.Migration - - def up do - alter table(:members) do - add :first_name, :text, null: false - add :last_name, :text, null: false - add :email, :text, null: false - add :birth_date, :date - add :paid, :boolean - add :phone_number, :text - add :join_date, :date - add :exit_date, :date - add :notes, :text - add :city, :text - add :street, :text - add :house_number, :text - add :postal_code, :text - end - end - - def down do - alter table(:members) do - remove :postal_code - remove :house_number - remove :street - remove :city - remove :notes - remove :exit_date - remove :join_date - remove :phone_number - remove :paid - remove :birth_date - remove :email - remove :last_name - remove :first_name - end - end -end diff --git a/priv/repo/migrations/20250617132424_member_delete.exs b/priv/repo/migrations/20250617132424_member_delete.exs deleted file mode 100644 index f0f539a..0000000 --- a/priv/repo/migrations/20250617132424_member_delete.exs +++ /dev/null @@ -1,38 +0,0 @@ -defmodule Mv.Repo.Migrations.MemberDelete do - @moduledoc """ - Updates resources based on their most recent snapshots. - - This file was autogenerated with `mix ash_postgres.generate_migrations` - """ - - use Ecto.Migration - - def up do - drop constraint(:properties, "properties_member_id_fkey") - - alter table(:properties) do - modify :member_id, - references(:members, - column: :id, - name: "properties_member_id_fkey", - type: :uuid, - prefix: "public", - on_delete: :delete_all - ) - end - end - - def down do - drop constraint(:properties, "properties_member_id_fkey") - - alter table(:properties) do - modify :member_id, - references(:members, - column: :id, - name: "properties_member_id_fkey", - type: :uuid, - prefix: "public" - ) - end - end -end diff --git a/priv/repo/migrations/20250620110849_add_accounts_domain_extensions.exs b/priv/repo/migrations/20250620110849_add_accounts_domain_extensions.exs deleted file mode 100644 index f77419c..0000000 --- a/priv/repo/migrations/20250620110849_add_accounts_domain_extensions.exs +++ /dev/null @@ -1,19 +0,0 @@ -defmodule Mv.Repo.Migrations.AddAccountsDomainExtensions do - @moduledoc """ - Installs any extensions that are mentioned in the repo's `installed_extensions/0` callback - - This file was autogenerated with `mix ash_postgres.generate_migrations` - """ - - use Ecto.Migration - - def up do - execute("CREATE EXTENSION IF NOT EXISTS \"citext\"") - end - - def down do - # Uncomment this if you actually want to uninstall the extensions - # when this migration is rolled back: - # execute("DROP EXTENSION IF EXISTS \"citext\"") - end -end diff --git a/priv/repo/migrations/20250620110850_add_accounts_domain.exs b/priv/repo/migrations/20250620110850_add_accounts_domain.exs deleted file mode 100644 index 4c7c54b..0000000 --- a/priv/repo/migrations/20250620110850_add_accounts_domain.exs +++ /dev/null @@ -1,58 +0,0 @@ -defmodule Mv.Repo.Migrations.AddAccountsDomain do - @moduledoc """ - Updates resources based on their most recent snapshots. - - This file was autogenerated with `mix ash_postgres.generate_migrations` - """ - - use Ecto.Migration - - def up do - create table(:users, primary_key: false) do - add :id, :uuid, null: false, default: fragment("gen_random_uuid()"), primary_key: true - add :email, :citext, null: false - add :hashed_password, :text - add :oidc_id, :text - - add :member_id, - references(:members, - column: :id, - name: "users_member_id_fkey", - type: :uuid, - prefix: "public" - ) - end - - create unique_index(:users, [:email], name: "users_unique_email_index") - - create unique_index(:users, [:oidc_id], name: "users_unique_oidc_id_index") - - create table(:tokens, primary_key: false) do - add :updated_at, :utc_datetime_usec, - null: false, - default: fragment("(now() AT TIME ZONE 'utc')") - - add :created_at, :utc_datetime_usec, - null: false, - default: fragment("(now() AT TIME ZONE 'utc')") - - add :extra_data, :map - add :purpose, :text, null: false - add :expires_at, :utc_datetime, null: false - add :subject, :text, null: false - add :jti, :text, null: false, primary_key: true - end - end - - def down do - drop table(:tokens) - - drop_if_exists unique_index(:users, [:oidc_id], name: "users_unique_oidc_id_index") - - drop_if_exists unique_index(:users, [:email], name: "users_unique_email_index") - - drop constraint(:users, "users_member_id_fkey") - - drop table(:users) - end -end diff --git a/priv/repo/seeds.exs b/priv/repo/seeds.exs index 1497096..8fca144 100644 --- a/priv/repo/seeds.exs +++ b/priv/repo/seeds.exs @@ -2,42 +2,38 @@ # # mix run priv/repo/seeds.exs # +# Inside the script, you can read and write to any of your +# repositories directly: +# +# Mv.Repo.insert!(%Mv.SomeSchema{}) +# +# We recommend using the bang functions (`insert!`, `update!` +# and so on) as they will fail if something goes wrong. alias Mv.Membership for attrs <- [ %{ - name: "String Field", - value_type: :string, - description: "Example for a field of type string", + name: "Vorname", + type: "string", + description: "Vorname des Mitglieds", immutable: true, required: true }, %{ - name: "Date Field", - value_type: :date, - description: "Example for a field of type date", - immutable: true, - required: true - }, - %{ - name: "Boolean Field", - value_type: :boolean, - description: "Example for a field of type boolean", - immutable: true, - required: true - }, - %{ - name: "Email Field", - value_type: :email, - description: "Example for a field of type email", + name: "Email", + type: "string", + description: "Email-Adresse des Mitglieds", immutable: true, required: true } ] do - Membership.create_property_type!( - attrs, - upsert?: true, - upsert_identity: :unique_name - ) + # upsert?: true sorgt dafür, dass bei bestehendem Namen kein Fehler, + # sondern ein Update (hier effektiv No-Op) ausgeführt wird + {:ok, _} = + Membership.create_property_type( + attrs, + upsert?: true, + upsert_identity: :unique_name + ) end diff --git a/priv/resource_snapshots/repo/extensions.json b/priv/resource_snapshots/repo/extensions.json index 323661b..33001db 100644 --- a/priv/resource_snapshots/repo/extensions.json +++ b/priv/resource_snapshots/repo/extensions.json @@ -1,7 +1,6 @@ { "ash_functions_version": 5, "installed": [ - "ash-functions", - "citext" + "ash-functions" ] } \ No newline at end of file diff --git a/priv/resource_snapshots/repo/members/20250528163901.json b/priv/resource_snapshots/repo/members/20250514151922.json similarity index 87% rename from priv/resource_snapshots/repo/members/20250528163901.json rename to priv/resource_snapshots/repo/members/20250514151922.json index 2d08b17..df811d8 100644 --- a/priv/resource_snapshots/repo/members/20250528163901.json +++ b/priv/resource_snapshots/repo/members/20250514151922.json @@ -16,7 +16,7 @@ "custom_indexes": [], "custom_statements": [], "has_create_action": true, - "hash": "35D45214D6D344B0AF6CFCB69B8682FCB3D382D85883D3D3AAC1AEE7F54FD89A", + "hash": "A0402269CB456075B81CA4CB3A2135A2C88D8B7FD51CD7A23084AA5264FEE344", "identities": [], "multitenancy": { "attribute": null, diff --git a/priv/resource_snapshots/repo/members/20250617090641.json b/priv/resource_snapshots/repo/members/20250617090641.json deleted file mode 100644 index 9e2d991..0000000 --- a/priv/resource_snapshots/repo/members/20250617090641.json +++ /dev/null @@ -1,187 +0,0 @@ -{ - "attributes": [ - { - "allow_nil?": false, - "default": "fragment(\"uuid_generate_v7()\")", - "generated?": false, - "precision": null, - "primary_key?": true, - "references": null, - "scale": null, - "size": null, - "source": "id", - "type": "uuid" - }, - { - "allow_nil?": false, - "default": "nil", - "generated?": false, - "precision": null, - "primary_key?": false, - "references": null, - "scale": null, - "size": null, - "source": "first_name", - "type": "text" - }, - { - "allow_nil?": false, - "default": "nil", - "generated?": false, - "precision": null, - "primary_key?": false, - "references": null, - "scale": null, - "size": null, - "source": "last_name", - "type": "text" - }, - { - "allow_nil?": false, - "default": "nil", - "generated?": false, - "precision": null, - "primary_key?": false, - "references": null, - "scale": null, - "size": null, - "source": "email", - "type": "text" - }, - { - "allow_nil?": true, - "default": "nil", - "generated?": false, - "precision": null, - "primary_key?": false, - "references": null, - "scale": null, - "size": null, - "source": "birth_date", - "type": "date" - }, - { - "allow_nil?": true, - "default": "nil", - "generated?": false, - "precision": null, - "primary_key?": false, - "references": null, - "scale": null, - "size": null, - "source": "paid", - "type": "boolean" - }, - { - "allow_nil?": true, - "default": "nil", - "generated?": false, - "precision": null, - "primary_key?": false, - "references": null, - "scale": null, - "size": null, - "source": "phone_number", - "type": "text" - }, - { - "allow_nil?": true, - "default": "nil", - "generated?": false, - "precision": null, - "primary_key?": false, - "references": null, - "scale": null, - "size": null, - "source": "join_date", - "type": "date" - }, - { - "allow_nil?": true, - "default": "nil", - "generated?": false, - "precision": null, - "primary_key?": false, - "references": null, - "scale": null, - "size": null, - "source": "exit_date", - "type": "date" - }, - { - "allow_nil?": true, - "default": "nil", - "generated?": false, - "precision": null, - "primary_key?": false, - "references": null, - "scale": null, - "size": null, - "source": "notes", - "type": "text" - }, - { - "allow_nil?": true, - "default": "nil", - "generated?": false, - "precision": null, - "primary_key?": false, - "references": null, - "scale": null, - "size": null, - "source": "city", - "type": "text" - }, - { - "allow_nil?": true, - "default": "nil", - "generated?": false, - "precision": null, - "primary_key?": false, - "references": null, - "scale": null, - "size": null, - "source": "street", - "type": "text" - }, - { - "allow_nil?": true, - "default": "nil", - "generated?": false, - "precision": null, - "primary_key?": false, - "references": null, - "scale": null, - "size": null, - "source": "house_number", - "type": "text" - }, - { - "allow_nil?": true, - "default": "nil", - "generated?": false, - "precision": null, - "primary_key?": false, - "references": null, - "scale": null, - "size": null, - "source": "postal_code", - "type": "text" - } - ], - "base_filter": null, - "check_constraints": [], - "custom_indexes": [], - "custom_statements": [], - "has_create_action": true, - "hash": "CF80317E7EE409618E08458B10EE122FF605640DDA8CD6000B433F1979614F5D", - "identities": [], - "multitenancy": { - "attribute": null, - "global": null, - "strategy": null - }, - "repo": "Elixir.Mv.Repo", - "schema": null, - "table": "members" -} \ No newline at end of file diff --git a/priv/resource_snapshots/repo/properties/20250528163901.json b/priv/resource_snapshots/repo/properties/20250514151922.json similarity index 95% rename from priv/resource_snapshots/repo/properties/20250528163901.json rename to priv/resource_snapshots/repo/properties/20250514151922.json index daf3222..0707bcf 100644 --- a/priv/resource_snapshots/repo/properties/20250528163901.json +++ b/priv/resource_snapshots/repo/properties/20250514151922.json @@ -18,7 +18,7 @@ "references": null, "size": null, "source": "value", - "type": "map" + "type": "text" }, { "allow_nil?": true, @@ -84,7 +84,7 @@ "custom_indexes": [], "custom_statements": [], "has_create_action": true, - "hash": "8CF241CB9E8239511914EDEC96186BB7879529372BD8A4162431CCE9961F4F1B", + "hash": "F2A42A3427E0428637F465E4F357A3BE21B33231F94CF77B4843084128F6BDA5", "identities": [], "multitenancy": { "attribute": null, diff --git a/priv/resource_snapshots/repo/properties/20250617132424.json b/priv/resource_snapshots/repo/properties/20250617132424.json deleted file mode 100644 index 49e3b48..0000000 --- a/priv/resource_snapshots/repo/properties/20250617132424.json +++ /dev/null @@ -1,105 +0,0 @@ -{ - "attributes": [ - { - "allow_nil?": false, - "default": "fragment(\"gen_random_uuid()\")", - "generated?": false, - "precision": null, - "primary_key?": true, - "references": null, - "scale": null, - "size": null, - "source": "id", - "type": "uuid" - }, - { - "allow_nil?": true, - "default": "nil", - "generated?": false, - "precision": null, - "primary_key?": false, - "references": null, - "scale": null, - "size": null, - "source": "value", - "type": "map" - }, - { - "allow_nil?": true, - "default": "nil", - "generated?": false, - "precision": null, - "primary_key?": false, - "references": { - "deferrable": false, - "destination_attribute": "id", - "destination_attribute_default": null, - "destination_attribute_generated": null, - "index?": false, - "match_type": null, - "match_with": null, - "multitenancy": { - "attribute": null, - "global": null, - "strategy": null - }, - "name": "properties_member_id_fkey", - "on_delete": "delete", - "on_update": null, - "primary_key?": true, - "schema": "public", - "table": "members" - }, - "scale": null, - "size": null, - "source": "member_id", - "type": "uuid" - }, - { - "allow_nil?": true, - "default": "nil", - "generated?": false, - "precision": null, - "primary_key?": false, - "references": { - "deferrable": false, - "destination_attribute": "id", - "destination_attribute_default": null, - "destination_attribute_generated": null, - "index?": false, - "match_type": null, - "match_with": null, - "multitenancy": { - "attribute": null, - "global": null, - "strategy": null - }, - "name": "properties_property_type_id_fkey", - "on_delete": null, - "on_update": null, - "primary_key?": true, - "schema": "public", - "table": "property_types" - }, - "scale": null, - "size": null, - "source": "property_type_id", - "type": "uuid" - } - ], - "base_filter": null, - "check_constraints": [], - "custom_indexes": [], - "custom_statements": [], - "has_create_action": true, - "hash": "4F17BE0106435A1D75D46A3ABDE6A3DA20FC9B1C43D101B6C310009279DD7CBA", - "identities": [], - "multitenancy": { - "attribute": null, - "global": null, - "strategy": null - }, - "repo": "Elixir.Mv.Repo", - "schema": null, - "table": "properties" -} \ No newline at end of file diff --git a/priv/resource_snapshots/repo/property_types/20250528163901.json b/priv/resource_snapshots/repo/property_types/20250514151922.json similarity index 94% rename from priv/resource_snapshots/repo/property_types/20250528163901.json rename to priv/resource_snapshots/repo/property_types/20250514151922.json index 55f066a..5da5fa8 100644 --- a/priv/resource_snapshots/repo/property_types/20250528163901.json +++ b/priv/resource_snapshots/repo/property_types/20250514151922.json @@ -27,7 +27,7 @@ "primary_key?": false, "references": null, "size": null, - "source": "value_type", + "source": "type", "type": "text" }, { @@ -66,7 +66,7 @@ "custom_indexes": [], "custom_statements": [], "has_create_action": true, - "hash": "F98A723AE0D20005FBE4205E46ABEE09A88DFF9334C85BADC1FBEEF100F3E25B", + "hash": "47210108DE1E7B2A20A67205E875B3440526941E61AB95B166976E8CD8AA0955", "identities": [ { "all_tenants?": false, diff --git a/priv/resource_snapshots/repo/tokens/20250620110850.json b/priv/resource_snapshots/repo/tokens/20250620110850.json deleted file mode 100644 index c702eff..0000000 --- a/priv/resource_snapshots/repo/tokens/20250620110850.json +++ /dev/null @@ -1,103 +0,0 @@ -{ - "attributes": [ - { - "allow_nil?": false, - "default": "fragment(\"(now() AT TIME ZONE 'utc')\")", - "generated?": false, - "precision": null, - "primary_key?": false, - "references": null, - "scale": null, - "size": null, - "source": "updated_at", - "type": "utc_datetime_usec" - }, - { - "allow_nil?": false, - "default": "fragment(\"(now() AT TIME ZONE 'utc')\")", - "generated?": false, - "precision": null, - "primary_key?": false, - "references": null, - "scale": null, - "size": null, - "source": "created_at", - "type": "utc_datetime_usec" - }, - { - "allow_nil?": true, - "default": "nil", - "generated?": false, - "precision": null, - "primary_key?": false, - "references": null, - "scale": null, - "size": null, - "source": "extra_data", - "type": "map" - }, - { - "allow_nil?": false, - "default": "nil", - "generated?": false, - "precision": null, - "primary_key?": false, - "references": null, - "scale": null, - "size": null, - "source": "purpose", - "type": "text" - }, - { - "allow_nil?": false, - "default": "nil", - "generated?": false, - "precision": null, - "primary_key?": false, - "references": null, - "scale": null, - "size": null, - "source": "expires_at", - "type": "utc_datetime" - }, - { - "allow_nil?": false, - "default": "nil", - "generated?": false, - "precision": null, - "primary_key?": false, - "references": null, - "scale": null, - "size": null, - "source": "subject", - "type": "text" - }, - { - "allow_nil?": false, - "default": "nil", - "generated?": false, - "precision": null, - "primary_key?": true, - "references": null, - "scale": null, - "size": null, - "source": "jti", - "type": "text" - } - ], - "base_filter": null, - "check_constraints": [], - "custom_indexes": [], - "custom_statements": [], - "has_create_action": true, - "hash": "EA1475C339B5BE2728560EFB2AF911275B2F65C2CE66CD1C093FAB5D9183BB11", - "identities": [], - "multitenancy": { - "attribute": null, - "global": null, - "strategy": null - }, - "repo": "Elixir.Mv.Repo", - "schema": null, - "table": "tokens" -} \ No newline at end of file diff --git a/priv/resource_snapshots/repo/users/20250620110850.json b/priv/resource_snapshots/repo/users/20250620110850.json deleted file mode 100644 index 54688a8..0000000 --- a/priv/resource_snapshots/repo/users/20250620110850.json +++ /dev/null @@ -1,127 +0,0 @@ -{ - "attributes": [ - { - "allow_nil?": false, - "default": "fragment(\"gen_random_uuid()\")", - "generated?": false, - "precision": null, - "primary_key?": true, - "references": null, - "scale": null, - "size": null, - "source": "id", - "type": "uuid" - }, - { - "allow_nil?": false, - "default": "nil", - "generated?": false, - "precision": null, - "primary_key?": false, - "references": null, - "scale": null, - "size": null, - "source": "email", - "type": "citext" - }, - { - "allow_nil?": true, - "default": "nil", - "generated?": false, - "precision": null, - "primary_key?": false, - "references": null, - "scale": null, - "size": null, - "source": "hashed_password", - "type": "text" - }, - { - "allow_nil?": true, - "default": "nil", - "generated?": false, - "precision": null, - "primary_key?": false, - "references": null, - "scale": null, - "size": null, - "source": "oidc_id", - "type": "text" - }, - { - "allow_nil?": true, - "default": "nil", - "generated?": false, - "precision": null, - "primary_key?": false, - "references": { - "deferrable": false, - "destination_attribute": "id", - "destination_attribute_default": null, - "destination_attribute_generated": null, - "index?": false, - "match_type": null, - "match_with": null, - "multitenancy": { - "attribute": null, - "global": null, - "strategy": null - }, - "name": "users_member_id_fkey", - "on_delete": null, - "on_update": null, - "primary_key?": true, - "schema": "public", - "table": "members" - }, - "scale": null, - "size": null, - "source": "member_id", - "type": "uuid" - } - ], - "base_filter": null, - "check_constraints": [], - "custom_indexes": [], - "custom_statements": [], - "has_create_action": true, - "hash": "03EBA1A8BCE47C4706E2D718E00364465E08C9A3999988D49FC1B89DEC5D717C", - "identities": [ - { - "all_tenants?": false, - "base_filter": null, - "index_name": "users_unique_email_index", - "keys": [ - { - "type": "atom", - "value": "email" - } - ], - "name": "unique_email", - "nils_distinct?": true, - "where": null - }, - { - "all_tenants?": false, - "base_filter": null, - "index_name": "users_unique_oidc_id_index", - "keys": [ - { - "type": "atom", - "value": "oidc_id" - } - ], - "name": "unique_oidc_id", - "nils_distinct?": true, - "where": null - } - ], - "multitenancy": { - "attribute": null, - "global": null, - "strategy": null - }, - "repo": "Elixir.Mv.Repo", - "schema": null, - "table": "users" -} \ No newline at end of file diff --git a/renovate.json b/renovate.json index d1281a3..7e12828 100644 --- a/renovate.json +++ b/renovate.json @@ -17,14 +17,9 @@ "matchDatasources": ["docker"] }, { - "description": "Disable elixir updates, as renovate does not work with their -otp- numbering scheme.", + "matchFileNames": [".tool-versions", "Dockerfile"], "matchCurrentValue": "**-otp-**", "enabled": false - }, - { - "description": "Disable erlang updates as they need to be coordinated with elixir updates.", - "matchDepNames": "erlang", - "enabled": false } ] } diff --git a/test/membership/member_test.exs b/test/membership/member_test.exs deleted file mode 100644 index 7015d34..0000000 --- a/test/membership/member_test.exs +++ /dev/null @@ -1,110 +0,0 @@ -defmodule Mv.Membership.MemberTest do - use Mv.DataCase, async: false - alias Mv.Membership - - describe "Fields and Validations" do - @valid_attrs %{ - first_name: "John", - last_name: "Doe", - birth_date: ~D[1990-01-01], - paid: true, - email: "john@example.com", - phone_number: "+49123456789", - join_date: ~D[2020-01-01], - exit_date: nil, - notes: "Test note", - city: "Berlin", - street: "Main Street", - house_number: "1A", - postal_code: "12345" - } - - test "First name is required and must not be empty" do - attrs = Map.put(@valid_attrs, :first_name, "") - assert {:error, %Ash.Error.Invalid{errors: errors}} = Membership.create_member(attrs) - assert error_message(errors, :first_name) =~ "must be present" - end - - test "Last name is required and must not be empty" do - attrs = Map.put(@valid_attrs, :last_name, "") - assert {:error, %Ash.Error.Invalid{errors: errors}} = Membership.create_member(attrs) - assert error_message(errors, :last_name) =~ "must be present" - end - - test "Email is required" do - attrs = Map.put(@valid_attrs, :email, "") - assert {:error, %Ash.Error.Invalid{errors: errors}} = Membership.create_member(attrs) - assert error_message(errors, :email) =~ "must be present" - end - - test "Email must be valid" do - attrs = Map.put(@valid_attrs, :email, "test@") - assert {:error, %Ash.Error.Invalid{errors: errors}} = Membership.create_member(attrs) - assert error_message(errors, :email) =~ "is not a valid email" - end - - test "Birth date is optional but must not be in the future" do - attrs = Map.put(@valid_attrs, :birth_date, Date.utc_today() |> Date.add(1)) - assert {:error, %Ash.Error.Invalid{errors: errors}} = Membership.create_member(attrs) - assert error_message(errors, :birth_date) =~ "cannot be in the future" - end - - test "Paid is optional but must be boolean if specified" do - attrs = Map.put(@valid_attrs, :paid, nil) - attrs2 = Map.put(@valid_attrs, :paid, "yes") - assert {:ok, _member} = Membership.create_member(Map.delete(attrs, :paid)) - assert {:error, %Ash.Error.Invalid{errors: errors}} = Membership.create_member(attrs2) - assert error_message(errors, :paid) =~ "is invalid" - end - - test "Phone number is optional but must have a valid format if specified" do - attrs = Map.put(@valid_attrs, :phone_number, "abc") - assert {:error, %Ash.Error.Invalid{errors: errors}} = Membership.create_member(attrs) - assert error_message(errors, :phone_number) =~ "is not a valid phone number" - attrs2 = Map.delete(@valid_attrs, :phone_number) - assert {:ok, _member} = Membership.create_member(attrs2) - end - - test "Join date is optional but must not be in the future" do - attrs = Map.put(@valid_attrs, :join_date, Date.utc_today() |> Date.add(1)) - assert {:error, %Ash.Error.Invalid{errors: errors}} = Membership.create_member(attrs) - assert error_message(errors, :join_date) =~ "cannot be in the future" - attrs2 = Map.delete(@valid_attrs, :join_date) - assert {:ok, _member} = Membership.create_member(attrs2) - end - - test "Exit date is optional but must not be before join date if both are specified" do - attrs = Map.put(@valid_attrs, :exit_date, ~D[2010-01-01]) - assert {:error, %Ash.Error.Invalid{errors: errors}} = Membership.create_member(attrs) - assert error_message(errors, :exit_date) =~ "cannot be before join date" - attrs2 = Map.delete(@valid_attrs, :exit_date) - assert {:ok, _member} = Membership.create_member(attrs2) - end - - test "Notes is optional" do - attrs = Map.delete(@valid_attrs, :notes) - assert {:ok, _member} = Membership.create_member(attrs) - end - - test "City, street, house number are optional" do - attrs = @valid_attrs |> Map.drop([:city, :street, :house_number]) - assert {:ok, _member} = Membership.create_member(attrs) - end - - test "Postal code is optional but must have 5 digits if specified" do - attrs = Map.put(@valid_attrs, :postal_code, "1234") - assert {:error, %Ash.Error.Invalid{errors: errors}} = Membership.create_member(attrs) - assert error_message(errors, :postal_code) =~ "must consist of 5 digits" - attrs2 = Map.delete(@valid_attrs, :postal_code) - assert {:ok, _member} = Membership.create_member(attrs2) - end - end - - # Helper function for error evaluation - defp error_message(errors, field) do - errors - |> Enum.filter(fn err -> Map.get(err, :field) == field end) - |> Enum.map(&Map.get(&1, :message, "")) - |> List.first() - end -end diff --git a/test/mv_web/controllers/auth_controller_test.exs b/test/mv_web/controllers/auth_controller_test.exs deleted file mode 100644 index e18ade2..0000000 --- a/test/mv_web/controllers/auth_controller_test.exs +++ /dev/null @@ -1,48 +0,0 @@ -defmodule MvWeb.AuthControllerTest do - use MvWeb.ConnCase, async: true - - test "GET /sign-in shows sign in form", %{conn: conn} do - conn = get(conn, ~p"/sign-in") - assert html_response(conn, 200) =~ "Sign in" - end - - test "POST /sign-in with valid credentials redirects to home", %{conn: conn} do - # Create a test user first - conn = conn_with_oidc_user(conn) - conn = get(conn, ~p"/sign-in") - - assert redirected_to(conn) == ~p"/" - end - - test "POST /sign-in with invalid credentials shows error", %{conn: conn} do - conn = - post(conn, ~p"/auth/sign_in", %{ - "user" => %{ - "email" => "wrong@example.com", - "password" => "wrongpassword" - } - }) - - assert conn.status == 404 - end - - test "GET /sign-out redirects to home", %{conn: conn} do - # First sign in a user - conn = conn_with_oidc_user(conn) - - # Then sign out - conn = get(conn, ~p"/sign-out") - assert redirected_to(conn) == ~p"/" - end - - test "unauthenticated user accessing protected route gets redirected to sign-in", %{conn: conn} do - conn = get(conn, ~p"/members") - assert redirected_to(conn) == ~p"/sign-in" - end - - test "authenticated user can access protected route", %{conn: conn} do - conn = conn_with_oidc_user(conn) - conn = get(conn, ~p"/members") - assert conn.status == 200 - end -end diff --git a/test/mv_web/controllers/page_controller_test.exs b/test/mv_web/controllers/page_controller_test.exs index ce3195b..702cd78 100644 --- a/test/mv_web/controllers/page_controller_test.exs +++ b/test/mv_web/controllers/page_controller_test.exs @@ -2,9 +2,7 @@ defmodule MvWeb.PageControllerTest do use MvWeb.ConnCase test "GET /", %{conn: conn} do - conn = conn_with_oidc_user(conn) - conn = get(conn, ~p"/") - assert html_response(conn, 200) =~ "Mitgliederverwaltung" + assert html_response(conn, 200) =~ "Peace of mind from prototype to production" end end diff --git a/test/mv_web/locale_test.exs b/test/mv_web/locale_test.exs deleted file mode 100644 index 1cc6693..0000000 --- a/test/mv_web/locale_test.exs +++ /dev/null @@ -1,14 +0,0 @@ -defmodule MvWeb.LocaleTest do - use MvWeb.ConnCase, async: true - import Phoenix.ConnTest - - test "language switch via form sets the locale to English in the session" do - conn = post(build_conn(), "/set_locale", %{"locale" => "en"}) - assert get_session(conn, :locale) == "en" - end - - test "language switch via form sets the locale to German in the session" do - conn = post(build_conn(), "/set_locale", %{"locale" => "de"}) - assert get_session(conn, :locale) == "de" - end -end diff --git a/test/mv_web/member_live/index_test.exs b/test/mv_web/member_live/index_test.exs deleted file mode 100644 index ce47a43..0000000 --- a/test/mv_web/member_live/index_test.exs +++ /dev/null @@ -1,66 +0,0 @@ -defmodule MvWeb.MemberLive.IndexTest do - use MvWeb.ConnCase, async: true - import Phoenix.LiveViewTest - - test "shows translated title in German", %{conn: conn} do - conn = conn_with_oidc_user(conn) - conn = Plug.Test.init_test_session(conn, locale: "de") - {:ok, _view, html} = live(conn, "/members") - # Expected German title - assert html =~ "Mitglieder" - end - - test "shows translated title in English", %{conn: conn} do - conn = conn_with_oidc_user(conn) - Gettext.put_locale(MvWeb.Gettext, "en") - {:ok, _view, html} = live(conn, "/members") - # Expected English title - assert html =~ "Members" - end - - test "shows translated button text in German", %{conn: conn} do - conn = conn_with_oidc_user(conn) - conn = Plug.Test.init_test_session(conn, locale: "de") - {:ok, _view, html} = live(conn, "/members/new") - assert html =~ "Speichern" - end - - test "shows translated button text in English", %{conn: conn} do - conn = conn_with_oidc_user(conn) - Gettext.put_locale(MvWeb.Gettext, "en") - {:ok, _view, html} = live(conn, "/members/new") - assert html =~ "Save" - end - - test "shows translated flash message after creating a member in German", %{conn: conn} do - conn = conn_with_oidc_user(conn) - conn = Plug.Test.init_test_session(conn, locale: "de") - {:ok, view, _html} = live(conn, "/members") - view |> element("a", "Neues Mitglied") |> render_click() - - form_data = %{ - "member[first_name]" => "Max", - "member[last_name]" => "Mustermann", - "member[email]" => "max@example.com" - } - - view |> form("#member-form", form_data) |> render_submit() - assert has_element?(view, "#flash-group", "Mitglied erstellt erfolgreich") - end - - test "shows translated flash message after creating a member in English", %{conn: conn} do - conn = conn_with_oidc_user(conn) - conn = Plug.Test.init_test_session(conn, locale: "en") - {:ok, view, _html} = live(conn, "/members") - view |> element("a", "New Member") |> render_click() - - form_data = %{ - "member[first_name]" => "Max", - "member[last_name]" => "Mustermann", - "member[email]" => "max@example.com" - } - - view |> form("#member-form", form_data) |> render_submit() - assert has_element?(view, "#flash-group", "Member create successfully") - end -end diff --git a/test/support/conn_case.ex b/test/support/conn_case.ex index d1804b7..7101531 100644 --- a/test/support/conn_case.ex +++ b/test/support/conn_case.ex @@ -31,38 +31,6 @@ defmodule MvWeb.ConnCase do end end - @doc """ - Creates a test user and returns the user struct. - """ - def create_test_user(attrs \\ %{}) do - email = "user@example.com" - password = "password" - {:ok, hashed_password} = AshAuthentication.BcryptProvider.hash(password) - - Ash.Seed.seed!(Mv.Accounts.User, %{ - email: email, - hashed_password: hashed_password - }) - end - - @doc """ - Signs in a user via OIDC for testing by creating a session with the user's token. - """ - def sign_in_user_via_oidc(conn, user) do - # Mock OIDC sign-in by creating a token directly - conn - |> Phoenix.ConnTest.init_test_session(%{}) - |> AshAuthentication.Plug.Helpers.store_in_session(user) - end - - @doc """ - Signs in a user via OIDC and returns a connection with the user authenticated. - """ - def conn_with_oidc_user(conn, user_attrs \\ %{}) do - user = create_test_user(user_attrs) - sign_in_user_via_oidc(conn, user) - end - setup tags do Mv.DataCase.setup_sandbox(tags) {:ok, conn: Phoenix.ConnTest.build_conn()}