PHP WebShell

Текущая директория: /var/www/bitcardoApp/backyard/user/rates

Просмотр файла: rates_fees.php

<?php
// backyard/user/rates/rates_fees.php
include '../common/header.php';

if (!isset($conn)) {
    include_once '../../config/db_config.php';
}
require_once '../../models/rates/rates.php';
date_default_timezone_set('Africa/Lagos');

function h($s){ return htmlspecialchars((string)$s, ENT_QUOTES, 'UTF-8'); }
$flash = null;

/* ------------------------------ POST HANDLERS ------------------------------ */
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    try {
        $action = $_POST['action'] ?? '';

        if ($action === 'save_coin_rate') {
            $rate_id = isset($_POST['rate_id']) && $_POST['rate_id'] !== '' ? (int)$_POST['rate_id'] : null;
            $data = [
                'coin'            => $_POST['coin'] ?? null,
                'coin_name'       => $_POST['coin_name'] ?? null,
                'coin_icon'       => $_POST['coin_icon'] ?? null,
                'sell_rate'       => $_POST['sell_rate'] ?? null,
                'buy_rate'        => $_POST['buy_rate'] ?? null,
                'min_swap'        => $_POST['min_swap'] ?? null,
                'margin_percent'  => $_POST['margin_percent'] ?? null,
                'use_online_rate' => isset($_POST['use_online_rate']) ? 1 : 0,
                'auto_update'     => isset($_POST['auto_update']) ? 1 : 0,
            ];
            $res = rr_coin_rate_upsert($conn, $rate_id, $data);
            if (!empty($res['ok'])) {
                $flash = ['type'=>'success','msg'=> $rate_id ? 'Coin rate updated.' : 'Coin rate added.'];
            } else {
                $flash = ['type'=>'danger','msg'=> 'Save failed: '.($res['error'] ?? 'Unknown error')];
            }

        } elseif ($action === 'toggle_flags') {
            $rate_id = (int)($_POST['rate_id'] ?? 0);
            $use_online = isset($_POST['use_online_rate']) ? (int)$_POST['use_online_rate'] : null;
            $auto_update = isset($_POST['auto_update']) ? (int)$_POST['auto_update'] : null;
            $ok = rr_coin_rate_toggle_flags($conn, $rate_id, $use_online, $auto_update);
            $flash = ['type'=>$ok?'success':'danger', 'msg'=>$ok?'Flags updated.':'Failed to update flags.'];

        } elseif ($action === 'save_fee') {
            $fee_id = isset($_POST['fee_id']) && $_POST['fee_id']!=='' ? (int)$_POST['fee_id'] : null;
            $data = [
                'coin'        => $_POST['fee_coin'] ?? null,
                'type'        => $_POST['fee_type'] ?? null, // crypto|fiat
                'threshold'   => $_POST['threshold'] ?? null,
                'percent_fee' => $_POST['percent_fee'] ?? null,
                'flat_fee'    => $_POST['flat_fee'] ?? null,
                'max_fee'     => $_POST['max_fee'] ?? null,
                'note'        => $_POST['note'] ?? null,
            ];
            $res = rr_withdraw_fee_upsert($conn, $fee_id, $data);
            if (!empty($res['ok'])) {
                $flash = ['type'=>'success','msg'=> $fee_id ? 'Withdraw fee updated.' : 'Withdraw fee added.'];
            } else {
                $flash = ['type'=>'danger','msg'=> 'Save failed: '.($res['error'] ?? 'Unknown error')];
            }

        } elseif ($action === 'delete_fee') {
            $fee_id = (int)($_POST['fee_id'] ?? 0);
            $ok = rr_withdraw_fee_delete($conn, $fee_id);
            $flash = ['type'=>$ok?'success':'danger', 'msg'=>$ok?'Fee deleted.':'Failed to delete fee.'];
        }

    } catch (Throwable $e) {
        $flash = ['type'=>'danger','msg'=>'Error: '.$e->getMessage()];
    }
}

