diff options
Diffstat (limited to 'server/src/jchat_http_static.erl')
-rw-r--r-- | server/src/jchat_http_static.erl | 110 |
1 files changed, 110 insertions, 0 deletions
diff --git a/server/src/jchat_http_static.erl b/server/src/jchat_http_static.erl new file mode 100644 index 0000000..e11a9c0 --- /dev/null +++ b/server/src/jchat_http_static.erl @@ -0,0 +1,110 @@ +-module(jchat_http_static). + +-export([init/2]). + +init(Req0, State) -> + Method = cowboy_req:method(Req0), + handle_request(Method, Req0, State). + +handle_request(<<"GET">>, Req0, State) -> + Path = cowboy_req:path(Req0), + serve_static_file(Path, Req0, State); + +handle_request(<<"HEAD">>, Req0, State) -> + Path = cowboy_req:path(Req0), + serve_static_file(Path, Req0, State); + +handle_request(<<"OPTIONS">>, Req0, State) -> + Req1 = cowboy_req:reply(200, #{ + <<"access-control-allow-origin">> => <<"*">>, + <<"access-control-allow-methods">> => <<"GET, HEAD, OPTIONS">>, + <<"access-control-allow-headers">> => <<"content-type">> + }, <<>>, Req0), + {ok, Req1, State}; + +handle_request(_Method, Req0, State) -> + Req1 = cowboy_req:reply(405, #{ + <<"content-type">> => <<"text/plain">> + }, <<"Method Not Allowed">>, Req0), + {ok, Req1, State}. + +serve_static_file(Path, Req0, State) -> + StaticDir = jchat_config:static_files_dir(), + + % Convert path to filename + Filename = case Path of + <<"/">> -> "index.html"; + <<"/", Rest/binary>> -> binary_to_list(Rest); + _ -> binary_to_list(Path) + end, + + FilePath = filename:join(StaticDir, Filename), + + case file:read_file(FilePath) of + {ok, Content} -> + ContentType = get_content_type(Filename), + Req1 = cowboy_req:reply(200, #{ + <<"content-type">> => ContentType, + <<"access-control-allow-origin">> => <<"*">>, + <<"cache-control">> => <<"public, max-age=3600">> + }, Content, Req0), + {ok, Req1, State}; + {error, enoent} -> + % If file not found and it's not an API request, serve index.html for SPA routing + case is_spa_route(Filename) of + true -> + IndexPath = filename:join(StaticDir, "index.html"), + case file:read_file(IndexPath) of + {ok, Content} -> + Req1 = cowboy_req:reply(200, #{ + <<"content-type">> => <<"text/html; charset=utf-8">>, + <<"access-control-allow-origin">> => <<"*">> + }, Content, Req0), + {ok, Req1, State}; + {error, _} -> + serve_404(Req0, State) + end; + false -> + serve_404(Req0, State) + end; + {error, _} -> + serve_500(Req0, State) + end. + +serve_404(Req0, State) -> + Req1 = cowboy_req:reply(404, #{ + <<"content-type">> => <<"text/plain">> + }, <<"Not Found">>, Req0), + {ok, Req1, State}. + +serve_500(Req0, State) -> + Req1 = cowboy_req:reply(500, #{ + <<"content-type">> => <<"text/plain">> + }, <<"Internal Server Error">>, Req0), + {ok, Req1, State}. + +get_content_type(Filename) -> + case filename:extension(Filename) of + ".html" -> <<"text/html; charset=utf-8">>; + ".css" -> <<"text/css; charset=utf-8">>; + ".js" -> <<"application/javascript; charset=utf-8">>; + ".json" -> <<"application/json; charset=utf-8">>; + ".png" -> <<"image/png">>; + ".jpg" -> <<"image/jpeg">>; + ".jpeg" -> <<"image/jpeg">>; + ".gif" -> <<"image/gif">>; + ".svg" -> <<"image/svg+xml">>; + ".ico" -> <<"image/x-icon">>; + ".woff" -> <<"font/woff">>; + ".woff2" -> <<"font/woff2">>; + ".ttf" -> <<"font/ttf">>; + ".eot" -> <<"application/vnd.ms-fontobject">>; + _ -> <<"application/octet-stream">> + end. + +is_spa_route(Filename) -> + % Don't serve index.html for actual file extensions + case filename:extension(Filename) of + "" -> true; % No extension, likely a SPA route + _ -> false % Has extension, likely a real file + end. |