Архивирование с быстрым подключением и серверный рендеринг для веб-клиента #15
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
-module(handler_calendar_view).
|
||||
-include("records.hrl").
|
||||
-export([init/2]).
|
||||
|
||||
init(Req, Opts) ->
|
||||
CalendarId = cowboy_req:binding(calendar_id, Req),
|
||||
case verify_token(Req) of
|
||||
{ok, UserId} ->
|
||||
case is_owner(UserId, CalendarId) of
|
||||
true ->
|
||||
process_view(Req, CalendarId, Opts);
|
||||
false ->
|
||||
cowboy_req:reply(403,
|
||||
#{<<"content-type">> => <<"application/json">>},
|
||||
jsx:encode(#{error => <<"Access denied">>}),
|
||||
Req),
|
||||
{ok, Req, Opts}
|
||||
end;
|
||||
{error, _Reason} ->
|
||||
cowboy_req:reply(401,
|
||||
#{<<"content-type">> => <<"application/json">>},
|
||||
jsx:encode(#{error => <<"Unauthorized">>}),
|
||||
Req),
|
||||
{ok, Req, Opts}
|
||||
end.
|
||||
|
||||
verify_token(Req) ->
|
||||
case cowboy_req:header(<<"authorization">>, Req) of
|
||||
undefined ->
|
||||
{error, no_token};
|
||||
<<"Bearer ", Token/binary>> ->
|
||||
case eventhub_auth:verify_user_token(Token) of
|
||||
{ok, UserId, _Role} -> {ok, UserId};
|
||||
{error, _} -> {error, invalid_token}
|
||||
end;
|
||||
_ ->
|
||||
{error, invalid_header}
|
||||
end.
|
||||
|
||||
is_owner(UserId, CalendarId) ->
|
||||
case mnesia:dirty_read({calendar, CalendarId}) of
|
||||
[#calendar{owner_id = UserId}] -> true;
|
||||
_ -> false
|
||||
end.
|
||||
|
||||
process_view(Req, CalendarId, Opts) ->
|
||||
Qs = cowboy_req:parse_qs(Req),
|
||||
MonthBin = case lists:keyfind(<<"month">>, 1, Qs) of
|
||||
{<<"month">>, Value} -> Value;
|
||||
false -> undefined
|
||||
end,
|
||||
case MonthBin of
|
||||
undefined ->
|
||||
cowboy_req:reply(400,
|
||||
#{<<"content-type">> => <<"application/json">>},
|
||||
jsx:encode(#{error => <<"Missing 'month' parameter">>}),
|
||||
Req),
|
||||
{ok, Req, Opts};
|
||||
_ ->
|
||||
case binary:split(MonthBin, <<"-">>) of
|
||||
[YearStr, MonthStr] ->
|
||||
Year = binary_to_integer(YearStr),
|
||||
Month = binary_to_integer(MonthStr),
|
||||
Events = fetch_events(CalendarId, Year, Month),
|
||||
Html = calendar_html_renderer:render_month(Year, Month, Events),
|
||||
Req2 = cowboy_req:reply(200,
|
||||
#{<<"content-type">> => <<"text/html">>,
|
||||
<<"cache-control">> => <<"public, max-age=86400">>},
|
||||
Html,
|
||||
Req),
|
||||
{ok, Req2, Opts};
|
||||
_ ->
|
||||
cowboy_req:reply(400,
|
||||
#{<<"content-type">> => <<"application/json">>},
|
||||
jsx:encode(#{error => <<"Invalid 'month' format. Use YYYY-MM">>}),
|
||||
Req),
|
||||
{ok, Req, Opts}
|
||||
end
|
||||
end.
|
||||
|
||||
fetch_events(CalendarId, Year, Month) ->
|
||||
IsHot = is_hot(Year, Month),
|
||||
if IsHot ->
|
||||
fetch_hot_events(CalendarId, Year, Month);
|
||||
true ->
|
||||
fetch_archive_events(CalendarId, Year, Month)
|
||||
end.
|
||||
|
||||
is_hot(Year, Month) ->
|
||||
Current = calendar:local_time(),
|
||||
Target = {{Year, Month, 1}, {0,0,0}},
|
||||
calendar:datetime_to_gregorian_seconds(Current)
|
||||
- calendar:datetime_to_gregorian_seconds(Target) < 30*86400.
|
||||
|
||||
fetch_hot_events(CalendarId, Year, Month) ->
|
||||
Start = {{Year, Month, 1}, {0,0,0}},
|
||||
End = {{Year, Month, calendar:last_day_of_the_month(Year, Month)}, {23,59,59}},
|
||||
mnesia:dirty_select(event,
|
||||
[{#event{calendar_id = CalendarId, start_time = '$1', _ = '_'},
|
||||
[{'>=','$1', {const, Start}}, {'=<','$1', {const, End}}],
|
||||
['$_']}]).
|
||||
|
||||
fetch_archive_events(CalendarId, Year, Month) ->
|
||||
DayStr = io_lib:format("~4.10.0B~2.10.0B", [Year, Month]),
|
||||
case archive_manager:get_archive_node(lists:flatten(DayStr)) of
|
||||
{ok, Node} ->
|
||||
rpc:call(Node, archive_fetcher, fetch, [CalendarId, Year, Month]);
|
||||
{error, _} ->
|
||||
[]
|
||||
end.
|
||||
Reference in New Issue
Block a user