Packages

phoenix_kit

1.7.105
1.7.207 1.7.206 1.7.205 1.7.204 1.7.203 1.7.202 1.7.201 1.7.200 1.7.199 1.7.198 1.7.197 1.7.196 1.7.194 1.7.193 1.7.192 1.7.191 1.7.190 1.7.189 1.7.187 1.7.186 1.7.185 1.7.184 1.7.183 1.7.182 1.7.181 1.7.180 1.7.179 1.7.178 1.7.177 1.7.176 1.7.175 1.7.174 1.7.173 1.7.172 1.7.171 1.7.170 1.7.169 1.7.168 1.7.167 1.7.166 1.7.165 1.7.164 1.7.162 1.7.161 1.7.160 1.7.159 1.7.157 1.7.156 1.7.155 1.7.154 1.7.153 1.7.152 1.7.151 1.7.150 1.7.149 1.7.146 1.7.145 1.7.144 1.7.143 1.7.138 1.7.133 1.7.132 1.7.131 1.7.130 1.7.128 1.7.126 1.7.125 1.7.121 1.7.120 1.7.119 1.7.118 1.7.117 1.7.116 1.7.115 1.7.114 1.7.113 1.7.112 1.7.111 1.7.110 1.7.109 1.7.108 1.7.107 1.7.106 1.7.105 1.7.104 1.7.103 1.7.102 1.7.101 1.7.100 1.7.99 1.7.98 1.7.97 1.7.96 1.7.95 1.7.94 1.7.93 1.7.92 1.7.91 1.7.90 1.7.89 1.7.88 1.7.87 1.7.86 1.7.85 1.7.84 1.7.83 1.7.82 1.7.81 1.7.80 1.7.79 1.7.78 1.7.77 1.7.76 1.7.75 1.7.74 1.7.71 1.7.70 1.7.69 1.7.66 1.7.65 1.7.64 1.7.63 1.7.62 1.7.61 1.7.59 1.7.58 1.7.57 1.7.56 1.7.55 1.7.54 1.7.53 1.7.52 1.7.51 1.7.49 1.7.44 1.7.43 1.7.42 1.7.41 1.7.39 1.7.38 1.7.37 1.7.36 1.7.34 1.7.33 1.7.31 1.7.30 1.7.29 1.7.28 1.7.27 1.7.26 1.7.25 1.7.24 1.7.23 1.7.22 1.7.21 1.7.20 1.7.19 1.7.18 1.7.17 1.7.16 1.7.15 1.7.14 1.7.13 1.7.12 1.7.11 1.7.10 1.7.9 1.7.8 1.7.7 1.7.6 1.7.5 1.7.4 1.7.3 1.7.2 1.7.1 1.7.0 1.6.20 1.6.19 1.6.18 1.6.17 1.6.16 1.6.15 1.6.14 1.6.13 1.6.12 1.6.11 1.6.10 1.6.9 1.6.8 1.6.7 1.6.6 1.6.5 1.6.4 1.6.3 1.5.2 1.5.1 1.5.0 1.4.9 1.4.8 1.4.7 1.4.6 1.4.5 1.4.4 1.4.3 1.4.2 1.4.1 1.4.0 1.3.2 1.3.1 1.3.0 1.2.10 1.2.9 1.2.8 1.2.7 1.2.5 1.2.4 1.2.2 1.2.1 1.2.0 1.1.0 1.0.0

A foundation for building Elixir Phoenix apps — SaaS, social networks, ERP systems, marketplaces, and more

Current section

221 Versions

Jump to

Compare versions

6 files changed
+182 additions
-4 deletions
unknownCHANGELOG.md
File is too large to be displayed (100 KB limit).
  @@ -1,6 +1,6 @@
