-module(eventhub_app). -behaviour(application). -export([start/2, stop/1]). start(_StartType, _StartArgs) -> pg:start_link(), application:ensure_all_started(mnesia), application:ensure_all_started(cowboy), case infra_sup:start_link() of {ok, Pid} -> ok = infra_mnesia:init_tables(), ok = infra_mnesia:wait_for_tables(), connect_nodes(), init_default_superadmin(), start_http(), % Пользовательский API (8080) start_admin_http(), % Административный API (8445) application:ensure_all_started(prometheus), application:ensure_all_started(prometheus_cowboy), {ok, Pid}; Error -> Error end. stop(_State) -> ok. %% =================================================================== %% Пользовательский HTTP (порт 8080) — только публичные эндпоинты %% =================================================================== start_http() -> Port = application:get_env(eventhub, http_port, 8080), Dispatch = cowboy_router:compile([ {'_', [ {"/metrics/[:registry]", prometheus_cowboy2_handler, []}, {"/health", handler_health, []}, {"/v1/register", handler_register, []}, {"/v1/login", handler_login, []}, {"/v1/refresh", handler_refresh, []}, {"/v1/user/me", handler_user_me, []}, {"/v1/user/bookings", handler_user_bookings, []}, {"/v1/user/reviews", handler_user_reviews, []}, {"/v1/search", handler_search, []}, {"/v1/calendars", handler_calendars, []}, {"/v1/calendars/:id", handler_calendar_by_id, []}, {"/v1/calendars/:calendar_id/events", handler_events, []}, {"/v1/events/:id", handler_event_by_id, []}, {"/v1/events/:id/occurrences", handler_event_occurrences, []}, {"/v1/events/:id/occurrences/:start_time", handler_event_occurrences, []}, {"/v1/events/:id/bookings", handler_bookings, []}, {"/v1/bookings/:id", handler_booking_by_id, []}, {"/v1/reviews", handler_reviews, []}, {"/v1/reviews/:id", handler_review_by_id, []}, {"/v1/reports", handler_reports, []}, {"/v1/tickets", handler_tickets, []}, {"/v1/tickets/:id", handler_ticket_by_id, []}, {"/v1/subscription", handler_subscription, []} ]} ]), Middlewares = [cowboy_router, cowboy_handler], Env = #{dispatch => Dispatch}, cowboy:start_clear(http, [{port, Port}], #{env => Env, middlewares => Middlewares}), io:format("HTTP server started on port ~p~n", [Port]). %% =================================================================== %% Административный HTTP (порт 8445) — все админские эндпоинты %% =================================================================== start_admin_http() -> Port = application:get_env(eventhub, admin_http_port, 8445), Dispatch = cowboy_router:compile([ {'_', [ % ================== БАЗОВЫЕ ================== {"/v1/admin/health", admin_handler_health, []}, {"/v1/admin/stats", admin_handler_stats, []}, {"/v1/admin/login", admin_handler_login, []}, % ================== ПОЛЬЗОВАТЕЛИ ================== {"/v1/admin/users", admin_handler_users, []}, {"/v1/admin/users/:id", admin_handler_user_by_id, []}, % ================== ОТЧЁТЫ ================== {"/v1/admin/reports", admin_handler_reports, []}, {"/v1/admin/reports/:id", admin_handler_report_by_id, []}, % ================== ОТЗЫВЫ ================== {"/v1/admin/reviews/:id", admin_handler_reviews, []}, % ================== БАН-СЛОВА ================== {"/v1/admin/banned-words", admin_handler_banned_words, []}, {"/v1/admin/banned-words/:word", admin_handler_banned_words, []}, % ================== ТИКЕТЫ ================== {"/v1/admin/tickets/stats", admin_handler_ticket_stats, []}, {"/v1/admin/tickets/:id", admin_handler_ticket_by_id, []}, {"/v1/admin/tickets", admin_handler_tickets, []}, % ================== ПОДПИСКИ ================== {"/v1/admin/subscriptions", admin_handler_subscriptions, []}, {"/v1/admin/subscriptions/:id", admin_handler_subscriptions, []}, % ================== МОДЕРАЦИЯ (общий маршрут) ================== {"/v1/admin/:target_type/:id", admin_handler_moderation, []}, % ================== Управление ролями (только для superadmin) ================== {"/v1/admin/me", admin_handler_me, []}, {"/v1/admin/admins", admin_handler_admins, []}, {"/v1/admin/admins/:id", admin_handler_admins, []}, {"/v1/admin/audit", admin_handler_audit, []} ]} ]), Middlewares = [cowboy_router, cowboy_handler], Env = #{dispatch => Dispatch}, cowboy:start_clear(admin_http, [{port, Port}], #{env => Env, middlewares => Middlewares}), io:format("Admin HTTP server started on port ~p~n", [Port]), % WebSocket для пользователей WsDispatch = cowboy_router:compile([{'_', [{"/ws", ws_handler, []}]}]), cowboy:start_clear(ws, [{port, 8081}], #{env => #{dispatch => WsDispatch}}), % WebSocket для админов AdminWsDispatch = cowboy_router:compile([{'_', [{"/admin/ws", admin_ws_handler, []}]}]), cowboy:start_clear(admin_ws, [{port, 8446}], #{env => #{dispatch => AdminWsDispatch}}), io:format("WebSocket started on ports 8081 (user) and 8446 (admin)~n"). %% =================================================================== %% Ручное подключение к нодам кластера (запасной вариант) %% =================================================================== connect_nodes() -> case os:getenv("JOIN_NODES") of false -> ok; NodesStr -> Nodes = [list_to_atom(string:trim(N)) || N <- string:tokens(NodesStr, ",")], lists:foreach(fun(Node) -> case net_kernel:connect_node(Node) of true -> io:format("Connected to ~s~n", [Node]); false -> io:format("ERROR: Failed to connect to ~s~n", [Node]); ignored -> ok end end, Nodes) end. init_default_superadmin() -> case core_admin:list_all() of [] -> AdminEmail = os:getenv("ADMIN_EMAIL", "admin@eventhub.local"), AdminPassword = os:getenv("ADMIN_PASSWORD", "123456"), {ok, _Admin} = core_admin:create( list_to_binary(AdminEmail), list_to_binary(AdminPassword), superadmin ), io:format("Default superadmin created: ~s~n", [AdminEmail]); _ -> io:format("Superadmin already exists. Skipping creation.~n") end.