/* ------------------------------ DATA LOAD ------------------------------ */
$coin_rates  = rr_coin_rates_all($conn);
$online_rows = rr_online_rates_all($conn);     // read-only snapshot
$fees        = rr_withdraw_fees_all($conn);
?>
<style>
  .table td, .table th { vertical-align: middle; }
  .form-switch .form-check-input { cursor: pointer; }
  @media (max-width: 767.98px){
    .stack-sm > * { margin-bottom: .5rem; }
  }
</style>

<div class="nk-content nk-content-fluid mt-5">
  <div class="container-xl wide-lg">
    <div class="nk-content-body">

      <div class="nk-block-head">
        <div class="nk-block-between-md g-4">
          <div class="nk-block-head-content">
            <h4 class="nk-block-title fw-normal">Rates & Fees Console</h4>
            <p class="text-soft mb-0">Manage coin rates (buy/sell), flags, and withdraw fees. Online snapshot is read-only.</p>
          </div>
        </div>
      </div>

      <?php if ($flash): ?>
        <div class="alert alert-<?= h($flash['type']); ?>"><?= h($flash['msg']); ?></div>
      <?php endif; ?>

      <!-- Coin Rates -->
      <div class="nk-block">
        <div class="card card-bordered">
          <div class="card-inner">
            <div class="d-flex justify-content-between align-items-center mb-2">
              <h6 class="mb-0">Coin Rates</h6>
              <button class="btn btn-sm btn-primary" data-bs-toggle="modal" data-bs-target="#coinRateModal" onclick="CR.resetForm()">Add Coin</button>
            </div>
            <div class="table-responsive">
              <table class="table table-striped">
                <thead class="small text-soft">
                  <tr>
                    <th>Coin</th>
                    <th>Name</th>
                    <th class="text-end">Sell</th>
                    <th class="text-end">Buy</th>
                    <th class="text-end">Min Swap</th>
                    <th class="text-end">Margin %</th>
                    <th>Use Online</th>
                    <th>Auto Update</th>
                    <th>Updated</th>
                    <th class="text-end">Actions</th>
                  </tr>
                </thead>
                <tbody>
                  <?php if (empty($coin_rates)): ?>
                    <tr><td colspan="10" class="text-center text-muted py-4">No coins configured.</td></tr>
                  <?php else: foreach ($coin_rates as $r): ?>
                    <tr>
                      <td class="fw-semibold"><?= h($r['coin']); ?></td>
                      <td><?= h($r['coin_name'] ?? ''); ?></td>
                      <td class="text-end"><?= h($r['sell_rate']); ?></td>
                      <td class="text-end"><?= h($r['buy_rate']); ?></td>
                      <td class="text-end"><?= h($r['min_swap']); ?></td>
                      <td class="text-end"><?= h($r['margin_percent']); ?></td>
                      <td>
                        <form method="post" class="m-0">
                          <input type="hidden" name="action" value="toggle_flags">
                          <input type="hidden" name="rate_id" value="<?= (int)$r['rate_id']; ?>">
                          <input type="hidden" name="use_online_rate" value="<?= $r['use_online_rate'] ? 0 : 1; ?>">
                          <button class="btn btn-xs <?= $r['use_online_rate']?'btn-success':'btn-outline-secondary'; ?>">
                            <?= $r['use_online_rate']?'ON':'OFF'; ?>
                          </button>
                        </form>
                      </td>
                      <td>
                        <form method="post" class="m-0">
                          <input type="hidden" name="action" value="toggle_flags">
                          <input type="hidden" name="rate_id" value="<?= (int)$r['rate_id']; ?>">
                          <input type="hidden" name="auto_update" value="<?= $r['auto_update'] ? 0 : 1; ?>">
                          <button class="btn btn-xs <?= $r['auto_update']?'btn-success':'btn-outline-secondary'; ?>">
                            <?= $r['auto_update']?'ON':'OFF'; ?>
                          </button>
                        </form>
                      </td>
                      <td class="small"><?= h($r['updated_at']); ?></td>
                      <td class="text-end">
                        <button class="btn btn-xs btn-outline-primary"
                                data-bs-toggle="modal"
                                data-bs-target="#coinRateModal"
                                onclick='CR.edit(<?= json_encode($r, JSON_HEX_TAG|JSON_HEX_APOS|JSON_HEX_AMP|JSON_HEX_QUOT); ?>)'>
                          Edit
                        </button>
                      </td>
                    </tr>
                  <?php endforeach; endif; ?>
                </tbody>
              </table>
            </div>
          </div>
        </div>
      </div>

      <!-- Online Rates Snapshot (read-only) -->
      <div class="nk-block">
        <div class="card card-bordered">
          <div class="card-inner">
            <h6 class="mb-2">Online Rates Snapshot (read-only)</h6>
            <div class="table-responsive">
              <table class="table table-sm">
                <thead class="small text-soft">
                  <tr>
                    <th>Coin</th>
                    <th class="text-end">Rate</th>
                    <th class="text-end">Sell</th>
                    <th class="text-end">Buy</th>
                    <th class="text-end">Margin %</th>
                    <th>Source</th>
                    <th>Fetched</th>
                  </tr>
                </thead>
                <tbody>
                  <?php if (empty($online_rows)): ?>
                    <tr><td colspan="7" class="text-center text-muted py-3">No snapshots yet.</td></tr>
                  <?php else: foreach ($online_rows as $o): ?>
                    <tr>
                      <td class="fw-semibold"><?= h(strtoupper($o['coin'])); ?></td>
                      <td class="text-end"><?= h($o['rate']); ?></td>
                      <td class="text-end"><?= h($o['sell_rate']); ?></td>
                      <td class="text-end"><?= h($o['buy_rate']); ?></td>
                      <td class="text-end"><?= h($o['margin_percent']); ?></td>
                      <td><?= h($o['source'] ?? ''); ?></td>
                      <td class="small"><?= h($o['fetched_at']); ?></td>
                    </tr>
                  <?php endforeach; endif; ?>
                </tbody>
              </table>
            </div>
          </div>
        </div>
      </div>

      <!-- Withdraw Fees -->
      <div class="nk-block">
        <div class="card card-bordered">
          <div class="card-inner">
            <div class="d-flex justify-content-between align-items-center mb-2">
              <h6 class="mb-0">Withdraw Fees</h6>
              <button class="btn btn-sm btn-primary" data-bs-toggle="modal" data-bs-target="#feeModal" onclick="WF.resetForm()">Add Fee</button>
            </div>
            <div class="table-responsive">
              <table class="table table-striped">
                <thead class="small text-soft">
                  <tr>
                    <th>Coin</th>
                    <th>Type</th>
                    <th class="text-end">Threshold</th>
                    <th class="text-end">Percent</th>
                    <th class="text-end">Flat</th>
                    <th class="text-end">Max</th>
                    <th>Note</th>
                    <th>Updated</th>
                    <th class="text-end">Actions</th>
                  </tr>
                </thead>
                <tbody>
                <?php if (empty($fees)): ?>
                  <tr><td colspan="9" class="text-center text-muted py-4">No fees configured.</td></tr>
                <?php else: foreach ($fees as $f): ?>
                  <tr>
                    <td class="fw-semibold"><?= h(strtoupper($f['coin'])); ?></td>
                    <td><?= h($f['type']); ?></td>
                    <td class="text-end"><?= h($f['threshold']); ?></td>
                    <td class="text-end"><?= h($f['percent_fee']); ?></td>
                    <td class="text-end"><?= h($f['flat_fee']); ?></td>
                    <td class="text-end"><?= h($f['max_fee']); ?></td>
                    <td class="small"><?= h($f['note']); ?></td>
                    <td class="small"><?= h($f['updated_at']); ?></td>
                    <td class="text-end stack-sm">
                      <button class="btn btn-xs btn-outline-primary"
                              data-bs-toggle="modal"
                              data-bs-target="#feeModal"
                              onclick='WF.edit(<?= json_encode($f, JSON_HEX_TAG|JSON_HEX_APOS|JSON_HEX_AMP|JSON_HEX_QUOT); ?>)'>
                        Edit
                      </button>
                      <form method="post" class="d-inline" onsubmit="return confirm('Delete this fee?')">
                        <input type="hidden" name="action" value="delete_fee">
                        <input type="hidden" name="fee_id" value="<?= (int)$f['fee_id']; ?>">
                        <button class="btn btn-xs btn-outline-danger">Delete</button>
                      </form>
                    </td>
                  </tr>
                <?php endforeach; endif; ?>
                </tbody>
              </table>
            </div>
          </div>
        </div>
      </div>

    </div><!-- .nk-content-body -->
  </div>
