add emails
This commit is contained in:
		| @@ -18,7 +18,8 @@ config :lokal, LokalWeb.Endpoint, | |||||||
|   secret_key_base: "KH59P0iZixX5gP/u+zkxxG8vAAj6vgt0YqnwEB5JP5K+E567SsqkCz69uWShjE7I", |   secret_key_base: "KH59P0iZixX5gP/u+zkxxG8vAAj6vgt0YqnwEB5JP5K+E567SsqkCz69uWShjE7I", | ||||||
|   render_errors: [view: LokalWeb.ErrorView, accepts: ~w(html json), layout: false], |   render_errors: [view: LokalWeb.ErrorView, accepts: ~w(html json), layout: false], | ||||||
|   pubsub_server: Lokal.PubSub, |   pubsub_server: Lokal.PubSub, | ||||||
|   live_view: [signing_salt: "zOLgd3lr"] |   live_view: [signing_salt: "zOLgd3lr"], | ||||||
|  |   registration: System.get_env("REGISTRATION") || "invite" | ||||||
|  |  | ||||||
| config :lokal, Lokal.Application, automigrate: false | config :lokal, Lokal.Application, automigrate: false | ||||||
|  |  | ||||||
| @@ -39,6 +40,15 @@ config :lokal, Lokal.Mailer, adapter: Swoosh.Adapters.Local | |||||||
| # Swoosh API client is needed for adapters other than SMTP. | # Swoosh API client is needed for adapters other than SMTP. | ||||||
| config :swoosh, :api_client, false | config :swoosh, :api_client, false | ||||||
|  |  | ||||||
|  | # Gettext | ||||||
|  | config :gettext, :default_locale, "en_US" | ||||||
|  |  | ||||||
|  | # Configure Oban | ||||||
|  | config :lokal, Oban, | ||||||
|  |   repo: Lokal.Repo, | ||||||
|  |   plugins: [Oban.Plugins.Pruner], | ||||||
|  |   queues: [default: 10, mailers: 20] | ||||||
|  |  | ||||||
| # Configure esbuild (the version is required) | # Configure esbuild (the version is required) | ||||||
| # config :esbuild, | # config :esbuild, | ||||||
| #   version: "0.14.0", | #   version: "0.14.0", | ||||||
|   | |||||||
| @@ -2,9 +2,6 @@ import Config | |||||||
|  |  | ||||||
| # Configure your database | # Configure your database | ||||||
| config :lokal, Lokal.Repo, | config :lokal, Lokal.Repo, | ||||||
|   url: |  | ||||||
|     System.get_env("DATABASE_URL") || |  | ||||||
|       "ecto://postgres:postgres@localhost/lokal_dev", |  | ||||||
|   show_sensitive_data_on_connection_error: true, |   show_sensitive_data_on_connection_error: true, | ||||||
|   pool_size: 10 |   pool_size: 10 | ||||||
|  |  | ||||||
| @@ -15,13 +12,10 @@ config :lokal, Lokal.Repo, | |||||||
| # watchers to your application. For example, we use it | # watchers to your application. For example, we use it | ||||||
| # with esbuild to bundle .js and .css sources. | # with esbuild to bundle .js and .css sources. | ||||||
| config :lokal, LokalWeb.Endpoint, | config :lokal, LokalWeb.Endpoint, | ||||||
|   # Binding to loopback ipv4 address prevents access from other machines. |  | ||||||
|   # Change to `ip: {0, 0, 0, 0}` to allow access from other machines. |  | ||||||
|   http: [ip: {0, 0, 0, 0}, port: 4000], |  | ||||||
|   check_origin: false, |   check_origin: false, | ||||||
|   code_reloader: true, |   code_reloader: true, | ||||||
|   debug_errors: true, |   debug_errors: true, | ||||||
|   secret_key_base: "cSLRa17z1D1qLwQuaw73DMT7BX8oDMkru/rJIsmCdlFypLGRQW3bpqJRrZQtoZJQ", |   secret_key_base: "dg2lccMgaY3+ZeKppR+ondk4ZRaANZGIN0LMZT1u1uzscH4jO5W9a9b9V9BkC+MW", | ||||||
|   watchers: [ |   watchers: [ | ||||||
|     # Start the esbuild watcher by calling Esbuild.install_and_run(:default, args) |     # Start the esbuild watcher by calling Esbuild.install_and_run(:default, args) | ||||||
|     # esbuild: {Esbuild, :install_and_run, [:default, ~w(--sourcemap=inline --watch)]} |     # esbuild: {Esbuild, :install_and_run, [:default, ~w(--sourcemap=inline --watch)]} | ||||||
|   | |||||||
| @@ -9,16 +9,7 @@ import Config | |||||||
| # manifest is generated by the `mix phx.digest` task, | # manifest is generated by the `mix phx.digest` task, | ||||||
| # which you should run after static files are built and | # which you should run after static files are built and | ||||||
| # before starting your production server. | # before starting your production server. | ||||||
| config :lokal, LokalWeb.Endpoint, | config :lokal, LokalWeb.Endpoint, cache_static_manifest: "priv/static/cache_manifest.json" | ||||||
|   url: [host: "localhost"], |  | ||||||
|   http: [port: 4000], |  | ||||||
|   cache_static_manifest: "priv/static/cache_manifest.json" |  | ||||||
|  |  | ||||||
| config :lokal, Lokal.Repo, |  | ||||||
|   url: "ecto://postgres:postgres@localhost/lokal", |  | ||||||
|   pool_size: 10 |  | ||||||
|  |  | ||||||
| config :lokal, Lokal.Application, automigrate: true |  | ||||||
|  |  | ||||||
| # Do not print debug messages in production | # Do not print debug messages in production | ||||||
| config :logger, level: :info | config :logger, level: :info | ||||||
|   | |||||||
| @@ -12,19 +12,49 @@ if System.get_env("PHX_SERVER") && System.get_env("RELEASE_NAME") do | |||||||
|   config :lokal, LokalWeb.Endpoint, server: true |   config :lokal, LokalWeb.Endpoint, server: true | ||||||
| end | end | ||||||
|  |  | ||||||
| if config_env() == :prod do | config :lokal, LokalWeb.ViewHelpers, shibao_mode: System.get_env("SHIBAO_MODE") == "true" | ||||||
|   database_url = |  | ||||||
|  | # Set locale | ||||||
|  | Gettext.put_locale(System.get_env("LOCALE") || "en_US") | ||||||
|  |  | ||||||
|  | maybe_ipv6 = if System.get_env("ECTO_IPV6") == "true", do: [:inet6], else: [] | ||||||
|  |  | ||||||
|  | database_url = | ||||||
|  |   if config_env() == :test do | ||||||
|  |     System.get_env("TEST_DATABASE_URL") || | ||||||
|  |       "ecto://postgres:postgres@localhost/lokal_test#{System.get_env("MIX_TEST_PARTITION")}" | ||||||
|  |   else | ||||||
|     System.get_env("DATABASE_URL") || |     System.get_env("DATABASE_URL") || | ||||||
|       "ecto://postgres:postgres@lokal-db/lokal" |       "ecto://postgres:postgres@lokal-db/lokal" | ||||||
|  |   end | ||||||
|  |  | ||||||
|   maybe_ipv6 = if System.get_env("ECTO_IPV6"), do: [:inet6], else: [] | host = | ||||||
|  |   System.get_env("HOST") || | ||||||
|  |     raise "No hostname set! Must be the domain and tld like `lokal.bubbletea.dev`." | ||||||
|  |  | ||||||
|   config :lokal, Lokal.Repo, | interface = | ||||||
|  |   if config_env() in [:dev, :test], | ||||||
|  |     do: {0, 0, 0, 0}, | ||||||
|  |     else: {0, 0, 0, 0, 0, 0, 0, 0} | ||||||
|  |  | ||||||
|  | config :lokal, Lokal.Repo, | ||||||
|   # ssl: true, |   # ssl: true, | ||||||
|   url: database_url, |   url: database_url, | ||||||
|   pool_size: String.to_integer(System.get_env("POOL_SIZE") || "10"), |   pool_size: String.to_integer(System.get_env("POOL_SIZE") || "10"), | ||||||
|   socket_options: maybe_ipv6 |   socket_options: maybe_ipv6 | ||||||
|  |  | ||||||
|  | config :lokal, LokalWeb.Endpoint, | ||||||
|  |   url: [scheme: "https", host: host, port: 443], | ||||||
|  |   http: [ | ||||||
|  |     # See the documentation on https://hexdocs.pm/plug_cowboy/Plug.Cowboy.html | ||||||
|  |     # for details about using IPv6 vs IPv4 and loopback vs public addresses. | ||||||
|  |     ip: interface, | ||||||
|  |     port: String.to_integer(System.get_env("PORT") || "4000") | ||||||
|  |   ], | ||||||
|  |   server: true, | ||||||
|  |   registration: System.get_env("REGISTRATION") || "invite" | ||||||
|  |  | ||||||
|  | if config_env() == :prod do | ||||||
|   # The secret key base is used to sign/encrypt cookies and other secrets. |   # The secret key base is used to sign/encrypt cookies and other secrets. | ||||||
|   # A default value is used in config/dev.exs and config/test.exs but you |   # A default value is used in config/dev.exs and config/test.exs but you | ||||||
|   # want to use a different value for prod and you most likely don't want |   # want to use a different value for prod and you most likely don't want | ||||||
| @@ -37,20 +67,21 @@ 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("HOST") || "localhost" |   config :lokal, LokalWeb.Endpoint, secret_key_base: secret_key_base | ||||||
|  |  | ||||||
|   config :lokal, LokalWeb.Endpoint, |   # Automatically apply migrations | ||||||
|     ururl: [scheme: "https", host: host, port: 443], |   config :lokal, Lokal.Application, automigrate: true | ||||||
|     http: [ |  | ||||||
|       # Enable IPv6 and bind on all interfaces. |   # Set up SMTP settings | ||||||
|       # Set it to  {0, 0, 0, 0, 0, 0, 0, 1} for local network only access. |   config :lokal, Lokal.Mailer, | ||||||
|       # See the documentation on https://hexdocs.pm/plug_cowboy/Plug.Cowboy.html |     adapter: Swoosh.Adapters.SMTP, | ||||||
|       # for details about using IPv6 vs IPv4 and loopback vs public addresses. |     relay: System.get_env("SMTP_HOST") || raise("No SMTP_HOST set!"), | ||||||
|       ip: {0, 0, 0, 0, 0, 0, 0, 0}, |     port: System.get_env("SMTP_PORT") || 587, | ||||||
|       port: String.to_integer(System.get_env("PORT") || "4000") |     username: System.get_env("SMTP_USERNAME") || raise("No SMTP_USERNAME set!"), | ||||||
|     ], |     password: System.get_env("SMTP_PASSWORD") || raise("No SMTP_PASSWORD set!"), | ||||||
|     secret_key_base: secret_key_base, |     ssl: System.get_env("SMTP_SSL") == "true", | ||||||
|     server: true |     email_from: System.get_env("EMAIL_FROM") || "no-reply@#{System.get_env("HOST")}", | ||||||
|  |     email_name: System.get_env("EMAIL_NAME") || "Lokal" | ||||||
|  |  | ||||||
|   # ## Using releases |   # ## Using releases | ||||||
|   # |   # | ||||||
| @@ -61,22 +92,4 @@ if config_env() == :prod do | |||||||
|   # |   # | ||||||
|   # Then you can assemble a release by calling `mix release`. |   # Then you can assemble a release by calling `mix release`. | ||||||
|   # See `mix help release` for more information. |   # See `mix help release` for more information. | ||||||
|  |  | ||||||
|   # ## Configuring the mailer |  | ||||||
|   # |  | ||||||
|   # In production you need to configure the mailer to use a different adapter. |  | ||||||
|   # Also, you may need to configure the Swoosh API client of your choice if you |  | ||||||
|   # are not using SMTP. Here is an example of the configuration: |  | ||||||
|   # |  | ||||||
|   #     config :lokal, Lokal.Mailer, |  | ||||||
|   #       adapter: Swoosh.Adapters.Mailgun, |  | ||||||
|   #       api_key: System.get_env("MAILGUN_API_KEY"), |  | ||||||
|   #       domain: System.get_env("MAILGUN_DOMAIN") |  | ||||||
|   # |  | ||||||
|   # For this example you need include a HTTP client required by Swoosh API client. |  | ||||||
|   # Swoosh supports Hackney and Finch out of the box: |  | ||||||
|   # |  | ||||||
|   #     config :swoosh, :api_client, Swoosh.ApiClient.Hackney |  | ||||||
|   # |  | ||||||
|   # See https://hexdocs.pm/swoosh/Swoosh.html#module-installation for details. |  | ||||||
| end | end | ||||||
|   | |||||||
| @@ -1,14 +1,14 @@ | |||||||
| import Config | import Config | ||||||
|  |  | ||||||
|  | # Only in tests, remove the complexity from the password hashing algorithm | ||||||
|  | config :bcrypt_elixir, :log_rounds, 1 | ||||||
|  |  | ||||||
| # Configure your database | # Configure your database | ||||||
| # | # | ||||||
| # The MIX_TEST_PARTITION environment variable can be used | # The MIX_TEST_PARTITION environment variable can be used | ||||||
| # to provide built-in test partitioning in CI environment. | # to provide built-in test partitioning in CI environment. | ||||||
| # Run `mix help test` for more information. | # Run `mix help test` for more information. | ||||||
| config :lokal, Lokal.Repo, | config :lokal, Lokal.Repo, | ||||||
|   url: |  | ||||||
|     System.get_env("TEST_DATABASE_URL") || |  | ||||||
|       "ecto://postgres:postgres@localhost/lokal_test#{System.get_env("MIX_TEST_PARTITION")}", |  | ||||||
|   pool: Ecto.Adapters.SQL.Sandbox, |   pool: Ecto.Adapters.SQL.Sandbox, | ||||||
|   pool_size: 10 |   pool_size: 10 | ||||||
|  |  | ||||||
| @@ -16,7 +16,7 @@ config :lokal, Lokal.Repo, | |||||||
| # you can enable the server option below. | # you can enable the server option below. | ||||||
| config :lokal, LokalWeb.Endpoint, | config :lokal, LokalWeb.Endpoint, | ||||||
|   http: [ip: {0, 0, 0, 0}, port: 4002], |   http: [ip: {0, 0, 0, 0}, port: 4002], | ||||||
|   secret_key_base: "T4DkRImgeMNCcPcTWBCZyKYp3KQ8yyPD33VT4wj6ogbP8fIGUsqmOTNX3clTMrLo", |   secret_key_base: "S3qq9QtUdsFtlYej+HTjAVN95uP5i5tf2sPYINWSQfCKJghFj2B1+wTAoljZyHOK", | ||||||
|   server: false |   server: false | ||||||
|  |  | ||||||
| # In test we don't send emails. | # In test we don't send emails. | ||||||
| @@ -27,3 +27,6 @@ config :logger, level: :warn | |||||||
|  |  | ||||||
| # Initialize plugs at runtime for faster test compilation | # Initialize plugs at runtime for faster test compilation | ||||||
| config :phoenix, :plug_init_mode, :runtime | config :phoenix, :plug_init_mode, :runtime | ||||||
|  |  | ||||||
|  | # Disable Oban | ||||||
|  | config :lokal, Oban, queues: false, plugins: false | ||||||
|   | |||||||
							
								
								
									
										124
									
								
								contributing.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								contributing.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,124 @@ | |||||||
|  | # Contribution Guide | ||||||
|  |  | ||||||
|  | ## Style Tips | ||||||
|  |  | ||||||
|  | - In order to keep code concise and improve readability, please try to make your | ||||||
|  |   functions as short as possible while keeping variable names descriptive! For | ||||||
|  |   instance, use inline `do:` blocks for short functions and make your aliases as | ||||||
|  |   short as possible without introducing ambiguity. | ||||||
|  |   - I.e. since there's only one `Changeset` in the app, please alias | ||||||
|  |     `Changeset.t(Type.t())` instead of using `Ecto.Changeset.t(Long.Type.t())` | ||||||
|  | - Use pipelines when possible. If only calling a single method, a pipeline isn't | ||||||
|  |   strictly necessary but still encouraged for future modification. | ||||||
|  | - Please add typespecs to your functions! Even your private functions may be | ||||||
|  |   used by others later down the line, and typespecs will be able to help | ||||||
|  |   document your code just a little bit better, and improve the debugging | ||||||
|  |   process. | ||||||
|  |   - Typespec arguments can be named like `@spec function(arg_name :: type()) :: | ||||||
|  |     return_type()`. Please use these for generic types, such as `map()` when the | ||||||
|  |     input data isn't immediately obvious. | ||||||
|  |   - Please define all typespecs for a function together in one place, instead of | ||||||
|  |     each function header. | ||||||
|  | - When making new models, please take inspiration from the existing models in | ||||||
|  |   regards to layout of sections, typespec design, and formatting. | ||||||
|  | - With Elixir convention, for methods that raise on error please name them like | ||||||
|  |   `function_that_raises!()`, and functions that return a boolean like | ||||||
|  |   `function_that_returns_boolean?()`. For other methods, it's encouraged to use | ||||||
|  |   status tuples for other functions like `{:ok, result}` or `{:error, | ||||||
|  |   reason_or_changeset}` instead of just returning `result` or `nil` for easy | ||||||
|  |   pattern matching. | ||||||
|  | - Instead of using the `.` operator, try to use pattern matching instead, | ||||||
|  |   especially for function headers. `.` in templates is fine to keep things | ||||||
|  |   concise. | ||||||
|  | - Use `Enum` functions over comprehensions whenever possible for clarity. | ||||||
|  |   However, comprehensions in templates are fine for legibility. | ||||||
|  | - When adding text, please use `gettext` macros to enable things to be | ||||||
|  |   translated in the future. After adding `gettext` macros, run `mix format` in | ||||||
|  |   order to add your new text strings to the files in `priv/gettext`. | ||||||
|  |   - Existing domains: `"default"` (for anything general), `"prompts"` | ||||||
|  |     (informational messages as a result of the user doing an action, i.e. in | ||||||
|  |     flashes), `"actions"` (actions that the user can take), `"emails"`, and | ||||||
|  |     `"errors"`. Using these domains accurately will let translators know which | ||||||
|  |     translations are higher and lower priority. Thank you! | ||||||
|  | - Before submitting a PR, please make sure all tests are passing using `mix | ||||||
|  |   test`. | ||||||
|  |  | ||||||
|  | # Technical Information | ||||||
|  |  | ||||||
|  | - Created using the [Phoenix Framework](https://www.phoenixframework.org) | ||||||
|  | - User Registration/Sign in via | ||||||
|  |   [`phx_gen_auth`](https://hexdocs.pm/phx_gen_auth/). | ||||||
|  | - `Dockerfile` and example `docker-compose.yml` | ||||||
|  | - Automatic migrations in `MIX_ENV=prod` or Docker image | ||||||
|  | - JS linting with [standard.js](https://standardjs.com), HEEx linting with | ||||||
|  |   [heex_formatter](https://github.com/feliperenan/heex_formatter) | ||||||
|  |  | ||||||
|  | ## Docs | ||||||
|  |  | ||||||
|  | More information can be found in the documentation generated by `mix docs`. | ||||||
|  | These are located in the `/docs` folder, and are generated in HTML and ePub. | ||||||
|  | Check them out! | ||||||
|  |  | ||||||
|  | # Instructions | ||||||
|  |  | ||||||
|  | 1. Clone the repo | ||||||
|  | 1. Install the elixir and erlang binaries. I recommend using [asdf version | ||||||
|  |    manager](https://asdf-vm.com/guide/getting-started.html#_1-install-dependencies), | ||||||
|  |    which will use the `.tool-versions` file to install the correct versions of | ||||||
|  |    Erlang, Elixir and npm for this project! | ||||||
|  | 1. Run `mix deps.get` and `mix compile` to fetch all dependencies | ||||||
|  | 1. Run `mix setup` to initialize your database. You can reset your database at | ||||||
|  |    any time with `mix ecto.reset`. | ||||||
|  | 1. Run migrations with `mix ecto.migrate` or rollback with `mix ecto.rollback`. | ||||||
|  | 1. Run `mix phx.server` to start the development server. | ||||||
|  |  | ||||||
|  | # Configuration | ||||||
|  |  | ||||||
|  | For development, I recommend setting environment variables with | ||||||
|  | [direnv](https://direnv.net). | ||||||
|  |  | ||||||
|  | By default, Lokal will always bind to all external IPv4 and IPv6 addresses in | ||||||
|  | `dev` and `prod` mode, respectively. If you would like to use different values, | ||||||
|  | they will need to be overridden in `config/dev.exs` and `config/runtime.exs` for | ||||||
|  | `dev` and `prod` modes, respectively. | ||||||
|  |  | ||||||
|  | ## `MIX_ENV=dev` | ||||||
|  |  | ||||||
|  | In `dev` mode, Lokal will listen for these environment variables at runtime. | ||||||
|  |  | ||||||
|  | - `HOST`: External url to generate links with. Set this especially if you're | ||||||
|  |   behind a reverse proxy. Defaults to `localhost`. External URLs will always be | ||||||
|  |   generated with `https://` and port `443`. | ||||||
|  | - `PORT`: Internal port to bind to. Defaults to `4000`. | ||||||
|  | - `DATABASE_URL`: Controls the database url to connect to. Defaults to | ||||||
|  |   `ecto://postgres:postgres@localhost/lokal_dev`. | ||||||
|  | - `ECTO_IPV6`: Controls if Ecto should use IPv6 to connect to PostgreSQL. | ||||||
|  |   Defaults to `false`. | ||||||
|  | - `POOL_SIZE`: Controls the pool size to use with PostgreSQL. Defaults to `10`. | ||||||
|  | - `REGISTRATION`: Controls if user sign-up should be invite only or set to public. Set to `public` to enable public registration. Defaults to `invite`. | ||||||
|  | - `LOCALE`: Sets a custom locale. Defaults to `en_US`. | ||||||
|  |  | ||||||
|  | ## `MIX_ENV=test` | ||||||
|  |  | ||||||
|  | In `test` mode (or in the Docker container), Lokal will listen for the same environment variables as dev mode, but also include the following at runtime: | ||||||
|  |  | ||||||
|  | - `TEST_DATABASE_URL`: REPLACES `DATABASE_URL`. Controls the database url to | ||||||
|  |   connect to. Defaults to `ecto://postgres:postgres@localhost/lokal_test`. | ||||||
|  | - `MIX_TEST_PARTITION`: Only used if `TEST_DATABASE_URL` is not specified. | ||||||
|  |   Appended to the default database url if you would like to partition your test | ||||||
|  |   databases. Defaults to not set. | ||||||
|  |  | ||||||
|  | ## `MIX_ENV=prod` | ||||||
|  |  | ||||||
|  | In `prod` mode (or in the Docker container), Lokal will listen for the same environment variables as dev mode, but also include the following at runtime: | ||||||
|  |  | ||||||
|  | - `SECRET_KEY_BASE`: Secret key base used to sign cookies. Must be generated | ||||||
|  |   with `docker run -it shibaobun/lokal mix phx.gen.secret` and set for server to start. | ||||||
|  | - `SMTP_HOST`: The url for your SMTP email provider. Must be set | ||||||
|  | - `SMTP_PORT`: The port for your SMTP relay. Defaults to `587`. | ||||||
|  | - `SMTP_USERNAME`: The username for your SMTP relay. Must be set! | ||||||
|  | - `SMTP_PASSWORD`: The password for your SMTP relay. Must be set! | ||||||
|  | - `SMTP_SSL`: Set to `true` to enable SSL for emails. Defaults to `false`. | ||||||
|  | - `EMAIL_FROM`: Sets the sender email in sent emails. Defaults to | ||||||
|  |   `no-reply@HOST` where `HOST` was previously defined. | ||||||
|  | - `EMAIL_NAME`: Sets the sender name in sent emails. Defaults to "Lokal". | ||||||
							
								
								
									
										48
									
								
								lib/lokal/accounts/email.ex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								lib/lokal/accounts/email.ex
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,48 @@ | |||||||
|  | defmodule Lokal.Email do | ||||||
|  |   @moduledoc """ | ||||||
|  |   Emails that can be sent using Swoosh. | ||||||
|  |  | ||||||
|  |   You can find the base email templates at | ||||||
|  |   `lib/Lokal_web/templates/layout/email.html.heex` for html emails and | ||||||
|  |   `lib/Lokal_web/templates/layout/email.txt.heex` for text emails. | ||||||
|  |   """ | ||||||
|  |  | ||||||
|  |   use Phoenix.Swoosh, view: LokalWeb.EmailView, layout: {LokalWeb.LayoutView, :email} | ||||||
|  |   import LokalWeb.Gettext | ||||||
|  |   alias Lokal.Accounts.User | ||||||
|  |   alias LokalWeb.EmailView | ||||||
|  |  | ||||||
|  |   @typedoc """ | ||||||
|  |   Represents an HTML and text body email that can be sent | ||||||
|  |   """ | ||||||
|  |   @type t() :: Swoosh.Email.t() | ||||||
|  |  | ||||||
|  |   @spec base_email(User.t(), String.t()) :: t() | ||||||
|  |   defp base_email(%User{email: email}, subject) do | ||||||
|  |     from = Application.get_env(:Lokal, Lokal.Mailer)[:email_from] || "noreply@localhost" | ||||||
|  |     name = Application.get_env(:Lokal, Lokal.Mailer)[:email_name] | ||||||
|  |     new() |> to(email) |> from({name, from}) |> subject(subject) | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   @spec generate_email(key :: String.t(), User.t(), attrs :: map()) :: t() | ||||||
|  |   def generate_email("welcome", user, %{"url" => url}) do | ||||||
|  |     user | ||||||
|  |     |> base_email(dgettext("emails", "Confirm your %{name} account", name: "Lokal")) | ||||||
|  |     |> render_body("confirm_email.html", %{user: user, url: url}) | ||||||
|  |     |> text_body(EmailView.render("confirm_email.txt", %{user: user, url: url})) | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   def generate_email("reset_password", user, %{"url" => url}) do | ||||||
|  |     user | ||||||
|  |     |> base_email(dgettext("emails", "Reset your %{name} password", name: "Lokal")) | ||||||
|  |     |> render_body("reset_password.html", %{user: user, url: url}) | ||||||
|  |     |> text_body(EmailView.render("reset_password.txt", %{user: user, url: url})) | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   def generate_email("update_email", user, %{"url" => url}) do | ||||||
|  |     user | ||||||
|  |     |> base_email(dgettext("emails", "Update your %{name} email", name: "Lokal")) | ||||||
|  |     |> render_body("update_email.html", %{user: user, url: url}) | ||||||
|  |     |> text_body(EmailView.render("update_email.txt", %{user: user, url: url})) | ||||||
|  |   end | ||||||
|  | end | ||||||
							
								
								
									
										13
									
								
								lib/lokal/accounts/email_worker.ex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								lib/lokal/accounts/email_worker.ex
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | |||||||
|  | defmodule Lokal.EmailWorker do | ||||||
|  |   @moduledoc """ | ||||||
|  |   Oban worker that dispatches emails | ||||||
|  |   """ | ||||||
|  |  | ||||||
|  |   use Oban.Worker, queue: :mailers, tags: ["email"] | ||||||
|  |   alias Lokal.{Accounts, Email, Mailer} | ||||||
|  |  | ||||||
|  |   @impl Oban.Worker | ||||||
|  |   def perform(%Oban.Job{args: %{"email" => email, "user_id" => user_id, "attrs" => attrs}}) do | ||||||
|  |     Email.generate_email(email, user_id |> Accounts.get_user!(), attrs) |> Mailer.deliver() | ||||||
|  |   end | ||||||
|  | end | ||||||
| @@ -1,7 +1,42 @@ | |||||||
| defmodule Lokal.Mailer do | defmodule Lokal.Mailer do | ||||||
|   @moduledoc """ |   @moduledoc """ | ||||||
|   Mailer, currently uses Swoosh |   Mailer adapter for emails | ||||||
|  |  | ||||||
|  |   Since emails are loaded as Oban jobs, the `:attrs` map must be serializable to | ||||||
|  |   json with Jason, which restricts the use of structs. | ||||||
|   """ |   """ | ||||||
|  |  | ||||||
|   use Swoosh.Mailer, otp_app: :lokal |   use Swoosh.Mailer, otp_app: :lokal | ||||||
|  |   alias Lokal.{Accounts.User, EmailWorker} | ||||||
|  |   alias Oban.Job | ||||||
|  |  | ||||||
|  |   @doc """ | ||||||
|  |   Deliver instructions to confirm account. | ||||||
|  |   """ | ||||||
|  |   @spec deliver_confirmation_instructions(User.t(), String.t()) :: Job.t() | ||||||
|  |   def deliver_confirmation_instructions(%User{id: user_id}, url) do | ||||||
|  |     %{email: :welcome, user_id: user_id, attrs: %{url: url}} | ||||||
|  |     |> EmailWorker.new() | ||||||
|  |     |> Oban.insert!() | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   @doc """ | ||||||
|  |   Deliver instructions to reset a user password. | ||||||
|  |   """ | ||||||
|  |   @spec deliver_reset_password_instructions(User.t(), String.t()) :: Job.t() | ||||||
|  |   def deliver_reset_password_instructions(%User{id: user_id}, url) do | ||||||
|  |     %{email: :reset_password, user_id: user_id, attrs: %{url: url}} | ||||||
|  |     |> EmailWorker.new() | ||||||
|  |     |> Oban.insert!() | ||||||
|  |   end | ||||||
|  |  | ||||||
|  |   @doc """ | ||||||
|  |   Deliver instructions to update a user email. | ||||||
|  |   """ | ||||||
|  |   @spec deliver_update_email_instructions(User.t(), String.t()) :: Job.t() | ||||||
|  |   def deliver_update_email_instructions(%User{id: user_id}, url) do | ||||||
|  |     %{email: :update_email, user_id: user_id, attrs: %{url: url}} | ||||||
|  |     |> EmailWorker.new() | ||||||
|  |     |> Oban.insert!() | ||||||
|  |   end | ||||||
| end | end | ||||||
|   | |||||||
							
								
								
									
										25
									
								
								lib/lokal_web/templates/email/confirm_email.html.eex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								lib/lokal_web/templates/email/confirm_email.html.eex
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | |||||||
|  | <div style="display: flex; flex-direction: column; justify-content: center; align-items: center;"> | ||||||
|  |   <span style="margin-bottom: 0.75em; font-size: 1.5em;"> | ||||||
|  |     <%= dgettext("emails", "Hi %{email},", email: @user.email) %> | ||||||
|  |   </span> | ||||||
|  |  | ||||||
|  |   <br/> | ||||||
|  |  | ||||||
|  |   <span style="margin-bottom: 1em; font-size: 1.25em;"> | ||||||
|  |     <%= dgettext("emails", "Welcome to %{name}!", name: "Lokal") %> | ||||||
|  |   </span> | ||||||
|  |  | ||||||
|  |   <br/> | ||||||
|  |  | ||||||
|  |   <%= dgettext("emails", "You can confirm your account by visiting the URL below:") %> | ||||||
|  |  | ||||||
|  |   <br/> | ||||||
|  |  | ||||||
|  |   <a style="margin: 1em; color: rgb(31, 31, 31);" href="<%= @url %>"><%= @url %></a> | ||||||
|  |  | ||||||
|  |   <br/> | ||||||
|  |  | ||||||
|  |   <%= dgettext("emails", | ||||||
|  |     "If you didn't create an account at %{name}, please ignore this.", | ||||||
|  |     name: "Lokal") %> | ||||||
|  | </div> | ||||||
							
								
								
									
										12
									
								
								lib/lokal_web/templates/email/confirm_email.txt.eex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								lib/lokal_web/templates/email/confirm_email.txt.eex
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | |||||||
|  |  | ||||||
|  | <%= dgettext("emails", "Hi %{email},", email: @user.email) %> | ||||||
|  |  | ||||||
|  | <%= dgettext("emails", "Welcome to %{name}%!", name: "Lokal") %> | ||||||
|  |  | ||||||
|  | <%= dgettext("emails", "You can confirm your account by visiting the URL below:") %> | ||||||
|  |  | ||||||
|  | <%= @url %> | ||||||
|  |  | ||||||
|  | <%= dgettext("emails", | ||||||
|  |   "If you didn't create an account at %{url}, please ignore this.", | ||||||
|  |   url: Routes.live_url(Endpoint, PageLive)) %> | ||||||
							
								
								
									
										19
									
								
								lib/lokal_web/templates/email/reset_password.html.eex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								lib/lokal_web/templates/email/reset_password.html.eex
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | |||||||
|  | <div style="display: flex; flex-direction: column; justify-content: center; align-items: center;"> | ||||||
|  |   <span style="margin-bottom: 0.5em; font-size: 1.5em;"> | ||||||
|  |     <%= dgettext("emails", "Hi %{email},", email: @user.email) %> | ||||||
|  |   </span> | ||||||
|  |  | ||||||
|  |   <br/> | ||||||
|  |  | ||||||
|  |   <%= dgettext("emails", "You can reset your password by visiting the URL below:") %> | ||||||
|  |  | ||||||
|  |   <br/> | ||||||
|  |  | ||||||
|  |   <a style="margin: 1em; color: rgb(31, 31, 31);" href="<%= @url %>"><%= @url %></a> | ||||||
|  |  | ||||||
|  |   <br/> | ||||||
|  |  | ||||||
|  |   <%= dgettext("emails", | ||||||
|  |     "If you didn't request this change from %{name}, please ignore this.", | ||||||
|  |     name: "Lokal") %> | ||||||
|  | </div> | ||||||
							
								
								
									
										10
									
								
								lib/lokal_web/templates/email/reset_password.txt.eex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								lib/lokal_web/templates/email/reset_password.txt.eex
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | |||||||
|  |  | ||||||
|  | <%= dgettext("emails", "Hi %{email},", email: @user.email) %> | ||||||
|  |  | ||||||
|  | <%= dgettext("emails", "You can reset your password by visiting the URL below:") %> | ||||||
|  |  | ||||||
|  | <%= @url %> | ||||||
|  |  | ||||||
|  | <%= dgettext("emails", | ||||||
|  |   "If you didn't request this change from %{url}, please ignore this.", | ||||||
|  |   url: Routes.live_url(Endpoint, PageLive)) %> | ||||||
							
								
								
									
										19
									
								
								lib/lokal_web/templates/email/update_email.html.eex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								lib/lokal_web/templates/email/update_email.html.eex
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | |||||||
|  | <div style="display: flex; flex-direction: column; justify-content: center; align-items: center;"> | ||||||
|  |   <span style="margin-bottom: 0.5em; font-size: 1.5em;"> | ||||||
|  |     <%= dgettext("emails", "Hi %{email},", email: @user.email) %> | ||||||
|  |   </span> | ||||||
|  |  | ||||||
|  |   <br/> | ||||||
|  |  | ||||||
|  |   <%= dgettext("emails", "You can change your email by visiting the URL below:") %> | ||||||
|  |  | ||||||
|  |   <br/> | ||||||
|  |  | ||||||
|  |   <a style="margin: 1em; color: rgb(31, 31, 31);" href="<%= @url %>"><%= @url %></a> | ||||||
|  |  | ||||||
|  |   <br/> | ||||||
|  |  | ||||||
|  |   <%= dgettext("emails", | ||||||
|  |     "If you didn't request this change from %{name}, please ignore this.", | ||||||
|  |     name: "Lokal") %> | ||||||
|  | </div> | ||||||
							
								
								
									
										10
									
								
								lib/lokal_web/templates/email/update_email.txt.eex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								lib/lokal_web/templates/email/update_email.txt.eex
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | |||||||
|  |  | ||||||
|  | <%= dgettext("emails", "Hi %{email},", email: @user.email) %> | ||||||
|  |  | ||||||
|  | <%= dgettext("emails", "You can change your email by visiting the URL below:") %> | ||||||
|  |  | ||||||
|  | <%= @url %> | ||||||
|  |  | ||||||
|  | <%= dgettext("emails", | ||||||
|  |   "If you didn't request this change from %{url}, please ignore this.", | ||||||
|  |   url: Routes.live_url(Endpoint, PageLive)) %> | ||||||
							
								
								
									
										24
									
								
								lib/lokal_web/templates/layout/email.html.heex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								lib/lokal_web/templates/layout/email.html.heex
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | |||||||
|  | <html> | ||||||
|  |   <head> | ||||||
|  |     <title> | ||||||
|  |       <%= @email.subject %> | ||||||
|  |     </title> | ||||||
|  |   </head> | ||||||
|  |   <body | ||||||
|  |     style="padding: 2em; color: rgb(31, 31, 31); background-color: rgb(220, 220, 228); font-family: 'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans Unicode', Geneva, Verdana, sans-serif; text-align: center;" | ||||||
|  |   > | ||||||
|  |     <%= @inner_content %> | ||||||
|  |  | ||||||
|  |     <hr | ||||||
|  |       style="margin: 2em auto; border-width: 1px; border-color: rgb(212, 212, 216); width: 100%; max-width: 42rem;" | ||||||
|  |     /> | ||||||
|  |  | ||||||
|  |     <a style="color: rgb(31, 31, 31);" href={Routes.live_url(Endpoint, PageLive)}> | ||||||
|  |       <%= dgettext( | ||||||
|  |         "emails", | ||||||
|  |         "This email was sent from %{name}, the self-hosted firearm tracker website.", | ||||||
|  |         name: "Lokal" | ||||||
|  |       ) %> | ||||||
|  |     </a> | ||||||
|  |   </body> | ||||||
|  | </html> | ||||||
							
								
								
									
										12
									
								
								lib/lokal_web/templates/layout/email.txt.eex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								lib/lokal_web/templates/layout/email.txt.eex
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | |||||||
|  | <%= @email.subject %> | ||||||
|  |  | ||||||
|  | ==================== | ||||||
|  |  | ||||||
|  | <%= @inner_content %> | ||||||
|  |  | ||||||
|  | ===================== | ||||||
|  |  | ||||||
|  | <%= dgettext("emails", | ||||||
|  |   "This email was sent from %{name} at %{url}, the self-hosted firearm tracker website.", | ||||||
|  |   name: "Lokal", | ||||||
|  |   url: Routes.live_url(Endpoint, PageLive)) %> | ||||||
							
								
								
									
										1
									
								
								lib/lokal_web/templates/layout/empty.html.heex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								lib/lokal_web/templates/layout/empty.html.heex
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | <%= @inner_content %> | ||||||
							
								
								
									
										8
									
								
								lib/lokal_web/views/email_view.ex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								lib/lokal_web/views/email_view.ex
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | |||||||
|  | defmodule LokalWeb.EmailView do | ||||||
|  |   @moduledoc """ | ||||||
|  |   A view for email-related helper functions | ||||||
|  |   """ | ||||||
|  |   alias LokalWeb.{Endpoint, PageLive} | ||||||
|  |  | ||||||
|  |   use LokalWeb, :view | ||||||
|  | end | ||||||
							
								
								
									
										1
									
								
								mix.exs
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								mix.exs
									
									
									
									
									
								
							| @@ -60,6 +60,7 @@ defmodule Lokal.MixProject do | |||||||
|       {:ex_doc, "~> 0.27", only: :dev, runtime: false}, |       {:ex_doc, "~> 0.27", only: :dev, runtime: false}, | ||||||
|       {:swoosh, "~> 1.6"}, |       {:swoosh, "~> 1.6"}, | ||||||
|       {:gen_smtp, "~> 1.0"}, |       {:gen_smtp, "~> 1.0"}, | ||||||
|  |       {:phoenix_swoosh, "~> 1.0"}, | ||||||
|       {:telemetry_metrics, "~> 0.6"}, |       {:telemetry_metrics, "~> 0.6"}, | ||||||
|       {:telemetry_poller, "~> 1.0"}, |       {:telemetry_poller, "~> 1.0"}, | ||||||
|       {:gettext, "~> 0.18"}, |       {:gettext, "~> 0.18"}, | ||||||
|   | |||||||
							
								
								
									
										1
									
								
								mix.lock
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								mix.lock
									
									
									
									
									
								
							| @@ -40,6 +40,7 @@ | |||||||
|   "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.3.3", "3a53772a6118d5679bf50fc1670505a290e32a1d195df9e069d8c53ab040c054", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "766796676e5f558dbae5d1bdb066849673e956005e3730dfd5affd7a6da4abac"}, |   "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.3.3", "3a53772a6118d5679bf50fc1670505a290e32a1d195df9e069d8c53ab040c054", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "766796676e5f558dbae5d1bdb066849673e956005e3730dfd5affd7a6da4abac"}, | ||||||
|   "phoenix_live_view": {:hex, :phoenix_live_view, "0.17.7", "05a42377075868a678d446361effba80cefef19ab98941c01a7a4c7560b29121", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.5.9 or ~> 1.6.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.1", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "25eaf41028eb351b90d4f69671874643a09944098fefd0d01d442f40a6091b6f"}, |   "phoenix_live_view": {:hex, :phoenix_live_view, "0.17.7", "05a42377075868a678d446361effba80cefef19ab98941c01a7a4c7560b29121", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.5.9 or ~> 1.6.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.1", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "25eaf41028eb351b90d4f69671874643a09944098fefd0d01d442f40a6091b6f"}, | ||||||
|   "phoenix_pubsub": {:hex, :phoenix_pubsub, "2.0.0", "a1ae76717bb168cdeb10ec9d92d1480fec99e3080f011402c0a2d68d47395ffb", [:mix], [], "hexpm", "c52d948c4f261577b9c6fa804be91884b381a7f8f18450c5045975435350f771"}, |   "phoenix_pubsub": {:hex, :phoenix_pubsub, "2.0.0", "a1ae76717bb168cdeb10ec9d92d1480fec99e3080f011402c0a2d68d47395ffb", [:mix], [], "hexpm", "c52d948c4f261577b9c6fa804be91884b381a7f8f18450c5045975435350f771"}, | ||||||
|  |   "phoenix_swoosh": {:hex, :phoenix_swoosh, "1.0.1", "0db6eb6405a6b06cae4fdf4144659b3f4fee4553e2856fe8a53ba12e9fb21a74", [:mix], [{:finch, "~> 0.8", [hex: :finch, repo: "hexpm", optional: true]}, {:hackney, "~> 1.10", [hex: :hackney, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6", [hex: :phoenix, repo: "hexpm", optional: true]}, {:phoenix_html, "~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_view, "~> 1.0", [hex: :phoenix_view, repo: "hexpm", optional: false]}, {:swoosh, "~> 1.5", [hex: :swoosh, repo: "hexpm", optional: false]}], "hexpm", "e34890004baec08f0fa12bd8c77bf64bfb4156b84a07fb79da9322fa94bc3781"}, | ||||||
|   "phoenix_view": {:hex, :phoenix_view, "1.1.2", "1b82764a065fb41051637872c7bd07ed2fdb6f5c3bd89684d4dca6e10115c95a", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "7ae90ad27b09091266f6adbb61e1d2516a7c3d7062c6789d46a7554ec40f3a56"}, |   "phoenix_view": {:hex, :phoenix_view, "1.1.2", "1b82764a065fb41051637872c7bd07ed2fdb6f5c3bd89684d4dca6e10115c95a", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "7ae90ad27b09091266f6adbb61e1d2516a7c3d7062c6789d46a7554ec40f3a56"}, | ||||||
|   "plug": {:hex, :plug, "1.13.3", "93b299039c21a8b82cc904d13812bce4ced45cf69153e8d35ca16ffb3e8c5d98", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "98c8003e4faf7b74a9ac41bee99e328b08f069bf932747d4a7532e97ae837a17"}, |   "plug": {:hex, :plug, "1.13.3", "93b299039c21a8b82cc904d13812bce4ced45cf69153e8d35ca16ffb3e8c5d98", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "98c8003e4faf7b74a9ac41bee99e328b08f069bf932747d4a7532e97ae837a17"}, | ||||||
|   "plug_cowboy": {:hex, :plug_cowboy, "2.5.2", "62894ccd601cf9597e2c23911ff12798a8a18d237e9739f58a6b04e4988899fe", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "ea6e87f774c8608d60c8d34022a7d073bd7680a0a013f049fc62bf35efea1044"}, |   "plug_cowboy": {:hex, :plug_cowboy, "2.5.2", "62894ccd601cf9597e2c23911ff12798a8a18d237e9739f58a6b04e4988899fe", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "ea6e87f774c8608d60c8d34022a7d073bd7680a0a013f049fc62bf35efea1044"}, | ||||||
|   | |||||||
							
								
								
									
										52
									
								
								readme.md
									
									
									
									
									
								
							
							
						
						
									
										52
									
								
								readme.md
									
									
									
									
									
								
							| @@ -14,40 +14,42 @@ shopping today! | |||||||
|  |  | ||||||
| # Installation | # Installation | ||||||
|  |  | ||||||
| 1. Clone the repo | 1. Install [Docker Compose](https://docs.docker.com/compose/install/) or alternatively [Docker Desktop](https://docs.docker.com/desktop/) on your machine. | ||||||
| 2. Run `mix setup` | 1. Copy the example [docker-compose.yml](https://gitea.bubbletea.dev/shibao/lokal/src/branch/stable/docker-compose.yml). into your local machine where you want. | ||||||
| 3. Run `mix phx.server` to start the development server |    Bind mounts are created in the same directory by default. | ||||||
|  | 1. Set the configuration variables in `docker-compose.yml`. You'll need to run | ||||||
|  |    `docker run -it shibaobun/lokal /app/priv/random.sh` to generate a new | ||||||
|  |    secret key base. | ||||||
|  | 1. Use `docker-compose up` or `docker-compose up -d` to start the container! | ||||||
|  |  | ||||||
|  | The first created user will be created as an admin. | ||||||
|  |  | ||||||
| # Configuration | # Configuration | ||||||
|  |  | ||||||
| For development, I recommend setting environment variables with | You can use the following environment variables to configure Lokal in | ||||||
| [direnv](https://direnv.net). | [docker-compose.yml](https://gitea.bubbletea.dev/shibao/lokal/src/branch/stable/docker-compose.yml). | ||||||
|  |  | ||||||
| ## `MIX_ENV=dev` | - `HOST`: External url to generate links with. Must be set with your hosted | ||||||
|  |   domain name! I.e. `lokal.mywebsite.tld` | ||||||
| In `dev` mode, Lokal will listen for these environment variables on compile. | - `PORT`: Internal port to bind to. Defaults to `4000`. Must be reverse proxied! | ||||||
|  |  | ||||||
| - `HOST`: External url to generate links with. Set these especially if you're |  | ||||||
|   behind a reverse proxy. Defaults to `localhost`. |  | ||||||
| - `PORT`: External port for urls. Defaults to `443`. |  | ||||||
| - `DATABASE_URL`: Controls the database url to connect to. Defaults to |  | ||||||
|   `ecto://postgres:postgres@localhost/lokal_dev`. |  | ||||||
|  |  | ||||||
| ## `MIX_ENV=prod` |  | ||||||
|  |  | ||||||
| In `prod` mode (or in the Docker container), Lokal will listen for these environment variables at runtime. |  | ||||||
|  |  | ||||||
| - `HOST`: External url to generate links with. Set these especially if you're |  | ||||||
|   behind a reverse proxy. Defaults to `localhost`. |  | ||||||
| - `PORT`: Internal port to bind to. Defaults to `4000` and attempts to bind to |  | ||||||
|   `0.0.0.0`. Must be reverse proxied! |  | ||||||
| - `DATABASE_URL`: Controls the database url to connect to. Defaults to | - `DATABASE_URL`: Controls the database url to connect to. Defaults to | ||||||
|   `ecto://postgres:postgres@lokal-db/lokal`. |   `ecto://postgres:postgres@lokal-db/lokal`. | ||||||
| - `ECTO_IPV6`: Controls if Ecto should use ipv6 to connect to PostgreSQL. | - `ECTO_IPV6`: If set to `true`, Ecto should use ipv6 to connect to PostgreSQL. | ||||||
|   Defaults to `false`. |   Defaults to `false`. | ||||||
| - `POOL_SIZE`: Controls the pool size to use with PostgreSQL. Defaults to `10`. | - `POOL_SIZE`: Controls the pool size to use with PostgreSQL. Defaults to `10`. | ||||||
| - `SECRET_KEY_BASE`: Secret key base used to sign cookies. Must be generated | - `SECRET_KEY_BASE`: Secret key base used to sign cookies. Must be generated | ||||||
|   with `mix phx.gen.secret` and set for server to start. |   with `docker run -it shibaobun/lokal mix phx.gen.secret` and set for server to start. | ||||||
|  | - `REGISTRATION`: Controls if user sign-up should be invite only or set to | ||||||
|  |   public. Set to `public` to enable public registration. Defaults to `invite`. | ||||||
|  | - `LOCALE`: Sets a custom locale. Defaults to `en_US`. | ||||||
|  | - `SMTP_HOST`: The url for your SMTP email provider. Must be set | ||||||
|  | - `SMTP_PORT`: The port for your SMTP relay. Defaults to `587`. | ||||||
|  | - `SMTP_USERNAME`: The username for your SMTP relay. Must be set! | ||||||
|  | - `SMTP_PASSWORD`: The password for your SMTP relay. Must be set! | ||||||
|  | - `SMTP_SSL`: Set to `true` to enable SSL for emails. Defaults to `false`. | ||||||
|  | - `EMAIL_FROM`: Sets the sender email in sent emails. Defaults to | ||||||
|  |   `no-reply@HOST` where `HOST` was previously defined. | ||||||
|  | - `EMAIL_NAME`: Sets the sender name in sent emails. Defaults to "Lokal". | ||||||
|  |  | ||||||
| --- | --- | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user