diff --git a/plugins/traction_innkeeper/traction_innkeeper/v1_0/innkeeper/routes.py b/plugins/traction_innkeeper/traction_innkeeper/v1_0/innkeeper/routes.py index c763a8d14..89eac03c2 100644 --- a/plugins/traction_innkeeper/traction_innkeeper/v1_0/innkeeper/routes.py +++ b/plugins/traction_innkeeper/traction_innkeeper/v1_0/innkeeper/routes.py @@ -809,7 +809,35 @@ async def innkeeper_tenant_delete(request: web.BaseRequest): return web.json_response({"success": f"Tenant {tenant_id} soft deleted."}) else: raise web.HTTPNotFound(reason=f"Tenant {tenant_id} not found.") - + + +@docs( + tags=[SWAGGER_CATEGORY], +) +@match_info_schema(TenantIdMatchInfoSchema()) +@response_schema(TenantRecordSchema(), 200, description="") +@innkeeper_only +@error_handler +async def innkeeper_tenant_hard_delete(request: web.BaseRequest): + context: AdminRequestContext = request["context"] + tenant_id = request.match_info["tenant_id"] + + mgr = context.inject(TenantManager) + profile = mgr.profile + async with profile.session() as session: + rec = await TenantRecord.retrieve_by_id(session, tenant_id) + if rec: + multitenant_mgr = context.profile.inject(BaseMultitenantManager) + wallet_id = rec.wallet_id + wallet_record = await WalletRecord.retrieve_by_id(session, wallet_id) + + await multitenant_mgr.remove_wallet(wallet_id) + await rec.delete_record(session) + LOGGER.info("Tenant %s rd deleted.", tenant_id) + return web.json_response({"success": f"Tenant {tenant_id} hard deleted."}) + else: + raise web.HTTPNotFound(reason=f"Tenant {tenant_id} not found.") + @docs( tags=[SWAGGER_CATEGORY], @@ -822,10 +850,10 @@ async def innkeeper_tenant_delete(request: web.BaseRequest): async def innkeeper_tenant_restore(request: web.BaseRequest): context: AdminRequestContext = request["context"] tenant_id = request.match_info["tenant_id"] - + mgr = context.inject(TenantManager) profile = mgr.profile - + async with profile.session() as session: rec = await TenantRecord.retrieve_by_id(session, tenant_id) if rec: @@ -837,7 +865,7 @@ async def innkeeper_tenant_restore(request: web.BaseRequest): return web.json_response({"success": f"Tenant {tenant_id} restored."}) else: raise web.HTTPNotFound(reason=f"Tenant {tenant_id} not found.") - + @docs(tags=[SWAGGER_CATEGORY], summary="Create API Key Record") @request_schema(TenantAuthenticationsApiRequestSchema()) @@ -960,9 +988,7 @@ async def innkeeper_config_handler(request: web.BaseRequest): profile = mgr.profile config = { - key: ( - profile.context.settings[key] - ) + key: (profile.context.settings[key]) for key in profile.context.settings if key not in [ @@ -975,9 +1001,13 @@ async def innkeeper_config_handler(request: web.BaseRequest): ] } try: - del config["plugin_config"]["traction_innkeeper"]["innkeeper_wallet"]["wallet_key"] + del config["plugin_config"]["traction_innkeeper"]["innkeeper_wallet"][ + "wallet_key" + ] except KeyError as e: - LOGGER.warn(f"The key to be removed: '{e.args[0]}' is missing from the dictionary.") + LOGGER.warn( + f"The key to be removed: '{e.args[0]}' is missing from the dictionary." + ) config["version"] = __version__ return web.json_response({"config": config}) @@ -1033,6 +1063,9 @@ async def register(app: web.Application): ), web.put("/innkeeper/tenants/{tenant_id}/config", tenant_config_update), web.delete("/innkeeper/tenants/{tenant_id}", innkeeper_tenant_delete), + web.delete( + "/innkeeper/tenants/{tenant_id}/hard", innkeeper_tenant_hard_delete + ), web.put("/innkeeper/tenants/{tenant_id}/restore", innkeeper_tenant_restore), web.get( "/innkeeper/default-config", diff --git a/plugins/traction_innkeeper/traction_innkeeper/v1_0/tenant/routes.py b/plugins/traction_innkeeper/traction_innkeeper/v1_0/tenant/routes.py index 993403793..eaf84cdbb 100644 --- a/plugins/traction_innkeeper/traction_innkeeper/v1_0/tenant/routes.py +++ b/plugins/traction_innkeeper/traction_innkeeper/v1_0/tenant/routes.py @@ -80,6 +80,7 @@ class TenantLedgerIdConfigSchema(OpenAPISchema): required=True, ) + @web.middleware async def setup_tenant_context(request: web.Request, handler): """Middle ware to extract tenant_id and provide it to log formatter @@ -465,6 +466,55 @@ async def tenant_server_config_handler(request: web.BaseRequest): return web.json_response({"config": config}) +@docs( + tags=[SWAGGER_CATEGORY], +) +@response_schema(TenantRecordSchema(), 200, description="") +@error_handler +async def tenant_delete_soft(request: web.BaseRequest): + context: AdminRequestContext = request["context"] + wallet_id = context.profile.settings.get("wallet.id") + + mgr = context.inject(TenantManager) + profile = mgr.profile + async with profile.session() as session: + rec = await TenantRecord.query_by_wallet_id(session, wallet_id) + if rec: + await rec.soft_delete(session) + LOGGER.info("Tenant %s soft deleted.", rec.tenant_id) + return web.json_response( + {"success": f"Tenant {rec.tenant_id} soft deleted."} + ) + else: + raise web.HTTPNotFound(reason=f"Tenant not found.") + + +@docs( + tags=[SWAGGER_CATEGORY], +) +@response_schema(TenantRecordSchema(), 200, description="") +@error_handler +async def tenant_delete(request: web.BaseRequest): + context: AdminRequestContext = request["context"] + wallet_id = context.profile.settings.get("wallet.id") + + mgr = context.inject(TenantManager) + profile = mgr.profile + async with profile.session() as session: + rec = await TenantRecord.query_by_wallet_id(session, wallet_id) + if rec: + multitenant_mgr = context.profile.inject(BaseMultitenantManager) + + await multitenant_mgr.remove_wallet(rec.wallet_id) + await rec.delete_record(session) + LOGGER.info("Tenant %s rd deleted.", rec.tenant_id) + return web.json_response(rec.serialize()) + else: + raise web.HTTPNotFound( + reason=f"Tenant with wallet id {wallet_id} not found." + ) + + async def register(app: web.Application): """Register routes.""" LOGGER.info("> registering routes") @@ -499,6 +549,8 @@ async def register(app: web.Application): tenant_server_config_handler, allow_head=False, ), + web.delete("/tenant/hard", tenant_delete), + web.delete("/tenant/soft", tenant_delete_soft), ] ) LOGGER.info("< registering routes") diff --git a/services/tenant-ui/frontend/src/assets/primevueOverrides/buttons.scss b/services/tenant-ui/frontend/src/assets/primevueOverrides/buttons.scss index 7faeeb697..68d980667 100644 --- a/services/tenant-ui/frontend/src/assets/primevueOverrides/buttons.scss +++ b/services/tenant-ui/frontend/src/assets/primevueOverrides/buttons.scss @@ -2,7 +2,7 @@ button { &.p-button { - &:not(.p-button-rounded):not(.p-button-text):not(.p-button-link) { + &:not(.p-button-rounded):not(.p-danger):not(.p-button-text):not(.p-button-link) { padding: 0.5rem 1rem; background-color: $tenant-ui-secondary-color; border-color: $tenant-ui-secondary-color; diff --git a/services/tenant-ui/frontend/src/components/innkeeper/tenants/deleteTenant/ConfirmTenantDeletion.vue b/services/tenant-ui/frontend/src/components/innkeeper/tenants/deleteTenant/ConfirmTenantDeletion.vue index 88a658761..4b20e2930 100644 --- a/services/tenant-ui/frontend/src/components/innkeeper/tenants/deleteTenant/ConfirmTenantDeletion.vue +++ b/services/tenant-ui/frontend/src/components/innkeeper/tenants/deleteTenant/ConfirmTenantDeletion.vue @@ -14,11 +14,34 @@ placeholder="Type the tenant name here" class="w-full mb-4" /> +
+
+ + +
+
+ + +
+
+
+ +
+
{{ $t('common.warning') }}
+
+ {{ $t('tenants.settings.tenantDeletionWarning') }} +
+
+