Browse Source

feat: add indie app, init micropub support

tags/v0.62.0
Inhji Y. 4 months ago
parent
commit
cd63797078
14 changed files with 249 additions and 31 deletions
  1. +3
    -4
      apps/http/lib/http.ex
  2. +5
    -0
      apps/indie/.formatter.exs
  3. +24
    -0
      apps/indie/.gitignore
  4. +21
    -0
      apps/indie/README.md
  5. +18
    -0
      apps/indie/lib/indie.ex
  6. +4
    -0
      apps/indie/lib/micropub/handler.ex
  7. +90
    -0
      apps/indie/lib/token.ex
  8. +31
    -0
      apps/indie/mix.exs
  9. +4
    -0
      apps/indie/test/indie_test.exs
  10. +1
    -0
      apps/indie/test/test_helper.exs
  11. +9
    -0
      apps/tomie_web/lib/tomie_web/router.ex
  12. +17
    -15
      apps/tomie_web/mix.exs
  13. +16
    -0
      config/config.exs
  14. +6
    -12
      mix.exs

+ 3
- 4
apps/http/lib/http.ex View File

@@ -4,13 +4,12 @@ defmodule Http do
"""

use HTTPoison.Base

@user_agent Application.compile_env!(:tomie, :user_agent)
@options [ssl: [{:versions, [:"tlsv1.2", :"tlsv1.1", :tlsv1]}]]
@headers [
{"User-Agent", "Tomie/0.x +https://dev.inhji.de"}
{"User-Agent", @user_agent}
]

@options [ssl: [{:versions, [:"tlsv1.2", :"tlsv1.1", :tlsv1]}]]

def process_request_headers(headers), do: headers ++ @headers
def process_request_options(options), do: options ++ @options
end

+ 5
- 0
apps/indie/.formatter.exs View File

@@ -0,0 +1,5 @@
# Used by "mix format"
[
import_deps: [:ecto],
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
]

+ 24
- 0
apps/indie/.gitignore View File

@@ -0,0 +1,24 @@
# The directory Mix will write compiled artifacts to.
/_build/

# If you run "mix test --cover", coverage assets end up here.
/cover/

# The directory Mix downloads your dependencies sources to.
/deps/

# Where third-party dependencies like ExDoc output generated docs.
/doc/

# Ignore .fetch files in case you like to edit your project deps locally.
/.fetch

# If the VM crashes, it generates a dump, let's ignore it too.
erl_crash.dump

# Also ignore archive artifacts (built via "mix archive.build").
*.ez

# Ignore package tarball (built via "mix hex.build").
indie-*.tar


+ 21
- 0
apps/indie/README.md View File

@@ -0,0 +1,21 @@
# Indie

**TODO: Add description**

## Installation

If [available in Hex](https://hex.pm/docs/publish), the package can be installed
by adding `indie` to your list of dependencies in `mix.exs`:

```elixir
def deps do
[
{:indie, "~> 0.1.0"}
]
end
```

Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc)
and published on [HexDocs](https://hexdocs.pm). Once published, the docs can
be found at [https://hexdocs.pm/indie](https://hexdocs.pm/indie).


+ 18
- 0
apps/indie/lib/indie.ex View File

@@ -0,0 +1,18 @@
defmodule Indie do
@moduledoc """
Documentation for `Indie`.
"""

@doc """
Hello world.

## Examples

iex> Indie.hello()
:world

