Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into board-moderators
Browse files Browse the repository at this point in the history
* origin/main: (36 commits)
  style(application): mix format
  refactor(bbc_parser): reduce bbc parser php call timeout to 1 second
  refactor(config): poolboy_config -> bbc_parser_poolboy_config
  feat(helpers/proxy_conversion): load board last post user avatar
  feat(docker): upgrade elixir version in docker file
  fix(credo): resolve credo error with user_json
  refactor(user-find): move manipulation of user object into user_json file
  fix(last-login): use users last login instead of test value, resolve credo error
  feat(last-active): implement last active, respecting flag showOnline if user doesnt want to be shown as active within the past 72 hours
  test(warnings): resolve test warnings
  feat(user-find): convert gender and dob into human readable format
  test(static): resolve dialyzer errors after upgrade of elixir/erlang
  feat(dob): calculate dob for user find proxy
  feat(gender): calculate gender for user find proxy
  refactor(cleanup): use BBCParser Gen Server for parsing user signature, run format
  feat(last-active): bring back last post date for last active in user find
  fix(credo): resolve credo issues in bbc parser
  feat(credo): upgrade credo to latest
  fix(parser): add missing settings that were causing portuguese characters to break
  feat(parser): user async parser for signature, code cleanup
  ...
  • Loading branch information
crod951 committed Nov 15, 2024
2 parents 755640e + 17baed6 commit 92640e3
Show file tree
Hide file tree
Showing 26 changed files with 529 additions and 170 deletions.
4 changes: 2 additions & 2 deletions .tool-versions
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
elixir 1.14.4-otp-25
erlang 25.3.1
elixir 1.17.3-otp-27
erlang 27.1.2
php 8.3.7
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM elixir:1.14.0
FROM elixir:1.17.3
# install php
RUN curl -sSL https://packages.sury.org/php/README.txt | bash -x
RUN apt update
Expand Down
10 changes: 10 additions & 0 deletions config/runtime.exs
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,16 @@ end

##### PROXY REPO CONFIGURATIONS #####

bbc_parser_poolboy_config = [
name: {:local, :bbc_parser},
worker_module: EpochtalkServer.BBCParser,
size: 5,
max_overflow: 2,
strategy: :fifo
]

config :epochtalk_server, bbc_parser_poolboy_config: bbc_parser_poolboy_config

# conditionally show debug logs in prod
if config_env() == :prod do
logger_level =
Expand Down
5 changes: 5 additions & 0 deletions lib/epochtalk_server/application.ex
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ defmodule EpochtalkServer.Application do
EpochtalkServer.Repo,
# Start the Smf repository
EpochtalkServer.SmfRepo,
# Start the BBC Parser
:poolboy.child_spec(:bbc_parser, bbc_parser_poolboy_config()),
# Start Role Cache
EpochtalkServer.Cache.Role,
# Warm frontend_config variable (referenced by api controllers)
Expand Down Expand Up @@ -68,4 +70,7 @@ defmodule EpochtalkServer.Application do

# fetch redix config
defp redix_config(), do: Application.get_env(:epochtalk_server, :redix)

defp bbc_parser_poolboy_config,
do: Application.get_env(:epochtalk_server, :bbc_parser_poolboy_config)
end
74 changes: 74 additions & 0 deletions lib/epochtalk_server/bbc_parser.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
defmodule EpochtalkServer.BBCParser do
use GenServer
require Logger
alias Porcelain.Process, as: Proc
@timeout 1_000

@moduledoc """
`BBCParser` genserver, runs interactive php shell to call bbcode parser
"""

## === genserver functions ====

@impl true
def init(:ok), do: {:ok, load()}

@impl true
def handle_call({:parse, ""}, _from, {proc, pid}),
do: {:reply, "", {proc, pid}}

def handle_call({:parse, bbcode_data}, _from, {proc, pid}) when is_binary(bbcode_data) do
Proc.send_input(proc, "echo parse_bbc('#{bbcode_data}');\n")

parsed =
receive do
{^pid, :data, :out, data} ->
Logger.debug(data)
data
end

{:reply, parsed, {proc, pid}}
end

## === parser api functions ====

@doc """
Start genserver and create a reference for supervision tree
"""
def start_link(_opts), do: GenServer.start_link(__MODULE__, :ok)

@doc """
Uses poolboy to call parser
"""
def async_parse(bbcode_data) do
:poolboy.transaction(
:bbc_parser,
fn pid ->
try do
Logger.debug("#{__MODULE__}(ASYNC PARSE): #{inspect(pid)}")
GenServer.call(pid, {:parse, bbcode_data}, @timeout)
catch
e, r ->
Logger.debug("poolboy transaction caught error: #{inspect(e)}, #{inspect(r)}")
:ok
end
end,
@timeout
)
end

## === private functions ====

# returns loaded interactive php shell
defp load() do
proc = %Proc{pid: pid} = Porcelain.spawn_shell("php -a", in: :receive, out: {:send, self()})
Proc.send_input(proc, "require 'parsing.php';\n")
Logger.debug("#{__MODULE__}(LOAD): #{inspect(pid)}")
# clear initial php interactive shell message
receive do
{^pid, :data, :out, data} -> Logger.debug("#{__MODULE__}: #{inspect(data)}")
end

