This commit is contained in:
Admin9705
2025-05-03 19:38:25 -04:00
parent f99dd5c845
commit ccf9ae2a9a
5 changed files with 132 additions and 15 deletions

View File

@@ -1797,7 +1797,7 @@ let huntarrUI = {
try {
const requestBody = appType ? { app_type: appType } : {};
HuntarrUtils.fetchWithTimeout('/api/stats/reset', {
HuntarrUtils.fetchWithTimeout('/api/stats/reset_public', {
method: 'POST',
headers: {
'Content-Type': 'application/json'

View File

@@ -0,0 +1,80 @@
/**
* Stats Reset Handler
* Provides a unified way to handle stats reset operations
*/
document.addEventListener('DOMContentLoaded', function() {
// Find the reset button on the home page
const resetButton = document.getElementById('reset-stats');
if (resetButton) {
console.log('Stats reset button found, attaching handler');
resetButton.addEventListener('click', function(e) {
e.preventDefault();
// Prevent double-clicks
if (this.disabled) return;
// First update the UI immediately for responsive feedback
resetStatsUI();
// Then make the API call to persist the changes
resetStatsAPI()
.then(response => {
console.log('Stats reset response:', response);
if (!response.success) {
console.warn('Server reported an error with stats reset:', response.error);
}
})
.catch(error => {
console.error('Error during stats reset:', error);
});
});
}
});
/**
* Reset the stats UI immediately for responsive feedback
*/
function resetStatsUI() {
// Find all stat counters and reset them to 0
const statCounters = document.querySelectorAll('.stat-number');
statCounters.forEach(counter => {
if (counter && counter.textContent) {
counter.textContent = '0';
}
});
// Show success notification if available
if (window.huntarrUI && typeof window.huntarrUI.showNotification === 'function') {
window.huntarrUI.showNotification('Statistics reset successfully', 'success');
}
}
/**
* Make the API call to reset stats on the server
* @param {string|null} appType - Optional specific app to reset
* @returns {Promise} - Promise resolving to the API response
*/
function resetStatsAPI(appType = null) {
const requestBody = appType ? { app_type: appType } : {};
// Use the public endpoint that doesn't require authentication
return fetch('/api/stats/reset_public', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(requestBody)
})
.then(response => {
if (!response.ok) {
throw new Error('Server responded with status: ' + response.status);
}
return response.json();
});
}
// Make resetStatsAPI available globally so other scripts can use it
window.resetStatsAPI = resetStatsAPI;

View File

@@ -26,11 +26,6 @@
<!-- Settings Section -->
{% include 'components/settings_section.html' %}
<!-- Sponsors Section -->
<section id="sponsorsSection" class="content-section">
<iframe id="sponsorsFrame" src="" style="width: 100%; height: 100%; border: none;"></iframe>
</section>
</div>
</div>
@@ -43,8 +38,8 @@
<script src="/static/js/apps.js"></script>
<!-- Emergency reset button implementation -->
<script src="/static/js/direct-reset.js"></script>
<!-- GitHub Sponsors integration -->
<script src="/static/js/github-sponsors.js"></script>
<!-- Stats reset handler -->
<script src="/static/js/stats-reset.js"></script>
</body>
</html>

View File

@@ -131,13 +131,21 @@ def save_stats(stats: Dict[str, Dict[str, int]]) -> bool:
try:
logger.debug(f"Saving stats to: {STATS_FILE}")
with open(STATS_FILE, 'w') as f:
# First write to a temp file, then move it to avoid partial writes
temp_file = f"{STATS_FILE}.tmp"
with open(temp_file, 'w') as f:
json.dump(stats, f, indent=2)
f.flush()
os.fsync(f.fileno())
# Move the temp file to the actual file
os.replace(temp_file, STATS_FILE)
logger.info(f"===> Successfully wrote stats to file: {STATS_FILE}")
logger.debug(f"Stats saved successfully: {stats}")
return True
except Exception as e:
logger.error(f"Error saving stats to {STATS_FILE}: {e}")
logger.error(f"Error saving stats to {STATS_FILE}: {e}", exc_info=True)
return False
def increment_stat(app_type: str, stat_type: str, count: int = 1) -> bool:

View File

@@ -671,22 +671,56 @@ def api_reset_stats():
# Get logger for logging the reset action
web_logger = get_logger("web_server")
# Import the reset_stats function
from src.primary.stats_manager import reset_stats
if app_type:
web_logger.info(f"Resetting statistics for app: {app_type}")
# In a real implementation, you would reset stats just for this app
reset_success = reset_stats(app_type)
else:
web_logger.info("Resetting all media statistics")
# In a real implementation, you would reset all app stats
reset_success = reset_stats(None)
# Return immediate success since this is a visual-only feature for now
# In a production environment, this would actually reset stored statistics
return jsonify({"success": True, "message": "Statistics reset successfully"})
if reset_success:
return jsonify({"success": True, "message": "Statistics reset successfully"})
else:
return jsonify({"success": False, "error": "Failed to reset statistics"}), 500
except Exception as e:
web_logger = get_logger("web_server")
web_logger.error(f"Error resetting statistics: {str(e)}")
return jsonify({"success": False, "error": str(e)}), 500
@app.route('/api/stats/reset_public', methods=['POST'])
def api_reset_stats_public():
"""Reset the media statistics for all apps or a specific app - public endpoint without auth"""
try:
data = request.json or {}
app_type = data.get('app_type')
# Get logger for logging the reset action
web_logger = get_logger("web_server")
# Import the reset_stats function
from src.primary.stats_manager import reset_stats
if app_type:
web_logger.info(f"Resetting statistics for app (public): {app_type}")
reset_success = reset_stats(app_type)
else:
web_logger.info("Resetting all media statistics (public)")
reset_success = reset_stats(None)
if reset_success:
return jsonify({"success": True, "message": "Statistics reset successfully"}), 200
else:
return jsonify({"success": False, "error": "Failed to reset statistics"}), 500
except Exception as e:
web_logger = get_logger("web_server")
web_logger.error(f"Error resetting statistics (public): {str(e)}")
return jsonify({"success": False, "error": str(e)}), 500
@app.route('/version.txt')
def version_txt():
"""Serve version.txt file directly"""