feat: implement automatic metrics collection and monitoring system
This commit is contained in:
+10
-2
@@ -36,16 +36,24 @@ COPY apache.conf /etc/apache2/sites-available/000-default.conf
|
||||
RUN chown -R www-data:www-data /var/www/html \
|
||||
&& chmod -R 755 /var/www/html/public
|
||||
|
||||
# Setup cron for client expiration and traffic limit checks (runs every hour)
|
||||
# Setup cron jobs
|
||||
RUN echo "0 * * * * www-data cd /var/www/html && /usr/local/bin/php bin/check_expired_clients.php >> /var/log/cron.log 2>&1" > /etc/cron.d/amnezia-cron \
|
||||
&& echo "0 * * * * www-data cd /var/www/html && /usr/local/bin/php bin/check_traffic_limits.php >> /var/log/cron.log 2>&1" >> /etc/cron.d/amnezia-cron \
|
||||
&& echo "*/3 * * * * root /bin/bash /var/www/html/bin/monitor_metrics.sh >> /var/log/metrics_monitor.log 2>&1" >> /etc/cron.d/amnezia-cron \
|
||||
&& chmod 0644 /etc/cron.d/amnezia-cron \
|
||||
&& crontab /etc/cron.d/amnezia-cron \
|
||||
&& touch /var/log/cron.log
|
||||
&& touch /var/log/cron.log \
|
||||
&& touch /var/log/metrics_monitor.log \
|
||||
&& touch /var/log/metrics_collector.log
|
||||
|
||||
# Make monitor script executable
|
||||
RUN chmod +x /var/www/html/bin/monitor_metrics.sh
|
||||
|
||||
# Create startup script
|
||||
RUN echo '#!/bin/bash\n\
|
||||
service cron start\n\
|
||||
# Start metrics collector on container startup\n\
|
||||
/bin/bash /var/www/html/bin/monitor_metrics.sh\n\
|
||||
apache2-foreground' > /start.sh \
|
||||
&& chmod +x /start.sh
|
||||
|
||||
|
||||
@@ -31,12 +31,10 @@ cp .env.example .env
|
||||
# For Docker Compose V2 (recommended)
|
||||
docker compose up -d
|
||||
docker compose exec web composer install
|
||||
docker compose exec -d web php bin/collect_metrics.php
|
||||
|
||||
# Or for older Docker Compose V1
|
||||
docker-compose up -d
|
||||
docker-compose exec web composer install
|
||||
docker-compose exec -d web php bin/collect_metrics.php
|
||||
```
|
||||
|
||||
Access: http://localhost:8082
|
||||
@@ -143,6 +141,26 @@ curl -X POST http://localhost:8082/api/servers/1/restore \
|
||||
-d '{"backup_id": 123}'
|
||||
```
|
||||
|
||||
### Automatic Monitoring and Metrics Collection
|
||||
|
||||
**Metrics collector runs automatically** on container startup and is monitored by cron every 3 minutes. If the process crashes, it will be automatically restarted.
|
||||
|
||||
Check metrics collector logs:
|
||||
```bash
|
||||
docker compose exec web tail -f /var/log/metrics_collector.log
|
||||
```
|
||||
|
||||
Check monitoring script logs:
|
||||
```bash
|
||||
docker compose exec web tail -f /var/log/metrics_monitor.log
|
||||
```
|
||||
|
||||
Restart metrics collector manually:
|
||||
```bash
|
||||
docker compose exec web pkill -f collect_metrics.php
|
||||
# It will be auto-restarted within 3 minutes by the monitoring script
|
||||
```
|
||||
|
||||
### Automatic Client Expiration Check
|
||||
|
||||
**Runs automatically in Docker container** every hour to disable expired clients.
|
||||
@@ -281,4 +299,9 @@ migrations/ - SQL migrations (executed in alphabetical order)
|
||||
## License
|
||||
|
||||
MIT
|
||||
|
||||
## Support the Project
|
||||
|
||||
If you find this project helpful, you can support its development through a donation via Tribute: https://t.me/tribute/app?startapp=dzX1
|
||||
|
||||
# amneziavpnphp
|
||||
|
||||
+20
-1
@@ -21,8 +21,21 @@ date_default_timezone_set('UTC');
|
||||
// Enable error logging
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', 1);
|
||||
ini_set('log_errors', 1);
|
||||
ini_set('error_log', '/var/log/metrics_collector_errors.log');
|
||||
|
||||
echo "[" . date('Y-m-d H:i:s') . "] Metrics collector started\n";
|
||||
// Write PID file for monitoring
|
||||
$pidFile = '/var/run/collect_metrics.pid';
|
||||
file_put_contents($pidFile, getmypid());
|
||||
|
||||
// Register shutdown function to clean up PID file
|
||||
register_shutdown_function(function() use ($pidFile) {
|
||||
if (file_exists($pidFile)) {
|
||||
unlink($pidFile);
|
||||
}
|
||||
});
|
||||
|
||||
echo "[" . date('Y-m-d H:i:s') . "] Metrics collector started (PID: " . getmypid() . ")\n";
|
||||
|
||||
// Main loop
|
||||
while (true) {
|
||||
@@ -79,6 +92,12 @@ while (true) {
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo "[" . date('Y-m-d H:i:s') . "] FATAL ERROR: " . $e->getMessage() . "\n";
|
||||
error_log("[FATAL] Metrics collector error: " . $e->getMessage());
|
||||
echo "Retrying in 30 seconds...\n\n";
|
||||
sleep(30);
|
||||
} catch (Error $e) {
|
||||
echo "[" . date('Y-m-d H:i:s') . "] CRITICAL ERROR: " . $e->getMessage() . "\n";
|
||||
error_log("[CRITICAL] Metrics collector error: " . $e->getMessage());
|
||||
echo "Retrying in 30 seconds...\n\n";
|
||||
sleep(30);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Monitor and restart metrics collector if it's not running
|
||||
# This script checks if collect_metrics.php is running and restarts it if needed
|
||||
|
||||
SCRIPT_PATH="/var/www/html/bin/collect_metrics.php"
|
||||
LOG_FILE="/var/log/metrics_monitor.log"
|
||||
PID_FILE="/var/run/collect_metrics.pid"
|
||||
|
||||
log_message() {
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> "$LOG_FILE"
|
||||
}
|
||||
|
||||
# Check if the process is running
|
||||
is_running() {
|
||||
if [ -f "$PID_FILE" ]; then
|
||||
PID=$(cat "$PID_FILE")
|
||||
if ps -p "$PID" > /dev/null 2>&1; then
|
||||
# Check if it's actually our script
|
||||
if ps -p "$PID" -o args= | grep -q "collect_metrics.php"; then
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
# Start the metrics collector
|
||||
start_collector() {
|
||||
log_message "Starting metrics collector..."
|
||||
/usr/local/bin/php "$SCRIPT_PATH" >> /var/log/metrics_collector.log 2>&1 &
|
||||
echo $! > "$PID_FILE"
|
||||
log_message "Metrics collector started with PID: $(cat $PID_FILE)"
|
||||
}
|
||||
|
||||
# Main logic
|
||||
if is_running; then
|
||||
log_message "Metrics collector is running (PID: $(cat $PID_FILE))"
|
||||
else
|
||||
log_message "Metrics collector is not running - starting it"
|
||||
start_collector
|
||||
fi
|
||||
+2
-2
@@ -40,9 +40,9 @@ class VpnClient {
|
||||
public static function create(int $serverId, int $userId, string $name, ?int $expiresInDays = null): int {
|
||||
$pdo = DB::conn();
|
||||
|
||||
// Sanitize client name (replace spaces and special characters)
|
||||
// Sanitize client name (replace only spaces with underscores, allow any other characters including Cyrillic)
|
||||
$name = trim($name);
|
||||
$name = preg_replace('/[^a-zA-Z0-9_-]/', '_', $name);
|
||||
$name = str_replace(' ', '_', $name);
|
||||
|
||||
// Get server data
|
||||
$server = new VpnServer($serverId);
|
||||
|
||||
+10
-1
@@ -597,7 +597,16 @@ Router::get('/clients/{id}/download', function ($params) {
|
||||
}
|
||||
|
||||
$config = $client->getConfig();
|
||||
$filename = preg_replace('/[^a-zA-Z0-9_-]/', '_', $clientData['name']) . '.conf';
|
||||
|
||||
// Check if name contains non-Latin characters
|
||||
$hasNonLatin = preg_match('/[^a-zA-Z0-9_-]/', $clientData['name']);
|
||||
if ($hasNonLatin) {
|
||||
// Use user_(client_id)_s(server_id).conf format for non-Latin names
|
||||
$filename = 'user_' . $clientData['id'] . '_s' . $clientData['server_id'] . '.conf';
|
||||
} else {
|
||||
// Use client name for Latin characters
|
||||
$filename = $clientData['name'] . '.conf';
|
||||
}
|
||||
|
||||
header('Content-Type: application/octet-stream');
|
||||
header('Content-Disposition: attachment; filename="' . $filename . '"');
|
||||
|
||||
@@ -95,8 +95,8 @@
|
||||
<h3 class="font-bold mb-4">{{ t('clients.create') }}</h3>
|
||||
<form method="POST" action="/servers/{{ server.id }}/clients/create" class="space-y-3" id="createClientForm">
|
||||
<div>
|
||||
<input name="name" placeholder="{{ t('clients.name') }}" required class="w-full px-3 py-2 border rounded" id="clientName" pattern="[a-zA-Z0-9_-]+" title="Only letters, numbers, underscore and dash allowed">
|
||||
<p class="text-xs text-gray-500 mt-1">Spaces and special characters will be replaced with underscore</p>
|
||||
<input name="name" placeholder="{{ t('clients.name') }}" required class="w-full px-3 py-2 border rounded" id="clientName">
|
||||
<p class="text-xs text-gray-500 mt-1">Spaces will be replaced with underscore. All characters allowed including Cyrillic.</p>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm text-gray-600 mb-1">{{ t('clients.expiration') }}</label>
|
||||
@@ -304,12 +304,12 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
const form = document.getElementById('createClientForm');
|
||||
const clientNameInput = document.getElementById('clientName');
|
||||
|
||||
// Auto-sanitize client name on input
|
||||
// Auto-replace spaces with underscores
|
||||
if (clientNameInput) {
|
||||
clientNameInput.addEventListener('input', function(e) {
|
||||
// Replace spaces and special characters with underscore
|
||||
// Replace only spaces with underscore, allow all other characters including Cyrillic
|
||||
let value = e.target.value;
|
||||
let sanitized = value.replace(/[^a-zA-Z0-9_-]/g, '_');
|
||||
let sanitized = value.replace(/ /g, '_');
|
||||
if (value !== sanitized) {
|
||||
e.target.value = sanitized;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user