Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,5 @@ Dockerfile-jackjoe
.env.*
docker-compose.yml
node_modules
.serena
.playwright-mcp
8 changes: 4 additions & 4 deletions .tool-versions
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
elixir 1.17.2
erlang 27.0.1
nodejs 20.17.0
elixir 1.19.5-otp-28
erlang 28.1
nodejs 24.13.0
rebar 3.22.1
pnpm 9.9.0
pnpm 10.28.2
yarn 1.22.19
21 changes: 0 additions & 21 deletions .travis.yml

This file was deleted.

91 changes: 91 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,94 @@
## 2603.2.0 (2026-03-11)

**BREAKING**

This is a major infrastructure upgrade. Please review the breaking changes below before upgrading.

### Added

- Dark/light/system theme support with user preference stored in database
- Theme picker in user edit form (system/light/dark)
- Inline JS in layout for instant theme application (no flash-of-wrong-theme)
- System theme option respects `prefers-color-scheme` media query and reacts to OS changes

### Security

- Use distinct `signing_salt` for LiveView (was identical to `secret_key_base`)
- Add `SECRET_KEY_BASE` env var override in `releases.exs`
- Remove `check_origin: false` from base config (keep in `dev.exs` only)
- Disable `debug_errors` in production config
- Remove API key from Mailgun client URLs (was logged in plaintext via `Logger.debug`)
- Guard S3 uploads with `store_messages` config check; replace `ExAws.request!` with error-handling `ExAws.request`
- Use `String.to_existing_atom/1` for log level env var (prevents atom table exhaustion)

### Changed

- Complete UI overhaul: industrial/utilitarian dark theme with monospace typography throughout
- Replaced inline styles in templates with semantic CSS classes
- Login page: centered card layout
- Tables: dense rows, uppercase mono headers, no rounded corners
- Navigation: sticky header, amber accent underline for active state
- Alerts: left-border accent style (amber for warnings, red for errors, green for info)
- Forms: dark inset inputs with amber focus borders
- All CSS via custom properties for easy theming
- Removed obsolete `X-UA-Compatible` meta tag and empty `description` meta tag
- Fixed `target="blank"` to `target="_blank"` in event links
- Removed stale hard-coded subnav breadcrumb from layout
- All templates converted from `.eex` to `.heex` (HEEx)
- Layout partials (head/foot) inlined into layout templates
- `~L` sigils (LEEx) replaced with `~H` (HEEx) throughout
- `link/2` helpers replaced with `<.link>` components
- `form_for` replaced with `<.form>` components
- `phx-feedback-for` and `phx-no-feedback` removed from core components (removed in LiveView 1.0)
- `Flop.Phoenix.cursor_pagination` replaced with `Flop.Phoenix.pagination`
- Added `compilers: [:phoenix_live_view]` and `listeners: [Phoenix.CodeReloader]` to mix.exs (Phoenix 1.8 requirement)
- Endpoint: `gzip: false` -> `gzip: not code_reloading?`
- Config: `config :logger, :console` -> `config :logger, :default_formatter`
- Config: `import_config "#{Mix.env()}.exs"` -> `"#{config_env()}.exs"`
- Fixed test config referencing `MailgunLogger.Endpoint` instead of `MailgunLoggerWeb.Endpoint`
- Fixed core_components referencing wrong Gettext module
- Added `lazy_html` test dependency (LiveView 1.1 requirement)
- Dev config: added `phoenix_live_view` debug annotations and expensive runtime checks

### Performance

- Add time filter to `get_stats/1` query (was scanning entire events table)
- Use `Repo.aggregate` for account count instead of loading all rows

### Removed

- Dead modules: `Pager`, `Pager.Page`, `RunTriggererLive`, `UserSocket`, `PagingHelpers`
- Unused functions: `Users.assign_role/2`, `Emails.test_mail/1`, duplicate `Events.bucket/0`
- Unused socket mount from endpoint
- Unused `ml_pagesize` config
- Dead Papertrail config from `releases.exs`
- Stale `config :phoenix, :serve_endpoints` from prod config
- Stale `root: "."` from prod endpoint config
- Duplicate `config :logger` block in `config.exs`
- Removed `:phoenix_live_view` from compilers (obsolete since LiveView 0.18)
- Removed `import Phoenix.LiveView.Helpers` (removed in LiveView 1.0)

