defmodule Mix.Tasks.Wac.Install do
@moduledoc """
Generates schemas, migrations, and contexts for users with WebauthnComponents as the primary authentication mechanism.
## Options
By default, contexts, schemas, tests, and web modules are generated or modified by `wac.install`. You may opt out of one or more generators with the following options:
- `--no-contexts`: Do not generate context modules.
- `--no-schemas`: Do not generate schema & migration modules.
- `--no-tests`: Do not generate test modules & scripts.
- `--no-web`: Do not generate the authentication LiveView, the session controller, or session hooks.
- `--no-router`: Do not modify the router.
## Templates
Within the [`webauthn_components`](https://github.com/liveshowy/webauthn_components) repo, the following templates are used by `wac.install` to scaffold the modules needed to support Passkeys in a LiveView application:
#{for dir <- File.ls!("templates") |> Enum.sort(),
file <- Path.join(["templates", dir]) |> File.ls!() |> Enum.sort() do
"\n- `templates/#{dir}/#{file}`"
end}
"""
use Mix.Task
alias Wac.Gen.Contexts
alias Wac.Gen.Controllers
alias Wac.Gen.Schemas
alias Wac.Gen.LiveViews
alias Wac.Gen.SessionHooks
alias Wac.Gen.Migrations
alias Wac.Gen.Router
alias Wac.Gen.Tests
alias Wac.Gen.Fixtures
alias Wac.Gen.Javascript
alias Wac.Gen.Components
alias Wac.Gen.AppHtml
alias Wac.Gen.Misc
@version Mix.Project.config()[:version]
@shortdoc "Generates a user schema."
@mix_task "wac.install"
@switches [
contexts: :boolean,
schemas: :boolean,
tests: :boolean,
web: :boolean,
router: :boolean
]
@default_opts [
contexts: true,
schemas: true,
tests: true,
web: true,
router: true
]
@requirements ["app.start"]
@impl Mix.Task
def run([version]) when version in ~w(-v --version) do
Mix.shell().info("WebauthnComponents Schemas generator v#{@version}")
end
def run([version]) when version in ~w(-h --help) do
Mix.Tasks.Help.run([@mix_task])
end
def run(args) do
if Mix.Project.umbrella?() do
Mix.shell().error("Sorry, umbrella projects are currently unsupported.")
end
case OptionParser.parse(args, strict: @switches) do
{flags, _args, []} ->
opts = Keyword.merge(@default_opts, flags)
dirname = File.cwd!() |> Path.basename()
dirname_camelized = Macro.camelize(dirname)
web_dirname = dirname <> "_web"
web_dirname_camelized = Macro.camelize(web_dirname)
assigns = [
app_snake_case: dirname,
app_pascal_case: Module.concat([dirname_camelized]),
web_snake_case: web_dirname,
web_pascal_case: Module.concat([web_dirname_camelized])
]
if opts[:contexts] do
Contexts.copy_templates(assigns)
Misc.copy_templates(assigns)
end
if opts[:schemas] do
Schemas.copy_templates(assigns)
Migrations.copy_templates(assigns)
end
if opts[:tests] do
Tests.copy_templates(assigns)
Fixtures.copy_templates(assigns)
end
if opts[:web] do
Controllers.copy_templates(assigns)
LiveViews.copy_templates(assigns)
AppHtml.update_app_html(assigns)
AppHtml.update_page_html(assigns)
SessionHooks.copy_templates(assigns)
Components.copy_templates(assigns)
Javascript.inject_hooks()
end
if opts[:router] do
Router.update_router(assigns)
end
success_message =
"""
✅ Successfully scaffolded WebauthnComponents for #{inspect(assigns[:app_pascal_case])}
📚 Resources
- Repo:\thttps://github.com/liveshowy/webauthn_components
- Hex:\thttps://hex.pm/packages/webauthn_components
- Docs:\thttps://hexdocs.pm/webauthn_components/readme.html
"""
IO.puts(success_message)
{_parsed, _args, errors} ->
invalid_opts =
errors
|> Enum.map_join(", ", &elem(&1, 0))
Mix.Tasks.Help.run([@mix_task])
Mix.shell().error("Invalid option(s): #{invalid_opts}")
end
end
end