"""
def hello do
:world
end
end

+ 4
- 0
apps/indie/lib/micropub/handler.ex View File

@@ -0,0 +1,4 @@
defmodule Indie.Micropub.Handler do
@behaviour PlugMicropub.HandlerBehaviour
require Logger
end

+ 90
- 0
apps/indie/lib/token.ex View File

@@ -0,0 +1,90 @@
defmodule Indie.Token do
require Logger

alias HTTPoison.Response

@supported_scopes Application.compile_env!(:indie, :supported_scopes)

def verify(access_token, required_scope, token_endpoint) do
headers = [authorization: "Bearer #{access_token}", accept: "application/json"]

with {:ok, %Response{status_code: 200, body: body}} <-
HTTP.get(token_endpoint, headers),
{:ok, body_map} <- Jason.decode(body),
:ok <- verify_token_response(body_map, required_scope) do
:ok
else
{:error, %HTTPoison.Response{status_code: code}} ->
Logger.error("Token endpoint responded with unexpected code: #{inspect(code)}")
{:error, :insufficient_scope, code}

{:error, %HTTPoison.Error{reason: reason}} ->
Logger.error("Could not reach token endpoint: #{inspect(reason)}")
{:error, :insufficient_scope, reason}

{:error, name, reason} ->
Logger.error("Error #{reason} in #{name}")
{:error, :insufficient_scope, reason}

error ->
Logger.error("Unexpected error: #{inspect(error)}")
{:error, :insufficient_scope, "Internal Server Error"}
end
end

defp verify_token_response(
%{
"me" => hostname,
"scope" => scope,
"client_id" => client_id,
"issued_at" => _issued_at,
"issued_by" => _issued_by,
"nonce" => _nonce
},
required_scope
) do
Logger.info("Hostname: '#{hostname}'")
Logger.info("ClientId: #{client_id}")
Logger.info("Scopes: '#{scope}'")

with {:ok, _hostname} <- verify_hostname_match(hostname),
{:ok, _scope} <- verify_scope_support(scope, required_scope, @supported_scopes) do
:ok
else
{:error, name, reason} ->
Logger.error("Could not verify token response: #{reason}")
{:error, name, reason}
end
end

defp verify_hostname_match(hostname) do
hostnames_match? = Akedia.Helpers.sanitize_hostname(hostname) == Akedia.url()

case hostnames_match? do
true ->
{:ok, hostname}

_ ->
Logger.warn("Hostnames do not match: Given #{hostname}, Actual: #{Akedia.url()}")
{:error, "verify_hostname_match", "hostname does not match"}
end
end

defp verify_scope_support(scopes, nil, _supported_scopes), do: {:ok, scopes}

defp verify_scope_support(scopes, required_scope, supported_scopes) do
required = Enum.member?(supported_scopes, required_scope)
requested = Enum.member?(String.split(scopes), required_scope)

cond do
required && requested ->
{:ok, scopes}

!required ->
{:error, "verify_scope_support", "scope '#{required_scope}' is not supported"}

!requested ->
{:error, "verify_scope_support", "scope '#{required_scope}' was not requested"}
end
end
end

+ 31
- 0
apps/indie/mix.exs View File

@@ -0,0 +1,31 @@
defmodule Indie.MixProject do
use Mix.Project

def project do
[
app: :indie,
version: "0.1.0",
build_path: "../../_build",
config_path: "../../config/config.exs",
deps_path: "../../deps",
lockfile: "../../mix.lock",
elixir: "~> 1.10",
start_permanent: Mix.env() == :prod,
deps: deps()
]
end

# Run "mix help compile.app" to learn about applications.
def application do
[
extra_applications: [:logger]
]
end

# Run "mix help deps" to learn about dependencies.
defp deps do
[
{:http, in_umbrella: true},
{:plug_micropub, "~> 0.1.0"} ]
end
end

+ 4
- 0
apps/indie/test/indie_test.exs View File

@@ -0,0 +1,4 @@
defmodule IndieTest do
use ExUnit.Case
doctest Indie
end

+ 1
- 0
apps/indie/test/test_helper.exs View File

@@ -0,0 +1 @@
ExUnit.start()

+ 9
- 0
apps/tomie_web/lib/tomie_web/router.ex View File

@@ -50,6 +50,15 @@ defmodule TomieWeb.Router do
live_dashboard "/dashboard", metrics: Tomie.Telemetry
end

scope "/indie" do
pipe_through [:api]

forward "/micropub",
PlugMicropub,
handler: Indie.Micropub.Handler,
json_decoder: Jason
end

scope "/admin", TomieWeb do
pipe_through [:browser, :protected]



+ 17
- 15
apps/tomie_web/mix.exs View File

@@ -38,29 +38,31 @@ defmodule TomieWeb.MixProject do
# Type `mix help deps` for examples and options.
defp deps do
[
{:bookmarks, in_umbrella: true},
{:chartkick, "~> 0.4.0"},
{:db, in_umbrella: true},
{:excoveralls, "~> 0.12.3", only: :test},
{:gettext, "~> 0.11"},
{:indie, in_umbrella: true},
{:jason, "~> 1.0"},
{:listens, in_umbrella: true},
{:phoenix, "~> 1.5.0"},
{:phoenix_pubsub, "~> 2.0"},
{:phoenix_active_link, "~> 0.3.0"},
{:phoenix_ecto, "~> 4.0"},
{:phoenix_form_awesomplete, "~> 0.1"},
{:phoenix_html, "~> 2.11"},
{:phoenix_live_dashboard, "~> 0.1"},
{:phoenix_live_reload, "~> 1.2", only: :dev},
{:phoenix_live_view, "~> 0.12.0"},
{:phoenix_live_dashboard, "~> 0.1"},
{:phoenix_form_awesomplete, "~> 0.1"},
{:phoenix_active_link, "~> 0.3.0"},
{:chartkick, "~> 0.4.0"},
{:gettext, "~> 0.11"},
{:jason, "~> 1.0"},
{:phoenix_pubsub, "~> 2.0"},
{:plug_cowboy, "~> 2.2"},
{:plug_micropub, "~> 0.1.0"},
{:pow, "~> 1.0.19"},
{:excoveralls, "~> 0.12.3", only: :test},
{:timex, "~> 3.5"},
{:telemetry_poller, "~> 0.4"},
{:telemetry_metrics, "~> 0.4"},
{:tomie, in_umbrella: true},
{:bookmarks, in_umbrella: true},
{:tags, in_umbrella: true},
{:db, in_umbrella: true},
{:listens, in_umbrella: true}
{:telemetry_metrics, "~> 0.4"},
{:telemetry_poller, "~> 0.4"},
{:timex, "~> 3.5"},
{:tomie, in_umbrella: true}
]
end



+ 16
- 0
config/config.exs View File

@@ -23,6 +23,22 @@ config :scraper, :weather,
api_key: "5e8d6569a828fd7860acf87bf1113a9e",
city_id: "2920512"

config :indie,
supported_scopes: [
# Micropub scopes
"create",
"update",
"delete",
"undelete",
"media",
# Microsub scopes
"read",
"follow",
"mute",
"block",
"channels"
]

config :tomie, Oban,
repo: Db.Repo,
prune: {:maxlen, 10_000},


+ 6
- 12
mix.exs View File

@@ -9,24 +9,18 @@ defmodule Tomie.Umbrella.MixProject do
version: @version,
start_permanent: Mix.env() == :prod,
deps: deps(),
test_coverage: [tool: ExCoveralls],
preferred_cli_env: [
coveralls: :test,
"coveralls.detail": :test,
"coveralls.post": :test,
"coveralls.html": :test
],
releases: [
tomie: [
applications: [
tomie: :permanent,
tomie_web: :permanent,
db: :permanent,
bookmarks: :permanent,
db: :permanent,
http: :permanent,
indie: :permanent,
listens: :permanent,
scraper: :permanent,
tags: :permanent,
listens: :permanent,
http: :permanent,
tomie: :permanent,
tomie_web: :permanent,
wiki: :permanent
]
]


Loading…
Cancel
Save