mirror of
https://github.com/jhd3197/ServerKit.git
synced 2026-10-28 15:47:50 +09:00
refactor(admin): move the owned-apps delete guard behind the service door
The user-delete guard 79a78c3c added queried Application straight from
the controller, tripping the API controller boundary ratchet. The count
now lives in application_restore.applications_owned_by — the module
that owns Application's recycle-bin semantics and documents why
tombstoned rows block too.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
27e6c0ba9a
commit
cc0597bf28
2 files changed
+15
-6
No files matched your search
@@ -216,12 +216,10 @@ def delete_user(user_id):
|
||||
if admin_count <= 1:
|
||||
return jsonify({'error': 'Cannot delete the last admin'}), 400
|
||||
|
||||
# Applications never ride a user delete: their user_id is NOT NULL, but the
|
||||
# rows own containers and volumes that must go through app delete + Recycle
|
||||
# Bin purge — never a silent cascade. Unfiltered on purpose: a tombstoned
|
||||
# app still holds the FK, so it blocks too.
|
||||
from app.models import Application
|
||||
owned = Application.query.filter_by(user_id=user.id).count()
|
||||
# Applications never ride a user delete — see applications_owned_by for
|
||||
# why tombstoned rows block too.
|
||||
from app.services.application_restore import applications_owned_by
|
||||
owned = applications_owned_by(user.id)
|
||||
if owned:
|
||||
return jsonify({'error': f'User still owns {owned} application(s), '
|
||||
'including any in the Recycle Bin — delete or '
|
||||
|
||||
@@ -33,6 +33,17 @@ from app.services.cron_service import CronService
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def applications_owned_by(user_id):
|
||||
"""Count of Application rows a user owns — live AND tombstoned.
|
||||
|
||||
Applications never ride a user delete: their user_id is NOT NULL, but the
|
||||
rows own containers and volumes that must go through app delete + Recycle
|
||||
Bin purge — never a silent cascade. Unfiltered on purpose: a tombstoned
|
||||
app still holds the FK, so it blocks too.
|
||||
"""
|
||||
return Application.query.filter_by(user_id=user_id).count()
|
||||
|
||||
|
||||
def _live_name_clash(app):
|
||||
"""Another LIVE app already using this one's name, if any."""
|
||||
return (Application.query_active()
|
||||
|
||||
Reference in new issue
Block a user