</div>

<!-- Coin Rate Modal -->
<div class="modal fade" id="coinRateModal" tabindex="-1">
  <div class="modal-dialog modal-dialog-centered">
    <form method="post" class="modal-content">
      <input type="hidden" name="action" value="save_coin_rate">
      <input type="hidden" name="rate_id" id="cr_rate_id">
      <div class="modal-header">
        <h6 class="modal-title">Coin Rate</h6>
        <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
      </div>
      <div class="modal-body">
        <div class="row g-2">
          <div class="col-4">
            <label class="form-label small">Coin *</label>
            <input type="text" class="form-control" name="coin" id="cr_coin" placeholder="BTC, ETH">
          </div>
          <div class="col-8">
            <label class="form-label small">Name</label>
            <input type="text" class="form-control" name="coin_name" id="cr_coin_name" placeholder="Bitcoin">
          </div>
          <div class="col-12">
            <label class="form-label small">Icon (path or filename)</label>
            <input type="text" class="form-control" name="coin_icon" id="cr_coin_icon" placeholder="btc.svg">
          </div>
          <div class="col-6">
            <label class="form-label small">Sell Rate</label>
            <input type="number" step="0.00000001" class="form-control" name="sell_rate" id="cr_sell_rate">
          </div>
          <div class="col-6">
            <label class="form-label small">Buy Rate</label>
            <input type="number" step="0.00000001" class="form-control" name="buy_rate" id="cr_buy_rate">
          </div>
          <div class="col-6">
            <label class="form-label small">Min Swap</label>
            <input type="number" step="0.00000001" class="form-control" name="min_swap" id="cr_min_swap">
          </div>
          <div class="col-6">
            <label class="form-label small">Margin %</label>
            <input type="number" step="0.01" class="form-control" name="margin_percent" id="cr_margin_percent">
          </div>
          <div class="col-6">
            <div class="form-check form-switch mt-3">
              <input class="form-check-input" type="checkbox" name="use_online_rate" id="cr_use_online">
              <label class="form-check-label" for="cr_use_online">Use Online Rate</label>
            </div>
          </div>
          <div class="col-6">
            <div class="form-check form-switch mt-3">
              <input class="form-check-input" type="checkbox" name="auto_update" id="cr_auto_update">
              <label class="form-check-label" for="cr_auto_update">Auto Update</label>
            </div>
          </div>
        </div>
      </div>
      <div class="modal-footer">
        <button class="btn btn-primary">Save</button>
        <button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Close</button>
      </div>
    </form>
  </div>
