Compare commits
15 commits
74f980414a
...
780efc3e91
| Author | SHA1 | Date | |
|---|---|---|---|
| 780efc3e91 | |||
| 2c708d47e6 | |||
| 8f14224eb1 | |||
| 6cce36b26e | |||
| feb747bd21 | |||
|
|
0d33f1baf7 | ||
| aeb9cb8e29 | |||
| 60f20ceacf | |||
| 9e7d8b2277 | |||
| a334ab6bc4 | |||
| 32fafad196 | |||
| ddee222f86 | |||
| 41634711fe | |||
| cf18d304ee | |||
| dce8fbc232 |
18 changed files with 351 additions and 35 deletions
45
.dockerignore
Normal file
45
.dockerignore
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
# This file excludes paths from the Docker build context.
|
||||||
|
#
|
||||||
|
# By default, Docker's build context includes all files (and folders) in the
|
||||||
|
# current directory. Even if a file isn't copied into the container it is still sent to
|
||||||
|
# the Docker daemon.
|
||||||
|
#
|
||||||
|
# There are multiple reasons to exclude files from the build context:
|
||||||
|
#
|
||||||
|
# 1. Prevent nested folders from being copied into the container (ex: exclude
|
||||||
|
# /assets/node_modules when copying /assets)
|
||||||
|
# 2. Reduce the size of the build context and improve build time (ex. /build, /deps, /doc)
|
||||||
|
# 3. Avoid sending files containing sensitive information
|
||||||
|
#
|
||||||
|
# More information on using .dockerignore is available here:
|
||||||
|
# https://docs.docker.com/engine/reference/builder/#dockerignore-file
|
||||||
|
|
||||||
|
.dockerignore
|
||||||
|
|
||||||
|
# Ignore git, but keep git HEAD and refs to access current commit hash if needed:
|
||||||
|
#
|
||||||
|
# $ cat .git/HEAD | awk '{print ".git/"$2}' | xargs cat
|
||||||
|
# d0b8727759e1e0e7aa3d41707d12376e373d5ecc
|
||||||
|
.git
|
||||||
|
!.git/HEAD
|
||||||
|
!.git/refs
|
||||||
|
|
||||||
|
# Common development/test artifacts
|
||||||
|
/cover/
|
||||||
|
/doc/
|
||||||
|
/test/
|
||||||
|
/tmp/
|
||||||
|
.elixir_ls
|
||||||
|
|
||||||
|
# Mix artifacts
|
||||||
|
/_build/
|
||||||
|
/deps/
|
||||||
|
*.ez
|
||||||
|
|
||||||
|
# Generated on crash by the VM
|
||||||
|
erl_crash.dump
|
||||||
|
|
||||||
|
# Static artifacts - These should be fetched and built inside the Docker image
|
||||||
|
/assets/node_modules/
|
||||||
|
/priv/static/assets/
|
||||||
|
/priv/static/cache_manifest.json
|
||||||
|
|
@ -72,13 +72,16 @@ trigger:
|
||||||
event:
|
event:
|
||||||
- cron
|
- cron
|
||||||
- custom
|
- custom
|
||||||
|
- push
|
||||||
|
branch:
|
||||||
|
- main
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
LOG_LEVEL: debug
|
LOG_LEVEL: debug
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: renovate
|
- name: renovate
|
||||||
image: renovate/renovate:39.264
|
image: renovate/renovate:40.22
|
||||||
environment:
|
environment:
|
||||||
RENOVATE_CONFIG_FILE: "renovate_backend_config.js"
|
RENOVATE_CONFIG_FILE: "renovate_backend_config.js"
|
||||||
RENOVATE_TOKEN:
|
RENOVATE_TOKEN:
|
||||||
|
|
|
||||||
93
Dockerfile
Normal file
93
Dockerfile
Normal file
|
|
@ -0,0 +1,93 @@
|
||||||
|
# Find eligible builder and runner images on Docker Hub. We use Ubuntu/Debian
|
||||||
|
# instead of Alpine to avoid DNS resolution issues in production.
|
||||||
|
#
|
||||||
|
# https://hub.docker.com/r/hexpm/elixir/tags?page=1&name=ubuntu
|
||||||
|
# https://hub.docker.com/_/ubuntu?tab=tags
|
||||||
|
#
|
||||||
|
# This file is based on these images:
|
||||||
|
#
|
||||||
|
# - https://hub.docker.com/r/hexpm/elixir/tags - for the build image
|
||||||
|
# - https://hub.docker.com/_/debian?tab=tags&page=1&name=bullseye-20250317-slim - for the release image
|
||||||
|
# - https://pkgs.org/ - resource for finding needed packages
|
||||||
|
# - Ex: hexpm/elixir:1.18.3-erlang-27.3-debian-bullseye-20250317-slim
|
||||||
|
#
|
||||||
|
ARG BUILDER_IMAGE="hexpm/elixir:1.18.3-erlang-27.3-debian-bullseye-20250317-slim"
|
||||||
|
ARG RUNNER_IMAGE="debian:bullseye-20250317-slim"
|
||||||
|
|
||||||
|
FROM ${BUILDER_IMAGE} as builder
|
||||||
|
|
||||||
|
# install build dependencies
|
||||||
|
RUN apt-get update -y && apt-get install -y build-essential git \
|
||||||
|
&& apt-get clean && rm -f /var/lib/apt/lists/*_*
|
||||||
|
|
||||||
|
# prepare build dir
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# install hex + rebar
|
||||||
|
RUN mix local.hex --force && \
|
||||||
|
mix local.rebar --force
|
||||||
|
|
||||||
|
# set build ENV
|
||||||
|
ENV MIX_ENV="prod"
|
||||||
|
|
||||||
|
# install mix dependencies
|
||||||
|
COPY mix.exs mix.lock ./
|
||||||
|
RUN mix deps.get --only $MIX_ENV
|
||||||
|
RUN mkdir config
|
||||||
|
|
||||||
|
# copy compile-time config files before we compile dependencies
|
||||||
|
# to ensure any relevant config change will trigger the dependencies
|
||||||
|
# to be re-compiled.
|
||||||
|
COPY config/config.exs config/${MIX_ENV}.exs config/
|
||||||
|
RUN mix deps.compile
|
||||||
|
|
||||||
|
COPY priv priv
|
||||||
|
|
||||||
|
COPY lib lib
|
||||||
|
|
||||||
|
COPY assets assets
|
||||||
|
|
||||||
|
# compile assets
|
||||||
|
RUN mix assets.deploy
|
||||||
|
|
||||||
|
# Compile the release
|
||||||
|
RUN mix compile
|
||||||
|
|
||||||
|
# Changes to config/runtime.exs don't require recompiling the code
|
||||||
|
COPY config/runtime.exs config/
|
||||||
|
|
||||||
|
COPY rel rel
|
||||||
|
RUN mix release
|
||||||
|
|
||||||
|
# start a new build stage so that the final image will only contain
|
||||||
|
# the compiled release and other runtime necessities
|
||||||
|
FROM ${RUNNER_IMAGE}
|
||||||
|
|
||||||
|
RUN apt-get update -y && \
|
||||||
|
apt-get install -y libstdc++6 openssl libncurses5 locales ca-certificates \
|
||||||
|
&& apt-get clean && rm -f /var/lib/apt/lists/*_*
|
||||||
|
|
||||||
|
# Set the locale
|
||||||
|
RUN sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen && locale-gen
|
||||||
|
|
||||||
|
ENV LANG en_US.UTF-8
|
||||||
|
ENV LANGUAGE en_US:en
|
||||||
|
ENV LC_ALL en_US.UTF-8
|
||||||
|
|
||||||
|
WORKDIR "/app"
|
||||||
|
RUN chown nobody /app
|
||||||
|
|
||||||
|
# set runner ENV
|
||||||
|
ENV MIX_ENV="prod"
|
||||||
|
|
||||||
|
# Only copy the final release from the build stage
|
||||||
|
COPY --from=builder --chown=nobody:root /app/_build/${MIX_ENV}/rel/mv ./
|
||||||
|
|
||||||
|
USER nobody
|
||||||
|
|
||||||
|
# If using an environment that doesn't automatically reap zombie processes, it is
|
||||||
|
# advised to add an init process such as tini via `apt-get install`
|
||||||
|
# above and adding an entrypoint. See https://github.com/krallin/tini for details
|
||||||
|
# ENTRYPOINT ["/tini", "--"]
|
||||||
|
|
||||||
|
CMD ["/app/bin/server"]
|
||||||
43
Justfile
43
Justfile
|
|
@ -1,4 +1,4 @@
|
||||||
run: install-dependencies start-database migrate-database
|
run: install-dependencies start-database migrate-database seed-database
|
||||||
mix phx.server
|
mix phx.server
|
||||||
|
|
||||||
install-dependencies:
|
install-dependencies:
|
||||||
|
|
@ -10,6 +10,9 @@ migrate-database:
|
||||||
reset-database:
|
reset-database:
|
||||||
mix ash.reset
|
mix ash.reset
|
||||||
|
|
||||||
|
seed-database:
|
||||||
|
mix run priv/repo/seeds.exs
|
||||||
|
|
||||||
start-database:
|
start-database:
|
||||||
docker compose up -d
|
docker compose up -d
|
||||||
|
|
||||||
|
|
@ -30,3 +33,41 @@ test:
|
||||||
|
|
||||||
format:
|
format:
|
||||||
mix format
|
mix format
|
||||||
|
|
||||||
|
build-docker-container:
|
||||||
|
docker build --tag mitgliederverwaltung .
|
||||||
|
|
||||||
|
# This is meant for debugging the container build process only.
|
||||||
|
run-docker-container: build-docker-container
|
||||||
|
podman 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}}"
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ if config_env() == :prod do
|
||||||
You can generate one by calling: mix phx.gen.secret
|
You can generate one by calling: mix phx.gen.secret
|
||||||
"""
|
"""
|
||||||
|
|
||||||
host = System.get_env("PHX_HOST") || "example.com"
|
host = System.get_env("PHX_HOST") || raise "Please define the PHX_HOST environment variable."
|
||||||
port = String.to_integer(System.get_env("PORT") || "4000")
|
port = String.to_integer(System.get_env("PORT") || "4000")
|
||||||
|
|
||||||
config :mv, :dns_cluster_query, System.get_env("DNS_CLUSTER_QUERY")
|
config :mv, :dns_cluster_query, System.get_env("DNS_CLUSTER_QUERY")
|
||||||
|
|
|
||||||
34
lib/membership/email.ex
Normal file
34
lib/membership/email.ex
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
defmodule Mv.Membership.Email do
|
||||||
|
@constraints [
|
||||||
|
match: ~r/^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$/,
|
||||||
|
trim?: true,
|
||||||
|
min_length: 5,
|
||||||
|
max_length: 254
|
||||||
|
]
|
||||||
|
|
||||||
|
use Ash.Type.NewType,
|
||||||
|
subtype_of: :string,
|
||||||
|
constraints: @constraints
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def cast_input(value, _) when is_binary(value) do
|
||||||
|
value = if @constraints[:trim?], do: String.trim(value), else: value
|
||||||
|
|
||||||
|
cond do
|
||||||
|
@constraints[:min_length] && String.length(value) < @constraints[:min_length] ->
|
||||||
|
:error
|
||||||
|
|
||||||
|
@constraints[:max_length] && String.length(value) > @constraints[:max_length] ->
|
||||||
|
:error
|
||||||
|
|
||||||
|
@constraints[:match] && !Regex.match?(@constraints[:match], value) ->
|
||||||
|
:error
|
||||||
|
|
||||||
|
true ->
|
||||||
|
{:ok, value}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def cast_input(_, _), do: :error
|
||||||
|
end
|
||||||
|
|
@ -16,8 +16,17 @@ defmodule Mv.Membership.Property do
|
||||||
attributes do
|
attributes do
|
||||||
uuid_primary_key :id
|
uuid_primary_key :id
|
||||||
|
|
||||||
attribute :value, :string,
|
attribute :value, :union,
|
||||||
description: "Speichert den Wert, Typ-Interpretation per property_type.typ"
|
constraints: [
|
||||||
|
storage: :type_and_value,
|
||||||
|
types: [
|
||||||
|
boolean: [type: :boolean],
|
||||||
|
date: [type: :date],
|
||||||
|
integer: [type: :integer],
|
||||||
|
string: [type: :string],
|
||||||
|
email: [type: Mv.Membership.Email]
|
||||||
|
]
|
||||||
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
relationships do
|
relationships do
|
||||||
|
|
@ -25,4 +34,8 @@ defmodule Mv.Membership.Property do
|
||||||
|
|
||||||
belongs_to :property_type, Mv.Membership.PropertyType
|
belongs_to :property_type, Mv.Membership.PropertyType
|
||||||
end
|
end
|
||||||
|
|
||||||
|
calculations do
|
||||||
|
calculate :value_to_string, :string, expr(value[:value] <> "")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ defmodule Mv.Membership.PropertyType do
|
||||||
|
|
||||||
actions do
|
actions do
|
||||||
defaults [:create, :read, :update, :destroy]
|
defaults [:create, :read, :update, :destroy]
|
||||||
default_accept [:name, :type, :description, :immutable, :required]
|
default_accept [:name, :value_type, :description, :immutable, :required]
|
||||||
end
|
end
|
||||||
|
|
||||||
attributes do
|
attributes do
|
||||||
|
|
@ -18,7 +18,8 @@ defmodule Mv.Membership.PropertyType do
|
||||||
|
|
||||||
attribute :name, :string, allow_nil?: false, public?: true
|
attribute :name, :string, allow_nil?: false, public?: true
|
||||||
|
|
||||||
attribute :type, :string,
|
attribute :value_type, :atom,
|
||||||
|
constraints: [one_of: [:string, :integer, :boolean, :date, :email]],
|
||||||
allow_nil?: false,
|
allow_nil?: false,
|
||||||
description: "Definies the datatype `Property.value` is interpreted as"
|
description: "Definies the datatype `Property.value` is interpreted as"
|
||||||
|
|
||||||
|
|
|
||||||
28
lib/mv/release.ex
Normal file
28
lib/mv/release.ex
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
defmodule Mv.Release do
|
||||||
|
@moduledoc """
|
||||||
|
Used for executing DB release tasks when run in production without Mix
|
||||||
|
installed.
|
||||||
|
"""
|
||||||
|
@app :mv
|
||||||
|
|
||||||
|
def migrate do
|
||||||
|
load_app()
|
||||||
|
|
||||||
|
for repo <- repos() do
|
||||||
|
{:ok, _, _} = Ecto.Migrator.with_repo(repo, &Ecto.Migrator.run(&1, :up, all: true))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def rollback(repo, version) do
|
||||||
|
load_app()
|
||||||
|
{:ok, _, _} = Ecto.Migrator.with_repo(repo, &Ecto.Migrator.run(&1, :down, to: version))
|
||||||
|
end
|
||||||
|
|
||||||
|
defp repos do
|
||||||
|
Application.fetch_env!(@app, :ecto_repos)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp load_app do
|
||||||
|
Application.load(@app)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -9,7 +9,11 @@ defmodule MvWeb.MemberLive.FormComponent do
|
||||||
Enum.map(property_types, fn pt ->
|
Enum.map(property_types, fn pt ->
|
||||||
%{
|
%{
|
||||||
"property_type_id" => pt.id,
|
"property_type_id" => pt.id,
|
||||||
"value" => nil
|
"value" => %{
|
||||||
|
"type" => pt.value_type,
|
||||||
|
"value" => nil,
|
||||||
|
"_union_type" => Atom.to_string(pt.value_type)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
|
@ -34,7 +38,15 @@ defmodule MvWeb.MemberLive.FormComponent do
|
||||||
>
|
>
|
||||||
<.inputs_for :let={f_property} field={@form[:properties]}>
|
<.inputs_for :let={f_property} field={@form[:properties]}>
|
||||||
<% type = Enum.find(@property_types, &(&1.id == f_property[:property_type_id].value)) %>
|
<% type = Enum.find(@property_types, &(&1.id == f_property[:property_type_id].value)) %>
|
||||||
<.input field={f_property[:value]} label={type && type.name} />
|
<.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} />
|
||||||
|
</.inputs_for>
|
||||||
<input
|
<input
|
||||||
type="hidden"
|
type="hidden"
|
||||||
name={f_property[:property_type_id].name}
|
name={f_property[:property_type_id].name}
|
||||||
|
|
@ -95,12 +107,27 @@ defmodule MvWeb.MemberLive.FormComponent do
|
||||||
not Enum.member?(existing_properties, Map.get(i, "property_type_id"))
|
not Enum.member?(existing_properties, Map.get(i, "property_type_id"))
|
||||||
end
|
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 =
|
form =
|
||||||
AshPhoenix.Form.for_update(
|
AshPhoenix.Form.for_update(
|
||||||
member,
|
member,
|
||||||
:update_member,
|
:update_member,
|
||||||
api: Mv.Membership,
|
api: Mv.Membership,
|
||||||
as: "member",
|
as: "member",
|
||||||
|
params: params,
|
||||||
forms: [auto?: true]
|
forms: [auto?: true]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ defmodule Mv.Repo.Migrations.InitialMigration do
|
||||||
create table(:property_types, primary_key: false) do
|
create table(:property_types, primary_key: false) do
|
||||||
add :id, :uuid, null: false, default: fragment("gen_random_uuid()"), primary_key: true
|
add :id, :uuid, null: false, default: fragment("gen_random_uuid()"), primary_key: true
|
||||||
add :name, :text, null: false
|
add :name, :text, null: false
|
||||||
add :type, :text, null: false
|
add :value_type, :text, null: false
|
||||||
add :description, :text
|
add :description, :text
|
||||||
add :immutable, :boolean, null: false, default: false
|
add :immutable, :boolean, null: false, default: false
|
||||||
add :required, :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
|
create table(:properties, primary_key: false) do
|
||||||
add :id, :uuid, null: false, default: fragment("gen_random_uuid()"), primary_key: true
|
add :id, :uuid, null: false, default: fragment("gen_random_uuid()"), primary_key: true
|
||||||
add :value, :text
|
add :value, :map
|
||||||
add :member_id, :uuid
|
add :member_id, :uuid
|
||||||
add :property_type_id, :uuid
|
add :property_type_id, :uuid
|
||||||
end
|
end
|
||||||
|
|
@ -2,35 +2,46 @@
|
||||||
#
|
#
|
||||||
# mix run priv/repo/seeds.exs
|
# 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
|
alias Mv.Membership
|
||||||
|
|
||||||
for attrs <- [
|
for attrs <- [
|
||||||
%{
|
%{
|
||||||
name: "Vorname",
|
name: "Vorname",
|
||||||
type: "string",
|
value_type: :string,
|
||||||
description: "Vorname des Mitglieds",
|
description: "Vorname des Mitglieds",
|
||||||
immutable: true,
|
immutable: true,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
|
%{
|
||||||
|
name: "Nachname",
|
||||||
|
value_type: :string,
|
||||||
|
description: "Nachname des Mitglieds",
|
||||||
|
immutable: true,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
%{
|
||||||
|
name: "Geburtsdatum",
|
||||||
|
value_type: :date,
|
||||||
|
description: "Geburtsdatum des Mitglieds",
|
||||||
|
immutable: true,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
%{
|
||||||
|
name: "Bezahlt",
|
||||||
|
value_type: :boolean,
|
||||||
|
description: "Status des Mitgliedsbeitrages des Mitglieds",
|
||||||
|
immutable: true,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
%{
|
%{
|
||||||
name: "Email",
|
name: "Email",
|
||||||
type: "string",
|
value_type: :email,
|
||||||
description: "Email-Adresse des Mitglieds",
|
description: "Email-Adresse des Mitglieds",
|
||||||
immutable: true,
|
immutable: true,
|
||||||
required: true
|
required: true
|
||||||
}
|
}
|
||||||
] do
|
] do
|
||||||
# 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(
|
Membership.create_property_type(
|
||||||
attrs,
|
attrs,
|
||||||
upsert?: true,
|
upsert?: true,
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@
|
||||||
"custom_indexes": [],
|
"custom_indexes": [],
|
||||||
"custom_statements": [],
|
"custom_statements": [],
|
||||||
"has_create_action": true,
|
"has_create_action": true,
|
||||||
"hash": "A0402269CB456075B81CA4CB3A2135A2C88D8B7FD51CD7A23084AA5264FEE344",
|
"hash": "35D45214D6D344B0AF6CFCB69B8682FCB3D382D85883D3D3AAC1AEE7F54FD89A",
|
||||||
"identities": [],
|
"identities": [],
|
||||||
"multitenancy": {
|
"multitenancy": {
|
||||||
"attribute": null,
|
"attribute": null,
|
||||||
|
|
@ -18,7 +18,7 @@
|
||||||
"references": null,
|
"references": null,
|
||||||
"size": null,
|
"size": null,
|
||||||
"source": "value",
|
"source": "value",
|
||||||
"type": "text"
|
"type": "map"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_nil?": true,
|
"allow_nil?": true,
|
||||||
|
|
@ -84,7 +84,7 @@
|
||||||
"custom_indexes": [],
|
"custom_indexes": [],
|
||||||
"custom_statements": [],
|
"custom_statements": [],
|
||||||
"has_create_action": true,
|
"has_create_action": true,
|
||||||
"hash": "F2A42A3427E0428637F465E4F357A3BE21B33231F94CF77B4843084128F6BDA5",
|
"hash": "8CF241CB9E8239511914EDEC96186BB7879529372BD8A4162431CCE9961F4F1B",
|
||||||
"identities": [],
|
"identities": [],
|
||||||
"multitenancy": {
|
"multitenancy": {
|
||||||
"attribute": null,
|
"attribute": null,
|
||||||
|
|
@ -27,7 +27,7 @@
|
||||||
"primary_key?": false,
|
"primary_key?": false,
|
||||||
"references": null,
|
"references": null,
|
||||||
"size": null,
|
"size": null,
|
||||||
"source": "type",
|
"source": "value_type",
|
||||||
"type": "text"
|
"type": "text"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -66,7 +66,7 @@
|
||||||
"custom_indexes": [],
|
"custom_indexes": [],
|
||||||
"custom_statements": [],
|
"custom_statements": [],
|
||||||
"has_create_action": true,
|
"has_create_action": true,
|
||||||
"hash": "47210108DE1E7B2A20A67205E875B3440526941E61AB95B166976E8CD8AA0955",
|
"hash": "F98A723AE0D20005FBE4205E46ABEE09A88DFF9334C85BADC1FBEEF100F3E25B",
|
||||||
"identities": [
|
"identities": [
|
||||||
{
|
{
|
||||||
"all_tenants?": false,
|
"all_tenants?": false,
|
||||||
5
rel/overlays/bin/migrate
Executable file
5
rel/overlays/bin/migrate
Executable file
|
|
@ -0,0 +1,5 @@
|
||||||
|
#!/bin/sh
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
cd -P -- "$(dirname -- "$0")"
|
||||||
|
exec ./mv eval Mv.Release.migrate
|
||||||
5
rel/overlays/bin/server
Executable file
5
rel/overlays/bin/server
Executable file
|
|
@ -0,0 +1,5 @@
|
||||||
|
#!/bin/sh
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
cd -P -- "$(dirname -- "$0")"
|
||||||
|
PHX_SERVER=true exec ./mv start
|
||||||
|
|
@ -8,8 +8,18 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"groupName": "asdf tool versions",
|
"groupName": "asdf tool versions",
|
||||||
"description": "Keep in mind that Renovate currently does not support updating PostgreSQL via asdf.",
|
|
||||||
"matchFileNames": [".tool-versions"]
|
"matchFileNames": [".tool-versions"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"groupName": "postgres",
|
||||||
|
"description": "Group updates to postgres across drone ci, docker-compose.yml and other files",
|
||||||
|
"matchPackageNames": ["postgres", "docker.io/library/postgres"],
|
||||||
|
"matchDatasources": ["docker"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"matchFileNames": [".tool-versions", "Dockerfile"],
|
||||||
|
"matchCurrentValue": "**-otp-**",
|
||||||
|
"enabled": false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue