diff --git a/Dockerfile b/Dockerfile index 640bc57..23f26d4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -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 diff --git a/README.md b/README.md index 7366d57..07f17cd 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/bin/collect_metrics.php b/bin/collect_metrics.php index 0f37455..49f4acb 100644 --- a/bin/collect_metrics.php +++ b/bin/collect_metrics.php @@ -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); } diff --git a/bin/monitor_metrics.sh b/bin/monitor_metrics.sh new file mode 100644 index 0000000..3af4f06 --- /dev/null +++ b/bin/monitor_metrics.sh @@ -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 diff --git a/inc/VpnClient.php b/inc/VpnClient.php index 1cf7732..58727ca 100644 --- a/inc/VpnClient.php +++ b/inc/VpnClient.php @@ -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); diff --git a/public/index.php b/public/index.php index e7335d1..034bf39 100644 --- a/public/index.php +++ b/public/index.php @@ -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 . '"'); diff --git a/templates/servers/view.twig b/templates/servers/view.twig index d1a8a8d..360acf5 100644 --- a/templates/servers/view.twig +++ b/templates/servers/view.twig @@ -95,8 +95,8 @@

{{ t('clients.create') }}

- -

Spaces and special characters will be replaced with underscore

+ +

Spaces will be replaced with underscore. All characters allowed including Cyrillic.

@@ -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; }