Bill transfer storage monthly with a 15-day grace period before deletion.
Deploy Ladill Transfer / deploy (push) Successful in 51s

Files stay available while wallet renewals succeed each month. Failed renewals
keep files for TRANSFER_GRACE_PERIOD_DAYS (default 15) before automatic cleanup.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
isaacclad
2026-06-08 12:28:33 +00:00
co-authored by Cursor
parent 65b634bb0b
commit 311b5b40bb
23 changed files with 502 additions and 56 deletions
@@ -140,7 +140,8 @@ class ChunkedUploadController extends Controller
'id' => $transfer->id,
'title' => $transfer->title,
'public_url' => $transfer->qrCode?->publicUrl(),
'expires_at' => $transfer->expires_at?->toIso8601String(),
'paid_until' => $transfer->paid_until?->toIso8601String(),
'expires_at' => $transfer->paid_until?->toIso8601String(),
'files' => $transfer->files->map(fn ($file) => [
'name' => $file->original_name,
'size_bytes' => $file->size_bytes,
@@ -69,7 +69,8 @@ class TransferController extends Controller
'id' => $transfer->id,
'title' => $transfer->title,
'public_url' => $transfer->qrCode?->publicUrl(),
'expires_at' => $transfer->expires_at?->toIso8601String(),
'paid_until' => $transfer->paid_until?->toIso8601String(),
'expires_at' => $transfer->paid_until?->toIso8601String(),
'files' => $transfer->files->map(fn ($file) => [
'name' => $file->original_name,
'size_bytes' => $file->size_bytes,
@@ -52,10 +52,7 @@ class AccountController extends Controller
$activeTransfers = Transfer::query()
->where('user_id', $user->id)
->where('status', Transfer::STATUS_ACTIVE)
->where(function ($query) {
$query->whereNull('expires_at')->orWhere('expires_at', '>', now());
})
->accessible()
->orderByDesc('storage_bytes')
->get();
@@ -21,7 +21,7 @@ class OverviewController extends Controller
$transfers = Transfer::query()
->where('user_id', $account->id)
->where('status', Transfer::STATUS_ACTIVE);
->accessible();
$activeCount = (clone $transfers)->count();
$storageBytes = (int) (clone $transfers)->sum('storage_bytes');
@@ -37,14 +37,14 @@ class OverviewController extends Controller
$expiringSoon = Transfer::query()
->where('user_id', $account->id)
->where('status', Transfer::STATUS_ACTIVE)
->whereNotNull('expires_at')
->whereBetween('expires_at', [now(), now()->addDays(7)])
->accessible()
->whereNotNull('paid_until')
->whereBetween('paid_until', [now(), now()->addDays(7)])
->count();
$recentTransfers = Transfer::query()
->where('user_id', $account->id)
->where('status', Transfer::STATUS_ACTIVE)
->accessible()
->with('qrCode')
->latest()
->limit(6)
@@ -31,7 +31,7 @@ class TransferController extends Controller
$transfers = Transfer::query()
->where('user_id', $account->id)
->where('status', Transfer::STATUS_ACTIVE)
->accessible()
->with(['qrCode', 'files'])
->latest()
->paginate(20);
@@ -42,9 +42,9 @@ class TransferController extends Controller
public function create(): View
{
return view('transfer.transfers.create', [
'defaultRetentionDays' => (int) config('transfer.default_retention_days', 30),
'maxFiles' => (int) config('transfer.max_files_per_transfer', 20),
'pricePerGb' => (float) config('transfer.price_per_gb_month', 0.30),
'gracePeriodDays' => (int) config('transfer.grace_period_days', 15),
]);
}
@@ -62,7 +62,6 @@ class TransferController extends Controller
'title' => 'required|string|max:120',
'message' => 'nullable|string|max:2000',
'password' => 'nullable|string|min:4|max:64',
'retention_days' => 'nullable|integer|min:1|max:365',
'files' => 'nullable|array',
'files.*' => $fileRules,
'upload_ids' => 'nullable|array',