Session log: KVOI bio, network scanning, git sync fix
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
426
projects/msp-tools/quote-wizard/admin/index.php
Normal file
426
projects/msp-tools/quote-wizard/admin/index.php
Normal file
@@ -0,0 +1,426 @@
|
||||
<?php
|
||||
/**
|
||||
* MSP Quote Wizard - Admin Dashboard
|
||||
* Simple PHP admin interface for viewing and managing quotes
|
||||
*/
|
||||
|
||||
session_start();
|
||||
|
||||
// Configuration
|
||||
define('ADMIN_PASSWORD', 'QuoteAdmin2026!'); // Change in production
|
||||
define('DB_HOST', '172.16.3.30');
|
||||
define('DB_NAME', 'claudetools');
|
||||
define('DB_USER', 'claudetools');
|
||||
define('DB_PASS', 'CT_e8fcd5a3952030a79ed6debae6c954ed');
|
||||
|
||||
// Handle logout
|
||||
if (isset($_GET['logout'])) {
|
||||
session_destroy();
|
||||
header('Location: index.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
// Handle login
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['password'])) {
|
||||
if ($_POST['password'] === ADMIN_PASSWORD) {
|
||||
$_SESSION['admin_authenticated'] = true;
|
||||
header('Location: index.php');
|
||||
exit;
|
||||
} else {
|
||||
$login_error = 'Invalid password';
|
||||
}
|
||||
}
|
||||
|
||||
// Check authentication
|
||||
if (!isset($_SESSION['admin_authenticated']) || !$_SESSION['admin_authenticated']) {
|
||||
showLoginPage($login_error ?? null);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Database connection
|
||||
try {
|
||||
$pdo = new PDO(
|
||||
"mysql:host=" . DB_HOST . ";dbname=" . DB_NAME . ";charset=utf8mb4",
|
||||
DB_USER,
|
||||
DB_PASS,
|
||||
[PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]
|
||||
);
|
||||
} catch (PDOException $e) {
|
||||
die("Database connection failed: " . $e->getMessage());
|
||||
}
|
||||
|
||||
// Handle status update
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['update_status'])) {
|
||||
$quote_id = $_POST['quote_id'];
|
||||
$new_status = $_POST['new_status'];
|
||||
$stmt = $pdo->prepare("UPDATE quotes SET status = ?, updated_at = NOW() WHERE id = ?");
|
||||
$stmt->execute([$new_status, $quote_id]);
|
||||
header('Location: index.php?updated=1');
|
||||
exit;
|
||||
}
|
||||
|
||||
// Get statistics
|
||||
$stats = getStats($pdo);
|
||||
|
||||
// Get quotes with filtering
|
||||
$status_filter = $_GET['status'] ?? '';
|
||||
$search = $_GET['search'] ?? '';
|
||||
$quotes = getQuotes($pdo, $status_filter, $search);
|
||||
|
||||
// Get single quote details if requested
|
||||
$quote_detail = null;
|
||||
if (isset($_GET['id'])) {
|
||||
$quote_detail = getQuoteDetail($pdo, $_GET['id']);
|
||||
}
|
||||
|
||||
// Helper functions
|
||||
function getStats($pdo) {
|
||||
$stats = [];
|
||||
|
||||
// Total quotes
|
||||
$stmt = $pdo->query("SELECT COUNT(*) FROM quotes");
|
||||
$stats['total'] = $stmt->fetchColumn();
|
||||
|
||||
// By status
|
||||
$stmt = $pdo->query("SELECT status, COUNT(*) as count FROM quotes GROUP BY status");
|
||||
$stats['by_status'] = $stmt->fetchAll(PDO::FETCH_KEY_PAIR);
|
||||
|
||||
// Total monthly value (submitted quotes only)
|
||||
$stmt = $pdo->query("SELECT COALESCE(SUM(monthly_total), 0) FROM quotes WHERE status = 'submitted'");
|
||||
$stats['total_monthly'] = $stmt->fetchColumn();
|
||||
|
||||
// This month
|
||||
$stmt = $pdo->query("SELECT COUNT(*) FROM quotes WHERE created_at >= DATE_FORMAT(NOW(), '%Y-%m-01')");
|
||||
$stats['this_month'] = $stmt->fetchColumn();
|
||||
|
||||
return $stats;
|
||||
}
|
||||
|
||||
function getQuotes($pdo, $status_filter = '', $search = '') {
|
||||
$sql = "SELECT q.*,
|
||||
(SELECT COUNT(*) FROM quote_items WHERE quote_id = q.id) as item_count
|
||||
FROM quotes q WHERE 1=1";
|
||||
$params = [];
|
||||
|
||||
if ($status_filter) {
|
||||
$sql .= " AND q.status = ?";
|
||||
$params[] = $status_filter;
|
||||
}
|
||||
|
||||
if ($search) {
|
||||
$sql .= " AND (q.company_name LIKE ? OR q.contact_name LIKE ? OR q.contact_email LIKE ?)";
|
||||
$params[] = "%$search%";
|
||||
$params[] = "%$search%";
|
||||
$params[] = "%$search%";
|
||||
}
|
||||
|
||||
$sql .= " ORDER BY q.created_at DESC LIMIT 100";
|
||||
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$stmt->execute($params);
|
||||
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
function getQuoteDetail($pdo, $id) {
|
||||
// Get quote
|
||||
$stmt = $pdo->prepare("SELECT * FROM quotes WHERE id = ?");
|
||||
$stmt->execute([$id]);
|
||||
$quote = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!$quote) return null;
|
||||
|
||||
// Get items
|
||||
$stmt = $pdo->prepare("SELECT * FROM quote_items WHERE quote_id = ? ORDER BY category, created_at");
|
||||
$stmt->execute([$id]);
|
||||
$quote['items'] = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
// Get activity
|
||||
$stmt = $pdo->prepare("SELECT * FROM quote_activity WHERE quote_id = ? ORDER BY created_at DESC");
|
||||
$stmt->execute([$id]);
|
||||
$quote['activities'] = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
return $quote;
|
||||
}
|
||||
|
||||
function showLoginPage($error = null) {
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Quote Admin - Login</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
</head>
|
||||
<body class="bg-gray-900 min-h-screen flex items-center justify-center">
|
||||
<div class="bg-gray-800 p-8 rounded-lg shadow-xl w-full max-w-md">
|
||||
<h1 class="text-2xl font-bold text-white mb-6 text-center">Quote Admin</h1>
|
||||
<?php if ($error): ?>
|
||||
<div class="bg-red-500/20 border border-red-500 text-red-400 px-4 py-2 rounded mb-4">
|
||||
<?= htmlspecialchars($error) ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<form method="POST">
|
||||
<div class="mb-4">
|
||||
<label class="block text-gray-400 text-sm mb-2">Password</label>
|
||||
<input type="password" name="password" required autofocus
|
||||
class="w-full bg-gray-700 border border-gray-600 rounded px-4 py-2 text-white focus:outline-none focus:border-blue-500">
|
||||
</div>
|
||||
<button type="submit" class="w-full bg-blue-600 hover:bg-blue-700 text-white font-semibold py-2 px-4 rounded transition">
|
||||
Login
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
<?php
|
||||
exit;
|
||||
}
|
||||
|
||||
function formatMoney($amount) {
|
||||
return '$' . number_format((float)$amount, 2);
|
||||
}
|
||||
|
||||
function statusBadge($status) {
|
||||
$colors = [
|
||||
'draft' => 'bg-gray-500',
|
||||
'submitted' => 'bg-blue-500',
|
||||
'viewed' => 'bg-purple-500',
|
||||
'followed_up' => 'bg-yellow-500',
|
||||
'converted' => 'bg-green-500',
|
||||
'expired' => 'bg-red-500',
|
||||
];
|
||||
$color = $colors[$status] ?? 'bg-gray-500';
|
||||
return "<span class=\"px-2 py-1 rounded text-xs font-medium text-white $color\">" . ucfirst(str_replace('_', ' ', $status)) . "</span>";
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Quote Admin Dashboard</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<style>
|
||||
.stat-card { transition: transform 0.2s; }
|
||||
.stat-card:hover { transform: translateY(-2px); }
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-gray-900 min-h-screen text-gray-100">
|
||||
<!-- Header -->
|
||||
<header class="bg-gray-800 border-b border-gray-700">
|
||||
<div class="max-w-7xl mx-auto px-4 py-4 flex items-center justify-between">
|
||||
<div class="flex items-center gap-4">
|
||||
<h1 class="text-xl font-bold text-white">Quote Admin</h1>
|
||||
<span class="text-gray-400 text-sm">azcomputerguru.com</span>
|
||||
</div>
|
||||
<a href="?logout=1" class="text-gray-400 hover:text-white text-sm">Logout</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="max-w-7xl mx-auto px-4 py-6">
|
||||
<?php if (isset($_GET['updated'])): ?>
|
||||
<div class="bg-green-500/20 border border-green-500 text-green-400 px-4 py-2 rounded mb-6">
|
||||
Quote status updated successfully.
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Stats Cards -->
|
||||
<div class="grid grid-cols-2 md:grid-cols-4 gap-4 mb-8">
|
||||
<div class="stat-card bg-gray-800 rounded-lg p-4 border border-gray-700">
|
||||
<div class="text-gray-400 text-sm">Total Quotes</div>
|
||||
<div class="text-3xl font-bold text-white"><?= $stats['total'] ?></div>
|
||||
</div>
|
||||
<div class="stat-card bg-gray-800 rounded-lg p-4 border border-gray-700">
|
||||
<div class="text-gray-400 text-sm">Submitted</div>
|
||||
<div class="text-3xl font-bold text-blue-400"><?= $stats['by_status']['submitted'] ?? 0 ?></div>
|
||||
</div>
|
||||
<div class="stat-card bg-gray-800 rounded-lg p-4 border border-gray-700">
|
||||
<div class="text-gray-400 text-sm">Converted</div>
|
||||
<div class="text-3xl font-bold text-green-400"><?= $stats['by_status']['converted'] ?? 0 ?></div>
|
||||
</div>
|
||||
<div class="stat-card bg-gray-800 rounded-lg p-4 border border-gray-700">
|
||||
<div class="text-gray-400 text-sm">Monthly Value</div>
|
||||
<div class="text-2xl font-bold text-emerald-400"><?= formatMoney($stats['total_monthly']) ?></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if ($quote_detail): ?>
|
||||
<!-- Quote Detail View -->
|
||||
<div class="bg-gray-800 rounded-lg border border-gray-700 mb-6">
|
||||
<div class="p-4 border-b border-gray-700 flex items-center justify-between">
|
||||
<h2 class="text-lg font-semibold">Quote Details</h2>
|
||||
<a href="index.php" class="text-blue-400 hover:text-blue-300 text-sm">← Back to List</a>
|
||||
</div>
|
||||
<div class="p-4">
|
||||
<div class="grid md:grid-cols-2 gap-6">
|
||||
<!-- Contact Info -->
|
||||
<div>
|
||||
<h3 class="text-sm font-medium text-gray-400 mb-3">Contact Information</h3>
|
||||
<div class="space-y-2">
|
||||
<div><span class="text-gray-500">Company:</span> <span class="text-white"><?= htmlspecialchars($quote_detail['company_name'] ?: '(not provided)') ?></span></div>
|
||||
<div><span class="text-gray-500">Contact:</span> <span class="text-white"><?= htmlspecialchars($quote_detail['contact_name'] ?: '(not provided)') ?></span></div>
|
||||
<div><span class="text-gray-500">Email:</span> <span class="text-white"><?= htmlspecialchars($quote_detail['contact_email'] ?: '(not provided)') ?></span></div>
|
||||
<div><span class="text-gray-500">Phone:</span> <span class="text-white"><?= htmlspecialchars($quote_detail['contact_phone'] ?: '(not provided)') ?></span></div>
|
||||
<div><span class="text-gray-500">Employees:</span> <span class="text-white"><?= $quote_detail['employee_count'] ?></span></div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Quote Summary -->
|
||||
<div>
|
||||
<h3 class="text-sm font-medium text-gray-400 mb-3">Quote Summary</h3>
|
||||
<div class="space-y-2">
|
||||
<div><span class="text-gray-500">Status:</span> <?= statusBadge($quote_detail['status']) ?></div>
|
||||
<div><span class="text-gray-500">Monthly Total:</span> <span class="text-2xl font-bold text-emerald-400"><?= formatMoney($quote_detail['monthly_total']) ?></span></div>
|
||||
<div><span class="text-gray-500">Setup Total:</span> <span class="text-white"><?= formatMoney($quote_detail['setup_total']) ?></span></div>
|
||||
<div><span class="text-gray-500">Created:</span> <span class="text-white"><?= $quote_detail['created_at'] ?></span></div>
|
||||
<?php if ($quote_detail['submitted_at']): ?>
|
||||
<div><span class="text-gray-500">Submitted:</span> <span class="text-white"><?= $quote_detail['submitted_at'] ?></span></div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<!-- Status Update Form -->
|
||||
<form method="POST" class="mt-4">
|
||||
<input type="hidden" name="quote_id" value="<?= $quote_detail['id'] ?>">
|
||||
<div class="flex gap-2">
|
||||
<select name="new_status" class="bg-gray-700 border border-gray-600 rounded px-3 py-2 text-white text-sm">
|
||||
<option value="draft" <?= $quote_detail['status'] === 'draft' ? 'selected' : '' ?>>Draft</option>
|
||||
<option value="submitted" <?= $quote_detail['status'] === 'submitted' ? 'selected' : '' ?>>Submitted</option>
|
||||
<option value="viewed" <?= $quote_detail['status'] === 'viewed' ? 'selected' : '' ?>>Viewed</option>
|
||||
<option value="followed_up" <?= $quote_detail['status'] === 'followed_up' ? 'selected' : '' ?>>Followed Up</option>
|
||||
<option value="converted" <?= $quote_detail['status'] === 'converted' ? 'selected' : '' ?>>Converted</option>
|
||||
<option value="expired" <?= $quote_detail['status'] === 'expired' ? 'selected' : '' ?>>Expired</option>
|
||||
</select>
|
||||
<button type="submit" name="update_status" value="1" class="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded text-sm">
|
||||
Update Status
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Line Items -->
|
||||
<?php if (!empty($quote_detail['items'])): ?>
|
||||
<div class="mt-6">
|
||||
<h3 class="text-sm font-medium text-gray-400 mb-3">Line Items</h3>
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full text-sm">
|
||||
<thead>
|
||||
<tr class="text-left text-gray-400 border-b border-gray-700">
|
||||
<th class="pb-2">Category</th>
|
||||
<th class="pb-2">Product</th>
|
||||
<th class="pb-2">Qty</th>
|
||||
<th class="pb-2 text-right">Unit Price</th>
|
||||
<th class="pb-2 text-right">Monthly</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($quote_detail['items'] as $item): ?>
|
||||
<tr class="border-b border-gray-700/50">
|
||||
<td class="py-2 text-gray-400"><?= ucfirst(str_replace('_', ' ', $item['category'])) ?></td>
|
||||
<td class="py-2 text-white"><?= htmlspecialchars($item['product_name']) ?></td>
|
||||
<td class="py-2"><?= $item['quantity'] ?></td>
|
||||
<td class="py-2 text-right"><?= formatMoney($item['unit_price']) ?></td>
|
||||
<td class="py-2 text-right text-emerald-400"><?= formatMoney($item['monthly_amount']) ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Activity Log -->
|
||||
<?php if (!empty($quote_detail['activities'])): ?>
|
||||
<div class="mt-6">
|
||||
<h3 class="text-sm font-medium text-gray-400 mb-3">Activity Log</h3>
|
||||
<div class="space-y-2">
|
||||
<?php foreach ($quote_detail['activities'] as $activity): ?>
|
||||
<div class="text-sm">
|
||||
<span class="text-gray-500"><?= $activity['created_at'] ?></span>
|
||||
<span class="text-gray-400 mx-2">|</span>
|
||||
<span class="text-white"><?= ucfirst($activity['action']) ?></span>
|
||||
<?php if ($activity['step_name']): ?>
|
||||
<span class="text-gray-500">(<?= $activity['step_name'] ?>)</span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Filters -->
|
||||
<div class="bg-gray-800 rounded-lg border border-gray-700 mb-6">
|
||||
<form method="GET" class="p-4 flex flex-wrap gap-4 items-end">
|
||||
<div>
|
||||
<label class="block text-gray-400 text-sm mb-1">Status</label>
|
||||
<select name="status" class="bg-gray-700 border border-gray-600 rounded px-3 py-2 text-white text-sm">
|
||||
<option value="">All Statuses</option>
|
||||
<option value="draft" <?= $status_filter === 'draft' ? 'selected' : '' ?>>Draft</option>
|
||||
<option value="submitted" <?= $status_filter === 'submitted' ? 'selected' : '' ?>>Submitted</option>
|
||||
<option value="viewed" <?= $status_filter === 'viewed' ? 'selected' : '' ?>>Viewed</option>
|
||||
<option value="followed_up" <?= $status_filter === 'followed_up' ? 'selected' : '' ?>>Followed Up</option>
|
||||
<option value="converted" <?= $status_filter === 'converted' ? 'selected' : '' ?>>Converted</option>
|
||||
<option value="expired" <?= $status_filter === 'expired' ? 'selected' : '' ?>>Expired</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="flex-1 min-w-[200px]">
|
||||
<label class="block text-gray-400 text-sm mb-1">Search</label>
|
||||
<input type="text" name="search" value="<?= htmlspecialchars($search) ?>" placeholder="Company, contact, or email..."
|
||||
class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-white text-sm">
|
||||
</div>
|
||||
<button type="submit" class="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded text-sm">
|
||||
Filter
|
||||
</button>
|
||||
<?php if ($status_filter || $search): ?>
|
||||
<a href="index.php" class="text-gray-400 hover:text-white text-sm px-4 py-2">Clear</a>
|
||||
<?php endif; ?>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Quotes Table -->
|
||||
<div class="bg-gray-800 rounded-lg border border-gray-700 overflow-hidden">
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full text-sm">
|
||||
<thead>
|
||||
<tr class="text-left text-gray-400 bg-gray-800/50">
|
||||
<th class="px-4 py-3">Date</th>
|
||||
<th class="px-4 py-3">Company</th>
|
||||
<th class="px-4 py-3">Contact</th>
|
||||
<th class="px-4 py-3">Status</th>
|
||||
<th class="px-4 py-3">Items</th>
|
||||
<th class="px-4 py-3 text-right">Monthly</th>
|
||||
<th class="px-4 py-3"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (empty($quotes)): ?>
|
||||
<tr>
|
||||
<td colspan="7" class="px-4 py-8 text-center text-gray-500">No quotes found</td>
|
||||
</tr>
|
||||
<?php else: ?>
|
||||
<?php foreach ($quotes as $quote): ?>
|
||||
<tr class="border-t border-gray-700/50 hover:bg-gray-700/30">
|
||||
<td class="px-4 py-3 text-gray-400"><?= date('M j, Y', strtotime($quote['created_at'])) ?></td>
|
||||
<td class="px-4 py-3 text-white"><?= htmlspecialchars($quote['company_name'] ?: '(not provided)') ?></td>
|
||||
<td class="px-4 py-3">
|
||||
<div class="text-white"><?= htmlspecialchars($quote['contact_name'] ?: '-') ?></div>
|
||||
<div class="text-gray-500 text-xs"><?= htmlspecialchars($quote['contact_email'] ?: '') ?></div>
|
||||
</td>
|
||||
<td class="px-4 py-3"><?= statusBadge($quote['status']) ?></td>
|
||||
<td class="px-4 py-3 text-gray-400"><?= $quote['item_count'] ?></td>
|
||||
<td class="px-4 py-3 text-right text-emerald-400 font-medium"><?= formatMoney($quote['monthly_total']) ?></td>
|
||||
<td class="px-4 py-3 text-right">
|
||||
<a href="?id=<?= $quote['id'] ?>" class="text-blue-400 hover:text-blue-300">View</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
@@ -319,3 +319,103 @@ Note: Must use `$'...'` quoting for the `!` in the password.
|
||||
8. **XenServer iptables persistence** — The SSH firewall rule added is not persistent across reboots
|
||||
9. **PBX management IP changed** — Was 192.168.100.2, now .196 via DHCP. Should set static.
|
||||
10. **Java 8 is now default JRE** — May need to switch back to Java 25 for other tasks: `sudo archlinux-java set java-25-openjdk`
|
||||
|
||||
---
|
||||
|
||||
## Update: 09:35 — KVOI Bio, Network Scanning, Git Sync (MacBook Air)
|
||||
|
||||
### Session Summary
|
||||
|
||||
Light session on MacBook Air: wrote radio show bio for KVOI website, attempted to find Valleywide iLO credentials, scanned local network for VMware login pages, fixed git permissions, synced with Gitea.
|
||||
|
||||
### 1. KVOI Radio Show Bio
|
||||
|
||||
**Created bio/blurb for The Computer Guru Show on KVOI:**
|
||||
|
||||
> Mike Swanson has been the Tucson community's go-to resource for technology solutions that make sense for over 20 years. Since founding Arizona Computer Guru in 2001 and launching The Computer Guru Show in 2009, his mission has stayed the same: solve your technology problems while treating you like a person in the process. Whether you're a home user battling a stubborn computer or a business owner looking for IT support that actually speaks your language, The Computer Guru Show delivers straight answers without the jargon or the drama. No politics, no fluff - just real solutions from someone who's been in the trenches. Tune in Saturdays at 9am, call in at 520-790-2040, or visit gurushow.com.
|
||||
|
||||
**Key details included:**
|
||||
- Mike Swanson name
|
||||
- Founded Arizona Computer Guru 2001
|
||||
- Show launched 2009
|
||||
- "Over 20 years" (evergreen)
|
||||
- Call-in: 520-790-2040
|
||||
- Website: gurushow.com (redirects to radio.azcomputerguru.com)
|
||||
- Tone: Expert + approachable, no politics
|
||||
|
||||
**To be used on:**
|
||||
- KVOI website
|
||||
- radio.azcomputerguru.com
|
||||
|
||||
### 2. Valleywide iLO Credential Search
|
||||
|
||||
**Searched credentials.md for VWP iLO creds — not found there.**
|
||||
|
||||
**Only iLO entry in credentials.md:**
|
||||
- HP iLO (172.16.9.125): root / r3tr0gradE99#
|
||||
- This is local lab infrastructure, not VWP
|
||||
|
||||
**VWP iDRAC credentials (already documented in earlier session):**
|
||||
- XenServer R720 iDRAC (192.168.3.30): root / r3tr0gradE99#
|
||||
- QB Server R640 iDRAC (192.168.3.189): root / r3tr0gradE99#
|
||||
|
||||
### 3. Network Scan for VMware Login Pages
|
||||
|
||||
**Scanned three network ranges for VMware/ESXi web interfaces:**
|
||||
|
||||
| Network | Status | Findings |
|
||||
|---------|--------|----------|
|
||||
| 192.168.3.x | Not reachable | Client site (VWP) |
|
||||
| 192.168.0.x | Not reachable | Client site (VWP) |
|
||||
| 172.16.9.x | Scanned successfully | See below |
|
||||
|
||||
**172.16.9.x discoveries:**
|
||||
- **172.16.9.1** — UniFi Dream Machine Pro (UDM Pro)
|
||||
- **172.16.9.124** — Avigilon security camera (SSL cert: AVIGILON-CAMERA-9C-H4A-3MH-270-112011126624)
|
||||
- **172.16.9.125** — HP iLO (as expected)
|
||||
|
||||
**No VMware/ESXi found on any reachable network.**
|
||||
|
||||
**Note:** `nmap` is not installed on MacBook Air. Used curl-based scanning instead.
|
||||
|
||||
### 4. Git Permissions Fix
|
||||
|
||||
**Problem:** Sync failed with "insufficient permission for adding an object to repository database"
|
||||
|
||||
**Root cause:** Some directories in `.git/objects/` were owned by `root` instead of `azcomputerguru`:
|
||||
```
|
||||
drwxr-xr-x 3 root staff 96 Mar 13 06:12 01
|
||||
```
|
||||
|
||||
**Fix:** User ran manually:
|
||||
```bash
|
||||
sudo chown -R azcomputerguru:staff /Users/azcomputerguru/ClaudeTools/.git/objects/
|
||||
```
|
||||
|
||||
### 5. Gitea Sync
|
||||
|
||||
**Successfully synced with Gitea after permissions fix.**
|
||||
|
||||
**Pulled 11 files:**
|
||||
- credentials.md (updated)
|
||||
- session-logs/2026-03-19-session.md (new)
|
||||
- session-logs/2026-03-20-session.md (new)
|
||||
- 7 new forum posts in docs/forum-posts/
|
||||
- projects/community-forum/theme-v2.less
|
||||
|
||||
**Recent commits from other machines:**
|
||||
- VWP infra docs, iDRAC fixes, XenServer inventory, PBX triage
|
||||
- Flarum theme v2, Matomo analytics, Cloudflare proxy re-enabled
|
||||
- Workstation setup, ESXi license resets, FreePBX phone system fix
|
||||
|
||||
### Infrastructure Notes
|
||||
|
||||
**MacBook Air network access:**
|
||||
- Can reach 172.16.9.x (home/lab network)
|
||||
- Cannot reach 192.168.0.x or 192.168.3.x (VWP client network — need VPN)
|
||||
|
||||
### Pending/Incomplete
|
||||
|
||||
1. **KVOI bio** — Ready to publish, may need similar for radio.azcomputerguru.com
|
||||
2. **VMware scan at VWP** — Need VPN access to scan 192.168.0.x and 192.168.3.x
|
||||
3. **Install nmap on MacBook Air** — Would improve network scanning: `brew install nmap`
|
||||
|
||||
Reference in New Issue
Block a user