### Fixed

- `IO.inspect(changeset)` left in user controller error path
- `event["delivery-status"]["attempt-no"]` crash on non-delivery events (use `get_in/2`)

### Breaking changes

- **Removed `logger_papertrail_backend`** dependency and all Papertrail logging configuration. If you relied on Papertrail for log forwarding, you will need to set up an alternative solution.
- **Removed `:backends` logger configuration**. Elixir 1.19 / OTP 28 deprecates the `:backends` key. Logger now uses the default Erlang handler.
- **`Argon2.check_pass/2`** replaced with `Argon2.verify_pass/2` — no user-facing impact, but custom auth code referencing the old API will need updating.

### Upgraded

- **Phoenix 1.7 -> 1.8.5**
- **Phoenix LiveView 0.20 -> 1.1.27**
- **Ecto SQL 3.11 -> 3.13**, Postgrex 0.20 -> 0.22
- **Flop 0.25 -> 0.26**, Flop Phoenix 0.22 -> 0.25
- **Gettext 0.16 -> 0.25** (now uses `Gettext.Backend`)
- Plug 1.7 -> 1.19, Plug Cowboy 2.1 -> 2.8
- Elixir requirement: ~> 1.17, tested on Elixir 1.19.5 / OTP 28

## 2410.1.0 (2024-10-09)

- Fix: prevent creation of admins when setup has been done
Expand Down
33 changes: 22 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ Since version 2302.1.0 (Feb 2023) the database has switched from MySQL to Postgr
<img src="./public/logo.svg" width="128" alt="" />
</div>

