From 9ea248753ea0728c9e44d924d42e62ba190df9d5 Mon Sep 17 00:00:00 2001 From: Lars Gebhardt-Kusche Date: Wed, 24 Jun 2026 00:50:15 +0200 Subject: [PATCH] qwewqe --- modules/mining-checker/assets/js/app.js | 61 ++++++++++- modules/mining-checker/src/Api/Router.php | 34 +++++++ public/admin/users/index.php | 13 ++- public/assets/apps/user-management/app.css | 56 +++++++++++ public/assets/apps/user-management/app.js | 111 +++++++++++++++++++++ src/App/LdapProvisioner.php | 108 ++++++++++++++++++++ src/App/RegistrationService.php | 8 ++ 7 files changed, 387 insertions(+), 4 deletions(-) diff --git a/modules/mining-checker/assets/js/app.js b/modules/mining-checker/assets/js/app.js index 14cd972e..87a73496 100644 --- a/modules/mining-checker/assets/js/app.js +++ b/modules/mining-checker/assets/js/app.js @@ -932,6 +932,7 @@ note: '', }); const [payoutModalOpen, setPayoutModalOpen] = useState(false); + const [payoutMode, setPayoutMode] = useState('partial'); const [walletTransferRows, setWalletTransferRows] = useState([]); const [walletWithdrawalForm, setWalletWithdrawalForm] = useState({ withdrawal_at: '', @@ -1006,6 +1007,21 @@ const currentMinerOffers = Array.isArray(currentSettings.miner_offers) ? currentSettings.miner_offers : []; const currentPurchasedMiners = Array.isArray(currentSettings.purchased_miners) ? currentSettings.purchased_miners : []; const currentMiningCurrency = String((latest && latest.coin_currency) || currentSettings.crypto_currency || 'DOGE').toUpperCase(); + const latestMeasurementDate = latest && latest.measured_at ? parseStoredUtcDate(latest.measured_at) : null; + const latestMeasurementAgeMs = latestMeasurementDate ? (Date.now() - latestMeasurementDate.getTime()) : null; + const canTransferAll = !!latestMeasurementDate + && Number.isFinite(latestMeasurementAgeMs) + && latestMeasurementAgeMs >= 0 + && latestMeasurementAgeMs <= 180000 + && Number(latest?.coins_total_visible ?? latest?.coins_total ?? 0) > 0; + const fullTransferCoins = Number(latest?.coins_total_visible ?? latest?.coins_total ?? 0); + const transferAllHint = !latestMeasurementDate + ? 'Kein Mining-Upload vorhanden.' + : latestMeasurementAgeMs < 0 + ? `Letzter Upload ${fmtDate(latest.measured_at)} liegt in der Zukunft.` + : canTransferAll + ? `Verwendet den letzten Upload von ${fmtDate(latest.measured_at)} mit ${fmtNumber(fullTransferCoins, 6)} ${currentMiningCurrency}.` + : `Alles uebertragen ist nur bis 3 Minuten nach dem letzten Upload moeglich. Letzter Upload: ${fmtDate(latest.measured_at)}.`; const latestWalletSnapshot = currentWalletSnapshots.length ? currentWalletSnapshots[0] : null; const currentWalletMiningBalance = walletAssetBalance(latestWalletSnapshot, currentMiningCurrency); const renewableOfferIds = new Set( @@ -1949,6 +1965,36 @@ } } + async function submitFullPayout() { + if (!canTransferAll) { + setError(transferAllHint); + return; + } + + setSaving(true); + setError(''); + setMessage(''); + try { + const savedPayout = await request(`${apiBase}/projects/${encodeURIComponent(projectKey)}/payouts`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + transfer_mode: 'full_latest', + note: 'Kompletttransfer aus letztem Upload', + }), + timeoutMs: 8000, + }); + applySavedPayout(savedPayout); + setWalletTransferRows((previous) => previous.filter((row) => String(row.id) !== String(savedPayout.id)).concat(savedPayout).sort((left, right) => String(left?.payout_at || '').localeCompare(String(right?.payout_at || '')))); + invalidateProjectBootstrapCache(projectKey); + setMessage('Gesamter aktueller Miner-Bestand wurde ins NC Wallet uebertragen.'); + } catch (err) { + setError(err.message); + } finally { + setSaving(false); + } + } + async function deleteWalletTransfer(transfer) { if (!transfer || !transfer.id) { return; @@ -2758,11 +2804,20 @@ : h('div', { className: 'mc-empty' }, 'Noch keine Wallet-Assets erkannt.')), panel('Wallet-Transfers', 'Transfers zum hier verfolgten Wallet erhoehen den Wallet-Bestand und reduzieren den sichtbaren Miner-Bestand. Reinvestierte Krypto-Minerkaeufe werden davon automatisch wieder abgezogen.', [ h('div', { key: 'actions', className: 'mc-inline-row' }, [ + h('button', { + key: 'transfer-all', + type: 'button', + className: 'mc-button mc-button--secondary', + onClick: submitFullPayout, + disabled: saving || !canTransferAll, + title: transferAllHint, + }, 'Alles uebertragen'), h('button', { key: 'add-transfer', type: 'button', className: 'mc-button mc-button--secondary', onClick: () => { + setPayoutMode('partial'); setPayoutForm((previous) => ({ payout_at: previous.payout_at || nowDateTimeLocalValue(), coins_amount: previous.coins_amount || '', @@ -2771,8 +2826,9 @@ })); setPayoutModalOpen(true); }, - }, 'Zu Wallet transferieren'), + }, 'Teiluebertragung'), ]), + h('div', { key: 'transfer-help', className: 'mc-text' }, transferAllHint), h('div', { key: 'transfer-list', className: 'mc-table-shell' }, [ h('table', { key: 'transfer-table', className: 'mc-table' }, [ h('thead', { key: 'head' }, h('tr', null, ['Zeit', 'Coins', 'Waehrung', 'Notiz', 'Aktion'].map((label) => h('th', { key: label }, label)))), @@ -3213,8 +3269,9 @@ ]), ]), ], () => setCostPlanModalOpen(false)) : null, - payoutModalOpen ? renderModal('Zu Wallet transferieren', [ + payoutModalOpen ? renderModal(payoutMode === 'partial' ? 'Teiluebertragung ins NC Wallet' : 'Zu Wallet transferieren', [ h('form', { key: 'form', className: 'mc-form', onSubmit: submitPayout }, [ + h('div', { key: 'hint', className: 'mc-text' }, 'Hier kannst du einen Teilbetrag vom aktuellen Miner-Bestand in das NC Wallet uebertragen.'), inputField('Transferzeitpunkt', 'datetime-local', payoutForm.payout_at, (value) => setPayoutForm({ ...payoutForm, payout_at: value })), inputField('Coins', 'number', payoutForm.coins_amount, (value) => setPayoutForm({ ...payoutForm, coins_amount: value }), '0.000001'), selectField('Waehrung', payoutForm.payout_currency, [currentCoinCurrency].concat(selectableCurrencies.map((currency) => currency.code).filter((code) => code !== currentCoinCurrency)), (value) => setPayoutForm({ ...payoutForm, payout_currency: value })), diff --git a/modules/mining-checker/src/Api/Router.php b/modules/mining-checker/src/Api/Router.php index c90524fb..2471f868 100644 --- a/modules/mining-checker/src/Api/Router.php +++ b/modules/mining-checker/src/Api/Router.php @@ -1788,6 +1788,40 @@ final class Router private function savePayout(string $projectKey, array $input): array { + $transferMode = $this->enumValue($input['transfer_mode'] ?? 'manual', ['manual', 'full_latest'], 'transfer_mode'); + if ($transferMode === 'full_latest') { + $measurements = $this->repository()->listRecentMeasurements($projectKey, 1); + $latestMeasurement = $measurements[0] ?? null; + if (!is_array($latestMeasurement)) { + throw new ApiException('Es ist kein aktueller Mining-Upload vorhanden.', 422); + } + + $latestTs = strtotime((string) ($latestMeasurement['measured_at'] ?? '')); + if ($latestTs === false || (time() - $latestTs) > 180) { + throw new ApiException('Alles uebertragen ist nur moeglich, wenn der letzte Upload hoechstens 3 Minuten alt ist.', 422); + } + + $settings = $this->settings($projectKey, [ + 'currencies' => false, + 'miner_offers' => false, + ]); + $summary = $this->analytics()->buildSummary($measurements, $settings, [], ['include_offer_scenarios' => false]); + $visibleCoins = (float) ($summary['payouts']['current_visible_coins'] ?? 0.0); + if ($visibleCoins <= 0) { + throw new ApiException('Es stehen keine Coins fuer einen Kompletttransfer zur Verfuegung.', 422); + } + + $coinCurrency = strtoupper(trim((string) ($latestMeasurement['coin_currency'] ?? $settings['crypto_currency'] ?? 'DOGE'))); + $payload = [ + 'payout_at' => (string) ($latestMeasurement['measured_at'] ?? ''), + 'coins_amount' => $visibleCoins, + 'payout_currency' => $coinCurrency !== '' ? $coinCurrency : 'DOGE', + 'note' => $this->optionalString($input['note'] ?? 'Kompletttransfer aus letztem Upload', 1000), + ]; + + return $this->repository()->savePayout($projectKey, $payload); + } + $payload = [ 'payout_at' => $this->requiredDateTime($input['payout_at'] ?? null, 'payout_at', $this->projectTimezone($projectKey)), 'coins_amount' => $this->requiredDecimal($input['coins_amount'] ?? null, 'coins_amount'), diff --git a/public/admin/users/index.php b/public/admin/users/index.php index 841b1f1d..9d856f9f 100644 --- a/public/admin/users/index.php +++ b/public/admin/users/index.php @@ -133,6 +133,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { $pendingRequests = $registration->pendingApprovals(); $availableGroups = $registration->availableGroups(); +$existingUsers = $registration->searchExistingUsers('', 250); $selected = $selectedId !== '' ? $registration->find($selectedId) : ($pendingRequests[0] ?? null); @@ -246,6 +247,8 @@ ob_start(); + +
@@ -337,7 +340,10 @@ ob_start();
@@ -376,7 +382,10 @@ ob_start();