Files
casadoc/public/userportal/api/account-delete.php
T
2026-07-27 21:04:07 +03:00

107 lines
4.2 KiB
PHP

<?php
require __DIR__ . '/_bootstrap.php';
/**
* @OA\Post(
* path="/account-delete.php",
* tags={"Auth"},
* summary="Delete the account and all related data",
* description="Immediate hard delete: documents on disk, homes, owners, sharing, tokens and the user row. Irreversible.",
* security={{"bearerAuth":{}}},
* @OA\RequestBody(required=true, @OA\JsonContent(
* required={"confirm"},
* @OA\Property(property="confirm", type="boolean", example=true,
* description="guard against an accidental call")
* )),
* @OA\Response(response=200, ref="#/components/responses/Success"),
* @OA\Response(response=401, ref="#/components/responses/Unauthorized"),
* @OA\Response(response=422, ref="#/components/responses/ValidationError")
* )
*/
require_method('POST');
$user = require_auth($pdo);
$config = require __DIR__ . '/config.php';
if (empty(body()['confirm'])) {
json_error(422, 'Validation failed', ['confirm' => ['Required']]);
}
$userId = (int) $user['id'];
$email = (string) $user['email'];
// Ids owned by the user.
$stmt = $pdo->prepare('SELECT idhome FROM home WHERE iduser = ?');
$stmt->execute([$userId]);
$homeIds = $stmt->fetchAll(PDO::FETCH_COLUMN);
$stmt = $pdo->prepare('SELECT owner_id FROM property_owners WHERE user_id = ?');
$stmt->execute([$userId]);
$ownerIds = $stmt->fetchAll(PDO::FETCH_COLUMN);
// Collect files before the rows disappear; they are unlinked after a successful commit.
$files = [];
if ($homeIds || $ownerIds) {
$where = [];
$params = [];
if ($homeIds) {
$where[] = 'idhome IN (' . implode(',', array_fill(0, count($homeIds), '?')) . ')';
$params = array_merge($params, $homeIds);
}
if ($ownerIds) {
$where[] = 'owner_id IN (' . implode(',', array_fill(0, count($ownerIds), '?')) . ')';
$params = array_merge($params, $ownerIds);
}
$stmt = $pdo->prepare('SELECT filename, entity_type, idhome FROM doc_storage WHERE ' . implode(' OR ', $where));
$stmt->execute($params);
foreach ($stmt->fetchAll() as $f) {
$isPerson = ($f['entity_type'] ?? '') === 'person' || empty($f['idhome']);
$files[] = ($isPerson ? $config['persondocs_dir'] : $config['homedocs_dir'])
. '/' . basename((string) $f['filename']);
}
}
$in = fn (array $ids) => implode(',', array_fill(0, count($ids), '?'));
$pdo->beginTransaction();
try {
// Sharing granted by the user, received by the user, or addressed to their e-mail.
$pdo->prepare('DELETE FROM home_sharing WHERE iduser = ? OR idshareduser = ? OR shared_email = ?')
->execute([$userId, $userId, $email]);
if ($homeIds) {
$pdo->prepare('DELETE FROM doc_storage WHERE idhome IN (' . $in($homeIds) . ')')->execute($homeIds);
$pdo->prepare('DELETE FROM home_owners WHERE home_id IN (' . $in($homeIds) . ')')->execute($homeIds);
$pdo->prepare('DELETE FROM home_sharing WHERE idhome IN (' . $in($homeIds) . ')')->execute($homeIds);
}
if ($ownerIds) {
$pdo->prepare('DELETE FROM doc_storage WHERE owner_id IN (' . $in($ownerIds) . ')')->execute($ownerIds);
$pdo->prepare('DELETE FROM home_owners WHERE owner_id IN (' . $in($ownerIds) . ')')->execute($ownerIds);
}
$pdo->prepare('DELETE FROM home WHERE iduser = ?')->execute([$userId]);
$pdo->prepare('DELETE FROM property_owners WHERE user_id = ?')->execute([$userId]);
// Credentials and sessions.
$pdo->prepare('DELETE FROM api_tokens WHERE user_id = ?')->execute([$userId]);
$pdo->prepare('DELETE FROM auth_sessions WHERE user_id = ?')->execute([$userId]);
$pdo->prepare('DELETE FROM auth_personal_access_tokens WHERE tokenable_id = ?')->execute([$userId]);
$pdo->prepare('DELETE FROM auth_password_resets WHERE email = ?')->execute([$email]);
// Cascades auth_social_logins, auth_user_activity, auth_announcements.
$pdo->prepare('DELETE FROM auth_users WHERE id = ?')->execute([$userId]);
$pdo->commit();
} catch (Throwable $e) {
$pdo->rollBack();
json_error(500, 'Account deletion failed');
}
// Files last: an orphaned file is harmless, a lost file after a rollback is not.
foreach ($files as $path) {
if (is_file($path)) {
@unlink($path);
}
}
json_ok();