197 lines
6.2 KiB
Elixir
197 lines
6.2 KiB
Elixir
defmodule BDS.Desktop.ShellController do
|
|
@moduledoc false
|
|
|
|
alias BDS.UI.Sidebar
|
|
|
|
def index_html do
|
|
BDS.UI.ShellPage.render()
|
|
end
|
|
|
|
def task_status_json do
|
|
Jason.encode!(BDS.Tasks.status_snapshot())
|
|
end
|
|
|
|
def projects_json do
|
|
Jason.encode!(BDS.Projects.shell_snapshot())
|
|
rescue
|
|
error in [Exqlite.Error] ->
|
|
if String.contains?(Exception.message(error), "no such table: projects") do
|
|
Jason.encode!(default_project_snapshot())
|
|
else
|
|
reraise error, __STACKTRACE__
|
|
end
|
|
end
|
|
|
|
def upsert_project_json(payload) when is_map(payload) do
|
|
case normalize_project_request(payload) do
|
|
{:create, attrs} -> create_project_json(attrs)
|
|
{:select, project_id} -> select_project_json(project_id)
|
|
:error -> Jason.encode!(%{status: "error", error: %{message: "Missing project name or project_id"}})
|
|
end
|
|
end
|
|
|
|
def choose_project_folder_json(payload \\ %{}) when is_map(payload) do
|
|
prompt = Map.get(payload, "prompt") || Map.get(payload, :prompt) || "Select existing blog folder"
|
|
|
|
case folder_picker().choose_directory(prompt) do
|
|
{:ok, path} -> Jason.encode!(project_folder_payload(path))
|
|
:cancel -> Jason.encode!(%{status: "cancel"})
|
|
{:error, error} -> Jason.encode!(%{status: "error", error: normalize_error(error)})
|
|
end
|
|
end
|
|
|
|
def command_json(payload) when is_map(payload) do
|
|
action = Map.get(payload, "action") || Map.get(payload, :action)
|
|
params = Map.get(payload, "params") || Map.get(payload, :params) || %{}
|
|
|
|
case BDS.Desktop.ShellCommands.execute(action, params) do
|
|
{:ok, result} -> Jason.encode!(%{status: "ok", result: result})
|
|
{:error, error} -> Jason.encode!(%{status: "error", error: normalize_error(error)})
|
|
end
|
|
end
|
|
|
|
def sidebar_json(payload) when is_map(payload) do
|
|
view = Map.get(payload, "view") || Map.get(payload, :view)
|
|
filters = Map.get(payload, "filters") || Map.get(payload, :filters) || %{}
|
|
|
|
data =
|
|
try do
|
|
case active_project_id() do
|
|
nil -> Sidebar.view(nil, view, filters)
|
|
project_id -> Sidebar.view(project_id, view, filters)
|
|
end
|
|
rescue
|
|
error in [Exqlite.Error, DBConnection.OwnershipError] ->
|
|
if match?(%Exqlite.Error{}, error) and not String.contains?(Exception.message(error), "no such table") do
|
|
reraise error, __STACKTRACE__
|
|
end
|
|
|
|
Sidebar.view(nil, view, filters)
|
|
end
|
|
|
|
Jason.encode!(%{status: "ok", view: view, data: data})
|
|
end
|
|
|
|
defp normalize_error(error) when is_map(error), do: error
|
|
defp normalize_error(error), do: %{message: inspect(error)}
|
|
|
|
defp normalize_project_request(payload) do
|
|
cond do
|
|
present?(Map.get(payload, "name") || Map.get(payload, :name)) ->
|
|
{:create,
|
|
%{
|
|
name: String.trim(Map.get(payload, "name") || Map.get(payload, :name)),
|
|
description: blank_to_nil(Map.get(payload, "description") || Map.get(payload, :description)),
|
|
data_path: blank_to_nil(Map.get(payload, "data_path") || Map.get(payload, :data_path))
|
|
}}
|
|
|
|
present?(Map.get(payload, "project_id") || Map.get(payload, :project_id)) ->
|
|
{:select, Map.get(payload, "project_id") || Map.get(payload, :project_id)}
|
|
|
|
true ->
|
|
:error
|
|
end
|
|
end
|
|
|
|
defp create_project_json(attrs) do
|
|
with {:ok, project} <- BDS.Projects.create_project(attrs),
|
|
{:ok, active_project} <- BDS.Projects.set_active_project(project.id) do
|
|
Jason.encode!(%{status: "ok", project: project_response(active_project), active_project_id: active_project.id})
|
|
else
|
|
{:error, error} -> Jason.encode!(%{status: "error", error: normalize_error(error)})
|
|
end
|
|
end
|
|
|
|
defp select_project_json(project_id) do
|
|
case BDS.Projects.set_active_project(project_id) do
|
|
{:ok, project} ->
|
|
Jason.encode!(%{status: "ok", project: project_response(project), active_project_id: project.id})
|
|
|
|
{:error, :not_found} ->
|
|
Jason.encode!(%{status: "error", error: %{message: "Project not found"}})
|
|
|
|
{:error, error} ->
|
|
Jason.encode!(%{status: "error", error: normalize_error(error)})
|
|
end
|
|
end
|
|
|
|
defp project_response(project) do
|
|
%{id: project.id, name: project.name, slug: project.slug, data_path: project.data_path, is_active: project.is_active}
|
|
end
|
|
|
|
defp project_folder_payload(path) do
|
|
normalized_path = Path.expand(path)
|
|
project_metadata = read_project_metadata(normalized_path)
|
|
existing_project = find_project_by_data_path(normalized_path)
|
|
|
|
%{
|
|
status: "ok",
|
|
path: normalized_path,
|
|
name: Map.get(project_metadata, "name") || Path.basename(normalized_path),
|
|
description: Map.get(project_metadata, "description"),
|
|
existing_project_id: existing_project && existing_project.id
|
|
}
|
|
end
|
|
|
|
defp read_project_metadata(path) do
|
|
project_json_path = Path.join([path, "meta", "project.json"])
|
|
|
|
case File.read(project_json_path) do
|
|
{:ok, contents} -> Jason.decode!(contents)
|
|
{:error, :enoent} -> %{}
|
|
end
|
|
end
|
|
|
|
defp find_project_by_data_path(path) do
|
|
normalized_path = Path.expand(path)
|
|
|
|
BDS.Projects.list_projects()
|
|
|> Enum.find(fn project ->
|
|
case project.data_path do
|
|
value when is_binary(value) -> Path.expand(value) == normalized_path
|
|
_other -> false
|
|
end
|
|
end)
|
|
end
|
|
|
|
defp folder_picker do
|
|
Application.get_env(:bds, :desktop, [])[:folder_picker] || BDS.Desktop.FolderPicker
|
|
end
|
|
|
|
defp default_project_snapshot do
|
|
%{
|
|
active_project_id: "default",
|
|
projects: [
|
|
%{
|
|
id: "default",
|
|
name: "My Blog",
|
|
slug: "my-blog",
|
|
data_path: nil,
|
|
is_active: true
|
|
}
|
|
]
|
|
}
|
|
end
|
|
|
|
defp active_project_id do
|
|
BDS.Projects.shell_snapshot().active_project_id
|
|
rescue
|
|
error in [Exqlite.Error, DBConnection.OwnershipError] ->
|
|
if match?(%Exqlite.Error{}, error) and not String.contains?(Exception.message(error), "no such table") do
|
|
reraise error, __STACKTRACE__
|
|
end
|
|
|
|
nil
|
|
end
|
|
|
|
defp present?(value) when is_binary(value), do: String.trim(value) != ""
|
|
defp present?(_value), do: false
|
|
|
|
defp blank_to_nil(value) when is_binary(value) do
|
|
trimmed = String.trim(value)
|
|
if trimmed == "", do: nil, else: trimmed
|
|
end
|
|
|
|
defp blank_to_nil(_value), do: nil
|
|
end
|