1043 lines
46 KiB
HTML
1043 lines
46 KiB
HTML
{% extends 'base.html' %}
|
|
{% load static %}
|
|
|
|
{% block title %}Cache Management{% endblock %}
|
|
|
|
{% block css %}
|
|
<link href="{% static 'assets/plugins/apexcharts/dist/apexcharts.css' %}" rel="stylesheet" />
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<div id="content" class="app-content">
|
|
<div class="container">
|
|
<ul class="breadcrumb">
|
|
<li class="breadcrumb-item"><a href="{% url 'core:dashboard' %}">Dashboard</a></li>
|
|
<li class="breadcrumb-item active">Cache Management</li>
|
|
</ul>
|
|
|
|
<div class="row align-items-center mb-3">
|
|
<div class="col">
|
|
<h1 class="page-header">Cache Management</h1>
|
|
<p class="text-muted">Monitor and manage system cache performance</p>
|
|
</div>
|
|
<div class="col-auto">
|
|
<div class="btn-group">
|
|
<button type="button" class="btn btn-outline-primary" onclick="refreshCacheStats()">
|
|
<i class="fa fa-sync me-2"></i>Refresh Stats
|
|
</button>
|
|
<button type="button" class="btn btn-outline-warning" onclick="clearAllCaches()">
|
|
<i class="fa fa-trash me-2"></i>Clear All
|
|
</button>
|
|
<button type="button" class="btn btn-outline-info" onclick="optimizeCache()">
|
|
<i class="fa fa-magic me-2"></i>Optimize
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Cache Overview -->
|
|
<div class="row mb-4">
|
|
<div class="col-md-3">
|
|
<div class="card">
|
|
<div class="card-body text-center">
|
|
<div class="fs-24px fw-600 text-primary" id="hitRateValue">94.2%</div>
|
|
<h6 class="text-muted">Hit Rate</h6>
|
|
<p class="text-muted small mb-0">Last 24 hours</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="card">
|
|
<div class="card-body text-center">
|
|
<div class="fs-24px fw-600 text-success" id="totalKeysValue">45,231</div>
|
|
<h6 class="text-muted">Total Keys</h6>
|
|
<p class="text-muted small mb-0">Across all caches</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="card">
|
|
<div class="card-body text-center">
|
|
<div class="fs-24px fw-600 text-info" id="memoryUsageValue">2.1 GB</div>
|
|
<h6 class="text-muted">Memory Usage</h6>
|
|
<p class="text-muted small mb-0">of 4 GB allocated</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="card">
|
|
<div class="card-body text-center">
|
|
<div class="fs-24px fw-600 text-warning" id="avgResponseValue">2.3ms</div>
|
|
<h6 class="text-muted">Avg Response</h6>
|
|
<p class="text-muted small mb-0">Cache lookup time</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Cache Performance Charts -->
|
|
<div class="row mb-4">
|
|
<div class="col-md-8">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h4 class="card-title">Cache Performance</h4>
|
|
<div class="card-tools">
|
|
<select id="chartTimeRange" class="form-select form-select-sm">
|
|
<option value="1h">Last Hour</option>
|
|
<option value="6h">Last 6 Hours</option>
|
|
<option value="24h" selected>Last 24 Hours</option>
|
|
<option value="7d">Last 7 Days</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="card-body">
|
|
<div id="performanceChart" style="height: 300px;"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-md-4">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h4 class="card-title">Cache Distribution</h4>
|
|
</div>
|
|
<div class="card-body">
|
|
<div id="distributionChart" style="height: 300px;"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Cache Instances -->
|
|
<div class="row mb-4">
|
|
<div class="col-12">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h4 class="card-title">Cache Instances</h4>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row">
|
|
<!-- Redis Cache -->
|
|
<div class="col-md-6 mb-4">
|
|
<div class="card border">
|
|
<div class="card-header bg-light">
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<h5 class="mb-0">
|
|
<i class="fa fa-database text-danger me-2"></i>
|
|
Redis Cache
|
|
</h5>
|
|
<span class="badge bg-success">Online</span>
|
|
</div>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row mb-3">
|
|
<div class="col-6">
|
|
<div class="text-muted small">Memory Usage</div>
|
|
<div class="fw-bold">1.8 GB / 2 GB</div>
|
|
<div class="progress mt-1">
|
|
<div class="progress-bar bg-danger" style="width: 90%"></div>
|
|
</div>
|
|
</div>
|
|
<div class="col-6">
|
|
<div class="text-muted small">Hit Rate</div>
|
|
<div class="fw-bold text-success">96.8%</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row mb-3">
|
|
<div class="col-6">
|
|
<div class="text-muted small">Keys</div>
|
|
<div class="fw-bold">32,145</div>
|
|
</div>
|
|
<div class="col-6">
|
|
<div class="text-muted small">Connections</div>
|
|
<div class="fw-bold">24</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row mb-3">
|
|
<div class="col-6">
|
|
<div class="text-muted small">Ops/sec</div>
|
|
<div class="fw-bold">1,247</div>
|
|
</div>
|
|
<div class="col-6">
|
|
<div class="text-muted small">Evictions</div>
|
|
<div class="fw-bold text-warning">12</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="d-flex gap-2">
|
|
<button class="btn btn-outline-primary btn-sm" onclick="flushCache('redis')">
|
|
<i class="fa fa-trash me-1"></i>Flush
|
|
</button>
|
|
<button class="btn btn-outline-info btn-sm" onclick="viewCacheKeys('redis')">
|
|
<i class="fa fa-key me-1"></i>Keys
|
|
</button>
|
|
<button class="btn btn-outline-success btn-sm" onclick="analyzeCacheUsage('redis')">
|
|
<i class="fa fa-chart-bar me-1"></i>Analyze
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Memcached -->
|
|
<div class="col-md-6 mb-4">
|
|
<div class="card border">
|
|
<div class="card-header bg-light">
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<h5 class="mb-0">
|
|
<i class="fa fa-server text-info me-2"></i>
|
|
Memcached
|
|
</h5>
|
|
<span class="badge bg-success">Online</span>
|
|
</div>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row mb-3">
|
|
<div class="col-6">
|
|
<div class="text-muted small">Memory Usage</div>
|
|
<div class="fw-bold">256 MB / 512 MB</div>
|
|
<div class="progress mt-1">
|
|
<div class="progress-bar bg-info" style="width: 50%"></div>
|
|
</div>
|
|
</div>
|
|
<div class="col-6">
|
|
<div class="text-muted small">Hit Rate</div>
|
|
<div class="fw-bold text-success">89.2%</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row mb-3">
|
|
<div class="col-6">
|
|
<div class="text-muted small">Items</div>
|
|
<div class="fw-bold">8,456</div>
|
|
</div>
|
|
<div class="col-6">
|
|
<div class="text-muted small">Connections</div>
|
|
<div class="fw-bold">12</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row mb-3">
|
|
<div class="col-6">
|
|
<div class="text-muted small">Gets/sec</div>
|
|
<div class="fw-bold">456</div>
|
|
</div>
|
|
<div class="col-6">
|
|
<div class="text-muted small">Sets/sec</div>
|
|
<div class="fw-bold">89</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="d-flex gap-2">
|
|
<button class="btn btn-outline-primary btn-sm" onclick="flushCache('memcached')">
|
|
<i class="fa fa-trash me-1"></i>Flush
|
|
</button>
|
|
<button class="btn btn-outline-info btn-sm" onclick="viewCacheKeys('memcached')">
|
|
<i class="fa fa-key me-1"></i>Items
|
|
</button>
|
|
<button class="btn btn-outline-success btn-sm" onclick="analyzeCacheUsage('memcached')">
|
|
<i class="fa fa-chart-bar me-1"></i>Analyze
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Django Cache -->
|
|
<div class="col-md-6 mb-4">
|
|
<div class="card border">
|
|
<div class="card-header bg-light">
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<h5 class="mb-0">
|
|
<i class="fa fa-code text-success me-2"></i>
|
|
Django Cache
|
|
</h5>
|
|
<span class="badge bg-success">Active</span>
|
|
</div>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row mb-3">
|
|
<div class="col-6">
|
|
<div class="text-muted small">Backend</div>
|
|
<div class="fw-bold">Redis</div>
|
|
</div>
|
|
<div class="col-6">
|
|
<div class="text-muted small">Hit Rate</div>
|
|
<div class="fw-bold text-success">92.1%</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row mb-3">
|
|
<div class="col-6">
|
|
<div class="text-muted small">Cached Views</div>
|
|
<div class="fw-bold">1,234</div>
|
|
</div>
|
|
<div class="col-6">
|
|
<div class="text-muted small">Template Cache</div>
|
|
<div class="fw-bold">567</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row mb-3">
|
|
<div class="col-6">
|
|
<div class="text-muted small">Session Cache</div>
|
|
<div class="fw-bold">89</div>
|
|
</div>
|
|
<div class="col-6">
|
|
<div class="text-muted small">Query Cache</div>
|
|
<div class="fw-bold">2,345</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="d-flex gap-2">
|
|
<button class="btn btn-outline-primary btn-sm" onclick="flushCache('django')">
|
|
<i class="fa fa-trash me-1"></i>Clear
|
|
</button>
|
|
<button class="btn btn-outline-warning btn-sm" onclick="warmupCache()">
|
|
<i class="fa fa-fire me-1"></i>Warmup
|
|
</button>
|
|
<button class="btn btn-outline-success btn-sm" onclick="analyzeCacheUsage('django')">
|
|
<i class="fa fa-chart-bar me-1"></i>Analyze
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Browser Cache -->
|
|
<div class="col-md-6 mb-4">
|
|
<div class="card border">
|
|
<div class="card-header bg-light">
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<h5 class="mb-0">
|
|
<i class="fa fa-globe text-warning me-2"></i>
|
|
Browser Cache
|
|
</h5>
|
|
<span class="badge bg-info">Configured</span>
|
|
</div>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row mb-3">
|
|
<div class="col-6">
|
|
<div class="text-muted small">Static Files</div>
|
|
<div class="fw-bold">1 year</div>
|
|
</div>
|
|
<div class="col-6">
|
|
<div class="text-muted small">Images</div>
|
|
<div class="fw-bold">30 days</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row mb-3">
|
|
<div class="col-6">
|
|
<div class="text-muted small">CSS/JS</div>
|
|
<div class="fw-bold">7 days</div>
|
|
</div>
|
|
<div class="col-6">
|
|
<div class="text-muted small">HTML</div>
|
|
<div class="fw-bold">1 hour</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row mb-3">
|
|
<div class="col-6">
|
|
<div class="text-muted small">Compression</div>
|
|
<div class="fw-bold text-success">Enabled</div>
|
|
</div>
|
|
<div class="col-6">
|
|
<div class="text-muted small">ETags</div>
|
|
<div class="fw-bold text-success">Enabled</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="d-flex gap-2">
|
|
<button class="btn btn-outline-primary btn-sm" onclick="configureBrowserCache()">
|
|
<i class="fa fa-cog me-1"></i>Configure
|
|
</button>
|
|
<button class="btn btn-outline-info btn-sm" onclick="testCacheHeaders()">
|
|
<i class="fa fa-check me-1"></i>Test Headers
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Cache Operations -->
|
|
<div class="row mb-4">
|
|
<div class="col-md-8">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h4 class="card-title">Cache Operations</h4>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<h6>Bulk Operations</h6>
|
|
<div class="d-grid gap-2 mb-3">
|
|
<button class="btn btn-outline-danger" onclick="clearExpiredKeys()">
|
|
<i class="fa fa-clock me-2"></i>Clear Expired Keys
|
|
</button>
|
|
<button class="btn btn-outline-warning" onclick="clearByPattern()">
|
|
<i class="fa fa-search me-2"></i>Clear by Pattern
|
|
</button>
|
|
<button class="btn btn-outline-info" onclick="exportCacheData()">
|
|
<i class="fa fa-download me-2"></i>Export Cache Data
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-md-6">
|
|
<h6>Maintenance</h6>
|
|
<div class="d-grid gap-2 mb-3">
|
|
<button class="btn btn-outline-success" onclick="optimizeMemory()">
|
|
<i class="fa fa-magic me-2"></i>Optimize Memory
|
|
</button>
|
|
<button class="btn btn-outline-primary" onclick="rebuildIndexes()">
|
|
<i class="fa fa-wrench me-2"></i>Rebuild Indexes
|
|
</button>
|
|
<button class="btn btn-outline-secondary" onclick="generateCacheReport()">
|
|
<i class="fa fa-file-alt me-2"></i>Generate Report
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Cache Key Search -->
|
|
<div class="mt-4">
|
|
<h6>Cache Key Management</h6>
|
|
<div class="row">
|
|
<div class="col-md-8">
|
|
<div class="input-group">
|
|
<input type="text" id="keySearchInput" class="form-control" placeholder="Search cache keys (supports wildcards)">
|
|
<button class="btn btn-outline-primary" onclick="searchCacheKeys()">
|
|
<i class="fa fa-search"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<select id="cacheInstanceSelect" class="form-select">
|
|
<option value="all">All Instances</option>
|
|
<option value="redis">Redis</option>
|
|
<option value="memcached">Memcached</option>
|
|
<option value="django">Django Cache</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="searchResults" class="mt-3" style="display: none;">
|
|
<div class="table-responsive">
|
|
<table class="table table-striped">
|
|
<thead>
|
|
<tr>
|
|
<th>Key</th>
|
|
<th>Type</th>
|
|
<th>Size</th>
|
|
<th>TTL</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="searchResultsBody">
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-md-4">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h4 class="card-title">Cache Configuration</h4>
|
|
</div>
|
|
<div class="card-body">
|
|
<form id="cacheConfigForm">
|
|
<div class="mb-3">
|
|
<label class="form-label">Default TTL (seconds)</label>
|
|
<input type="number" name="default_ttl" class="form-control" value="3600">
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label class="form-label">Max Memory (MB)</label>
|
|
<input type="number" name="max_memory" class="form-control" value="2048">
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label class="form-label">Eviction Policy</label>
|
|
<select name="eviction_policy" class="form-select">
|
|
<option value="allkeys-lru">LRU (Least Recently Used)</option>
|
|
<option value="allkeys-lfu">LFU (Least Frequently Used)</option>
|
|
<option value="volatile-lru">Volatile LRU</option>
|
|
<option value="volatile-ttl">Volatile TTL</option>
|
|
<option value="noeviction">No Eviction</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" name="enable_compression" checked>
|
|
<label class="form-check-label">Enable Compression</label>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" name="enable_persistence" checked>
|
|
<label class="form-check-label">Enable Persistence</label>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label class="form-label">Backup Interval (hours)</label>
|
|
<select name="backup_interval" class="form-select">
|
|
<option value="1">Every Hour</option>
|
|
<option value="6">Every 6 Hours</option>
|
|
<option value="12">Every 12 Hours</option>
|
|
<option value="24" selected>Daily</option>
|
|
</select>
|
|
</div>
|
|
|
|
<button type="submit" class="btn btn-primary">
|
|
<i class="fa fa-save me-2"></i>Save Configuration
|
|
</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Cache Keys Modal -->
|
|
<div class="modal fade" id="cacheKeysModal" tabindex="-1">
|
|
<div class="modal-dialog modal-xl">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Cache Keys</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div id="cacheKeysContent">
|
|
<!-- Cache keys will be loaded here -->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Cache Analysis Modal -->
|
|
<div class="modal fade" id="cacheAnalysisModal" tabindex="-1">
|
|
<div class="modal-dialog modal-lg">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Cache Usage Analysis</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div id="cacheAnalysisContent">
|
|
<!-- Analysis will be loaded here -->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Clear by Pattern Modal -->
|
|
<div class="modal fade" id="clearPatternModal" tabindex="-1">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Clear Cache by Pattern</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<form id="clearPatternForm">
|
|
<div class="modal-body">
|
|
{% csrf_token %}
|
|
<div class="mb-3">
|
|
<label class="form-label">Pattern</label>
|
|
<input type="text" name="pattern" class="form-control" placeholder="e.g., user:*, session:*, views:*" required>
|
|
<div class="form-text">Use * as wildcard. Be careful with broad patterns.</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label class="form-label">Cache Instance</label>
|
|
<select name="cache_instance" class="form-select" required>
|
|
<option value="redis">Redis</option>
|
|
<option value="memcached">Memcached</option>
|
|
<option value="django">Django Cache</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="alert alert-warning">
|
|
<i class="fa fa-exclamation-triangle me-2"></i>
|
|
This action cannot be undone. Please verify the pattern before proceeding.
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
|
<button type="submit" class="btn btn-danger">Clear Keys</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block js %}
|
|
<script src="{% static 'assets/plugins/apexcharts/dist/apexcharts.min.js' %}"></script>
|
|
|
|
<script>
|
|
var performanceChart, distributionChart;
|
|
|
|
$(document).ready(function() {
|
|
initializeCharts();
|
|
loadCacheStats();
|
|
|
|
// Auto-refresh every 30 seconds
|
|
setInterval(refreshCacheStats, 30000);
|
|
|
|
// Form submissions
|
|
$('#cacheConfigForm').submit(function(e) {
|
|
e.preventDefault();
|
|
saveCacheConfiguration();
|
|
});
|
|
|
|
$('#clearPatternForm').submit(function(e) {
|
|
e.preventDefault();
|
|
clearCacheByPattern();
|
|
});
|
|
|
|
// Chart time range change
|
|
$('#chartTimeRange').change(function() {
|
|
loadCacheStats();
|
|
});
|
|
});
|
|
|
|
function initializeCharts() {
|
|
// Performance Chart
|
|
var performanceOptions = {
|
|
series: [{
|
|
name: 'Hit Rate',
|
|
data: []
|
|
}, {
|
|
name: 'Miss Rate',
|
|
data: []
|
|
}],
|
|
chart: {
|
|
type: 'line',
|
|
height: 300,
|
|
toolbar: { show: false }
|
|
},
|
|
stroke: {
|
|
curve: 'smooth',
|
|
width: 2
|
|
},
|
|
colors: ['#28a745', '#dc3545'],
|
|
xaxis: {
|
|
type: 'datetime'
|
|
},
|
|
yaxis: {
|
|
min: 0,
|
|
max: 100,
|
|
labels: {
|
|
formatter: function(val) {
|
|
return val + '%';
|
|
}
|
|
}
|
|
},
|
|
legend: {
|
|
position: 'top'
|
|
}
|
|
};
|
|
performanceChart = new ApexCharts(document.querySelector("#performanceChart"), performanceOptions);
|
|
performanceChart.render();
|
|
|
|
// Distribution Chart
|
|
var distributionOptions = {
|
|
series: [],
|
|
chart: {
|
|
type: 'donut',
|
|
height: 300
|
|
},
|
|
labels: ['Redis', 'Memcached', 'Django Cache', 'Browser Cache'],
|
|
colors: ['#dc3545', '#17a2b8', '#28a745', '#ffc107'],
|
|
legend: {
|
|
position: 'bottom'
|
|
}
|
|
};
|
|
distributionChart = new ApexCharts(document.querySelector("#distributionChart"), distributionOptions);
|
|
distributionChart.render();
|
|
}
|
|
|
|
function loadCacheStats() {
|
|
var timeRange = $('#chartTimeRange').val();
|
|
|
|
$.get('{% url "core:get_cache_stats" %}', {time_range: timeRange}, function(data) {
|
|
updateCacheOverview(data.overview);
|
|
updateCharts(data.charts);
|
|
}).fail(function() {
|
|
toastr.error('Failed to load cache statistics');
|
|
});
|
|
}
|
|
|
|
function updateCacheOverview(overview) {
|
|
$('#hitRateValue').text(overview.hit_rate + '%');
|
|
$('#totalKeysValue').text(overview.total_keys.toLocaleString());
|
|
$('#memoryUsageValue').text(overview.memory_usage);
|
|
$('#avgResponseValue').text(overview.avg_response + 'ms');
|
|
}
|
|
|
|
function updateCharts(chartData) {
|
|
// Update performance chart
|
|
performanceChart.updateSeries([{
|
|
name: 'Hit Rate',
|
|
data: chartData.performance.hit_rate
|
|
}, {
|
|
name: 'Miss Rate',
|
|
data: chartData.performance.miss_rate
|
|
}]);
|
|
|
|
// Update distribution chart
|
|
distributionChart.updateSeries(chartData.distribution.values);
|
|
}
|
|
|
|
function refreshCacheStats() {
|
|
loadCacheStats();
|
|
toastr.success('Cache statistics refreshed');
|
|
}
|
|
|
|
function clearAllCaches() {
|
|
if (confirm('Are you sure you want to clear ALL caches? This action cannot be undone.')) {
|
|
$.post('{% url "core:clear_all_caches" %}', function(data) {
|
|
if (data.success) {
|
|
toastr.success('All caches cleared successfully');
|
|
loadCacheStats();
|
|
} else {
|
|
toastr.error('Failed to clear caches: ' + data.error);
|
|
}
|
|
}).fail(function() {
|
|
toastr.error('Failed to clear caches');
|
|
});
|
|
}
|
|
}
|
|
|
|
function optimizeCache() {
|
|
toastr.info('Starting cache optimization...');
|
|
|
|
$.post('{% url "core:optimize_cache" %}', function(data) {
|
|
if (data.success) {
|
|
toastr.success('Cache optimization completed');
|
|
loadCacheStats();
|
|
} else {
|
|
toastr.error('Cache optimization failed: ' + data.error);
|
|
}
|
|
}).fail(function() {
|
|
toastr.error('Failed to optimize cache');
|
|
});
|
|
}
|
|
|
|
function flushCache(cacheType) {
|
|
if (confirm('Are you sure you want to flush the ' + cacheType + ' cache?')) {
|
|
$.post('{% url "core:flush_cache" %}', {cache_type: cacheType}, function(data) {
|
|
if (data.success) {
|
|
toastr.success(cacheType + ' cache flushed successfully');
|
|
loadCacheStats();
|
|
} else {
|
|
toastr.error('Failed to flush cache: ' + data.error);
|
|
}
|
|
}).fail(function() {
|
|
toastr.error('Failed to flush cache');
|
|
});
|
|
}
|
|
}
|
|
|
|
function viewCacheKeys(cacheType) {
|
|
$.get('{% url "core:get_cache_keys" %}', {cache_type: cacheType}, function(data) {
|
|
var keysHtml = '<div class="table-responsive">' +
|
|
'<table class="table table-striped">' +
|
|
'<thead>' +
|
|
'<tr>' +
|
|
'<th>Key</th>' +
|
|
'<th>Type</th>' +
|
|
'<th>Size</th>' +
|
|
'<th>TTL</th>' +
|
|
'<th>Actions</th>' +
|
|
'</tr>' +
|
|
'</thead>' +
|
|
'<tbody>';
|
|
|
|
data.keys.forEach(function(key) {
|
|
keysHtml += '<tr>' +
|
|
'<td><code>' + key.name + '</code></td>' +
|
|
'<td>' + key.type + '</td>' +
|
|
'<td>' + key.size + '</td>' +
|
|
'<td>' + (key.ttl > 0 ? key.ttl + 's' : 'No expiry') + '</td>' +
|
|
'<td>' +
|
|
'<button class="btn btn-outline-danger btn-sm" onclick="deleteKey(\'' + key.name + '\', \'' + cacheType + '\')">' +
|
|
'<i class="fa fa-trash"></i>' +
|
|
'</button>' +
|
|
'</td>' +
|
|
'</tr>';
|
|
});
|
|
|
|
keysHtml += '</tbody></table></div>';
|
|
|
|
$('#cacheKeysContent').html(keysHtml);
|
|
$('#cacheKeysModal').modal('show');
|
|
});
|
|
}
|
|
|
|
function analyzeCacheUsage(cacheType) {
|
|
$.get('{% url "core:analyze_cache_usage" %}', {cache_type: cacheType}, function(data) {
|
|
var analysisHtml = '<div class="row">' +
|
|
'<div class="col-md-6">' +
|
|
'<h6>Usage Statistics</h6>' +
|
|
'<ul class="list-unstyled">' +
|
|
'<li><strong>Total Keys:</strong> ' + data.stats.total_keys + '</li>' +
|
|
'<li><strong>Memory Usage:</strong> ' + data.stats.memory_usage + '</li>' +
|
|
'<li><strong>Hit Rate:</strong> ' + data.stats.hit_rate + '%</li>' +
|
|
'<li><strong>Average Key Size:</strong> ' + data.stats.avg_key_size + '</li>' +
|
|
'</ul>' +
|
|
'</div>' +
|
|
'<div class="col-md-6">' +
|
|
'<h6>Top Key Patterns</h6>' +
|
|
'<ul class="list-unstyled">';
|
|
|
|
data.patterns.forEach(function(pattern) {
|
|
analysisHtml += '<li><code>' + pattern.pattern + '</code> (' + pattern.count + ' keys)</li>';
|
|
});
|
|
|
|
analysisHtml += '</ul></div></div>';
|
|
|
|
if (data.recommendations.length > 0) {
|
|
analysisHtml += '<hr><h6>Recommendations</h6><ul>';
|
|
data.recommendations.forEach(function(rec) {
|
|
analysisHtml += '<li>' + rec + '</li>';
|
|
});
|
|
analysisHtml += '</ul>';
|
|
}
|
|
|
|
$('#cacheAnalysisContent').html(analysisHtml);
|
|
$('#cacheAnalysisModal').modal('show');
|
|
});
|
|
}
|
|
|
|
function deleteKey(keyName, cacheType) {
|
|
if (confirm('Are you sure you want to delete this key?')) {
|
|
$.post('{% url "core:delete_cache_key" %}', {
|
|
key: keyName,
|
|
cache_type: cacheType
|
|
}, function(data) {
|
|
if (data.success) {
|
|
toastr.success('Key deleted successfully');
|
|
viewCacheKeys(cacheType); // Refresh the list
|
|
} else {
|
|
toastr.error('Failed to delete key: ' + data.error);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
function searchCacheKeys() {
|
|
var pattern = $('#keySearchInput').val();
|
|
var instance = $('#cacheInstanceSelect').val();
|
|
|
|
if (!pattern) {
|
|
toastr.warning('Please enter a search pattern');
|
|
return;
|
|
}
|
|
|
|
$.get('{% url "core:search_cache_keys" %}', {
|
|
pattern: pattern,
|
|
instance: instance
|
|
}, function(data) {
|
|
var resultsHtml = '';
|
|
|
|
data.results.forEach(function(result) {
|
|
resultsHtml += '<tr>' +
|
|
'<td><code>' + result.key + '</code></td>' +
|
|
'<td>' + result.type + '</td>' +
|
|
'<td>' + result.size + '</td>' +
|
|
'<td>' + (result.ttl > 0 ? result.ttl + 's' : 'No expiry') + '</td>' +
|
|
'<td>' +
|
|
'<button class="btn btn-outline-danger btn-sm" onclick="deleteKey(\'' + result.key + '\', \'' + result.instance + '\')">' +
|
|
'<i class="fa fa-trash"></i>' +
|
|
'</button>' +
|
|
'</td>' +
|
|
'</tr>';
|
|
});
|
|
|
|
$('#searchResultsBody').html(resultsHtml);
|
|
$('#searchResults').show();
|
|
});
|
|
}
|
|
|
|
function clearExpiredKeys() {
|
|
if (confirm('Clear all expired keys from all cache instances?')) {
|
|
$.post('{% url "core:clear_expired_keys" %}', function(data) {
|
|
if (data.success) {
|
|
toastr.success(data.cleared_count + ' expired keys cleared');
|
|
loadCacheStats();
|
|
} else {
|
|
toastr.error('Failed to clear expired keys: ' + data.error);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
function clearByPattern() {
|
|
$('#clearPatternModal').modal('show');
|
|
}
|
|
|
|
function clearCacheByPattern() {
|
|
var formData = new FormData($('#clearPatternForm')[0]);
|
|
|
|
$.post('{% url "core:clear_cache_by_pattern" %}', formData, function(data) {
|
|
if (data.success) {
|
|
$('#clearPatternModal').modal('hide');
|
|
toastr.success(data.cleared_count + ' keys cleared');
|
|
loadCacheStats();
|
|
} else {
|
|
toastr.error('Failed to clear keys: ' + data.error);
|
|
}
|
|
}).fail(function() {
|
|
toastr.error('Failed to clear keys');
|
|
});
|
|
}
|
|
|
|
function exportCacheData() {
|
|
window.open('{% url "core:export_cache_data" %}', '_blank');
|
|
toastr.success('Cache data export started');
|
|
}
|
|
|
|
function optimizeMemory() {
|
|
$.post('{% url "core:optimize_cache_memory" %}', function(data) {
|
|
if (data.success) {
|
|
toastr.success('Memory optimization completed. Freed: ' + data.freed_memory);
|
|
loadCacheStats();
|
|
} else {
|
|
toastr.error('Memory optimization failed: ' + data.error);
|
|
}
|
|
});
|
|
}
|
|
|
|
function rebuildIndexes() {
|
|
$.post('{% url "core:rebuild_cache_indexes" %}', function(data) {
|
|
if (data.success) {
|
|
toastr.success('Cache indexes rebuilt successfully');
|
|
} else {
|
|
toastr.error('Failed to rebuild indexes: ' + data.error);
|
|
}
|
|
});
|
|
}
|
|
|
|
function generateCacheReport() {
|
|
window.open('{% url "core:generate_cache_report" %}', '_blank');
|
|
toastr.success('Cache report generation started');
|
|
}
|
|
|
|
function warmupCache() {
|
|
toastr.info('Starting cache warmup...');
|
|
|
|
$.post('{% url "core:warmup_cache" %}', function(data) {
|
|
if (data.success) {
|
|
toastr.success('Cache warmup completed. Warmed: ' + data.warmed_count + ' items');
|
|
loadCacheStats();
|
|
} else {
|
|
toastr.error('Cache warmup failed: ' + data.error);
|
|
}
|
|
});
|
|
}
|
|
|
|
function configureBrowserCache() {
|
|
toastr.info('Browser cache configuration feature coming soon');
|
|
}
|
|
|
|
function testCacheHeaders() {
|
|
$.get('{% url "core:test_cache_headers" %}', function(data) {
|
|
var message = 'Cache headers test results:\n';
|
|
data.results.forEach(function(result) {
|
|
message += '• ' + result.url + ': ' + result.status + '\n';
|
|
});
|
|
alert(message);
|
|
});
|
|
}
|
|
|
|
function saveCacheConfiguration() {
|
|
var formData = new FormData($('#cacheConfigForm')[0]);
|
|
|
|
$.post('{% url "core:save_cache_config" %}', formData, function(data) {
|
|
if (data.success) {
|
|
toastr.success('Cache configuration saved successfully');
|
|
} else {
|
|
toastr.error('Failed to save configuration: ' + data.error);
|
|
}
|
|
}).fail(function() {
|
|
toastr.error('Failed to save configuration');
|
|
});
|
|
}
|
|
</script>
|
|
|
|
<style>
|
|
.fs-24px {
|
|
font-size: 24px;
|
|
}
|
|
|
|
.fw-600 {
|
|
font-weight: 600;
|
|
}
|
|
|
|
.card-tools {
|
|
margin-left: auto;
|
|
}
|
|
|
|
.progress {
|
|
height: 8px;
|
|
}
|
|
|
|
.cache-instance-card {
|
|
transition: transform 0.2s ease;
|
|
}
|
|
|
|
.cache-instance-card:hover {
|
|
transform: translateY(-2px);
|
|
}
|
|
|
|
.timeline {
|
|
position: relative;
|
|
padding-left: 30px;
|
|
}
|
|
|
|
.timeline-item {
|
|
position: relative;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.timeline-marker {
|
|
position: absolute;
|
|
left: -35px;
|
|
top: 5px;
|
|
width: 10px;
|
|
height: 10px;
|
|
border-radius: 50%;
|
|
}
|
|
|
|
code {
|
|
font-size: 0.875em;
|
|
color: #e83e8c;
|
|
background-color: #f8f9fa;
|
|
padding: 0.2em 0.4em;
|
|
border-radius: 0.25rem;
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|