sdsf
This commit is contained in:
@@ -179,6 +179,40 @@
|
|||||||
min-width: 240px;
|
min-width: 240px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#mining-checker-app .mc-history-stack {
|
||||||
|
display: grid;
|
||||||
|
gap: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mining-checker-app .mc-history-card {
|
||||||
|
display: grid;
|
||||||
|
gap: 14px;
|
||||||
|
padding: 18px;
|
||||||
|
border: 1px solid var(--mc-line);
|
||||||
|
border-radius: 16px;
|
||||||
|
background: var(--mc-surface);
|
||||||
|
box-shadow: 0 10px 24px rgba(15, 23, 42, 0.06);
|
||||||
|
}
|
||||||
|
|
||||||
|
#mining-checker-app .mc-history-asset-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mining-checker-app .mc-history-asset-card {
|
||||||
|
display: grid;
|
||||||
|
gap: 6px;
|
||||||
|
padding: 12px 14px;
|
||||||
|
border: 1px solid var(--mc-line);
|
||||||
|
border-radius: 14px;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
#mining-checker-app .mc-history-asset-card strong {
|
||||||
|
color: var(--mc-text);
|
||||||
|
}
|
||||||
|
|
||||||
#mining-checker-app .mc-asset-row {
|
#mining-checker-app .mc-asset-row {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 2px;
|
gap: 2px;
|
||||||
|
|||||||
@@ -305,7 +305,7 @@
|
|||||||
return 'Historie der letzten Uploads mit Performance- und Trendwerten.';
|
return 'Historie der letzten Uploads mit Performance- und Trendwerten.';
|
||||||
}
|
}
|
||||||
if (sectionId === 'wallet') {
|
if (sectionId === 'wallet') {
|
||||||
return 'Wallet-Snapshots, Transfers und erkannte Asset-Bestände auswerten.';
|
return 'NC-Wallet, Transfers, echte Wallet-Auszahlungen und erkannte Asset-Bestaende auswerten.';
|
||||||
}
|
}
|
||||||
if (sectionId === 'mining') {
|
if (sectionId === 'mining') {
|
||||||
return 'Miner, Angebote und operative Daten verwalten.';
|
return 'Miner, Angebote und operative Daten verwalten.';
|
||||||
@@ -472,6 +472,7 @@
|
|||||||
cost_plans: [],
|
cost_plans: [],
|
||||||
currencies: [],
|
currencies: [],
|
||||||
payouts: [],
|
payouts: [],
|
||||||
|
wallet_withdrawals: [],
|
||||||
miner_offers: [],
|
miner_offers: [],
|
||||||
purchased_miners: [],
|
purchased_miners: [],
|
||||||
measurement_rates: [],
|
measurement_rates: [],
|
||||||
@@ -922,6 +923,13 @@
|
|||||||
note: '',
|
note: '',
|
||||||
});
|
});
|
||||||
const [payoutModalOpen, setPayoutModalOpen] = useState(false);
|
const [payoutModalOpen, setPayoutModalOpen] = useState(false);
|
||||||
|
const [walletWithdrawalForm, setWalletWithdrawalForm] = useState({
|
||||||
|
withdrawal_at: '',
|
||||||
|
coins_amount: '',
|
||||||
|
withdrawal_currency: 'DOGE',
|
||||||
|
note: '',
|
||||||
|
});
|
||||||
|
const [walletWithdrawalModalOpen, setWalletWithdrawalModalOpen] = useState(false);
|
||||||
const [minerOfferForm, setMinerOfferForm] = useState({
|
const [minerOfferForm, setMinerOfferForm] = useState({
|
||||||
label: '',
|
label: '',
|
||||||
runtime_months: '',
|
runtime_months: '',
|
||||||
@@ -982,6 +990,7 @@
|
|||||||
];
|
];
|
||||||
const currentCostPlans = Array.isArray(currentSettings.cost_plans) ? currentSettings.cost_plans : [];
|
const currentCostPlans = Array.isArray(currentSettings.cost_plans) ? currentSettings.cost_plans : [];
|
||||||
const currentPayouts = Array.isArray(currentSettings.payouts) ? currentSettings.payouts : [];
|
const currentPayouts = Array.isArray(currentSettings.payouts) ? currentSettings.payouts : [];
|
||||||
|
const currentWalletWithdrawals = Array.isArray(currentSettings.wallet_withdrawals) ? currentSettings.wallet_withdrawals : [];
|
||||||
const currentWalletSnapshots = Array.isArray(payload?.wallet_snapshots) ? payload.wallet_snapshots : [];
|
const currentWalletSnapshots = Array.isArray(payload?.wallet_snapshots) ? payload.wallet_snapshots : [];
|
||||||
const currentMinerOffers = Array.isArray(currentSettings.miner_offers) ? currentSettings.miner_offers : [];
|
const currentMinerOffers = Array.isArray(currentSettings.miner_offers) ? currentSettings.miner_offers : [];
|
||||||
const currentPurchasedMiners = Array.isArray(currentSettings.purchased_miners) ? currentSettings.purchased_miners : [];
|
const currentPurchasedMiners = Array.isArray(currentSettings.purchased_miners) ? currentSettings.purchased_miners : [];
|
||||||
@@ -1946,6 +1955,61 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function submitWalletWithdrawal(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
setSaving(true);
|
||||||
|
setError('');
|
||||||
|
setMessage('');
|
||||||
|
try {
|
||||||
|
await request(`${apiBase}/projects/${encodeURIComponent(projectKey)}/wallet-withdrawals`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify(walletWithdrawalForm),
|
||||||
|
timeoutMs: 8000,
|
||||||
|
});
|
||||||
|
invalidateProjectBootstrapCache(projectKey);
|
||||||
|
setMessage('Wallet-Auszahlung gespeichert.');
|
||||||
|
setWalletWithdrawalForm({
|
||||||
|
withdrawal_at: '',
|
||||||
|
coins_amount: '',
|
||||||
|
withdrawal_currency: currentSettings.crypto_currency || 'DOGE',
|
||||||
|
note: '',
|
||||||
|
});
|
||||||
|
setWalletWithdrawalModalOpen(false);
|
||||||
|
await reloadBootstrapAfterMutation('Wallet-Auszahlung gespeichert.');
|
||||||
|
} catch (err) {
|
||||||
|
setError(err.message);
|
||||||
|
} finally {
|
||||||
|
setSaving(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deleteWalletWithdrawal(withdrawal) {
|
||||||
|
if (!withdrawal || !withdrawal.id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const label = `${fmtNumber(withdrawal.coins_amount, 6)} ${withdrawal.withdrawal_currency || ''}`.trim();
|
||||||
|
if (!window.confirm(`Diese Wallet-Auszahlung (${label}) wirklich loeschen?`)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setSaving(true);
|
||||||
|
setError('');
|
||||||
|
setMessage('');
|
||||||
|
try {
|
||||||
|
await request(`${apiBase}/projects/${encodeURIComponent(projectKey)}/wallet-withdrawals/${encodeURIComponent(withdrawal.id)}`, {
|
||||||
|
method: 'DELETE',
|
||||||
|
});
|
||||||
|
invalidateProjectBootstrapCache(projectKey);
|
||||||
|
await reloadBootstrapAfterMutation('Wallet-Auszahlung geloescht.');
|
||||||
|
} catch (err) {
|
||||||
|
setError(err.message);
|
||||||
|
} finally {
|
||||||
|
setSaving(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function submitMinerOffer(event) {
|
async function submitMinerOffer(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
setSaving(true);
|
setSaving(true);
|
||||||
@@ -2637,7 +2701,7 @@
|
|||||||
: [];
|
: [];
|
||||||
|
|
||||||
return h('div', { className: 'mc-stack' }, [
|
return h('div', { className: 'mc-stack' }, [
|
||||||
panel('Wallet-Bestaende', 'Der letzte Wallet-Snapshot zeigt alle erkannten Assets separat.', latestWalletAssets.length
|
panel('NC Wallet-Bestaende', 'Der letzte Snapshot zeigt alle erkannten Assets des internen NC Wallet separat.', latestWalletAssets.length
|
||||||
? h('div', { className: 'mc-asset-grid' }, latestWalletAssets.map(([code, asset]) => {
|
? h('div', { className: 'mc-asset-grid' }, latestWalletAssets.map(([code, asset]) => {
|
||||||
const balance = asset && typeof asset === 'object' ? asset.balance : asset;
|
const balance = asset && typeof asset === 'object' ? asset.balance : asset;
|
||||||
const priceAmount = asset && typeof asset === 'object' ? asset.price_amount : null;
|
const priceAmount = asset && typeof asset === 'object' ? asset.price_amount : null;
|
||||||
@@ -2651,33 +2715,6 @@
|
|||||||
]);
|
]);
|
||||||
}))
|
}))
|
||||||
: h('div', { className: 'mc-empty' }, 'Noch keine Wallet-Assets erkannt.')),
|
: h('div', { className: 'mc-empty' }, 'Noch keine Wallet-Assets erkannt.')),
|
||||||
panel('Wallet-Historie', 'Die letzten 10 Wallet-Uploads mit allen aus dem Screenshot gelesenen Assets.', h('div', { className: 'mc-table-shell' }, [
|
|
||||||
h('table', { key: 'wallet-table', className: 'mc-table' }, [
|
|
||||||
h('thead', { key: 'thead' }, h('tr', null, [
|
|
||||||
'Zeit', 'Quelle', 'Assets'
|
|
||||||
].map((label) => h('th', { key: label }, label)))),
|
|
||||||
h('tbody', { key: 'tbody' },
|
|
||||||
currentWalletSnapshots.length
|
|
||||||
? currentWalletSnapshots.slice(0, 10).map((row) => h('tr', { key: row.id }, [
|
|
||||||
h('td', { key: 'measured' }, fmtDate(row.measured_at)),
|
|
||||||
h('td', { key: 'source' }, row.source || 'manual'),
|
|
||||||
h('td', { key: 'assets' }, h('div', { className: 'mc-asset-list' }, Object.entries(row.balances_json || {}).map(([code, asset]) => {
|
|
||||||
const balance = asset && typeof asset === 'object' ? asset.balance : asset;
|
|
||||||
const priceAmount = asset && typeof asset === 'object' ? asset.price_amount : null;
|
|
||||||
const priceCurrency = asset && typeof asset === 'object' ? asset.price_currency : null;
|
|
||||||
return h('div', { key: code, className: 'mc-asset-row' }, [
|
|
||||||
h('strong', { key: 'code' }, code),
|
|
||||||
h('span', { key: 'balance' }, `${fmtNumber(balance, 8)} ${code}`),
|
|
||||||
priceAmount !== null && priceAmount !== undefined
|
|
||||||
? h('span', { key: 'price', className: 'mc-text' }, `1 ${code} = ${fmtNumber(priceAmount, 6)} ${priceCurrency || ''}`.trim())
|
|
||||||
: null,
|
|
||||||
]);
|
|
||||||
}))),
|
|
||||||
]))
|
|
||||||
: [h('tr', { key: 'empty' }, h('td', { colSpan: 3 }, 'Noch keine Wallet-Snapshots gespeichert.'))]
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
])),
|
|
||||||
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.', [
|
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('div', { key: 'actions', className: 'mc-inline-row' }, [
|
||||||
h('button', {
|
h('button', {
|
||||||
@@ -2717,6 +2754,71 @@
|
|||||||
]),
|
]),
|
||||||
]),
|
]),
|
||||||
]),
|
]),
|
||||||
|
panel('Wallet-Auszahlungen', 'Echte Auszahlungen vom NC Wallet an ein externes, hier nicht getracktes Wallet reduzieren den NC-Wallet-Bestand.', [
|
||||||
|
h('div', { key: 'actions', className: 'mc-inline-row' }, [
|
||||||
|
h('button', {
|
||||||
|
key: 'add-wallet-withdrawal',
|
||||||
|
type: 'button',
|
||||||
|
className: 'mc-button mc-button--secondary',
|
||||||
|
onClick: () => {
|
||||||
|
setWalletWithdrawalForm((previous) => ({
|
||||||
|
withdrawal_at: previous.withdrawal_at || nowDateTimeLocalValue(),
|
||||||
|
coins_amount: previous.coins_amount || '',
|
||||||
|
withdrawal_currency: currentCoinCurrency,
|
||||||
|
note: previous.note || '',
|
||||||
|
}));
|
||||||
|
setWalletWithdrawalModalOpen(true);
|
||||||
|
},
|
||||||
|
}, 'Aus NC Wallet auszahlen'),
|
||||||
|
]),
|
||||||
|
h('div', { key: 'withdrawal-list', className: 'mc-table-shell' }, [
|
||||||
|
h('table', { key: 'withdrawal-table', className: 'mc-table' }, [
|
||||||
|
h('thead', { key: 'head' }, h('tr', null, ['Zeit', 'Coins', 'Waehrung', 'Notiz', 'Aktion'].map((label) => h('th', { key: label }, label)))),
|
||||||
|
h('tbody', { key: 'body' },
|
||||||
|
currentWalletWithdrawals.length
|
||||||
|
? currentWalletWithdrawals.slice().reverse().map((withdrawal) => h('tr', { key: withdrawal.id }, [
|
||||||
|
h('td', { key: 'time' }, fmtDate(withdrawal.withdrawal_at)),
|
||||||
|
h('td', { key: 'coins' }, fmtNumber(withdrawal.coins_amount, 6)),
|
||||||
|
h('td', { key: 'currency' }, withdrawal.withdrawal_currency),
|
||||||
|
h('td', { key: 'note' }, withdrawal.note || '—'),
|
||||||
|
h('td', { key: 'action' }, h('button', {
|
||||||
|
type: 'button',
|
||||||
|
className: 'mc-button mc-button--ghost',
|
||||||
|
onClick: () => deleteWalletWithdrawal(withdrawal),
|
||||||
|
disabled: saving,
|
||||||
|
}, 'Loeschen')),
|
||||||
|
]))
|
||||||
|
: [h('tr', { key: 'empty' }, h('td', { colSpan: 5 }, 'Noch keine Wallet-Auszahlungen hinterlegt.'))]
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
panel('NC Wallet-Historie', 'Die letzten 10 NC-Wallet-Uploads mit allen aus dem Screenshot gelesenen Assets.', h('div', { className: 'mc-history-stack' },
|
||||||
|
currentWalletSnapshots.length
|
||||||
|
? currentWalletSnapshots.slice(0, 10).map((row) => h('article', { key: row.id, className: 'mc-history-card' }, [
|
||||||
|
h('div', { key: 'head', className: 'mc-flex-split' }, [
|
||||||
|
h('div', { key: 'time' }, [
|
||||||
|
h('h3', { key: 'timestamp' }, fmtDate(row.measured_at)),
|
||||||
|
h('div', { key: 'source', className: 'mc-kicker' }, row.source || 'manual'),
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
h('div', { key: 'assets', className: 'mc-history-asset-grid' },
|
||||||
|
Object.entries(row.balances_json || {}).map(([code, asset]) => {
|
||||||
|
const balance = asset && typeof asset === 'object' ? asset.balance : asset;
|
||||||
|
const priceAmount = asset && typeof asset === 'object' ? asset.price_amount : null;
|
||||||
|
const priceCurrency = asset && typeof asset === 'object' ? asset.price_currency : null;
|
||||||
|
return h('div', { key: code, className: 'mc-history-asset-card' }, [
|
||||||
|
h('strong', { key: 'code' }, code),
|
||||||
|
h('span', { key: 'balance' }, `${fmtNumber(balance, 8)} ${code}`),
|
||||||
|
priceAmount !== null && priceAmount !== undefined
|
||||||
|
? h('span', { key: 'price', className: 'mc-text' }, `1 ${code} = ${fmtNumber(priceAmount, 6)} ${priceCurrency || ''}`.trim())
|
||||||
|
: h('span', { key: 'price-empty', className: 'mc-text' }, 'Kein Kurs erkannt'),
|
||||||
|
]);
|
||||||
|
})
|
||||||
|
),
|
||||||
|
]))
|
||||||
|
: h('div', { className: 'mc-empty' }, 'Noch keine NC-Wallet-Snapshots gespeichert.')
|
||||||
|
)),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3080,6 +3182,18 @@
|
|||||||
]),
|
]),
|
||||||
]),
|
]),
|
||||||
], () => setPayoutModalOpen(false)) : null,
|
], () => setPayoutModalOpen(false)) : null,
|
||||||
|
walletWithdrawalModalOpen ? renderModal('Aus NC Wallet auszahlen', [
|
||||||
|
h('form', { key: 'form', className: 'mc-form', onSubmit: submitWalletWithdrawal }, [
|
||||||
|
inputField('Auszahlungszeitpunkt', 'datetime-local', walletWithdrawalForm.withdrawal_at, (value) => setWalletWithdrawalForm({ ...walletWithdrawalForm, withdrawal_at: value })),
|
||||||
|
inputField('Coins', 'number', walletWithdrawalForm.coins_amount, (value) => setWalletWithdrawalForm({ ...walletWithdrawalForm, coins_amount: value }), '0.000001'),
|
||||||
|
selectField('Waehrung', walletWithdrawalForm.withdrawal_currency, [currentCoinCurrency].concat(selectableCurrencies.map((currency) => currency.code).filter((code) => code !== currentCoinCurrency)), (value) => setWalletWithdrawalForm({ ...walletWithdrawalForm, withdrawal_currency: value })),
|
||||||
|
textareaField('Notiz', walletWithdrawalForm.note, (value) => setWalletWithdrawalForm({ ...walletWithdrawalForm, note: value })),
|
||||||
|
h('div', { className: 'mc-inline-row' }, [
|
||||||
|
h('button', { type: 'button', className: 'mc-button mc-button--ghost', onClick: () => setWalletWithdrawalModalOpen(false) }, 'Abbrechen'),
|
||||||
|
h('button', { type: 'submit', className: 'mc-button mc-button--secondary', disabled: saving }, saving ? 'Speichert …' : 'Wallet-Auszahlung speichern'),
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
], () => setWalletWithdrawalModalOpen(false)) : null,
|
||||||
minerOfferModalOpen ? renderModal('Basis-Miner-Angebot anlegen', [
|
minerOfferModalOpen ? renderModal('Basis-Miner-Angebot anlegen', [
|
||||||
h('form', { key: 'form', className: 'mc-form', onSubmit: submitMinerOffer }, [
|
h('form', { key: 'form', className: 'mc-form', onSubmit: submitMinerOffer }, [
|
||||||
inputField('Label', 'text', minerOfferForm.label, (value) => setMinerOfferForm({ ...minerOfferForm, label: value })),
|
inputField('Label', 'text', minerOfferForm.label, (value) => setMinerOfferForm({ ...minerOfferForm, label: value })),
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
{ "key": "overview", "label": "Übersicht" },
|
{ "key": "overview", "label": "Übersicht" },
|
||||||
{ "key": "upload", "label": "Upload" },
|
{ "key": "upload", "label": "Upload" },
|
||||||
{ "key": "measurements", "label": "Mining-History" },
|
{ "key": "measurements", "label": "Mining-History" },
|
||||||
{ "key": "wallet", "label": "Wallet" },
|
{ "key": "wallet", "label": "NC Wallet" },
|
||||||
{ "key": "mining", "label": "Miner-Daten" },
|
{ "key": "mining", "label": "Miner-Daten" },
|
||||||
{ "key": "dashboards", "label": "Dashboards" }
|
{ "key": "dashboards", "label": "Dashboards" }
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -126,6 +126,20 @@ CREATE TABLE IF NOT EXISTS miningcheck_wallet_snapshots (
|
|||||||
KEY idx_miningcheck_wallet_snapshots_project_measured_at (project_key, owner_sub, measured_at)
|
KEY idx_miningcheck_wallet_snapshots_project_measured_at (project_key, owner_sub, measured_at)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS miningcheck_wallet_withdrawals (
|
||||||
|
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
project_key VARCHAR(64) NOT NULL,
|
||||||
|
owner_sub VARCHAR(128) NOT NULL,
|
||||||
|
withdrawal_at TIMESTAMP NOT NULL,
|
||||||
|
coins_amount DECIMAL(20,6) NOT NULL,
|
||||||
|
withdrawal_currency VARCHAR(10) NOT NULL DEFAULT 'DOGE',
|
||||||
|
note TEXT,
|
||||||
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||||
|
CONSTRAINT fk_mining_wallet_withdrawals_project FOREIGN KEY (project_key) REFERENCES miningcheck_projects(project_key) ON DELETE CASCADE,
|
||||||
|
KEY idx_miningcheck_wallet_withdrawals_project_owner_at (project_key, owner_sub, withdrawal_at)
|
||||||
|
);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS miningcheck_miner_offers (
|
CREATE TABLE IF NOT EXISTS miningcheck_miner_offers (
|
||||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||||
project_key VARCHAR(64) NOT NULL,
|
project_key VARCHAR(64) NOT NULL,
|
||||||
|
|||||||
@@ -137,6 +137,22 @@ CREATE TABLE IF NOT EXISTS miningcheck_wallet_snapshots (
|
|||||||
CREATE INDEX IF NOT EXISTS idx_miningcheck_wallet_snapshots_project_measured_at
|
CREATE INDEX IF NOT EXISTS idx_miningcheck_wallet_snapshots_project_measured_at
|
||||||
ON miningcheck_wallet_snapshots(project_key, owner_sub, measured_at);
|
ON miningcheck_wallet_snapshots(project_key, owner_sub, measured_at);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS miningcheck_wallet_withdrawals (
|
||||||
|
id BIGSERIAL PRIMARY KEY,
|
||||||
|
project_key VARCHAR(64) NOT NULL,
|
||||||
|
owner_sub VARCHAR(128) NOT NULL,
|
||||||
|
withdrawal_at TIMESTAMP NOT NULL,
|
||||||
|
coins_amount NUMERIC(20,6) NOT NULL,
|
||||||
|
withdrawal_currency VARCHAR(10) NOT NULL DEFAULT 'DOGE',
|
||||||
|
note TEXT,
|
||||||
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
CONSTRAINT fk_mining_wallet_withdrawals_project FOREIGN KEY (project_key) REFERENCES miningcheck_projects(project_key) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_miningcheck_wallet_withdrawals_project_owner_at
|
||||||
|
ON miningcheck_wallet_withdrawals(project_key, owner_sub, withdrawal_at);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS miningcheck_miner_offers (
|
CREATE TABLE IF NOT EXISTS miningcheck_miner_offers (
|
||||||
id BIGSERIAL PRIMARY KEY,
|
id BIGSERIAL PRIMARY KEY,
|
||||||
project_key VARCHAR(64) NOT NULL,
|
project_key VARCHAR(64) NOT NULL,
|
||||||
|
|||||||
@@ -126,6 +126,20 @@ CREATE TABLE IF NOT EXISTS miningcheck_wallet_snapshots (
|
|||||||
KEY idx_miningcheck_wallet_snapshots_project_measured_at (project_key, owner_sub, measured_at)
|
KEY idx_miningcheck_wallet_snapshots_project_measured_at (project_key, owner_sub, measured_at)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS miningcheck_wallet_withdrawals (
|
||||||
|
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
project_key VARCHAR(64) NOT NULL,
|
||||||
|
owner_sub VARCHAR(128) NOT NULL,
|
||||||
|
withdrawal_at TIMESTAMP NOT NULL,
|
||||||
|
coins_amount DECIMAL(20,6) NOT NULL,
|
||||||
|
withdrawal_currency VARCHAR(10) NOT NULL DEFAULT 'DOGE',
|
||||||
|
note TEXT,
|
||||||
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||||
|
CONSTRAINT fk_mining_wallet_withdrawals_project FOREIGN KEY (project_key) REFERENCES miningcheck_projects(project_key) ON DELETE CASCADE,
|
||||||
|
KEY idx_miningcheck_wallet_withdrawals_project_owner_at (project_key, owner_sub, withdrawal_at)
|
||||||
|
);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS miningcheck_miner_offers (
|
CREATE TABLE IF NOT EXISTS miningcheck_miner_offers (
|
||||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||||
project_key VARCHAR(64) NOT NULL,
|
project_key VARCHAR(64) NOT NULL,
|
||||||
|
|||||||
@@ -271,6 +271,20 @@ final class Router
|
|||||||
Http::json(['data' => $this->saveWalletSnapshot($projectKey, Http::input())], 201);
|
Http::json(['data' => $this->saveWalletSnapshot($projectKey, Http::input())], 201);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($resource === 'wallet-withdrawals' && $method === 'GET') {
|
||||||
|
Http::json(['data' => $this->walletWithdrawals($projectKey)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($resource === 'wallet-withdrawals' && $method === 'POST') {
|
||||||
|
$this->repository()->ensureProject($projectKey);
|
||||||
|
Http::json(['data' => $this->saveWalletWithdrawal($projectKey, Http::input())], 201);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (preg_match('~^wallet-withdrawals/(\d+)$~', $resource, $matches) && $method === 'DELETE') {
|
||||||
|
$this->deleteWalletWithdrawal($projectKey, (int) $matches[1]);
|
||||||
|
Http::json(['data' => ['deleted' => true]]);
|
||||||
|
}
|
||||||
|
|
||||||
if ($resource === 'miner-offers' && $method === 'GET') {
|
if ($resource === 'miner-offers' && $method === 'GET') {
|
||||||
Http::json(['data' => $this->minerOffers($projectKey)]);
|
Http::json(['data' => $this->minerOffers($projectKey)]);
|
||||||
}
|
}
|
||||||
@@ -343,6 +357,7 @@ final class Router
|
|||||||
'cost_plans' => [],
|
'cost_plans' => [],
|
||||||
'currencies' => [],
|
'currencies' => [],
|
||||||
'payouts' => [],
|
'payouts' => [],
|
||||||
|
'wallet_withdrawals' => [],
|
||||||
'miner_offers' => [],
|
'miner_offers' => [],
|
||||||
'purchased_miners' => [],
|
'purchased_miners' => [],
|
||||||
'measurement_rates' => [],
|
'measurement_rates' => [],
|
||||||
@@ -1161,6 +1176,7 @@ final class Router
|
|||||||
$includeCostPlans = !array_key_exists('cost_plans', $options) || (bool) $options['cost_plans'];
|
$includeCostPlans = !array_key_exists('cost_plans', $options) || (bool) $options['cost_plans'];
|
||||||
$includeCurrencies = !array_key_exists('currencies', $options) || (bool) $options['currencies'];
|
$includeCurrencies = !array_key_exists('currencies', $options) || (bool) $options['currencies'];
|
||||||
$includePayouts = !array_key_exists('payouts', $options) || (bool) $options['payouts'];
|
$includePayouts = !array_key_exists('payouts', $options) || (bool) $options['payouts'];
|
||||||
|
$includeWalletWithdrawals = !array_key_exists('wallet_withdrawals', $options) || (bool) $options['wallet_withdrawals'];
|
||||||
$includeMinerOffers = !array_key_exists('miner_offers', $options) || (bool) $options['miner_offers'];
|
$includeMinerOffers = !array_key_exists('miner_offers', $options) || (bool) $options['miner_offers'];
|
||||||
$includePurchasedMiners = !array_key_exists('purchased_miners', $options) || (bool) $options['purchased_miners'];
|
$includePurchasedMiners = !array_key_exists('purchased_miners', $options) || (bool) $options['purchased_miners'];
|
||||||
|
|
||||||
@@ -1177,6 +1193,7 @@ final class Router
|
|||||||
'module_theme_mode' => 'inherit',
|
'module_theme_mode' => 'inherit',
|
||||||
'module_theme_accent' => 'teal',
|
'module_theme_accent' => 'teal',
|
||||||
'preferred_currencies' => $this->preferredCurrencies(),
|
'preferred_currencies' => $this->preferredCurrencies(),
|
||||||
|
'wallet_withdrawals' => [],
|
||||||
];
|
];
|
||||||
if (!$this->isValidTimezone((string) ($base['display_timezone'] ?? ''))) {
|
if (!$this->isValidTimezone((string) ($base['display_timezone'] ?? ''))) {
|
||||||
$base['display_timezone'] = nexus_display_timezone_name();
|
$base['display_timezone'] = nexus_display_timezone_name();
|
||||||
@@ -1192,6 +1209,7 @@ final class Router
|
|||||||
$base['currencies'] = $includeCurrencies ? $this->currencies() : [];
|
$base['currencies'] = $includeCurrencies ? $this->currencies() : [];
|
||||||
$base['preferred_currencies'] = $this->preferredCurrencies($base['preferred_currencies'] ?? null);
|
$base['preferred_currencies'] = $this->preferredCurrencies($base['preferred_currencies'] ?? null);
|
||||||
$base['payouts'] = $includePayouts ? $this->payouts($projectKey) : [];
|
$base['payouts'] = $includePayouts ? $this->payouts($projectKey) : [];
|
||||||
|
$base['wallet_withdrawals'] = $includeWalletWithdrawals ? $this->walletWithdrawals($projectKey) : [];
|
||||||
$base['miner_offers'] = $includeMinerOffers ? $this->minerOffers($projectKey) : [];
|
$base['miner_offers'] = $includeMinerOffers ? $this->minerOffers($projectKey) : [];
|
||||||
$base['purchased_miners'] = $includePurchasedMiners ? $this->purchasedMiners($projectKey) : [];
|
$base['purchased_miners'] = $includePurchasedMiners ? $this->purchasedMiners($projectKey) : [];
|
||||||
$base['measurement_rates'] = [];
|
$base['measurement_rates'] = [];
|
||||||
@@ -1378,8 +1396,9 @@ final class Router
|
|||||||
],
|
],
|
||||||
'wallet' => [
|
'wallet' => [
|
||||||
'cost_plans' => false,
|
'cost_plans' => false,
|
||||||
'currencies' => false,
|
'currencies' => true,
|
||||||
'payouts' => false,
|
'payouts' => true,
|
||||||
|
'wallet_withdrawals' => true,
|
||||||
'miner_offers' => false,
|
'miner_offers' => false,
|
||||||
'purchased_miners' => false,
|
'purchased_miners' => false,
|
||||||
],
|
],
|
||||||
@@ -1806,6 +1825,32 @@ final class Router
|
|||||||
return $this->repository()->saveWalletSnapshot($projectKey, $payload);
|
return $this->repository()->saveWalletSnapshot($projectKey, $payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function walletWithdrawals(string $projectKey): array
|
||||||
|
{
|
||||||
|
if (!$this->repository()->tableExists('wallet_withdrawals')) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->repository()->listWalletWithdrawals($projectKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function saveWalletWithdrawal(string $projectKey, array $input): array
|
||||||
|
{
|
||||||
|
$payload = [
|
||||||
|
'withdrawal_at' => $this->requiredDateTime($input['withdrawal_at'] ?? null, 'withdrawal_at', $this->projectTimezone($projectKey)),
|
||||||
|
'coins_amount' => $this->requiredDecimal($input['coins_amount'] ?? null, 'coins_amount'),
|
||||||
|
'withdrawal_currency' => $this->requiredCurrency($input['withdrawal_currency'] ?? 'DOGE', 'withdrawal_currency'),
|
||||||
|
'note' => $this->optionalString($input['note'] ?? null, 1000),
|
||||||
|
];
|
||||||
|
|
||||||
|
return $this->repository()->saveWalletWithdrawal($projectKey, $payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function deleteWalletWithdrawal(string $projectKey, int $withdrawalId): void
|
||||||
|
{
|
||||||
|
$this->repository()->deleteWalletWithdrawal($projectKey, $withdrawalId);
|
||||||
|
}
|
||||||
|
|
||||||
private function saveMinerOffer(string $projectKey, array $input): array
|
private function saveMinerOffer(string $projectKey, array $input): array
|
||||||
{
|
{
|
||||||
$paymentType = $this->enumValue($input['payment_type'] ?? 'fiat', ['fiat', 'crypto'], 'payment_type');
|
$paymentType = $this->enumValue($input['payment_type'] ?? 'fiat', ['fiat', 'crypto'], 'payment_type');
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ final class AnalyticsService
|
|||||||
$baselineAt = (string) ($settings['baseline_measured_at'] ?? '');
|
$baselineAt = (string) ($settings['baseline_measured_at'] ?? '');
|
||||||
$costPlans = is_array($settings['cost_plans'] ?? null) ? $settings['cost_plans'] : [];
|
$costPlans = is_array($settings['cost_plans'] ?? null) ? $settings['cost_plans'] : [];
|
||||||
$payouts = is_array($settings['payouts'] ?? null) ? $settings['payouts'] : [];
|
$payouts = is_array($settings['payouts'] ?? null) ? $settings['payouts'] : [];
|
||||||
|
$walletWithdrawals = is_array($settings['wallet_withdrawals'] ?? null) ? $settings['wallet_withdrawals'] : [];
|
||||||
$purchasedMiners = is_array($settings['purchased_miners'] ?? null) ? $settings['purchased_miners'] : [];
|
$purchasedMiners = is_array($settings['purchased_miners'] ?? null) ? $settings['purchased_miners'] : [];
|
||||||
$preferredPriceCurrencies = array_values(array_unique(array_filter([
|
$preferredPriceCurrencies = array_values(array_unique(array_filter([
|
||||||
strtoupper(trim((string) ($settings['report_currency'] ?? ''))),
|
strtoupper(trim((string) ($settings['report_currency'] ?? ''))),
|
||||||
@@ -368,7 +369,7 @@ final class AnalyticsService
|
|||||||
'reinvest'
|
'reinvest'
|
||||||
)
|
)
|
||||||
: null;
|
: null;
|
||||||
$walletBalances = $this->walletBalances($payouts, $purchasedMiners, $latestMeasuredTs);
|
$walletBalances = $this->walletBalances($payouts, $walletWithdrawals, $purchasedMiners, $latestMeasuredTs);
|
||||||
$latestWalletSnapshot = $this->latestWalletSnapshot(is_array($options['wallet_snapshots'] ?? null) ? $options['wallet_snapshots'] : []);
|
$latestWalletSnapshot = $this->latestWalletSnapshot(is_array($options['wallet_snapshots'] ?? null) ? $options['wallet_snapshots'] : []);
|
||||||
if (is_array($latestWalletSnapshot)) {
|
if (is_array($latestWalletSnapshot)) {
|
||||||
$snapshotBalances = $this->walletSnapshotBalances($latestWalletSnapshot);
|
$snapshotBalances = $this->walletSnapshotBalances($latestWalletSnapshot);
|
||||||
@@ -1297,7 +1298,7 @@ final class AnalyticsService
|
|||||||
return $endIndex - $startIndex + 1;
|
return $endIndex - $startIndex + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function walletBalances(array $payouts, array $purchasedMiners, int $measurementTs): array
|
private function walletBalances(array $payouts, array $walletWithdrawals, array $purchasedMiners, int $measurementTs): array
|
||||||
{
|
{
|
||||||
$balances = [];
|
$balances = [];
|
||||||
|
|
||||||
@@ -1334,6 +1335,21 @@ final class AnalyticsService
|
|||||||
$balances[$currency] = ($balances[$currency] ?? 0.0) - $amount;
|
$balances[$currency] = ($balances[$currency] ?? 0.0) - $amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach ($walletWithdrawals as $withdrawal) {
|
||||||
|
$withdrawalTs = $this->utcTimestamp((string) ($withdrawal['withdrawal_at'] ?? ''));
|
||||||
|
if ($measurementTs > 0 && $withdrawalTs > $measurementTs) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$currency = strtoupper(trim((string) ($withdrawal['withdrawal_currency'] ?? '')));
|
||||||
|
$amount = is_numeric($withdrawal['coins_amount'] ?? null) ? (float) $withdrawal['coins_amount'] : null;
|
||||||
|
if ($currency === '' || $amount === null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$balances[$currency] = ($balances[$currency] ?? 0.0) - $amount;
|
||||||
|
}
|
||||||
|
|
||||||
ksort($balances);
|
ksort($balances);
|
||||||
return array_map(fn (float $value): float => round($value, 8), $balances);
|
return array_map(fn (float $value): float => round($value, 8), $balances);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -459,6 +459,76 @@ final class MiningRepository
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function listWalletWithdrawals(string $projectKey): array
|
||||||
|
{
|
||||||
|
$stmt = $this->pdo->prepare(
|
||||||
|
'SELECT * FROM ' . $this->table('wallet_withdrawals') . ' WHERE project_key = :project_key AND owner_sub = :owner_sub ORDER BY withdrawal_at ASC, id ASC'
|
||||||
|
);
|
||||||
|
$stmt->execute(['project_key' => $projectKey, 'owner_sub' => $this->ownerSub]);
|
||||||
|
return $this->normalizeRows($stmt->fetchAll() ?: []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function saveWalletWithdrawal(string $projectKey, array $payload): array
|
||||||
|
{
|
||||||
|
if ($this->driver === 'pgsql') {
|
||||||
|
$stmt = $this->pdo->prepare(
|
||||||
|
'INSERT INTO ' . $this->table('wallet_withdrawals') . ' (
|
||||||
|
project_key, owner_sub, withdrawal_at, coins_amount, withdrawal_currency, note
|
||||||
|
) VALUES (
|
||||||
|
:project_key, :owner_sub, :withdrawal_at, :coins_amount, :withdrawal_currency, :note
|
||||||
|
)
|
||||||
|
RETURNING *'
|
||||||
|
);
|
||||||
|
$stmt->execute([
|
||||||
|
'project_key' => $projectKey,
|
||||||
|
'owner_sub' => $this->ownerSub,
|
||||||
|
'withdrawal_at' => $payload['withdrawal_at'],
|
||||||
|
'coins_amount' => $payload['coins_amount'],
|
||||||
|
'withdrawal_currency' => $payload['withdrawal_currency'],
|
||||||
|
'note' => $payload['note'],
|
||||||
|
]);
|
||||||
|
return $this->normalizeRow($stmt->fetch() ?: []);
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt = $this->pdo->prepare(
|
||||||
|
'INSERT INTO ' . $this->table('wallet_withdrawals') . ' (
|
||||||
|
project_key, owner_sub, withdrawal_at, coins_amount, withdrawal_currency, note
|
||||||
|
) VALUES (
|
||||||
|
:project_key, :owner_sub, :withdrawal_at, :coins_amount, :withdrawal_currency, :note
|
||||||
|
)'
|
||||||
|
);
|
||||||
|
$stmt->execute([
|
||||||
|
'project_key' => $projectKey,
|
||||||
|
'owner_sub' => $this->ownerSub,
|
||||||
|
'withdrawal_at' => $payload['withdrawal_at'],
|
||||||
|
'coins_amount' => $payload['coins_amount'],
|
||||||
|
'withdrawal_currency' => $payload['withdrawal_currency'],
|
||||||
|
'note' => $payload['note'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$id = (int) $this->pdo->lastInsertId();
|
||||||
|
$fetch = $this->pdo->prepare('SELECT * FROM ' . $this->table('wallet_withdrawals') . ' WHERE id = :id LIMIT 1');
|
||||||
|
$fetch->execute(['id' => $id]);
|
||||||
|
return $this->normalizeRow($fetch->fetch() ?: []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function deleteWalletWithdrawal(string $projectKey, int $withdrawalId): void
|
||||||
|
{
|
||||||
|
if ($withdrawalId <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt = $this->pdo->prepare(
|
||||||
|
'DELETE FROM ' . $this->table('wallet_withdrawals') . '
|
||||||
|
WHERE id = :id AND project_key = :project_key AND owner_sub = :owner_sub'
|
||||||
|
);
|
||||||
|
$stmt->execute([
|
||||||
|
'id' => $withdrawalId,
|
||||||
|
'project_key' => $projectKey,
|
||||||
|
'owner_sub' => $this->ownerSub,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
public function listWalletSnapshots(string $projectKey, int $limit = 100): array
|
public function listWalletSnapshots(string $projectKey, int $limit = 100): array
|
||||||
{
|
{
|
||||||
$stmt = $this->pdo->prepare(
|
$stmt = $this->pdo->prepare(
|
||||||
|
|||||||
@@ -205,6 +205,10 @@ final class SchemaManager
|
|||||||
$this->ensureWalletSnapshotsTable();
|
$this->ensureWalletSnapshotsTable();
|
||||||
$applied[] = 'wallet_snapshots_table';
|
$applied[] = 'wallet_snapshots_table';
|
||||||
}
|
}
|
||||||
|
if (!$this->tableExists($this->prefix . 'wallet_withdrawals')) {
|
||||||
|
$this->ensureWalletWithdrawalsTable();
|
||||||
|
$applied[] = 'wallet_withdrawals_table';
|
||||||
|
}
|
||||||
if (!$this->tableExists($this->prefix . 'fx_fetches') || !$this->tableExists($this->prefix . 'fx_rates')) {
|
if (!$this->tableExists($this->prefix . 'fx_fetches') || !$this->tableExists($this->prefix . 'fx_rates')) {
|
||||||
$this->ensureFxRatesTable();
|
$this->ensureFxRatesTable();
|
||||||
$applied[] = 'fx_rates_table';
|
$applied[] = 'fx_rates_table';
|
||||||
@@ -324,6 +328,10 @@ final class SchemaManager
|
|||||||
$this->ensureWalletSnapshotsTable();
|
$this->ensureWalletSnapshotsTable();
|
||||||
$applied[] = 'wallet_snapshots_table';
|
$applied[] = 'wallet_snapshots_table';
|
||||||
}
|
}
|
||||||
|
if (!$this->tableExists($this->prefix . 'wallet_withdrawals')) {
|
||||||
|
$this->ensureWalletWithdrawalsTable();
|
||||||
|
$applied[] = 'wallet_withdrawals_table';
|
||||||
|
}
|
||||||
|
|
||||||
if (!$this->tableExists($this->prefix . 'miner_offers')) {
|
if (!$this->tableExists($this->prefix . 'miner_offers')) {
|
||||||
$this->upgradeMinerOffersTable();
|
$this->upgradeMinerOffersTable();
|
||||||
@@ -679,6 +687,9 @@ final class SchemaManager
|
|||||||
if (!$this->tableExists($this->prefix . 'wallet_snapshots')) {
|
if (!$this->tableExists($this->prefix . 'wallet_snapshots')) {
|
||||||
$upgrades[] = 'wallet_snapshots_table';
|
$upgrades[] = 'wallet_snapshots_table';
|
||||||
}
|
}
|
||||||
|
if (!$this->tableExists($this->prefix . 'wallet_withdrawals')) {
|
||||||
|
$upgrades[] = 'wallet_withdrawals_table';
|
||||||
|
}
|
||||||
if (!$this->tableExists($this->prefix . 'miner_offers')) {
|
if (!$this->tableExists($this->prefix . 'miner_offers')) {
|
||||||
$upgrades[] = 'miner_offers_table';
|
$upgrades[] = 'miner_offers_table';
|
||||||
}
|
}
|
||||||
@@ -978,6 +989,49 @@ final class SchemaManager
|
|||||||
$this->executeUpgradeStatements($statements, 'Schema-Upgrade fuer Wallet-Snapshots fehlgeschlagen.');
|
$this->executeUpgradeStatements($statements, 'Schema-Upgrade fuer Wallet-Snapshots fehlgeschlagen.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function ensureWalletWithdrawalsTable(): void
|
||||||
|
{
|
||||||
|
if ($this->tableExists($this->prefix . 'wallet_withdrawals')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$table = $this->prefix . 'wallet_withdrawals';
|
||||||
|
$projectTable = $this->prefix . 'projects';
|
||||||
|
$statements = $this->driver === 'pgsql'
|
||||||
|
? [
|
||||||
|
'CREATE TABLE IF NOT EXISTS ' . $table . ' (
|
||||||
|
id BIGSERIAL PRIMARY KEY,
|
||||||
|
project_key VARCHAR(64) NOT NULL,
|
||||||
|
owner_sub VARCHAR(128) NOT NULL,
|
||||||
|
withdrawal_at TIMESTAMP NOT NULL,
|
||||||
|
coins_amount NUMERIC(20,6) NOT NULL,
|
||||||
|
withdrawal_currency VARCHAR(10) NOT NULL DEFAULT \'DOGE\',
|
||||||
|
note TEXT,
|
||||||
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
CONSTRAINT fk_mining_wallet_withdrawals_project FOREIGN KEY (project_key) REFERENCES ' . $projectTable . '(project_key) ON DELETE CASCADE
|
||||||
|
)',
|
||||||
|
'CREATE INDEX IF NOT EXISTS idx_miningcheck_wallet_withdrawals_project_owner_at ON ' . $table . ' (project_key, owner_sub, withdrawal_at)',
|
||||||
|
]
|
||||||
|
: [
|
||||||
|
'CREATE TABLE IF NOT EXISTS `' . $table . '` (
|
||||||
|
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
project_key VARCHAR(64) NOT NULL,
|
||||||
|
owner_sub VARCHAR(128) NOT NULL,
|
||||||
|
withdrawal_at TIMESTAMP NOT NULL,
|
||||||
|
coins_amount DECIMAL(20,6) NOT NULL,
|
||||||
|
withdrawal_currency VARCHAR(10) NOT NULL DEFAULT \'DOGE\',
|
||||||
|
note TEXT,
|
||||||
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||||
|
CONSTRAINT fk_mining_wallet_withdrawals_project FOREIGN KEY (project_key) REFERENCES `' . $projectTable . '`(project_key) ON DELETE CASCADE,
|
||||||
|
KEY idx_miningcheck_wallet_withdrawals_project_owner_at (project_key, owner_sub, withdrawal_at)
|
||||||
|
)',
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->executeUpgradeStatements($statements, 'Schema-Upgrade fuer Wallet-Auszahlungen fehlgeschlagen.');
|
||||||
|
}
|
||||||
|
|
||||||
public function ensureMinerTables(): void
|
public function ensureMinerTables(): void
|
||||||
{
|
{
|
||||||
if (!$this->tableExists($this->prefix . 'miner_offers')) {
|
if (!$this->tableExists($this->prefix . 'miner_offers')) {
|
||||||
@@ -1451,6 +1505,7 @@ final class SchemaManager
|
|||||||
$this->prefix . 'measurement_rates',
|
$this->prefix . 'measurement_rates',
|
||||||
$this->prefix . 'payouts',
|
$this->prefix . 'payouts',
|
||||||
$this->prefix . 'wallet_snapshots',
|
$this->prefix . 'wallet_snapshots',
|
||||||
|
$this->prefix . 'wallet_withdrawals',
|
||||||
$this->prefix . 'miner_offers',
|
$this->prefix . 'miner_offers',
|
||||||
$this->prefix . 'purchased_miners',
|
$this->prefix . 'purchased_miners',
|
||||||
];
|
];
|
||||||
@@ -1471,6 +1526,7 @@ final class SchemaManager
|
|||||||
$this->prefix . 'measurement_rates',
|
$this->prefix . 'measurement_rates',
|
||||||
$this->prefix . 'payouts',
|
$this->prefix . 'payouts',
|
||||||
$this->prefix . 'wallet_snapshots',
|
$this->prefix . 'wallet_snapshots',
|
||||||
|
$this->prefix . 'wallet_withdrawals',
|
||||||
$this->prefix . 'miner_offers',
|
$this->prefix . 'miner_offers',
|
||||||
$this->prefix . 'targets',
|
$this->prefix . 'targets',
|
||||||
$this->prefix . 'dashboard_definitions',
|
$this->prefix . 'dashboard_definitions',
|
||||||
|
|||||||
Reference in New Issue
Block a user