</div>

<!-- Withdraw Fee Modal -->
<div class="modal fade" id="feeModal" tabindex="-1">
  <div class="modal-dialog modal-dialog-centered">
    <form method="post" class="modal-content">
      <input type="hidden" name="action" value="save_fee">
      <input type="hidden" name="fee_id" id="wf_fee_id">
      <div class="modal-header">
        <h6 class="modal-title">Withdraw Fee</h6>
        <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
      </div>
      <div class="modal-body">
        <div class="row g-2">
          <div class="col-4">
            <label class="form-label small">Coin *</label>
            <input type="text" class="form-control" name="fee_coin" id="wf_coin" placeholder="BTC, NGN">
          </div>
          <div class="col-8">
            <label class="form-label small">Type *</label>
            <select class="form-select" name="fee_type" id="wf_type">
              <option value="crypto">crypto</option>
              <option value="fiat">fiat</option>
            </select>
          </div>
          <div class="col-6">
            <label class="form-label small">Threshold</label>
            <input type="number" step="0.01" class="form-control" name="threshold" id="wf_threshold">
          </div>
          <div class="col-6">
            <label class="form-label small">Percent Fee</label>
            <input type="number" step="0.0001" class="form-control" name="percent_fee" id="wf_percent">
          </div>
          <div class="col-6">
            <label class="form-label small">Flat Fee</label>
            <input type="number" step="0.01" class="form-control" name="flat_fee" id="wf_flat">
          </div>
          <div class="col-6">
            <label class="form-label small">Max Fee</label>
            <input type="number" step="0.01" class="form-control" name="max_fee" id="wf_max">
          </div>
          <div class="col-12">
            <label class="form-label small">Note</label>
            <input type="text" class="form-control" name="note" id="wf_note" placeholder="Optional note">
          </div>
        </div>
      </div>
      <div class="modal-footer">
        <button class="btn btn-primary">Save</button>
        <button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Close</button>
      </div>
    </form>
  </div>
