Packages

phoenix_kit

1.7.53
1.7.208 1.7.207 1.7.206 1.7.205 1.7.204 1.7.203 1.7.202 1.7.201 1.7.200 1.7.199 1.7.198 1.7.197 1.7.196 1.7.194 1.7.193 1.7.192 1.7.191 1.7.190 1.7.189 1.7.187 1.7.186 1.7.185 1.7.184 1.7.183 1.7.182 1.7.181 1.7.180 1.7.179 1.7.178 1.7.177 1.7.176 1.7.175 1.7.174 1.7.173 1.7.172 1.7.171 1.7.170 1.7.169 1.7.168 1.7.167 1.7.166 1.7.165 1.7.164 1.7.162 1.7.161 1.7.160 1.7.159 1.7.157 1.7.156 1.7.155 1.7.154 1.7.153 1.7.152 1.7.151 1.7.150 1.7.149 1.7.146 1.7.145 1.7.144 1.7.143 1.7.138 1.7.133 1.7.132 1.7.131 1.7.130 1.7.128 1.7.126 1.7.125 1.7.121 1.7.120 1.7.119 1.7.118 1.7.117 1.7.116 1.7.115 1.7.114 1.7.113 1.7.112 1.7.111 1.7.110 1.7.109 1.7.108 1.7.107 1.7.106 1.7.105 1.7.104 1.7.103 1.7.102 1.7.101 1.7.100 1.7.99 1.7.98 1.7.97 1.7.96 1.7.95 1.7.94 1.7.93 1.7.92 1.7.91 1.7.90 1.7.89 1.7.88 1.7.87 1.7.86 1.7.85 1.7.84 1.7.83 1.7.82 1.7.81 1.7.80 1.7.79 1.7.78 1.7.77 1.7.76 1.7.75 1.7.74 1.7.71 1.7.70 1.7.69 1.7.66 1.7.65 1.7.64 1.7.63 1.7.62 1.7.61 1.7.59 1.7.58 1.7.57 1.7.56 1.7.55 1.7.54 1.7.53 1.7.52 1.7.51 1.7.49 1.7.44 1.7.43 1.7.42 1.7.41 1.7.39 1.7.38 1.7.37 1.7.36 1.7.34 1.7.33 1.7.31 1.7.30 1.7.29 1.7.28 1.7.27 1.7.26 1.7.25 1.7.24 1.7.23 1.7.22 1.7.21 1.7.20 1.7.19 1.7.18 1.7.17 1.7.16 1.7.15 1.7.14 1.7.13 1.7.12 1.7.11 1.7.10 1.7.9 1.7.8 1.7.7 1.7.6 1.7.5 1.7.4 1.7.3 1.7.2 1.7.1 1.7.0 1.6.20 1.6.19 1.6.18 1.6.17 1.6.16 1.6.15 1.6.14 1.6.13 1.6.12 1.6.11 1.6.10 1.6.9 1.6.8 1.6.7 1.6.6 1.6.5 1.6.4 1.6.3 1.5.2 1.5.1 1.5.0 1.4.9 1.4.8 1.4.7 1.4.6 1.4.5 1.4.4 1.4.3 1.4.2 1.4.1 1.4.0 1.3.2 1.3.1 1.3.0 1.2.10 1.2.9 1.2.8 1.2.7 1.2.5 1.2.4 1.2.2 1.2.1 1.2.0 1.1.0 1.0.0

A foundation for building Elixir Phoenix apps — SaaS, social networks, ERP systems, marketplaces, and more

Current section

Files

Jump to
phoenix_kit lib modules pages file_operations.ex
Raw

lib/modules/pages/file_operations.ex

