Packages
nova
0.10.4
0.15.1
0.14.3
0.14.1
0.13.7
0.13.1
0.13.0
0.12.1
0.12.0
0.11.0
0.10.4
0.10.3
0.10.2
0.10.1
0.10.0
0.9.24
0.9.23
0.9.22
0.9.21
0.9.20
0.9.19
0.9.18
0.9.17
0.9.16
0.9.15
0.9.11
0.9.5
0.9.4
0.9.3
0.9.2
0.9.1
0.9.0
0.8.6
0.8.5
0.8.4
0.8.3
0.8.2
0.8.1
0.8.0
0.7.2
0.7.1
0.7.0
0.6.2
0.6.1
0.6.0
0.5.2
0.5.1
0.4.4
0.4.3
0.4.2
0.4.1
0.2.2
0.1.0
0.0.1
0.0.0
Nova is a web application framework
Current section
Files
Jump to
Current section
Files
src/controllers/nova_file_controller.erl
-module(nova_file_controller).
-export([
get_file/1,
get_dir/1
]).
-include_lib("kernel/include/file.hrl").
get_file(#{extra_state := #{static := File, options := Options}}) ->
Filepath = get_filepath(File),
MimeType =
case maps:get(mimetype, Options, undefined) of
undefined ->
{T, V, _} = cow_mimetypes:web(erlang:list_to_binary(Filepath)),
<<T/binary, "/", V/binary>>;
MType ->
MType
end,
%% We can just build the full path
#{size := Size} = file_info("", Filepath),
{sendfile, 200, #{}, {0, Size, Filepath}, MimeType};
get_file(_Req) ->
{status, 404}.
get_dir(#{extra_state := #{pathinfo := Pathinfo, static := Dir, options := Options}} = Req) ->
%% This case will be invoked if a directory was set with wildcard - pathinfo will then
%% contain the segments of the wildcard value
Filepath = get_filepath(Dir),
Filepath0 = lists:foldl(fun(F, Acc) -> filename:join(Acc, binary_to_list(F)) end, Filepath, Pathinfo),
case filelib:is_dir(Filepath0) of
false ->
%% Check if it's a directory
case filelib:is_file(Filepath0) of
true ->
%% It's a file
get_file(Req#{extra_state => #{static => {file, Filepath0}, options => Options}});
false ->
{status, 404}
end;
true ->
get_dir(Req#{extra_state => #{static => {dir, Filepath0}, options => Options}})
end;
get_dir(#{path := Path, extra_state := #{static := Dir, options := Options}}) ->
Filepath = get_filepath(Dir),
{ok, Files} = file:list_dir(Filepath),
case get_index_file(Files, maps:get(index_files, Options, ["index.html"])) of
{ok, IndexFile} ->
get_file(#{extra_state => #{static => {file, filename:join(Filepath, IndexFile)}, options => Options}});
false ->
case maps:get(list_dir, Options, false) of
false ->
%% We will not show the directory listing
{status, 403};
true ->
{ok, Files} = file:list_dir(Filepath),
FileInfos = [file_info(Filepath, F) || F <- Files],
ParentDir = case re:replace(Path , "/[^/]+/?$", "") of
[[]] -> undefined;
Parent -> Parent
end,
{ok, #{date => calendar:local_time(), parent_dir => ParentDir, path => Path, files => FileInfos}}
end
end;
get_dir(_Req) ->
{status, 404}.
%%%%%%%%%%%%%%%%%%%%%%%%
%% Internal functions %%
%%%%%%%%%%%%%%%%%%%%%%%%
get_index_file([], _) -> false;
get_index_file([File|Tl], IndexFiles) ->
case lists:member(File, IndexFiles) of
true ->
{ok, File};
false ->
get_index_file(Tl, IndexFiles)
end.
get_filepath({file, LocalFile}) ->
LocalFile;
get_filepath({priv_file, App, PrivFile}) ->
filename:join(code:priv_dir(App), PrivFile);
get_filepath({dir, LocalPath}) ->
LocalPath;
get_filepath({priv_dir, App, LocalPath}) ->
filename:join(code:priv_dir(App), LocalPath).
file_info(Filepath, Filename) ->
case file:read_file_info(filename:join(Filepath, Filename)) of
{ok, #file_info{type = Type, size = Size, mtime = LastModified}} ->
#{type => Type, size => Size,
last_modified => LastModified, filename => Filename};
_ ->
undefined
end.