</div>

<script>
  // Coin Rate modal helpers
  const CR = {
    resetForm(){
      document.getElementById('cr_rate_id').value = '';
      document.getElementById('cr_coin').value = '';
      document.getElementById('cr_coin_name').value = '';
      document.getElementById('cr_coin_icon').value = '';
      document.getElementById('cr_sell_rate').value = '';
      document.getElementById('cr_buy_rate').value = '';
      document.getElementById('cr_min_swap').value = '';
      document.getElementById('cr_margin_percent').value = '';
      document.getElementById('cr_use_online').checked = false;
      document.getElementById('cr_auto_update').checked = false;
    },
    edit(row){
      CR.resetForm();
      document.getElementById('cr_rate_id').value = row.rate_id || '';
      document.getElementById('cr_coin').value = row.coin || '';
      document.getElementById('cr_coin_name').value = row.coin_name || '';
      document.getElementById('cr_coin_icon').value = row.coin_icon || '';
      document.getElementById('cr_sell_rate').value = row.sell_rate || '';
      document.getElementById('cr_buy_rate').value = row.buy_rate || '';
      document.getElementById('cr_min_swap').value = row.min_swap || '';
      document.getElementById('cr_margin_percent').value = row.margin_percent || '';
      document.getElementById('cr_use_online').checked = (row.use_online_rate == 1);
      document.getElementById('cr_auto_update').checked = (row.auto_update == 1);
    }
  };

  // Withdraw Fee modal helpers
  const WF = {
    resetForm(){
      document.getElementById('wf_fee_id').value = '';
      document.getElementById('wf_coin').value = '';
      document.getElementById('wf_type').value = 'crypto';
      document.getElementById('wf_threshold').value = '';
      document.getElementById('wf_percent').value = '';
      document.getElementById('wf_flat').value = '';
      document.getElementById('wf_max').value = '';
      document.getElementById('wf_note').value = '';
    },
    edit(row){
      WF.resetForm();
      document.getElementById('wf_fee_id').value = row.fee_id || '';
      document.getElementById('wf_coin').value = row.coin || '';
      document.getElementById('wf_type').value = row.type || 'crypto';
      document.getElementById('wf_threshold').value = row.threshold || '';
      document.getElementById('wf_percent').value = row.percent_fee || '';
      document.getElementById('wf_flat').value = row.flat_fee || '';
      document.getElementById('wf_max').value = row.max_fee || '';
      document.getElementById('wf_note').value = row.note || '';
    }
  };
</script>

<?php include '../common/footer.php'; ?>

Выполнить команду


Для локальной разработки. Не используйте в интернете!