defmodule PhoenixKit.Modules.Pages.FileOperations do
@moduledoc """
Filesystem operations for Pages module.
Handles reading, writing, listing files and folders.
"""
alias PhoenixKit.Modules.Pages.FilePaths
require Logger
@doc """
Lists all files and folders in a directory.
Returns a list of maps with name, type (:file or :folder), and path.
## Examples
iex> FileOperations.list_directory("/")
{:ok, [
%{name: "blog", type: :folder, path: "/blog"},
%{name: "hello.md", type: :file, path: "/hello.md"}
]}
"""
def list_directory(relative_path \\ "/") do
full_path = FilePaths.build_full_path(relative_path)
# Debug logging
Logger.debug("Pages list_directory: full_path=#{inspect(full_path)}")
case File.ls(full_path) do
{:ok, entries} ->
Logger.debug(
"Pages list_directory: found #{length(entries)} entries: #{inspect(entries)}"
)
items =
entries
|> Enum.map(fn entry ->
entry_path = Path.join(full_path, entry)
relative = Path.join(relative_path, entry)
type =
case File.dir?(entry_path) do
true -> :folder
false -> :file
end
%{
name: entry,
type: type,
path: relative,
full_path: entry_path
}
end)
|> Enum.sort_by(fn item ->
# Folders first, then files, alphabetically
{item.type != :folder, String.downcase(item.name)}
end)
{:ok, items}
{:error, reason} ->
Logger.debug("Pages list_directory: error #{inspect(reason)}")
{:error, reason}
end
end
@doc """
Reads file content.
## Examples
iex> FileOperations.read_file("/hello.md")
{:ok, "# Hello World"}
"""
def read_file(relative_path) do
full_path = FilePaths.build_full_path(relative_path)
File.read(full_path)
end
@doc """
Returns the absolute filesystem path for a relative page path.
"""
def absolute_path(relative_path) do
FilePaths.build_full_path(relative_path)
end
@doc """
Writes content to a file.
Creates parent directories if they don't exist.
## Examples
iex> FileOperations.write_file("/hello.md", "# Hello World")
:ok
"""
def write_file(relative_path, content) do
full_path = FilePaths.build_full_path(relative_path)
# Ensure parent directory exists
full_path
|> Path.dirname()
|> File.mkdir_p!()
File.write(full_path, content)
end
@doc """
Creates a new directory.
## Examples
iex> FileOperations.create_directory("/blog")
:ok
"""
def create_directory(relative_path) do
full_path = FilePaths.build_full_path(relative_path)
File.mkdir_p(full_path)
end
@doc """
Checks if a path exists (file or folder).
## Examples
iex> FileOperations.exists?("/hello.md")
true
"""
def exists?(relative_path) do
full_path = FilePaths.build_full_path(relative_path)
File.exists?(full_path)
end
@doc """
Checks if a file (not folder) exists at the path.
## Examples
iex> FileOperations.file_exists?("/hello.md")
true
iex> FileOperations.file_exists?("/blog")
false
"""
def file_exists?(relative_path) do
full_path = FilePaths.build_full_path(relative_path)
File.exists?(full_path) and not File.dir?(full_path)
end
@doc """
Checks if a directory exists at the path.
## Examples
iex> FileOperations.directory_exists?("/blog")
true
iex> FileOperations.directory_exists?("/hello.md")
false
"""
def directory_exists?(relative_path) do
full_path = FilePaths.build_full_path(relative_path)
File.dir?(full_path)
end
@doc """
Gets file info (size, modified time, etc).
## Examples
iex> FileOperations.file_info("/hello.md")
{:ok, %{size: 1024, mtime: ~U[2025-01-01 00:00:00Z]}}
"""
def file_info(relative_path) do
full_path = FilePaths.build_full_path(relative_path)
case File.stat(full_path) do
{:ok, stat} ->
{:ok,
%{
size: stat.size,
mtime: stat.mtime,
type: stat.type
}}
error ->
error
end
end
@doc """
Deletes a file or directory.
Directories are deleted recursively.
## Examples
iex> FileOperations.delete("/hello.md")
:ok
iex> FileOperations.delete("/blog")
:ok
"""
def delete(relative_path) do
full_path = FilePaths.build_full_path(relative_path)
cond do
File.dir?(full_path) ->
File.rm_rf(full_path)
File.exists?(full_path) ->
File.rm(full_path)
true ->
{:error, :enoent}
end
end
@doc """
Copies a file to a new location.
Creates parent directories if they don't exist.
## Examples
iex> FileOperations.copy("/hello.md", "/blog/hello.md")
:ok
"""
def copy(source_path, dest_path) do
source_full = FilePaths.build_full_path(source_path)
dest_full = FilePaths.build_full_path(dest_path)
# Only allow copying files, not directories
if File.dir?(source_full) do
{:error, :eisdir}
else
# Ensure destination parent directory exists
dest_full
|> Path.dirname()
|> File.mkdir_p!()
File.copy(source_full, dest_full)
end
end
@doc """
Moves a file or directory to a new location.
Creates parent directories if they don't exist.
## Examples
iex> FileOperations.move("/hello.md", "/blog/hello.md")
:ok
"""
def move(source_path, dest_path) do
source_full = FilePaths.build_full_path(source_path)
dest_full = FilePaths.build_full_path(dest_path)
# Ensure destination parent directory exists
dest_full
|> Path.dirname()
|> File.mkdir_p!()
File.rename(source_full, dest_full)
end
@doc """
Counts files and folders inside a directory recursively.
Returns a map with :files and :folders counts.
## Examples
iex> FileOperations.count_contents("/blog")
%{files: 5, folders: 2}
"""
def count_contents(relative_path) do
full_path = FilePaths.build_full_path(relative_path)
if File.dir?(full_path) do
do_count_contents(full_path)
else
%{files: 0, folders: 0}
end
end
defp do_count_contents(dir_path) do
case File.ls(dir_path) do
{:ok, entries} ->
Enum.reduce(entries, %{files: 0, folders: 0}, fn entry, acc ->
entry_path = Path.join(dir_path, entry)
if File.dir?(entry_path) do
# It's a folder, count it and recurse
subfolder_counts = do_count_contents(entry_path)
%{
files: acc.files + subfolder_counts.files,
folders: acc.folders + 1 + subfolder_counts.folders
}
else
# It's a file, count it
%{acc | files: acc.files + 1}
end
end)
{:error, _reason} ->
%{files: 0, folders: 0}
end
end
@doc """
Generates a unique filename by adding a number suffix if needed.
## Examples
iex> FileOperations.generate_unique_name("/hello.md")
"/hello-2.md" # if /hello.md exists
iex> FileOperations.generate_unique_name("/blog")
"/blog-2" # if /blog exists
"""
def generate_unique_name(relative_path) do
if exists?(relative_path) do
do_generate_unique_name(relative_path, 2)
else
relative_path
end
end
defp do_generate_unique_name(relative_path, counter) do
# Handle both files and directories
{base, ext} =
if Path.extname(relative_path) != "" do
# File with extension
{Path.rootname(relative_path), Path.extname(relative_path)}
else
# Directory or file without extension
{relative_path, ""}
end
new_path = "#{base}-#{counter}#{ext}"
if exists?(new_path) do
do_generate_unique_name(relative_path, counter + 1)
else
new_path
end
end
end