diff --git a/big_tests/tests/domain_rest_helper.erl b/big_tests/tests/domain_rest_helper.erl index b41d77e8c1..dc3bddd032 100644 --- a/big_tests/tests/domain_rest_helper.erl +++ b/big_tests/tests/domain_rest_helper.erl @@ -11,6 +11,7 @@ putt_domain_with_custom_body/2, rest_select_domain/2, rest_delete_domain/3, + request_delete_domain/3, delete_custom/4, patch_custom/4]). @@ -73,6 +74,13 @@ rest_delete_domain(Config, Domain, HostType) -> creds => make_creds(Config), body => Params }). +request_delete_domain(Config, Domain, HostType) -> + Params = #{<<"host_type">> => HostType, <<"request">> => true}, + rest_helper:make_request(#{ role => admin, method => <<"DELETE">>, + path => domain_path(Domain), + creds => make_creds(Config), + body => Params }). + delete_custom(Config, Role, Path, Body) -> rest_helper:make_request(#{ role => Role, method => <<"DELETE">>, path => Path, creds => make_creds(Config), diff --git a/src/domain/mongoose_domain_api.erl b/src/domain/mongoose_domain_api.erl index 15c2076908..ca165592e9 100644 --- a/src/domain/mongoose_domain_api.erl +++ b/src/domain/mongoose_domain_api.erl @@ -11,6 +11,7 @@ %% domain API -export([insert_domain/2, delete_domain/2, + request_delete_domain/2, disable_domain/1, enable_domain/1, get_domain_host_type/1, @@ -30,7 +31,8 @@ get_all_subdomains_for_domain/1]). %% Helper for remove_domain --export([remove_domain_wrapper/3]). +-export([remove_domain_wrapper/3, + do_delete_domain_in_progress/3]). %% For testing -export([get_all_dynamic/0]). @@ -82,13 +84,20 @@ insert_domain(Domain, HostType) -> %% Domain should be nameprepped using `jid:nameprep'. -spec delete_domain(domain(), host_type()) -> delete_domain_return(). delete_domain(Domain, HostType) -> + do_delete_domain(Domain, HostType, sync). + +-spec request_delete_domain(domain(), host_type()) -> delete_domain_return(). +request_delete_domain(Domain, HostType) -> + do_delete_domain(Domain, HostType, async). + +do_delete_domain(Domain, HostType, RequestType) -> case check_domain(Domain, HostType) of ok -> Res0 = check_db(mongoose_domain_sql:set_domain_for_deletion(Domain, HostType)), case Res0 of ok -> delete_domain_password(Domain), - do_delete_domain_in_progress(Domain, HostType); + do_delete_domain_in_progress(Domain, HostType, RequestType); Other -> Other end; @@ -98,8 +107,10 @@ delete_domain(Domain, HostType) -> %% This is ran only in the context of `do_delete_domain', %% so it can already skip some checks --spec do_delete_domain_in_progress(domain(), host_type()) -> delete_domain_return(). -do_delete_domain_in_progress(Domain, HostType) -> +-spec do_delete_domain_in_progress(domain(), host_type(), sync | async) -> delete_domain_return(). +do_delete_domain_in_progress(Domain, HostType, async) -> + mongoose_domain_db_cleaner:request_delete_domain(Domain, HostType); +do_delete_domain_in_progress(Domain, HostType, sync) -> case mongoose_hooks:remove_domain(HostType, Domain) of #{failed := []} -> check_db(mongoose_domain_sql:delete_domain(Domain, HostType)); diff --git a/src/domain/mongoose_domain_db_cleaner.erl b/src/domain/mongoose_domain_db_cleaner.erl index 17fc6c1813..ff2bc8e3e9 100644 --- a/src/domain/mongoose_domain_db_cleaner.erl +++ b/src/domain/mongoose_domain_db_cleaner.erl @@ -4,7 +4,7 @@ -include("mongoose_logger.hrl"). -export([start/1, stop/0]). --export([start_link/1]). +-export([start_link/1, request_delete_domain/2]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, @@ -37,6 +37,10 @@ stop() -> start_link(Opts) -> gen_server:start_link({local, ?MODULE}, ?MODULE, Opts, []). +-spec request_delete_domain(jid:lserver(), mongooseim:host_type()) -> ok. +request_delete_domain(Domain, HostType) -> + gen_server:cast(?MODULE, {delete_domain, Domain, HostType}). + %% --------------------------------------------------------------------------- %% Server callbacks @@ -51,6 +55,8 @@ handle_call(Request, From, State) -> ?UNEXPECTED_CALL(Request, From), {reply, ok, State}. +handle_cast({delete_domain, Domain, HostType}, State) -> + {noreply, handle_delete_domain(Domain, HostType, State)}; handle_cast(Msg, State) -> ?UNEXPECTED_CAST(Msg), {noreply, State}. @@ -90,3 +96,13 @@ schedule_removal(State = #{max_age := MaxAge}) -> handle_timeout(_TimerRef, {do_removal, LastEventId}, State) -> mongoose_domain_sql:delete_events_older_than(LastEventId), State. + +handle_delete_domain(Domain, HostType, State) -> + try + mongoose_domain_api:do_delete_domain_in_progress(Domain, HostType, sync) + catch Class:Reason:Stacktrace -> + ?LOG_ERROR(#{what => domain_deletion_failed, + domain => Domain, host_type => HostType, + class => Class, reason => Reason, stacktrace => Stacktrace}) + end, + State. diff --git a/src/domain/mongoose_domain_sql.erl b/src/domain/mongoose_domain_sql.erl index 7844d4a1f1..b474cfff51 100644 --- a/src/domain/mongoose_domain_sql.erl +++ b/src/domain/mongoose_domain_sql.erl @@ -340,6 +340,8 @@ set_status(Domain, Status) -> case mongoose_domain_core:is_host_type_allowed(HostType) of false -> {error, unknown_host_type}; + true when deleting =:= CurrentStatus -> + {error, domain_deleted}; true when Status =:= CurrentStatus -> ok; true -> diff --git a/src/mongoose_admin_api/mongoose_admin_api_domain.erl b/src/mongoose_admin_api/mongoose_admin_api_domain.erl index affede9ca6..2e854a6bdf 100644 --- a/src/mongoose_admin_api/mongoose_admin_api_domain.erl +++ b/src/mongoose_admin_api/mongoose_admin_api_domain.erl @@ -11,7 +11,8 @@ allowed_methods/2, to_json/2, from_json/2, - delete_resource/2]). + delete_resource/2, + delete_completed/2]). -ignore_xref([to_json/2, from_json/2]). @@ -69,6 +70,12 @@ from_json(Req, State) -> delete_resource(Req, State) -> try_handle_request(Req, State, fun handle_delete/2). +-spec delete_completed(req(), state()) -> {boolean(), req(), state()}. +delete_completed(Req, #{deletion := in_process} = State) -> + {false, Req, State}; +delete_completed(Req, State) -> + {true, Req, State}. + %% Internal functions handle_get(Req, State) -> @@ -128,7 +135,24 @@ handle_delete(Req, State) -> Bindings = cowboy_req:bindings(Req), Domain = get_domain(Bindings), Args = parse_body(Req), - HostType = get_host_type(Args), + handle_delete(Req, State, Domain, Args). + +handle_delete(Req, State, Domain, #{host_type := HostType, request := true}) -> + async_delete(Req, State, Domain, HostType); +handle_delete(Req, State, Domain, #{host_type := HostType}) -> + sync_delete(Req, State, Domain, HostType); +handle_delete(_Req, _State, _Domain, #{}) -> + throw_error(bad_request, <<"'host_type' field is missing">>); +handle_delete(_Req, _State, _Domain, {error, empty}) -> + throw_error(bad_request, <<"body is empty">>); +handle_delete(_Req, _State, _Domain, {error, _}) -> + throw_error(bad_request, <<"failed to parse JSON">>). + +async_delete(Req, State, Domain, HostType) -> + mongoose_domain_api:request_delete_domain(Domain, HostType), + {true, Req, State#{deletion => in_process}}. + +sync_delete(Req, State, Domain, HostType) -> case mongoose_domain_api:delete_domain(Domain, HostType) of ok -> {true, Req, State};