1 1 {<<"links">>,[{<<"GitHub">>,<<"https://github.com/BeamLabEU/phoenix_kit">>}]}.
2 2 {<<"name">>,<<"phoenix_kit">>}.
3 - {<<"version">>,<<"1.7.104">>}.
3 + {<<"version">>,<<"1.7.105">>}.
4 4 {<<"description">>,
5 5 <<65,32,102,111,117,110,100,97,116,105,111,110,32,102,111,114,32,98,117,105,
6 6 108,100,105,110,103,32,69,108,105,120,105,114,32,80,104,111,101,110,105,
  @@ -457,6 +457,7 @@
457 457 <<"lib/phoenix_kit/migrations/postgres/v88.ex">>,
458 458 <<"lib/phoenix_kit/migrations/postgres/v20.ex">>,
459 459 <<"lib/phoenix_kit/migrations/postgres/v50.ex">>,
460 + <<"lib/phoenix_kit/migrations/postgres/v110.ex">>,
460 461 <<"lib/phoenix_kit/migrations/postgres/v18.ex">>,
461 462 <<"lib/phoenix_kit/migrations/postgres/v47.ex">>,
462 463 <<"lib/phoenix_kit/migrations/postgres/v107.ex">>,
  @@ -185,6 +185,79 @@ defmodule PhoenixKit.Migration do
185 185 migrator().migrated_version(opts)
186 186 end
187 187
188 + @doc """
189 + Idempotently brings `repo` up to the latest PhoenixKit migration version.
190 +
191 + Designed for test helpers and re-runnable boot paths where the
192 + database is long-lived but the calling process restarts on every
193 + invocation.
194 +
195 + ## Why this exists
196 +
197 + The natural-looking pattern
198 +
199 + Ecto.Migrator.run(repo, [{0, PhoenixKit.Migration}], :up, all: true)
200 +
201 + is broken for re-runnable contexts: `Ecto.Migrator` records "version
202 + 0 applied" in `schema_migrations` after the first call and filters
203 + `{0, PhoenixKit.Migration}` out of pending on every subsequent call.
204 + `PhoenixKit.Migration.up/1` is never re-invoked, so newly-shipped
205 + Vxxx migrations don't get applied even though PhoenixKit's own marker
206 + (the comment on the `phoenix_kit` table) is itself idempotent.
207 +
208 + `ensure_current/2` works around that by passing a fresh wall-clock
209 + version (`:os.system_time(:microsecond)`) to `Ecto.Migrator.up/4` on
210 + every call. Ecto sees a "new" migration each time and invokes the
211 + inner runner; PhoenixKit's marker then short-circuits if there's
212 + nothing new to apply. The `schema_migrations` table accumulates one
213 + row per call — cosmetic noise acceptable for the test-DB use case.
214 + Microsecond precision keeps the collision and clock-skew windows
215 + small enough that an NTP correction would have to rewind the clock
216 + by µs at exactly the wrong moment to hide a newly-shipped migration.
217 +
218 + For one-shot production migrations, prefer the documented
219 + `mix ecto.migrate` path with a hand-rolled migration that calls
220 + `PhoenixKit.Migration.up/1` directly.
221 +
222 + ## Options
223 +
224 + Forwarded verbatim to `Ecto.Migrator.up/4` and through to
225 + `PhoenixKit.Migration.up/1`. Common values:
226 +
227 + * `:log` — Ecto-level migration log (`:info` default; `false` to
228 + silence)
229 + * `:prefix` — runs PhoenixKit's tables under a non-default schema
230 +
231 + ## Return contract
232 +
233 + Returns `:ok` on success. Failures (advisory-lock contention,
234 + migration crashes, connection errors) propagate as raises from
235 + `Ecto.Migrator.up/4`; `ensure_current/2` does not wrap them in
236 + `{:error, _}`.
237 +
238 + ## Example
239 +
240 + # In test/test_helper.exs
241 + PhoenixKit.Migration.ensure_current(MyApp.Test.Repo, log: false)
242 + """
243 + @spec ensure_current(Ecto.Repo.t(), keyword()) :: :ok
244 + def ensure_current(repo, opts \\ []) do
245 + # Microsecond precision (rather than millisecond) shrinks the
246 + # collision and clock-skew windows by 1000x. Two concurrent calls
247 + # within the same microsecond would still collide, but in practice
248 + # `ensure_current/2` runs once per `mix test` invocation. Bigint-
249 + # safe (Postgres `bigint` covers ~9 quintillion microseconds, ~292
250 + # years).
251 + Ecto.Migrator.up(
252 + repo,
253 + :os.system_time(:microsecond),
254 + PhoenixKit.Migration.Runner,
255 + opts
256 + )
257 +
258 + :ok
259 + end
260 +
188 261 defp migrator do
189 262 case repo().__adapter__() do
190 263 Ecto.Adapters.Postgres -> PhoenixKit.Migrations.Postgres
  @@ -194,3 +267,39 @@ defmodule PhoenixKit.Migration do
194 267 end
195 268 end
196 269 end
270 +
271 + defmodule PhoenixKit.Migration.Runner do
272 + @moduledoc false
273 + # Static `Ecto.Migration` wrapper consumed by
274 + # `PhoenixKit.Migration.ensure_current/2`. Lives at module scope (not
275 + # an anonymous one defined per call) so `Ecto.Migrator.up/4` can
276 + # resolve `up/0` and `down/0` against a known module name.
277 + #
278 + # `prefix/0` is imported by `use Ecto.Migration` and reads the
279 + # current migration's prefix from the runner's process state. When
280 + # `ensure_current/2` is called with `prefix: "auth"`, Ecto.Migrator
281 + # propagates that into the runner context; without forwarding it
282 + # back into `PhoenixKit.Migration.up/1` here, the inner migrator
283 + # would default to `"public"` and silently apply the migrations to
284 + # the wrong schema.
285 +
286 + use Ecto.Migration
287 +
288 + def up, do: PhoenixKit.Migration.up(runner_opts(prefix()))
289 + def down, do: PhoenixKit.Migration.down(runner_opts(prefix()))
290 +
291 + # Pure transform of the runner-context prefix into opts threaded to
292 + # `PhoenixKit.Migration.up/1` / `down/1`. Split out of `up/0` and
293 + # `down/0` so it can be regression-tested without standing up a real
294 + # `Ecto.Migration.Runner` process — see
295 + # `test/phoenix_kit/migration_test.exs`.
296 + #
297 + # `prefix/0` returns nil when no `:prefix` opt was passed to
298 + # `Ecto.Migrator.up/4`. Forwarding `prefix: nil` to PhoenixKit's
299 + # migrator would override the `"public"` default in `with_defaults/2`
300 + # and crash inside `String.replace/4`. Only thread `:prefix` when it's
301 + # actually set.
302 + @doc false
303 + def runner_opts(nil), do: []
304 + def runner_opts(prefix), do: [prefix: prefix]
305 + end
  @@ -529,7 +529,17 @@ defmodule PhoenixKit.Migrations.Postgres do
529 529 - Replaces unique index with partial index (slug-mode only, WHERE slug IS NOT NULL)
530 530 - Adds unique index on `(group_uuid, post_date, post_time)` for timestamp-mode posts
531 531
532 - ### V109 - Rename Customer Service module to Customer Support ⚡ LATEST
532 + ### V110 - Add `language` to Document Creator templates ⚡ LATEST
533 + - Adds nullable `language VARCHAR(10)` to `phoenix_kit_doc_templates` so
534 + each template can be tagged with a single locale (full code, e.g.
535 + `en-US`, `et-EE`). Parent apps read it back via the public listing
536 + API to fill template variables in the matching language.
537 + - Documents intentionally do not get a language columnthey inherit
538 + from `template_uuidtemplates.language`.
539 + - Existing rows survive without a backfill; the form pre-selects the
540 + project's primary language when creating new templates.
541 +
542 + ### V109 - Rename Customer Service module to Customer Support
533 543 - Renames 7 settings keys from `customer_service_*` → `customer_support_*`
534 544 - Renames `auto_granted_perm:customer_service` → `auto_granted_perm:customer_support`
535 545 - Updates `phoenix_kit_role_permissions.module_key` from `customer_service` → `customer_support`
  @@ -813,7 +823,7 @@ defmodule PhoenixKit.Migrations.Postgres do
813 823 use Ecto.Migration
814 824
815 825 @initial_version 1
816 - @current_version 109
826 + @current_version 110
817 827 @default_prefix "public"
818 828
819 829 @doc false
  @@ -0,0 +1,58 @@
1 + defmodule PhoenixKit.Migrations.Postgres.V110 do
2 + @moduledoc """
3 + V110: Add `language` column to `phoenix_kit_doc_templates`.
4 +
5 + Each Document Creator template represents a single Google Doc, which is
6 + inherently single-language (translations live in separate templates).
7 + Storing the locale on the template lets parent apps fill template variables
8 + in the matching language regardless of the admin's UI locale.
9 +
10 + - `language` (VARCHAR(10), nullable) — full locale code (`"en-US"`,
11 + `"et-EE"`, `"ja"`, etc.) sourced from `PhoenixKit.Modules.Languages`.
12 + Nullable so existing rows survive without a backfill; the form
13 + pre-selects the project's primary language for new templates and
14 + callers can update the value at any time.
15 +
16 + Documents intentionally do not get a language column — they inherit
17 + language from `template_uuid → templates.language`.
18 +
19 + All operations are idempotent.
20 + """
21 +
22 + use Ecto.Migration
23 +
24 + def up(opts) do
25 + prefix = Map.get(opts, :prefix, "public")
26 + p = prefix_str(prefix)
27 + schema = if prefix == "public", do: "public", else: prefix
28 +
29 + execute("""
30 + DO $$
31 + BEGIN
32 + IF NOT EXISTS (
33 + SELECT FROM information_schema.columns
34 + WHERE table_schema = '#{schema}'
35 + AND table_name = 'phoenix_kit_doc_templates'
36 + AND column_name = 'language'
37 + ) THEN
38 + ALTER TABLE #{p}phoenix_kit_doc_templates
39 + ADD COLUMN language VARCHAR(10);
40 + END IF;
41 + END $$;
42 + """)
43 +
44 + execute("COMMENT ON TABLE #{p}phoenix_kit IS '110'")
45 + end
46 +
47 + def down(opts) do
48 + prefix = Map.get(opts, :prefix, "public")
49 + p = prefix_str(prefix)
50 +
51 + execute("ALTER TABLE #{p}phoenix_kit_doc_templates DROP COLUMN IF EXISTS language")
52 +
53 + execute("COMMENT ON TABLE #{p}phoenix_kit IS '109'")
54 + end
55 +
56 + defp prefix_str("public"), do: "public."
57 + defp prefix_str(prefix), do: "#{prefix}."
58 + end
Loading more files…