{proc, pid}
end
end
3 changes: 0 additions & 3 deletions lib/epochtalk_server_web/controllers/board.ex
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,6 @@ defmodule EpochtalkServerWeb.Controllers.Board do
400,
"Error, cannot convert slug: board does not exist"
)

_ ->
ErrorHelpers.render_json_error(conn, 400, "Error, cannot convert board slug to id")
end
end

Expand Down
2 changes: 0 additions & 2 deletions lib/epochtalk_server_web/controllers/image_reference.ex
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,6 @@ defmodule EpochtalkServerWeb.Controllers.ImageReference do
# checksum <- Validate.cast(attrs, "checksum", :string, required: true),
file_type <- Validate.cast(attrs, "file_type", :string, required: true) do
%{length: length, type: file_type}
else
_ -> %{error: "Invalid attrs"}
end
end
end
5 changes: 2 additions & 3 deletions lib/epochtalk_server_web/controllers/mention.ex
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,7 @@ defmodule EpochtalkServerWeb.Controllers.Mention do
pagination_data: data,
extended: extended
}),
else:
({:auth, nil} ->
ErrorHelpers.render_json_error(conn, 400, "Not logged in, cannot page mentions"))
else: ({:auth, nil} ->
ErrorHelpers.render_json_error(conn, 400, "Not logged in, cannot page mentions"))
end
end
7 changes: 1 addition & 6 deletions lib/epochtalk_server_web/controllers/moderation_log.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ defmodule EpochtalkServerWeb.Controllers.ModerationLog do
@moduledoc """
Controller For `ModerationLog` related API requests
"""
alias EpochtalkServer.Auth.Guardian
alias EpochtalkServer.Models.ModerationLog
alias EpochtalkServerWeb.ErrorHelpers
alias EpochtalkServerWeb.Helpers.Validate
Expand All @@ -14,16 +13,12 @@ defmodule EpochtalkServerWeb.Controllers.ModerationLog do
Used to page `ModerationLog` models for moderation log view`
"""
def page(conn, attrs) do
with {:auth, true} <- {:auth, Guardian.Plug.authenticated?(conn)},
:ok <- ACL.allow!(conn, "moderationLogs.page"),
with :ok <- ACL.allow!(conn, "moderationLogs.page"),
page <- Validate.cast(attrs, "page", :integer, min: 1),
limit <- Validate.cast(attrs, "limit", :integer, min: 1),
{:ok, moderation_logs, data} <- ModerationLog.page(attrs, page, per_page: limit) do
render(conn, :page, %{moderation_logs: moderation_logs, pagination_data: data})
else
{:auth, false} ->
ErrorHelpers.render_json_error(conn, 400, "Not logged in, cannot page moderation log")

{:error, data} ->
ErrorHelpers.render_json_error(conn, 400, data)

Expand Down
33 changes: 6 additions & 27 deletions lib/epochtalk_server_web/controllers/notification.ex
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,16 @@ defmodule EpochtalkServerWeb.Controllers.Notification do
Used to retrieve `Notification` counts for a specific `User`
"""
def counts(conn, attrs) do
with {:auth, %{} = user} <- {:auth, Guardian.Plug.current_resource(conn)},
with user <- Guardian.Plug.current_resource(conn),
:ok <- ACL.allow!(conn, "notifications.counts"),
max <- Validate.cast(attrs, "max", :integer, min: 1) do
render(conn, :counts, data: Notification.counts_by_user_id(user.id, max: max || 99))
else
{:auth, nil} ->
ErrorHelpers.render_json_error(
conn,
400,
"Not logged in, cannot fetch notification counts"
)

{:access, false} ->
_ ->
ErrorHelpers.render_json_error(
conn,
400,
"Not logged in, cannot fetch notification counts"
500,
"Something went wrong, cannot fetch notification counts"
)
end
end
Expand All @@ -39,19 +32,12 @@ defmodule EpochtalkServerWeb.Controllers.Notification do
Used to dismiss `Notification` counts for a specific `User`
"""
def dismiss(conn, %{"id" => id}) do
with {:auth, %{} = user} <- {:auth, Guardian.Plug.current_resource(conn)},
with user <- Guardian.Plug.current_resource(conn),
:ok <- ACL.allow!(conn, "notifications.dismiss"),
{_count, nil} <- Notification.dismiss(id) do
EpochtalkServerWeb.Endpoint.broadcast("user:#{user.id}", "refreshMentions", %{})
render(conn, :dismiss, success: true)
else
{:auth, nil} ->
ErrorHelpers.render_json_error(
conn,
400,
"Not logged in, cannot dismiss notification counts"
)

_ ->
ErrorHelpers.render_json_error(
conn,
Expand All @@ -62,19 +48,12 @@ defmodule EpochtalkServerWeb.Controllers.Notification do
end

def dismiss(conn, %{"type" => type}) do
with {:auth, %{} = user} <- {:auth, Guardian.Plug.current_resource(conn)},
with user <- Guardian.Plug.current_resource(conn),
:ok <- ACL.allow!(conn, "notifications.dismiss"),
{_count, nil} <- Notification.dismiss_type_by_user_id(user.id, type) do
EpochtalkServerWeb.Endpoint.broadcast("user:#{user.id}", "refreshMentions", %{})
render(conn, :dismiss, success: true)
else
{:auth, nil} ->
ErrorHelpers.render_json_error(
conn,
400,
"Not logged in, cannot dismiss notification counts"
)

{:error, :invalid_notification_type} ->
ErrorHelpers.render_json_error(conn, 400, "Cannot dismiss, invalid notification type")

Expand Down
14 changes: 1 addition & 13 deletions lib/epochtalk_server_web/controllers/poll.ex
Original file line number Diff line number Diff line change
Expand Up @@ -193,9 +193,6 @@ defmodule EpochtalkServerWeb.Controllers.Poll do
"Account must be active to modify lock on poll"
)

{:error, data} ->
ErrorHelpers.render_json_error(conn, 400, data)

_ ->
ErrorHelpers.render_json_error(conn, 400, "Error, cannot lock poll")
end
Expand Down Expand Up @@ -316,9 +313,6 @@ defmodule EpochtalkServerWeb.Controllers.Poll do
poll <- Poll.by_thread(thread_id) do
render(conn, :poll, %{poll: poll, has_voted: false})
else
{:valid_answers_list, false} ->
ErrorHelpers.render_json_error(conn, 400, "Error, 'answer_ids' must be a list")

{:can_read, {:ok, false}} ->
ErrorHelpers.render_json_error(
conn,
Expand Down Expand Up @@ -355,14 +349,8 @@ defmodule EpochtalkServerWeb.Controllers.Poll do
{:board_banned, {:ok, true}} ->
ErrorHelpers.render_json_error(conn, 403, "Unauthorized, you are banned from this board")

{:error, :board_does_not_exist} ->
ErrorHelpers.render_json_error(conn, 400, "Error, board does not exist")

{:error, data} ->
ErrorHelpers.render_json_error(conn, 400, data)

_ ->
ErrorHelpers.render_json_error(conn, 400, "Error, cannot cast vote")
ErrorHelpers.render_json_error(conn, 400, "Error, cannot delete vote")
end
end

Expand Down
31 changes: 27 additions & 4 deletions lib/epochtalk_server_web/controllers/post.ex
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ defmodule EpochtalkServerWeb.Controllers.Post do

@max_post_title_length 255

plug :check_proxy when action in [:by_thread]
plug :check_proxy when action in [:by_thread, :by_username]

@doc """
Used to create posts
Expand Down Expand Up @@ -420,9 +420,6 @@ defmodule EpochtalkServerWeb.Controllers.Post do
Validate.cast(attrs, "body", :string, required: true, max: post_max_length, min: 1),
parsed_body <- Parse.markdown(body) do
render(conn, :preview, %{parsed_body: parsed_body})
else
_ ->
ErrorHelpers.render_json_error(conn, 400, "Error, cannot generate preview")
end
end

Expand Down Expand Up @@ -542,13 +539,39 @@ defmodule EpochtalkServerWeb.Controllers.Post do
conn
end

:by_username ->
conn
|> proxy_by_username(conn.params)
|> halt()

_ ->
conn
end

conn
end

defp proxy_by_username(conn, attrs) do
# Parameter Validation
with user_id <- Validate.cast(attrs, "id", :integer, required: true),
page <- Validate.cast(attrs, "page", :integer, default: 1, min: 1),
limit <- Validate.cast(attrs, "limit", :integer, default: 25, min: 1, max: 100),
desc <- Validate.cast(attrs, "desc", :boolean, default: true),
{:ok, posts, data} <-
ProxyConversion.build_model("posts.by_user", user_id, page, limit, desc) do
render(conn, :proxy_by_username, %{
posts: posts,
count: data.total_records,
limit: data.per_page,
page: data.page,
desc: desc
})
else
_ ->
ErrorHelpers.render_json_error(conn, 400, "Error, cannot get posts by username")
end
end

defp proxy_by_thread(conn, attrs) do
with thread_id <- Validate.cast(attrs, "thread_id", :integer, required: true),
page <- Validate.cast(attrs, "page", :integer, default: 1),
Expand Down
9 changes: 6 additions & 3 deletions lib/epochtalk_server_web/controllers/preference.ex
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,11 @@ defmodule EpochtalkServerWeb.Controllers.Preference do
def preferences(conn, _attrs) do
with {:auth, %{} = user} <- {:auth, Guardian.Plug.current_resource(conn)},
do: render(conn, :preferences, preferences: Preference.by_user_id(user.id)),
else:
({:auth, nil} ->
ErrorHelpers.render_json_error(conn, 400, "Not logged in, cannot fetch preferences"))
else: ({:auth, nil} ->
ErrorHelpers.render_json_error(
conn,
400,
"Not logged in, cannot fetch preferences"
))
end
end
Loading

0 comments on commit 92640e3

Please sign in to comment.