[![Build Status](https://app.travis-ci.com/jackjoe/mailgun_logger.svg?branch=master)](https://app.travis-ci.com/jackjoe/mailgun_logger)

Simple Mailgun log persistence in Phoenix/Elixir.

MailgunLogger is a simple admin tool that uses the Mailgun API to retrieves events on a regular basis from Mailgun - who only provide a limited time of event storage - and stores them inside a PostgreSQL database.
Expand All @@ -34,6 +32,12 @@ _This is NOT meant as a replacement for the excellent online tooling provided by

_Jack + Joe is not responsible for your use of this tool, neither for any persistence guarantees. Free comes at a price :)_

## Requirements

- Elixir ~> 1.17
- Phoenix ~> 1.8
- PostgreSQL

## Versioning

We use a simplified version numbering in the format of:
Expand Down Expand Up @@ -78,11 +82,15 @@ Following variables are available:
- **ML_DB_PASSWORD**: database password
- **ML_DB_NAME**: database name
- **ML_DB_HOST**: database host
- **SECRET_KEY_BASE**: secret key base for Phoenix (required for releases)
- **HOST**: application host (e.g. `localhost`)
- **PORT**: application port (default: `5050`)

[Optional]

- **ML_DB_PORT**: database port (default: `5432`)
- **ML_PAGESIZE**: events per page (default: `50`)
- **ML_STORE_MESSAGES**: when true, persist messages to the S3 bucket described below, defaults to false
- **ML_PAGESIZE**: events per page
- **ML_LOG_LEVEL**: log level (info, debug, warn, ...)
- **MAILGUN_API_KEY**: to send the password reset email
- **MAILGUN_DOMAIN**: to send the password reset email
Expand All @@ -106,6 +114,8 @@ $ docker run -d -p 5050:5050 \
-e "ML_DB_PASSWORD=password" \
-e "ML_DB_NAME=mailgun_logger" \
-e "ML_DB_HOST=my_db_host" \
-e "SECRET_KEY_BASE=your-secret-key-base" \
-e "HOST=localhost" \
--name mailgun_logger jackjoe/mailgun_logger
```

Expand All @@ -114,8 +124,6 @@ $ docker run -d -p 5050:5050 \
With the following `docker-compose.yml`:

```yml
version: "3"

services:
db:
image: postgres
Expand All @@ -142,6 +150,8 @@ services:
- "ML_DB_PASSWORD=logger"
- "ML_DB_NAME=mailgun_logger"
- "ML_DB_HOST=db"
- "SECRET_KEY_BASE=generate-a-secret-key-base"
- "HOST=localhost"
- "MAILGUN_API_KEY=*****"
- "MAILGUN_DOMAIN=m.mydomain.be"
- "AWS_ACCESS_KEY_ID=******"
Expand Down Expand Up @@ -175,7 +185,7 @@ Then head over to [http://0.0.0.0:5050](http://0.0.0.0:5050).
If you find yourself with many events in your database and a slow search, consider installing the [**pg_trm** extension](https://www.postgresql.org/docs/current/pgtrgm.html).
Adding an index on these 3 fields will give you a huge speed gain:

```mysql
```sql
CREATE EXTENSION pg_trgm;

CREATE INDEX events_recipient_gin_trgm_idx ON events USING gin (recipient gin_trgm_ops);
Expand All @@ -189,12 +199,13 @@ To run on your local machine, you need to setup shop first.
Mailgun Logger requires a PostgreSQL database using the following environment variables along with their defaults:

```elixir
# config/config.ex
# config/config.exs
config :mailgun_logger, MailgunLogger.Repo,
username: System.get_env("ML_DB_USER", "mailgun_logger_ci"),
password: System.get_env("ML_DB_PASSWORD", "johndoe"),
database: System.get_env("ML_DB_NAME", "mailgun_logger_ci_test"),
hostname: System.get_env("ML_DB_HOST", "localhost"),
username: System.get_env("ML_DB_USER") || "root",
password: System.get_env("ML_DB_PASSWORD"),
database: System.get_env("ML_DB_NAME") || "mailgun_logger_dev",
hostname: System.get_env("ML_DB_HOST") || "localhost",
port: System.get_env("ML_DB_PORT") || 5432
```

Either export your own enviroment variables or adhere to the defaults. Then, for convenience, run:
Expand Down
33 changes: 9 additions & 24 deletions config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,15 @@ config :mailgun_logger,
ecto_repos: [MailgunLogger.Repo],
env: Mix.env(),
store_messages: System.get_env("ML_STORE_MESSAGES") || false,
ml_pagesize: System.get_env("ML_PAGESIZE") || "100"
page_size: String.to_integer(System.get_env("ML_PAGESIZE", "50"))

# Configures the endpoint
config :mailgun_logger, MailgunLoggerWeb.Endpoint,
url: [host: System.get_env("HOST")],
secret_key_base: "9zFYul0/t5smQYyvAsFKC+Lk3AQbQrMw9Fp/OgOOJGQtHEn1dvH6WmdH26mGvv2d",
render_errors: [view: MailgunLoggerWeb.ErrorView, accepts: ~w(html json)],
live_view: [signing_salt: "9zFYul0/t5smQYyvAsFKC+Lk3AQbQrMw9Fp/OgOOJGQtHEn1dvH6WmdH26mGvv2d"],
pubsub_server: MailgunLogger.PubSub,
check_origin: false
live_view: [signing_salt: "ML_LV_Salt"],
pubsub_server: MailgunLogger.PubSub

# Configure esbuild (the version is required)
config :esbuild,
Expand All @@ -30,23 +29,15 @@ config :esbuild,
env: %{"NODE_PATH" => Path.expand("../deps", __DIR__)}
]

config :phoenix, :format_encoders, json: Jason
config :phoenix, :json_library, Jason
config :phoenix,
format_encoders: [json: Jason],
json_library: Jason

# config :scrivener_html,
# routes_helper: MailgunLoggerWeb.Router.Helpers,
# # If you use a single view style everywhere, you can configure it here. See View Styles below for more info.
# view_style: :bootstrap

# Configures Elixir's Logger
config :logger, :console,
format: "$time $metadata[$level] $message\n",
metadata: [:user_id]

config :logger,
backends: [:console],
level: String.to_atom(System.get_env("ML_LOG_LEVEL", "debug")) || :debug

config :flop, repo: MailgunLogger.Repo

config :ex_aws,
Expand Down Expand Up @@ -90,16 +81,10 @@ config :mailgun_logger, MailgunLogger.Repo,

# Configures Elixir's Logger
config :logger,
backends: [:console],
level: :debug,
console: [
level: String.to_existing_atom(System.get_env("ML_LOG_LEVEL", "debug")),
default_formatter: [
format: "$time $metadata[$level] $message\n",
metadata: [:user_id]
],
logger_papertrail_backend: [
host: System.get_env("PAPERTRAIL_HOST"),
system_name: "mailgun-logger",
format: "$metadata $message"
]

import_config "#{Mix.env()}.exs"
import_config "#{config_env()}.exs"
10 changes: 7 additions & 3 deletions config/dev.exs
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ config :mailgun_logger, MailgunLoggerWeb.Endpoint,
certfile: "priv/cert/selfsigned.pem"
],
live_reload: [
web_console_logger: true,
patterns: [
~r{priv/static/.*(js|css|png|jpeg|jpg|gif|svg)$},
~r{priv/gettext/.*(po)$},
~r{lib/mailgun_logger_web/views/.*(ex)$},
~r{lib/mailgun_logger_web/templates/.*(eex)$}
~r{lib/mailgun_logger_web/(?:controllers|components|lives|views|templates|router)/?.*\.(ex|heex)$}
]
],
debug_errors: true,
Expand All @@ -36,5 +36,9 @@ config :mailgun_logger, MailgunLogger.Scheduler, jobs: []
# config :mailgun_logger, MailgunLogger.Mailer, adapter: Bamboo.LocalAdapter

# Do not include metadata nor timestamps in development logs
config :logger, :console, format: "[$level] $message\n"
config :logger, :default_formatter, format: "[$level] $message\n"
config :phoenix, :stacktrace_depth, 20

config :phoenix_live_view,
debug_heex_annotations: true,
enable_expensive_runtime_checks: true
5 changes: 1 addition & 4 deletions config/prod.exs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,12 @@ config :mailgun_logger, MailgunLoggerWeb.Endpoint,
http: [port: System.fetch_env!("PORT"), compress: true],
url: [host: System.fetch_env!("HOST"), port: 80],
cache_static_manifest: "priv/static/cache_manifest.json",
debug_errors: true,
debug_errors: false,
code_reloader: false,
root: ".",
version: Application.spec(:mailgun_logger, :vsn)

config :phoenix, :serve_endpoints, true

config :logger,
backends: [:console, LoggerPapertrailBackend.Logger],
level: String.to_existing_atom(System.get_env("ML_LOG_LEVEL", "info")) || :info,
compile_time_purge_matching: [[application: :remote_ip]]

Expand Down
5 changes: 3 additions & 2 deletions config/releases.exs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import Config

config :mailgun_logger, MailgunLoggerWeb.Endpoint,
secret_key_base: System.fetch_env!("SECRET_KEY_BASE")

config :mailgun_logger, MailgunLogger.Repo,
username: System.get_env("ML_DB_USER"),
password: System.get_env("ML_DB_PASSWORD"),
Expand All @@ -13,8 +16,6 @@ config :mailgun_logger, MailgunLogger.Mailer,
domain: System.get_env("MAILGUN_DOMAIN"),
from: System.get_env("MAILGUN_FROM") || "no-reply@jackjoe.be"

config :logger, logger_papertrail_backend: [host: System.get_env("PAPERTRAIL_HOST")]

config :ex_aws,
access_key_id: System.get_env("AWS_ACCESS_KEY_ID"),
secret_access_key: System.get_env("AWS_SECRET_ACCESS_KEY"),
Expand Down
9 changes: 3 additions & 6 deletions config/test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,15 @@ import Config

# We don't run a server during test. If one is required,
# you can enable the server option below.
config :mailgun_logger, MailgunLogger.Endpoint,
config :mailgun_logger, MailgunLoggerWeb.Endpoint,
http: [port: 4001],
server: false

# Quantum cron schedule
config :mailgun_logger, MailgunLogger.Scheduler,
jobs: [
# Every hour
# {"50 * * * *", {MailgunLogger, :run, []}}
]
jobs: []

config :logger, level: :warn
config :logger, level: :warning
config :mailgun_logger, MailgunLogger.Repo, pool: Ecto.Adapters.SQL.Sandbox

config :mailgun_logger, MailgunLogger.Repo,
Expand Down
Loading
Loading