diff --git a/ansible/DEPLOYMENT_GUIDE.md b/ansible/DEPLOYMENT_GUIDE.md deleted file mode 100644 index 550c59192..000000000 --- a/ansible/DEPLOYMENT_GUIDE.md +++ /dev/null @@ -1,380 +0,0 @@ -# Veza V5 Ultra Deployment Guide - -This guide provides step-by-step instructions for deploying Veza V5 Ultra using Ansible, Incus containers, OVN networking, HAProxy, and Let's Encrypt. - -## Table of Contents - -- [Prerequisites](#prerequisites) -- [Quick Start](#quick-start) -- [Step-by-Step Deployment](#step-by-step-deployment) -- [Troubleshooting](#troubleshooting) -- [Post-Deployment](#post-deployment) -- [Maintenance](#maintenance) - -## Prerequisites - -### Control Node (Your Machine) -- Ansible 2.16+ -- SSH access to target host -- Required collections: `community.general`, `community.docker` - -### Target Host (192.168.0.12) -- Debian 12 (Bookworm) -- SSH key authentication configured -- Root or sudo access -- Internet connectivity - -### DNS Configuration -- Domain: `veza.talas.fr` -- A record pointing to target host IP (192.168.0.12) - -## Quick Start - -```bash -# 1. Clone and navigate to ansible directory -cd ansible - -# 2. Install required collections -ansible-galaxy collection install community.general community.docker - -# 3. Run full deployment -./deploy-veza.sh - -# 4. Configure DNS and re-run HAProxy playbook -ansible-playbook -i inventory/prod/hosts.yml playbooks/30-haproxy-acme.yml -e domain=veza.talas.fr -e acme_email=ops@talas.fr - -# 5. Run smoke tests -ansible-playbook -i inventory/prod/hosts.yml playbooks/50-smoke-tests.yml -``` - -## Step-by-Step Deployment - -### Step 1: Bootstrap Target Host - -```bash -ansible-playbook -i inventory/prod/hosts.yml playbooks/00-bootstrap-remote.yml -``` - -**What this does:** -- Installs essential packages (python3, sudo, curl, etc.) -- Configures SSH for better performance -- Sets up firewall rules for required ports -- Installs Incus dependencies - -**Expected output:** -``` -TASK [Install essential packages] ********************************************** -ok: [edge-1] - -TASK [Configure firewall for Veza ports] ************************************** -ok: [edge-1] - -TASK [Test connectivity] ****************************************************** -ok: [edge-1] -``` - -### Step 2: Install Incus and OVN - -```bash -ansible-playbook -i inventory/prod/hosts.yml playbooks/10-incus-ovn.yml -``` - -**What this does:** -- Installs Incus via snap -- Initializes Incus in standalone mode -- Creates OVN network `veza-ovn` -- Creates `veza` profile for containers - -**Expected output:** -``` -TASK [Install Incus via snap] ************************************************* -ok: [edge-1] - -TASK [Create OVN network for Veza] ******************************************** -ok: [edge-1] - -TASK [Verify Incus is running] ************************************************ -ok: [edge-1] -``` - -### Step 3: Create Containers - -```bash -ansible-playbook -i inventory/prod/hosts.yml playbooks/20-incus-containers.yml -``` - -**What this does:** -- Creates 5 containers: haproxy, backend, chat, stream, web -- Configures networking with static IPs -- Sets up proxy devices for external access -- Starts all containers - -**Expected output:** -``` -TASK [Create Veza containers] ************************************************* -ok: [edge-1] => (item=veza-haproxy) -ok: [edge-1] => (item=veza-backend) -ok: [edge-1] => (item=veza-chat) -ok: [edge-1] => (item=veza-stream) -ok: [edge-1] => (item=veza-web) -``` - -### Step 4: Configure HAProxy and Let's Encrypt - -```bash -ansible-playbook -i inventory/prod/hosts.yml playbooks/30-haproxy-acme.yml -e domain=veza.talas.fr -e acme_email=ops@talas.fr -``` - -**What this does:** -- Installs HAProxy and ACME tools in container -- Configures nginx for ACME challenges -- Sets up HAProxy with SSL termination -- Requests Let's Encrypt certificate -- Configures automatic renewal - -**Expected output:** -``` -TASK [Install HAProxy and ACME tools in container] **************************** -ok: [edge-1] - -TASK [Request Let's Encrypt certificate] *************************************** -ok: [edge-1] - -TASK [Test HAProxy configuration] ********************************************** -ok: [edge-1] -``` - -### Step 5: Deploy Applications - -```bash -ansible-playbook -i inventory/prod/hosts.yml playbooks/40-veza-apps.yml -``` - -**What this does:** -- Installs Go and builds backend API -- Installs Rust and builds chat server -- Installs Rust and builds stream server -- Installs Node.js and deploys web app -- Creates systemd services for all apps - -**Expected output:** -``` -TASK [Deploy Go Backend API] ************************************************** -ok: [edge-1] - -TASK [Deploy Rust Chat Server] *********************************************** -ok: [edge-1] - -TASK [Deploy Rust Stream Server] ********************************************** -ok: [edge-1] - -TASK [Deploy React Web Application] ******************************************* -ok: [edge-1] -``` - -### Step 6: Run Smoke Tests - -```bash -ansible-playbook -i inventory/prod/hosts.yml playbooks/50-smoke-tests.yml -``` - -**What this does:** -- Tests all container connectivity -- Validates all service endpoints -- Checks HAProxy configuration -- Tests external access (if DNS configured) -- Generates comprehensive test report - -**Expected output:** -``` -TASK [Test container connectivity] ********************************************* -ok: [edge-1] - -TASK [Test Backend API service] *********************************************** -ok: [edge-1] - -TASK [Generate smoke test summary] ******************************************** -ok: [edge-1] -``` - -## Troubleshooting - -### Common Issues - -#### 1. SSH Connection Failed -```bash -# Test SSH connectivity -ssh -o ConnectTimeout=10 senke@192.168.0.12 "echo 'SSH test'" - -# Check SSH config -grep -n "compressionlevel" ~/.ssh/config -``` - -**Solution:** Fix SSH config or ensure target host is reachable. - -#### 2. Incus Installation Failed -```bash -# Check snapd status -incus exec veza-haproxy -- systemctl status snapd - -# Reinstall Incus -incus exec veza-haproxy -- snap remove incus -incus exec veza-haproxy -- snap install incus --classic -``` - -#### 3. Container Creation Failed -```bash -# Check Incus status -incus list -incus network list -incus profile list - -# Clean up and retry -incus delete veza-haproxy --force -ansible-playbook -i inventory/prod/hosts.yml playbooks/20-incus-containers.yml -``` - -#### 4. HAProxy Configuration Error -```bash -# Test HAProxy config -incus exec veza-haproxy -- haproxy -c -f /etc/haproxy/haproxy.cfg - -# Check HAProxy logs -incus exec veza-haproxy -- journalctl -u haproxy -f -``` - -#### 5. Let's Encrypt Certificate Failed -```bash -# Check ACME challenges -incus exec veza-haproxy -- curl http://localhost:8888/.well-known/acme-challenge/test - -# Manual certificate request -incus exec veza-haproxy -- dehydrated -c -d veza.talas.fr -``` - -#### 6. Application Service Failed -```bash -# Check service status -incus exec veza-backend -- systemctl status veza-backend -incus exec veza-chat -- systemctl status veza-chat -incus exec veza-stream -- systemctl status veza-stream -incus exec veza-web -- systemctl status veza-web - -# Check logs -incus exec veza-backend -- journalctl -u veza-backend -f -``` - -### Debug Commands - -```bash -# Check all container status -incus list --format=json | jq '.[] | {name: .name, status: .status, state: .state}' - -# Check network configuration -incus network show veza-ovn - -# Check HAProxy statistics -incus exec veza-haproxy -- curl -s http://localhost:8404/stats - -# Test internal connectivity -incus exec veza-web -- curl -s http://10.10.0.101:8080/api/health -incus exec veza-web -- curl -s http://10.10.0.102:8081/health -incus exec veza-web -- curl -s http://10.10.0.103:8082/stream/health -``` - -## Post-Deployment - -### 1. Configure DNS -Point your domain's A record to the target host IP: -``` -veza.talas.fr. IN A 192.168.0.12 -``` - -### 2. Re-run HAProxy Playbook -After DNS is configured, re-run the HAProxy playbook to get the Let's Encrypt certificate: -```bash -ansible-playbook -i inventory/prod/hosts.yml playbooks/30-haproxy-acme.yml -e domain=veza.talas.fr -e acme_email=ops@talas.fr -``` - -### 3. Verify HTTPS Access -```bash -curl -I https://veza.talas.fr -curl -I https://veza.talas.fr/api/health -``` - -### 4. Monitor Application Logs -```bash -# Follow all logs -incus exec veza-haproxy -- journalctl -u haproxy -f & -incus exec veza-backend -- journalctl -u veza-backend -f & -incus exec veza-chat -- journalctl -u veza-chat -f & -incus exec veza-stream -- journalctl -u veza-stream -f & -incus exec veza-web -- journalctl -u veza-web -f & -``` - -## Maintenance - -### Certificate Renewal -Certificates are automatically renewed via cron. To check: -```bash -incus exec veza-haproxy -- crontab -l -incus exec veza-haproxy -- ls -la /etc/haproxy/certs/ -``` - -### Container Updates -```bash -# Update container images -incus exec veza-backend -- apt update && apt upgrade -y -incus exec veza-chat -- apt update && apt upgrade -y -incus exec veza-stream -- apt update && apt upgrade -y -incus exec veza-web -- apt update && apt upgrade -y -``` - -### Backup -```bash -# Backup container configurations -incus export veza-haproxy /backup/veza-haproxy.tar.gz -incus export veza-backend /backup/veza-backend.tar.gz -incus export veza-chat /backup/veza-chat.tar.gz -incus export veza-stream /backup/veza-stream.tar.gz -incus export veza-web /backup/veza-web.tar.gz -``` - -### Scaling -To add more backend instances: -```bash -# Create additional backend container -incus launch debian/bookworm veza-backend-2 --profile veza -incus config device set veza-backend-2 eth0 ipv4.address=10.10.0.105/24 -incus start veza-backend-2 - -# Update HAProxy configuration to include new backend -incus exec veza-haproxy -- sed -i 's/server api1 10.10.0.101:8080/server api1 10.10.0.101:8080\n server api2 10.10.0.105:8080/' /etc/haproxy/haproxy.cfg -incus exec veza-haproxy -- systemctl reload haproxy -``` - -## Support - -For issues or questions: -1. Check the troubleshooting section above -2. Review container logs for error messages -3. Run smoke tests to identify failing components -4. Check the Ansible playbook logs for deployment issues - -## Architecture Overview - -``` -Internet (veza.talas.fr) - ↓ -HAProxy Container (80/443) - ↓ -OVN Network (veza-ovn) - ↓ -┌─────────┬─────────┬─────────┬─────────┐ -│Backend │ Chat │ Stream │ Web │ -│:8080 │ :8081 │ :8082 │ :3000 │ -│(Go) │ (Rust) │ (Rust) │ (Node) │ -└─────────┴─────────┴─────────┴─────────┘ -``` - -This deployment provides a complete, production-ready Veza V5 Ultra platform with automatic SSL certificate management, load balancing, and comprehensive monitoring. diff --git a/ansible/README.md b/ansible/README.md deleted file mode 100644 index 0d4cdfff9..000000000 --- a/ansible/README.md +++ /dev/null @@ -1,215 +0,0 @@ -# Veza V5 Ultra - Ansible Deployment - -This directory contains Ansible playbooks and configuration for deploying Veza V5 Ultra using Incus/OVN + HAProxy-in-container + Let's Encrypt. - -## Architecture - -- **Single Debian host** (192.168.0.12) with Incus containers -- **HAProxy** running inside an Incus container as edge proxy -- **Let's Encrypt** ACME HTTP-01 validation handled in HAProxy container -- **OVN networking** for container communication -- **Applications** in separate containers: - - `veza-backend` (Go API on port 8080) - - `veza-chat` (Rust WebSocket on port 8081) - - `veza-stream` (Rust HLS on port 8082) - - `veza-web` (React + nginx on port 80) - -## Prerequisites - -### Control Node (Your Machine) -- Ansible ≥ 2.16 -- SSH access to target host with key-based authentication -- Required collections: - ```bash - ansible-galaxy collection install community.general - ansible-galaxy collection install community.docker - ``` - -### Target Host (192.168.0.12) -- Debian 12 (Bookworm) -- SSH access for user `senke` -- Open ports: 22, 80, 443, 8080, 8081, 8082 -- Sufficient resources for containers - -## Quick Start - -### 1. Full Deployment -```bash -cd ansible -./deploy-veza.sh -``` - -### 2. Custom Domain and Email -```bash -./deploy-veza.sh -d myapp.example.com -e admin@example.com -``` - -### 3. Step-by-Step Deployment -```bash -# Bootstrap host -./deploy-veza.sh --bootstrap-only - -# Setup infrastructure -./deploy-veza.sh --infra-only - -# Deploy applications -./deploy-veza.sh --apps-only - -# Run tests -./deploy-veza.sh --test-only -``` - -## Manual Playbook Execution - -```bash -# 1. Bootstrap remote host -ansible-playbook -i inventory/prod/hosts.yml playbooks/00-bootstrap-remote.yml - -# 2. Install Incus + OVN -ansible-playbook -i inventory/prod/hosts.yml playbooks/10-incus-ovn.yml - -# 3. Create containers -ansible-playbook -i inventory/prod/hosts.yml playbooks/20-incus-containers.yml - -# 4. Configure HAProxy + ACME -ansible-playbook -i inventory/prod/hosts.yml playbooks/30-haproxy-in-container.yml \ - -e domain=veza.talas.fr -e acme_email=ops@talas.fr - -# 5. Deploy applications -ansible-playbook -i inventory/prod/hosts.yml playbooks/40-veza-apps.yml - -# 6. Run smoke tests -ansible-playbook -i inventory/prod/hosts.yml playbooks/50-smoke.yml -``` - -## Configuration - -### Inventory -- `inventory/prod/hosts.yml` - Target host configuration -- `group_vars/all.yml` - Global variables (domain, ports, etc.) - -### Key Variables -- `domain`: Target domain (default: veza.talas.fr) -- `acme_email`: Email for Let's Encrypt (default: ops@talas.fr) -- `veza_*_port`: Application ports -- `veza_database_url`: PostgreSQL connection string -- `veza_redis_url`: Redis connection string - -## Post-Deployment - -### 1. DNS Configuration -Point your domain's A record to the target host IP: -``` -veza.talas.fr. IN A 192.168.0.12 -``` - -### 2. Get Let's Encrypt Certificate -After DNS is configured, re-run the HAProxy playbook: -```bash -ansible-playbook -i inventory/prod/hosts.yml playbooks/30-haproxy-in-container.yml \ - -e domain=veza.talas.fr -e acme_email=ops@talas.fr -``` - -### 3. Verify Deployment -```bash -# Check container status -incus list - -# Check services -incus exec veza-haproxy -- systemctl status haproxy -incus exec veza-backend -- systemctl status veza-backend -incus exec veza-chat -- systemctl status veza-chat -incus exec veza-stream -- systemctl status veza-stream -incus exec veza-web -- systemctl status nginx - -# Test endpoints -curl -k https://192.168.0.12/ -curl -k https://192.168.0.12/api/health -``` - -## Troubleshooting - -### Container Issues -```bash -# Check container logs -incus exec -- journalctl -u -f - -# Restart container -incus restart - -# Access container shell -incus exec -- bash -``` - -### HAProxy Issues -```bash -# Check HAProxy config -incus exec veza-haproxy -- haproxy -c -f /etc/haproxy/haproxy.cfg - -# Check HAProxy logs -incus exec veza-haproxy -- journalctl -u haproxy -f - -# Reload HAProxy -incus exec veza-haproxy -- systemctl reload haproxy -``` - -### ACME Issues -```bash -# Check ACME webroot -incus exec veza-haproxy -- ls -la /var/www/acme-challenge/ - -# Test ACME challenge -curl http://192.168.0.12/.well-known/acme-challenge/test - -# Manual certificate renewal -incus exec veza-haproxy -- /opt/dehydrated/dehydrated -c -``` - -## File Structure - -``` -ansible/ -├── deploy-veza.sh # Deployment script -├── inventory/ -│ └── prod/ -│ └── hosts.yml # Target host inventory -├── group_vars/ -│ └── all.yml # Global variables -├── playbooks/ -│ ├── 00-bootstrap-remote.yml # Host bootstrap -│ ├── 10-incus-ovn.yml # Incus + OVN setup -│ ├── 20-incus-containers.yml # Container creation -│ ├── 30-haproxy-in-container.yml # HAProxy + ACME -│ ├── 40-veza-apps.yml # Application deployment -│ └── 50-smoke.yml # Smoke tests -└── roles/ # Existing Ansible roles - ├── incus/ - ├── ovn/ - ├── haproxy/ - └── ... -``` - -## Security Notes - -- All containers run with `security.nesting=true` -- HAProxy enforces HTTPS redirects -- Security headers are configured (HSTS, CSP, etc.) -- Let's Encrypt certificates are automatically renewed -- Firewall rules restrict access to necessary ports only - -## Monitoring - -The deployment includes basic health checks and logging. For production monitoring, consider: - -- Prometheus + Grafana for metrics -- ELK stack for log aggregation -- Uptime monitoring for external services -- Container resource monitoring - -## Support - -For issues or questions: -1. Check container logs first -2. Verify network connectivity -3. Check HAProxy configuration -4. Review Ansible playbook output for errors diff --git a/ansible/demo-veza-deployment.sh b/ansible/demo-veza-deployment.sh deleted file mode 100644 index 56c002bac..000000000 --- a/ansible/demo-veza-deployment.sh +++ /dev/null @@ -1,212 +0,0 @@ -#!/bin/bash -# Veza V5 Ultra Deployment Demo Script -# Shows the deployment process and configuration - -set -euo pipefail - -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -NC='\033[0m' # No Color - -# Functions -log_info() { - echo -e "${BLUE}[INFO]${NC} $1" -} - -log_success() { - echo -e "${GREEN}[SUCCESS]${NC} $1" -} - -log_warning() { - echo -e "${YELLOW}[WARNING]${NC} $1" -} - -log_error() { - echo -e "${RED}[ERROR]${NC} $1" -} - -show_header() { - echo - echo "========================================" - echo "Veza V5 Ultra Deployment Demo" - echo "========================================" - echo -} - -check_system() { - log_info "Checking system information..." - - echo "System: $(uname -a)" - echo "Python: $(python3 --version 2>/dev/null || echo 'Not available')" - echo "User: $(whoami)" - echo "Home: $HOME" - echo -} - -check_packages() { - log_info "Checking required packages..." - - local packages=("python3" "curl" "git" "wget" "ansible") - for pkg in "${packages[@]}"; do - if command -v "$pkg" &> /dev/null; then - log_success "$pkg: Available" - else - log_warning "$pkg: Not installed" - fi - done - echo -} - -check_ansible() { - log_info "Checking Ansible setup..." - - echo "Ansible version: $(ansible --version | head -1)" - echo "Ansible collections:" - ansible-galaxy collection list 2>/dev/null | grep -E "(community|incus)" || echo " No relevant collections found" - echo -} - -check_network() { - log_info "Checking network configuration..." - - echo "Network interfaces:" - ip addr show | grep -E "(inet |UP)" | head -10 - echo - - echo "Default route:" - ip route show | grep default - echo -} - -check_target_host() { - log_info "Checking target host connectivity..." - - local target_host="192.168.0.12" - if ping -c 1 -W 1 "$target_host" &> /dev/null; then - log_success "Target host $target_host is reachable" - else - log_warning "Target host $target_host is not reachable" - echo " This is expected if the host is not currently running" - fi - echo -} - -show_deployment_steps() { - log_info "Veza V5 Ultra Deployment Steps:" - echo - echo "1. Bootstrap Host (00-bootstrap-remote.yml)" - echo " - Install Python, sudo, curl, gnupg, net-tools" - echo " - Configure SSH and firewall" - echo " - Install Incus dependencies" - echo - echo "2. Install Incus + OVN (10-incus-ovn.yml)" - echo " - Install Incus via snap" - echo " - Install OVN packages" - echo " - Create OVN network 'veza-ovn'" - echo - echo "3. Create Containers (20-incus-containers.yml)" - echo " - veza-haproxy (Debian 12) - Edge proxy" - echo " - veza-backend (Debian 12) - Go API on 8080" - echo " - veza-chat (Debian 12) - Rust WebSocket on 8081" - echo " - veza-stream (Debian 12) - Rust HLS on 8082" - echo " - veza-web (Debian 12) - React + nginx on 80" - echo - echo "4. Configure HAProxy + ACME (30-haproxy-in-container.yml)" - echo " - Install HAProxy in container" - echo " - Setup Let's Encrypt HTTP-01 validation" - echo " - Configure routing and SSL termination" - echo " - Generate certificates for veza.talas.fr" - echo - echo "5. Deploy Applications (40-veza-apps.yml)" - echo " - Build and run Go backend with systemd" - echo " - Build and run Rust chat server with systemd" - echo " - Build and run Rust stream server with systemd" - echo " - Build React app and serve with nginx" - echo - echo "6. Run Smoke Tests (50-smoke.yml)" - echo " - Test HTTPS access" - echo " - Test API endpoints" - echo " - Test WebSocket connectivity" - echo " - Test HLS streaming" - echo -} - -show_architecture() { - log_info "Veza V5 Ultra Architecture:" - echo - echo "┌─────────────────────────────────────────────────────────────┐" - echo "│ Internet (veza.talas.fr) │" - echo "└─────────────────────┬───────────────────────────────────────┘" - echo " │" - echo "┌─────────────────────▼───────────────────────────────────────┐" - echo "│ HAProxy Container (80/443) │" - echo "│ - SSL Termination │" - echo "│ - Let's Encrypt ACME │" - echo "│ - Request Routing │" - echo "└─────────────────────┬───────────────────────────────────────┘" - echo " │" - echo "┌─────────────────────▼───────────────────────────────────────┐" - echo "│ OVN Network │" - echo "│ (veza-ovn) │" - echo "└─────┬─────────┬─────────┬─────────┬─────────────────────────┘" - echo " │ │ │ │" - echo "┌─────▼───┐ ┌───▼───┐ ┌───▼───┐ ┌───▼───┐" - echo "│ Backend │ │ Chat │ │Stream │ │ Web │" - echo "│ :8080 │ │ :8081 │ │ :8082 │ │ :80 │" - echo "│ (Go) │ │(Rust) │ │(Rust) │ │(React)│" - echo "└─────────┘ └───────┘ └───────┘ └───────┘" - echo -} - -show_commands() { - log_info "Deployment Commands:" - echo - echo "# Full deployment:" - echo "./deploy-veza.sh" - echo - echo "# Step-by-step deployment:" - echo "ansible-playbook -i inventory/prod/hosts.yml playbooks/00-bootstrap-remote.yml" - echo "ansible-playbook -i inventory/prod/hosts.yml playbooks/10-incus-ovn.yml" - echo "ansible-playbook -i inventory/prod/hosts.yml playbooks/20-incus-containers.yml" - echo "ansible-playbook -i inventory/prod/hosts.yml playbooks/30-haproxy-in-container.yml -e domain=veza.talas.fr -e acme_email=ops@talas.fr" - echo "ansible-playbook -i inventory/prod/hosts.yml playbooks/40-veza-apps.yml" - echo "ansible-playbook -i inventory/prod/hosts.yml playbooks/50-smoke.yml" - echo - echo "# Custom domain:" - echo "./deploy-veza.sh -d myapp.example.com -e admin@example.com" - echo -} - -show_next_steps() { - log_info "Next Steps:" - echo - echo "1. Ensure target host (192.168.0.12) is running and accessible" - echo "2. Verify SSH key authentication works:" - echo " ssh senke@192.168.0.12 'echo \"SSH test successful\"'" - echo "3. Run the deployment:" - echo " ./deploy-veza.sh" - echo "4. Point DNS A record for veza.talas.fr to 192.168.0.12" - echo "5. Re-run HAProxy playbook to get Let's Encrypt certificate" - echo -} - -main() { - show_header - check_system - check_packages - check_ansible - check_network - check_target_host - show_deployment_steps - show_architecture - show_commands - show_next_steps - - log_success "Demo completed! Veza V5 Ultra deployment is ready to run." - echo -} - -main "$@" diff --git a/ansible/deploy-veza.sh b/ansible/deploy-veza.sh deleted file mode 100644 index d0232927f..000000000 --- a/ansible/deploy-veza.sh +++ /dev/null @@ -1,235 +0,0 @@ -#!/bin/bash -# Veza V5 Ultra Deployment Script -# Deploys Veza using Ansible + Incus/OVN + HAProxy-in-container + Let's Encrypt - -set -euo pipefail - -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -NC='\033[0m' # No Color - -# Configuration -INVENTORY="ansible/inventory/prod/hosts.yml" -DOMAIN="veza.talas.fr" -ACME_EMAIL="ops@talas.fr" -TARGET_HOST="192.168.0.12" - -# Functions -log_info() { - echo -e "${BLUE}[INFO]${NC} $1" -} - -log_success() { - echo -e "${GREEN}[SUCCESS]${NC} $1" -} - -log_warning() { - echo -e "${YELLOW}[WARNING]${NC} $1" -} - -log_error() { - echo -e "${RED}[ERROR]${NC} $1" -} - -check_prerequisites() { - log_info "Checking prerequisites..." - - # Check if ansible is installed - if ! command -v ansible-playbook &> /dev/null; then - log_error "ansible-playbook is not installed. Please install Ansible first." - exit 1 - fi - - # Check if inventory file exists - if [[ ! -f "$INVENTORY" ]]; then - log_error "Inventory file $INVENTORY not found!" - exit 1 - fi - - # Check if playbooks exist - for playbook in ansible/playbooks/00-bootstrap-remote.yml ansible/playbooks/10-incus-ovn.yml ansible/playbooks/20-incus-containers.yml ansible/playbooks/30-haproxy-in-container.yml ansible/playbooks/40-veza-apps.yml ansible/playbooks/50-smoke.yml; do - if [[ ! -f "$playbook" ]]; then - log_error "Playbook $playbook not found!" - exit 1 - fi - done - - # Check SSH connectivity - log_info "Testing SSH connectivity to $TARGET_HOST..." - if ! ssh -o ConnectTimeout=10 -o BatchMode=yes senke@$TARGET_HOST "echo 'SSH connection successful'" &> /dev/null; then - log_error "Cannot connect to $TARGET_HOST via SSH. Please check your SSH key and connectivity." - exit 1 - fi - - log_success "Prerequisites check passed!" -} - -run_playbook() { - local playbook="$1" - local description="$2" - local extra_vars="$3" - - log_info "Running: $description" - log_info "Playbook: $playbook" - - if [[ -n "$extra_vars" ]]; then - log_info "Extra vars: $extra_vars" - ansible-playbook -i "$INVENTORY" "$playbook" -e "$extra_vars" -v - else - ansible-playbook -i "$INVENTORY" "$playbook" -v - fi - - if [[ $? -eq 0 ]]; then - log_success "$description completed successfully!" - else - log_error "$description failed!" - exit 1 - fi -} - -deploy_veza() { - log_info "Starting Veza V5 Ultra deployment..." - log_info "Target host: $TARGET_HOST" - log_info "Domain: $DOMAIN" - log_info "ACME Email: $ACME_EMAIL" - echo - - # Step 1: Bootstrap remote host - run_playbook "ansible/playbooks/00-bootstrap-remote.yml" "Bootstrap Debian host" - echo - - # Step 2: Install Incus + OVN - run_playbook "ansible/playbooks/10-incus-ovn.yml" "Install Incus + OVN single-host" - echo - - # Step 3: Create containers - run_playbook "ansible/playbooks/20-incus-containers.yml" "Create Incus containers" - echo - - # Step 4: Configure HAProxy + ACME - run_playbook "ansible/playbooks/30-haproxy-in-container.yml" "Configure HAProxy + ACME" "domain=$DOMAIN acme_email=$ACME_EMAIL" - echo - - # Step 5: Deploy applications - run_playbook "ansible/playbooks/40-veza-apps.yml" "Deploy Veza applications" - echo - - # Step 6: Run smoke tests - run_playbook "ansible/playbooks/50-smoke.yml" "Run smoke tests" - echo - - log_success "Veza V5 Ultra deployment completed successfully!" - echo - log_info "Next steps:" - log_info "1. Point DNS A record for $DOMAIN to $TARGET_HOST" - log_info "2. Re-run HAProxy playbook to get Let's Encrypt certificate:" - log_info " ansible-playbook -i $INVENTORY ansible/playbooks/30-haproxy-in-container.yml -e domain=$DOMAIN -e acme_email=$ACME_EMAIL" - log_info "3. Test full functionality with real domain" - echo - log_info "Access URLs:" - log_info "- HTTP: http://$TARGET_HOST/" - log_info "- HTTPS: https://$TARGET_HOST/ (self-signed cert until DNS is configured)" - log_info "- API: https://$TARGET_HOST/api/" - log_info "- WS: wss://$TARGET_HOST/ws/" - log_info "- Stream: https://$TARGET_HOST/stream/" -} - -show_help() { - echo "Veza V5 Ultra Deployment Script" - echo - echo "Usage: $0 [OPTIONS]" - echo - echo "Options:" - echo " -h, --help Show this help message" - echo " -d, --domain DOMAIN Set domain (default: $DOMAIN)" - echo " -e, --email EMAIL Set ACME email (default: $ACME_EMAIL)" - echo " -t, --target HOST Set target host (default: $TARGET_HOST)" - echo " --bootstrap-only Run only bootstrap playbook" - echo " --infra-only Run bootstrap + infrastructure playbooks" - echo " --apps-only Run only applications playbook" - echo " --test-only Run only smoke tests" - echo - echo "Examples:" - echo " $0 # Full deployment" - echo " $0 -d myapp.example.com -e admin@example.com # Custom domain and email" - echo " $0 --bootstrap-only # Only bootstrap the host" - echo " $0 --infra-only # Only setup infrastructure" -} - -# Parse command line arguments -BOOTSTRAP_ONLY=false -INFRA_ONLY=false -APPS_ONLY=false -TEST_ONLY=false - -while [[ $# -gt 0 ]]; do - case $1 in - -h|--help) - show_help - exit 0 - ;; - -d|--domain) - DOMAIN="$2" - shift 2 - ;; - -e|--email) - ACME_EMAIL="$2" - shift 2 - ;; - -t|--target) - TARGET_HOST="$2" - shift 2 - ;; - --bootstrap-only) - BOOTSTRAP_ONLY=true - shift - ;; - --infra-only) - INFRA_ONLY=true - shift - ;; - --apps-only) - APPS_ONLY=true - shift - ;; - --test-only) - TEST_ONLY=true - shift - ;; - *) - log_error "Unknown option: $1" - show_help - exit 1 - ;; - esac -done - -# Main execution -main() { - log_info "Veza V5 Ultra Deployment Script" - log_info "================================" - echo - - check_prerequisites - - if [[ "$BOOTSTRAP_ONLY" == true ]]; then - run_playbook "ansible/playbooks/00-bootstrap-remote.yml" "Bootstrap Debian host" - elif [[ "$INFRA_ONLY" == true ]]; then - run_playbook "ansible/playbooks/00-bootstrap-remote.yml" "Bootstrap Debian host" - run_playbook "ansible/playbooks/10-incus-ovn.yml" "Install Incus + OVN single-host" - run_playbook "ansible/playbooks/20-incus-containers.yml" "Create Incus containers" - run_playbook "ansible/playbooks/30-haproxy-in-container.yml" "Configure HAProxy + ACME" "domain=$DOMAIN acme_email=$ACME_EMAIL" - elif [[ "$APPS_ONLY" == true ]]; then - run_playbook "ansible/playbooks/40-veza-apps.yml" "Deploy Veza applications" - elif [[ "$TEST_ONLY" == true ]]; then - run_playbook "ansible/playbooks/50-smoke.yml" "Run smoke tests" - else - deploy_veza - fi -} - -# Run main function -main "$@" diff --git a/ansible/group_vars/all.yml b/ansible/group_vars/all.yml deleted file mode 100644 index 3dcc1de46..000000000 --- a/ansible/group_vars/all.yml +++ /dev/null @@ -1,74 +0,0 @@ -# Group variables for Veza V5 Ultra deployment -# Domain and ACME configuration -domain: veza.talas.fr -acme_email: ops@talas.fr - -# Frontend runtime/build environment variables -VITE_API_URL: "https://{{ domain }}/api" -VITE_WS_URL: "wss://{{ domain }}/ws" -VITE_STREAM_URL: "https://{{ domain }}/stream" - -# HAProxy configuration (for in-container setup) -haproxy_letsencrypt: true -haproxy_https_monitoring: - - "{{ domain }}" - -# OVN/Incus single-host configuration -ovn_cluster_name: veza_single -ovn_cluster_main_name: edge-1 -ovn_ip: 127.0.0.1 -ovn_central_servers: [edge-1] - -# Incus profile for Veza network (created in play 20) -incus_network_profiles: - - name: veza - devices: - root: - type: disk - path: / - pool: default - eth0: - type: nic - nictype: ovn - network: veza-ovn - -# Container configuration -veza_containers: - - name: veza-haproxy - image: debian/bookworm - profiles: [veza] - proxy_devices: - - name: http80 - listen: tcp:0.0.0.0:80 - connect: tcp:127.0.0.1:80 - - name: https443 - listen: tcp:0.0.0.0:443 - connect: tcp:127.0.0.1:443 - - name: veza-backend - image: debian/bookworm - profiles: [veza] - - name: veza-chat - image: debian/bookworm - profiles: [veza] - - name: veza-stream - image: debian/bookworm - profiles: [veza] - - name: veza-web - image: debian/bookworm - profiles: [veza] - -# Application ports -veza_backend_port: 8080 -veza_chat_port: 8081 -veza_stream_port: 8082 -veza_web_port: 80 - -# Database and Redis configuration (will be set via vault) -veza_database_url: "postgresql://veza:veza_password@localhost:5432/veza_db" -veza_redis_url: "redis://localhost:6379" -veza_jwt_secret: "super-secret-jwt-key-change-in-production" -veza_jwt_refresh_secret: "super-secret-refresh-key" - -# Storage paths -veza_storage_path: "/opt/veza/storage" -veza_stream_path: "/opt/veza/streams" diff --git a/ansible/inventory/prod/hosts.yml b/ansible/inventory/prod/hosts.yml deleted file mode 100644 index ade32a02b..000000000 --- a/ansible/inventory/prod/hosts.yml +++ /dev/null @@ -1,17 +0,0 @@ -# Inventory for Veza V5 Ultra deployment -# Single Debian host with Incus/OVN + HAProxy-in-container + Let's Encrypt - -all: - vars: - ansible_user: senke - ansible_ssh_private_key_file: ~/.ssh/id_ed25519 # adjust as needed - ansible_become: true - ansible_python_interpreter: /usr/bin/python3 - children: - edge: - hosts: - edge-1: - ansible_host: 192.168.0.12 - veza_nodes: - hosts: - edge-1: diff --git a/ansible/inventory/test/hosts.yml b/ansible/inventory/test/hosts.yml deleted file mode 100644 index b2868a49b..000000000 --- a/ansible/inventory/test/hosts.yml +++ /dev/null @@ -1,18 +0,0 @@ -# Test inventory for Veza V5 Ultra deployment -# Using localhost for testing when target host is not available - -all: - vars: - ansible_user: senke - ansible_ssh_private_key_file: ~/.ssh/id_ed25519 - ansible_become: true - ansible_python_interpreter: /usr/bin/python3 - ansible_connection: local - children: - edge: - hosts: - edge-1: - ansible_host: localhost - veza_nodes: - hosts: - edge-1: diff --git a/ansible/playbooks/00-bootstrap-local.yml b/ansible/playbooks/00-bootstrap-local.yml deleted file mode 100644 index 62be2aecb..000000000 --- a/ansible/playbooks/00-bootstrap-local.yml +++ /dev/null @@ -1,104 +0,0 @@ ---- -# Bootstrap localhost for Veza V5 Ultra deployment testing -# Ensures python3, sudo, and essential tools are available - -- name: Bootstrap localhost for Veza deployment testing - hosts: edge - gather_facts: false - become: false - connection: local - - pre_tasks: - - name: Install essential packages (Fedora) - dnf: - name: - - python3 - - python3-pip - - sudo - - curl - - gnupg2 - - net-tools - - ca-certificates - - wget - - unzip - - git - - vim - - htop - - iotop - - nethogs - - snapd - - zfs - - lxd-tools - - bridge-utils - - dnsmasq - - openvswitch - - openvswitch-ovn-central - - openvswitch-ovn-host - - openvswitch-ovn-common - - firewalld - state: present - use_backend: dnf4 - - - name: Ensure python3 is available - command: which python3 - register: python3_check - failed_when: false - - - name: Create symlink for python if needed - file: - src: /usr/bin/python3 - dest: /usr/bin/python - state: link - when: python3_check.rc != 0 - - - name: Install Python packages for Ansible - pip: - name: - - ansible-core - - docker - - requests - - urllib3 - state: present - - - name: Ensure snapd service is enabled - systemd: - name: snapd - state: started - enabled: true - - - name: Enable and start OpenVSwitch - systemd: - name: "{{ item }}" - state: started - enabled: true - loop: - - openvswitch-switch - - ovn-northd - - ovn-controller - - - name: Start and enable firewalld - systemd: - name: firewalld - state: started - enabled: true - - - name: Configure firewall for Veza ports - command: firewall-cmd --permanent --add-port={{ item }}/tcp - loop: - - "22" # SSH - - "80" # HTTP - - "443" # HTTPS - - "8080" # Backend API - - "8081" # Chat WebSocket - - "8082" # Stream HLS - register: firewall_result - failed_when: false - - - name: Reload firewall rules - command: firewall-cmd --reload - register: firewall_reload_result - failed_when: false - - post_tasks: - - name: Test connectivity - ping: diff --git a/ansible/playbooks/00-bootstrap-remote.yml b/ansible/playbooks/00-bootstrap-remote.yml deleted file mode 100644 index 788e2040a..000000000 --- a/ansible/playbooks/00-bootstrap-remote.yml +++ /dev/null @@ -1,103 +0,0 @@ ---- -# Bootstrap remote Debian host for Veza V5 Ultra deployment -# Ensures python3, sudo, and essential tools are available - -- name: Bootstrap Debian host for Veza deployment - hosts: edge - gather_facts: false - become: true - - pre_tasks: - - name: Install essential packages - raw: | - apt-get update && apt-get install -y \ - python3 \ - python3-pip \ - sudo \ - curl \ - gnupg \ - net-tools \ - ca-certificates \ - apt-transport-https \ - lsb-release \ - wget \ - unzip \ - git \ - vim \ - htop \ - iotop \ - nethogs - - - name: Ensure python3 is available - raw: which python3 - register: python3_check - failed_when: false - - - name: Create symlink for python if needed - raw: ln -sf /usr/bin/python3 /usr/bin/python - when: python3_check.rc != 0 - - - name: Install additional packages - raw: | - apt-get install -y \ - python3-pip \ - python3-venv \ - snapd - - - name: Ensure user has sudo access - raw: | - if ! grep -q "senke ALL=(ALL) NOPASSWD:ALL" /etc/sudoers.d/senke; then - echo "senke ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/senke - chmod 440 /etc/sudoers.d/senke - fi - - - name: Configure SSH for better performance - lineinfile: - path: /etc/ssh/sshd_config - regexp: "{{ item.regexp }}" - line: "{{ item.line }}" - state: present - loop: - - { regexp: "^#?ClientAliveInterval", line: "ClientAliveInterval 60" } - - { regexp: "^#?ClientAliveCountMax", line: "ClientAliveCountMax 3" } - - { regexp: "^#?TCPKeepAlive", line: "TCPKeepAlive yes" } - notify: restart ssh - - - name: Ensure SSH service is enabled and running - systemd: - name: ssh - state: started - enabled: true - - - name: Install UFW - apt: - name: ufw - state: present - - - name: Configure firewall for Veza ports - community.general.ufw: - rule: allow - port: "{{ item }}" - proto: tcp - loop: - - "22" # SSH - - "80" # HTTP - - "443" # HTTPS - - "8080" # Backend API - - "8081" # Chat WebSocket - - "8082" # Stream HLS - - - name: Enable UFW - community.general.ufw: - state: enabled - policy: deny - - handlers: - - name: restart ssh - systemd: - name: ssh - state: restarted - - post_tasks: - - name: Test connectivity - ping: diff --git a/ansible/playbooks/00-demo-setup.yml b/ansible/playbooks/00-demo-setup.yml deleted file mode 100644 index b1d3590c8..000000000 --- a/ansible/playbooks/00-demo-setup.yml +++ /dev/null @@ -1,105 +0,0 @@ ---- -# Demo setup for Veza V5 Ultra deployment -# Shows the deployment process without requiring sudo - -- name: Demo Veza V5 Ultra deployment setup - hosts: edge - gather_facts: true - become: false - connection: local - - tasks: - - name: Check system information - debug: - msg: | - System: {{ ansible_distribution }} {{ ansible_distribution_version }} - Architecture: {{ ansible_architecture }} - Python: {{ ansible_python_version }} - User: {{ ansible_user_id }} - - - name: Check if required packages are installed - command: which {{ item }} - register: package_check - failed_when: false - loop: - - python3 - - curl - - git - - wget - - - name: Display package availability - debug: - msg: "{{ item.item }}: {{ 'Available' if item.rc == 0 else 'Not installed' }}" - loop: "{{ package_check.results }}" - - - name: Check if Incus is available - command: which incus - register: incus_check - failed_when: false - - - name: Display Incus status - debug: - msg: "Incus: {{ 'Available' if incus_check.rc == 0 else 'Not installed' }}" - - - name: Check if snapd is available - command: which snap - register: snap_check - failed_when: false - - - name: Display snapd status - debug: - msg: "Snapd: {{ 'Available' if snap_check.rc == 0 else 'Not installed' }}" - - - name: Check network interfaces - command: ip addr show - register: network_info - failed_when: false - - - name: Display network interfaces - debug: - var: network_info.stdout_lines - - - name: Check if ports are available - wait_for: - port: "{{ item }}" - host: localhost - timeout: 1 - register: port_check - failed_when: false - loop: - - 80 - - 443 - - 8080 - - 8081 - - 8082 - - - name: Display port availability - debug: - msg: "Port {{ item.item }}: {{ 'Available' if item.skipped else 'In use' }}" - loop: "{{ port_check.results }}" - - - name: Show deployment summary - debug: - msg: | - ======================================== - Veza V5 Ultra Deployment Demo - ======================================== - - This demo shows the deployment process for Veza V5 Ultra: - - 1. Bootstrap host (install packages, configure firewall) - 2. Install Incus + OVN (container runtime and networking) - 3. Create containers (haproxy, backend, chat, stream, web) - 4. Configure HAProxy + ACME (SSL termination and routing) - 5. Deploy applications (Go, Rust, React) - 6. Run smoke tests (validate all services) - - Target host: {{ ansible_host }} - Domain: {{ domain | default('veza.talas.fr') }} - - Next steps: - - Ensure target host is reachable via SSH - - Run full deployment with: ./deploy-veza.sh - - Or run individual playbooks step by step - - ======================================== diff --git a/ansible/playbooks/10-incus-ovn-local.yml b/ansible/playbooks/10-incus-ovn-local.yml deleted file mode 100644 index 94d4f01e8..000000000 --- a/ansible/playbooks/10-incus-ovn-local.yml +++ /dev/null @@ -1,83 +0,0 @@ ---- -# Install and configure Incus + OVN for Veza V5 Ultra deployment (local testing) -# Single-host setup with OVN networking - -- name: Install Incus and OVN (local testing) - hosts: edge - become: true - gather_facts: true - connection: local - - pre_tasks: - - name: Update package cache - apt: - update_cache: true - cache_valid_time: 3600 - - - name: Install Incus via snap - snap: - name: incus - state: present - classic: true - - - name: Wait for snapd to be ready - wait_for: - timeout: 30 - delegate_to: localhost - - tasks: - - name: Initialize Incus (standalone mode) - command: incus init --auto - register: incus_init_result - failed_when: false - - - name: Display Incus init result - debug: - var: incus_init_result.stdout_lines - when: incus_init_result.stdout_lines is defined - - - name: Create OVN network for Veza - command: | - incus network create veza-ovn \ - --type=ovn \ - --config network=veza-ovn \ - --config ipv4.address=10.10.0.1/24 \ - --config ipv4.nat=true \ - --config ipv6.address=fd42:veza::1/64 \ - --config ipv6.nat=true - register: ovn_network_result - failed_when: false - - - name: Display OVN network creation result - debug: - var: ovn_network_result.stdout_lines - when: ovn_network_result.stdout_lines is defined - - - name: Verify Incus is running - command: incus list - register: incus_status - failed_when: false - - - name: Display Incus status - debug: - var: incus_status.stdout_lines - when: incus_status.stdout_lines is defined - - - name: Verify OVN network exists - command: incus network list - register: network_list - failed_when: false - - - name: Display network list - debug: - var: network_list.stdout_lines - when: network_list.stdout_lines is defined - - post_tasks: - - name: Show Incus version - command: incus version - register: incus_version - - - name: Display Incus version - debug: - var: incus_version.stdout_lines diff --git a/ansible/playbooks/10-incus-ovn.yml b/ansible/playbooks/10-incus-ovn.yml deleted file mode 100644 index 7d29d2bf3..000000000 --- a/ansible/playbooks/10-incus-ovn.yml +++ /dev/null @@ -1,137 +0,0 @@ ---- -# Install and configure Incus + OVN for Veza V5 Ultra deployment -# Single-host setup with OVN networking - -- name: Install Incus and OVN for Veza V5 Ultra - hosts: edge - become: true - gather_facts: true - - pre_tasks: - - name: Update package cache - apt: - update_cache: true - cache_valid_time: 3600 - - - name: Install snapd if not present - apt: - name: snapd - state: present - - - name: Enable snapd service - systemd: - name: snapd - state: started - enabled: true - - - name: Create snapd socket symlink - file: - src: /var/lib/snapd/snapd.socket - dest: /run/snapd.socket - state: link - failed_when: false - - - name: Wait for snapd to be ready - wait_for: - path: /run/snapd.socket - timeout: 30 - - tasks: - - name: Install Incus via snap - command: snap install incus --classic - register: incus_install_result - failed_when: false - - - name: Wait for Incus to initialize - wait_for: - timeout: 30 - delegate_to: localhost - - - name: Initialize Incus (standalone mode) - command: incus init --auto - register: incus_init_result - failed_when: false - - - name: Display Incus init result - debug: - var: incus_init_result.stdout_lines - when: incus_init_result.stdout_lines is defined - - - name: Create OVN network for Veza - command: | - incus network create veza-ovn \ - --type=ovn \ - --config network=veza-ovn \ - --config ipv4.address=10.10.0.1/24 \ - --config ipv4.nat=true \ - --config ipv6.address=fd42:veza::1/64 \ - --config ipv6.nat=true - register: ovn_network_result - failed_when: false - - - name: Display OVN network creation result - debug: - var: ovn_network_result.stdout_lines - when: ovn_network_result.stdout_lines is defined - - - name: Create Veza network profile - command: | - incus profile create veza || true - incus profile set veza security.nesting=true - incus profile set veza security.privileged=false - incus profile device add veza root disk path=/ pool=default - incus profile device add veza eth0 nic nictype=ovn parent=veza-ovn - register: profile_result - failed_when: false - - - name: Display profile creation result - debug: - var: profile_result.stdout_lines - when: profile_result.stdout_lines is defined - - - name: Verify Incus is running - command: incus list - register: incus_status - failed_when: false - - - name: Display Incus status - debug: - var: incus_status.stdout_lines - when: incus_status.stdout_lines is defined - - - name: Verify OVN network exists - command: incus network list - register: network_list - failed_when: false - - - name: Display network list - debug: - var: network_list.stdout_lines - when: network_list.stdout_lines is defined - - - name: Verify Veza profile exists - command: incus profile list - register: profile_list - failed_when: false - - - name: Display profile list - debug: - var: profile_list.stdout_lines - when: profile_list.stdout_lines is defined - - post_tasks: - - name: Show Incus version - command: incus version - register: incus_version - - - name: Display Incus version - debug: - var: incus_version.stdout_lines - - - name: Show system resources - command: incus info - register: incus_info - - - name: Display Incus info - debug: - var: incus_info.stdout_lines \ No newline at end of file diff --git a/ansible/playbooks/20-incus-containers.yml b/ansible/playbooks/20-incus-containers.yml deleted file mode 100644 index af3b15ea4..000000000 --- a/ansible/playbooks/20-incus-containers.yml +++ /dev/null @@ -1,150 +0,0 @@ ---- -# Create Incus containers for Veza V5 Ultra deployment -# Creates all necessary containers with proper networking - -- name: Create Incus containers for Veza V5 Ultra - hosts: edge - become: true - gather_facts: true - - vars: - containers: - - name: veza-haproxy - image: debian/bookworm - profile: veza - cpu: 2 - memory: 2GB - disk: 10GB - ip: 10.10.0.100 - ports: - - "80:80" - - "443:443" - - name: veza-backend - image: debian/bookworm - profile: veza - cpu: 4 - memory: 4GB - disk: 20GB - ip: 10.10.0.101 - ports: - - "8080:8080" - - name: veza-chat - image: debian/bookworm - profile: veza - cpu: 2 - memory: 2GB - disk: 10GB - ip: 10.10.0.102 - ports: - - "8081:8081" - - name: veza-stream - image: debian/bookworm - profile: veza - cpu: 2 - memory: 2GB - disk: 20GB - ip: 10.10.0.103 - ports: - - "8082:8082" - - name: veza-web - image: debian/bookworm - profile: veza - cpu: 2 - memory: 2GB - disk: 10GB - ip: 10.10.0.104 - ports: - - "3000:3000" - - tasks: - - name: Create Veza containers - command: | - incus launch {{ item.image }} {{ item.name }} \ - --profile {{ item.profile }} \ - --config limits.cpu={{ item.cpu }} \ - --config limits.memory={{ item.memory }} \ - --config limits.disk={{ item.disk }} \ - --config boot.autostart=true \ - --config boot.autostart.delay=10 - register: container_create_result - failed_when: false - loop: "{{ containers }}" - - - name: Display container creation results - debug: - msg: "Container {{ item.item.name }}: {{ 'Created' if item.rc == 0 else 'Failed' }}" - loop: "{{ container_create_result.results }}" - - - name: Configure container networking - command: | - incus config device set {{ item.name }} eth0 ipv4.address={{ item.ip }}/24 - register: network_config_result - failed_when: false - loop: "{{ containers }}" - - - name: Display networking results - debug: - msg: "Network config {{ item.item.name }}: {{ 'Success' if item.rc == 0 else 'Failed' }}" - loop: "{{ network_config_result.results }}" - - - name: Add proxy devices for external access - command: | - incus config device add {{ item.name }} proxy{{ loop.index0 }} proxy \ - listen=tcp:0.0.0.0:{{ port.split(':')[0] }} \ - connect=tcp:127.0.0.1:{{ port.split(':')[1] }} - register: proxy_result - failed_when: false - loop: "{{ containers }}" - vars: - port_list: "{{ item.ports | default([]) }}" - when: item.ports is defined and item.ports | length > 0 - - - name: Start all containers - command: incus start {{ item.name }} - register: start_result - failed_when: false - loop: "{{ containers }}" - - - name: Display start results - debug: - msg: "Container {{ item.item.name }}: {{ 'Started' if item.rc == 0 else 'Failed to start' }}" - loop: "{{ start_result.results }}" - - - name: Wait for containers to be ready - wait_for: - port: 22 - host: "{{ item.ip }}" - timeout: 60 - register: container_ready - failed_when: false - loop: "{{ containers }}" - - - name: Display container readiness - debug: - msg: "Container {{ item.item.name }} ({{ item.item.ip }}): {{ 'Ready' if item.skipped else 'Not ready' }}" - loop: "{{ container_ready.results }}" - - - name: List all containers - command: incus list - register: container_list - - - name: Display container list - debug: - var: container_list.stdout_lines - - - name: Show container network configuration - command: incus network show veza-ovn - register: network_show - - - name: Display network configuration - debug: - var: network_show.stdout_lines - - post_tasks: - - name: Verify all containers are running - command: incus list --format=json - register: containers_json - - - name: Display running containers - debug: - msg: "Running containers: {{ containers_json.stdout | from_json | map(attribute='name') | list }}" \ No newline at end of file diff --git a/ansible/playbooks/30-haproxy-acme.yml b/ansible/playbooks/30-haproxy-acme.yml deleted file mode 100644 index 1bafa95c5..000000000 --- a/ansible/playbooks/30-haproxy-acme.yml +++ /dev/null @@ -1,286 +0,0 @@ ---- -# Configure HAProxy + Let's Encrypt ACME in container -# Sets up SSL termination and request routing - -- name: Configure HAProxy + Let's Encrypt ACME for Veza V5 Ultra - hosts: edge - become: true - gather_facts: true - - vars: - domain: "{{ domain | default('veza.talas.fr') }}" - acme_email: "{{ acme_email | default('ops@talas.fr') }}" - haproxy_container: "veza-haproxy" - webroot_port: 8888 - - tasks: - - name: Install HAProxy and ACME tools in container - command: | - incus exec {{ haproxy_container }} -- apt update - incus exec {{ haproxy_container }} -- apt install -y haproxy dehydrated nginx-light - register: install_result - failed_when: false - - - name: Display installation result - debug: - var: install_result.stdout_lines - - - name: Create ACME webroot directory - command: | - incus exec {{ haproxy_container }} -- mkdir -p /var/www/acme-challenge - incus exec {{ haproxy_container }} -- chown -R www-data:www-data /var/www/acme-challenge - register: webroot_result - failed_when: false - - - name: Configure nginx for ACME challenges - command: | - incus exec {{ haproxy_container }} -- tee /etc/nginx/sites-available/acme << 'EOF' - server { - listen 127.0.0.1:{{ webroot_port }}; - server_name _; - root /var/www/acme-challenge; - location /.well-known/acme-challenge/ { - try_files $uri =404; - } - } - EOF - register: nginx_config_result - failed_when: false - - - name: Enable nginx ACME site - command: | - incus exec {{ haproxy_container }} -- ln -sf /etc/nginx/sites-available/acme /etc/nginx/sites-enabled/ - incus exec {{ haproxy_container }} -- rm -f /etc/nginx/sites-enabled/default - incus exec {{ haproxy_container }} -- systemctl restart nginx - register: nginx_enable_result - failed_when: false - - - name: Configure dehydrated for Let's Encrypt - command: incus exec {{ haproxy_container }} -- bash -c 'echo "CA=\"https://acme-v02.api.letsencrypt.org/directory\"" > /etc/dehydrated/config' - register: dehydrated_config_result - failed_when: false - - - name: Add CHALLENGETYPE to dehydrated config - command: incus exec {{ haproxy_container }} -- bash -c 'echo "CHALLENGETYPE=\"http-01\"" >> /etc/dehydrated/config' - register: dehydrated_config_result2 - failed_when: false - - - name: Add WELLKNOWN to dehydrated config - command: incus exec {{ haproxy_container }} -- bash -c 'echo "WELLKNOWN=\"/var/www/acme-challenge\"" >> /etc/dehydrated/config' - register: dehydrated_config_result3 - failed_when: false - - - name: Add CONTACT_EMAIL to dehydrated config - command: incus exec {{ haproxy_container }} -- bash -c 'echo "CONTACT_EMAIL=\"{{ acme_email }}\"" >> /etc/dehydrated/config' - register: dehydrated_config_result4 - failed_when: false - - - name: Add HOOK to dehydrated config - command: incus exec {{ haproxy_container }} -- bash -c 'echo "HOOK=\"/etc/dehydrated/hook.sh\"" >> /etc/dehydrated/config' - register: dehydrated_config_result - failed_when: false - - - name: Create dehydrated hook script - command: | - incus exec {{ haproxy_container }} -- bash -c 'cat > /etc/dehydrated/hook.sh << "EOF" - #!/bin/bash - # Dehydrated hook for HAProxy certificate management - - case "$1" in - "deploy_cert") - # Deploy certificate to HAProxy - cat "$3" "$5" > /etc/haproxy/certs/${2}.pem - systemctl reload haproxy - ;; - "clean_challenge") - # Clean up challenge files - rm -f /var/www/acme-challenge/* - ;; - "deploy_challenge") - # Deploy challenge file - cp "$2" "/var/www/acme-challenge/$3" - ;; - "unchanged_cert") - # Certificate unchanged - ;; - esac - EOF' - register: hook_script_result - failed_when: false - - - name: Make hook script executable - command: | - incus exec {{ haproxy_container }} -- chmod +x /etc/dehydrated/hook.sh - register: hook_executable_result - failed_when: false - - - name: Create HAProxy configuration - command: | - incus exec {{ haproxy_container }} -- tee /etc/haproxy/haproxy.cfg << 'EOF' - global - daemon - user haproxy - group haproxy - log stdout local0 - chroot /var/lib/haproxy - stats socket /run/haproxy/admin.sock mode 660 level admin - stats timeout 30s - tune.ssl.default-dh-param 2048 - - defaults - mode http - log global - option httplog - option dontlognull - option log-health-checks - option forwardfor - option httpchk GET /health - timeout connect 5000 - timeout client 50000 - timeout server 50000 - errorfile 400 /etc/haproxy/errors/400.http - errorfile 403 /etc/haproxy/errors/403.http - errorfile 408 /etc/haproxy/errors/408.http - errorfile 500 /etc/haproxy/errors/500.http - errorfile 502 /etc/haproxy/errors/502.http - errorfile 503 /etc/haproxy/errors/503.http - errorfile 504 /etc/haproxy/errors/504.http - - # ACME challenge backend - backend acme - server acme 127.0.0.1:{{ webroot_port }} check - - # API backend - backend be_api - balance roundrobin - option httpchk GET /api/health - http-check expect status 200 - server api1 10.10.0.101:8080 check inter 2000 rise 2 fall 3 - - # WebSocket backend - backend be_ws - mode tcp - balance roundrobin - server ws1 10.10.0.102:8081 check inter 2000 rise 2 fall 3 - - # Stream backend - backend be_stream - balance roundrobin - option httpchk GET /stream/health - http-check expect status 200 - server stream1 10.10.0.103:8082 check inter 2000 rise 2 fall 3 - - # Web frontend backend - backend be_web - balance roundrobin - option httpchk GET / - http-check expect status 200 - server web1 10.10.0.104:3000 check inter 2000 rise 2 fall 3 - - # HTTP frontend (redirect to HTTPS) - frontend http_frontend - bind *:80 - acl acme_challenge path_beg /.well-known/acme-challenge/ - use_backend acme if acme_challenge - redirect scheme https code 301 if !acme_challenge - - # HTTPS frontend - frontend https_frontend - bind *:443 ssl crt /etc/haproxy/certs/{{ domain }}.pem alpn h2,http/1.1 - - # Security headers - http-response set-header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" - http-response set-header X-Content-Type-Options "nosniff" - http-response set-header X-Frame-Options "DENY" - http-response set-header X-XSS-Protection "1; mode=block" - http-response set-header Referrer-Policy "no-referrer" - http-response set-header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self' wss:;" - - # Routing rules - acl is_api path_beg /api - acl is_ws path_beg /ws - acl is_stream path_beg /stream - - use_backend be_api if is_api - use_backend be_ws if is_ws - use_backend be_stream if is_stream - default_backend be_web - - # Statistics - listen stats - bind *:8404 - stats enable - stats uri /stats - stats refresh 30s - stats admin if TRUE - EOF - register: haproxy_config_result - failed_when: false - - - name: Create HAProxy certificates directory - command: | - incus exec {{ haproxy_container }} -- mkdir -p /etc/haproxy/certs - register: certs_dir_result - failed_when: false - - - name: Generate self-signed certificate (temporary) - command: | - incus exec {{ haproxy_container }} -- openssl req -x509 -newkey rsa:4096 -keyout /etc/haproxy/certs/{{ domain }}.pem -out /etc/haproxy/certs/{{ domain }}.pem -days 365 -nodes -subj "/C=FR/ST=France/L=Paris/O=Veza/OU=IT/CN={{ domain }}" - register: self_signed_result - failed_when: false - - - name: Start HAProxy service - command: | - incus exec {{ haproxy_container }} -- systemctl enable haproxy - incus exec {{ haproxy_container }} -- systemctl start haproxy - register: haproxy_start_result - failed_when: false - - - name: Check HAProxy status - command: | - incus exec {{ haproxy_container }} -- systemctl status haproxy - register: haproxy_status - failed_when: false - - - name: Display HAProxy status - debug: - var: haproxy_status.stdout_lines - - - name: Request Let's Encrypt certificate - command: | - incus exec {{ haproxy_container }} -- dehydrated -c -d {{ domain }} - register: acme_cert_result - failed_when: false - - - name: Display ACME certificate result - debug: - var: acme_cert_result.stdout_lines - - - name: Setup certificate renewal cron - command: | - incus exec {{ haproxy_container }} -- tee /etc/cron.d/dehydrated << 'EOF' - 0 12 * * * root /usr/bin/dehydrated -c - EOF - register: cron_result - failed_when: false - - - name: Test HAProxy configuration - command: | - incus exec {{ haproxy_container }} -- haproxy -c -f /etc/haproxy/haproxy.cfg - register: haproxy_test_result - failed_when: false - - - name: Display HAProxy test result - debug: - var: haproxy_test_result.stdout_lines - - post_tasks: - - name: Show HAProxy statistics - command: | - incus exec {{ haproxy_container }} -- curl -s http://localhost:8404/stats - register: haproxy_stats - failed_when: false - - - name: Display HAProxy statistics - debug: - var: haproxy_stats.stdout_lines diff --git a/ansible/playbooks/30-haproxy-in-container.yml b/ansible/playbooks/30-haproxy-in-container.yml deleted file mode 100644 index e79235fbb..000000000 --- a/ansible/playbooks/30-haproxy-in-container.yml +++ /dev/null @@ -1,269 +0,0 @@ ---- -# Configure HAProxy inside container with Let's Encrypt ACME HTTP-01 -# Handles SSL termination and routing for Veza V5 Ultra - -- name: Configure HAProxy in container with ACME - hosts: edge - become: true - gather_facts: true - - vars: - haproxy_container: veza-haproxy - acme_webroot_port: 8888 - haproxy_certs_dir: /etc/haproxy/certs - acme_webroot_dir: /var/www/acme-challenge - - tasks: - - name: Install HAProxy and ACME tools in container - command: | - incus exec {{ haproxy_container }} -- bash -c " - apt update && apt install -y \ - haproxy \ - curl \ - wget \ - socat \ - cron \ - nginx-light \ - openssl - " - register: haproxy_install_result - failed_when: false - - - name: Display HAProxy installation result - debug: - var: haproxy_install_result.stdout_lines - when: haproxy_install_result.stdout_lines is defined - - - name: Create ACME webroot directory - command: | - incus exec {{ haproxy_container }} -- mkdir -p {{ acme_webroot_dir }} - register: webroot_create_result - failed_when: false - - - name: Create HAProxy certificates directory - command: | - incus exec {{ haproxy_container }} -- mkdir -p {{ haproxy_certs_dir }} - register: certs_dir_result - failed_when: false - - - name: Install dehydrated for ACME - command: | - incus exec {{ haproxy_container }} -- bash -c " - cd /opt && \ - git clone https://github.com/dehydrated-io/dehydrated.git && \ - chmod +x dehydrated/dehydrated - " - register: dehydrated_install_result - failed_when: false - - - name: Create dehydrated configuration - command: | - incus exec {{ haproxy_container }} -- bash -c " - cat > /opt/dehydrated/config << 'EOF' - WELLKNOWN={{ acme_webroot_dir }} - DOMAINS_TXT=/opt/dehydrated/domains.txt - HOOK=/opt/dehydrated/hook.sh - CHALLENGETYPE=http-01 - EOF - " - register: dehydrated_config_result - failed_when: false - - - name: Create domains file for ACME - command: | - incus exec {{ haproxy_container }} -- bash -c " - echo '{{ domain }}' > /opt/dehydrated/domains.txt - " - register: domains_file_result - failed_when: false - - - name: Create ACME hook script - command: | - incus exec {{ haproxy_container }} -- bash -c " - cat > /opt/dehydrated/hook.sh << 'EOF' - #!/bin/bash - case \"\$1\" in - deploy_challenge) - # Start nginx for ACME challenge - nginx -c /etc/nginx/nginx.conf -g 'daemon on;' - ;; - clean_challenge) - # Stop nginx after challenge - nginx -s quit - ;; - deploy_cert) - # Combine cert and key for HAProxy - cat \$3 \$5 > {{ haproxy_certs_dir }}/{{ domain }}.pem - # Reload HAProxy - systemctl reload haproxy - ;; - esac - EOF - chmod +x /opt/dehydrated/hook.sh - " - register: hook_script_result - failed_when: false - - - name: Create nginx config for ACME webroot - command: | - incus exec {{ haproxy_container }} -- bash -c " - cat > /etc/nginx/nginx.conf << 'EOF' - events { worker_connections 1024; } - http { - server { - listen {{ acme_webroot_port }}; - location /.well-known/acme-challenge/ { - root {{ acme_webroot_dir }}; - } - } - } - EOF - " - register: nginx_config_result - failed_when: false - - - name: Create HAProxy configuration - command: | - incus exec {{ haproxy_container }} -- bash -c " - cat > /etc/haproxy/haproxy.cfg << 'EOF' - global - log stdout local0 - chroot /var/lib/haproxy - stats socket /run/haproxy/admin.sock mode 660 level admin - stats timeout 30s - user haproxy - group haproxy - daemon - maxconn 20000 - ssl-default-bind-options no-sslv3 no-tls-tickets - ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384 - ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256 - - defaults - log global - mode http - option httplog - option dontlognull - option forwardfor - timeout connect 5s - timeout client 50s - timeout server 50s - timeout http-request 5s - errorfile 400 /etc/haproxy/errors/400.http - errorfile 403 /etc/haproxy/errors/403.http - errorfile 408 /etc/haproxy/errors/408.http - errorfile 500 /etc/haproxy/errors/500.http - errorfile 502 /etc/haproxy/errors/502.http - errorfile 503 /etc/haproxy/errors/503.http - errorfile 504 /etc/haproxy/errors/504.http - - frontend http_frontend - bind *:80 - acl acme_challenge path_beg /.well-known/acme-challenge/ - use_backend acme_backend if acme_challenge - redirect scheme https code 301 if !acme_challenge - - frontend https_frontend - bind *:443 ssl crt {{ haproxy_certs_dir }}/{{ domain }}.pem alpn h2,http/1.1 - http-response set-header Strict-Transport-Security \"max-age=31536000; includeSubDomains; preload\" - http-response set-header X-Content-Type-Options nosniff - http-response set-header X-Frame-Options DENY - http-response set-header Referrer-Policy no-referrer - http-response set-header Content-Security-Policy \"default-src 'self'; connect-src 'self' wss://{{ domain }} https://{{ domain }}; img-src 'self' data:; script-src 'self'; style-src 'self' 'unsafe-inline'\" - - acl is_api path_beg /api - acl is_ws path_beg /ws - acl is_stream path_beg /stream - - use_backend api_backend if is_api - use_backend ws_backend if is_ws - use_backend stream_backend if is_stream - default_backend web_backend - - backend acme_backend - server acme_server 127.0.0.1:{{ acme_webroot_port }} - - backend api_backend - server api_server veza-backend:{{ veza_backend_port }} check - - backend ws_backend - mode tcp - server ws_server veza-chat:{{ veza_chat_port }} check - - backend stream_backend - server stream_server veza-stream:{{ veza_stream_port }} check - - backend web_backend - server web_server veza-web:{{ veza_web_port }} check - EOF - " - register: haproxy_config_result - failed_when: false - - - name: Create self-signed certificate for initial setup - command: | - incus exec {{ haproxy_container }} -- bash -c " - openssl req -x509 -newkey rsa:2048 -keyout {{ haproxy_certs_dir }}/{{ domain }}.key -out {{ haproxy_certs_dir }}/{{ domain }}.crt -days 365 -nodes -subj '/CN={{ domain }}' && \ - cat {{ haproxy_certs_dir }}/{{ domain }}.crt {{ haproxy_certs_dir }}/{{ domain }}.key > {{ haproxy_certs_dir }}/{{ domain }}.pem - " - register: selfsigned_cert_result - failed_when: false - - - name: Start HAProxy service - command: | - incus exec {{ haproxy_container }} -- systemctl enable haproxy && \ - incus exec {{ haproxy_container }} -- systemctl start haproxy - register: haproxy_start_result - failed_when: false - - - name: Display HAProxy start result - debug: - var: haproxy_start_result.stdout_lines - when: haproxy_start_result.stdout_lines is defined - - - name: Check HAProxy status - command: | - incus exec {{ haproxy_container }} -- systemctl status haproxy - register: haproxy_status_result - failed_when: false - - - name: Display HAProxy status - debug: - var: haproxy_status_result.stdout_lines - when: haproxy_status_result.stdout_lines is defined - - - name: Create ACME renewal cron job - command: | - incus exec {{ haproxy_container }} -- bash -c " - echo '0 2 * * * /opt/dehydrated/dehydrated -c' | crontab - - " - register: cron_setup_result - failed_when: false - - - name: Display cron setup result - debug: - var: cron_setup_result.stdout_lines - when: cron_setup_result.stdout_lines is defined - - post_tasks: - - name: Test HAProxy configuration - command: | - incus exec {{ haproxy_container }} -- haproxy -c -f /etc/haproxy/haproxy.cfg - register: haproxy_test_result - failed_when: false - - - name: Display HAProxy test result - debug: - var: haproxy_test_result.stdout_lines - when: haproxy_test_result.stdout_lines is defined - - - name: Show final HAProxy status - command: | - incus exec {{ haproxy_container }} -- netstat -tlnp | grep haproxy - register: final_haproxy_status - failed_when: false - - - name: Display final HAProxy status - debug: - var: final_haproxy_status.stdout_lines - when: final_haproxy_status.stdout_lines is defined diff --git a/ansible/playbooks/31-haproxy-fix.yml b/ansible/playbooks/31-haproxy-fix.yml deleted file mode 100644 index e2a30de3e..000000000 --- a/ansible/playbooks/31-haproxy-fix.yml +++ /dev/null @@ -1,131 +0,0 @@ ---- -- name: Configurer HAProxy avec Let's Encrypt (version fixée) - hosts: edge - become: true - gather_facts: true - - vars: - domain: "{{ domain | default('veza.talas.fr') }}" - acme_email: "{{ acme_email | default('ops@talas.fr') }}" - haproxy_container: "veza-haproxy" - - tasks: - - name: Installer les packages de base dans HAProxy - command: | - incus exec {{ haproxy_container }} -- apt update - incus exec {{ haproxy_container }} -- apt install -y haproxy certbot nginx-light curl - register: install_result - failed_when: false - - - name: Créer les répertoires nécessaires - command: | - incus exec {{ haproxy_container }} -- mkdir -p /etc/haproxy/certs /var/www/acme - - - name: Créer la configuration HAProxy directement dans le conteneur - command: | - incus exec {{ haproxy_container }} -- bash -c 'cat > /etc/haproxy/haproxy.cfg << EOF - global - daemon - maxconn 2000 - log stdout local0 - tune.ssl.default-dh-param 2048 - - defaults - mode http - log global - option httplog - option dontlognull - timeout connect 5000 - timeout client 50000 - timeout server 50000 - - frontend http_front - bind *:80 - acl letsencrypt path_beg /.well-known/acme-challenge/ - use_backend letsencrypt if letsencrypt - redirect scheme https code 301 if !letsencrypt - - backend letsencrypt - server certbot 127.0.0.1:8888 - - frontend https_front - bind *:443 ssl crt /etc/haproxy/certs/{{ domain }}.pem alpn h2,http/1.1 - http-response set-header Strict-Transport-Security "max-age=31536000; includeSubDomains" - - acl is_api path_beg /api - acl is_ws path_beg /ws - acl is_stream path_beg /stream - - use_backend be_api if is_api - use_backend be_ws if is_ws - use_backend be_stream if is_stream - default_backend be_web - - backend be_api - balance roundrobin - server api1 10.20.0.101:8080 check - - backend be_ws - balance roundrobin - server ws1 10.20.0.102:8081 check - - backend be_stream - balance roundrobin - server stream1 10.20.0.103:8082 check - - backend be_web - balance roundrobin - server web1 10.20.0.104:3000 check - EOF' - - - name: Créer certificat auto-signé temporaire - command: | - incus exec {{ haproxy_container }} -- openssl req -x509 -newkey rsa:2048 \ - -keyout /etc/haproxy/certs/{{ domain }}.pem \ - -out /etc/haproxy/certs/{{ domain }}.pem \ - -days 365 -nodes -subj "/CN={{ domain }}" - - - name: Démarrer HAProxy - command: | - incus exec {{ haproxy_container }} -- systemctl enable haproxy - incus exec {{ haproxy_container }} -- systemctl restart haproxy - - - name: Configurer nginx pour ACME - command: | - incus exec {{ haproxy_container }} -- bash -c 'cat > /etc/nginx/sites-available/acme << EOF - server { - listen 127.0.0.1:8888; - root /var/www/acme; - location /.well-known/acme-challenge/ { - try_files \$uri =404; - } - } - EOF' - - - name: Activer le site nginx - command: | - incus exec {{ haproxy_container }} -- ln -sf /etc/nginx/sites-available/acme /etc/nginx/sites-enabled/ - incus exec {{ haproxy_container }} -- rm -f /etc/nginx/sites-enabled/default - incus exec {{ haproxy_container }} -- systemctl restart nginx - - - name: Obtenir certificat Let's Encrypt - command: | - incus exec {{ haproxy_container }} -- certbot certonly \ - --webroot -w /var/www/acme \ - -d {{ domain }} \ - --email {{ acme_email }} \ - --agree-tos --non-interactive - register: certbot_result - failed_when: false - - - name: Créer le PEM pour HAProxy - command: | - incus exec {{ haproxy_container }} -- bash -c \ - 'cat /etc/letsencrypt/live/{{ domain }}/fullchain.pem \ - /etc/letsencrypt/live/{{ domain }}/privkey.pem \ - > /etc/haproxy/certs/{{ domain }}.pem' - when: certbot_result.rc == 0 - - - name: Recharger HAProxy - command: | - incus exec {{ haproxy_container }} -- systemctl reload haproxy diff --git a/ansible/playbooks/40-veza-apps-simple.yml b/ansible/playbooks/40-veza-apps-simple.yml deleted file mode 100644 index 1d922e180..000000000 --- a/ansible/playbooks/40-veza-apps-simple.yml +++ /dev/null @@ -1,298 +0,0 @@ ---- -# Deploy Veza V5 Ultra applications in containers (simplified version) -# Builds and runs backend, chat, stream, and web services - -- name: Deploy Veza V5 Ultra applications - hosts: edge - become: true - gather_facts: true - - vars: - domain: "{{ domain | default('veza.talas.fr') }}" - backend_container: "veza-backend" - chat_container: "veza-chat" - stream_container: "veza-stream" - web_container: "veza-web" - - tasks: - - name: Deploy Go Backend API - block: - - name: Install Go in backend container - command: | - incus exec {{ backend_container }} -- apt update - incus exec {{ backend_container }} -- apt install -y wget git - incus exec {{ backend_container }} -- wget https://go.dev/dl/go1.21.5.linux-amd64.tar.gz - incus exec {{ backend_container }} -- tar -C /usr/local -xzf go1.21.5.linux-amd64.tar.gz - incus exec {{ backend_container }} -- echo 'export PATH=$PATH:/usr/local/go/bin' >> /root/.bashrc - register: go_install_result - failed_when: false - - - name: Create backend application directory - command: | - incus exec {{ backend_container }} -- mkdir -p /opt/veza-backend - register: backend_dir_result - failed_when: false - - - name: Create simple backend server - copy: - content: | - package main - - import ( - "fmt" - "log" - "net/http" - "os" - ) - - func main() { - port := os.Getenv("PORT") - if port == "" { - port = "8080" - } - - http.HandleFunc("/api/health", func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, `{"status":"ok","service":"veza-backend"}`) - }) - - http.HandleFunc("/api/", func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, `{"message":"Veza V5 Ultra Backend API","version":"1.0.0"}`) - }) - - log.Printf("Backend API server starting on port %s", port) - log.Fatal(http.ListenAndServe(":"+port, nil)) - } - dest: /tmp/main.go - delegate_to: localhost - - - name: Copy backend code to container - command: | - incus file push /tmp/main.go {{ backend_container }}/opt/veza-backend/main.go - register: backend_code_result - failed_when: false - - - name: Build backend application - command: | - incus exec {{ backend_container }} -- bash -c "cd /opt/veza-backend && /usr/local/go/bin/go mod init veza-backend && /usr/local/go/bin/go build -ldflags '-s -w' -o veza-backend main.go" - register: backend_build_result - failed_when: false - - - name: Create backend systemd service - copy: - content: | - [Unit] - Description=Veza V5 Ultra Backend API - After=network.target - - [Service] - Type=simple - User=root - WorkingDirectory=/opt/veza-backend - ExecStart=/opt/veza-backend/veza-backend - Restart=always - RestartSec=5 - Environment=PORT=8080 - Environment=DATABASE_URL=postgresql://veza:password@localhost:5432/veza_db - Environment=REDIS_URL=redis://localhost:6379 - Environment=JWT_SECRET=super-secret-jwt-key - Environment=JWT_REFRESH_SECRET=super-secret-refresh-key - - [Install] - WantedBy=multi-user.target - dest: /tmp/veza-backend.service - delegate_to: localhost - - - name: Copy systemd service to container - command: | - incus file push /tmp/veza-backend.service {{ backend_container }}/etc/systemd/system/veza-backend.service - register: backend_service_result - failed_when: false - - - name: Start backend service - command: | - incus exec {{ backend_container }} -- systemctl daemon-reload - incus exec {{ backend_container }} -- systemctl enable veza-backend - incus exec {{ backend_container }} -- systemctl start veza-backend - register: backend_start_result - failed_when: false - - - name: Check backend service status - command: | - incus exec {{ backend_container }} -- systemctl status veza-backend - register: backend_status - failed_when: false - - - name: Display backend status - debug: - var: backend_status.stdout_lines - - rescue: - - name: Backend deployment failed - debug: - msg: "Backend deployment failed, continuing with other services" - - - name: Deploy simple web application - block: - - name: Install Node.js in web container - command: | - incus exec {{ web_container }} -- apt update - incus exec {{ web_container }} -- apt install -y curl nginx - incus exec {{ web_container }} -- curl -fsSL https://deb.nodesource.com/setup_18.x | bash - - incus exec {{ web_container }} -- apt install -y nodejs - register: node_install_result - failed_when: false - - - name: Create web application directory - command: | - incus exec {{ web_container }} -- mkdir -p /var/www/veza - register: web_dir_result - failed_when: false - - - name: Create simple web page - copy: - content: | - - - - Veza V5 Ultra - - - -
-
-

🎵 Veza V5 Ultra

-

Collaborative Audio Streaming Platform

-
-
-
✅ System Online
-

Services Status

-
    -
  • Backend API: Checking...
  • -
  • Chat WebSocket: Checking...
  • -
  • Stream HLS: Checking...
  • -
-

Features

-
    -
  • Real-time collaborative audio streaming
  • -
  • WebSocket chat integration
  • -
  • HLS video streaming
  • -
  • Modern React frontend
  • -
-
-
- - - - dest: /tmp/index.html - delegate_to: localhost - - - name: Copy web page to container - command: | - incus file push /tmp/index.html {{ web_container }}/var/www/veza/index.html - register: web_page_result - failed_when: false - - - name: Configure nginx - copy: - content: | - server { - listen 3000; - server_name _; - root /var/www/veza; - index index.html; - - location / { - try_files $uri $uri/ =404; - } - - location /api/ { - proxy_pass http://10.10.0.101:8080; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - } - - location /ws { - proxy_pass http://10.10.0.102:8081; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "upgrade"; - proxy_set_header Host $host; - } - - location /stream/ { - proxy_pass http://10.10.0.103:8082; - proxy_set_header Host $host; - } - } - dest: /tmp/veza-nginx.conf - delegate_to: localhost - - - name: Copy nginx config to container - command: | - incus file push /tmp/veza-nginx.conf {{ web_container }}/etc/nginx/sites-available/veza - register: nginx_config_result - failed_when: false - - - name: Enable nginx site - command: | - incus exec {{ web_container }} -- ln -sf /etc/nginx/sites-available/veza /etc/nginx/sites-enabled/ - incus exec {{ web_container }} -- rm -f /etc/nginx/sites-enabled/default - incus exec {{ web_container }} -- systemctl restart nginx - register: nginx_enable_result - failed_when: false - - - name: Check web service status - command: | - incus exec {{ web_container }} -- systemctl status nginx - register: web_status - failed_when: false - - - name: Display web status - debug: - var: web_status.stdout_lines - - rescue: - - name: Web deployment failed - debug: - msg: "Web deployment failed" - - post_tasks: - - name: Clean up temporary files - file: - path: "{{ item }}" - state: absent - loop: - - /tmp/main.go - - /tmp/veza-backend.service - - /tmp/index.html - - /tmp/veza-nginx.conf - delegate_to: localhost - failed_when: false - - - name: Show all running services - command: | - incus exec {{ backend_container }} -- systemctl list-units --type=service --state=running | grep veza || true - incus exec {{ web_container }} -- systemctl list-units --type=service --state=running | grep nginx || true - register: all_services - failed_when: false - - - name: Display all services - debug: - var: all_services.stdout_lines diff --git a/ansible/playbooks/40-veza-apps.yml b/ansible/playbooks/40-veza-apps.yml deleted file mode 100644 index 2aab9e33c..000000000 --- a/ansible/playbooks/40-veza-apps.yml +++ /dev/null @@ -1,599 +0,0 @@ ---- -# Deploy Veza V5 Ultra applications in containers -# Builds and runs backend, chat, stream, and web services - -- name: Deploy Veza V5 Ultra applications - hosts: edge - become: true - gather_facts: true - - vars: - domain: "{{ domain | default('veza.talas.fr') }}" - backend_container: "veza-backend" - chat_container: "veza-chat" - stream_container: "veza-stream" - web_container: "veza-web" - - tasks: - - name: Deploy Go Backend API - block: - - name: Install Go in backend container - command: | - incus exec {{ backend_container }} -- apt update - incus exec {{ backend_container }} -- apt install -y wget git - incus exec {{ backend_container }} -- wget https://go.dev/dl/go1.21.5.linux-amd64.tar.gz - incus exec {{ backend_container }} -- tar -C /usr/local -xzf go1.21.5.linux-amd64.tar.gz - incus exec {{ backend_container }} -- echo 'export PATH=$PATH:/usr/local/go/bin' >> /root/.bashrc - register: go_install_result - failed_when: false - - - name: Display Go installation result - debug: - var: go_install_result.stdout_lines - - - name: Create backend application directory - command: | - incus exec {{ backend_container }} -- mkdir -p /opt/veza-backend - register: backend_dir_result - failed_when: false - - - name: Copy backend source code (placeholder) - command: | - incus exec {{ backend_container }} -- bash -c 'cat > /opt/veza-backend/main.go << "EOF" - package main - - import ( - "fmt" - "log" - "net/http" - "os" - ) - - func main() { - port := os.Getenv("PORT") - if port == "" { - port = "8080" - } - - http.HandleFunc("/api/health", func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, `{"status":"ok","service":"veza-backend"}`) - }) - - http.HandleFunc("/api/", func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - fmt.Fprintf(w, `{"message":"Veza V5 Ultra Backend API","version":"1.0.0"}`) - }) - - log.Printf("Backend API server starting on port %s", port) - log.Fatal(http.ListenAndServe(":"+port, nil)) - } - EOF' - register: backend_code_result - failed_when: false - - - name: Build backend application - command: | - incus exec {{ backend_container }} -- bash -c "cd /opt/veza-backend && /usr/local/go/bin/go mod init veza-backend && /usr/local/go/bin/go build -ldflags '-s -w' -o veza-backend main.go" - register: backend_build_result - failed_when: false - - - name: Create backend systemd service - command: | - incus exec {{ backend_container }} -- bash -c 'cat > /etc/systemd/system/veza-backend.service << "EOF" - [Unit] - Description=Veza V5 Ultra Backend API - After=network.target - - [Service] - Type=simple - User=root - WorkingDirectory=/opt/veza-backend - ExecStart=/opt/veza-backend/veza-backend - Restart=always - RestartSec=5 - Environment=PORT=8080 - Environment=DATABASE_URL=postgresql://veza:password@localhost:5432/veza_db - Environment=REDIS_URL=redis://localhost:6379 - Environment=JWT_SECRET=super-secret-jwt-key - Environment=JWT_REFRESH_SECRET=super-secret-refresh-key - - [Install] - WantedBy=multi-user.target - EOF' - register: backend_service_result - failed_when: false - - - name: Start backend service - command: | - incus exec {{ backend_container }} -- systemctl daemon-reload - incus exec {{ backend_container }} -- systemctl enable veza-backend - incus exec {{ backend_container }} -- systemctl start veza-backend - register: backend_start_result - failed_when: false - - - name: Check backend service status - command: | - incus exec {{ backend_container }} -- systemctl status veza-backend - register: backend_status - failed_when: false - - - name: Display backend status - debug: - var: backend_status.stdout_lines - - rescue: - - name: Backend deployment failed - debug: - msg: "Backend deployment failed, continuing with other services" - - - name: Deploy Rust Chat Server - block: - - name: Install Rust in chat container - command: | - incus exec {{ chat_container }} -- apt update - incus exec {{ chat_container }} -- apt install -y curl git - incus exec {{ chat_container }} -- curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y - incus exec {{ chat_container }} -- bash -c "source /root/.cargo/env && cargo --version" - register: rust_install_result - failed_when: false - - - name: Display Rust installation result - debug: - var: rust_install_result.stdout_lines - - - name: Create chat application directory - command: | - incus exec {{ chat_container }} -- mkdir -p /opt/veza-chat - register: chat_dir_result - failed_when: false - - - name: Copy chat source code (placeholder) - command: | - incus exec {{ chat_container }} -- bash -c 'cat > /opt/veza-chat/Cargo.toml << "EOF" - [package] - name = "veza-chat" - version = "0.1.0" - edition = "2021" - - [dependencies] - tokio = { version = "1.0", features = ["full"] } - axum = "0.7" - tower = "0.4" - tower-http = { version = "0.5", features = ["cors"] } - serde = { version = "1.0", features = ["derive"] } - serde_json = "1.0" - uuid = { version = "1.0", features = ["v4"] } - tracing = "0.1" - tracing-subscriber = "0.3" - EOF' - register: chat_cargo_result - failed_when: false - - - name: Create chat main.rs - command: | - incus exec {{ chat_container }} -- tee /opt/veza-chat/src/main.rs << 'EOF' - use axum::{ - extract::ws::{Message, WebSocket, WebSocketUpgrade}, - response::Response, - routing::get, - Router, - }; - use std::net::SocketAddr; - use tokio::net::TcpListener; - use tracing::{info, warn}; - - #[tokio::main] - async fn main() { - tracing_subscriber::init(); - - let app = Router::new() - .route("/ws", get(websocket_handler)) - .route("/health", get(health_handler)); - - let addr = SocketAddr::from(([0, 0, 0, 0], 8081)); - info!("Chat server starting on {}", addr); - - let listener = TcpListener::bind(addr).await.unwrap(); - axum::serve(listener, app).await.unwrap(); - } - - async fn websocket_handler(ws: WebSocketUpgrade) -> Response { - ws.on_upgrade(handle_websocket) - } - - async fn handle_websocket(socket: WebSocket) { - info!("New WebSocket connection"); - - // Simple echo server for now - let (mut sender, mut receiver) = socket.split(); - - while let Some(msg) = receiver.recv().await { - match msg { - Ok(Message::Text(text)) => { - info!("Received: {}", text); - if sender.send(Message::Text(format!("Echo: {}", text))).await.is_err() { - break; - } - } - Ok(Message::Close(_)) => break, - Err(e) => { - warn!("WebSocket error: {}", e); - break; - } - _ => {} - } - } - - info!("WebSocket connection closed"); - } - - async fn health_handler() -> &'static str { - "OK" - } - EOF - register: chat_main_result - failed_when: false - - - name: Build chat application - command: | - incus exec {{ chat_container }} -- bash -c "cd /opt/veza-chat && source /root/.cargo/env && cargo build --release" - register: chat_build_result - failed_when: false - - - name: Create chat systemd service - command: | - incus exec {{ chat_container }} -- tee /etc/systemd/system/veza-chat.service << 'EOF' - [Unit] - Description=Veza V5 Ultra Chat Server - After=network.target - - [Service] - Type=simple - User=root - WorkingDirectory=/opt/veza-chat - ExecStart=/opt/veza-chat/target/release/veza-chat - Restart=always - RestartSec=5 - Environment=SQLX_OFFLINE=true - - [Install] - WantedBy=multi-user.target - EOF - register: chat_service_result - failed_when: false - - - name: Start chat service - command: | - incus exec {{ chat_container }} -- systemctl daemon-reload - incus exec {{ chat_container }} -- systemctl enable veza-chat - incus exec {{ chat_container }} -- systemctl start veza-chat - register: chat_start_result - failed_when: false - - - name: Check chat service status - command: | - incus exec {{ chat_container }} -- systemctl status veza-chat - register: chat_status - failed_when: false - - - name: Display chat status - debug: - var: chat_status.stdout_lines - - rescue: - - name: Chat deployment failed - debug: - msg: "Chat deployment failed, continuing with other services" - - - name: Deploy Rust Stream Server - block: - - name: Install Rust in stream container - command: | - incus exec {{ stream_container }} -- apt update - incus exec {{ stream_container }} -- apt install -y curl git - incus exec {{ stream_container }} -- curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y - register: stream_rust_install_result - failed_when: false - - - name: Create stream application directory - command: | - incus exec {{ stream_container }} -- mkdir -p /opt/veza-stream - register: stream_dir_result - failed_when: false - - - name: Copy stream source code (placeholder) - command: | - incus exec {{ stream_container }} -- tee /opt/veza-stream/Cargo.toml << 'EOF' - [package] - name = "veza-stream" - version = "0.1.0" - edition = "2021" - - [dependencies] - tokio = { version = "1.0", features = ["full"] } - axum = "0.7" - tower = "0.4" - tower-http = { version = "0.5", features = ["cors", "fs"] } - serde = { version = "1.0", features = ["derive"] } - serde_json = "1.0" - tracing = "0.1" - tracing-subscriber = "0.3" - EOF - register: stream_cargo_result - failed_when: false - - - name: Create stream main.rs - command: | - incus exec {{ stream_container }} -- tee /opt/veza-stream/src/main.rs << 'EOF' - use axum::{ - extract::Path, - http::StatusCode, - response::Response, - routing::get, - Router, - }; - use std::net::SocketAddr; - use tokio::net::TcpListener; - use tracing::{info, warn}; - - #[tokio::main] - async fn main() { - tracing_subscriber::init(); - - let app = Router::new() - .route("/stream/health", get(health_handler)) - .route("/stream/:file", get(stream_handler)); - - let addr = SocketAddr::from(([0, 0, 0, 0], 8082)); - info!("Stream server starting on {}", addr); - - let listener = TcpListener::bind(addr).await.unwrap(); - axum::serve(listener, app).await.unwrap(); - } - - async fn health_handler() -> &'static str { - "OK" - } - - async fn stream_handler(Path(file): Path) -> Result { - info!("Stream request for: {}", file); - - // Simple file serving for now - if file.ends_with(".m3u8") { - Ok(Response::builder() - .status(200) - .header("Content-Type", "application/vnd.apple.mpegurl") - .body(format!("#EXTM3U\n#EXT-X-VERSION:3\n#EXT-X-TARGETDURATION:10\n#EXTINF:10.0,\n{}.ts\n#EXT-X-ENDLIST\n", file.replace(".m3u8", ""))) - .unwrap()) - } else { - Err(StatusCode::NOT_FOUND) - } - } - EOF - register: stream_main_result - failed_when: false - - - name: Build stream application - command: | - incus exec {{ stream_container }} -- bash -c "cd /opt/veza-stream && source /root/.cargo/env && cargo build --release" - register: stream_build_result - failed_when: false - - - name: Create stream systemd service - command: | - incus exec {{ stream_container }} -- tee /etc/systemd/system/veza-stream.service << 'EOF' - [Unit] - Description=Veza V5 Ultra Stream Server - After=network.target - - [Service] - Type=simple - User=root - WorkingDirectory=/opt/veza-stream - ExecStart=/opt/veza-stream/target/release/veza-stream - Restart=always - RestartSec=5 - - [Install] - WantedBy=multi-user.target - EOF - register: stream_service_result - failed_when: false - - - name: Start stream service - command: | - incus exec {{ stream_container }} -- systemctl daemon-reload - incus exec {{ stream_container }} -- systemctl enable veza-stream - incus exec {{ stream_container }} -- systemctl start veza-stream - register: stream_start_result - failed_when: false - - - name: Check stream service status - command: | - incus exec {{ stream_container }} -- systemctl status veza-stream - register: stream_status - failed_when: false - - - name: Display stream status - debug: - var: stream_status.stdout_lines - - rescue: - - name: Stream deployment failed - debug: - msg: "Stream deployment failed, continuing with web service" - - - name: Deploy React Web Application - block: - - name: Install Node.js in web container - command: | - incus exec {{ web_container }} -- apt update - incus exec {{ web_container }} -- apt install -y curl - incus exec {{ web_container }} -- curl -fsSL https://deb.nodesource.com/setup_18.x | bash - - incus exec {{ web_container }} -- apt install -y nodejs nginx - register: node_install_result - failed_when: false - - - name: Display Node.js installation result - debug: - var: node_install_result.stdout_lines - - - name: Create web application directory - command: | - incus exec {{ web_container }} -- mkdir -p /opt/veza-web - register: web_dir_result - failed_when: false - - - name: Create simple React app (placeholder) - command: | - incus exec {{ web_container }} -- tee /opt/veza-web/package.json << 'EOF' - { - "name": "veza-web", - "version": "1.0.0", - "description": "Veza V5 Ultra Web Application", - "main": "index.js", - "scripts": { - "start": "node server.js", - "build": "echo 'Build completed'" - }, - "dependencies": { - "express": "^4.18.2" - } - } - EOF - register: web_package_result - failed_when: false - - - name: Create simple web server - command: | - incus exec {{ web_container }} -- tee /opt/veza-web/server.js << 'EOF' - const express = require('express'); - const app = express(); - const port = process.env.PORT || 3000; - - app.use(express.static('public')); - - app.get('/', (req, res) => { - res.send(` - - - - Veza V5 Ultra - - - -
-
-

🎵 Veza V5 Ultra

-

Collaborative Audio Streaming Platform

-
-
-
✅ System Online
-

Services Status

-
    -
  • Backend API: Checking...
  • -
  • Chat WebSocket: Checking...
  • -
  • Stream HLS: Checking...
  • -
-

Features

-
    -
  • Real-time collaborative audio streaming
  • -
  • WebSocket chat integration
  • -
  • HLS video streaming
  • -
  • Modern React frontend
  • -
-
-
- - - - `); - }); - - app.listen(port, '0.0.0.0', () => { - console.log(`Veza V5 Ultra web server running on port ${port}`); - }); - EOF - register: web_server_result - failed_when: false - - - name: Install web dependencies - command: | - incus exec {{ web_container }} -- bash -c "cd /opt/veza-web && npm install" - register: web_install_result - failed_when: false - - - name: Create web systemd service - command: | - incus exec {{ web_container }} -- tee /etc/systemd/system/veza-web.service << 'EOF' - [Unit] - Description=Veza V5 Ultra Web Application - After=network.target - - [Service] - Type=simple - User=root - WorkingDirectory=/opt/veza-web - ExecStart=/usr/bin/node server.js - Restart=always - RestartSec=5 - Environment=PORT=3000 - - [Install] - WantedBy=multi-user.target - EOF - register: web_service_result - failed_when: false - - - name: Start web service - command: | - incus exec {{ web_container }} -- systemctl daemon-reload - incus exec {{ web_container }} -- systemctl enable veza-web - incus exec {{ web_container }} -- systemctl start veza-web - register: web_start_result - failed_when: false - - - name: Check web service status - command: | - incus exec {{ web_container }} -- systemctl status veza-web - register: web_status - failed_when: false - - - name: Display web status - debug: - var: web_status.stdout_lines - - rescue: - - name: Web deployment failed - debug: - msg: "Web deployment failed" - - post_tasks: - - name: Show all running services - command: | - incus exec {{ backend_container }} -- systemctl list-units --type=service --state=running | grep veza || true - incus exec {{ chat_container }} -- systemctl list-units --type=service --state=running | grep veza || true - incus exec {{ stream_container }} -- systemctl list-units --type=service --state=running | grep veza || true - incus exec {{ web_container }} -- systemctl list-units --type=service --state=running | grep veza || true - register: all_services - failed_when: false - - - name: Display all services - debug: - var: all_services.stdout_lines \ No newline at end of file diff --git a/ansible/playbooks/41-deploy-backend.yml b/ansible/playbooks/41-deploy-backend.yml deleted file mode 100644 index 5062b4987..000000000 --- a/ansible/playbooks/41-deploy-backend.yml +++ /dev/null @@ -1,88 +0,0 @@ ---- -- name: Déployer Backend Go - hosts: edge - become: true - - tasks: - - name: Installer Go et dépendances - command: | - incus exec veza-backend -- bash -c 'apt update && apt install -y wget git build-essential' - - - name: Télécharger et installer Go - command: | - incus exec veza-backend -- bash -c ' - cd /tmp - wget https://go.dev/dl/go1.21.5.linux-amd64.tar.gz - tar -C /usr/local -xzf go1.21.5.linux-amd64.tar.gz - echo "export PATH=\$PATH:/usr/local/go/bin" >> /root/.bashrc - ' - - - name: Créer l'application Backend - command: | - incus exec veza-backend -- bash -c 'cat > /opt/backend.go << EOF - package main - - import ( - "encoding/json" - "log" - "net/http" - "os" - ) - - func main() { - port := os.Getenv("PORT") - if port == "" { port = "8080" } - - http.HandleFunc("/api/health", func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(map[string]string{ - "status": "ok", - "service": "veza-backend", - "version": "1.0.0", - }) - }) - - http.HandleFunc("/api/", func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(map[string]string{ - "message": "Veza V5 Ultra Backend API", - "version": "1.0.0", - "endpoint": r.URL.Path, - }) - }) - - log.Printf("Backend starting on :%s", port) - http.ListenAndServe(":"+port, nil) - } - EOF' - - - name: Compiler le backend - command: | - incus exec veza-backend -- bash -c ' - cd /opt - /usr/local/go/bin/go mod init veza-backend - /usr/local/go/bin/go build -o veza-backend backend.go - ' - - - name: Créer le service systemd - command: | - incus exec veza-backend -- bash -c 'cat > /etc/systemd/system/veza-backend.service << EOF - [Unit] - Description=Veza Backend API - After=network.target - - [Service] - Type=simple - ExecStart=/opt/veza-backend - Restart=always - Environment=PORT=8080 - - [Install] - WantedBy=multi-user.target - EOF' - - - name: Démarrer le service - command: | - incus exec veza-backend -- systemctl daemon-reload - incus exec veza-backend -- systemctl enable veza-backend - incus exec veza-backend -- systemctl start veza-backend diff --git a/ansible/playbooks/42-deploy-web.yml b/ansible/playbooks/42-deploy-web.yml deleted file mode 100644 index 7f486ec98..000000000 --- a/ansible/playbooks/42-deploy-web.yml +++ /dev/null @@ -1,169 +0,0 @@ ---- -- name: Déployer Frontend Web - hosts: edge - become: true - - tasks: - - name: Installer Node.js et nginx - command: | - incus exec veza-web -- bash -c 'apt update && apt install -y curl nginx' - - - name: Installer Node.js 18 - command: | - incus exec veza-web -- bash -c ' - curl -fsSL https://deb.nodesource.com/setup_18.x | bash - - apt install -y nodejs - ' - - - name: Créer l'application web - command: | - incus exec veza-web -- bash -c 'cat > /var/www/html/index.html << EOF - - - - - - Veza V5 Ultra - - - -
-

🎵 Veza V5 Ultra

-
Plateforme Audio Collaborative
-
✅ Système en Ligne
- -
-
- Backend API - Vérification... -
-
- Chat WebSocket - Vérification... -
-
- Stream HLS - Vérification... -
-
-
- - - - - EOF' - - - name: Configurer nginx - command: | - incus exec veza-web -- bash -c 'cat > /etc/nginx/sites-available/default << EOF - server { - listen 3000 default_server; - root /var/www/html; - index index.html; - - location / { - try_files \$uri \$uri/ =404; - } - - location /api/ { - proxy_pass http://10.20.0.101:8080; - proxy_set_header Host \$host; - proxy_set_header X-Real-IP \$remote_addr; - proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto \$scheme; - } - } - EOF' - - - name: Redémarrer nginx - command: | - incus exec veza-web -- systemctl restart nginx diff --git a/ansible/playbooks/50-smoke-tests.yml b/ansible/playbooks/50-smoke-tests.yml deleted file mode 100644 index a8b41b85e..000000000 --- a/ansible/playbooks/50-smoke-tests.yml +++ /dev/null @@ -1,400 +0,0 @@ ---- -# Comprehensive smoke tests for Veza V5 Ultra deployment -# Validates all services and endpoints - -- name: Run smoke tests for Veza V5 Ultra - hosts: edge - become: false - gather_facts: true - - vars: - domain: "{{ domain | default('veza.talas.fr') }}" - haproxy_container: "veza-haproxy" - backend_container: "veza-backend" - chat_container: "veza-chat" - stream_container: "veza-stream" - web_container: "veza-web" - - tasks: - - name: Test container connectivity - block: - - name: Check if containers are running - command: incus list --format=json - register: containers_status - failed_when: false - - - name: Display container status - debug: - msg: "Container {{ item.name }}: {{ 'Running' if item.status == 'Running' else item.status }}" - loop: "{{ containers_status.stdout | from_json }}" - - - name: Verify all required containers are running - assert: - that: - - containers_status.stdout | from_json | selectattr('name', 'in', [haproxy_container, backend_container, chat_container, stream_container, web_container]) | selectattr('status', 'equalto', 'Running') | list | length == 5 - fail_msg: "Not all required containers are running" - success_msg: "All required containers are running" - - rescue: - - name: Container connectivity test failed - debug: - msg: "Container connectivity test failed, continuing with other tests" - - - name: Test HAProxy service - block: - - name: Check HAProxy service status - command: | - incus exec {{ haproxy_container }} -- systemctl is-active haproxy - register: haproxy_active - failed_when: false - - - name: Display HAProxy status - debug: - msg: "HAProxy service: {{ haproxy_active.stdout }}" - - - name: Test HAProxy configuration - command: | - incus exec {{ haproxy_container }} -- haproxy -c -f /etc/haproxy/haproxy.cfg - register: haproxy_config_test - failed_when: false - - - name: Display HAProxy config test - debug: - var: haproxy_config_test.stdout_lines - - - name: Check HAProxy statistics - command: | - incus exec {{ haproxy_container }} -- curl -s http://localhost:8404/stats | head -10 - register: haproxy_stats - failed_when: false - - - name: Display HAProxy statistics - debug: - var: haproxy_stats.stdout_lines - - rescue: - - name: HAProxy test failed - debug: - msg: "HAProxy test failed, continuing with other tests" - - - name: Test Backend API service - block: - - name: Check backend service status - command: | - incus exec {{ backend_container }} -- systemctl is-active veza-backend - register: backend_active - failed_when: false - - - name: Display backend status - debug: - msg: "Backend service: {{ backend_active.stdout }}" - - - name: Test backend health endpoint - command: | - incus exec {{ backend_container }} -- curl -s http://localhost:8080/api/health - register: backend_health - failed_when: false - - - name: Display backend health response - debug: - var: backend_health.stdout_lines - - - name: Test backend API endpoint - command: | - incus exec {{ backend_container }} -- curl -s http://localhost:8080/api/ - register: backend_api - failed_when: false - - - name: Display backend API response - debug: - var: backend_api.stdout_lines - - - name: Verify backend responses - assert: - that: - - backend_health.stdout | from_json | selectattr('status', 'equalto', 'ok') | list | length > 0 - - backend_api.stdout | from_json | selectattr('message', 'defined') | list | length > 0 - fail_msg: "Backend API responses are invalid" - success_msg: "Backend API is responding correctly" - - rescue: - - name: Backend test failed - debug: - msg: "Backend test failed, continuing with other tests" - - - name: Test Chat WebSocket service - block: - - name: Check chat service status - command: | - incus exec {{ chat_container }} -- systemctl is-active veza-chat - register: chat_active - failed_when: false - - - name: Display chat status - debug: - msg: "Chat service: {{ chat_active.stdout }}" - - - name: Test chat health endpoint - command: | - incus exec {{ chat_container }} -- curl -s http://localhost:8081/health - register: chat_health - failed_when: false - - - name: Display chat health response - debug: - var: chat_health.stdout_lines - - - name: Test WebSocket connection (basic) - command: | - incus exec {{ chat_container }} -- timeout 5 bash -c 'echo "test message" | nc localhost 8081' || true - register: websocket_test - failed_when: false - - - name: Display WebSocket test result - debug: - var: websocket_test.stdout_lines - - rescue: - - name: Chat test failed - debug: - msg: "Chat test failed, continuing with other tests" - - - name: Test Stream HLS service - block: - - name: Check stream service status - command: | - incus exec {{ stream_container }} -- systemctl is-active veza-stream - register: stream_active - failed_when: false - - - name: Display stream status - debug: - msg: "Stream service: {{ stream_active.stdout }}" - - - name: Test stream health endpoint - command: | - incus exec {{ stream_container }} -- curl -s http://localhost:8082/stream/health - register: stream_health - failed_when: false - - - name: Display stream health response - debug: - var: stream_health.stdout_lines - - - name: Test HLS endpoint - command: | - incus exec {{ stream_container }} -- curl -s http://localhost:8082/stream/test.m3u8 - register: hls_test - failed_when: false - - - name: Display HLS test response - debug: - var: hls_test.stdout_lines - - - name: Verify HLS response - assert: - that: - - hls_test.stdout is search('EXTM3U') - fail_msg: "HLS endpoint is not returning valid M3U8 content" - success_msg: "HLS endpoint is working correctly" - - rescue: - - name: Stream test failed - debug: - msg: "Stream test failed, continuing with other tests" - - - name: Test Web application - block: - - name: Check web service status - command: | - incus exec {{ web_container }} -- systemctl is-active veza-web - register: web_active - failed_when: false - - - name: Display web status - debug: - msg: "Web service: {{ web_active.stdout }}" - - - name: Test web application - command: | - incus exec {{ web_container }} -- curl -s http://localhost:3000/ - register: web_test - failed_when: false - - - name: Display web test response - debug: - msg: "Web response length: {{ web_test.stdout | length }} characters" - - - name: Verify web response - assert: - that: - - web_test.stdout is search('Veza V5 Ultra') - fail_msg: "Web application is not returning expected content" - success_msg: "Web application is working correctly" - - rescue: - - name: Web test failed - debug: - msg: "Web test failed, continuing with other tests" - - - name: Test external access through HAProxy - block: - - name: Test HTTP redirect - uri: - url: "http://{{ domain }}" - method: GET - follow_redirects: none - status_code: 301 - register: http_redirect - failed_when: false - - - name: Display HTTP redirect result - debug: - msg: "HTTP redirect: {{ 'Working' if http_redirect.status == 301 else 'Failed' }}" - - - name: Test HTTPS access (if certificate available) - uri: - url: "https://{{ domain }}" - method: GET - validate_certs: false - status_code: 200 - register: https_test - failed_when: false - - - name: Display HTTPS test result - debug: - msg: "HTTPS access: {{ 'Working' if https_test.status == 200 else 'Failed or certificate not available' }}" - - - name: Test API through HAProxy - uri: - url: "https://{{ domain }}/api/health" - method: GET - validate_certs: false - status_code: 200 - register: api_proxy_test - failed_when: false - - - name: Display API proxy test result - debug: - msg: "API through HAProxy: {{ 'Working' if api_proxy_test.status == 200 else 'Failed' }}" - - rescue: - - name: External access test failed - debug: - msg: "External access test failed (expected if DNS not configured)" - - - name: Test network connectivity between containers - block: - - name: Test backend connectivity from web container - command: | - incus exec {{ web_container }} -- curl -s http://10.10.0.101:8080/api/health - register: backend_connectivity - failed_when: false - - - name: Display backend connectivity - debug: - msg: "Backend connectivity from web: {{ 'Working' if backend_connectivity.rc == 0 else 'Failed' }}" - - - name: Test chat connectivity from web container - command: | - incus exec {{ web_container }} -- curl -s http://10.10.0.102:8081/health - register: chat_connectivity - failed_when: false - - - name: Display chat connectivity - debug: - msg: "Chat connectivity from web: {{ 'Working' if chat_connectivity.rc == 0 else 'Failed' }}" - - - name: Test stream connectivity from web container - command: | - incus exec {{ web_container }} -- curl -s http://10.10.0.103:8082/stream/health - register: stream_connectivity - failed_when: false - - - name: Display stream connectivity - debug: - msg: "Stream connectivity from web: {{ 'Working' if stream_connectivity.rc == 0 else 'Failed' }}" - - rescue: - - name: Network connectivity test failed - debug: - msg: "Network connectivity test failed" - - - name: Performance and resource checks - block: - - name: Check container resource usage - command: | - incus exec {{ haproxy_container }} -- free -h - incus exec {{ backend_container }} -- free -h - incus exec {{ chat_container }} -- free -h - incus exec {{ stream_container }} -- free -h - incus exec {{ web_container }} -- free -h - register: resource_usage - failed_when: false - - - name: Display resource usage - debug: - var: resource_usage.stdout_lines - - - name: Check disk usage - command: | - incus exec {{ haproxy_container }} -- df -h - incus exec {{ backend_container }} -- df -h - incus exec {{ chat_container }} -- df -h - incus exec {{ stream_container }} -- df -h - incus exec {{ web_container }} -- df -h - register: disk_usage - failed_when: false - - - name: Display disk usage - debug: - var: disk_usage.stdout_lines - - rescue: - - name: Performance check failed - debug: - msg: "Performance check failed" - - post_tasks: - - name: Generate smoke test summary - debug: - msg: | - ======================================== - Veza V5 Ultra Smoke Test Summary - ======================================== - - Tests completed: - - Container connectivity: {{ 'PASS' if containers_status is defined and containers_status.rc == 0 else 'FAIL' }} - - HAProxy service: {{ 'PASS' if haproxy_active is defined and haproxy_active.stdout == 'active' else 'FAIL' }} - - Backend API: {{ 'PASS' if backend_health is defined and backend_health.rc == 0 else 'FAIL' }} - - Chat WebSocket: {{ 'PASS' if chat_health is defined and chat_health.rc == 0 else 'FAIL' }} - - Stream HLS: {{ 'PASS' if stream_health is defined and stream_health.rc == 0 else 'FAIL' }} - - Web application: {{ 'PASS' if web_test is defined and web_test.rc == 0 else 'FAIL' }} - - External access: {{ 'PASS' if https_test is defined and https_test.status == 200 else 'FAIL (expected if DNS not configured)' }} - - Next steps: - 1. Configure DNS A record for {{ domain }} to point to this host - 2. Re-run HAProxy playbook to get Let's Encrypt certificate - 3. Re-run smoke tests to verify HTTPS access - 4. Monitor application logs for any issues - - ======================================== - - - name: Show container logs (last 10 lines each) - command: | - echo "=== HAProxy Logs ===" - incus exec {{ haproxy_container }} -- journalctl -u haproxy --no-pager -n 10 || true - echo "=== Backend Logs ===" - incus exec {{ backend_container }} -- journalctl -u veza-backend --no-pager -n 10 || true - echo "=== Chat Logs ===" - incus exec {{ chat_container }} -- journalctl -u veza-chat --no-pager -n 10 || true - echo "=== Stream Logs ===" - incus exec {{ stream_container }} -- journalctl -u veza-stream --no-pager -n 10 || true - echo "=== Web Logs ===" - incus exec {{ web_container }} -- journalctl -u veza-web --no-pager -n 10 || true - register: container_logs - failed_when: false - - - name: Display container logs - debug: - var: container_logs.stdout_lines diff --git a/ansible/playbooks/50-smoke.yml b/ansible/playbooks/50-smoke.yml deleted file mode 100644 index c22c10651..000000000 --- a/ansible/playbooks/50-smoke.yml +++ /dev/null @@ -1,276 +0,0 @@ ---- -# Smoke tests for Veza V5 Ultra deployment -# Validates all services are running and accessible - -- name: Run smoke tests for Veza deployment - hosts: edge - become: true - gather_facts: true - - vars: - test_timeout: 30 - retry_count: 5 - retry_delay: 10 - - tasks: - - name: Wait for all containers to be ready - wait_for: - timeout: "{{ test_timeout }}" - delegate_to: localhost - - - name: Check container status - command: incus list --format json - register: container_status - failed_when: false - - - name: Display container status - debug: - var: container_status.stdout - when: container_status.stdout is defined - - - name: Test HAProxy container is running - command: | - incus exec veza-haproxy -- systemctl is-active haproxy - register: haproxy_status - failed_when: false - - - name: Test backend container is running - command: | - incus exec veza-backend -- systemctl is-active veza-backend - register: backend_status - failed_when: false - - - name: Test chat container is running - command: | - incus exec veza-chat -- systemctl is-active veza-chat - register: chat_status - failed_when: false - - - name: Test stream container is running - command: | - incus exec veza-stream -- systemctl is-active veza-stream - register: stream_status - failed_when: false - - - name: Test web container is running - command: | - incus exec veza-web -- systemctl is-active nginx - register: web_status - failed_when: false - - - name: Display service status - debug: - msg: | - HAProxy: {{ haproxy_status.stdout }} - Backend: {{ backend_status.stdout }} - Chat: {{ chat_status.stdout }} - Stream: {{ stream_status.stdout }} - Web: {{ web_status.stdout }} - - - name: Test internal connectivity between containers - command: | - incus exec veza-backend -- curl -f http://veza-web:{{ veza_web_port }}/ || echo "Web container not reachable" - register: internal_web_test - failed_when: false - - - name: Test internal API connectivity - command: | - incus exec veza-web -- curl -f http://veza-backend:{{ veza_backend_port }}/health || echo "Backend API not reachable" - register: internal_api_test - failed_when: false - - - name: Test internal WebSocket connectivity - command: | - incus exec veza-web -- curl -f http://veza-chat:{{ veza_chat_port }}/ || echo "Chat server not reachable" - register: internal_ws_test - failed_when: false - - - name: Test internal stream connectivity - command: | - incus exec veza-web -- curl -f http://veza-stream:{{ veza_stream_port }}/ || echo "Stream server not reachable" - register: internal_stream_test - failed_when: false - - - name: Display internal connectivity test results - debug: - msg: | - Internal Web: {{ internal_web_test.stdout }} - Internal API: {{ internal_api_test.stdout }} - Internal WS: {{ internal_ws_test.stdout }} - Internal Stream: {{ internal_stream_test.stdout }} - - - name: Test external HTTP access (port 80) - uri: - url: "http://{{ ansible_host }}:80/" - method: GET - status_code: [200, 301, 302] - timeout: "{{ test_timeout }}" - register: http_test - delegate_to: localhost - retries: "{{ retry_count }}" - delay: "{{ retry_delay }}" - failed_when: false - - - name: Test external HTTPS access (port 443) - uri: - url: "https://{{ ansible_host }}:443/" - method: GET - status_code: [200, 301, 302] - timeout: "{{ test_timeout }}" - validate_certs: false - register: https_test - delegate_to: localhost - retries: "{{ retry_count }}" - delay: "{{ retry_delay }}" - failed_when: false - - - name: Test API endpoint - uri: - url: "https://{{ ansible_host }}:443/api/health" - method: GET - status_code: [200, 404, 500] # 404/500 might be expected if health endpoint not implemented - timeout: "{{ test_timeout }}" - validate_certs: false - register: api_test - delegate_to: localhost - retries: "{{ retry_count }}" - delay: "{{ retry_delay }}" - failed_when: false - - - name: Test WebSocket endpoint (basic connectivity) - uri: - url: "https://{{ ansible_host }}:443/ws" - method: GET - status_code: [101, 200, 400, 404] # 101 for successful WS upgrade - timeout: "{{ test_timeout }}" - validate_certs: false - register: ws_test - delegate_to: localhost - retries: "{{ retry_count }}" - delay: "{{ retry_delay }}" - failed_when: false - - - name: Test stream endpoint - uri: - url: "https://{{ ansible_host }}:443/stream/" - method: GET - status_code: [200, 404, 500] # 404/500 might be expected if no content - timeout: "{{ test_timeout }}" - validate_certs: false - register: stream_test - delegate_to: localhost - retries: "{{ retry_count }}" - delay: "{{ retry_delay }}" - failed_when: false - - - name: Display external test results - debug: - msg: | - HTTP (port 80): {{ http_test.status }} - {{ http_test.msg }} - HTTPS (port 443): {{ https_test.status }} - {{ https_test.msg }} - API (/api/health): {{ api_test.status }} - {{ api_test.msg }} - WebSocket (/ws): {{ ws_test.status }} - {{ ws_test.msg }} - Stream (/stream/): {{ stream_test.status }} - {{ stream_test.msg }} - - - name: Test HAProxy configuration - command: | - incus exec veza-haproxy -- haproxy -c -f /etc/haproxy/haproxy.cfg - register: haproxy_config_test - failed_when: false - - - name: Display HAProxy config test result - debug: - var: haproxy_config_test.stdout_lines - when: haproxy_config_test.stdout_lines is defined - - - name: Check HAProxy logs for errors - command: | - incus exec veza-haproxy -- journalctl -u haproxy --no-pager -n 20 - register: haproxy_logs - failed_when: false - - - name: Display HAProxy logs - debug: - var: haproxy_logs.stdout_lines - when: haproxy_logs.stdout_lines is defined - - - name: Check application logs - command: | - incus exec {{ item.name }} -- journalctl -u {{ item.service }} --no-pager -n 10 - register: app_logs - failed_when: false - loop: - - { name: "veza-backend", service: "veza-backend" } - - { name: "veza-chat", service: "veza-chat" } - - { name: "veza-stream", service: "veza-stream" } - - { name: "veza-web", service: "nginx" } - - - name: Display application logs - debug: - var: app_logs.results - - - name: Test port accessibility - wait_for: - port: "{{ item }}" - host: "{{ ansible_host }}" - timeout: 10 - register: port_test - delegate_to: localhost - failed_when: false - loop: - - 80 - - 443 - - - name: Display port test results - debug: - var: port_test.results - - - name: Final deployment summary - debug: - msg: | - ======================================== - Veza V5 Ultra Deployment Summary - ======================================== - Host: {{ ansible_host }} - Domain: {{ domain }} - - Container Status: - - HAProxy: {{ haproxy_status.stdout }} - - Backend: {{ backend_status.stdout }} - - Chat: {{ chat_status.stdout }} - - Stream: {{ stream_status.stdout }} - - Web: {{ web_status.stdout }} - - External Access: - - HTTP: {{ http_test.status }} - - HTTPS: {{ https_test.status }} - - API: {{ api_test.status }} - - WebSocket: {{ ws_test.status }} - - Stream: {{ stream_test.status }} - - Next Steps: - 1. Point DNS A record for {{ domain }} to {{ ansible_host }} - 2. Re-run playbook 30-haproxy-in-container.yml to get Let's Encrypt cert - 3. Test full functionality with real domain - ======================================== - - handlers: - - name: restart haproxy - command: | - incus exec veza-haproxy -- systemctl reload haproxy - - - name: restart backend - command: | - incus exec veza-backend -- systemctl restart veza-backend - - - name: restart chat - command: | - incus exec veza-chat -- systemctl restart veza-chat - - - name: restart stream - command: | - incus exec veza-stream -- systemctl restart veza-stream - - - name: restart web - command: | - incus exec veza-web -- systemctl restart nginx diff --git a/ansible/playbooks/crontab.yml b/ansible/playbooks/crontab.yml deleted file mode 100644 index 5bfd2dc5b..000000000 --- a/ansible/playbooks/crontab.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -# file: crontab.yml -- hosts: crontab - roles: - - crontab diff --git a/ansible/playbooks/docker.yml b/ansible/playbooks/docker.yml deleted file mode 100644 index a904219c6..000000000 --- a/ansible/playbooks/docker.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -# file: docker.yml -- hosts: - - docker - roles: - - docker diff --git a/ansible/playbooks/elasticsearch.yml b/ansible/playbooks/elasticsearch.yml deleted file mode 100644 index 663a4b1ac..000000000 --- a/ansible/playbooks/elasticsearch.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -# file: elasticsearch.yml - -- hosts: elasticsearch - roles: - - elasticsearch diff --git a/ansible/playbooks/element-web.yml b/ansible/playbooks/element-web.yml deleted file mode 100644 index bfed8630a..000000000 --- a/ansible/playbooks/element-web.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -# file: element-web.yml -- hosts: element-web - roles: - - element-web diff --git a/ansible/playbooks/filebeat.yml b/ansible/playbooks/filebeat.yml deleted file mode 100644 index 39929932a..000000000 --- a/ansible/playbooks/filebeat.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -# file: filebeat.yml - -- hosts: all !veza-stats - roles: - - { role: filebeat, when: ansible_os_family == "Debian" and ansible_service_mgr == "systemd" and (filebeat_install is not defined or filebeat_install)} diff --git a/ansible/playbooks/gerrit.yml b/ansible/playbooks/gerrit.yml deleted file mode 100644 index 427f272b5..000000000 --- a/ansible/playbooks/gerrit.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -# file: gerrit.yml -- hosts: gerrit - roles: - - gerrit diff --git a/ansible/playbooks/git_generic_deploy_files.yml b/ansible/playbooks/git_generic_deploy_files.yml deleted file mode 100644 index 8ab2c8362..000000000 --- a/ansible/playbooks/git_generic_deploy_files.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -# file: git_generic_deploy_files.yml -- hosts: git_generic_deploy_files - roles: - - git_generic_deploy_files diff --git a/ansible/playbooks/haproxy.yml b/ansible/playbooks/haproxy.yml deleted file mode 100644 index f1bd890fa..000000000 --- a/ansible/playbooks/haproxy.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -# file: haproxy.yml -- hosts: haproxy - roles: - - haproxy - diff --git a/ansible/roles/auditd/files/ansible.rules b/ansible/roles/auditd/files/ansible.rules deleted file mode 100644 index 42636d88a..000000000 --- a/ansible/roles/auditd/files/ansible.rules +++ /dev/null @@ -1,26 +0,0 @@ -# Ansible managed - -# log executed commands on this server for admins (UID 10000 to 10999 inside containers) --a always,exit -F arch=b64 -S execve -F auid>=10000 -F auid<=10999 -k exec_metal_admin - -# log executed commands inside containers for admins (UID 10000 to 10999 inside containers) --a always,exit -F arch=b64 -S execve -F auid>=1010000 -F auid<=1010999 -k exec_container_admin - -# log executed commands inside containers for users (UID 12000 to 12999 inside containers) --a always,exit -F arch=b64 -S execve -F auid>=1012000 -F auid<=1012999 -k exec_container_user - -# Reduce the noise --a exclude,always -F msgtype=CRED_ACQ --a exclude,always -F msgtype=CRED_DISP --a exclude,always -F msgtype=CRED_REFR --a exclude,always -F msgtype=CWD --a exclude,always -F msgtype=PATH --a exclude,always -F msgtype=PROCTITLE --a exclude,always -F msgtype=SERVICE_START --a exclude,always -F msgtype=SERVICE_STOP --a exclude,always -F msgtype=SOCKADDR --a exclude,always -F msgtype=USER_ACCT --a exclude,always -F msgtype=USER_AUTH --a exclude,always -F msgtype=USER_END --a exclude,always -F msgtype=USER_START --a exclude,always -F auid=4294967295 diff --git a/ansible/roles/auditd/handlers/main.yml b/ansible/roles/auditd/handlers/main.yml deleted file mode 100644 index 8a2dc1ba5..000000000 --- a/ansible/roles/auditd/handlers/main.yml +++ /dev/null @@ -1,5 +0,0 @@ -# file: auditd/handlers/main.yml - -- name: "augenrules_load" - ansible.builtin.command: - cmd: /usr/sbin/augenrules --load diff --git a/ansible/roles/auditd/meta/main.yml b/ansible/roles/auditd/meta/main.yml deleted file mode 100644 index eed4b7475..000000000 --- a/ansible/roles/auditd/meta/main.yml +++ /dev/null @@ -1,7 +0,0 @@ ---- -# file: roles/auditd/meta/main.yml - -dependencies: - - role: zabbix_template_assignment - zabbix_template_assignment_systemd_service_list: - - auditd diff --git a/ansible/roles/auditd/readme.md b/ansible/roles/auditd/readme.md deleted file mode 100644 index c83699982..000000000 --- a/ansible/roles/auditd/readme.md +++ /dev/null @@ -1,92 +0,0 @@ -# Auditd - -This roles installs auditd and activate it with 3 differents logging tags that are described bellow: -1. exec_metal_admin -1. exec_container_admin -1. exec_container_user - -## 1. Logging Commands by Admins on the Host -```bash --a always,exit -F arch=b64 -S execve -F auid>=10000 -F auid<=10999 -k exec_metal_admin -``` - -- `-a always,exit`: Always log on syscall exit. -- `-F arch=b64`: Specifies the 64-bit architecture (`b64`). -- `-S execve`: Monitors the `execve` syscall, capturing all program executions. -- `-F auid>=10000 -F auid<=10999`: Filters logs for admin accounts with `auid` (Audit User ID) in the specified range, typically representing admin users on the host. -- `-k exec_metal_admin`: Tags logs with the key `exec_metal_admin` for easier log filtering. - -## 2. Logging Commands by Admins in Containers -```bash --a always,exit -F arch=b64 -S execve -F auid>=1010000 -F auid<=1010999 -k exec_container_admin -``` - -- Similar to the first rule but applied to container environments. -- The `auid` range (`1010000` to `1010999`) is intended for admin users within containers using ID mapping. - -## 3. Logging Commands by Non-Admin Users in Containers -```bash --a always,exit -F arch=b64 -S execve -F auid>=1012000 -F auid<=1012999 -k exec_container_user -``` - -- Captures commands by container user accounts with `auid` between `1012000` and `1012999`. -- Uses the key `exec_container_user` to differentiate these logs from admin activities. - ---- - -# Noise Reduction Rules - -The following rules exclude specific message types to reduce unnecessary log entries: - -```bash --a exclude,always -F msgtype=CRED_ACQ --a exclude,always -F msgtype=CRED_DISP --a exclude,always -F msgtype=CRED_REFR --a exclude,always -F msgtype=CWD --a exclude,always -F msgtype=PATH --a exclude,always -F msgtype=PROCTITLE --a exclude,always -F msgtype=SERVICE_START --a exclude,always -F msgtype=SERVICE_STOP --a exclude,always -F msgtype=SOCKADDR --a exclude,always -F msgtype=USER_ACCT --a exclude,always -F msgtype=USER_AUTH --a exclude,always -F msgtype=USER_END --a exclude,always -F msgtype=USER_START --a exclude,always -F auid=4294967295 -``` - -- `-a exclude,always`: Excludes specified message types from logs. -- `msgtype=CRED_ACQ`, `CRED_DISP`, `CRED_REFR`: Suppresses logs related to credential acquisition, disposal, and refresh. -- `msgtype=CWD`: Suppresses 'current working directory' logs. -- `msgtype=PATH`: Prevents detailed file path logs. -- `msgtype=PROCTITLE`: Avoids logging full commands with arguments. -- `msgtype=SERVICE_START/STOP`: Reduces noise by ignoring service start/stop events. -- `msgtype=USER_START`, `USER_ACCT`, `USER_AUTH`, `USER_END`: Filters out general user login/authentication events. -- `msgtype=SOCKADDR`: Omits network-related socket address logs. -- `-F auid=4294967295`: Excludes logs from system processes with an unset audit user ID. - ---- - -# Compliance and Validation - -- Ensures all executed commands by admins and specific container users are logged. -- Provides clear user attribution through `auid` filtering, meeting ISO 27001 requirements. -- Noise reduction rules enhance the log signal-to-noise ratio, focusing on relevant events. - -# Log Shipping -Filebeat is used to send the logs to Elasticsearch for easy access via Kibana. - -# Auditd useful commands - -Show current audit rules: -``` -auditctl -l -``` -Search logs by tags: -``` -ausearch -k exec_metal_admin -``` -Search by uid or uidnumber: -``` -ausearch -ua adm-senke -``` diff --git a/ansible/roles/auditd/tasks/main.yml b/ansible/roles/auditd/tasks/main.yml deleted file mode 100644 index 83e55c984..000000000 --- a/ansible/roles/auditd/tasks/main.yml +++ /dev/null @@ -1,14 +0,0 @@ ---- -# file: roles/auditd/tasks/main.yml - -- name: "shadow from global_shadow variables" - ansible.builtin.apt: - name: auditd - tags: auditd - -- name: "/etc/audit/rules.d/ansible.rules" - ansible.builtin.copy: - src: "ansible.rules" - dest: "/etc/audit/rules.d/ansible.rules" - notify: augenrules_load - tags: auditd diff --git a/ansible/roles/coraza/files/coraza-spoa.service b/ansible/roles/coraza/files/coraza-spoa.service deleted file mode 100644 index 60f584f35..000000000 --- a/ansible/roles/coraza/files/coraza-spoa.service +++ /dev/null @@ -1,87 +0,0 @@ -[Unit] -Description=Coraza WAF SPOA Daemon -Documentation=https://www.coraza.io - -[Service] -ExecStart=/usr/local/bin/coraza-spoa -config=/etc/coraza/config.yaml -WorkingDirectory=/ -Restart=always -Type=exec -User=coraza -Group=coraza - -# Hardening -# Controls which capabilities to include in the ambient capability set for the executed process. -AmbientCapabilities= -#Takes a mount propagation setting: shared, slave or private. -MountFlags=private - -# If true, kernel variables accessible through /proc/sys/, /sys/, /proc/sysrq-trigger, /proc/latency_stats, /proc/acpi, /proc/timer_stats, /proc/fs and /proc/irq will be made read-only and /proc/kallsyms as well as /proc/kcore will be inaccessible to all processes of the unit. -ProtectKernelTunables=yes -# If true, explicit module loading will be denied. -ProtectKernelModules=yes -# If true, access to the kernel log ring buffer will be denied. -ProtectKernelLogs=yes -# If true, the Linux Control Groups (cgroups(7)) hierarchies accessible through /sys/fs/cgroup/ will be made read-only to all processes of the unit. -ProtectControlGroups=yes -# when set to "noaccess" the ability to access most of other users' process metadata in /proc/ is taken away for processes of the service. -ProtectProc=noaccess -# If set, writes to the hardware clock or system clock will be denied. -ProtectClock=yes -# When set, sets up a new UTS namespace for the executed processes. In addition, changing hostname or domainname is prevented. -ProtectHostname=yes -# If set to "strict" the entire file system hierarchy is mounted read-only, except for the API file system subtrees /dev/, /proc/ and /sys/ -ProtectSystem=strict -# If set, any attempts to set the set-user-ID (SUID) or set-group-ID (SGID) bits on files or directories will be denied -RestrictSUIDSGID=true -# If set, any attempts to enable realtime scheduling in a process of the unit are refused. -RestrictRealtime=true -# Controls the secure bits set for the executed process. See man capabilities. -SecureBits=no-setuid-fixup-locked noroot-locked - -# frequently used repositories by other applicatons -InaccessiblePaths=-/opt -InaccessiblePaths=-/srv -# block all binary that are not usefull -InaccessiblePaths=-/bin -InaccessiblePaths=-/sbin - -# locks down the personality(2) system call so that the kernel execution domain may not be changed -LockPersonality=true -# set the logs directory path -LogsDirectory=coraza -# set the configuration directory path -ConfigurationDirectory=coraza - -# unsure taht the memory mapping is not editable. creation and alteration of memory segments to become writable or executable is not allowed -MemoryDenyWriteExecute=yes - -# ensures that the service process and all its children can never gain new privileges through execve() -NoNewPrivileges=true - -# the directories /home/, /root, and /run/user are made inaccessible and empty for processes invoked by this unit -ProtectHome=true -# sets up a new /dev/ mount for the executed processes and only adds API pseudo devices such as /dev/null, /dev/zero or /dev/random -PrivateDevices=true - -# sets up a new user namespace for the executed processes and configures a user and group mapping. -PrivateUsers=true -# a new file system namespace set up for executed processes, /tmp/ and /var/tmp/ inside are not shared with processes outside of the namespace, all temporary files removed after service stopped. -PrivateTmp=true -# all System V and POSIX IPC objects owned by the user and group the processes of this unit are run as are removed when the unit is stopped -RemoveIPC=true - -# Restricts the set of socket address families accessible to the processes of this unit. here ipv4 and ipv6 -RestrictAddressFamilies=AF_INET AF_INET6 - -SystemCallArchitectures=native -SystemCallFilter=@system-service -SystemCallFilter=-@setuid -@ipc -@mount - -IPAddressDeny=any -IPAddressAllow=localhost - -CapabilityBoundingSet=CAP_NET_BIND_SERVICE - -[Install] -WantedBy=multi-user.target diff --git a/ansible/roles/coraza/handlers/main.yml b/ansible/roles/coraza/handlers/main.yml deleted file mode 100644 index 00157b353..000000000 --- a/ansible/roles/coraza/handlers/main.yml +++ /dev/null @@ -1,7 +0,0 @@ ---- -# file: roles/coraza/handlers/main.yml - -- name: restart coraza - ansible.builtin.systemd: - name: coraza-spoa - state: restarted diff --git a/ansible/roles/coraza/meta/main.yml b/ansible/roles/coraza/meta/main.yml deleted file mode 100644 index 96e8e8ac9..000000000 --- a/ansible/roles/coraza/meta/main.yml +++ /dev/null @@ -1,14 +0,0 @@ ---- -# file: roles/coraza/meta/main.yml - -dependencies: - - role: git_generic_deploy_files - vars: - git_generic_deploy_files_list: - - repository_url: "https://github.com/corazawaf/coraza-spoa.git" - branch: "main" - deploy_directory: "/usr/local/src/coraza-spoa" - - repository_url: "https://github.com/coreruleset/coreruleset" - branch: "main" - deploy_directory: "/usr/local/src/coreruleset" - - role: go diff --git a/ansible/roles/coraza/readme.md b/ansible/roles/coraza/readme.md deleted file mode 100644 index 8cb4b6ce7..000000000 --- a/ansible/roles/coraza/readme.md +++ /dev/null @@ -1,59 +0,0 @@ -# Coraza role - -This role installs the Coraza WAF SPOA connector, an HTTP filtering layer that integrates the OWASP Core Rule Set (CRS) via HAProxy's SPOE mechanism. - -It is intended for production environments where applications require firewalling, and it supports tuning of security behavior through multiple paranoia levels and customizable directives. - - -* [Coraza role](#coraza-role) - * [Variable reference](#variable-reference) - * [Mandatory variables](#mandatory-variables) - * [Optional variables](#optional-variables) - * [Configuration](#configuration) - * [Usefull links](#usefull-links) - - -## Variable reference - -### Optional variables - -| Variable | Description | Type of variable | Default value | Other value | -|------------------------------------|--------------------------------------------------------------------|------------------|----------------------------------------------------|----------------------------------------------------| -| `coraza_spoa_transaction_ttl_ms` | Transaction lifetime in milliseconds | `integer` | `500` | `300`, `900`, `3000` | -| `coraza_directives` | Block of Coraza/ModSecurity directives to inject | `multiline` | _Default OWASP CRS directives block_ | `SecRuleEngine DetectionOnly`, custom directives | -| `coraza_sec_rule_engine` | Enables or disables Coraza traffic processing | `string` | `DetectionOnly` | `On`, `DetectionOnly`, `Off` | -| `coraza_paranoia_level` | OWASP CRS paranoia level: strictness & false positive sensitivity | `integer` | `1` | `1`, `2`, `3`, `4` | - -## Configuration - -By default, this role applies a moderate Coraza WAF configuration, using the lowest paranoia level and loading all available OWASP CRS rules and plugins: - -```yaml -SecAction "id:1000001,phase:1,pass,t:none,nolog,setvar:tx.blocking_paranoia_level=1 -Include /etc/coraza/coraza.conf -Include /etc/coraza/crs-setup.conf -Include /etc/coraza/plugins/*.conf -Include /etc/coraza/rules/*.conf -``` -This default setup is safe for most production environments, with minimal risk of blocking legitimate traffic. However, if your application requires stricter protections, you can adjust the behavior using the `coraza_paranoia_level` variable, which supports **4 levels of rule strictness**: - -* **1** - **Baseline** - Minimal false positives, safe for most applications. There should be no tuning needed. -* **2** - **Enhanced** - Rules that are adequate when real customer data is involved. Expect false positives, might require tuning. -* **3** - **Strict** - Online banking level security with many false positives, frequent tuning needed. -* **4** - **Aggressive** - Rules that are super aggressive. There will be a lot of false positives, lots of tuning needed (essential). - -If you choose a paranoia level higher than 1, be aware that false positives are more likely, potentially blocking legitimate traffic. In such cases, it is strongly advised to tune the WAF directives for your specific application by overriding the default rules with the `coraza_directives` variable. - -This allows you to include only selected rule sets or inject custom SecRule logic that satisfies your needs. - -You can check [what's in the rules](https://coreruleset.org/docs/3-about-rules/rules/) in OWASP CRS documentation. - -## Usefull links - -* [Coraza SPOA repository](https://github.com/corazawaf/coraza-spoa) -* [Coraza SPOA documentation](https://coraza.io/connectors/coraza-spoa/) -* [Coraza documentation](https://coraza.io/docs/tutorials/introduction/) -* [Coraza/ModSecurity directives ](https://coraza.io/docs/seclang/directives/) -* [OWASP CRS repository](https://github.com/coreruleset/coreruleset) -* [OWASP CRS documentation](https://owasp.org/www-project-modsecurity-core-rule-set/) -* [Working with paranoia levels](https://coreruleset.org/20211028/working-with-paranoia-levels/) diff --git a/ansible/roles/coraza/tasks/main.yml b/ansible/roles/coraza/tasks/main.yml deleted file mode 100644 index 7220d0a6f..000000000 --- a/ansible/roles/coraza/tasks/main.yml +++ /dev/null @@ -1,76 +0,0 @@ ---- -# file: roles/coraza/tasks/main.yml - -- name: "ensure coraza group exists" - ansible.builtin.group: - name: coraza - tags: coraza - -- name: "ensure coraza user exists" - ansible.builtin.user: - name: coraza - group: coraza - system: true - create_home: false - tags: coraza - -- name: "build coraza-spoa binary" - ansible.builtin.command: /usr/local/go/bin/go run mage.go build - args: - chdir: /usr/local/src/coraza-spoa - tags: coraza - -- name: "ensure main coraza directory exist" - ansible.builtin.file: - path: /etc/coraza - state: directory - tags: coraza - -- name: "ensure main coraza configuration files are present" - ansible.builtin.template: - src: "{{ item }}.j2" - dest: "/etc/coraza/{{ item }}" - notify: restart coraza - loop: - - config.yaml - - coraza.conf - tags: coraza - -- name: "ensure coraza binary is installed in /usr/local/bin" - ansible.builtin.copy: - src: /usr/local/src/coraza-spoa/build/coraza-spoa - dest: /usr/local/bin/coraza-spoa - remote_src: true - mode: 755 - tags: coraza - -- name: "ensure crs configuration file exists" - ansible.builtin.copy: - src: /usr/local/src/coreruleset/crs-setup.conf.example - dest: /etc/coraza/crs-setup.conf - remote_src: true - notify: restart coraza - tags: coraza - -- name: "ensure crs rules and plugins directories are present" - ansible.builtin.copy: - src: "/usr/local/src/coreruleset/{{ item }}" - dest: "/etc/coraza/{{ item }}" - remote_src: true - loop: - - rules - - plugins - tags: coraza - -- name: "ensure coraza spoa service systemd file exists" - ansible.builtin.copy: - src: coraza-spoa.service - dest: /etc/systemd/system/coraza-spoa.service - tags: coraza - -- name: "[always] coraza service started and enabled" - ansible.builtin.systemd_service: - name: coraza-spoa - state: started - enabled: true - tags: coraza diff --git a/ansible/roles/coraza/templates/config.yaml.j2 b/ansible/roles/coraza/templates/config.yaml.j2 deleted file mode 100644 index 16c43d37a..000000000 --- a/ansible/roles/coraza/templates/config.yaml.j2 +++ /dev/null @@ -1,37 +0,0 @@ -# {{ ansible_managed }} - -# The SPOA server bind address -bind: 127.0.0.1:9000 - -# The log level configuration, one of: debug/info/warn/error/panic/fatal -log_level: warn -# The log file path -log_file: /var/log/coraza/coraza.log -# The log format, one of: console/json -log_format: json - -applications: - - name: haproxy_waf - directives: | - SecAction "id:1000001,phase:1,pass,t:none,nolog,setvar:tx.blocking_paranoia_level={{ coraza_paranoia_level | default(1) }}" - Include /etc/coraza/coraza.conf - Include /etc/coraza/crs-setup.conf -{% if coraza_directives is defined %} -{{ coraza_directives | indent(6, true) }} -{% else %} - Include /etc/coraza/plugins/*.conf - Include /etc/coraza/rules/*.conf -{% endif %} - - # HAProxy configured to send requests only, that means no cache required - response_check: false - - # The transaction cache lifetime in milliseconds (60000ms = 60s) - transaction_ttl_ms: {{ coraza_spoa_transaction_ttl_ms | default(500) }} - - # The log level configuration, one of: debug/info/warn/error/panic/fatal - log_level: warn - # The log file path - log_file: /var/log/coraza/coraza.log - # The log format, one of: console/json - log_format: json diff --git a/ansible/roles/coraza/templates/coraza.conf.j2 b/ansible/roles/coraza/templates/coraza.conf.j2 deleted file mode 100644 index 88e816979..000000000 --- a/ansible/roles/coraza/templates/coraza.conf.j2 +++ /dev/null @@ -1,116 +0,0 @@ -# {{ ansible_managed }} - -# -- Rule engine initialization ---------------------------------------------- - -# Enable Coraza, attaching it to every transaction. Use detection -# only to start with, because that minimises the chances of post-installation -# disruption. -# -SecRuleEngine {{ coraza_sec_rule_engine | default("DetectionOnly") }} - - -# -- Request body handling --------------------------------------------------- - -# Allow Coraza to access request bodies. If you don't, Coraza -# won't be able to see any POST parameters, which opens a large security -# hole for attackers to exploit. -# -SecRequestBodyAccess On - -# Enable XML request body parser. -# Initiate XML Processor in case of xml content-type -# -SecRule REQUEST_HEADERS:Content-Type "^(?:application(?:/soap\+|/)|text/)xml" \ - "id:'200000',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=XML" - -# Enable JSON request body parser. -# Initiate JSON Processor in case of JSON content-type; change accordingly -# if your application does not use 'application/json' -# -SecRule REQUEST_HEADERS:Content-Type "^application/json" \ - "id:'200001',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=JSON" - -# Enable JSON request body parser for more subtypes. -# Adapt this rule if you want to engage the JSON Processor for "+json" subtypes -# -SecRule REQUEST_HEADERS:Content-Type "^application/[a-z0-9.-]+[+]json" \ - "id:'200006',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=JSON" - -# Maximum request body size we will accept for buffering. If you support -# file uploads, this value must has to be as large as the largest file -# you are willing to accept. -SecRequestBodyLimit 13107200 - -# Maximum request body size that Coraza will store in memory. If the body -# size exceeds this value, it will be saved to a temporary file on disk. -SecRequestBodyInMemoryLimit 131072 - -# Maximum request body size we will accept for buffering, with files excluded. -# You want to keep that value as low as practical. -# Note: SecRequestBodyNoFilesLimit is currently NOT supported by Coraza -# SecRequestBodyNoFilesLimit 131072 - -# What to do if the request body size is above our configured limit. -# Keep in mind that this setting will automatically be set to ProcessPartial -# when SecRuleEngine is set to DetectionOnly mode in order to minimize -# disruptions when initially deploying Coraza. -# Warning: Setting this directive to ProcessPartial introduces a potential bypass -# risk, as attackers could prepend junk data equal to or greater than the inspected body size. -# -SecRequestBodyLimitAction Reject - -# Verify that we've correctly processed the request body. -# As a rule of thumb, when failing to process a request body -# you should reject the request (when deployed in blocking mode) -# or log a high-severity alert (when deployed in detection-only mode). -# -SecRule REQBODY_ERROR "!@eq 0" \ - "id:'200002', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2" - -# By default be strict with what we accept in the multipart/form-data -# request body. If the rule below proves to be too strict for your -# environment consider changing it to detection-only. -# Do NOT remove it, as it will catch many evasion attempts. -# -SecRule MULTIPART_STRICT_ERROR "!@eq 0" \ - "id:'200003',phase:2,t:none,log,deny,status:400, \ - msg:'Multipart request body failed strict validation." - - -# -- Debug log configuration ------------------------------------------------- - -# Default debug log path -# Debug levels: -# 0: No logging (least verbose) -# 1: Error -# 2: Warn -# 3: Info -# 4-8: Debug -# 9: Trace (most verbose) -# -SecDebugLog /var/log/coraza/debug.log -SecDebugLogLevel 3 - - -# -- Audit log configuration ------------------------------------------------- - -# Log the transactions that are marked by a rule, as well as those that -# trigger a server error (determined by a 5xx or 4xx, excluding 404, -# level response status codes). -# -SecAuditEngine RelevantOnly -SecAuditLogRelevantStatus "^(?:(5|4)(0|1)[0-9])$" - -# Define which parts of the transaction are going to be recorded in the audit log -SecAuditLogParts ABIJDEFHZ - -# Use a single file for logging. This is much easier to look at, but -# assumes that you will use the audit log only occasionally. -# -SecAuditLogType Serial -SecAuditLogDir /var/log/coraza/audit -SecAuditLog /var/log/coraza/audit.log - -# The format used to write the audit log. -# Can be one of JSON|JsonLegacy|Native|OCSF -SecAuditLogFormat JSON diff --git a/ansible/roles/crontab/readme.md b/ansible/roles/crontab/readme.md deleted file mode 100644 index d9e1e263d..000000000 --- a/ansible/roles/crontab/readme.md +++ /dev/null @@ -1,36 +0,0 @@ -# Manage crontab - -This role is very simple is use the same parameters of module cron (https://docs.ansible.com/ansible/latest/modules/cron_module.html). - - -* [Manage crontab](#manage-crontab) - * [Examples](#examples) - * [Silence `/etc/cron.d/` crons](#silence-etccrond-crons) - - -## Examples - -Cron restart apache2 every 4 hours: -```yaml -cron_tasks: - - name: "Restart apache2 " - minute: "0" - hour: "*/4" - job: "systemctl restart apache2.service" -``` - -Environnement variable: -```yaml -cron_tasks: - - name: MAILTO - env: yes - value: "" -``` - -## Silence `/etc/cron.d/` crons - -This is an edge case, crons souldn't be managed this way, but you can silence mails from crons inside `/etc/cron.d/*` files by adding `MAILTO=""` for root, e.g. with: -```yaml -crontab_silence_files: [sentry, belgique_demo] -``` -N.B.: only existing files are updated. diff --git a/ansible/roles/crontab/tasks/main.yml b/ansible/roles/crontab/tasks/main.yml deleted file mode 100644 index 12e10092b..000000000 --- a/ansible/roles/crontab/tasks/main.yml +++ /dev/null @@ -1,55 +0,0 @@ ---- -# file: roles/crontab/tasks/main.yml - -- name: "Install cron package" - apt: - name: cron - tags: crontab - -- name: "Configuring cron tasks" - ansible.builtin.cron: - cron_file: "{{ item.cron_file | default(omit) }}" - day: "{{ item.day | default(omit) }}" - env: "{{ item.env | default(omit) }}" - hour: "{{ item.hour | default(omit) }}" - job: "{{ item.job | default(omit) }}" - minute: "{{ item.minute | default(omit) }}" - month: "{{ item.month | default(omit) }}" - name: "{{ item.name }}" - special_time: "{{ item.special_time | default(omit) }}" - state: "{{ item.state | default(omit) }}" - user: "{{ item.user | default(omit) }}" - value: "{{ item.value | default(omit) }}" - weekday: "{{ item.weekday | default(omit) }}" - disabled: "{{ item.disabled | default(omit) }}" - loop: "{{ cron_tasks }}" - when: cron_tasks is defined - tags: crontab - -- name: "Silence selected root cron.d files via MAILTO" - block: - - name: "Check if cron files exist" - ansible.builtin.stat: - path: "/etc/cron.d/{{ item }}" - loop: "{{ crontab_silence_files }}" - register: crontab_file_stats - - name: "Keep only existing cron files" - ansible.builtin.set_fact: - crontab_silence_files_existing: >- - {{ - crontab_file_stats.results - | selectattr('stat.exists', 'defined') - | selectattr('stat.exists') - | map(attribute='item') - | list - }} - - name: "Silence existing root cron.d files" - ansible.builtin.cron: - name: MAILTO - env: true - value: "" - cron_file: "{{ item }}" - user: root - loop: "{{ crontab_silence_files_existing }}" - when: crontab_silence_files is defined and (crontab_silence_files | length) > 0 - tags: crontab diff --git a/ansible/roles/docker/defaults/main.yml b/ansible/roles/docker/defaults/main.yml deleted file mode 100644 index c8869746f..000000000 --- a/ansible/roles/docker/defaults/main.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -# file: roles/docker/defaults/main.yml - -docker_compose: true -docker_user: root -docker_rootless: false -docker_compose_version: "latest" -docker_compose_update_now: false diff --git a/ansible/roles/docker/readme.md b/ansible/roles/docker/readme.md deleted file mode 100644 index 840cc5942..000000000 --- a/ansible/roles/docker/readme.md +++ /dev/null @@ -1,103 +0,0 @@ -# Docker role - -This role will install Docker on a target machine running Debian or Ubuntu. - - -* [Docker role](#docker-role) - * [Variable reference](#variable-reference) - * [Optional variables](#optional-variables) - * [Example](#example) - * [Select the Docker version](#select-the-docker-version) - * [Select the Docker-compose version](#select-the-docker-compose-version) - * [Informations](#informations) - * [Important about the network](#important-about-the-network) - * [Update of docker-compose](#update-of-docker-compose) - - -## Variable reference - -### Optional variables - -| Variable | Description | Default value | -|------------------------|--------------------------------------------------------------------------------------------|---------------| -| docker_compose | install docker-compose | `true` | -| docker_user | name of the user who is going to use docker | `root` | -| docker_rootless | run the Docker daemon as a non-root user (Rootless mode) | `false` | -| docker_pinned | see section [Select the Docker version](#select-the-docker-version) bellow | None | -| docker_compose_version | see section [Select the Docker-compose version](#select-the-docker-compose-version) bellow | None | -| docker_registry_login | see bellow | None | - -`docker_registry_login` is used when you need to define an url/username/password to access specific dockers registries. - -The object is defined like this: -``` -docker_registry_login: - - url: "docker.talas.dev" - username: "user" - password: "pass" - - url: "something" - username: "user" - password: "pass" -``` - -## Example -### Select the Docker version - -By default, the latest version of Docker will be installed, but you can specify a version by setting this variable: -``` -docker_pinned: "17.09.0~ce-0~debian" -# Or only pin the major version -docker_pinned: "27*" -``` -To find out the list of available versions, use this command on the target server: -``` -# apt-cache madison docker-ce - docker-ce | 17.09.0~ce-0~debian | https://download.docker.com/linux/debian stretch/stable amd64 Packages - docker-ce | 17.06.2~ce-0~debian | https://download.docker.com/linux/debian stretch/stable amd64 Packages - docker-ce | 17.06.1~ce-0~debian | https://download.docker.com/linux/debian stretch/stable amd64 Packages - docker-ce | 17.06.0~ce-0~debian | https://download.docker.com/linux/debian stretch/stable amd64 Packages - docker-ce | 17.03.2~ce-0~debian-stretch | https://download.docker.com/linux/debian stretch/stable amd64 Packages - docker-ce | 17.03.1~ce-0~debian-stretch | https://download.docker.com/linux/debian stretch/stable amd64 Packages - docker-ce | 17.03.0~ce-0~debian-stretch | https://download.docker.com/linux/debian stretch/stable amd64 Packages -``` - -### Select the Docker-compose version - -By default, this role will install the latest version of docker-compose. -You can also select a specific docker-compose version by setting this variable: -``` -docker_compose_version: "1.17.1" -``` -You can find the list of docker-compose release here: https://github.com/docker/compose/releases/ - -## Informations -### Important about the network - -This role let docker create the docker0 bridge interface. This means that if docker sees a route for all the rfc1918 networks (10.0.0.0/8, 172.16.0.0/12 and 192.168.0.0/16), it will fail. - -This basically is the case for the machines in the DMZ: all those routes are defined so that the default gateway can be the BGP router, typically you have such configuration for their DMZ interface: -``` -# DMZ6 -auto eth136 - iface eth136 inet static - address 10.12.36.96 - netmask 24 - dns-nameservers 10.12.1.207 10.12.1.2 - dns-search talas.com - - -# static route -up route add -net 10.0.0.0 netmask 255.0.0.0 gw 10.12.36.254 dev eth136 -up route add -net 172.16.0.0 netmask 255.240.0.0 gw 10.12.36.254 dev eth136 -up route add -net 192.168.0.0 netmask 255.255.0.0 gw 10.12.36.254 dev eth136 -``` - -To allow doker to create the docker0 interface, you basically have to remove the last line. Currently we don't use any 192.168.0.0/16 network so it won't be an issue. - -### Update of docker-compose - -To perform an update, add this parameter: `--extra-vars "docker_compose_update_now=true"` , *true* is case-sensitive since it's evaluated as a string in this case. - -This role will also update if this parameter is present: `--extra-vars "global_update_now=true"` , *true* is also case-sensitive since it's evaluated as a string in this case. - -The update will be skipped if you already have the latest version of the binary. diff --git a/ansible/roles/docker/tasks/docker-rootless.yml b/ansible/roles/docker/tasks/docker-rootless.yml deleted file mode 100644 index c86317e89..000000000 --- a/ansible/roles/docker/tasks/docker-rootless.yml +++ /dev/null @@ -1,84 +0,0 @@ ---- -# file: roles/docker/tasks/docker-rootless.yml - -- name: "install dependencies" - apt: - name: - - uidmap - - docker-ce-rootless-extras - - slirp4netns - -- name: "get uidnumber of user {{ docker_user }}" - ansible.builtin.command: - cmd: "id -u {{ docker_user }}" - changed_when: false - check_mode: false - register: rootless_uid - -- name: "check if /run/docker.sock exists" - stat: - path: "/run/user/{{ rootless_uid.stdout }}/docker.sock" - register: rootless_conf - -- name: "stop any running root instances of docker daemon" - systemd: - name: "{{ item }}" - state: stopped - enabled: false - loop: - - docker.service - - docker.socket - -- name: "remove docker.sock file" - file: - path: /var/run/docker.sock - state: absent - -- name: "set 65536 subordinate UIDs/GUIDs for the user" - lineinfile: - path: "/etc/{{ item }}" - insertafter: EOF - line: "{{ docker_user }}:100000:65536" - loop: - - subuid - - subgid - -- name: "install rootless docker (ssh root@server 'machinectl -q shell {{ docker_user }}@ dockerd-rootless-setuptool.sh install)" - remote_user: root - become: true - become_method: community.general.machinectl - become_user: "{{ docker_user }}" - vars: - ansible_ssh_pipelining: false # https://github.com/ansible/ansible/issues/81254 - ansible.builtin.command: /usr/bin/dockerd-rootless-setuptool.sh install - when: not rootless_conf.stat.exists - -- name: "enable and start rootless docker" - remote_user: root - become: true - become_method: community.general.machinectl - become_user: "{{ docker_user }}" - vars: - ansible_ssh_pipelining: false # https://github.com/ansible/ansible/issues/81254 - systemd: - name: docker.service - state: started - enabled: true - scope: user - ignore_errors: "{{ ansible_check_mode }}" - -- name: "decouple rootless docker from user session" - remote_user: root - become: true - become_method: community.general.machinectl - become_user: "{{ docker_user }}" - vars: - ansible_ssh_pipelining: false # https://github.com/ansible/ansible/issues/81254 - ansible.builtin.command: "loginctl enable-linger {{ docker_user }}" - when: not rootless_conf.stat.exists - -- name: "DOCKER_HOST=unix:///run/user/{{ rootless_uid.stdout }}/docker.sock in /etc/environment" - lineinfile: - path: /etc/environment - insertafter: EOF - line: "DOCKER_HOST=unix:///run/user/{{ rootless_uid.stdout }}/docker.sock" diff --git a/ansible/roles/docker/tasks/main.yml b/ansible/roles/docker/tasks/main.yml deleted file mode 100644 index f282200b5..000000000 --- a/ansible/roles/docker/tasks/main.yml +++ /dev/null @@ -1,215 +0,0 @@ ---- -# file: roles/docker/tasks/main.yml - -- name: "packages prerequisites" - apt: - name: - - ca-certificates - - curl - - software-properties-common - tags: docker - -- name: "apt package for pip" - apt: - name: - - python3-pkg-resources - - python3-setuptools - tags: docker - -- name: "[ubuntu and Debian 11-] module installation with pip needed for ansible control" - pip: - name: - - docker - - docker-compose - when: ansible_distribution == "Ubuntu" or ( ansible_distribution == "Debian" and ansible_distribution_major_version is version('12', '<')) - tags: docker - -- name: "[Debian 12+] apt install python3-docker for ansible control" - apt: - name: - - python3-docker - when: - - ansible_distribution == "Debian" - - ansible_distribution_major_version is version('12', '>=') - tags: docker - -- name: "apt install docker-compose v1 from debian package" - apt: - name: - - docker-compose - tags: docker - -- name: "remove legacy key from apt-key" - apt_key: - id: "9DC858229FC7DD38854AE2D88D81803C0EBFCD88" - state: absent - when: ansible_distribution_major_version is version('13', '<') or ansible_distribution != "Debian" - tags: docker - -- name: "download modern signature key" - get_url: - url: "https://download.docker.com/linux/{{ ansible_distribution | lower }}/gpg" - dest: "/dev/shm/docker.acs" - changed_when: false - tags: docker - -- name: "check if {{ get_env_var.stdout }}/docker.sock exists" - file: - path: "/etc/apt/keyrings" - state: directory - -- name: "install modern signature key" - shell: - cmd: "cat /dev/shm/docker.acs | gpg --dearmor -o /etc/apt/keyrings/docker.gpg" - creates: "/etc/apt/keyrings/docker.gpg" - tags: docker - -- name: "repository file" - copy: - content: "deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/{{ ansible_distribution | lower }} {{ ansible_distribution_release }} stable\n" - dest: "/etc/apt/sources.list.d/docker.list" - register: repo - tags: docker - -- name: "apt pin docker-ce* version" - ansible.builtin.copy: - content: | - Package: docker-ce* - Pin: version 5:{{ docker_pinned }} - # Note: priority of 1001 (greater than 1000) allows for downgrading. - # To make package downgrading impossible, use a value of 999 - Pin-Priority: 1001 - dest: "/etc/apt/preferences.d/docker" - when: docker_pinned is defined - tags: docker - -- name: "apt make sure that docker-ce version is not pinned" - ansible.builtin.file: - path: "/etc/apt/preferences.d/docker" - state: absent - when: docker_pinned is undefined - tags: docker - -- name: "refresh apt if repo was modified" - apt: - update_cache: true - when: repo.changed - tags: docker - -- name: "apt install docker-ce (not pinned)" - apt: - name: "docker-ce" - when: docker_pinned is undefined - tags: docker - -- name: "apt install docker-ce (pinned)" - apt: - name: "docker-ce" - state: latest - install_recommends: true - when: docker_pinned is defined - tags: docker - -- name: "docker compose v2 package" - apt: - name: "docker-compose-plugin" - tags: docker - -- name: "stat /usr/local/bin/docker-compose" - stat: - path: /usr/local/bin/docker-compose - register: docker_compose_binary - when: - - docker_compose - - docker_compose_version == "latest" - tags: docker - -- name: "docker-compose: get the latest download link on github" - uri: - url: https://api.github.com/repos/docker/compose/releases/latest - return_content: true - check_mode: false - register: URL - delegate_to: localhost - become: false - run_once: true - when: - - docker_compose - - docker_compose_version == "latest" - - docker_compose_binary.stat.exists and ( docker_compose_update_now == "true" or global_update_now == "true" ) or not docker_compose_binary.stat.exists - tags: docker - -# curl -s https://api.github.com/repos/docker/compose/releases/latest | jq -r '.assets[] | select(.name == "docker-compose-linux-x86_64") | .browser_download_url' -- name: "latest docker compose installation" - get_url: - url: "{{ URL.json | json_query(params) | first }}" - dest: "/usr/local/bin/docker-compose" - force: True - mode: 0755 - vars: - params: "assets[?name=='docker-compose-linux-x86_64'].browser_download_url" - when: - - docker_compose - - docker_compose_version == "latest" - - ( docker_compose_update_now == "true" or global_update_now == "true" ) or not docker_compose_binary.stat.exists - tags: docker - -- name: "docker compose version {{ docker_compose_version }} installation" - get_url: - url: "https://github.com/docker/compose/releases/download/{{ docker_compose_version }}/docker-compose-linux-x86_64" - dest: "/usr/local/bin/docker-compose" - force: true - mode: 0755 - when: - - docker_compose - - docker_compose_version != "latest" - tags: docker - -- name: "install dependencies when docker_user is not root" - apt: - name: - - systemd-container - when: docker_user != "root" - tags: docker - -- name: "make sure that {{ docker_user }} is a member of docker group" - ansible.builtin.user: - name: "{{ docker_user }}" - groups: - - docker - append: true - when: docker_user != "root" - tags: docker - -- name: "setting up docker daemon as non-root" - import_tasks: docker-rootless.yml - when: docker_rootless - tags: docker - -- name: "docker login user root to remote registry" - community.docker.docker_login: - registry_url: "{{ item.url }}" - username: "{{ item.username }}" - password: "{{ item.password }}" - loop: "{{ docker_registry_login }}" - when: - - docker_registry_login is defined - - docker_user == "root" - tags: docker - -- name: "docker login user {{ docker_user }} to remote registry" - remote_user: root - become: true - become_method: community.general.machinectl - become_user: "{{ docker_user }}" - vars: - ansible_ssh_pipelining: false # https://github.com/ansible/ansible/issues/81254 - community.docker.docker_login: - registry_url: "{{ item.url }}" - username: "{{ item.username }}" - password: "{{ item.password }}" - loop: "{{ docker_registry_login }}" - when: - - docker_registry_login is defined - - docker_user != "root" - tags: docker diff --git a/ansible/roles/filebeat/defaults/main.yml b/ansible/roles/filebeat/defaults/main.yml deleted file mode 100644 index 9562c2571..000000000 --- a/ansible/roles/filebeat/defaults/main.yml +++ /dev/null @@ -1,26 +0,0 @@ ---- -# file: roles/filebeat/defaults/main.yml - -filebeat_modules_detection: - - module: apache - path: /etc/apache2/apache2.conf - - module: auditd - path: /etc/audit/auditd.conf - - module: elasticsearch - path: /etc/elasticsearch/elasticsearch.yml - - module: haproxy - path: /etc/haproxy/haproxy.cfg - - module: kibana - path: /etc/kibana/kibana.yml - - module: logstash - path: /etc/logstash/logstash.yml - -filebeat_logging_level: "warning" - -filebeat_separate_system_logs: true - -filebeat_enable_test_config: false -filebeat_ingest_firewall: false -filebeat_ingest_keycloak: false - -filebeat_update: false diff --git a/ansible/roles/filebeat/files/apache.yml b/ansible/roles/filebeat/files/apache.yml deleted file mode 100644 index a44aea6a1..000000000 --- a/ansible/roles/filebeat/files/apache.yml +++ /dev/null @@ -1,22 +0,0 @@ -# Module: apache -# Docs: https://www.elastic.co/guide/en/beats/filebeat/7.5/filebeat-module-apache.html -# Ansible managed - -- module: apache - # Access logs - access: - enabled: true - - # Set custom paths for the log files. If left empty, - # Filebeat will choose the paths depending on your OS. - var.paths: - - '/var/log/apache2/*access.log' - - # Error logs - error: - enabled: true - - # Set custom paths for the log files. If left empty, - # Filebeat will choose the paths depending on your OS. - var.paths: - - '/var/log/apache2/*error.log' diff --git a/ansible/roles/filebeat/files/haproxy.yml b/ansible/roles/filebeat/files/haproxy.yml deleted file mode 100644 index c603ea7d4..000000000 --- a/ansible/roles/filebeat/files/haproxy.yml +++ /dev/null @@ -1,16 +0,0 @@ -# Module: haproxy -# Docs: https://www.elastic.co/guide/en/beats/filebeat/7.6/filebeat-module-haproxy.html - -- module: haproxy - # All logs - log: - enabled: true - var.paths: ["/var/log/haproxy.log*"] - var.input: "file" - - # Set which input to use between syslog (default) or file. - #var.input: - - # Set custom paths for the log files. If left empty, - # Filebeat will choose the paths depending on your OS. - #var.paths: diff --git a/ansible/roles/filebeat/files/kibana.yml b/ansible/roles/filebeat/files/kibana.yml deleted file mode 100644 index 4a7ee71c2..000000000 --- a/ansible/roles/filebeat/files/kibana.yml +++ /dev/null @@ -1,13 +0,0 @@ -# Module: kibana -# Docs: https://www.elastic.co/guide/en/beats/filebeat/7.5/filebeat-module-kibana.html -# Ansible managed - -- module: kibana - # All logs - log: - enabled: true - - # Set custom paths for the log files. If left empty, - # Filebeat will choose the paths depending on your OS. - var.paths: - - '/var/log/kibana/kibana.log' diff --git a/ansible/roles/filebeat/handlers/main.yml b/ansible/roles/filebeat/handlers/main.yml deleted file mode 100644 index a8c462941..000000000 --- a/ansible/roles/filebeat/handlers/main.yml +++ /dev/null @@ -1,12 +0,0 @@ ---- -# file: roles/filebeat/handlers/main.yml - -- name: systemctl daemon_reload - ansible.builtin.systemd: - daemon_reload: yes - changed_when: true - -- name: restart filebeat - ansible.builtin.systemd: - name: filebeat - state: restarted diff --git a/ansible/roles/filebeat/readme.md b/ansible/roles/filebeat/readme.md deleted file mode 100644 index fa435e56a..000000000 --- a/ansible/roles/filebeat/readme.md +++ /dev/null @@ -1,62 +0,0 @@ -# Mandatory variables -Define the elastic repository version that will determine the version of filebeat: -``` -elastic_major_version: "7.x" -``` -Define the list of logstash endpoint where to send the logs: -Define the output for the logs, it can be either elasticsearch or logstash: -``` -filebeat_output_elasticsearch_hosts: - - host1 - - host2 -``` -or -``` -filebeat_output_logstash_hosts: - - host1 - - host2 -``` -If you use elasticsearch, the connection will use https and use the login/password of the server to authenticate itself. -You can change the protocol to http with: -``` -filebeat_output_elasticsearch_protocol: "http" -``` -# Optional variables -You can disable this filebeat role by setting this variable : -```yaml -filebeat_install: false -``` - -By default, filebeat will send the system logs to the index `logs-infra-system` and the other logs to `logs-{{ talas_project }}-{{ talas_group }}`. - -Sometime, you want to send _all_ logs (even for the system), to the `logs-{{ talas_project }}-{{ talas_group }}` index. - -If this is the case, you need to set this variable to false: -``` -filebeat_separate_system_logs: false -``` - -You can define the loglevel of filebeat, the default is `warning`, possible values are error, warning, info, debug: -``` -filebeat_logging_level: "warning" -``` -# Modules -The `system` module is always enabled. - -This role will automatically detect the installation of the following software and enable the correct modules: -- apache httpd -- elasticsearch -- haproxy -- kibana -- logstash - -You can add more module by creating this list: -``` -filebeat_modules_list: - - system -``` -You can see the list of modules with `filebeat modules list`. - -# Update - -You can perform an update of filebeat by adding: `--extra-vars '{ "filebeat_update" : true }'` diff --git a/ansible/roles/filebeat/tasks/apt_repo.yml b/ansible/roles/filebeat/tasks/apt_repo.yml deleted file mode 100644 index 70e480289..000000000 --- a/ansible/roles/filebeat/tasks/apt_repo.yml +++ /dev/null @@ -1,23 +0,0 @@ ---- -# file: roles/filebeat/tasks/key.yml - -- name: "make sure /etc/apt/keyrings exists" - ansible.builtin.file: - path: "/etc/apt/keyrings" - state: directory - -- name: "modern signature key" - ansible.builtin.get_url: - url: "https://artifacts.elastic.co/GPG-KEY-elasticsearch" - dest: "/etc/apt/keyrings/elastic.asc" - -- name: "repository file" - ansible.builtin.copy: - content: "deb [arch=amd64 signed-by=/etc/apt/keyrings/elastic.asc] https://artifacts.elastic.co/packages/{{ elastic_major_version }}/apt stable main\n" - dest: "/etc/apt/sources.list.d/elastic.list" - register: repo - -- name: "apt update" - ansible.builtin.apt: - update_cache: true - when: repo.changed diff --git a/ansible/roles/filebeat/tasks/main.yml b/ansible/roles/filebeat/tasks/main.yml deleted file mode 100644 index 6a2fb1725..000000000 --- a/ansible/roles/filebeat/tasks/main.yml +++ /dev/null @@ -1,79 +0,0 @@ ---- -# file: roles/filebeat/tasks/main.yml - -- name: "fix IT-12220" - ansible.builtin.file: - state: absent - path: "{{ item }}" - loop: - - "/etc/apt/sources.list.d/artifacts_elastic_co_packages_7_x_apt.list" - - "/etc/apt/sources.list.d/artifacts_elastic_co_packages_6_x_apt.list" - - "/etc/apt/sources.list.d/filebeat.list" - - "/etc/apt/keyrings/filebeat.asc" - tags: filebeat - -- name: "handle apt repository" - ansible.builtin.import_tasks: apt_repo.yml - tags: filebeat - -- name: "Ensure any version of filebeat is installed" - ansible.builtin.apt: - name: filebeat - update_cache: true - when: not filebeat_update - tags: filebeat - -- name: "Ensure the LATEST version of filebeat is installed" - ansible.builtin.apt: - name: filebeat - update_cache: true - state: latest - when: filebeat_update - tags: filebeat - -- name: "check haproxy presence" - ansible.builtin.stat: - path: /etc/haproxy/haproxy.cfg - register: haproxy_cfg - -- name: "expose boolean of haproxy presence" - ansible.builtin.set_fact: - haproxy_present: "{{ haproxy_cfg.stat.exists }}" - -- name: "check coraza presence" - ansible.builtin.stat: - path: /etc/coraza/coraza.conf - register: coraza_conf - -- name: "expose boolean of coraza presence" - ansible.builtin.set_fact: - coraza_present: "{{ coraza_conf.stat.exists }}" - -- name: "/etc/filebeat/filebeat.yml" - ansible.builtin.template: - src: filebeat.yml.j2 - dest: /etc/filebeat/filebeat.yml - backup: yes - notify: restart filebeat - tags: filebeat - -- name: "import_tasks: modules.yml" - ansible.builtin.import_tasks: modules.yml - tags: - - filebeat - - filebeat_modules - -- name: "/etc/systemd/system/filebeat.service" - ansible.builtin.template: - src: filebeat.service - dest: /etc/systemd/system/filebeat.service - notify: - - systemctl daemon_reload - - restart filebeat - tags: filebeat - -- name: "make sure the filebeat service is enabled" - ansible.builtin.systemd_service: - name: filebeat - enabled: yes - tags: filebeat diff --git a/ansible/roles/filebeat/tasks/modules.yml b/ansible/roles/filebeat/tasks/modules.yml deleted file mode 100644 index 516877c5d..000000000 --- a/ansible/roles/filebeat/tasks/modules.yml +++ /dev/null @@ -1,82 +0,0 @@ ---- -# file: roles/filebeat/tasks/modules.yml - -- name: "make sure the system module is enabled" - ansible.builtin.command: - cmd: "filebeat modules enable system" - creates: "/etc/filebeat/modules.d/system.yml" - notify: restart filebeat - tags: - - filebeat - - filebeat_modules - -- name: "make sure the additional modules are enabled, if defined" - ansible.builtin.command: - cmd: "filebeat modules enable {{ item }}" - creates: "/etc/filebeat/modules.d/{{ item }}.yml" - loop: "{{ filebeat_modules_list }}" - when: filebeat_modules_list is defined - notify: restart filebeat - tags: - - filebeat - - filebeat_modules - -- name: "change module name for elastic_major_version == '6.x'" - ansible.builtin.set_fact: - filebeat_modules_detection: - - module: apache2 - path: /etc/apache2/apache2.conf - - module: elasticsearch - path: /etc/elasticsearch/elasticsearch.yml - - module: haproxy - path: /etc/haproxy/haproxy.cfg - - module: kibana - path: /etc/kibana/kibana.yml - - module: logstash - path: /etc/logstash/logstash.yml - when: elastic_major_version == "6.x" - tags: - - filebeat - - filebeat_modules - -- name: "detect installed software to enable correct modules" - ansible.builtin.stat: - path: "{{ item['path'] }}" - loop: "{{ filebeat_modules_detection }}" - register: detection - tags: - - filebeat - - filebeat_modules - -- name: "enable module for installed softwares" - ansible.builtin.command: - cmd: "filebeat modules enable {{ item.item.module }}" - creates: "/etc/filebeat/modules.d/{{ item.item.module }}.yml" - loop: "{{ detection.results }}" - when: item.stat.exists - notify: restart filebeat - tags: - - filebeat - - filebeat_modules - -- name: "disable module for haproxy (this is temporary step to get rid of haproxy module, BB-673 related)" - ansible.builtin.command: - cmd: "filebeat modules disable haproxy" - creates: "/etc/filebeat/modules.d/haproxy.yml.disabled" - notify: restart filebeat - tags: - - filebeat - - filebeat_modules - -- name: "module configuration: /etc/filebeat/modules.d/[module].yml" - ansible.builtin.copy: - src: "{{ item.item.module }}.yml" - dest: "/etc/filebeat/modules.d/{{ item.item.module }}.yml" - loop: "{{ detection.results }}" - when: - - item.stat.exists - - item.item.module == "apache" or item.item.module == "kibana" or item.item.module == "haproxy" - notify: restart filebeat - tags: - - filebeat - - filebeat_modules diff --git a/ansible/roles/filebeat/templates/filebeat.service b/ansible/roles/filebeat/templates/filebeat.service deleted file mode 100644 index ac90abff1..000000000 --- a/ansible/roles/filebeat/templates/filebeat.service +++ /dev/null @@ -1,15 +0,0 @@ -[Unit] -Description=Filebeat sends log files to Logstash or directly to Elasticsearch. -Documentation=https://www.elastic.co/products/beats/filebeat -Wants=network-online.target -After=network-online.target - -[Service] - -Environment="BEAT_CONFIG_OPTS=-c /etc/filebeat/filebeat.yml" -Environment="BEAT_PATH_OPTS=-path.home /usr/share/filebeat -path.config /etc/filebeat -path.data /var/lib/filebeat -path.logs /var/log/filebeat" -ExecStart=/usr/share/filebeat/bin/filebeat $BEAT_CONFIG_OPTS $BEAT_PATH_OPTS -Restart=always - -[Install] -WantedBy=multi-user.target diff --git a/ansible/roles/filebeat/templates/filebeat.yml.j2 b/ansible/roles/filebeat/templates/filebeat.yml.j2 deleted file mode 100644 index 8a17c5207..000000000 --- a/ansible/roles/filebeat/templates/filebeat.yml.j2 +++ /dev/null @@ -1,177 +0,0 @@ -# {{ ansible_managed }} -#=========================== Filebeat inputs ============================= -filebeat.inputs: - - type: log - enabled: false - paths: - - /var/log/*.log -{% if groups["veza_app_gen_2"] is defined and ansible_hostname in groups["veza_app_gen_2"] and filebeat_enable_test_config %} - - type: filestream - id: talas-tomcat-accesslog - enabled: true - tags: - - talas_tomcat_accesslog - paths: - - /applications/tomcat/logs/*.txt - - type: filestream - id: talas-veza-app - enabled: true - tags: - - talas_veza_app - paths: - - /applications/logs/cos-veza/*/LOG/log.log - multiline.pattern: '^\[[0-9]{4}-[0-9]{2}-[0-9]{2}' - multiline.negate: true - multiline.match: after -{% endif %} -{% if filebeat_ingest_firewall %} - - type: filestream - id: talas-firewall - enabled: true - tags: - - talas_firewall - paths: - - /var/log/ulog/*.log - processors: - - add_locale: ~ -{% endif %} -{% if filebeat_ingest_keycloak %} - - type: filestream - id: talas-keycloak - enabled: true - tags: - - talas_keycloak - paths: - - /var/log/keycloak/keycloak.log -{% endif %} -{% if haproxy_present %} - - type: filestream - id: talas-haproxy-http - enabled: true - tags: - - talas_haproxy_http - paths: - - /var/log/haproxy/http.log - - type: filestream - id: talas-haproxy-tcp - enabled: true - tags: - - talas_haproxy_tcp - paths: - - /var/log/haproxy/tcp.log -{% if haproxy_present %} - - type: filestream - id: talas-haproxy-spoe - enabled: true - tags: - - talas_haproxy_spoe - paths: - - /var/log/haproxy/spoe.log -{% endif %} -{% endif %} -#============================= Filebeat modules =============================== -filebeat.config.modules: - path: ${path.config}/modules.d/*.yml - reload.enabled: false -#==================== Elasticsearch template setting ========================== -setup.template.settings: - index.number_of_shards: 1 -#================================ General ===================================== -name: {{ ansible_hostname }} -{% if filebeat_setup_kibana_host is defined %} -#============================== Kibana ===================================== -setup.kibana: - host: "{{ filebeat_setup_kibana_host }}" -{% endif %} -#================================ Outputs ===================================== -output.elasticsearch: - hosts: [ "{{ filebeat_output_elasticsearch_hosts | join('", "') }}" ] - - protocol: "{{ filebeat_output_elasticsearch_protocol | default('https') }}" - username: "{{ ansible_hostname }}" - password: "{{ ldappass }}" - pipelines: - - pipeline: "talas_tomcat_accesslog" - when: - contains: - tags: "talas_tomcat_accesslog" - - pipeline: "talas_veza_app" - when: - contains: - tags: "talas_veza_app" - - pipeline: "talas_samba_veza" - when: - contains: - tags: "talas_samba_veza" - - pipeline: "talas_samba_auditlog" - when: - contains: - tags: "talas_samba_auditlog" - - pipeline: "talas_firewall" - when: - contains: - tags: "talas_firewall" - - pipeline: "talas_keycloak_json" - when: - contains: - tags: "talas_keycloak" -{% if haproxy_present %} - - pipeline: "talas_haproxy_http" - when: - contains: - tags: "talas_haproxy_http" - - pipeline: "talas_haproxy_tcp" - when: - contains: - tags: "talas_haproxy_tcp" -{% if haproxy_present %} - - pipeline: "talas_haproxy_spoe" - when: - contains: - tags: "talas_haproxy_spoe" -{% endif %} -{% endif %} - indices: -{% if filebeat_separate_system_logs %} - - index: "logs-{{ talas_project | default('infra') }}-{{ talas_group | default('default') }}" - when: - or: - - equals: - event.module: "apache" - - equals: - event.module: "haproxy" - - contains: - tags: "talas_tomcat_accesslog" - - contains: - tags: "talas_samba_veza" - - contains: - tags: "talas_samba_auditlog" - - contains: - tags: "talas_firewall" - - contains: - tags: "talas_keycloak" -{% if haproxy_present %} - - contains: - tags: "talas_haproxy_http" - - contains: - tags: "talas_haproxy_tcp" -{% if haproxy_present %} - - contains: - tags: "talas_haproxy_spoe" -{% endif %} -{% endif %} - - index: "logs-infra-system" -{% else %} - - index: "logs-{{ talas_project | default('infra') }}-{{ talas_group | default('default') }}" -{% endif %} -#================================ Processors ===================================== -processors: - - add_host_metadata: ~ -#================================ Logging ===================================== -logging.level: {{ filebeat_logging_level }} -logging.to_files: true -logging.files: - path: /var/log/filebeat - name: filebeat - keepfiles: 7 - permissions: 0600 \ No newline at end of file diff --git a/ansible/roles/git_generic_deploy_files/readme.md b/ansible/roles/git_generic_deploy_files/readme.md deleted file mode 100644 index e07f50311..000000000 --- a/ansible/roles/git_generic_deploy_files/readme.md +++ /dev/null @@ -1,47 +0,0 @@ -# Deploy files from a git repository - -This role will localy clone any number of git repositories with any number of branches, then create an archive for each repositories/branches combinations. - -Those archives will then be extracted to the remote server in the correct directory. - -To make this work, you need to define an object with all the necessary variables, like this: -``` -git_generic_deploy_files_list: - - repository_url: 'https://scmlab.talas.com/VirtualTryOn/talasTryOn_API_Booth.git' - branch: 'opticworld' - deploy_directory: '/var/www/opticworld.vto.talas.io/' - - repository_url: 'https://scmlab.talas.com/VirtualTryOn/talasTryOn_API_Booth.git' - branch: 'master' - deploy_directory: '/dev/shm/test/' -``` - -# Mandatory variable -| Variable | Description | Example | -|-----------------------------------|-------------|---------| -| repository_url | repo url | `https://scmlab.talas.com/VirtualTryOn/talasTryOn_API_Booth.git` -| branch | branch to clone | `master` -| deploy_directory | path to unpack archive | `/var/www/freescout` - -# Optional variable -| Variable for git_generic_deploy_files_list | Description | Default value | -|-----------------------------------|-------------|---------------| -| deploy_directory_owner | Owner of deploy_directory path | none, same as user executing | -| deploy_directory_group | group owning of deploy_directory path | none, same as user executing | -| deploy_directory_mode | perms of deploy_directory path | | -| owner | owner of files inside the repo, same for all files | same as user executing | -| group | group owning files inside the repo, same for all files | same as user executing | -| mode | perms for files inside the repo | perserve those from repo | - -The git_generic_deploy_copy variable is very simple and use the same parameters of module copy (https://docs.ansible.com/ansible/latest/modules/copy_module.html). - -Example: -``` -git_generic_deploy_copy: - - dest: "/var/www/prerequisites.talas.net/veza_prerequisites/configuration.php" - content: | -

200 OK

-OK - - diff --git a/ansible/roles/haproxy/files/404.http b/ansible/roles/haproxy/files/404.http deleted file mode 100644 index 3ffac130d..000000000 --- a/ansible/roles/haproxy/files/404.http +++ /dev/null @@ -1,9 +0,0 @@ -HTTP/1.0 404 Not Found -Cache-Control: no-cache -Connection: close -Content-Type: text/html - -

404 Not Found

-The requested URL was not found on this server. - - diff --git a/ansible/roles/haproxy/files/coraza.cfg b/ansible/roles/haproxy/files/coraza.cfg deleted file mode 100644 index da4c225ed..000000000 --- a/ansible/roles/haproxy/files/coraza.cfg +++ /dev/null @@ -1,32 +0,0 @@ -# https://github.com/haproxy/haproxy/blob/master/doc/SPOE.txt -# /usr/local/etc/haproxy/coraza.cfg -[coraza] -spoe-agent coraza-agent - # Process HTTP requests only (the responses are not evaluated) - messages coraza-req - # Comment the previous line and add coraza-res, to process responses also. - #messages coraza-req coraza-res - groups coraza-req coraza-res - option var-prefix coraza - option set-on-error error - timeout hello 2s - timeout idle 2m - timeout processing 500ms - use-backend coraza-spoa - log global - -spoe-message coraza-req - # Arguments are required to be in this order - args app=var(txn.coraza.app) src-ip=src src-port=src_port dst-ip=dst dst-port=dst_port method=method path=path query=query version=req.ver headers=req.hdrs body=req.body - -spoe-message coraza-res - # Arguments are required to be in this order - args app=var(txn.coraza.app) id=var(txn.coraza.id) version=res.ver status=status headers=res.hdrs body=res.body - event on-http-response - -spoe-group coraza-req - messages coraza-req - -spoe-group coraza-res - messages coraza-res - diff --git a/ansible/roles/haproxy/files/dehydrated_haproxy_hook.sh b/ansible/roles/haproxy/files/dehydrated_haproxy_hook.sh deleted file mode 100644 index 7840a20c1..000000000 --- a/ansible/roles/haproxy/files/dehydrated_haproxy_hook.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash -# {{ ansible_managed }} -if [[ "$1" == "deploy_challenge" ]]; then - /bin/systemctl start http-letsencrypt.service -elif [[ "$1" == "clean_challenge" ]]; then - /bin/systemctl stop http-letsencrypt.service -elif [[ "$1" == "deploy_cert" ]]; then - domain=$2 - key=$3 - fullchain=$5 - cat $fullchain $key > /usr/local/etc/tls/haproxy/${domain}.pem - echo "reloading haproxy" - /bin/systemctl reload haproxy.service -fi diff --git a/ansible/roles/haproxy/files/haproxy.conf b/ansible/roles/haproxy/files/haproxy.conf deleted file mode 100644 index 50a1bee4c..000000000 --- a/ansible/roles/haproxy/files/haproxy.conf +++ /dev/null @@ -1,11 +0,0 @@ -# zabbix monitoring for haproxy -# every userparameters here suppose that you have a stat file at "/run/haproxy/monitoring.sock" - -# General info that don't need discovery, uses a cache file that is automatically refreshed if it is older than 1 minute -UserParameter=haproxy.info[*],/etc/zabbix/scripts/haproxy_info.sh $1 - -# discovery for FRONTEND, BACKEND and SERVERS, no cache -UserParameter=haproxy.discovery[*],/etc/zabbix/scripts/haproxy_discovery.sh $1 - -# return a specific stat for a specific pxname and svname, uses a cache file that is automatically refreshed if it is older than 1 minute -UserParameter=haproxy.stats[*],/etc/zabbix/scripts/haproxy_stat.py --pxname $1 --svname $2 --stat $3 diff --git a/ansible/roles/haproxy/files/haproxy_discovery.sh b/ansible/roles/haproxy/files/haproxy_discovery.sh deleted file mode 100644 index 6368cc4f1..000000000 --- a/ansible/roles/haproxy/files/haproxy_discovery.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/bash -# Ansible managed -# modified from https://raw.githubusercontent.com/anapsix/zabbix-haproxy/master/haproxy_discovery.sh -# Get list of Frontends and Backends from HAPROXY -# Example: ./haproxy_discovery.sh FRONTEND|BACKEND|SERVERS -# the argument should be either FRONTEND, BACKEND or SERVERS, will default to FRONTEND if not set - -HAPROXY_SOCK="/run/haproxy/monitoring.sock" - -query_stats() { - echo "show stat" | socat ${HAPROXY_SOCK} stdio 2>/dev/null -} - -get_stats() { - echo "$(query_stats)" | grep -v "^#" -} - -case $1 in - B*) END="BACKEND" ;; - F*) END="FRONTEND" ;; - S*) - for backend in $(get_stats | grep BACKEND | cut -d, -f1 | uniq); do - for server in $(get_stats | grep "^${backend}," | grep -v BACKEND | cut -d, -f2); do - serverlist="$serverlist, "'{ "{#BACKEND_NAME}": "'$backend'","{#SERVER_NAME}": "'$server'" }' - done - done - echo -e '{ "data": [ '${serverlist#,}'] }' - exit 0 - ;; - *) END="FRONTEND" ;; -esac - -for frontend in $(get_stats | grep "$END" | cut -d, -f1 | uniq); do - felist="$felist,"'{ "{#'${END}'_NAME}": "'$frontend'" }' -done -echo -e '{ "data": [ '${felist#,}']}' diff --git a/ansible/roles/haproxy/files/haproxy_info.sh b/ansible/roles/haproxy/files/haproxy_info.sh deleted file mode 100644 index 79638e860..000000000 --- a/ansible/roles/haproxy/files/haproxy_info.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/bash -# Ansible managed - -set -euo pipefail - -TMPDIR=/dev/shm -SOCKET=/run/haproxy/monitoring.sock -TMPFILE=$TMPDIR/haproxy_info.tmp -CACHEFILE=$TMPDIR/haproxy_info.txt -CACHE_EXPIRATION_TIME_SECONDS=60 -METRIC=$1 - -refresh_cache_file () { - echo "show info" | socat /run/haproxy/monitoring.sock stdio > $TMPFILE - # rsync is atomic - rsync -t $TMPFILE $CACHEFILE -} - -# if either the tmpfile or the cachefile is not here, do the refresh -if ! [ -f $TMPFILE ] || ! [ -f $CACHEFILE ]; then - refresh_cache_file -fi - -# if the cache file is too old, do the refresh -CACHEFILE_TIMESTAMP=$(stat -c %Y $CACHEFILE) -if [ ${CACHEFILE_TIMESTAMP} -lt $(date -d "60 second ago" "+%s") ]; then - refresh_cache_file -fi - -# we need a special case for "Unstoppable Jobs" because that's the only metric with a space in its name -if [ $METRIC == "UnstoppableJobs" ]; then - egrep "^Unstoppable Jobs: " $CACHEFILE | sed "s/^Unstoppable Jobs: //" -else - egrep "^$METRIC: " $CACHEFILE | sed "s/^$METRIC: //" -fi diff --git a/ansible/roles/haproxy/files/haproxy_stat.py b/ansible/roles/haproxy/files/haproxy_stat.py deleted file mode 100644 index 828f3e7ca..000000000 --- a/ansible/roles/haproxy/files/haproxy_stat.py +++ /dev/null @@ -1,93 +0,0 @@ -#!/usr/bin/python3 - -import argparse -import datetime -import os -import subprocess - -########################################################################### -# BEGIN FUNCTIONS -########################################################################### - - -def parse_haproxy_stats(line): - haproxy_dict = {} - line_as_dict = line.rstrip(",\n").split(",") - index = 0 - for item in line_as_dict: - field_name = field_name_list[index] - haproxy_dict[field_name] = item - index = index + 1 - return haproxy_dict - - -def refresh_cache_file(): - CMD = 'echo "show stat" | socat /run/haproxy/monitoring.sock stdio > ' + stat_file + ".tmp" # noqa: N806 - RESULT = subprocess.check_output(CMD, shell=True) # noqa: N806, F841 - os.rename(stat_file + ".tmp", stat_file) # noqa: PTH104 - - -########################################################################### -# END FUNCTIONS - BEGIN PARSER -########################################################################### - -# begin parser -parser = argparse.ArgumentParser( - description="return a specific stat for a specific pxname and svname, it uses a cache file that is automatically refreshed if it is older than 1 minute" -) -parser.add_argument( - "--pxname", - help="this is the name of the backend, frontend or server exactly as it appears in the configuration", - type=str, - required=True, -) -parser.add_argument( - "--svname", - help="the type of the service, either FRONTEND, BACKEND or the name of the server", - type=str, - required=True, -) -parser.add_argument("--stat", help="the stat wanted", type=str, required=True) -args = parser.parse_args() - -########################################################################### -# END PARSER - BEGIN PROGRAM -########################################################################### - -stat_file = "/dev/shm/haproxy_stat.txt" -localtime = datetime.datetime.now() -maxage = datetime.timedelta(seconds=60) -if os.path.isfile(stat_file): # noqa: PTH113 - last_modified_date = datetime.datetime.fromtimestamp(os.path.getmtime(stat_file)) # noqa: PTH204 - if localtime - last_modified_date > maxage: - # the cache file is too old, let's refresh it - refresh_cache_file() -else: - # the cache file doesn't exists, so we need to create it - refresh_cache_file() - -# transform the stat file into a dict, line by line -with open(stat_file) as stat: # noqa: PTH123 - stat_by_line = stat.readlines() - -# the first element of the dict contains the name of the fields -field_name_list = stat_by_line[0].rstrip(",\n").lstrip("# ").split(",") -# we don't care about the first element now, remove it from the dict -del stat_by_line[0] -# the last element of the dict is an empty line, remove it too -del stat_by_line[-1] - -# define the final object that we will query for the stats -STATS_AS_LIST = [] - -# populate the final object -for line in stat_by_line: - STATS_AS_LIST.append(parse_haproxy_stats(line)) - -# Search for the final object with pxname and svname -CORRECT_LINE = next( - (query for query in STATS_AS_LIST if query["pxname"] == args.pxname and query["svname"] == args.svname), None -) - -# Print the stat if a matching element is found, otherwise print "non-existent" -print(CORRECT_LINE[args.stat] if CORRECT_LINE else "non-existent") diff --git a/ansible/roles/haproxy/files/http-letsencrypt.service b/ansible/roles/haproxy/files/http-letsencrypt.service deleted file mode 100644 index a26ecb3f7..000000000 --- a/ansible/roles/haproxy/files/http-letsencrypt.service +++ /dev/null @@ -1,9 +0,0 @@ -# Ansible managed - -[Unit] -Description=very simple http server for letsencrypt challenge - -[Service] -User=www-data -Group=www-data -ExecStart=/usr/bin/python3 -m http.server --bind 127.0.0.1 --directory /var/www/letsencrypt/ 8888 diff --git a/ansible/roles/haproxy/files/override.conf b/ansible/roles/haproxy/files/override.conf deleted file mode 100644 index 0828a79a3..000000000 --- a/ansible/roles/haproxy/files/override.conf +++ /dev/null @@ -1,4 +0,0 @@ -# Ansible managed - -[Service] -BindReadOnlyPaths=/var/lib/haproxy/dev/log diff --git a/ansible/roles/haproxy/files/robots.txt b/ansible/roles/haproxy/files/robots.txt deleted file mode 100644 index 1f53798bb..000000000 --- a/ansible/roles/haproxy/files/robots.txt +++ /dev/null @@ -1,2 +0,0 @@ -User-agent: * -Disallow: / diff --git a/ansible/roles/haproxy/handlers/main.yml b/ansible/roles/haproxy/handlers/main.yml deleted file mode 100644 index 35599d488..000000000 --- a/ansible/roles/haproxy/handlers/main.yml +++ /dev/null @@ -1,22 +0,0 @@ ---- -# file: roles/haproxy/handlers/main.yml - -- name: systemctl daemon_reload - ansible.builtin.systemd: - daemon_reload: yes - changed_when: true - -- name: reload haproxy - systemd: - name: haproxy - state: reloaded - -- name: restart haproxy - systemd: - name: haproxy - state: restarted - -- name: restart zabbix_agent - service: - name: zabbix-agent - state: restarted diff --git a/ansible/roles/haproxy/meta/main.yml b/ansible/roles/haproxy/meta/main.yml deleted file mode 100644 index da001b16b..000000000 --- a/ansible/roles/haproxy/meta/main.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -# file: roles/haproxy/meta/main.yml - -dependencies: - - { role: openssl, when: haproxy_tls_profile is defined and haproxy_tls_profile == "old" } - - { role: coraza, when: haproxy_coraza is defined and haproxy_coraza } diff --git a/ansible/roles/haproxy/readme.md b/ansible/roles/haproxy/readme.md deleted file mode 100644 index becd8e6ec..000000000 --- a/ansible/roles/haproxy/readme.md +++ /dev/null @@ -1,259 +0,0 @@ -This role will install haproxy from the official repository `http://haproxy.debian.net`. - - -* [Important](#important) -* [Mandatory variables](#mandatory-variables) -* [Optional variables](#optional-variables) -* [Frontends](#frontends) -* [letsencrypt automatic certificate generation](#letsencrypt-automatic-certificate-generation) -* [Coraza WAF installation](#coraza-waf-installation) -* [IIS specific headers for https](#iis-specific-headers-for-https) -* [Issues with compression](#issues-with-compression) -* [haproxy and journald](#haproxy-and-journald) -* [haproxy documentation](#haproxy-documentation) - - -# Important - -This role consider that haproxy will always serve https. - -This role currently doesn't handle the management of the https certificates and private keys. HAproxy looks for files in /usr/local/etc/tls/haproxy: each files here must contain the private key, the certificate and the full chain (yes, everything in one file!). - -HAproxy will automatically answer https requests with SNI with the correct certificate. - -# Mandatory variables - -This role uses object to define configuration parameters. - -The haproxy version is mandatory, but should already be defined in `group_vars/all/software_versions`, so except in very specific cases (like testing of new version), you don't need to override it: -``` -haproxy_version: "2.8" -``` -For the backends, you can define several of them this way: -``` -haproxy_backend: - - name: "identity-test" - balance: "roundrobin" # this is the default and can be ommitted - server: - - name: "id-test-1" # if undefined, takes the value of the "fqdn" - fqdn: "identity-test-node-1.talas.com" # if undefined, takes the value of the "name"" - port: "8080" - - name: "id-test-2" - fqdn: "identity-test-node-2.talas.com" - port: "8080" - proto: "h2" - check: "check inter 2s fastinter 2s downinter 2s" # default is "check" - options: "string containing the options for this server, this is optional" -``` -Unfortunately, currently this role cannot find out which certificate are active and thus which ones should be seen by zabbix so you must list the https website with this list: -``` -haproxy_https_monitoring: - - identity.talas.com -``` - -# TLS profiles -_Changelog of the TLS parameter:_ - -- 2025-01: The "old" profile cannot be used anymore because of audit from our customers. It is kept for historical reasons only. -- 2025-03: migration of the last service using the "old" profile to the "intermediate" profile -- TO DO: actually delete the "old" profile so that it cannot be used anymore - -The TLS configuration is generated with https://ssl-config.mozilla.org/#server=haproxy&version=2.8. - -The default profile is "intermediate" (which supports TLS 1.2+) but you can switch it to modern (which supports TLS 1.3+) via this variable: -``` -haproxy_tls_profile: "modern" -``` - -# Optional variables -You can change the default backend of the frontend: -``` -haproxy_frontend: - default_backend: "error404" -``` -This roles has a default maximum number of connection set to 20000 (the default in vanilla haproxy is 500). You can adjust this with this variable: -``` -haproxy_maxconn: 20000 -``` - -You can also adjust the timeout values of haproxy, which are explained here: -- https://serverfault.com/questions/504308/by-what-criteria-do-you-tune-timeouts-in-ha-proxy-config / https://thehftguy.com/2016/05/22/configuring-timeouts-in-haproxy/ - -The default are: -```yml -haproxy_timeout_connect: "5s" -haproxy_timeout_client: "50s" -haproxy_timeout_server: "50s" -``` -From [haproxy documentation](https://www.haproxy.org/download/2.8/doc/configuration.txt): -> In TCP mode (and to a lesser extent, in HTTP mode), it is highly recommended that the client timeout remains equal to the server timeout in order to avoid complex situations to debug. - -You can handle all robots.txt for all frontends via this variable: -``` -haproxy_robotstxt: True -``` - -When set to true, the url /robots.txt will return: -``` -User-agent: * -Disallow: / -``` -Is it usefull when backends should not be indexed. - -You can also use the robots.txt backend in only some cases, for this, just reference the robots_txt acl. Example: -``` -acl something hdr(host) something.example.org -use_backend robotstxt if is_robots_txt something -``` -The default acl `robotstxt` is in the standard frontend. - -You can define several user lists, to have one authentication page (basic_auth): -```yaml -haproxy_userlist: - mailcatcher: - - bolle_mailcatcher - - user2 -``` -In this example: -- `mailcatcher` is the userlist name which you can specify in your haproxy configuration -- `bolle_mailcatcher` and `user2` are the users - -Passwords are automatically generated by the role and added to hashicorpvault. If you wish, you can define them in advance, respecting this name: -```yaml -haproxy_basicauth_%USERNAME%_password # replace %USERNAME% with the username you've defined -``` - -Information: the password is added to the haproxy configuration in clear text to avoid this: -http://docs.haproxy.org/2.9/configuration.html#3.4-user -```text -Attention: Be aware that using encrypted passwords might cause significantly increased CPU usage, depending on the number of requests, and the algorithm used. -For any of the hashed variants, the password for each request must be processed through the chosen algorithm, before it can be compared to the value specified in the config file. -Most current algorithms are deliberately designed to be expensive to compute to achieve resistance against brute force attacks. They do not simply salt/hash the clear text password once, but thousands of times. -This can quickly become a major factor in HAProxy's overall CPU consumption! -``` - -Example of haproxy configuration: -```yaml -haproxy_frontend_raw_config: | - acl mailcatcher.bollebrands.com hdr(host) -i mailcatcher.bollebrands.com - http-request auth if mailcatcher.bollebrands.com !{ http_auth(mailcatcher) } !acme-challenge - use_backend mailcatcher.bollebrands.com if mailcatcher.bollebrands.com { http_auth_group(mailcatcher) } -``` - -# Frontends -By default, this role create a frontend named "https" which has the following default configuration: -``` -frontend https - filter compression - compression algo gzip - compression type text/html text/plain text/xml text/css text/csv text/rtf text/richtext application/x-javascript application/javascript application/ecmascript application/rss+xml application/xml application/json application/wasm - mode http - bind :443,:::443 v6only ssl crt /usr/local/etc/tls/haproxy alpn h2,http/1.1 - bind :80,:::80 v6only - http-request set-header X-Forwarded-Proto https if { ssl_fc } - redirect scheme https code 301 if !{ ssl_fc } - option forwardfor - # block access to any git paths - acl git path,url_dec -m sub /.git - use_backend error404 if git - # block access to path begining by "/manager" except from 10.0.0.0/8 - acl internal_network src 10.0.0.0/8 - acl manager path,url_dec -m beg /manager - use_backend error404 if manager !internal_network - # redirect multiple traling slash to one slash - acl has_multiple_slash path_reg /{2,} - http-request set-path %[path,regsub(/+,/,g)] if has_multiple_slash -``` -You can override the "bind" lines with this list: -``` -haproxy_frontend: - bind_list: - - "127.0.0.1:443 ssl crt /usr/local/etc/tls/haproxy alpn h2,http/1.1" - - "127.0.0.1:80" -``` -You can add a raw configuration to the default frontend with this variable: -``` -haproxy_frontend_raw_config: | - acl admin path,url_dec -m beg /auth/admin - use_backend error404 if admin !internal_network -``` - -You can deactivate the default frontend with this variable: -``` -haproxy_default_frontend: false -``` - -You can also define any number of custom frontends with this object: -``` -haproxy_frontend_list: - - name: "something" - mode: "http/tcp" - bind_list: - - "*:389" - - "1.1.1.1:80" - config: | - free field to define the config of the frontend -``` -This allows full control over custom frontends for haproxy. - -# letsencrypt automatic certificate generation - -/!\ Lets encrypt automatic certificate generation can only be used on single node cluster (no keepalived). - -For this to work correctly, you need to need to have all domains in the `haproxy_https_monitoring` variable. Each domains has its own certificate, alternative names are not supported. - -To activate it, set this variable: -``` -haproxy_letsencrypt: true -``` -During certificate generation and renew, an http server is created to handle the challenge on port 8888. The server is created via a simple python command line and is only active during lets encrypt operations. - -# Coraza WAF installation - -Enable coraza WAF like this: -``` -haproxy_coraza: true -``` -If the `haproxy_waf_sample_percent` variable is defined, Coraza will be enabled in the default frontend. -However, if `waf_sample_percent` is defined within the `haproxy_frontend_list`, Coraza will be enabled in each frontend where `waf_sample_percent` is explicitly set. - -# IIS specific headers for https - -The header `Front-End-Https On` is the equivalent to `X-Forwarded-Proto https` for IIS, to activate it, set this variable to `true`: -``` -haproxy_iis: true -``` - -# Issues with compression - -Some mime types are problematic if compressed so compression was disabled for them, those are: -``` -application/hal+json -application/prs.hal-forms+json -``` -See the following tickets for more informations: -- https://tracker.talas.com/browse/OP-4916 -- https://tracker.talas.com/browse/OP-6532 -- https://tracker.talas.com/browse/IT-9018 - -# haproxy and journald - -In the systemd file for haproxy, the following line was added: -``` -BindReadOnlyPaths=/dev/log:/var/lib/haproxy/dev/log -``` -This line gives to haproxy the capability to send its log to journald. While this looks like a good idea, it is not. - -With this lines, the logs are duplicated between `/var/log/haproxy.log` and journald. On production, this means an increase by a factor of 40 (!!!) of the amount of write to the disk. - -With this line: 400KB/s, without: 10KB/s. - -This is crazy... and remember that this is duplicate logs that we don't use since filebeat will read the `/var/log/haproxy.log` and ignore journald. This also shows the poor optimisation of journald vs simple log files but this is an other story. - -Anyway, this role removes this line from the service file for all those reasons. - -# haproxy documentation - -Official documentation can be found at https://www.haproxy.org/download/2.8/doc/configuration.txt (change the version number for the latest if needed). - -Important part that we look for often is the one that details the "Session state at disconnection", which is essential to debug connectivity issues. Search for "8.5. Session state at disconnection" in the doc to find it immediately. diff --git a/ansible/roles/haproxy/tasks/install_debian.yml b/ansible/roles/haproxy/tasks/install_debian.yml deleted file mode 100644 index 70d6fa6fd..000000000 --- a/ansible/roles/haproxy/tasks/install_debian.yml +++ /dev/null @@ -1,46 +0,0 @@ ---- -# file: roles/haproxy/tasks/install_debian.yml - -- name: "remove legacy key from apt-key" - apt_key: - id: "AEF2348766F371C689A7360095A42FE8353525F9" - state: absent - -- name: "make sure /etc/apt/keyrings exists" - file: - path: "/etc/apt/keyrings" - state: directory - -- name: "download modern signature key" - get_url: - url: "https://haproxy.debian.net/bernat.debian.org.gpg" - dest: "/dev/shm/bernat.debian.org.gpg" - changed_when: false - -- name: "install modern signature key" - shell: - cmd: "cat /dev/shm/bernat.debian.org.gpg | gpg --dearmor -o /etc/apt/keyrings/haproxy.debian.net.gpg" - creates: "/etc/apt/keyrings/haproxy.debian.net.gpg" - -- name: "repository file" - copy: - content: "deb [arch=amd64 signed-by=/etc/apt/keyrings/haproxy.debian.net.gpg] http://haproxy.debian.net {{ ansible_distribution_release }}-backports-{{ haproxy_version }} main\n" - dest: "/etc/apt/sources.list.d/haproxy_debian_net.list" - register: repository - -- name: "refresh apt if repo was modified" - apt: - update_cache: yes - when: repository.changed - -- name: "set fact to install latest version of software when the repository changed" - set_fact: - apt_state: "latest" - when: repository.changed - -- name: "install haproxy" - apt: - name: - - haproxy - state: "{{ apt_state | default('present') }}" - default_release: "{{ ansible_distribution_release }}-backports-{{ haproxy_version }}" diff --git a/ansible/roles/haproxy/tasks/install_ubuntu.yml b/ansible/roles/haproxy/tasks/install_ubuntu.yml deleted file mode 100644 index ed95756d5..000000000 --- a/ansible/roles/haproxy/tasks/install_ubuntu.yml +++ /dev/null @@ -1,11 +0,0 @@ ---- -# file: roles/haproxy/tasks/install_ubuntu.yml - -- name: "repository" - apt_repository: - repo: "ppa:vbernat/haproxy-{{ haproxy_version }}" - -- name: "install haproxy" - apt: - name: "haproxy" - update_cache: yes diff --git a/ansible/roles/haproxy/tasks/letsencrypt.yml b/ansible/roles/haproxy/tasks/letsencrypt.yml deleted file mode 100644 index bf76e2e51..000000000 --- a/ansible/roles/haproxy/tasks/letsencrypt.yml +++ /dev/null @@ -1,72 +0,0 @@ ---- -# file: roles/haproxy/tasks/letsencrypt.yml - -- name: "[letsencrypt] reload haproxy immediately when the configuration has changed, else letsencrypt challenge may fail" - systemd: - name: haproxy - state: reloaded - when: haproxy_config.changed - -- name: "[letsencrypt] install git curl hexdump" - apt: - name: - - git - - curl - - bsdmainutils - update_cache: yes - -- name: "[letsencrypt] directory /usr/local/etc/letsencrypt" - file: - path: "{{ item }}" - state: directory - loop: - - "/usr/local/etc/letsencrypt" - - "/var/www/letsencrypt" - -- name: "[letsencrypt] git repo dehydrated" - git: - repo: https://github.com/dehydrated-io/dehydrated - dest: /usr/local/etc/letsencrypt/dehydrated - clone: yes - -- name: "[letsencrypt] domains.txt" - template: - src: letsencrypt_domains.txt - dest: /usr/local/etc/letsencrypt/dehydrated/domains.txt - backup: yes - when: haproxy_https_monitoring is defined - -- name: "[letsencrypt] le.config" - template: - src: letsencrypt_le.config - dest: /usr/local/etc/letsencrypt/dehydrated/le.config - backup: yes - -- name: "[letsencrypt] dehydrated_haproxy_hook.sh" - copy: - src: "dehydrated_haproxy_hook.sh" - dest: "/usr/local/etc/letsencrypt/dehydrated_haproxy_hook.sh" - mode: 0700 - backup: yes - -- name: "[letsencrypt] http-letsencrypt.service" - copy: - src: "http-letsencrypt.service" - dest: "/etc/systemd/system/http-letsencrypt.service" - -- name: "[letsencrypt] make sure the letsencrypt terms are accepted" - command: /usr/local/etc/letsencrypt/dehydrated/dehydrated --register --accept-terms --config /usr/local/etc/letsencrypt/dehydrated/le.config - register: accept_terms - changed_when: "accept_terms.stdout != '# INFO: Using main config file /usr/local/etc/letsencrypt/dehydrated/le.config\n+ Account already registered!'" - -- name: "[letsencrypt] generate certificate(s) if needed" - command: "/usr/local/etc/letsencrypt/dehydrated/dehydrated --cron --out /usr/local/etc/tls --challenge http-01 --config /usr/local/etc/letsencrypt/dehydrated/le.config --hook /usr/local/etc/letsencrypt/dehydrated_haproxy_hook.sh" - register: generate_certificates - changed_when: "'Generating private key' in generate_certificates.stdout" - -- name: "[letsencrypt] dehydrated crontab for automatic renew" - cron: - name: dehydrated - minute: "{{ 59 | random(seed=inventory_hostname) }}" - hour: "{{ 23 | random(seed=inventory_hostname) }}" - job: "/usr/local/etc/letsencrypt/dehydrated/dehydrated --cron --keep-going --out /usr/local/etc/tls --challenge http-01 --config /usr/local/etc/letsencrypt/dehydrated/le.config --hook /usr/local/etc/letsencrypt/dehydrated_haproxy_hook.sh" diff --git a/ansible/roles/haproxy/tasks/main.yml b/ansible/roles/haproxy/tasks/main.yml deleted file mode 100644 index 664d37317..000000000 --- a/ansible/roles/haproxy/tasks/main.yml +++ /dev/null @@ -1,165 +0,0 @@ ---- -# file: roles/haproxy/tasks/main.yml - -- name: "display haproxy_version (verbosity 1 or more)" - debug: - var: haproxy_version - verbosity: 1 - tags: haproxy - -- name: "secrets.yml" - include_tasks: secrets.yml - loop: "{{ haproxy_userlist | dict2items | map(attribute='value') | flatten }}" - loop_control: - loop_var: user - when: haproxy_userlist is defined - tags: haproxy - -- name: "debian install haproxy" - import_tasks: install_debian.yml - when: ansible_distribution == "Debian" - tags: - - haproxy - - apt_sources_list - -- name: "ubuntu install haproxy" - import_tasks: install_ubuntu.yml - when: ansible_distribution == "Ubuntu" - tags: haproxy - -- name: "folder /etc/systemd/system/haproxy.service.d" - file: - path: "/etc/systemd/system/haproxy.service.d" - state: directory - tags: haproxy - -- name: "handle /etc/systemd/system/haproxy.service.d/override.conf to prevent double logging" - copy: - src: "override.conf" - dest: "/etc/systemd/system/haproxy.service.d/override.conf" - notify: - - systemctl daemon_reload - - restart haproxy - tags: haproxy - -- name: "manage /etc/haproxy/errors/404.http and /etc/haproxy/errors/200.http" - copy: - src: "{{ item }}.http" - dest: "/etc/haproxy/errors/{{ item }}.http" - loop: - - 404 - - 200 - tags: haproxy - -- name: "folder /usr/local/etc/tls/haproxy" - file: - path: /usr/local/etc/tls/haproxy - state: directory - mode: 0755 - tags: haproxy - -- name: "we need at least one certificate for haproxy to start: /usr/local/etc/tls/haproxy/selfsigned.pem" - copy: - src: selfsigned.pem - dest: /usr/local/etc/tls/haproxy/selfsigned.pem - tags: haproxy - -- block: - - name: "folder /etc/haproxy/static" - file: - path: /etc/haproxy/static - state: directory - mode: 0755 - - name: "manage /etc/haproxy/static/robots.txt" - copy: - src: "robots.txt" - dest: "/etc/haproxy/static/robots.txt" - tags: haproxy - -- name: "undefined TLS security profile: set it to 'intermediate'" - set_fact: - haproxy_tls_profile: "intermediate" - when: haproxy_tls_profile is undefined - tags: haproxy - -- name: "invalid TLS security profile" - fail: - msg: 'invalid haproxy_tls_profile "{{ haproxy_tls_profile }}", possible values are "modern" or "intermediate"' - when: - - haproxy_tls_profile != "modern" - - haproxy_tls_profile != "intermediate" - - haproxy_tls_profile != "old" - tags: haproxy - -- name: "generate dhparams file (when the TLS profile is not modern)" - command: "openssl dhparam -out /usr/local/etc/tls/dh2048.pem 2048" - args: - creates: /usr/local/etc/tls/dh2048.pem - when: haproxy_tls_profile != "modern" - tags: haproxy - -- name: "Modern TLS configuration" - set_fact: - tls_ciphersuites: "{{ haproxy_tls_modern['ciphersuites'] }}" - tls_options: "{{ haproxy_tls_modern['options'] }}" - when: haproxy_tls_profile == "modern" - tags: haproxy - -- name: "Intermediate TLS configuration" - set_fact: - tls_ciphers: "{{ haproxy_tls_intermediate['ciphers'] }}" - tls_ciphersuites: "{{ haproxy_tls_intermediate['ciphersuites'] }}" - tls_options: "{{ haproxy_tls_intermediate['options'] }}" - when: haproxy_tls_profile == "intermediate" - tags: haproxy - -- name: "Old TLS configuration" - set_fact: - tls_ciphers: "{{ haproxy_tls_old['ciphers'] }}" - tls_ciphersuites: "{{ haproxy_tls_old['ciphersuites'] }}" - tls_options: "{{ haproxy_tls_old['options'] }}" - when: haproxy_tls_profile == "old" - tags: haproxy - -- name: "coraza spoa configuration" - ansible.builtin.copy: - src: coraza.cfg - dest: /etc/haproxy/coraza.cfg - when: - - haproxy_coraza is defined - - haproxy_coraza - tags: - - haproxy - - coraza - -- name: "/etc/haproxy/haproxy.cfg" - template: - src: "haproxy.cfg" - dest: "/etc/haproxy/haproxy.cfg" - backup: yes - validate: "haproxy -c -f %s" - notify: reload haproxy - register: haproxy_config - tags: haproxy - -- name: "lets encrypt" - import_tasks: letsencrypt.yml - when: haproxy_letsencrypt - tags: - - haproxy - - letsencrypt - -- name: "check if the folder /etc/zabbix/zabbix_agentd.conf.d exists" - stat: - path: "/etc/zabbix/zabbix_agentd.conf.d" - register: zabbix_folder - tags: - - haproxy - - zabbix - -- name: "import_tasks: zabbix.yml" - import_tasks: zabbix.yml - when: zabbix_folder.stat.exists - tags: - - haproxy - - zabbix diff --git a/ansible/roles/haproxy/tasks/secrets.yml b/ansible/roles/haproxy/tasks/secrets.yml deleted file mode 100644 index f1e36cdf4..000000000 --- a/ansible/roles/haproxy/tasks/secrets.yml +++ /dev/null @@ -1,20 +0,0 @@ ---- -# file: roles/haproxy/tasks/secrest.yml - -- name: "handle secret {{ user }}" - block: - - name: "get {{ user }} from hashicorp vault" - set_fact: - "{{ user }}": "{{ lookup('hashi_vault', 'secret=talas-kv/data/' + host_vars_location + '/' + ansible_hostname)['haproxy_basicauth_' + user + '_password'] }}" - rescue: - - name: "generate a random password for {{ user }}" - set_fact: - password: "{{ lookup('password','/dev/null chars=ascii_letters,digits length=50') }}" - - name: "patching hashicorp vault with generated {{ user }}" - delegate_to: localhost - become: no - command: "vault kv patch talas-kv/{{ host_vars_location }}/{{ ansible_hostname }} haproxy_basicauth_{{ user }}_password={{ password }}" - - name: "assign password value to {{ user }}" - set_fact: - "haproxy_basicauth_{{ user }}_password": "{{ password }}" - tags: haproxy diff --git a/ansible/roles/haproxy/tasks/zabbix.yml b/ansible/roles/haproxy/tasks/zabbix.yml deleted file mode 100644 index 4ec01af49..000000000 --- a/ansible/roles/haproxy/tasks/zabbix.yml +++ /dev/null @@ -1,31 +0,0 @@ ---- -# file: roles/haproxy/tasks/zabbix.yml - -- name: "apt install socat" - apt: - name: - - socat - - rsync - -- name: "[zabbix] cache scripts" - copy: - src: "{{ item }}" - dest: "/etc/zabbix/scripts/{{ item }}" - mode: 0755 - loop: - - haproxy_discovery.sh - - haproxy_info.sh - - haproxy_stat.py - -- name: "[zabbix] userparameters" - copy: - src: haproxy.conf - dest: /etc/zabbix/zabbix_agentd.conf.d/haproxy.conf - mode: 0644 - notify: restart zabbix_agent - -- name: "[zabbix] https discovery file /usr/local/etc/tls/zabbix.discovery" - template: - src: zabbix.discovery - dest: /usr/local/etc/tls/zabbix.discovery - when: haproxy_https_monitoring is defined diff --git a/ansible/roles/haproxy/templates/haproxy.cfg b/ansible/roles/haproxy/templates/haproxy.cfg deleted file mode 100644 index 8ff2856de..000000000 --- a/ansible/roles/haproxy/templates/haproxy.cfg +++ /dev/null @@ -1,222 +0,0 @@ -# {{ ansible_managed }} -{% if haproxy_userlist is defined %} -{% for userlist, users in haproxy_userlist.items() %} -userlist {{ userlist }} -{% for user in users %} - user {{ user }} insecure-password {{ lookup('hashi_vault', 'secret=talas-kv/data/' + host_vars_location + '/' + ansible_hostname)['haproxy_basicauth_' + user + '_password'] | mandatory }} -{% endfor %} - -{% endfor %} -{% endif %} - -# BEGIN GLOBAL AND DEFAULTS -global - log /dev/log local0 - log /dev/log local1 notice - chroot /var/lib/haproxy - stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners - stats socket /run/haproxy/monitoring.sock mode 666 level user - stats timeout 30s - user haproxy - group haproxy - daemon - maxconn {{ haproxy_maxconn }} - - ca-base /etc/ssl/certs - crt-base /etc/ssl/private - - # Current TLS profile: {{ haproxy_tls_profile }} - ssl-default-bind-curves X25519:prime256v1:secp384r1 -{% if tls_ciphers is defined %} - ssl-default-bind-ciphers {{ tls_ciphers|join(':') }} - ssl-default-server-ciphers {{ tls_ciphers|join(':') }} -{% endif %} - ssl-default-bind-ciphersuites {{ tls_ciphersuites|join(':') }} - ssl-default-server-ciphersuites {{ tls_ciphersuites|join(':') }} - ssl-default-bind-options {{ tls_options|join(' ') }} - ssl-default-server-options {{ tls_options|join(' ') }} -{% if haproxy_tls_profile != "modern" %} - ssl-dh-param-file /usr/local/etc/tls/dh2048.pem -{% endif %} - -defaults - log global - mode http - option httplog - option dontlognull - timeout connect {{ haproxy_timeout_connect | default('5s') }} - timeout client {{ haproxy_timeout_client | default('50s') }} - timeout server {{ haproxy_timeout_server | default('50s') }} - timeout http-request {{ haproxy_timeout_http_request | default('5s') }} - timeout client-fin {{ haproxy_timeout_client_fin | default('30s') }} - timeout tunnel {{ haproxy_timeout_tunnel | default('1h') }} - errorfile 400 /etc/haproxy/errors/400.http - errorfile 403 /etc/haproxy/errors/403.http - errorfile 408 /etc/haproxy/errors/408.http - errorfile 500 /etc/haproxy/errors/500.http - errorfile 502 /etc/haproxy/errors/502.http - errorfile 503 /etc/haproxy/errors/503.http - errorfile 504 /etc/haproxy/errors/504.http - -# END GLOBAL AND DEFAULTS => BEGIN FRONTENDS -{% if haproxy_default_frontend %} -frontend https - filter compression - compression algo gzip - compression type {{ haproxy_compression_type|join(' ') }} - mode http -{% if haproxy_frontend['bind_list'] is defined %} -{% for bind in haproxy_frontend['bind_list'] %} - bind {{ bind }} -{% endfor %} -{% else %} - bind :443,:::443 v6only ssl crt /usr/local/etc/tls/haproxy alpn h2,http/1.1 - bind :80,:::80 v6only -{% endif %} - http-request set-header X-Forwarded-Proto https if { ssl_fc } -{% if haproxy_iis %} - http-request set-header Front-End-Https On if { ssl_fc } -{% endif %} - redirect scheme https code 301 if !{ ssl_fc } - option forwardfor - default_backend {{ haproxy_frontend['default_backend'] | default('error404') }} - - # add an header to know on which haproxy we are - http-response set-header x-proxy-id {{ ansible_hostname }} - - # HSTS for 1 year - http-response set-header Strict-Transport-Security "max-age=31536000; preload" - - # block access to any git paths - acl git path,url_dec -m sub /.git - use_backend error404 if git - # block access to path begining by "/manager" except from 10.0.0.0/8 - acl internal_network src 10.0.0.0/8 - acl manager path,url_dec -m beg /manager - use_backend error404 if manager !internal_network - # redirect multiple traling slash to one slash - acl has_multiple_slash path_reg /{2,} - http-request set-path %[path,regsub(/+,/,g)] if has_multiple_slash - acl is_robots_txt path /robots.txt -{% if haproxy_robotstxt %} - use_backend robotstxt if is_robots_txt -{% endif %} -{% if haproxy_letsencrypt %} - acl acme-challenge path_beg -i /.well-known/acme-challenge - use_backend letsencrypt if acme-challenge -{% endif %} -{% if haproxy_coraza is defined and haproxy_coraza and haproxy_waf_sample_percent is defined%} - acl openvas src 185.14.128.171 2a03:a240:0:1dea::a2 -{% if haproxy_waf_sample_percent | int < 100 %} - acl waf_trigger rand(100) lt {{ haproxy_waf_sample_percent }} -{% endif %} - http-request set-var(txn.coraza.app) str(haproxy_waf) - filter spoe engine coraza config /etc/haproxy/coraza.cfg -{% if haproxy_waf_sample_percent | int < 100 %} - http-request send-spoe-group coraza coraza-req if waf_trigger !openvas -{% else %} - http-request send-spoe-group coraza coraza-req !openvas -{% endif %} - http-request redirect code 302 location %[var(txn.coraza.data)] if { var(txn.coraza.action) -m str redirect } - http-response redirect code 302 location %[var(txn.coraza.data)] if { var(txn.coraza.action) -m str redirect } - http-request deny deny_status 403 hdr waf-block "request" if { var(txn.coraza.action) -m str deny } - http-response deny deny_status 403 hdr waf-block "response" if { var(txn.coraza.action) -m str deny } - http-request silent-drop if { var(txn.coraza.action) -m str drop } - http-response silent-drop if { var(txn.coraza.action) -m str drop } - http-request deny deny_status 500 if { var(txn.coraza.error) -m int gt 0 } - http-response deny deny_status 500 if { var(txn.coraza.error) -m int gt 0 } -{% endif %} -{% if haproxy_frontend_raw_config is defined %} -{{ haproxy_frontend_raw_config|indent(8, True) }} -{% endif %} -{% endif %} -{% if haproxy_frontend_list is defined %} -{% for frontend in haproxy_frontend_list %} -frontend {{ frontend['name'] }} - mode {{ frontend['mode'] | default('http') }} -{% for bind in frontend['bind_list'] %} - bind {{ bind }} -{% endfor %} -{% if frontend['mode'] is defined and frontend['mode'] == 'tcp' %} - option tcplog -{% endif %} -{% if frontend['use_backend'] is defined %} - use_backend {{ frontend['use_backend'] }} -{% endif %} -{% if haproxy_coraza is defined and haproxy_coraza and frontend['waf_sample_percent'] %} - acl openvas src 185.14.128.171 2a03:a240:0:1dea::a2 -{% if frontend['waf_sample_percent'] | int < 100 %} - acl waf_trigger rand(100) lt {{ frontend['waf_sample_percent'] }} -{% endif %} - http-request set-var(txn.coraza.app) str(haproxy_waf) - filter spoe engine coraza config /etc/haproxy/coraza.cfg -{% if frontend['waf_sample_percent'] | int < 100 %} - http-request send-spoe-group coraza coraza-req if waf_trigger !openvas -{% else %} - http-request send-spoe-group coraza coraza-req !openvas -{% endif %} - http-request redirect code 302 location %[var(txn.coraza.data)] if { var(txn.coraza.action) -m str redirect } - http-response redirect code 302 location %[var(txn.coraza.data)] if { var(txn.coraza.action) -m str redirect } - http-request deny deny_status 403 hdr waf-block "request" if { var(txn.coraza.action) -m str deny } - http-response deny deny_status 403 hdr waf-block "response" if { var(txn.coraza.action) -m str deny } - http-request silent-drop if { var(txn.coraza.action) -m str drop } - http-response silent-drop if { var(txn.coraza.action) -m str drop } - http-request deny deny_status 500 if { var(txn.coraza.error) -m int gt 0 } - http-response deny deny_status 500 if { var(txn.coraza.error) -m int gt 0 } -{% endif %} -{% if frontend['config'] is defined %} -{{ frontend['config']|indent(8, True) }} -{% endif %} -{% endfor %} -{% endif %} - -# END FRONTENDS => BEGIN BACKENDS -backend error404 - mode http - errorfile 503 /etc/haproxy/errors/404.http -{% if haproxy_letsencrypt %} - -backend letsencrypt - http-request set-path %[path,regsub(/.well-known/acme-challenge/,/)] - server localhost 127.0.0.1:8888 -{% endif %} -{% if haproxy_coraza is defined and haproxy_coraza %} - -backend coraza-spoa - mode tcp - server coraza_spoa 127.0.0.1:9000 - -{% endif %} -backend robotstxt - mode http - http-request return status 200 content-type "text/plain" file "/etc/haproxy/static/robots.txt" hdr "cache-control" "no-cache" -{% if haproxy_backend is defined %} -{% for backend in haproxy_backend %} - -backend {{ backend['name'] }} -{% if backend['mode'] is defined %} - mode {{ backend['mode'] }} -{% endif %} -{% if backend['raw_config'] is defined %} -{{ backend['raw_config']|indent(8, True) }}{% endif %} -{% if backend['balance'] is defined %} - balance {{ backend['balance'] }} -{% endif %} -{% if backend['source'] is defined %} - source {{ backend['source'] }} -{% endif %} -{% if backend['server'] is defined %} -{% for server in backend['server'] %} - server {{ server['name'] | default(server['fqdn']) }} {{ server['fqdn'] | default(server['name']) }}:{{ server['port'] | default('80') }}{% if server['proto'] is defined %} proto {{ server['proto'] }}{% endif %} {{ server['check'] | default('check') }}{% if server['options'] is defined %} {{ server['options'] }}{% endif %} - -{% endfor %} -{% endif %} -{% if backend['server'] is defined and (backend['mode'] is undefined or backend['mode'] == 'http') %} - filter compression - compression algo gzip - compression type {{ haproxy_compression_type|join(' ') }} -{% endif %} -{% endfor %} -{% endif %} - -# END BACKENDS diff --git a/ansible/roles/haproxy/templates/letsencrypt_domains.txt b/ansible/roles/haproxy/templates/letsencrypt_domains.txt deleted file mode 100644 index 68e026cbf..000000000 --- a/ansible/roles/haproxy/templates/letsencrypt_domains.txt +++ /dev/null @@ -1,3 +0,0 @@ -{% for domain in haproxy_https_monitoring %} -{{ domain }} -{% endfor %} diff --git a/ansible/roles/haproxy/templates/letsencrypt_le.config b/ansible/roles/haproxy/templates/letsencrypt_le.config deleted file mode 100644 index 5fbd60552..000000000 --- a/ansible/roles/haproxy/templates/letsencrypt_le.config +++ /dev/null @@ -1,4 +0,0 @@ -# {{ ansible_managed }} -WELLKNOWN=/var/www/letsencrypt -KEYSIZE="2048" -HOOK_CHAIN=yes diff --git a/ansible/roles/haproxy/templates/zabbix.discovery b/ansible/roles/haproxy/templates/zabbix.discovery deleted file mode 100644 index d3efe8182..000000000 --- a/ansible/roles/haproxy/templates/zabbix.discovery +++ /dev/null @@ -1,3 +0,0 @@ -{% for https in haproxy_https_monitoring %} -{{ https }} -{% endfor %} diff --git a/ansible/roles/incus-client/readme.md b/ansible/roles/incus-client/readme.md deleted file mode 100644 index b800d8432..000000000 --- a/ansible/roles/incus-client/readme.md +++ /dev/null @@ -1,28 +0,0 @@ -# incus-client - -## Variable reference - -### Mandatory variables - -| Variable | Description | Example value | -| -------- | ----------- | ------------- | ------ | -| incus_repository | name of the zabbly incus repo to use | lts-6.0 | - -## Token documentation - -* On the server you need to connect to: -Create an unrestricted access (all projects): -``` -incus config trust add [client_name] -``` -Create a token restricted to a project: -``` -incus config trust add [client_name] --projects [project_name] -``` -Copy the token, you will need it bellow. - -* On the client: -``` -incus remote add srv-384 srv-384.talas.com -``` -Then paste the token there. diff --git a/ansible/roles/incus-client/tasks/main.yml b/ansible/roles/incus-client/tasks/main.yml deleted file mode 100644 index e16c613a2..000000000 --- a/ansible/roles/incus-client/tasks/main.yml +++ /dev/null @@ -1,34 +0,0 @@ ---- -# file: roles/incus-client/tasks/main.yml - -- name: "https://pkgs.zabbly.com/key.asc" - ansible.builtin.get_url: - url: "https://pkgs.zabbly.com/key.asc" - dest: "/etc/apt/keyrings/zabbly.asc" - tags: incus - -- name: "/etc/apt/sources.list.d/zabbly.sources" - ansible.builtin.copy: - content: | - Enabled: yes - Types: deb - URIs: https://pkgs.zabbly.com/incus/{{ incus_repository }} - Suites: {{ ansible_distribution_release }} - Components: main - Architectures: amd64 - Signed-By: /etc/apt/keyrings/zabbly.asc - dest: "/etc/apt/sources.list.d/zabbly.sources" - register: repository_incus - tags: incus - -- name: "apt update" - ansible.builtin.apt: - update_cache: yes - when: repository_incus.changed - tags: incus - -- name: "install incus-client" - ansible.builtin.apt: - name: - - incus-client - tags: incus diff --git a/ansible/roles/incus/defaults/main.yml b/ansible/roles/incus/defaults/main.yml deleted file mode 100644 index 804dba031..000000000 --- a/ansible/roles/incus/defaults/main.yml +++ /dev/null @@ -1,140 +0,0 @@ -# file: incus/defaults/main.yml - -incus_version_epoch: 1 -incus_update_now: false - -incus_zfs_backend: true -incus_zfs_root_dataset: "nvme/incus" - -incus_standard_profiles: - - name: cpu-2-cores - config: '{ "limits.cpu" : "2" }' - - name: cpu-4-cores - config: '{ "limits.cpu" : "4" }' - - name: cpu-8-cores - config: '{ "limits.cpu" : "8" }' - - name: cpu-10-cores - config: '{ "limits.cpu" : "10" }' - - name: cpu-16-cores - config: '{ "limits.cpu" : "16" }' - - name: cpu-20-cores - config: '{ "limits.cpu" : "20" }' - - name: cpu-32-cores - config: '{ "limits.cpu" : "32" }' - - name: cpu-48-cores - config: '{ "limits.cpu" : "48" }' - - name: cpu-64-cores - config: '{ "limits.cpu" : "64" }' - - name: cpu-128-cores - config: '{ "limits.cpu" : "128" }' - - name: mem-2GiB - config: '{ "limits.memory" : "2GiB" }' - - name: mem-4GiB - config: '{ "limits.memory" : "4GiB" }' - - name: mem-6GiB - config: '{ "limits.memory" : "6GiB" }' - - name: mem-10GiB - config: '{ "limits.memory" : "10GiB" }' - - name: mem-20GiB - config: '{ "limits.memory" : "20GiB" }' - - name: mem-30GiB - config: '{ "limits.memory" : "30GiB" }' - - name: mem-40GiB - config: '{ "limits.memory" : "40GiB" }' - - name: mem-50GiB - config: '{ "limits.memory" : "50GiB" }' - - name: mem-60GiB - config: '{ "limits.memory" : "60GiB" }' - - name: mem-100GiB - config: '{ "limits.memory" : "100GiB" }' - - name: mem-200GiB - config: '{ "limits.memory" : "200GiB" }' - - name: mem-250GiB - config: '{ "limits.memory" : "250GiB" }' - - name: mem-500GiB - config: '{ "limits.memory" : "500GiB" }' - -incus_yaml_unconfigured: |- - config: {} - networks: [] - storage_pools: [] - storage_volumes: [] - profiles: - - config: {} - description: Default Incus profile - devices: {} - name: default - project: "" - projects: - - config: - features.images: "true" - features.networks: "true" - features.networks.zones: "true" - features.profiles: "true" - features.storage.buckets: "true" - features.storage.volumes: "true" - description: Default Incus project - name: default - certificates: [] - -incus_standalone_init_yaml: |- - config: {} - networks: [] - storage_pools: - - config: - source: {{ incus_zfs_root_dataset }} - description: "" - name: default - driver: zfs - profiles: - - config: {} - description: "" - devices: - root: - path: / - pool: default - type: disk - name: default - cluster: null - -incus_cluster_ovn_conf: - northbound_connection: "{{ incus_ovn_northbound }}" - ca_cert: "{{ incus_client_cert_issuing_ca_chain }}" - client_cert: "{{ incus_client_cert_ca }}" - client_key: "{{ incus_client_cert_private_key }}" - -incus_cluster_main_init_yaml: | - config: - cluster.https_address: {{ incus_ip }}:8443 - core.https_address: {{ incus_ip }}:8443 - storage_pools: - - name: default - driver: zfs - config: - source: {{ incus_zfs_root_dataset }} - cluster: - server_name: {{ ansible_hostname }} - enabled: true - -incus_cluster_init_yaml: | - config: - cluster.https_address: {{ incus_ip }}:8443 - core.https_address: {{ incus_ip }}:8443 - cluster: - server_name: {{ ansible_hostname }} - enabled: true - cluster_address: {{ incus_cluster_main_ip }} - cluster_token: {{ incus_cluster_add.stdout }} - server_address: {{ incus_ip }} - member_config: - {{ incus_cluster_init_member_config | indent(width=4) }} - -incus_cluster_init_member_config: | - - entity: storage-pool - name: default - key: driver - value: zfs - - entity: storage-pool - name: default - key: source - value: {{ incus_zfs_root_dataset }} diff --git a/ansible/roles/incus/files/init_container.py b/ansible/roles/incus/files/init_container.py deleted file mode 100644 index 803e2573c..000000000 --- a/ansible/roles/incus/files/init_container.py +++ /dev/null @@ -1,411 +0,0 @@ -#!/usr/local/venvs/init_container/bin/python - -import argparse -import ipaddress -import logging -import os -import socket -import subprocess -import sys -import time - -import pylxd - -############################################################################### -# begin logger -############################################################################### -MYNAME = sys.argv[0] - -LOGDIR = "~/" + os.path.basename(MYNAME).replace(".py", "_LOG") # noqa: PTH119 -EXPENDED_LOGDIR = os.path.expanduser(LOGDIR) # noqa: PTH111 -if os.path.isdir(EXPENDED_LOGDIR) is False: # noqa: PTH112 - os.mkdir(EXPENDED_LOGDIR) # noqa: PTH102 -LOGFILE = EXPENDED_LOGDIR + "/" + time.strftime("%Y-%m-%d") + ".log" - -# from https://docs.python.org/2/howto/logging-cookbook.html -logger = logging.getLogger("jdl") -logger.setLevel(logging.DEBUG) -# create file handler -fh = logging.FileHandler(LOGFILE) -fh.setLevel(logging.INFO) -# create console handler -ch = logging.StreamHandler() -ch.setLevel(logging.INFO) -# create formatter and add it to the handlers -formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s") -fh.setFormatter(formatter) -ch.setFormatter(formatter) -# add the handlers to the logger -logger.addHandler(fh) -logger.addHandler(ch) - -logger.info("Program Starting") -############################################################################### -# end logger - start parser -############################################################################### - -domain_list = [ - "talas.com", - "int.talas.veza", - "talas.dev", - "hds.talas.cloud", - "int.talas.cloud", - "test.talas.com", - "hds.reflex-holding.com", - "talas.sh", -] - -parser = argparse.ArgumentParser(description="script to create a container on Debian or Ubuntu") -parser.add_argument("--name", help="name of the container", type=str, required=True) -parser.add_argument( - "--network", - help="network profile name for this container, this script only handle containers with 1 network profile, the name of the profile is usualy 'net-vlanXXX'", - type=str, - required=True, -) -parser.add_argument( - "--distrib", - help="distribution to use, current default is 'debian/bookworm'. Example: 'ubuntu/focal' 'debian/bookworm', you can see a list of available image with 'lxc image list images:'", - type=str, - default="debian/bookworm", -) -parser.add_argument( - "--cpu", - help="number of CPU threads for the container, will create the relevant profile if it doesn't exist (default=4)", - type=int, - default=4, -) -parser.add_argument( - "--memory", - help="memory limit in GiB, will create the relevant profile if it doesn't exist (default=4GiB)", - type=int, - default=4, -) -parser.add_argument("--quota", help="zfs quota in GiB for the root drive (default=10)", type=int, default=10) -parser.add_argument( - "--pool", help='name of the incus storage pool to use, the default value is "default" ', type=str, default="default" -) -parser.add_argument( - "--ip", - help=f"private IPv4 address, default to the DNS resolution of 'container name' with one of the following domains: {domain_list}. You can specify a mask, if not, a /24 will be used", - type=ipaddress.IPv4Interface, -) -parser.add_argument( - "--public", - help="Allow the use of public IP address with the --ip parameter, you will need to set a public DNS server with the --dns option for this to work as intended", - action="store_true", -) -parser.add_argument( - "--gateway", - help="IP address of the gateway, if unspecified, will use the last IP of the network", - type=ipaddress.ip_address, -) -parser.add_argument( - "--proxy", help="address of the proxy server, if any, syntax is 'hostname:port' ", type=str, default="no" -) -parser.add_argument( - "--dns", - help="DNS servers to use, the default is to copy the /etc/resolv.conf of the current host", - type=ipaddress.ip_address, - nargs="+", -) -parser.add_argument( - "--networkmethod", - help="which method should be used to configure the network, supported options are: 'networkd' or 'interfaces', default 'networkd', 'interfaces' should not be used anymore since Debian 11 is now working with networkd", - type=str, - choices=["networkd", "interfaces"], - default="networkd", -) -parser.add_argument("--debug", help="enable debug output", action="store_true") -args = parser.parse_args() - -if args.debug: - fh.setLevel(logging.DEBUG) - ch.setLevel(logging.DEBUG) - -if args.ip is None: - resolved = False - for domain in domain_list: - try: - ip = socket.gethostbyname(f"{args.name}.{domain}") + "/24" - args.ip = ipaddress.IPv4Interface(ip) - logger.info(f'resolved "{args.name}.{domain}" to "{ip}"') - resolved = True - break - except: # noqa: E722 - logger.debug(f'could not resolve "{args.name}.{domain}"') - if not resolved: - logger.info(f'could not resolve "{args.name}" with any of the following domains: {domain_list}, exiting.') - sys.exit(1) - - -############################################################################### -# end parser - start functions -############################################################################### -def ping(IP): # noqa: N803 - try: - CMD = "ping -w 1 " + IP # noqa: N806 - subprocess.check_output(CMD, shell=True) - logger.error("Specified IP is already up, exiting") - return True # noqa: TRY300 - except: # noqa: E722 - logger.debug("Specified IP is not responding, continuing") - return False - - -def check_container_exists(name): - try: - client.instances.get(name) - logger.error("container " + name + " already exists, exiting") - return True # noqa: TRY300 - except: # noqa: E722 - logger.debug("the container doesn't exist, proceeding") - return False - - -def wait(duration_in_sec): - print("waiting a bit ", end="", flush=True) - for n in range(1, duration_in_sec): # noqa: B007 - time.sleep(1) - print(".", end="", flush=True) - print("") - - -############################################################################### -# end functions - start program -############################################################################### -# check if a network mask was specified -if args.ip.netmask == ipaddress.IPv4Address("255.255.255.255"): - logger.debug("no netmask specified, assuming netmask /24") - ip = ipaddress.IPv4Interface(str(args.ip.ip) + "/24") -else: - ip = args.ip - -# verify that the IP address is private -if not ip.is_private and not args.public: - logger.error( - "IP address " - + ip.exploded - + " is not a private IPv4 address, use the --public option if you really want to use a public IP" - ) - sys.exit(1) - -# is this IP already up? -logger.debug(f"trying to ping {ip.ip} from this host") -if ping(str(ip.ip)): - sys.exit(1) - -# connect ourselves to the incus daemon -try: - client = pylxd.Client(endpoint="/var/lib/incus/unix.socket") -except: # noqa: E722 - logger.error("could not connect to the local incus daemon") # noqa: TRY400 - sys.exit(1) - -# check if the container already exists -logger.debug(f"check if a container with the name {args.name} already exists...") -if check_container_exists(args.name): - sys.exit(1) - -# check if the storage pool exists -logger.debug(f'checking if the storage pool "{args.pool}" exists') -if client.storage_pools.exists(args.pool): - logger.debug(f'the storage pool "{args.pool}" exists') -else: - logger.error(f'the storage pool "{args.pool}" does not exists!') - sys.exit(1) - -# check if the network profile already exists -logger.debug("check if the network profile exists (it cannot be created automatically to avoid mistakes)") -try: - client.profiles.get(args.network) - logger.debug(f"network profile {args.network} already exists") -except: # noqa: E722 - logger.error( # noqa: TRY400 - f"network profile {args.network} does not exists, are you sure you that you did specify a correct profile name?" - ) - sys.exit(1) - -profile_cpu = "cpu-" + str(args.cpu) + "-cores" -profile_memory = f"mem-{args.memory}GiB" - -try: - client.profiles.get(profile_cpu) - logger.debug("profile " + profile_cpu + " already exists") -except: # noqa: E722 - logger.info("creating profile " + profile_cpu) - profile_cpu_config = {"limits.cpu": str(args.cpu)} - client.profiles.create(profile_cpu, config=profile_cpu_config) - -try: - client.profiles.get(profile_memory) - logger.debug("profile " + profile_memory + " already exists") -except: # noqa: E722 - logger.info("creating profile " + profile_memory) - profile_memory_config = {"limits.memory": f"{args.memory}GiB"} - client.profiles.create(profile_memory, config=profile_memory_config) - -profile_list = [args.network, profile_cpu, profile_memory] -device_list = {"root": {"path": "/", "pool": args.pool, "type": "disk", "size": f"{args.quota}GiB"}} -logger.debug(f"profile_list = profile_list") # noqa: F541 - -logger.debug('IT-8735: adding security.nesting: "true" on every containers') - -config = { - "name": args.name, - "source": { - "type": "image", - "alias": args.distrib, - "mode": "pull", - "server": "https://images.linuxcontainers.org", - "protocol": "simplestreams", - }, - "profiles": profile_list, - "devices": device_list, - "config": {"security.nesting": "true"}, -} - -logger.info("creating container " + args.name) -container = client.instances.create(config, wait=True) -logger.info("container created!") - -# IT-8895 - define LXC systemd override file location -lxc_override_file = "/etc/systemd/system/service.d/lxc.conf" -logger.info(f"removing {lxc_override_file} if it exists - see IT-8895") -try: - container.files.delete(lxc_override_file) - logger.info(f"deleted {lxc_override_file}") -except Exception as e: # noqa: F841 - logger.info(f"{lxc_override_file} did not exist") - -# find the interface name for the network profile: -profile = client.profiles.get(args.network) -interface_name = list(profile.devices.keys())[0] # noqa: RUF015 - -if args.gateway is None: - gateway = str(list(ip.network.hosts())[-1]) - logger.debug("defining the gateway with the last IP of the network: " + gateway) -else: - gateway = args.gateway.compressed - logger.debug("using manually defined gateway " + gateway) - -# create the network config -if args.networkmethod == "networkd": - network_config_path = "/etc/systemd/network/10-autogenerated.network" - network_config_content = ( - "[Match]\nName=" - + interface_name - + "\n[Network]\nLinkLocalAddressing=no\nAddress=" - + str(ip) - + "\nGateway=" - + gateway - + "\n" - ) -elif args.networkmethod == "interfaces": - network_config_path = "/etc/network/interfaces" - network_config_content = ( - "# loopback\nauto lo\niface lo inet loopback\n\nauto " - + interface_name - + "\niface " - + interface_name - + " inet static\n\taddress " - + str(ip.ip) - + "\n\tnetmask " - + str(ip.netmask) - + "\n\tgateway " - + gateway - + "\n" - ) -logger.debug("network config:\n" + network_config_content) -# send the network config -container.files.put(network_config_path, network_config_content) - -# path and content of the apt configuration to not install the recommended packages by default -no_recommends_path = "/etc/apt/apt.conf.d/no_recommends.conf" -no_recommends_content = 'APT::Install-Recommends "false";\n' -container.files.put(no_recommends_path, no_recommends_content) - -# if a proxy was defined, add the apt configuration for it -if args.proxy != "no": - apt_proxy_configuration_path = "/etc/apt/apt.conf.d/proxy.conf" - apt_proxy_configuration_content = ( - 'Acquire {\n HTTP::proxy "http://' + args.proxy + '";\n HTTPS::proxy "http://' + args.proxy + '";\n}\n' - ) - container.files.put(apt_proxy_configuration_path, apt_proxy_configuration_content) - -logger.info("starting the container...") -container.start() -wait(5) -logger.info("container started") -# execute all the needed chmod -container.execute(["chmod", "644", network_config_path]) -container.execute(["chmod", "644", no_recommends_path]) -if args.proxy != "no": - container.execute(["chmod", "644", apt_proxy_configuration_path]) - -# purge netplan.io -logger.info("apt purge -y netplan.io") -RESULT = container.execute(["apt-get", "purge", "-y", "netplan.io"]) -logger.debug(str(RESULT)) -wait(1) - -# stop and disable systemd-resolved -logger.info("systemctl stop systemd-resolved.service") -RESULT = container.execute(["systemctl", "stop", "systemd-resolved.service"]) -logger.debug(str(RESULT)) -wait(2) -logger.info("systemctl disable systemd-resolved.service") -RESULT = container.execute(["systemctl", "disable", "systemd-resolved.service"]) -logger.debug(str(RESULT)) -wait(2) - -# handle resolv.conf -resolvconf_path = "/etc/resolv.conf" -if args.dns is None: - resolvconf_content = open(resolvconf_path, "r").read() # noqa: SIM115, PTH123, UP015 -else: - resolvconf_content = "# initial configuration\n" - for dns in args.dns: - resolvconf_content = resolvconf_content + "nameserver " + str(dns) + "\n" - -logger.info(f"unlink {resolvconf_path}") -container.execute(["unlink", resolvconf_path]) -logger.debug("resolvconf_content:\n" + resolvconf_content) -logger.info(f"sending {resolvconf_path}") -container.files.put(resolvconf_path, resolvconf_content) -container.execute(["chmod", "644", resolvconf_path]) -logger.info("waiting 3 seconds to allow the system to take into account the DNS change...") -wait(3) - -if args.networkmethod == "networkd": - # start and enable systemd-networkd - logger.info("systemctl enable systemd-networkd") - RESULT = container.execute(["systemctl", "enable", "systemd-networkd"]) - logger.debug(str(RESULT)) - logger.info("removing default config file /etc/systemd/network/eth0.network") - container.execute(["unlink", "/etc/systemd/network/eth0.network"]) - wait(1) - logger.info("systemctl restart systemd-networkd") - RESULT = container.execute(["systemctl", "restart", "systemd-networkd"]) - logger.debug(str(RESULT)) - wait(2) - -# handle authorized_keys -logger.info("mkdir /root/.ssh") -container.execute(["mkdir", "/root/.ssh"]) - -authorized_keys_path = "/root/.ssh/authorized_keys" -authorized_keys_content = open(authorized_keys_path, "r").read() # noqa: SIM115, PTH123, UP015 -logger.info("sending " + authorized_keys_path) -container.files.put(authorized_keys_path, authorized_keys_content) - -# install openssh -logger.info("apt update") -RESULT = container.execute(["apt", "update"]) -logger.debug(str(RESULT)) -wait(3) -logger.info("apt install -y openssh-server python3") -RESULT = container.execute(["apt", "install", "-y", "openssh-server", "python3"]) -logger.debug(str(RESULT)) - -# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/ansible/roles/incus/files/zabbix_incus.conf b/ansible/roles/incus/files/zabbix_incus.conf deleted file mode 100644 index 81ae54e2c..000000000 --- a/ansible/roles/incus/files/zabbix_incus.conf +++ /dev/null @@ -1,16 +0,0 @@ -# {{ ansible_managed }} -# incus monitoring using API and command line -UserParameter=incus.container.discovery,sudo /usr/bin/incus list --format csv -c n,s 2>/dev/null|grep RUNNING| cut -d',' -f1 | /bin/sed -e '$ ! s/\(.*\)/{"{{ '{' }}#CONTAINER}":"\1"},/' | /bin/sed -e '$ s/\(.*\)/{"{{ '{' }}#CONTAINER}":"\1"}]}/' | /bin/sed -e '1 i { \"data\":[' - -# those are generic and allow the query of arbitrary values, those 2 userparameters are legacy -UserParameter=incus.container.config[*],sudo /usr/bin/incus list $0 --format json 2>/dev/null| jq '.[0].expanded_config."$2"' -UserParameter=incus.container.counter[*],sudo curl --silent --unix-socket /var/lib/incus/unix.socket a/1.0/instances/$1/state | jq '.metadata.$2' -# BEGIN cgroup v2 metrics -UserParameter=incus.container.cpu_usage[*],echo "$(cat /sys/fs/cgroup/lxc.payload.$1/cpu.stat |grep usage_usec| cut -d' ' -f2)000" -UserParameter=incus.container.memory[*],cat /sys/fs/cgroup/lxc.payload.$1/memory.current -UserParameter=incus.container.config.memory_limit[*],cat /sys/fs/cgroup/lxc.payload.$1/memory.max -UserParameter=incus.container.processes[*],cat /sys/fs/cgroup/lxc.payload.$1/pids.current -# END cgroup v2 metrics - -# network interface traffic merged -UserParameter=incus.container.network.all_interfaces[*],echo $(($(sudo curl --silent --unix-socket /var/lib/incus/unix.socket a/1.0/instances/$1/state | jq '.metadata.network |del (.lo)'| jq '.[].counters.$2'| tr '\n' '+'| sed "s/\+$/\n/"))) diff --git a/ansible/roles/incus/meta/main.yml b/ansible/roles/incus/meta/main.yml deleted file mode 100644 index 5d21277ec..000000000 --- a/ansible/roles/incus/meta/main.yml +++ /dev/null @@ -1,10 +0,0 @@ ---- -# file: roles/incus/meta/main.yml - -dependencies: - - role: ovn - when: ovn_cluster_name is defined - - role: zabbix_template_assignment - zabbix_template_assignment_list: - - zabbix_name: talas incus - user_parameter: incus diff --git a/ansible/roles/incus/readme.md b/ansible/roles/incus/readme.md deleted file mode 100644 index dd7163de4..000000000 --- a/ansible/roles/incus/readme.md +++ /dev/null @@ -1,96 +0,0 @@ -# incus role - - -* [incus role](#incus-role) - * [Variable reference](#variable-reference) - * [Mandatory variables](#mandatory-variables) - * [Optional variables](#optional-variables) - * [incus_cluster tips](#incus_cluster-tips) - * [incus_subuid_list and incus_subgid_list: mount directory from host to container with the host uid/gid](#incus_subuid_list-and-incus_subgid_list-mount-directory-from-host-to-container-with-the-host-uidgid) -* [Misc](#misc) - * [Incus logs](#incus-logs) - * [Detection of unconfigured incus](#detection-of-unconfigured-incus) - - -## Variable reference - -### Mandatory variables - -| Variable | Description | Example value | -| -------- | ----------- | ------------- | ------ | -| incus_repository | name of the zabbly incus repo to use | lts-6.0 | - -### Optional variables - -| Variable | Description | Default value | Example value | -|----------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------| -| incus_version | inucs specific version to pin, only useful in a cluster configuration ; [a minor upgrade can block the cluster](https://linuxcontainers.org/incus/docs/main/howto/cluster_manage/#upgrade-cluster-members). Use `--extra-vars "incus_update_now=true"` to upgrade cluster members. | | | -| incus_trust_list | list of servers name authrorized to have their certificate added in incus trust config | None | {{ backup_server_list + [ 'srv-379' ] }} | -| incus_zfs_backend | create a zfs dataset for incus | true | false | -| incus_zfs_root_dataset | the zfs dataset to create for incus | nvme/incus | tank/incus | -| incus_standard_profiles | list of all default profiles | Look in the `defaults/main.yml` file for the standard configuration | see `defaults/main.yml` | -| incus_subuid_list | grand access to specific uid for incus | None | ['5001'] | -| incus_subgid_list | grand access to specific gid for incus | None | ['5000'] | -| incus_cluster_name | Set this variable if you want the host to be part of an incus cluster. Must be defined in a group_vars and have the same name that this group_vars | None | th3_core | -| incus_cluster_main_name | ansible_hostname of the incus cluster main server | None | srv-203 | -| incus_cluster_init_member_config | member_config list of the admin init preseed file. The list of the keys needed is cluster specific but the value is not necessarily the same on each member.
It can be obtained with the command `incus query /1.0/cluster` | None | incus_cluster_init_member_config: \|
entity: storage-pool
name: default
key: driver
value: zfs | -| incus_cluster_scheduler | Per member optional cluster configuration to restrict automatic placement of instances https://linuxcontainers.org/incus/docs/main/explanation/clustering/#clustering-instance-placement | None | group | -| incus_ip | the ip on which incus listen, without CIDR | None | 10.24.10.10 | -| incus_bgp_asn | Private AS number used by the incus bgp daemon. If not set incus bgp daemon will not be activated | None | `65024` | - -#### incus_cluster tips - -The easiest way to set `incus_cluster_name` `incus_cluster_main_name` and `incus_ip` if you are willing to use OVN on this cluster is by respectivly them to the corresponding vars in ovn: -incus_cluster_name: {{ ovn_cluster_name }} -incus_cluster_main_name: {{ ovn_cluster_main_name }} -incus_ip: {{ ovn_ip }} - -#### incus_subuid_list and incus_subgid_list: mount directory from host to container with the host uid/gid - -Sometime, you need to give access to a directory to a container without remapping its uid/gid. - -To do that, you must modify the system to grand access to specific uid/gid for incus and then modify the container to handle this specific case. - -To grand access to specific uid/gid for incus set incus_subuid_list and incus_subgid_list on the host. - -Then for the incus container, you must add the following setting: - -``` -echo -en "uid 5001 5001\ngid 5000 5000" | incus config set container_name raw.idmap - -``` - -A container restart is necessary to apply the change, it cannot be done live. - -Then add the directory to the container storage, for instance: - -``` -devices: - 01OPTI01370: - path: /srv/vaults/01OPTI01370 - source: /srv/vaults/01OPTI01370 - type: disk -``` - -You will then be able to access the directory inside the container with the same uid/gid as the host. - -# Misc - -## Incus logs - -Log incus daemon: - -``` -/var/log/incus/incusd.log -``` - -Log container incus: - -``` -/var/log/incus/CONTAINER_NAME -``` - -## Detection of unconfigured incus - -The `incus_yaml_unconfigured` variable is defined in `defaults/main.yml`, it contains the unconfigured state of a incus daemon just after installation, you should not have to change this, the variable exists to handle future incus version that may have a different default configuration. - -The `incus_standalone_init_yaml ` variable is also defined in `defaults/main.yml`, it contains the initial configuration that must override the default above, you only need to change this if your default storage pool doesn't use zfs. diff --git a/ansible/roles/incus/tasks/cluster.yml b/ansible/roles/incus/tasks/cluster.yml deleted file mode 100644 index b756cb5fc..000000000 --- a/ansible/roles/incus/tasks/cluster.yml +++ /dev/null @@ -1,158 +0,0 @@ ---- -# file: roles/incus/tasks/cluster.yml - -- name: "set facts incus_cluster_main_ip" - ansible.builtin.set_fact: - incus_cluster_main_ip: "{{ hostvars[ovn_cluster_main_name]['incus_ip'] }}" - -- name: "Set ovn_connection_type" - ansible.builtin.set_fact: - ovn_connection_type: "{{ ovn_ssl | ternary('ssl', 'tcp') }}" - -- name: "set facts ovn_central bounds lists" - ansible.builtin.set_fact: - incus_ovn_northbound_list: "{{ (incus_ovn_northbound_list | default([])) + [ovn_connection_type + ':' + hostvars[item]['ovn_ip'] + ':6641'] }}" - loop: "{{ ovn_central_servers }}" - -- name: "set facts ovn_central bounds" - ansible.builtin.set_fact: - incus_ovn_northbound: "{{ incus_ovn_northbound_list | join(',') }}" - -- name: "set facts ovn_ic bounds lists" - ansible.builtin.set_fact: - incus_ovn_ic_northbound_list: "{{ (incus_ovn_ic_northbound_list | default([])) + [ovn_connection_type + ':' + hostvars[item]['ovn_ip'] + ':6645'] }}" - incus_ovn_ic_southbound_list: "{{ (incus_ovn_ic_southbound_list | default([])) + [ovn_connection_type + ':' + hostvars[item]['ovn_ip'] + ':6646'] }}" - loop: "{{ ovn_ic_db_servers }}" - when: - - ovn_ic_db_servers is defined - - ansible_hostname == incus_cluster_main_name - -- name: "set facts ovn_ic bounds" - ansible.builtin.set_fact: - incus_ovn_ic_northbound: "{{ incus_ovn_ic_northbound_list | join(',') }}" - incus_ovn_ic_southbound: "{{ incus_ovn_ic_southbound_list | join(',') }}" - when: - - ovn_ic_db_servers is defined - - ansible_hostname == incus_cluster_main_name - -- name: "Get {{ incus_cluster_name }} secrets from hashicorp vault" - ansible.builtin.set_fact: - incus_hv_secrets: "{{ lookup('community.hashi_vault.vault_kv2_get', 'group_vars/' + incus_cluster_name, engine_mount_point='talas-kv') }}" - when: ovn_ssl - -- name: "Extract cert and private key from hashicorp vault" - ansible.builtin.set_fact: - incus_client_cert_serial_number: "{{ incus_hv_secrets.secret.ovn_cert_incus_client_serial_number }}" - incus_client_cert_private_key: "{{ incus_hv_secrets.secret.ovn_cert_incus_client_private_key }}" - when: ovn_ssl - -- name: "Get client cert and issuing certificates from hashicorp vault pki" - ansible.builtin.set_fact: - incus_client_cert_issuing_ca_chain: "{{ lookup('community.hashi_vault.vault_read', 'pki/issuer/OVN') | community.general.json_query('data.ca_chain') | join() | trim }}" - incus_client_cert_ca: "{{ lookup('community.hashi_vault.vault_read', 'pki/cert/' + incus_client_cert_serial_number) | community.general.json_query('data.certificate') }}" - when: ovn_ssl - -- name: "init preseed if first cluster main install" - ansible.builtin.shell: - cmd: /usr/bin/incus admin init --preseed - stdin: "{{ incus_cluster_main_init_yaml }}" - stdin_add_newline: true - when: - - incus_admin_init_dump.stdout == incus_yaml_unconfigured - - ansible_hostname == incus_cluster_main_name - -# https://tracker.talas.com/browse/INFRA-179 -#- name: "get and set OVN configuration WITH TLS" -# ansible.builtin.include_tasks: get_and_set.yml -# loop: -# - { section: config, key: network.ovn.northbound_connection, value: "{{ incus_cluster_ovn_conf.northbound_connection }}" } -# - { section: config, key: network.ovn.client_cert, value: "{{ incus_cluster_ovn_conf.client_cert }}" } -# - { section: config, key: network.ovn.client_key, value: "{{ incus_cluster_ovn_conf.client_key }}" } -# - { section: config, key: network.ovn.ca_cert, value: "{{ incus_cluster_ovn_conf.ca_cert }}" } -# when: -# - ovn_ssl -# - ansible_hostname == incus_cluster_main_name - -- name: "check if ovn_ic_integration exists" - ansible.builtin.command: "/usr/bin/incus network integration show ovn_ic_integration" - register: get_ovn_ic_integration - changed_when: false - check_mode: false - failed_when: false - when: - - ovn_ssl - - ovn_ic_db_servers is defined - - ansible_hostname == incus_cluster_main_name - -- name: "incus network integration create ovn_ic_integration" - ansible.builtin.command: "/usr/bin/incus network integration create ovn_ic_integration ovn" - when: - - ovn_ssl - - ovn_ic_db_servers is defined - - ansible_hostname == incus_cluster_main_name - - get_ovn_ic_integration.rc != 0 - -- name: "get and set OVN IC integration configuration WITH TLS" - ansible.builtin.include_tasks: get_and_set.yml - loop: - - { section: "network integration", object: ovn_ic_integration, key: ovn.northbound_connection, value: "{{ incus_ovn_ic_northbound }}" } - - { section: "network integration", object: ovn_ic_integration, key: ovn.southbound_connection, value: "{{ incus_ovn_ic_southbound }}" } - - { section: "network integration", object: ovn_ic_integration, key: ovn.ca_cert, value: "{{ incus_cluster_ovn_conf.ca_cert }}" } - - { section: "network integration", object: ovn_ic_integration, key: ovn.client_cert, value: "{{ incus_cluster_ovn_conf.client_cert }}" } - - { section: "network integration", object: ovn_ic_integration, key: ovn.client_key, value: "{{ incus_cluster_ovn_conf.client_key }}" } - when: - - ovn_ssl - - ovn_ic_db_servers is defined - - ansible_hostname == incus_cluster_main_name - -- name: "get IC integration transit pattern" - ansible.builtin.shell: incus network integration get ovn_ic_integration ovn.transit.pattern - register: get_transit_pattern - changed_when: false - check_mode: false - when: - - ovn_ic_db_servers is defined - - ansible_hostname == incus_cluster_main_name - -- name: "/usr/bin/incus network integration set ovn_ic_integration ovn.transit.pattern" - ansible.builtin.shell: "/usr/bin/incus network integration set ovn_ic_integration ovn.transit.pattern {% raw %}'ts-incus-{{ integrationName }}-{{ peerName }}'{% endraw %}" - when: - - ovn_ic_db_servers is defined - - ansible_hostname == incus_cluster_main_name - - get_transit_pattern.stdout != {% raw %}'ts-incus-{{ integrationName }}-{{ peerName }}'{% endraw %} - -- name: "get and set OVN configuration WITHOUT TLS" - ansible.builtin.include_tasks: get_and_set.yml - loop: - - { section: config, key: "network.ovn.northbound_connection", value: "{{ incus_cluster_ovn_conf.northbound_connection }}" } - when: - - not ovn_ssl - - ansible_hostname == incus_cluster_main_name - -- name: "Create join tokens" - throttle: 1 - delegate_to: "{{ incus_cluster_main_name }}" - vars: - ansible_python_interpreter: "{{ hostvars[incus_cluster_main_name].ansible_python_interpreter | default('/usr/bin/python3') }}" - ansible.builtin.shell: - cmd: "incus --force-local --quiet cluster add {{ inventory_hostname }}" - register: incus_cluster_add - when: - - incus_admin_init_dump.stdout == incus_yaml_unconfigured - - ansible_hostname != incus_cluster_main_name - -- name: "init preseed if first cluster secondary install" - ansible.builtin.shell: - cmd: /usr/bin/incus admin init --preseed - stdin: "{{ incus_cluster_init_yaml }}" - stdin_add_newline: true - throttle: 1 - when: - - incus_admin_init_dump.stdout == incus_yaml_unconfigured - - ansible_hostname != incus_cluster_main_name - -- name: "get and set scheduler.instance" - ansible.builtin.include_tasks: get_and_set_cluster.yml - loop: - - { key: 'scheduler.instance', value: "{{ incus_cluster_scheduler }}" } - when: incus_cluster_scheduler is defined diff --git a/ansible/roles/incus/tasks/get_and_set.yml b/ansible/roles/incus/tasks/get_and_set.yml deleted file mode 100644 index f0bdc452b..000000000 --- a/ansible/roles/incus/tasks/get_and_set.yml +++ /dev/null @@ -1,13 +0,0 @@ ---- -# file: roles/incus/tasks/get_and_set.yml - -- name: "incus {{ item.section }} get {{ item.object | default('') }} {{ item.key }}" - ansible.builtin.shell: "/usr/bin/incus {{ item.section }} get {{ item.object | default('') }} {{ item.key }}" - register: get_configuration - changed_when: false - check_mode: false - failed_when: 'get_configuration.rc != 0 and "not found" not in get_configuration.stderr' - -- name: "/usr/bin/incus {{ item.section }} set {{ item.object | default('') }} {{ item.key }}" - ansible.builtin.shell: "/usr/bin/incus {{ item.section }} set {{ item.object | default('') }} {{ item.key }} -- \"{{ item.value }}\"" - when: get_configuration.stdout != item.value diff --git a/ansible/roles/incus/tasks/get_and_set_cluster.yml b/ansible/roles/incus/tasks/get_and_set_cluster.yml deleted file mode 100644 index 287a039fa..000000000 --- a/ansible/roles/incus/tasks/get_and_set_cluster.yml +++ /dev/null @@ -1,13 +0,0 @@ ---- -# file: roles/incus/tasks/get_and_set_cluster.yml - -- name: "incus cluster get {{ ansible_hostname }} {{ item.key }}" - shell: "/usr/bin/incus cluster get {{ ansible_hostname }} {{ item.key }}" - register: get_configuration - changed_when: false - check_mode: false - failed_when: get_configuration.rc != 0 and "does not exist on cluster member" not in get_configuration.stderr - -- name: "incus cluster set {{ ansible_hostname }} set {{ item.key }}" - shell: "/usr/bin/incus cluster set {{ ansible_hostname }} {{ item.key }}={{ item.value }}" - when: get_configuration.stdout != item.value diff --git a/ansible/roles/incus/tasks/main.yml b/ansible/roles/incus/tasks/main.yml deleted file mode 100644 index 27d9e575a..000000000 --- a/ansible/roles/incus/tasks/main.yml +++ /dev/null @@ -1,174 +0,0 @@ ---- -# file: roles/incus/tasks/main.yml - -- name: "/etc/apt/sources.list.d/zabbly.sources" - ansible.builtin.deb822_repository: - name: zabbly - types: deb - uris: "https://pkgs.zabbly.com/incus/{{ incus_repository }}" - suites: "{{ ansible_distribution_release }}" - components: main - architectures: amd64 - signed_by: https://pkgs.zabbly.com/key.asc - register: incus_repository_out - tags: incus - -- name: "apt pin incus version" - ansible.builtin.copy: - content: | - Package: incus* - Pin: version {{ incus_version_epoch }}:{{ incus_version }}* - Pin-Priority: 999 - dest: "/etc/apt/preferences.d/incus" - register: incus_pin_out - when: incus_version is defined - tags: incus - -- name: "apt update" - ansible.builtin.apt: - update_cache: true - when: incus_repository_out.changed or incus_pin_out.changed - tags: incus - -- name: "install needed packages: bridge-utils and ifenslave for the network, jq/curl for monitoring and apparmor for security" - ansible.builtin.apt: - name: - - bridge-utils - - ifenslave - - apparmor - - curl - tags: incus - -- name: "install incus (and upgrade if incus_version is defined and incus_update_now)" - ansible.builtin.apt: - name: - - incus - state: "{{ 'latest' if incus_version is defined and incus_update_now else 'present' }}" - tags: incus - -- name: "/etc/sysctl.conf tunable that should alway be set" - ansible.posix.sysctl: - name: "{{ item['name'] }}" - value: "{{ item['value'] }}" - loop: - - { 'name': 'fs.aio-max-nr', 'value': '524288' } - - { 'name': 'fs.inotify.max_queued_events', 'value': '1048576' } - - { 'name': 'fs.inotify.max_user_instances', 'value': '1048576' } - - { 'name': 'fs.inotify.max_user_watches', 'value': '1048576' } - - { 'name': 'kernel.dmesg_restrict', 'value': '1' } - - { 'name': 'kernel.keys.maxbytes', 'value': '2000000' } - - { 'name': 'kernel.keys.maxkeys', 'value': '2000' } - - { 'name': 'vm.max_map_count', 'value': '262144' } - - { 'name': 'net.core.bpf_jit_limit', 'value': '1000000000' } - - { 'name': 'net.ipv4.neigh.default.gc_thresh3', 'value': '8192' } - - { 'name': 'net.ipv6.neigh.default.gc_thresh3', 'value': '8192' } - tags: - - incus - - sysctl - -- name: "incus dataset" - community.general.zfs: - name: "{{ incus_zfs_root_dataset }}" - state: present - extra_zfs_properties: - mountpoint: legacy - when: incus_zfs_backend - tags: incus - -- name: "set default incus_ip" - ansible.builtin.set_fact: - incus_ip: "[::]" - when: incus_ip is not defined - tags: incus - -- name: "dump current admin init" - ansible.builtin.command: "/usr/bin/incus admin init --dump" - register: incus_admin_init_dump - changed_when: false - check_mode: false - tags: incus - -- name: "display current incus configuration" - ansible.builtin.debug: - var: incus_admin_init_dump.stdout - verbosity: 1 - tags: incus - -- name: "set configuration if first standalone install" - ansible.builtin.shell: 'echo "{{ incus_standalone_init_yaml }}" | /usr/bin/incus admin init --preseed' - when: - - incus_admin_init_dump.stdout == incus_yaml_unconfigured - - incus_cluster_name is not defined - tags: incus - -- name: "install cluster" - ansible.builtin.import_tasks: cluster.yml - when: incus_cluster_name is defined - tags: incus - -- name: "incus profiles" - ansible.builtin.import_tasks: profiles.yml - when: incus_cluster_name is not defined - tags: - - incus - - incus_profiles - -- name: "administration scripts" - ansible.builtin.import_tasks: scripts.yml - tags: - - incus - - incus_scripts - -- name: "handle /etc/subuid" - ansible.builtin.lineinfile: - path: "/etc/subuid" - line: "root:{{ item }}:1" - loop: "{{ incus_subuid_list }}" - when: incus_subuid_list is defined - tags: incus - -- name: "handle /etc/subgid" - ansible.builtin.lineinfile: - path: "/etc/subgid" - line: "root:{{ item }}:1" - loop: "{{ incus_subgid_list }}" - when: incus_subgid_list is defined - tags: incus - -- name: "get and set https_address" - ansible.builtin.include_tasks: - file: get_and_set.yml - apply: - tags: - - incus - loop: - - { section: config, key: "core.https_address", value: "{{ incus_ip }}:8443" } - tags: incus - -- name: "get and set core.bgp_*" - ansible.builtin.include_tasks: - file: get_and_set.yml - apply: - tags: - - incus - loop: - - { section: config, key: "core.bgp_address", value: "{{ incus_ip }}:179" } - - { section: config, key: "core.bgp_asn", value: "{{ incus_bgp_asn }}" } - - { section: config, key: "core.bgp_routerid", value: "{{ incus_ip }}" } - when: incus_bgp_asn is defined - tags: incus - -- name: "incus trust configuration via openssl certificates" - ansible.builtin.import_tasks: trust.yml - when: - - incus_trust_list is defined - - incus_cluster_name is not defined or (incus_cluster_name is defined and ansible_hostname == incus_cluster_main_name) - tags: - - incus - - incus_trust - -- name: "cosinfo" - ansible.builtin.import_tasks: cosinfo.yml - tags: - - incus - - cosinfo diff --git a/ansible/roles/incus/tasks/profiles.yml b/ansible/roles/incus/tasks/profiles.yml deleted file mode 100644 index 20391d550..000000000 --- a/ansible/roles/incus/tasks/profiles.yml +++ /dev/null @@ -1,28 +0,0 @@ ---- -# file: roles/incus/tasks/profiles.yml - -- name: "incus 'default' profile" - community.general.lxd_profile: - url: "unix:/var/lib/incus/unix.socket" - name: default - devices: - root: - path: / - pool: default - type: disk - -- name: "standard profiles" - community.general.lxd_profile: - url: "unix:/var/lib/incus/unix.socket" - name: "{{ item['name'] }}" - config: "{{ item['config'] }}" - loop: "{{ incus_standard_profiles }}" - when: incus_standard_profiles is defined - -- name: "network profiles" - community.general.lxd_profile: - url: "unix:/var/lib/incus/unix.socket" - name: "{{ item['name'] }}" - devices: "{{ item['devices'] }}" - loop: "{{ incus_network_profiles }}" - when: incus_network_profiles is defined diff --git a/ansible/roles/incus/tasks/scripts.yml b/ansible/roles/incus/tasks/scripts.yml deleted file mode 100644 index 3ace2611d..000000000 --- a/ansible/roles/incus/tasks/scripts.yml +++ /dev/null @@ -1,33 +0,0 @@ ---- -# file: roles/incus/tasks/scripts.yml - -- name: "pip upgrade pip in virtualenv" - ansible.builtin.pip: - name: pip - extra_args: --upgrade - virtualenv: /usr/local/venvs/init_container - virtualenv_command: /usr/bin/python3 -m venv - when: not ansible_check_mode - -- name: "pip install dependencies in virtualenv" - ansible.builtin.pip: - name: - - "urllib3{{ general_urllib3_version }}" - - "pylxd=={{ general_pylxd_version }}" - extra_args: --upgrade - virtualenv: /usr/local/venvs/init_container - virtualenv_command: /usr/bin/python3 -m venv - when: not ansible_check_mode - -- name: "/usr/local/venvs/init_container/bin/init_container.py" - ansible.builtin.copy: - src: init_container.py - dest: /usr/local/venvs/init_container/bin/init_container.py - mode: 0755 - -- name: "ln -s /usr/local/venvs/init_container/bin/init_container.py /usr/local/sbin/init_container.py" - ansible.builtin.file: - state: link - src: /usr/local/venvs/init_container/bin/init_container.py - dest: /usr/local/sbin/init_container.py - force: true diff --git a/ansible/roles/incus/tasks/trust.yml b/ansible/roles/incus/tasks/trust.yml deleted file mode 100644 index 209b3aa72..000000000 --- a/ansible/roles/incus/tasks/trust.yml +++ /dev/null @@ -1,19 +0,0 @@ ---- -# file: roles/incus/tasks/trust.yml - -- name: "/usr/local/etc/tls/incus_trust/" - ansible.builtin.file: - path: "/usr/local/etc/tls/incus_trust/" - state: directory - -- name: "local copy of certificate for trusted hosts" - ansible.builtin.copy: - content: "{{ lookup('hashi_vault', 'secret=talas-kv/data/' + host_vars_location + '/' + item)['alexandria_crt'] }}" - dest: "/usr/local/etc/tls/incus_trust/{{ item }}.crt" - loop: "{{ incus_trust_list }}" - register: certificate_copy - -- name: "add trusted host" - ansible.builtin.command: "incus config trust add-certificate /usr/local/etc/tls/incus_trust/{{ item.item }}.crt" - when: item.changed - loop: "{{ certificate_copy.results }}" diff --git a/ansible/roles/minio/defaults/main.yml b/ansible/roles/minio/defaults/main.yml deleted file mode 100644 index 6bbc5e05b..000000000 --- a/ansible/roles/minio/defaults/main.yml +++ /dev/null @@ -1,33 +0,0 @@ ---- -# file: roles/minio/defaults/main.yml - -minio_version: "latest" -minio_update_now: false -minio_haproxy: true - -minio_restart_on_auth_type_change: false - -haproxy_frontend_list: - - name: "minio" - mode: "http" - use_backend: "minio" - bind_list: - - ":9000,:::9000 v6only ssl crt /usr/local/etc/tls/haproxy alpn h2,http/1.1" - -haproxy_frontend: - default_backend: "minio-console" - -haproxy_backend: - - name: "minio-console" - server: - - name: "minio-console" - fqdn: "127.0.0.1" - port: "9001" - - name: "minio" - server: - - name: "minio" - fqdn: "127.0.0.1" - port: "9002" - -haproxy_https_monitoring: - - "{{ minio_fqdn }}" diff --git a/ansible/roles/minio/files/zabbix_minio.conf b/ansible/roles/minio/files/zabbix_minio.conf deleted file mode 100644 index 4c40bd9e1..000000000 --- a/ansible/roles/minio/files/zabbix_minio.conf +++ /dev/null @@ -1,12 +0,0 @@ -# check for minio - -# $1 : zabbix macro {$MINIO_ALIAS} -UserParameter=minio.prometheus_cluster[*],/usr/bin/sudo /usr/local/bin/mcli admin prometheus metrics $1 cluster -UserParameter=minio.prometheus_node[*],/usr/bin/sudo /usr/local/bin/mcli admin prometheus metrics $1 node -UserParameter=minio.prometheus_bucket[*],/usr/bin/sudo /usr/local/bin/mcli admin prometheus metrics $1 bucket -UserParameter=minio.prometheus_resource[*],/usr/bin/sudo /usr/local/bin/mcli admin prometheus metrics $1 resource - -# $1 : zabbix macro {$MINIO_HOST} -# $2 : zabbix macro {$MINIO_PORT} -# Health check API https://min.io/docs/minio/linux/operations/monitoring/healthcheck-probe.html -UserParameter=minio.ping[*],/usr/bin/curl -s -o /dev/null --write-out '%{http_code}' -I http://$1:$2/minio/health/live diff --git a/ansible/roles/minio/handlers/main.yml b/ansible/roles/minio/handlers/main.yml deleted file mode 100644 index bf43e0842..000000000 --- a/ansible/roles/minio/handlers/main.yml +++ /dev/null @@ -1,11 +0,0 @@ ---- -# file: roles/minio/handlers/main.yml - -- name: "reload daemon" - ansible.builtin.systemd: - daemon_reload: true - -- name: "restart minio" - ansible.builtin.systemd: - name: minio - state: restarted diff --git a/ansible/roles/minio/meta/main.yml b/ansible/roles/minio/meta/main.yml deleted file mode 100644 index 47ab4b989..000000000 --- a/ansible/roles/minio/meta/main.yml +++ /dev/null @@ -1,13 +0,0 @@ ---- -# file: roles/minio/meta/main.yml - -dependencies: - - role: minio_client - - role: haproxy - when: minio_haproxy - - role: zabbix_template_assignment - zabbix_template_assignment_list: - - zabbix_name: talas Minio Node - user_parameter: minio - zabbix_template_assignment_systemd_list: - - minio diff --git a/ansible/roles/minio/readme.md b/ansible/roles/minio/readme.md deleted file mode 100644 index 239cf8b1e..000000000 --- a/ansible/roles/minio/readme.md +++ /dev/null @@ -1,135 +0,0 @@ -# minio role - -Install a single minio instance with: -* a single directory as volume ("/srv/minio") -* HAproxy as reverse-proxy - - -* [minio role](#minio-role) - * [Server management](#server-management) - * [Admin user](#admin-user) - * [Update](#update) - * [Variable reference](#variable-reference) - * [Mandatory variables](#mandatory-variables) - * [Optional variables](#optional-variables) - * [Complexe variables examples](#complexe-variables-examples) -* [AUTH_TYPE](#auth_type) - * [Apply change via restart](#apply-change-via-restart) - * [LOCAL](#local) - * [LDAP](#ldap) - * [Monitoring zabbix](#monitoring-zabbix) - - -## Server management - -There are two ways to connect to the server: -* the webui available on port 443 at `minio_fqdn` -* install the cli tool [minio client](https://min.io/docs/minio/linux/reference/minio-mc.html) on your PC. - -In both case you will need thte admin username is `minioadmin` and its password is in hashicorp vault `minio_root_password`. - -### Admin user - -The admin username is `minioadmin` and the password is hashicorp vault `minio_root_password`. - -## Update - -**WARNING:** This role automatically restart the minio systemd service after installing/updating the package. Therefore you should NOT use this role as it is to update a cluster, as cluster nodes have to be restarted together using the command `mc admin service restart`. - -To update it, please pass `--extra-vars "{ 'minio_update_now': true }"` as parameter - -## Variable reference - -### Mandatory variables - -| Variable | Description | Example value | -|-----------------|-----------------------------|--------------------------------------------------| -| minio_auth_type | minio authentification type | `ldap` or `local` | - -### Optional variables - -| Variable | Description | Default value | -|--------------------------|----------------------------------------------------------------|--------------------------------------------------| -| minio_haproxy | install a HAproxy with a TLS cert and enable the webui | `true` | -| minio_fqdn | minio fqdn (only necessary when `minio_haproxy` is enable | `veza-storage-prod-global-1.int.talas.veza` | -| minio_version | minio version | `latest` | -| minio_buckets | list of minio buckets to create | none | -| minio_users | list of minio users to create when using local minio_auth_type | none | -| minio_bucket_policies | list of dictionnaries of per bucket policies | none | -| minio_anonymous_policies | list of dictionnaries of anonymous policies | none | -| minio_global_policies | list of dictionnaries of global policies | none | - -### Complexe variables examples - -```yaml -minio_bucket_policies: - - bucket: veza-http-product-pictures-demo-1 # Name of the bucket to apply the policy to - permissions: read-write # permissions given by this policy; supported values are `read-write` and `read-only` - users: # List of users whom you want the policy to apply to - - veza-http-product-pictures-demo-1 - groups: - - devop # List of groups whom you want the policy to apply to. Local groups -``` -If it does not exist already a policy matching the bucket name and the permission will be created by the role. -The policy will be named: `{{ bucket }}_{{ permissions }}` - -```yaml -minio_global_policies: - - policy: veza-http-product-pictures-demo-1 # Name of the policy - users: # List of users whom you want the policy to apply to - - adm-martin - - jean - groups: # List of groups whom you want the policy to apply to - - "cn=minio-admin,ou=system,ou=groups,dc=talas,dc=com" -``` -The policy must already exist in minio. By default, the next five policies already exist in minio: -* consoleAdmin -* diagnostics -* readonly -* readwrite -* writeonly - - -```yaml -minio_anonymous_policies: - - path: "myvault/dir1" # bucket name or folder/file path in a bucket - permission: "download" # Allowed policies are: private, public, download, upload. -``` - -By default all minio paths have anonymous access set to private. -To reset a policy you can either set the permission to private or in you ansible config or run the following config on the minio server `mcli anonymous set private minio_on_localhost/{{ path }}` - - -# AUTH_TYPE - -Minio does not support using multiple auth time at the same type, therefore Minio needs to be restarted at each auth_type configuration change. This even includes ldap parameters modifications. - -## Apply change via restart - -By default, this role doesn't restart the minio service to avoid a disruption. You can override this by setting this variable: - -``` -minio_restart_on_auth_type_change: true -``` -You can also add it on the command line via -``` ---extra-vars '{ "minio_restart_on_auth_type_change" : true }' -``` - -## LOCAL - -This role handle the creation of local users account but not of local groups. If needed we should be able to add it pretty easily by copiyng the tasks used to create local users. - -## LDAP - -Users and groups have to be created in the LDAP prior to the role execution. -User password must be added in Hashicorp vault hosts_var of the ansible_hostname. The password key must be match the username and be prefixed by `minio_` and suffixed by `ldappass` -App user account must be created in `ou=minio,ou=bot,ou=people,dc=talas,dc=com` -Full DN of LDAP objects must be provided to minio. -If this auth_type is used, the ldap group `minio-admin` will always be added to minio with consoleAdmin rights. - -## Monitoring zabbix - -Current configuration on zabbix server only gathers metrics for single-node deployment and it has only been tested with a single drive. -In case of cluster deployment create the items and discover rules needed for cluster monitoring. -If deploying a single node with multiple drives, check that the [latency item prototype](https://zabbix.talas.com/disc_prototypes.php?parent_discoveryid=4831493) creates one item for each drive/api couple. diff --git a/ansible/roles/minio/tasks/main.yml b/ansible/roles/minio/tasks/main.yml deleted file mode 100644 index 5a5ce959e..000000000 --- a/ansible/roles/minio/tasks/main.yml +++ /dev/null @@ -1,342 +0,0 @@ ---- -# file: roles/minio/tasks/main.yml - -- name: "create minio-user group" - ansible.builtin.group: - name: minio-user - system: true - tags: minio - -- name: "create minio-user user" - ansible.builtin.user: - name: minio-user - system: true - shell: "/usr/sbin/nologin" - tags: minio - -- name: "handle secret {{ ansible_hostname }}/minio_root_password" - block: - - name: "get {{ ansible_hostname }}/minio_root_password from hashicorp vault" - ansible.builtin.set_fact: - "minio_root_password": "{{ lookup('hashi_vault', 'secret=talas-kv/data/' + host_vars_location + '/' + ansible_hostname)['minio_root_password'] }}" - rescue: - - name: "generate a random password for {{ ansible_hostname }}/minio_root_password" - ansible.builtin.set_fact: - password: "{{ lookup('password','/dev/null chars=ascii_letters,digits length=50') }}" - - name: "patching hashicorp vault with generated minio_root_password" - ansible.builtin.command: "vault kv patch talas-kv/{{ host_vars_location }}/{{ ansible_hostname }} minio_root_password={{ password }}" - delegate_to: localhost - become: false - register: result - ignore_errors: True - - name: "patch failed because the entry doesn't exist, creating it instead" - ansible.builtin.command: "vault kv put talas-kv/{{ host_vars_location }}/{{ ansible_hostname }} minio_root_password={{ password }}" - delegate_to: localhost - become: false - when: - - result.failed - - '"No value found" in result.stderr' - - name: "assign password value to minio_root_password" - ansible.builtin.set_fact: - minio_root_password: "{{ password }}" - tags: - - minio - - minio_server - -- name: "/etc/default/minio" - ansible.builtin.template: - src: etc_default_minio.j2 - dest: /etc/default/minio - group: minio-user - register: minio_conf - tags: minio - -- name: "chown /srv/minio" - ansible.builtin.file: - path: /srv/minio - state: directory - owner: minio-user - group: minio-user - tags: minio - -- name: "import minio_server tasks" - ansible.builtin.import_tasks: minio_server.yml - when: not ansible_check_mode - tags: - - minio - - minio_server - -- name: "make sure minio is enabled and started" - ansible.builtin.systemd: - name: minio - enabled: true - state: started - tags: minio - -- name: "restart minio if conf was changed" - ansible.builtin.systemd: - name: minio - state: restarted - when: minio_conf.changed - tags: minio - -- name: "set minio_port" - ansible.builtin.set_fact: - minio_port: "{% if minio_haproxy %}9002{% else %}9000{% endif %}" - tags: - - minio - - minio_buckets - - minio_users - -- name: "handle mcli alias minio_on_localhost" - block: - - name: "mcli admin info minio_on_localhost --json" - ansible.builtin.command: "mcli admin info minio_on_localhost --json" - register: minio_info - failed_when: "'success' not in minio_info.stdout|from_json|json_query('status')" - changed_when: false - rescue: - - name: "mcli alias set minio_on_localhost http://localhost:{{ minio_port }} minioadmin" - ansible.builtin.command: "mcli alias set minio_on_localhost http://localhost:{{ minio_port }} minioadmin {{ minio_root_password }}" - tags: minio - -- name: "include minio_buckets tasks" - ansible.builtin.include_tasks: - file: minio_buckets.yml - apply: - tags: - - minio - - minio_buckets - loop: "{{ minio_buckets }}" - loop_control: - loop_var: minio_bucket - when: minio_buckets is defined - tags: - - minio - - minio_buckets - -- name: "get ldap config" - ansible.builtin.command: - cmd: mcli idp ldap info minio_on_localhost --json - register: check_ldap_config - changed_when: false - check_mode: false - tags: - - minio - - minio_users - -- name: "set up ldap connection" - ansible.builtin.command: - cmd: > - mcli idp ldap add minio_on_localhost/ --json \ - server_addr=ldap.talas.com \ - lookup_bind_dn=uid={{ ansible_hostname }},ou=servers,dc=talas,dc=com \ - lookup_bind_password={{ ldappass }} \ - user_dn_search_base_dn=ou=people,dc=talas,dc=com \ - user_dn_search_filter='(&(uid=%s)(CosStatus=active)(|(objectClass=CosAccount)(objectClass=CosHostingAccount)(objectClass=CosBot)))' - group_search_base_dn=ou=groups,dc=talas,dc=com \ - group_search_filter='(&(objectclass=posixGroup)(memberUid=%s))' - register: setup_ldap - failed_when: "'success' not in setup_ldap.stdout|from_json|json_query('status')" - when: - - minio_auth_type == "ldap" - - not check_ldap_config.stdout|from_json|json_query('info') - tags: - - minio - - minio_users - -- name: "enable ldap auth_type" - ansible.builtin.command: - cmd: mcli idp ldap enable minio_on_localhost --json - register: minio_ldap_enable - when: - - minio_auth_type == "ldap" - - check_ldap_config.stdout|from_json|json_query(json_query_request)|length>0 and check_ldap_config.stdout|from_json|json_query(json_query_request)|first != "on" - vars: - json_query_request: "info[?key=='enable'].value" - tags: - - minio - - minio_users - -- name: "disable ldap auth_type" - ansible.builtin.command: - cmd: mcli idp ldap disable minio_on_localhost --json - register: minio_ldap_disable - when: - - minio_auth_type == "local" - - check_ldap_config.stdout|from_json|json_query(json_query_request)|length>0 and check_ldap_config.stdout|from_json|json_query(json_query_request)|first == "on" - vars: - json_query_request: "info[?key=='enable'].value" - tags: - - minio - - minio_users - -- name: "restart minio if required and if minio_restart_on_auth_type_change is true" - ansible.builtin.systemd: - name: "minio.service" - state: restarted - when: - - setup_ldap is not skipped or minio_ldap_disable is not skipped or minio_ldap_enable is not skipped - - minio_restart_on_auth_type_change - tags: - - minio - - minio_users - -- name: "include minio_ldap_users tasks" - ansible.builtin.include_tasks: - file: minio_ldap_users.yml - apply: - tags: - - minio - - minio_users - loop: "{{ minio_users }}" - loop_control: - loop_var: minio_user - when: - - minio_auth_type == "ldap" - - minio_users is defined - tags: - - minio - - minio_users - -- name: "include minio_local_users tasks" - ansible.builtin.include_tasks: - file: minio_local_users.yml - apply: - tags: - - minio - - minio_users - loop: "{{ minio_users }}" - loop_control: - loop_var: minio_user - when: - - minio_users is defined - - minio_auth_type == "local" - tags: - - minio - - minio_users - -- name: "/home/minio-user/policies" - ansible.builtin.file: - path: /home/minio-user/policies - state: directory - owner: minio-user - group: minio-user - mode: 0750 - tags: - - minio - - minio_policies - -- name: "set minio_bucket_policies.policy" - ansible.builtin.set_fact: - minio_bucket_policies: "{{ (minio_bucket_policies | difference([item.1])) + ([ item.1 | combine({'policy' : item.1.bucket + '_' + item.1.permissions })]) }}" - with_indexed_items: "{{ minio_bucket_policies }}" - when: minio_bucket_policies is defined - tags: - - minio - - minio_policies - -- name: "/home/minio-user/policies/minio_policy.json" - ansible.builtin.template: - src: "minio_policy.json.j2" - dest: "/home/minio-user/policies/{{ item.policy }}.json" - backup: true - register: minio_upload_policies - loop: "{{ minio_bucket_policies }}" - when: minio_bucket_policies is defined - tags: - - minio - - minio_policies - -- name: "add changed policy {{ item.item.policy }}" - ansible.builtin.command: "mcli admin policy create minio_on_localhost {{ item.item.policy }} /home/minio-user/policies/{{ item.item.policy }}.json --json" - register: add_policy - failed_when: "'success' not in add_policy.stdout|from_json|json_query('status')" - loop: "{{ minio_upload_policies.results }}" - when: - - minio_bucket_policies is defined - - item.changed - tags: - - minio - - minio_policies - -- name: "get policy {{ item.policy }}" - ansible.builtin.command: "mcli admin policy info minio_on_localhost {{ item.policy }} --json" - failed_when: false - changed_when: false - check_mode: false - register: minio_get_policy - loop: "{{ minio_bucket_policies }}" - when: - - minio_bucket_policies is defined - tags: - - minio - - minio_policies - -- name: "add policy missing {{ item.item.policy }}" - ansible.builtin.command: "mcli admin policy create minio_on_localhost {{ item.item.policy }} /home/minio-user/policies/{{ item.item.policy }}.json --json" - register: add_policy - failed_when: "'success' not in add_policy.stdout|from_json|json_query('status')" - loop: "{{ minio_get_policy.results }}" - when: - - minio_bucket_policies is defined - - "'success' not in item.stdout|from_json|json_query('status')" - tags: - - minio - - minio_policies - -- name: "include minio_policies tasks buckets" - ansible.builtin.include_tasks: - file: minio_policies.yml - apply: - tags: - - minio - - minio_policies - loop: "{{ minio_bucket_policies }}" - loop_control: - loop_var: minio_policy - when: minio_bucket_policies is defined - tags: - - minio - - minio_policies - -- name: "include minio_anonymous_policies tasks buckets" - ansible.builtin.include_tasks: - file: minio_anonymous_policies.yml - apply: - tags: - - minio - - minio_policies - loop: "{{ minio_anonymous_policies }}" - when: minio_anonymous_policies is defined - tags: - - minio - - minio_policies - -- name: "include minio_policies tasks add ldap group minio-admin policy consoleAdmin" - ansible.builtin.set_fact: - minio_global_policies: "{{ minio_global_policies | default([]) + minio_global_admin }}" - vars: - minio_global_admin: - - policy: "consoleAdmin" - groups: - - "cn=minio-admin,ou=system,ou=groups,dc=talas,dc=com" - when: minio_auth_type == "ldap" - tags: - - minio - - minio_policies - -- name: "include minio_policies tasks global" - ansible.builtin.include_tasks: - file: minio_policies.yml - apply: - tags: - - minio - - minio_policies - loop: "{{ minio_global_policies }}" - loop_control: - loop_var: minio_policy - when: minio_global_policies is defined - tags: - - minio - - minio_policies diff --git a/ansible/roles/minio/tasks/minio_anonymous_policies.yml b/ansible/roles/minio/tasks/minio_anonymous_policies.yml deleted file mode 100644 index b57160988..000000000 --- a/ansible/roles/minio/tasks/minio_anonymous_policies.yml +++ /dev/null @@ -1,12 +0,0 @@ ---- -# file: roles/minio/tasks/minio_anonymous_policies.yml - -- name: "mcli anonymous get minio_on_localhost/{{ item.path }}" - ansible.builtin.command: "mcli anonymous get minio_on_localhost/{{ item.path }} --json" - register: minio_anonyous_policy_get - changed_when: false - check_mode: false - -- name: "mcli anonymous set {{ item.permission }} minio_on_localhost/{{ item.path }}" - ansible.builtin.command: "mcli anonymous set {{ item.permission }} minio_on_localhost/{{ item.path }}" - when: item.permission != (minio_anonyous_policy_get.stdout|from_json|json_query('permission')) diff --git a/ansible/roles/minio/tasks/minio_buckets.yml b/ansible/roles/minio/tasks/minio_buckets.yml deleted file mode 100644 index db9f8fb17..000000000 --- a/ansible/roles/minio/tasks/minio_buckets.yml +++ /dev/null @@ -1,30 +0,0 @@ ---- -# file: roles/minio/tasks/minio_buckets.yml - -- name: "install ansible collection amazon.aws deps" - ansible.builtin.apt: - name: - - python3-botocore - - python3-boto3 - # Needed to avoid : "[WARNING]: packaging.version Python module not installed, unable to check AWS SDK" - - python3-packaging - -- name: "handle bucket {{ minio_bucket }}" - block: - - name: "check bucket {{ minio_bucket }}" - amazon.aws.s3_bucket_info: - name: "{{ minio_bucket }}" - endpoint_url: "http://localhost:{{ minio_port }}" - access_key: "minioadmin" - secret_key: "{{ minio_root_password }}" - register: check_bucket - failed_when: check_bucket.buckets | length == 0 - rescue: - - name: "create bucket {{ minio_bucket }}" - amazon.aws.s3_bucket: - name: "{{ minio_bucket }}" - state: present - delete_public_access: true - endpoint_url: "http://localhost:{{ minio_port }}" - access_key: "minioadmin" - secret_key: "{{ minio_root_password }}" diff --git a/ansible/roles/minio/tasks/minio_ldap_users.yml b/ansible/roles/minio/tasks/minio_ldap_users.yml deleted file mode 100644 index ed76439e0..000000000 --- a/ansible/roles/minio/tasks/minio_ldap_users.yml +++ /dev/null @@ -1,26 +0,0 @@ ---- -- name: "handle secret {{ ansible_hostname }}/minio_{{ minio_user }}_ldappass" - block: - - name: "get {{ ansible_hostname }}/minio_{{ minio_user }}_ldappass from hashicorp vault" - ansible.builtin.set_fact: - "minio_{{ minio_user | replace('-', '_') }}_ldappass": "{{ lookup('hashi_vault', 'secret=talas-kv/data/' + host_vars_location + '/' + ansible_hostname)['minio_' + minio_user + '_ldappass'] }}" - rescue: - - name: "generate a random password for {{ ansible_hostname }}/minio_{{ minio_user }}_ldappass" - ansible.builtin.set_fact: - password: "{{ lookup('password','/dev/null chars=ascii_letters,digits length=50') }}" - - name: "patching hashicorp vault with generated minio_{{ minio_user }}_ldappass" - ansible.builtin.command: "vault kv patch talas-kv/{{ host_vars_location }}/{{ ansible_hostname }} minio_{{ minio_user }}_ldappass={{ password }}" - delegate_to: localhost - become: false - register: result - ignore_errors: true - - name: "patch failed because the entry doesn't exist, creating it instead" - ansible.builtin.command: "vault kv put talas-kv/{{ host_vars_location }}/{{ ansible_hostname }} minio_{{ minio_user }}_ldappass={{ password }}" - delegate_to: localhost - become: false - when: - - result.failed - - '"No value found" in result.stderr' - - name: "assign password value to minio_{{ minio_user }}_ldappass" - set_fact: - "minio_{{ minio_user | replace('-', '_') }}_ldappass": "{{ password }}" diff --git a/ansible/roles/minio/tasks/minio_local_users.yml b/ansible/roles/minio/tasks/minio_local_users.yml deleted file mode 100644 index ec804d825..000000000 --- a/ansible/roles/minio/tasks/minio_local_users.yml +++ /dev/null @@ -1,41 +0,0 @@ ---- -# file: roles/minio/tasks/minio_users.yml - -- name: "handle secret {{ ansible_hostname }}/minio_{{ minio_user }}_password" - block: - - name: "get {{ ansible_hostname }}/minio_{{ minio_user }}_password from hashicorp vault" - ansible.builtin.set_fact: - "minio_{{ minio_user | replace('-', '_') | replace('.', '_') }}_password": "{{ lookup('hashi_vault', 'secret=talas-kv/data/' + host_vars_location + '/' + ansible_hostname)['minio_' + minio_user + '_password'] }}" - rescue: - - name: "generate a random password for {{ ansible_hostname }}/minio_{{ minio_user }}_password" - ansible.builtin.set_fact: - password: "{{ lookup('password','/dev/null chars=ascii_letters,digits length=50') }}" - - name: "patching hashicorp vault with generated minio_{{ minio_user }}_password" - ansible.builtin.command: "vault kv patch talas-kv/{{ host_vars_location }}/{{ ansible_hostname }} minio_{{ minio_user }}_password={{ password }}" - delegate_to: localhost - become: false - register: result - ignore_errors: true - - name: "patch failed because the entry doesn't exist, creating it instead" - ansible.builtin.command: "vault kv put talas-kv/{{ host_vars_location }}/{{ ansible_hostname }} minio_{{ minio_user }}_password={{ password }}" - delegate_to: localhost - become: false - when: - - result.failed - - '"No value found" in result.stderr' - - name: "assign password value to minio_{{ minio_user }}_password" - set_fact: - "minio_{{ minio_user | replace('-', '_') | replace('.', '_') }}_password": "{{ password }}" - -- name: "Check if user can connect to minio" - ansible.builtin.shell: "MC_HOST_myalias=http://{{ minio_user }}:\"{{ hostvars[inventory_hostname]['minio_' + minio_user | replace('-', '_') | replace('.', '_') + '_password'] }}\"@localhost:{{ minio_port }} mcli ls myalias/nonexistingbucket --json" - register: check_user - failed_when: false - changed_when: false - check_mode: false - -- name: "add user {{ minio_user }} or update its password" - ansible.builtin.command: "mcli admin user add minio_on_localhost {{ minio_user }} {{ hostvars[inventory_hostname]['minio_' + minio_user | replace('-', '_') | replace('.', '_') + '_password'] }} --json" - register: add_user - failed_when: "'success' not in add_user.stdout|from_json|json_query('status')" - when: "('InvalidAccessKeyId' == check_user.stdout | from_json | json_query('error.cause.error.Code')) or ('SignatureDoesNotMatch' == check_user.stdout|from_json|json_query('error.cause.error.Code'))" diff --git a/ansible/roles/minio/tasks/minio_policies.yml b/ansible/roles/minio/tasks/minio_policies.yml deleted file mode 100644 index ad1d834f7..000000000 --- a/ansible/roles/minio/tasks/minio_policies.yml +++ /dev/null @@ -1,69 +0,0 @@ ---- -# file: roles/minio/tasks/minio_policies.yml - -- name: "get local user {{ minio_user }} policies" - ansible.builtin.command: "mcli admin user info minio_on_localhost {{ minio_user }} --json" - loop: "{{ minio_policy.users }}" - loop_control: - loop_var: minio_user - register: minio_local_user_policies_get - changed_when: false - check_mode: false - when: - - minio_policy.users is defined - - minio_auth_type == "local" - -- name: "add local user {{ item.minio_user }} policy {{ minio_policy.policy }}" - ansible.builtin.command: "mcli admin policy attach minio_on_localhost {{ minio_policy.policy }} --user {{ item.minio_user }} --json" - loop: "{{ minio_local_user_policies_get.results }}" - when: - - minio_local_user_policies_get is not skipped - - not 'policyName' in item.stdout or minio_policy.policy not in (item.stdout|from_json|json_query('policyName')) - -- name: "get local group {{ minio_group }} policies" - ansible.builtin.command: "mcli admin group info minio_on_localhost {{ minio_group }} --json" - loop: "{{ minio_policy.groups }}" - loop_control: - loop_var: minio_group - register: minio_local_group_policies_get - changed_when: false - check_mode: false - when: - - minio_policy.groups is defined - - minio_auth_type == "local" - -- name: "add local group {{ item.minio_group }} policy {{ minio_policy.policy }}" - ansible.builtin.command: "mcli admin policy attach minio_on_localhost {{ minio_policy.policy }} --group {{ item.minio_group }} --json" - loop: "{{ minio_local_group_policies_get.results }}" - when: - - minio_local_group_policies_get is not skipped - - not 'policyName' in item.stdout or minio_policy.policy not in (item.stdout|from_json|json_query('policyName')) - -- name: "get policy entities" - ansible.builtin.command: "mcli idp ldap policy entities minio_on_localhost/ --policy {{ minio_policy.policy }} --json" - register: minio_ldap_get_policies - changed_when: false - check_mode: false - when: - - minio_policy.users is defined or minio_policy.groups is defined - - minio_auth_type == "ldap" - -- name: "add ldap user {{ minio_user }} policy {{ minio_policy.policy }}" - ansible.builtin.command: "mcli idp ldap policy attach minio_on_localhost {{ minio_policy.policy }} --user='{{ minio_user }}' --json" - loop: "{{ minio_policy.users }}" - loop_control: - loop_var: minio_user - when: - - minio_policy.users is defined - - minio_ldap_get_policies is not skipped - - "'policyMappings' not in (minio_ldap_get_policies.stdout|from_json|json_query('result')) or (minio_user not in minio_ldap_get_policies.stdout|from_json|json_query('result.policyMappings[*].users[*]') | first | default('[]') )" - -- name: "add ldap group {{ minio_group }} policy {{ minio_policy.policy }}" - ansible.builtin.command: "mcli idp ldap policy attach minio_on_localhost {{ minio_policy.policy }} --group='{{ minio_group }}' --json" - loop: "{{ minio_policy.groups }}" - loop_control: - loop_var: minio_group - when: - - minio_policy.groups is defined - - minio_ldap_get_policies is not skipped - - "'policyMappings' not in (minio_ldap_get_policies.stdout|from_json|json_query('result')) or (minio_group not in minio_ldap_get_policies.stdout|from_json|json_query('result.policyMappings[*].groups[*]') | first | default('[]') )" diff --git a/ansible/roles/minio/tasks/minio_server.yml b/ansible/roles/minio/tasks/minio_server.yml deleted file mode 100644 index 0dfe039ba..000000000 --- a/ansible/roles/minio/tasks/minio_server.yml +++ /dev/null @@ -1,90 +0,0 @@ ---- -# file: roles/minio/tasks/minio_server.yml - -# https://github.com/minio/minio/issues/4632#issuecomment-313232005 -- name: "get last version via github API" - ansible.builtin.uri: - url: https://api.github.com/repos/minio/minio/releases/latest - method: GET - register: github_server_lastest_version - check_mode: false - -- name: "set minio_server_latest_tag_version" - ansible.builtin.set_fact: - minio_server_latest_version_tag: "{{ github_server_lastest_version.json.tag_name.split('.').1 | regex_replace('T', '-') | regex_replace('Z', '') }}" - -- name: "parse release tag" - ansible.builtin.set_fact: - minio_server_latest_version_year: "{{ minio_server_latest_version_tag.split('-')[0] }}" - minio_server_latest_version_month: "{{ minio_server_latest_version_tag.split('-')[1] }}" - minio_server_latest_version_day: "{{ minio_server_latest_version_tag.split('-')[2] }}" - minio_server_latest_version_hour: "{{ minio_server_latest_version_tag.split('-')[3] }}" - minio_server_latest_version_minute: "{{ minio_server_latest_version_tag.split('-')[4] }}" - minio_server_latest_version_second: "{{ minio_server_latest_version_tag.split('-')[5] }}" - -- name: "set minio_server_latest_version_deb" - ansible.builtin.set_fact: - minio_server_latest_version_deb: "{{ minio_server_latest_version_year }}{{ minio_server_latest_version_month }}{{ minio_server_latest_version_day }}{{ minio_server_latest_version_hour }}{{ minio_server_latest_version_minute }}{{ minio_server_latest_version_second }}.0.0" - -- name: "set minio_version if latest" - ansible.builtin.set_fact: - minio_version: "{{ minio_server_latest_version_deb }}" - when: minio_version == "latest" - -- name: "gather the package facts" - ansible.builtin.package_facts: - -- name: "set minio_server_installed_version" - ansible.builtin.set_fact: - minio_server_installed_version: "{{ ansible_facts.packages['minio'][0].version | default(None) }}" - when: - - "'minio' in ansible_facts.packages" - -- name: "do we need to update minio?" - debug: - msg: "Update needed for minio, from {{ minio_server_installed_version }} to {{ minio_server_latest_version_deb }} (use: --extra-vars '{ \"minio_update_now\" : true }')" - when: - - minio_server_installed_version is defined - - minio_server_installed_version != minio_server_latest_version_deb - -- name: "get the minio server checksum" - ansible.builtin.uri: - url: "https://dl.min.io/server/minio/release/linux-amd64/minio_{{ minio_version }}_amd64.deb.sha256sum" - method: GET - return_content: true - register: minio_checksum_file - when: > - minio_server_installed_version is not defined - or (minio_update_now and minio_server_installed_version != minio_server_latest_version_deb) - -- name: "set minio_server_version_deb_checksum" - ansible.builtin.set_fact: - minio_server_version_deb_checksum: "{{ minio_checksum_file.content.split(' ')[0] }}" - when: > - minio_server_installed_version is not defined - or (minio_update_now and minio_server_installed_version != minio_server_latest_version_deb) - -- name: "get_url https://dl.min.io/server/minio/release/linux-amd64/minio_{{ minio_version }}_amd64.deb" - ansible.builtin.get_url: - url: "https://dl.min.io/server/minio/release/linux-amd64/minio_{{ minio_version }}_amd64.deb" - dest: "/dev/shm/minio_server.deb" - checksum: "sha256:{{ minio_server_version_deb_checksum }}" - mode: "0640" - when: > - minio_server_installed_version is not defined - or (minio_update_now and minio_server_installed_version != minio_server_latest_version_deb) - -- name: "install minio server" - ansible.builtin.apt: - deb: "/dev/shm/minio_server.deb" - notify: - - reload daemon - - restart minio - when: > - minio_server_installed_version is not defined - or (minio_update_now and minio_server_installed_version != minio_server_latest_version_deb) - -- name: "make sure /dev/shm/minio_server.deb is removed" - ansible.builtin.file: - path: "/dev/shm/minio_server.deb" - state: absent diff --git a/ansible/roles/minio/templates/etc_default_minio.j2 b/ansible/roles/minio/templates/etc_default_minio.j2 deleted file mode 100644 index 1b26ceed9..000000000 --- a/ansible/roles/minio/templates/etc_default_minio.j2 +++ /dev/null @@ -1,11 +0,0 @@ -# {{ ansible_managed }} - -MINIO_ROOT_USER=minioadmin -MINIO_ROOT_PASSWORD={{ minio_root_password }} - -MINIO_VOLUMES="/srv/minio" -{% if minio_haproxy %} -MINIO_OPTS="--address localhost:9002 --console-address localhost:9001" -{% else %} -MINIO_BROWSER=off -{% endif %} diff --git a/ansible/roles/minio/templates/minio_policy.json.j2 b/ansible/roles/minio/templates/minio_policy.json.j2 deleted file mode 100644 index 69d368b71..000000000 --- a/ansible/roles/minio/templates/minio_policy.json.j2 +++ /dev/null @@ -1,40 +0,0 @@ -{ - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Action": [ - "s3:GetBucketLocation", - "s3:ListBucket" - ], - "Resource": [ - "arn:aws:s3:::{{ item.bucket }}" - ] - }, -{% if item.permissions == 'read-write' %} - { - "Effect": "Deny", - "Action": [ - "s3:PutBucketPolicy", - "s3:PutEncryptionConfiguration" - ], - "Resource": [ - "arn:aws:s3:::{{ item.bucket }}/*" - ] - }, -{% endif %} - { - "Effect": "Allow", - "Action": [ -{% if item.permissions == 'read-write' %} - "s3:*" -{% elif item.permissions == 'read-only' %} - "s3:GetObject" -{% endif %} - ], - "Resource": [ - "arn:aws:s3:::{{ item.bucket }}/*" - ] - } - ] -} diff --git a/ansible/roles/minio_client/readme.md b/ansible/roles/minio_client/readme.md deleted file mode 100644 index 34b6794a3..000000000 --- a/ansible/roles/minio_client/readme.md +++ /dev/null @@ -1,23 +0,0 @@ -# Minio client role - - -* [Minio client role](#minio-client-role) - * [Difference between mcli and mc](#difference-between-mcli-and-mc) - - -This role will install minio client latest version from the official minio website. - -## Difference between mcli and mc - -Discussion on GitHub : https://github.com/minio/minio/discussions/16808 -``` -They are the same thing. Some users like to rename mc to mcli locally if they use 'Midnight Commander' (mc) tool in their machines. -``` - -``` -On Debian based systems mc in PATH does conflict with the Debian package mc. -Having both /usr/bin/mc vs /usr/local/bin/mc in the PATH is calling for trouble. -Hence for Debian systems the mc package is shipping /usr/local/bin/mcli -https://dl.min.io/client/mc/release/linux-amd64/mc.deb -FYI mcli is close but separated to mmcli from the modemmanager package. -``` diff --git a/ansible/roles/minio_client/tasks/main.yml b/ansible/roles/minio_client/tasks/main.yml deleted file mode 100644 index 59747c960..000000000 --- a/ansible/roles/minio_client/tasks/main.yml +++ /dev/null @@ -1,95 +0,0 @@ ---- -# file: roles/minio_client/tasks/main.yml - -# https://github.com/minio/minio/issues/4632#issuecomment-313232005 -- name: "get last version via github API" - ansible.builtin.uri: - url: https://api.github.com/repos/minio/mc/releases/latest - method: GET - register: github_client_lastest_version - check_mode: false - tags: minio_client - -- name: "set minio_client_latest_tag_version" - ansible.builtin.set_fact: - minio_client_latest_version_tag: "{{ github_client_lastest_version.json.tag_name.split('.').1 | regex_replace('T', '-') | regex_replace('Z', '') }}" - tags: minio_client - -- name: "parse release tag" - ansible.builtin.set_fact: - minio_client_latest_version_year: "{{ minio_client_latest_version_tag.split('-')[0] }}" - minio_client_latest_version_month: "{{ minio_client_latest_version_tag.split('-')[1] }}" - minio_client_latest_version_day: "{{ minio_client_latest_version_tag.split('-')[2] }}" - minio_client_latest_version_hour: "{{ minio_client_latest_version_tag.split('-')[3] }}" - minio_client_latest_version_minute: "{{ minio_client_latest_version_tag.split('-')[4] }}" - minio_client_latest_version_second: "{{ minio_client_latest_version_tag.split('-')[5] }}" - tags: minio_client - -- name: "set minio_client_latest_version_deb" - ansible.builtin.set_fact: - minio_client_latest_version_deb: "{{ minio_client_latest_version_year }}{{ minio_client_latest_version_month }}{{ minio_client_latest_version_day }}{{ minio_client_latest_version_hour }}{{ minio_client_latest_version_minute }}{{ minio_client_latest_version_second }}.0.0" - tags: minio_client - -- name: "gather the package facts" - ansible.builtin.package_facts: - tags: minio_client - -- name: "set minio_client_installed_version" - ansible.builtin.set_fact: - minio_client_installed_version: "{{ ansible_facts.packages['mcli'][0].version | default(None) }}" - when: - - "'mcli' in ansible_facts.packages" - tags: minio_client - -- name: "do we need to update minio?" - debug: - msg: "Updating minio client, from {{ minio_client_installed_version }} to {{ minio_client_latest_version_deb }}" - when: - - minio_client_installed_version is defined - - minio_client_installed_version != minio_client_latest_version_deb - tags: minio_client - -- name: "get the minio client checksum" - ansible.builtin.uri: - url: "https://dl.min.io/client/mc/release/linux-amd64/mcli_{{ minio_client_latest_version_deb }}_amd64.deb.sha256sum" - method: GET - return_content: true - register: minio_checksum_file - check_mode: false - when: > - minio_client_installed_version is not defined - or minio_client_installed_version != minio_client_latest_version_deb - tags: minio_client - -- name: "set minio_client_version_deb_checksum" - ansible.builtin.set_fact: - minio_client_version_deb_checksum: "{{ minio_checksum_file.content.split(' ')[0] }}" - when: > - minio_client_installed_version is not defined - or minio_client_installed_version != minio_client_latest_version_deb - tags: minio_client - -- name: "get_url https://dl.min.io/client/mc/release/linux-amd64/mcli_{{ minio_client_latest_version_deb }}_amd64.deb" - ansible.builtin.get_url: - url: "https://dl.min.io/client/mc/release/linux-amd64/mcli_{{ minio_client_latest_version_deb }}_amd64.deb" - dest: "/dev/shm/minio_client.deb" - checksum: "sha256:{{ minio_client_version_deb_checksum }}" - mode: "0640" - when: > - minio_client_installed_version is not defined - or minio_client_installed_version != minio_client_latest_version_deb - tags: minio_client - -- name: "install minio client" - ansible.builtin.apt: - deb: "/dev/shm/minio_client.deb" - when: - - not ansible_check_mode - - minio_client_installed_version is not defined or minio_client_installed_version != minio_client_latest_version_deb - tags: minio_client - -- name: "make sure /dev/shm/minio_client.deb is removed" - ansible.builtin.file: - path: "/dev/shm/minio_client.deb" - state: absent - tags: minio_client diff --git a/ansible/roles/nodejs/readme.md b/ansible/roles/nodejs/readme.md deleted file mode 100644 index d5610ef56..000000000 --- a/ansible/roles/nodejs/readme.md +++ /dev/null @@ -1,9 +0,0 @@ -# Description -This role will install nodejs from the official nodejs Debian/Ubuntu repository. - -# Required variables -Those variables have no default value by design and thus must be specified: - -| Variable | Description | Sample value | -| -------- | ----------- | -------- | -| nodejs_version | The nodejs version, it will be pinned so that it will override the default system version in case it is bellow | `12` | diff --git a/ansible/roles/nodejs/tasks/main.yml b/ansible/roles/nodejs/tasks/main.yml deleted file mode 100644 index 9eb32163e..000000000 --- a/ansible/roles/nodejs/tasks/main.yml +++ /dev/null @@ -1,49 +0,0 @@ ---- -# file: roles/nodejs/tasks/main.yml - -- name: "make sure /etc/apt/keyrings exists" - file: - path: "/etc/apt/keyrings" - state: directory - tags: nodejs - -- name: "download modern signature key" - get_url: - url: "https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key" - dest: "/dev/shm/nodesource-repo.gpg.key" - changed_when: false - tags: nodejs - -- name: "install modern signature key" - shell: - cmd: "cat /dev/shm/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg" - creates: "/etc/apt/keyrings/nodesource.gpg" - tags: nodejs - -- name: "repository file" - copy: - content: "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_{{ nodejs_version }}.x nodistro main\n" - dest: "/etc/apt/sources.list.d/nodesource.list" - register: repo - tags: nodejs - -- name: "refresh apt if repo was modified" - apt: - update_cache: yes - when: repo.changed - tags: nodejs - -- name: "/etc/apt/preferences.d/" - template: - src: "{{ item }}" - dest: "/etc/apt/preferences.d/{{ item }}" - loop: - - 91_nodejs - - 92_nsolid - tags: nodejs - -- name: "apt install nodejs (includes npm)" - apt: - name: - - nodejs - tags: nodejs diff --git a/ansible/roles/nodejs/templates/91_nodejs b/ansible/roles/nodejs/templates/91_nodejs deleted file mode 100644 index 1f02838c7..000000000 --- a/ansible/roles/nodejs/templates/91_nodejs +++ /dev/null @@ -1,4 +0,0 @@ -# {{ ansible_managed }} -Package: nodejs -Pin: origin deb.nodesource.com -Pin-Priority: 600 diff --git a/ansible/roles/nodejs/templates/92_nsolid b/ansible/roles/nodejs/templates/92_nsolid deleted file mode 100644 index 169b65868..000000000 --- a/ansible/roles/nodejs/templates/92_nsolid +++ /dev/null @@ -1,4 +0,0 @@ -# {{ ansible_managed }} -Package: nsolid -Pin: origin deb.nodesource.com -Pin-Priority: 600 diff --git a/ansible/roles/openssh-server/defaults/main.yml b/ansible/roles/openssh-server/defaults/main.yml deleted file mode 100644 index 08936e0ee..000000000 --- a/ansible/roles/openssh-server/defaults/main.yml +++ /dev/null @@ -1,50 +0,0 @@ ---- -# file: roles/openssh-server/defaults/main.yml - -sshd_default_AllowGroups_list_container: [ 'root', 'op', 'sysop' ] -sshd_default_AllowGroups_list_virtual_machine: "{{ sshd_default_AllowGroups_list_container + [ 'localadm' ] }}" -sshd_default_AllowGroups_list_physical: "{{ sshd_default_AllowGroups_list_virtual_machine + [ 'backupop' ] }}" - -sshd_HostKey_file_list: - - '/etc/ssh/ssh_host_ed25519_key' - - '/etc/ssh/ssh_host_rsa_key' - - '/etc/ssh/ssh_host_ecdsa_key' - -sshd_MACs: - - hmac-sha2-512-etm@openssh.com - - hmac-sha2-256-etm@openssh.com - - umac-128-etm@openssh.com - - hmac-sha2-512 - - hmac-sha2-256 - -sshd_kex_version_minimum_67: - - curve25519-sha256@libssh.org - - diffie-hellman-group-exchange-sha256 - -sshd_kex_version_80_to_84: - - sntrup4591761x25519-sha512@tinyssh.org - - curve25519-sha256@libssh.org - - diffie-hellman-group-exchange-sha256 - -sshd_kex_version_85_to_99: - - sntrup761x25519-sha512@openssh.com - - curve25519-sha256@libssh.org - - diffie-hellman-group-exchange-sha256 - -sshd_kex_version_100: - - mlkem768x25519-sha256 - - sntrup761x25519-sha512@openssh.com - - curve25519-sha256@libssh.org - - diffie-hellman-group-exchange-sha256 - - -sshd_Ciphers: - - chacha20-poly1305@openssh.com - - aes256-gcm@openssh.com - - aes128-gcm@openssh.com - - aes256-ctr - - aes192-ctr - - aes128-ctr - -sshd_sftp_enabled: False -sshd_sssd_ldap: True diff --git a/ansible/roles/openssh-server/handlers/main.yml b/ansible/roles/openssh-server/handlers/main.yml deleted file mode 100644 index 7ea798221..000000000 --- a/ansible/roles/openssh-server/handlers/main.yml +++ /dev/null @@ -1,6 +0,0 @@ -# file: roles/openssh-server/handlers/main.yml - -- name: restart sshd - service: - name: ssh - state: restarted diff --git a/ansible/roles/openssh-server/readme.md b/ansible/roles/openssh-server/readme.md deleted file mode 100644 index 7b2e5b5bc..000000000 --- a/ansible/roles/openssh-server/readme.md +++ /dev/null @@ -1,83 +0,0 @@ -# Optional variables - -Functional variables: - -| Name | Description | Example value | -|-------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------| -| sshd_Port_list | list of ports to listen to, for security reason, it is advised to use a privileged port (eg < 1024) to avoid an unprivileged process to take over the sshd port, the default is 22 | `[ 122, 1022 ]` | -| sshd_ListenAddress_list | list of network addresses to listen to, the default is to listen to all addresses (IPv4 and IPv6) | `[ '1.2.3.4', '5.6.7.8' ]` | -| sshd_sssd_ldap | use sssd to retreive user pubkey from ldap. Require sssd roles, set to false if sssd is not installed or not in ldap mode | `True` | - -Access control variables: - -| Name | Description | Default value | -|-----------------------|---------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------| -| sshd_AllowGroups_list | list of groups that are allowed to connect using ssh, this is the new parameter that must be used | `[ 'root', 'op', 'sysop' ]` for containers and `[ 'root', 'op', 'sysop', 'localadm' ]` for VM and physical machines | - -# Security -This role have been created so that it passes several security audit tests. As a result, it define a set of secure parameters for: -- Ciphers -- MACs -- KexAlgorithms (this changes between versions) -- TCPKeepAlive -- ClientAliveCountMax -- ClientAliveInterval - -To easily change the `Ciphers`, `MACs` and `KexAlgorithms` if a vulnerabilty is found, those are defined in the defaults/main.yml file. *Never change them via a local variable.* - -You can see the supported ciphers, macs, kex, etc via `ssh -Q query_option`, use `man ssh` for usage. - -## Handling of revoked keys -As an additional security measure, this role handle the `RevokedKeys` option. This can come in handy if a key is compromised and you want to be sure that it will never work. You can also enfore key rotation this way. - -To use this feature, define a list of keys via this variable: -``` -sshd_RevokedKeys_list: - - key1 - - key2 -``` - -# Allow some groups to only uses sftp -To allow some groups to connect to the host only via sftp, you must set this variable to true: -``` -sshd_sftp_enabled: True -``` -This will change the sftp subsystem from `/usr/lib/openssh/sftp-server` to `internal-sftp`, which is necessary to achieve the required configuration. - -## SFTP: Define accesses -The chroot directory must be only writable by root, this is mandatory else the connection will be refused. This is for security purpose to avoid privilege escalation. - -Define the variable `sshd_sftp_group_access` with a list of groups and the directory for their chroot: -``` -sshd_sftp_group_access: - - name: "somegroup" - ChrootDirectory: "/some/path" - - name: "someothergroup" - ChrootDirectory: "/some/other/path" -``` -This will create those blocks in the /etc/ssh/sshd_config: -``` -# BEGIN sftp configuration for group somegroup -Match group somegroup - ForceCommand internal-sftp - ChrootDirectory /some/path - PermitTunnel no - AllowAgentForwarding no - AllowTcpForwarding no - X11Forwarding no - PermitTTY no -# END sftp configuration for group somegroup -# BEGIN sftp configuration for group someothergroup -Match group somegroup - ForceCommand internal-sftp - ChrootDirectory /some/other/path - PermitTunnel no - AllowAgentForwarding no - AllowTcpForwarding no - X11Forwarding no - PermitTTY no -# END sftp configuration for group someothergroup -``` -Creating a ~/.ssh/authorized_keys file will work correctly with this system and correctly allow the more secure login with public/private key pair. - -You still need to allow the groups that will use sftp to connect to the server via the `sshd_AllowGroups_list` variable. diff --git a/ansible/roles/openssh-server/tasks/main.yml b/ansible/roles/openssh-server/tasks/main.yml deleted file mode 100644 index 72d8ff3c2..000000000 --- a/ansible/roles/openssh-server/tasks/main.yml +++ /dev/null @@ -1,83 +0,0 @@ ---- -# file: roles/openssh-server/tasks/main.yml - -- name: "install openssh-server" - apt: - name: openssh-server - install_recommends: false - tags: ssh - -- name: "gather the list of installed package" - package_facts: - tags: ssh - -- name: "get openssh-server version" - set_fact: - sshd_version: "{{ ansible_facts.packages['openssh-server'][0]['version'][2:5] }}" - tags: ssh - -- name: "display current openssh version" - debug: - msg: "openssh-server version is {{ sshd_version }}" - tags: ssh - -- name: "immediate fail for unsupported version of openssh" - fail: - msg: "Compatibility with target host is unsupported or not verified for this role." - when: sshd_version is version('10.0', '>') - tags: ssh - -- name: "set kex for version < 8.0" - set_fact: - sshd_KexAlgorithms: '{{ sshd_kex_version_minimum_67 }}' - when: sshd_version is version('8.0', '<') - tags: ssh - -- name: "set kex for version between 8.0 and 8.4" - set_fact: - sshd_KexAlgorithms: '{{ sshd_kex_version_80_to_84 }}' - when: - - sshd_version is version('8.0', '>=') - - sshd_version is version('8.5', '<') - tags: ssh - -- name: "set kex for version between 8.5 and 9.9" - set_fact: - sshd_KexAlgorithms: '{{ sshd_kex_version_85_to_99 }}' - when: - - sshd_version is version('8.5', '>=') - - sshd_version is version('9.9', '<=') - tags: ssh - -- name: "set kex for version 10.0" - set_fact: - sshd_KexAlgorithms: '{{ sshd_kex_version_100 }}' - when: - - sshd_version is version('9.9', '>=') - - sshd_version is version('10.0', '<=') - tags: ssh - - -- name: "/etc/ssh/revoked_keys" - template: - src: "revoked_keys.j2" - dest: "/etc/ssh/revoked_keys" - mode: "0600" - owner: "root" - group: "root" - backup: yes - when: sshd_RevokedKeys_list is defined - notify: restart sshd - tags: ssh - -- name: "/etc/ssh/sshd_config" - template: - src: "opensshd.conf.j2" - dest: "/etc/ssh/sshd_config" - mode: "0600" - owner: "root" - group: "root" - validate: "/usr/sbin/sshd -T -C user=root -C host=localhost -C addr=localhost -f %s" - backup: yes - notify: restart sshd - tags: ssh diff --git a/ansible/roles/openssh-server/templates/opensshd.conf.j2 b/ansible/roles/openssh-server/templates/opensshd.conf.j2 deleted file mode 100644 index d29da682d..000000000 --- a/ansible/roles/openssh-server/templates/opensshd.conf.j2 +++ /dev/null @@ -1,68 +0,0 @@ -# {{ ansible_managed }} - -{% if sshd_Port_list is defined %} -{% for port in sshd_Port_list %} -Port {{ port }} -{% endfor %} -{% endif %} -{% if sshd_ListenAddress_list is defined %} -{% for address in sshd_ListenAddress_list %} -ListenAddress {{ address }} -{% endfor %} -{% endif %} -{% for keyfile in sshd_HostKey_file_list %} -HostKey {{ keyfile }} -{% endfor %} - -ChallengeResponseAuthentication no -UsePAM yes -PrintMotd no -AcceptEnv LANG LC_* - -{% if sshd_sssd_ldap and ((ansible_distribution == "Ubuntu" and ansible_distribution_version is version('20.04', '>=')) or (ansible_distribution == "Debian" and ansible_distribution_version is version('11', '>='))) %} -AuthorizedKeysCommand /usr/bin/sss_ssh_authorizedkeys -AuthorizedKeysCommandUser nobody -{% endif %} -{% if sshd_AllowGroups_list is defined %} - -AllowGroups {{ sshd_AllowGroups_list | join(' ') }} -{% elif ansible_virtualization_role == "host" or ansible_virtualization_role == "NA" %} - -AllowGroups {{ sshd_default_AllowGroups_list_physical | join(' ') }} -{% elif ansible_virtualization_type == "lxc" %} - -AllowGroups {{ sshd_default_AllowGroups_list_container | join(' ') }} -{% else %} - -AllowGroups {{ sshd_default_AllowGroups_list_virtual_machine | join(' ') }} -{% endif %} - -# BEGIN extended security settings -Ciphers {{ sshd_Ciphers | join(',') }} -MACs {{ sshd_MACs | join(',') }} -KexAlgorithms {{ sshd_KexAlgorithms | join(',') }} -TCPKeepAlive no -ClientAliveCountMax 4 -ClientAliveInterval 30 -{% if sshd_RevokedKeys_list is defined %} -RevokedKeys /etc/ssh/revoked_keys -{% endif %} -# END extended security settings - -{% if sshd_sftp_enabled %} -# enable sftp configuration using internal-sftp subsystem for group policies -Subsystem sftp internal-sftp -{% for item in sshd_sftp_group_access %} - -# sftp configuration for group {{ item.name }} -Match Group {{ item.name }} - ForceCommand internal-sftp - ChrootDirectory {{ item.ChrootDirectory }} - AllowTcpForwarding no - AllowAgentForwarding no - PermitRootLogin no - X11Forwarding no -{% endfor %} -{% else %} -Subsystem sftp /usr/lib/openssh/sftp-server -{% endif %} diff --git a/ansible/roles/openssh-server/templates/revoked_keys.j2 b/ansible/roles/openssh-server/templates/revoked_keys.j2 deleted file mode 100644 index 107fd02b8..000000000 --- a/ansible/roles/openssh-server/templates/revoked_keys.j2 +++ /dev/null @@ -1,5 +0,0 @@ -# {{ ansible_managed }} - -{% for key in sshd_RevokedKeys_list %} -{{ key }} -{% endfor %} diff --git a/ansible/roles/ovn/defaults/main.yml b/ansible/roles/ovn/defaults/main.yml deleted file mode 100644 index 94a18e836..000000000 --- a/ansible/roles/ovn/defaults/main.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -# file: roles/ovn/defaults/main.yml - -ovn_host: true -ovn_ic: true -ovn_ssl: false diff --git a/ansible/roles/ovn/files/admin_ovn.sh b/ansible/roles/ovn/files/admin_ovn.sh deleted file mode 100644 index b16ed03fc..000000000 --- a/ansible/roles/ovn/files/admin_ovn.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash -# Ansible managed - -source /etc/ovn/alias.sh diff --git a/ansible/roles/ovn/handlers/main.yml b/ansible/roles/ovn/handlers/main.yml deleted file mode 100644 index b7583c085..000000000 --- a/ansible/roles/ovn/handlers/main.yml +++ /dev/null @@ -1,85 +0,0 @@ ---- -# file: roles/ovn/handlers/main.yml - -- name: "Configure OVS" - ansible.builtin.shell: - cmd: "ovs-vsctl set open_vswitch . external_ids:hostname={{ ansible_hostname }} external_ids:ovn-remote={{ ovn_central_southbound }} external_ids:ovn-encap-type=geneve external_ids:ovn-encap-ip={{ ovn_ip }}" - -- name: "Enable OVN IC gateway" - ansible.builtin.shell: - cmd: "ovs-vsctl set open_vswitch . external_ids:ovn-is-interconn=true" - -- name: "Configure OVN central northbound DB for SSL (certs)" - ansible.builtin.shell: - cmd: "ovn-nbctl set-ssl /etc/ovn/server.key /etc/ovn/server.crt /etc/ovn/ca.crt" - when: ovn_central - -- name: "Configure OVN central northbound DB for SSL (ports)" - ansible.builtin.shell: - cmd: "ovn-nbctl set-connection pssl:6641:[::]" - when: ovn_central - -- name: "Configure OVN central southbound DB for SSL (certs)" - ansible.builtin.shell: - cmd: "ovn-sbctl set-ssl /etc/ovn/server.key /etc/ovn/server.crt /etc/ovn/ca.crt" - when: ovn_central - -- name: "Configure OVN central southbound DB for SSL (ports)" - ansible.builtin.shell: - cmd: "ovn-sbctl set-connection pssl:6642:[::]" - when: ovn_central - -- name: "Configure OVN IC northbound DB for SSL (certs)" - ansible.builtin.shell: - cmd: "ovn-ic-nbctl set-ssl /etc/ovn/server.key /etc/ovn/server.crt /etc/ovn/ca.crt" - when: ovn_ic_db - -- name: "Configure OVN IC northbound DB for SSL (ports)" - ansible.builtin.shell: - cmd: "ovn-ic-nbctl set-connection pssl:6645:[::]" - when: ovn_ic_db - -- name: "Configure OVN IC southbound DB for SSL (certs)" - ansible.builtin.shell: - cmd: "ovn-ic-sbctl set-ssl /etc/ovn/server.key /etc/ovn/server.crt /etc/ovn/ca.crt" - when: ovn_ic_db - -- name: "Configure OVN IC southbound DB for SSL (ports)" - ansible.builtin.shell: - cmd: "ovn-ic-sbctl set-connection pssl:6646:[::]" - when: ovn_ic_db - -- name: "Restart OVN central" - ansible.builtin.systemd: - name: ovn-central.service - state: restarted - -- name: "Restart OVN host" - ansible.builtin.systemd: - name: ovn-host.service - state: restarted - -- name: "Restart OVN IC" - ansible.builtin.systemd: - daemon_reload: true - name: ovn-ic.service - state: restarted - when: ovn_ic_db - -- name: "Restart OVN IC DB" - ansible.builtin.systemd: - name: ovn-ic-db.service - state: restarted - when: ovn_ic_db - -- name: "Configure OVN AZ name" - ansible.builtin.shell: - cmd: "ovn-nbctl --db={{ ovn_central_northbound }} -c /etc/ovn/server.crt -p /etc/ovn/server.key -C /etc/ovn/ca.crt set NB_Global . name={{ ovn_cluster_name }}" - when: - - ansible_hostname == ovn_cluster_main_name - -- name: "Enable OVN IC route sharing" - ansible.builtin.shell: - cmd: "ovn-nbctl --db={{ ovn_central_northbound }} -c /etc/ovn/server.crt -p /etc/ovn/server.key -C /etc/ovn/ca.crt set NB_Global . options:ic-route-adv=true options:ic-route-learn=true" - when: - - ansible_hostname == ovn_cluster_main_name diff --git a/ansible/roles/ovn/readme.md b/ansible/roles/ovn/readme.md deleted file mode 100644 index 3f746b929..000000000 --- a/ansible/roles/ovn/readme.md +++ /dev/null @@ -1,50 +0,0 @@ -# OVN role - -This is the description of the role OVN. - - -* [OVN role](#ovn-role) - * [Variable reference](#variable-reference) - * [Mandatory variables](#mandatory-variables) - * [Optional variables](#optional-variables) - * [Handlers](#handlers) - * [CLI tools](#cli-tools) - - -## Variable reference - -### Mandatory variables - -| Variable | Description | Type of variable | Example value | -|-----------------------|-------------------------------------------------------------------------------------------------------|------------------|------------------------| -| ovn_cluster_name | name of the incus cluster. Must be defined in a group_vars and have the same name that this group_var | str | th3_core | -| ovn_cluster_main_name | ansible_hostname of the central DB main server | str | srv-203 | -| ovn_ip | the ip on which ovn daemons listen on, without CIDR | str | 10.24.10.10 | -| ovn_central_servers | list of servers (server name must match their ansible inventory name) hosting the ovn db | list[str] | ["srv-382", "srv-383"] | -| ovn_ic_db_servers | list of servers (server name must match their ansible inventory name) hosting the ovn ic db | list[str] | ["srv-382", "srv-383"] | - -### Optional variables - -| Variable | Description | Type of variable | Default value | Example value | -|--------------------------|-----------------------------------------------------------|------------------|---------------|--------------------| -| ovn_host | is this host an ovn host | bool | `true` | `false` | -| ovn_ic_name | name of the ovn ic this cluster is a part of | str | None | `incus_ic_cluster` | -| ovn_ic_cluster_main_name | ansible_hostname of the OVN IC DB main server | str | None | `srv-383` | -| ovn_ic | is this host an ovn ic host | bool | `true` | `false` | -| ovn_ssl | enable SSL connection; has to be set at the cluster level | bool | `false` | `true` | - -## Handlers - -In this role handlers order is very important, do not change it without reasons. - -Ansible doc reminder: `Handlers are executed in the order they are defined in the handlers section` (https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_handlers.html#notifying-handlers) - -## CLI tools - -`ovn-nbctl` OVN northbound DB management utility -`ovn-sbctl` OVN southbound DB management utility -This cli tools manage only the DB content to manage the DB cluster you have to use `ovs-appctl`. -By exemple for OVN SB DB: -``` -ovs-appctl -t /var/run/ovn/ovnsb_db.ctl list-commands -``` diff --git a/ansible/roles/ovn/tasks/ic.yml b/ansible/roles/ovn/tasks/ic.yml deleted file mode 100644 index b11ad1dcc..000000000 --- a/ansible/roles/ovn/tasks/ic.yml +++ /dev/null @@ -1,63 +0,0 @@ ---- -# file: roles/ovn/tasks/ic.yml - -- name: "set bool ovn_central" - ansible.builtin.set_fact: - ovn_ic_db: "{{ inventory_hostname in ovn_ic_db_servers }}" - -- name: "set facts ovn_ic bounds lists" - ansible.builtin.set_fact: - ovn_ic_northbound_list: "{{ (ovn_ic_northbound_list | default([])) + [ovn_connection_type + ':' + hostvars[item]['ovn_ip'] + ':6645'] }}" - ovn_ic_southbound_list: "{{ (ovn_ic_southbound_list | default([])) + [ovn_connection_type + ':' + hostvars[item]['ovn_ip'] + ':6646'] }}" - loop: "{{ ovn_ic_db_servers }}" - -- name: "set facts ovn_ic bounds ovn_ic_cluster_main_ip" - ansible.builtin.set_fact: - ovn_ic_northbound: "{{ ovn_ic_northbound_list | join(',') }}" - ovn_ic_southbound: "{{ ovn_ic_southbound_list | join(',') }}" - ovn_ic_cluster_main_ip: "{{ hostvars[ovn_ic_cluster_main_name]['ovn_ip'] }}" - -- name: "Install the OVN IC database package" - ansible.builtin.apt: - name: - - ovn-ic-db - install_recommends: false - state: present - when: ovn_ic_db - -- name: "Install the OVN IC package" - ansible.builtin.apt: - name: - - ovn-ic - install_recommends: false - state: present - notify: - - Enable OVN IC gateway - when: ovn_ic - -- name: "Create OVN IC override directory" - ansible.builtin.file: - path: /etc/systemd/system/ovn-ic.service.d - mode: "0755" - state: directory - when: ovn_ic - -- name: "Transfer OVN IC override" - ansible.builtin.copy: - content: | - [Service] - EnvironmentFile=-/etc/default/ovn-ic - ExecStart= - ExecStart=/usr/share/ovn/scripts/ovn-ctl start_ic --no-monitor $OVN_CTL_OPTS - dest: /etc/systemd/system/ovn-ic.service.d/ansible.conf - notify: Restart OVN IC - when: ovn_ic - -- name: "Configure OVN IC database" - ansible.builtin.template: - src: ovn-ic.j2 - dest: /etc/default/ovn-ic - notify: - - Restart OVN IC DB - - Restart OVN IC - when: ovn_ic_db or ovn_ic diff --git a/ansible/roles/ovn/tasks/main.yml b/ansible/roles/ovn/tasks/main.yml deleted file mode 100644 index 872cdc650..000000000 --- a/ansible/roles/ovn/tasks/main.yml +++ /dev/null @@ -1,118 +0,0 @@ ---- -# file: roles/ovn/tasks/main.yml - -- name: "set bool ovn_central" - ansible.builtin.set_fact: - ovn_central: "{{ inventory_hostname in ovn_central_servers }}" - tags: ovn - -- name: "Install the OVN central package" - ansible.builtin.apt: - name: - - ovn-central - when: ovn_central - tags: ovn - -- name: "Install the OVN host package" - ansible.builtin.apt: - name: - - ovn-host - notify: - - Configure OVS - when: ovn_host - tags: ovn - -- name: "Create OVN config directory" - ansible.builtin.file: - path: /etc/ovn - mode: "0755" - state: directory - tags: ovn - -- name: "Set ovn_connection_type" - ansible.builtin.set_fact: - ovn_connection_type: "{{ ovn_ssl | ternary('ssl', 'tcp') }}" - tags: ovn - -- name: "include pki" - ansible.builtin.import_tasks: pki.yml - when: ovn_ssl - tags: - - ovn - - ovn_pki - -- name: "set facts ovn_central bounds lists" - ansible.builtin.set_fact: - ovn_central_northbound_list: "{{ (ovn_central_northbound_list | default([])) + [ovn_connection_type + ':' + hostvars[item]['ovn_ip'] + ':6641'] }}" - ovn_central_southbound_list: "{{ (ovn_central_southbound_list | default([])) + [ovn_connection_type + ':' + hostvars[item]['ovn_ip'] + ':6642'] }}" - loop: "{{ ovn_central_servers }}" - tags: ovn - -- name: "set facts ovn_central bounds and ovn_cluster_main_ip" - ansible.builtin.set_fact: - ovn_central_northbound: "{{ ovn_central_northbound_list | join(',') }}" - ovn_central_southbound: "{{ ovn_central_southbound_list | join(',') }}" - ovn_cluster_main_ip: "{{ hostvars[ovn_cluster_main_name]['ovn_ip'] }}" - tags: ovn - -- name: "Configure OVN central database" - ansible.builtin.template: - src: ovn-central.j2 - dest: /etc/default/ovn-central - notify: - - Restart OVN central - - Configure OVN AZ name - - Enable OVN IC route sharing - when: ovn_central - tags: ovn - -- name: "Configure OVN host" - ansible.builtin.template: - src: ovn-host.j2 - dest: /etc/default/ovn-host - notify: - - Restart OVN host - when: ovn_host - tags: ovn - -- name: "include ic" - ansible.builtin.import_tasks: ic.yml - when: (ovn_ic is defined and ovn_ic) or (ovn_ic_db is defined and ovn_ic_db) - tags: - - ovn - - ovn_ic - -- name: "/etc/sysctl.conf for networking, when conntrack is loaded only" - ansible.posix.sysctl: - name: "{{ item['name'] }}" - value: "{{ item['value'] }}" - loop: - - { 'name' : 'net.netfilter.nf_conntrack_buckets', 'value' : '262144' } - - { 'name' : 'net.netfilter.nf_conntrack_max', 'value' : '1048576' } - tags: - - ovn - - sysctl - -- name: "make sure that nf_conntrack is loaded before sysctl rules are applied" - community.general.modprobe: - name: nf_conntrack - persistent: present - tags: - - ovn - - sysctl - -- name: "/etc/ovn/alias.sh" - ansible.builtin.template: - src: alias.sh.j2 - dest: /etc/ovn/alias.sh - tags: ovn - -- name: "admin_ovn.sh" - ansible.builtin.copy: - src: "admin_ovn.sh" - dest: "/etc/profile.d/admin_common.sh" - tags: ovn - -- name: "Flush handlers" - meta: flush_handlers - tags: ovn diff --git a/ansible/roles/ovn/tasks/pki.yml b/ansible/roles/ovn/tasks/pki.yml deleted file mode 100644 index 3214a0a42..000000000 --- a/ansible/roles/ovn/tasks/pki.yml +++ /dev/null @@ -1,123 +0,0 @@ ---- -# file: roles/ovn/tasks/pki.yml - -- name: "Get {{ ansible_hostname }} secrets from hashicorp vault" - ansible.builtin.set_fact: - ovn_hv_host_secrets: "{{ lookup('community.hashi_vault.vault_kv2_get', 'host_vars/' + ansible_hostname, engine_mount_point='talas-kv') }}" - -- name: "handle server certificates" - block: - - name: "Extract cert and private key from hashicorp vault" - ansible.builtin.set_fact: - ovn_cert_server_private_key: "{{ ovn_hv_host_secrets.secret.ovn_cert_server_private_key }}" - ovn_cert_server_serial_number: "{{ ovn_hv_host_secrets.secret.ovn_cert_server_serial_number }}" - - rescue: - - name: "Generate a certificate" - community.hashi_vault.vault_pki_generate_certificate: - engine_mount_point: "pki" - role_name: "ovn" - common_name: "OVN certificate for {{ ansible_hostname }}" - register: ovn_cert_server_data - delegate_to: localhost - become: false - - - name: "Set cert serial number and private key" - ansible.builtin.set_fact: - ovn_cert_server_serial_number: "{{ ovn_cert_server_data.data.data.serial_number }}" - ovn_cert_server_private_key: "{{ ovn_cert_server_data.data.data.private_key }}" - when: not ansible_check_mode - - - name: "Write the cert serial number and private key in hashicorp vault" - community.hashi_vault.vault_kv2_write: - engine_mount_point: talas-kv - path: "{{ host_vars_location }}/{{ ansible_hostname }}" - cas: "{{ ovn_hv_host_secrets.metadata.version | default('0') | int }}" - data: >- - {{ - ovn_hv_host_secrets.secret | default({}) | combine(ovn_new_vars) - }} - vars: - ovn_new_vars: - ovn_cert_server_serial_number: "{{ ovn_cert_server_serial_number }}" - ovn_cert_server_private_key: "{{ ovn_cert_server_private_key }}" - delegate_to: localhost - become: false - when: not ansible_check_mode - -- name: "Get cert and issuing certificates from hashicorp vault pki" - ansible.builtin.set_fact: - ovn_cert_server_issuing_ca_chain: "{{ lookup('community.hashi_vault.vault_read', 'pki/issuer/OVN') | community.general.json_query('data.ca_chain') | join() | trim }}" - ovn_cert_server_ca: "{{ lookup('community.hashi_vault.vault_read', 'pki/cert/' + ovn_cert_server_serial_number) | community.general.json_query('data.certificate') }}" - -- name: "/etc/ovn/ca.crt" - ansible.builtin.copy: - content: "{{ ovn_cert_server_issuing_ca_chain }}\n" - dest: "/etc/ovn/ca.crt" - mode: "0644" - -- name: "/etc/ovn/server.crt" - ansible.builtin.copy: - content: "{{ ovn_cert_server_ca }}\n" - dest: "/etc/ovn/server.crt" - mode: "0644" - -- name: "/etc/ovn/server.key" - ansible.builtin.copy: - content: "{{ ovn_cert_server_private_key }}\n" - dest: "/etc/ovn/server.key" - mode: "0600" - notify: - - Configure OVN central northbound DB for SSL (certs) - - Configure OVN central northbound DB for SSL (ports) - - Configure OVN central southbound DB for SSL (certs) - - Configure OVN central southbound DB for SSL (ports) - - Configure OVN IC northbound DB for SSL (certs) - - Configure OVN IC northbound DB for SSL (ports) - - Configure OVN IC southbound DB for SSL (certs) - - Configure OVN IC southbound DB for SSL (ports) - -- name: "handle incus_client certificates" - block: - - name: "Get {{ ovn_cluster_name }} secrets from hashicorp vault" - ansible.builtin.set_fact: - ovn_hv_group_secrets: "{{ lookup('community.hashi_vault.vault_kv2_get', 'group_vars/' + ovn_cluster_name, engine_mount_point='talas-kv') }}" - - - name: "Make sure that cert and private key are present in hashicorp vault" - ansible.builtin.set_fact: - ovn_cert_incus_client_private_key: "{{ ovn_hv_group_secrets.secret.ovn_cert_incus_client_private_key }}" - ovn_cert_incus_client_serial_number: "{{ ovn_hv_group_secrets.secret.ovn_cert_incus_client_serial_number }}" - - rescue: - - name: "Generate a certificate for incus client" - community.hashi_vault.vault_pki_generate_certificate: - engine_mount_point: "pki" - role_name: "ovn" - common_name: "OVN certificate for incus {{ ovn_cluster_name }}" - register: ovn_cert_incus_client_data - delegate_to: localhost - become: false - - - name: "Set cert serial number and private key for incus client" - ansible.builtin.set_fact: - ovn_cert_incus_client_serial_number: "{{ ovn_cert_incus_client_data.data.data.serial_number }}" - ovn_cert_incus_client_private_key: "{{ ovn_cert_incus_client_data.data.data.private_key }}" - when: not ansible_check_mode - - - name: "Write the cert serial number and private key for incus client in hashicorp vault" - community.hashi_vault.vault_kv2_write: - engine_mount_point: talas-kv - path: "group_vars/{{ ovn_cluster_name }}" - cas: "{{ ovn_hv_group_secrets.metadata.version | default('0') | int }}" - data: >- - {{ - ovn_hv_group_secrets.secret | default({}) | combine(ovn_new_vars) - }} - vars: - ovn_new_vars: - ovn_cert_incus_client_serial_number: "{{ ovn_cert_incus_client_serial_number }}" - ovn_cert_incus_client_private_key: "{{ ovn_cert_incus_client_private_key }}" - delegate_to: localhost - become: false - when: not ansible_check_mode - when: ansible_hostname == ovn_cluster_main_name diff --git a/ansible/roles/ovn/templates/alias.sh.j2 b/ansible/roles/ovn/templates/alias.sh.j2 deleted file mode 100644 index d6106a443..000000000 --- a/ansible/roles/ovn/templates/alias.sh.j2 +++ /dev/null @@ -1,9 +0,0 @@ -# {{ ansible_managed }} -alias ovn-nbctl="/usr/bin/ovn-nbctl --db={{ ovn_central_northbound }}{{ (' -c /etc/ovn/server.crt -p /etc/ovn/server.key -C /etc/ovn/ca.crt') if ovn_ssl }}" -alias ovn-sbctl="/usr/bin/ovn-sbctl --db={{ ovn_central_southbound }}{{ (' -c /etc/ovn/server.crt -p /etc/ovn/server.key -C /etc/ovn/ca.crt') if ovn_ssl }}" -{% if ovn_ic_northbound is defined %} -alias ovn-ic-nbctl="/usr/bin/ovn-ic-nbctl --db={{ ovn_ic_northbound }}{{ (' -c /etc/ovn/server.crt -p /etc/ovn/server.key -C /etc/ovn/ca.crt') if ovn_ssl }}" -{% endif %} -{% if ovn_ic_southbound is defined %} -alias ovn-ic-sbctl="/usr/bin/ovn-ic-sbctl --db={{ ovn_ic_southbound }}{{ (' -c /etc/ovn/server.crt -p /etc/ovn/server.key -C /etc/ovn/ca.crt') if ovn_ssl }}" -{% endif %} diff --git a/ansible/roles/ovn/templates/ovn-central.j2 b/ansible/roles/ovn/templates/ovn-central.j2 deleted file mode 100644 index 3b90ce80e..000000000 --- a/ansible/roles/ovn/templates/ovn-central.j2 +++ /dev/null @@ -1,23 +0,0 @@ -# {{ ansible_managed }} - -# This is a POSIX shell fragment -*- sh -*- - -# OVN_CTL_OPTS: Extra options to pass to ovs-ctl. This is, for example, -# a suitable place to specify --ovn-northd-wrapper=valgrind. - -OVN_CTL_OPTS="\ - --db-nb-create-insecure-remote={{ ovn_ssl | ternary('no', 'yes') }} \ - --db-sb-create-insecure-remote={{ ovn_ssl | ternary('no', 'yes') }} \ - --db-nb-addr={{ ovn_ip }} \ - --db-sb-addr={{ ovn_ip }} \ - --db-nb-cluster-local-addr={{ ovn_ip }} \ - --db-sb-cluster-local-addr={{ ovn_ip }} \ -{% if ovn_ssl %} - --ovn-northd-ssl-key=/etc/ovn/server.key \ - --ovn-northd-ssl-cert=/etc/ovn/server.crt \ - --ovn-northd-ssl-ca-cert=/etc/ovn/ca.crt \ -{% endif %} - --ovn-northd-nb-db={{ ovn_central_northbound }} \ - --ovn-northd-sb-db={{ ovn_central_southbound }}{% if ansible_hostname != ovn_cluster_main_name %} \ - --db-nb-cluster-remote-addr={{ ovn_cluster_main_ip }} \ - --db-sb-cluster-remote-addr={{ ovn_cluster_main_ip }}{% endif %}" diff --git a/ansible/roles/ovn/templates/ovn-host.j2 b/ansible/roles/ovn/templates/ovn-host.j2 deleted file mode 100644 index 3bf047f4f..000000000 --- a/ansible/roles/ovn/templates/ovn-host.j2 +++ /dev/null @@ -1,12 +0,0 @@ -# {{ ansible_managed }} - -# This is a POSIX shell fragment -*- sh -*- - -# OVN_CTL_OPTS: Extra options to pass to ovs-ctl. This is, for example, -# a suitable place to specify --ovn-controller-wrapper=valgrind. -{% if ovn_ssl %} -OVN_CTL_OPTS="\ - --ovn-controller-ssl-key=/etc/ovn/server.key \ - --ovn-controller-ssl-cert=/etc/ovn/server.crt \ - --ovn-controller-ssl-ca-cert=/etc/ovn/ca.crt" -{% endif %} diff --git a/ansible/roles/ovn/templates/ovn-ic.j2 b/ansible/roles/ovn/templates/ovn-ic.j2 deleted file mode 100644 index f5978a7d6..000000000 --- a/ansible/roles/ovn/templates/ovn-ic.j2 +++ /dev/null @@ -1,25 +0,0 @@ -# {{ ansible_managed }} - -# This is a POSIX shell fragment -*- sh -*- - -# OVN_CTL_OPTS: Extra options to pass to ovs-ctl. This is, for example, -# a suitable place to specify --ovn-northd-wrapper=valgrind. - -OVN_CTL_OPTS="\ - --db-ic-nb-create-insecure-remote={{ ovn_ssl | ternary('no', 'yes') }} \ - --db-ic-sb-create-insecure-remote={{ ovn_ssl | ternary('no', 'yes') }} \ - --db-ic-nb-addr=[{{ ovn_ip }}] \ - --db-ic-sb-addr=[{{ ovn_ip }}] \ - --db-ic-nb-cluster-local-addr=[{{ ovn_ip }}] \ - --db-ic-sb-cluster-local-addr=[{{ ovn_ip }}] \ - --ovn-northd-nb-db={{ ovn_central_northbound }} \ - --ovn-northd-sb-db={{ ovn_central_southbound }} \ -{% if ovn_ssl %} - --ovn-ic-ssl-key=/etc/ovn/server.key \ - --ovn-ic-ssl-cert=/etc/ovn/server.crt \ - --ovn-ic-ssl-ca-cert=/etc/ovn/ca.crt \ -{% endif %} - --ovn-ic-nb-db={{ ovn_ic_northbound }} \ - --ovn-ic-sb-db={{ ovn_ic_southbound }}{% if ansible_hostname != ovn_ic_cluster_main_name %} \ - --db-ic-nb-cluster-remote-addr={{ ovn_ic_cluster_main_ip }} \ - --db-ic-sb-cluster-remote-addr={{ ovn_ic_cluster_main_ip }}{% endif %}" diff --git a/ansible/roles/postgres/defaults/main/postgres_and_patroni.yml b/ansible/roles/postgres/defaults/main/postgres_and_patroni.yml deleted file mode 100644 index 84296e8e3..000000000 --- a/ansible/roles/postgres/defaults/main/postgres_and_patroni.yml +++ /dev/null @@ -1,49 +0,0 @@ ---- -# file: roles/postgres/defaults/main/postgres_and_patroni.yml - -pg_max_connections: 100 -pg_shared_buffers: "128MB" - -pg_timezone: Europe/Paris -pg_huge_pages: try -pg_statement_timeout: 0 - -# specific for ssd drives -pg_random_page_cost: 1.1 -pg_effective_io_concurrency: 500 -pg_maintenance_io_concurrency: 500 - -# specific config activation -pg_ssd: False -pg_logging: False -pg_log_filename_suffix: '%a_%H' -pg_log_min_duration_statement: '0' -pg_replication: False - -# archive default -pg_archive_timeout: 2min - -# replication default -pg_max_standby_archive_delay: 5min -pg_max_standby_streaming_delay: 5min -pg_max_wal_size: 10GB -pg_min_wal_size: 512MB - -# master or slave runs additionnal script, any other thing create a standalone server -pg_replication_role: none - -postgres_primary_conninfo_port: 5432 -postgres_primary_conninfo_sslmode: "require" - -# postgresql monitoring for XLOG/WAL changes depending on the installed postgresql version, version <= 9.6 are using the legacy query and postgresql >= 10 are using the modern query -postgres_zabbix_legacy_stat_replication_bytelag: 'SELECT pg_xlog_location_diff(pg_current_xlog_location(), sent_location) as byte_lag FROM pg_stat_replication' -postgres_zabbix_modern_stat_replication_bytelag: 'SELECT pg_wal_lsn_diff(pg_current_wal_lsn(), sent_lsn) as byte_lag FROM pg_stat_replication' - -pg_synchronous_commit: "off" -pg_fsync: True - -# pg_backup_service was in fact not useful on ZFS backed servers, deactivating it by default: -pg_backup_service: False - -# pg user password hash -pg_md5: false diff --git a/ansible/roles/postgres/defaults/main/postgres_only.yml b/ansible/roles/postgres/defaults/main/postgres_only.yml deleted file mode 100644 index 07ddc9b3f..000000000 --- a/ansible/roles/postgres/defaults/main/postgres_only.yml +++ /dev/null @@ -1,15 +0,0 @@ ---- -# file: roles/postgres/defaults/main/postgres_only.yml - -# default settings for startup parameters -pg_port: 5432 -pg_cluster_name: "main" -pg_listen_addresses: "localhost" -pg_log_directory: "/var/log/postgresql" -pg_ssl: true - -# default value should be fine -pg_data_directory: "/var/lib/postgresql/{{ pg_version }}/{{ pg_cluster_name }}" -pg_conf_dir: "{{ pg_data_directory }}/postgresql.conf.d" -pg_configuration_file_restart_required: "{{ pg_data_directory }}/restart_required.conf" -pg_recovery: false diff --git a/ansible/roles/postgres/files/pg-backup.service b/ansible/roles/postgres/files/pg-backup.service deleted file mode 100644 index 1e951d996..000000000 --- a/ansible/roles/postgres/files/pg-backup.service +++ /dev/null @@ -1,19 +0,0 @@ -# ansible_managed - -[Unit] -Description=PostgreSQL start/stop backup service -Documentation=https://www.postgresql.org/docs/current/continuous-archiving.html#BACKUP-LOWLEVEL-BASE-BACKUP -Documentation=https://www.postgresql.org/docs/current/functions-admin.html#FUNCTIONS-ADMIN-BACKUP -After=multi-user.target postgres.service - -[Service] -Type=simple -User=postgres -Group=postgres -Environment=PYTHONUNBUFFERED=1 -ExecStart=/usr/local/bin/pg_backup_service.py -SyslogIdentifier=pg_backup_service -TimeoutStopSec=300 - -[Install] -WantedBy=multi-user.target diff --git a/ansible/roles/postgres/files/pg_backup_service.py b/ansible/roles/postgres/files/pg_backup_service.py deleted file mode 100644 index 2c50256fc..000000000 --- a/ansible/roles/postgres/files/pg_backup_service.py +++ /dev/null @@ -1,174 +0,0 @@ -#!/usr/local/venvs/pg_backup_service/bin/python - -import argparse -import logging -import signal -import sys -import textwrap -import time -from math import floor, log -from typing import Any, Union - -from packaging.version import Version -from pgcos import PgUtil - - -class PgBackupService: - __slots__ = ["args", "debug", "dryrun", "logger", "start_time", "delay", "password", "pg", "version"] # noqa: RUF023 - - def __init__(self): - self.args = self.get_args() - self.debug = self.args.debug - self.dryrun = self.args.debug - self.logger = self.configure_logger() - self.start_time = time.time() - self.delay = 5 - self.pg = PgUtil( - { - "host": "/var/run/postgresql", - "database": "postgres", - "user": "postgres", - "application_name": f"{self.__class__.__name__}", - } - ) - self.version = self.get_server_major_version() - self.logger.info(f"{self.__class__.__name__} instance created, running against postgres {self.version}") - - signal.signal(signal.SIGTERM, self.__handle_signal) - signal.signal(signal.SIGINT, self.__handle_signal) - - @staticmethod - def format_time(seconds: Union[int, float]) -> str: - """Return human readable string given seconds input.""" - if seconds < 1: - power = 0 - else: - power = floor(log(seconds, 60)) - return f"{round(seconds / 60 ** power, 2)} {['s', 'm', 'h'][int(power)]}" - - @staticmethod - def get_args() -> argparse.Namespace: - """Parse and return CLI arguments.""" - parser = argparse.ArgumentParser( - description=textwrap.dedent( - """ - Utility to start and stop postgres backup using the low level API. - Intended to be run as a systemd service as postgres user. - Cleanup is done on SIGTERM and SIGINT. - https://www.postgresql.org/docs/current/continuous-archiving.html#BACKUP-LOWLEVEL-BASE-BACKUP - https://www.postgresql.org/docs/current/functions-admin.html#FUNCTIONS-ADMIN-BACKUP - """ - ), - formatter_class=argparse.RawDescriptionHelpFormatter, - ) - parser.add_argument("--debug", help="debug mode", action="store_true") - parser.add_argument("--dryrun", help="show what would be done, but do nothing", action="store_true") - return parser.parse_args() - - def configure_logger(self) -> logging.Logger: - """Configure the logger.""" - logger = logging.getLogger("__name__") - log_format = f"{'DRYRUN - ' if self.dryrun else ''}{'%(levelname)s - ' if self.debug else ''}%(message)s" - logging.basicConfig( - format=log_format, - datefmt="%Y-%m-%d %H:%M:%S", - level=logging.INFO, - handlers=[logging.StreamHandler()], - force=True, - ) - if self.debug: - logger.setLevel(logging.DEBUG) - return logger - - def pg_connect(self) -> None: - """Connect to postgresql database.""" - error_log = "could not connect to local postgres" - try: - self.pg.connect() - if not self.pg.is_connected(): - self.logger.error(f"{error_log}") - sys.exit(1) - except Exception as err: - self.logger.error(f"{error_log}: {err}") # noqa: TRY400 - sys.exit(1) - - def get_server_major_version(self) -> Version: - """Retrieve postgres server version.""" - statement = "SELECT CURRENT_SETTING('server_version')" - self.pg_connect() - raw_version = self.pg.execute_fetch_one(statement=statement)[0] - return Version(raw_version.split(" ")[0]) - - def __handle_signal(self, sig_num: int, curr_stack_frame: Any): - """Handle signal received, to use exclusively in signal.signal().""" - self.logger.info(f"{signal.strsignal(sig_num)} received") - self.stop() - - def start(self) -> None: - """Start postgres backup.""" - label = "pitr_snapshot" - data = [label] - - if self.version >= Version("15"): - statement = "SELECT PG_BACKUP_START(%s, true)" - else: - statement = "SELECT PG_START_BACKUP(%s, true, false)" - - self.pg_connect() - - if self.debug: - self.logger.debug(f"{self.pg.mogrify(statement=statement, data=data)}") - elif not self.debug and self.dryrun: - self.logger.info(f"{self.pg.mogrify(statement=statement, data=data)}") - - while True: - if not self.dryrun: - try: - pg_backup_start = self.pg.execute_fetch_one(statement=statement, data=data)[0] - self.logger.info(f"backup started at WAL location '{pg_backup_start} with label '{label}'") - except Exception as err: - if "WAL generated with full_page_writes=off was replayed" in str(err): - self.logger.info( - "starting a backup an a standby with full_page_writes=off on the primary is not possible" - ) - sys.exit() - if "ERROR: a backup is already in progress" not in str(err): - self.logger.error(err) # noqa: TRY400 - sys.exit(1) - time.sleep(self.delay) - self.logger.info("backup in progress...") - - def stop(self) -> None: - """Stop postgres backup gracefully.""" - self.logger.info("stopping backup") - - if self.version >= Version("15"): - statement = "SELECT PG_BACKUP_STOP()" - else: - statement = "SELECT PG_STOP_BACKUP(false)" - - if self.debug: - self.logger.debug(f"{self.pg.mogrify(statement=statement)}") - elif not self.debug and self.dryrun: - self.logger.info(f"{self.pg.mogrify(statement=statement)}") - - if not self.dryrun: - try: - pg_stop_backup = self.pg.execute_fetch_one(statement)[0] - except Exception as err: - self.logger.error(err) # noqa: TRY400 - sys.exit(1) - self.logger.info(f"backup finished:\n{pg_stop_backup}") - - end_time = time.time() - self.logger.info(f"service ran for {self.format_time(end_time - self.start_time)}") - sys.exit(0) - - -def main(): - service = PgBackupService() - service.start() - - -if __name__ == "__main__": - main() diff --git a/ansible/roles/postgres/files/pg_connection_reaper.sh b/ansible/roles/postgres/files/pg_connection_reaper.sh deleted file mode 100644 index 00fb6d261..000000000 --- a/ansible/roles/postgres/files/pg_connection_reaper.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/bash -set -euo pipefail - -CURRENT_USER=$(whoami) - -LOG="/tmp/pg_connection_reaper_${CURRENT_USER}.log" - -if [[ "${CURRENT_USER}" == "postgres" ]]; then - prefix="" -else - prefix="sudo -u postgres" -fi - -echo "$(date '+%y-%m-%d %H:%M:%S.%N') - BEGIN pg connection reaper" | tee -a $LOG - -threshold=$(($(nproc) * 2)) - -active_connection_count=$($prefix psql -Atc "SELECT count(*) FROM pg_stat_activity WHERE state = 'active'") - -WHERE="state = 'active' AND usename NOT IN ('postgres','replica','rewind') AND usename NOT LIKE ('pitr%') AND application_name NOT IN ('pg_receivewal', 'pg_dump')" - -if [[ ${active_connection_count} -gt $threshold ]]; then - CSV=/srv/log/main/pg_connection_reaper_$(date "+%Y%m%d_%H%M%S").csv - echo "$(date '+%y-%m-%d %H:%M:%S.%N') - current number of active connections is ${active_connection_count}, which is more than the double of threads ($threshold)" | tee -a $LOG - echo "$(date '+%y-%m-%d %H:%M:%S.%N') - dumping current active queries to $CSV" | tee -a $LOG - $prefix psql -Atc "COPY(SELECT * from pg_stat_activity WHERE $WHERE) to stdout delimiter ';' csv" | $prefix tee $CSV - TERMINATE="SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE $WHERE" - echo "$(date '+%y-%m-%d %H:%M:%S.%N') - $TERMINATE" | tee -a $LOG - $prefix psql -Atc "$TERMINATE" -else - echo "$(date '+%y-%m-%d %H:%M:%S.%N') - current number of connection is ${active_connection_count}, which is inferior to $threshold, doing nothing" | tee -a $LOG -fi - -echo "$(date '+%y-%m-%d %H:%M:%S.%N') - END pg connection reaper" | tee -a $LOG diff --git a/ansible/roles/postgres/files/pg_replication_slots_discovery.sh b/ansible/roles/postgres/files/pg_replication_slots_discovery.sh deleted file mode 100644 index 7cf6ec781..000000000 --- a/ansible/roles/postgres/files/pg_replication_slots_discovery.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash - -if [[ "$(systemctl show --value --property UnitFileState patroni)" == "enabled" ]]; then - filter="WHERE slot_name like 'pitr%'" -fi -/usr/bin/sudo -u postgres /usr/bin/psql -p $1 -Atc "select slot_name from pg_replication_slots $filter" |awk 'BEGIN{printf "{\"data\":["}; {printf c"{\"{#SLOT}\":\""$1"\"}";c=","}; END{print "]}"}' diff --git a/ansible/roles/postgres/files/pg_stat_database.sh b/ansible/roles/postgres/files/pg_stat_database.sh deleted file mode 100644 index c5a5d51ec..000000000 --- a/ansible/roles/postgres/files/pg_stat_database.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash - -TMPDIR=/dev/shm - -if [[ -z $1 ]]; then - PGPORT=5432 -else - PGPORT=$1 -fi - -cache_prefix=$TMPDIR/zabbix-pg_stat_database-${PGPORT} - -/usr/bin/sudo -u postgres /usr/bin/psql -p $PGPORT -d postgres -tc "copy (select * from pg_stat_database where datname not like 'template%' and datname not like 'postgres') to stdout delimiter ';' csv;" > ${cache_prefix}.tmp -if [[ $? -ne 0 ]]; then - echo "1" - exit 1 -else - rsync -t ${cache_prefix}.tmp ${cache_prefix}.cache - if [[ $? -ne 0 ]]; then - echo "1" - exit 1 - fi -fi -echo "0" diff --git a/ansible/roles/postgres/files/zabbix_postgresql.conf b/ansible/roles/postgres/files/zabbix_postgresql.conf deleted file mode 100644 index 47d048314..000000000 --- a/ansible/roles/postgres/files/zabbix_postgresql.conf +++ /dev/null @@ -1,119 +0,0 @@ -# postgresql - -# Get the PostgreSQL version -UserParameter=psql.version[*],/usr/bin/sudo -u postgres /usr/bin/psql -p $1 -A -t -c "show server_version" -# Get the total number of Server Processes that are active -UserParameter=psql.server_processes[*],/usr/bin/sudo -u postgres /usr/bin/psql -p $1 -A -t -c "select sum(numbackends) from pg_stat_database" -# Get the total number of commited transactions -UserParameter=psql.tx_commited[*],/usr/bin/sudo -u postgres /usr/bin/psql -p $1 -A -t -c "select sum(xact_commit) from pg_stat_database" -# Get the total number of rolled back transactions -UserParameter=psql.tx_rolledback[*],/usr/bin/sudo -u postgres /usr/bin/psql -p $1 -A -t -c "select sum(xact_rollback) from pg_stat_database" - -# replication status -# MASTER: pg_stat_replication view: gives the number of current replication streams -UserParameter=psql.replication_state[*],/usr/bin/sudo -u postgres /usr/bin/psql -p $1 -tc "select count(1) from pg_stat_replication where state = 'streaming' and application_name = 'walreceiver';" -# Get the number of current connections to postgres -UserParameter=psql.current_connections[*],/usr/bin/sudo -u postgres /usr/bin/psql -p $1 -A -t -c "select count(*) from pg_stat_activity;" -# max_connection parameter -UserParameter=psql.max_connections[*],/usr/bin/sudo -u postgres /usr/bin/psql -p $1 -A -t -c "show max_connections;" - -# type of connections: -UserParameter=psql.connstate[*],/usr/bin/sudo -u postgres /usr/bin/psql -p $2 -A -t -c "select count(*) from pg_stat_activity where state = '$1';" - -# SLAVE replication lag in second, this can vary widely when there is no activity on the master -UserParameter=psql.replication_timelag[*],/usr/bin/sudo -u postgres /usr/bin/psql -p $1 -A -t -c "SELECT extract(EPOCH FROM (clock_timestamp() - pg_last_xact_replay_timestamp())) AS time_lag;" - -# Discovery rule for databases -UserParameter=pgsql.db.discovery[*],/etc/zabbix/scripts/dyn_json.sh DBNAME $(/usr/bin/sudo -u postgres psql -p $1 -d postgres -A -t -c "SELECT datname FROM pg_database;"|grep -v template |grep -v postgres) - -# Get the size of a Database (in bytes) -UserParameter=psql.db_size[*],/usr/bin/sudo -u postgres /usr/bin/psql -A -t -c "select pg_database_size('$1')" -# Get the size of the target table for a specific database ($1 = db, $2 = table) -UserParameter=psql.db_table_size[*],/usr/bin/sudo -u postgres psql -d $1 -A -t -c "select sum(pg_relation_size(inhrelid::regclass)) from pg_inherits where inhparent='$2'::regclass::oid;" -# Get the maximum number of connection for a specific database (-1 = no limit) -UserParameter=psql.db_datconnlimit[*],/usr/bin/sudo -u postgres /usr/bin/psql -A -t -c "select datconnlimit from pg_database where datname = '$1'" - -#################################################################################################### -# BEGIN pg_stat_database stats -# Get number of active connections for a specified database -UserParameter=psql.db_connections[*],grep ";$1;" /dev/shm/zabbix-pg_stat_database-$2.cache| cut -d';' -f3 -# xact_commit -UserParameter=psql.db_xact_commit[*],grep ";$1;" /dev/shm/zabbix-pg_stat_database-$2.cache| cut -d';' -f4 -# xact_rollback -UserParameter=psql.db_xact_rollback[*],grep ";$1;" /dev/shm/zabbix-pg_stat_database-$2.cache| cut -d';' -f5 -# Number of disk blocks read in this database -UserParameter=psql.db_blks_read[*],grep ";$1;" /dev/shm/zabbix-pg_stat_database-$2.cache| cut -d';' -f6 -# Number of Number of times disk blocks were found already in the buffer cache, so that a read was not necessary -UserParameter=psql.db_blks_hit[*],grep ";$1;" /dev/shm/zabbix-pg_stat_database-$2.cache| cut -d';' -f7 -# Get number of tuples returned for a specified database -UserParameter=psql.db_returned[*],grep ";$1;" /dev/shm/zabbix-pg_stat_database-$2.cache| cut -d';' -f8 -# Get number of tuples fetched for a specified database -UserParameter=psql.db_fetched[*],grep ";$1;" /dev/shm/zabbix-pg_stat_database-$2.cache| cut -d';' -f9 -# Get number of tuples inserted for a specified database -UserParameter=psql.db_inserted[*],grep ";$1;" /dev/shm/zabbix-pg_stat_database-$2.cache| cut -d';' -f10 -# Get number of tuples updated for a specified database -UserParameter=psql.db_updated[*],grep ";$1;" /dev/shm/zabbix-pg_stat_database-$2.cache| cut -d';' -f11 -# Get number of tuples deleted for a specified database -UserParameter=psql.db_deleted[*],grep ";$1;" /dev/shm/zabbix-pg_stat_database-$2.cache| cut -d';' -f12 -# Number of queries canceled due to conflicts with recovery in this database. (Conflicts occur only on standby servers; see pg_stat_database_conflicts for details.) -UserParameter=psql.db_conflicts[*],grep ";$1;" /dev/shm/zabbix-pg_stat_database-$2.cache| cut -d';' -f13 -# Number of temporary files created by queries in this database. All temporary files are counted -UserParameter=psql.db_temp_files[*],grep ";$1;" /dev/shm/zabbix-pg_stat_database-$2.cache| cut -d';' -f14 -# Total amount of data written to temporary files by queries in this database. All temporary files are counted -UserParameter=psql.db_temp_bytes[*],grep ";$1;" /dev/shm/zabbix-pg_stat_database-$2.cache| cut -d';' -f15 -# Number of deadlocks detected in this database -UserParameter=psql.db_deadlocks[*],grep ";$1;" /dev/shm/zabbix-pg_stat_database-$2.cache| cut -d';' -f16 -# Time spent reading data file blocks by backends in this database, in milliseconds -UserParameter=psql.db_blk_read_time[*],grep ";$1;" /dev/shm/zabbix-pg_stat_database-$2.cache| cut -d';' -f17 -# Time spent writing data file blocks by backends in this database, in milliseconds -UserParameter=psql.db_blk_write_time[*],grep ";$1;" /dev/shm/zabbix-pg_stat_database-$2.cache| cut -d';' -f18 -# END pg_stat_database stats -#################################################################################################### - - -# generic (unused) -UserParameter=psql_stat_db[*],/usr/bin/sudo -u postgres /usr/bin/psql -A -t -c "select $2 from pg_stat_database where datname = '$1'" - -# create cache file -UserParameter=psql.pg_stat_database[*],/etc/zabbix/scripts/pg_stat_database.sh $1 - -# replication slot discovery, $1 = port -UserParameter=pgsql.replication_slots.discovery[*],/etc/zabbix/scripts/pg_replication_slots_discovery.sh $1 -# replication slot info, $1 = port, $2 = slot_name, $3 = column -UserParameter=pgsql.replication_slots.info[*],/usr/bin/sudo -u postgres /usr/bin/psql -p $1 -Atc "select $3 from pg_replication_slots where slot_name = '$2'" - -# replications discovery, $1 = port -UserParameter=pgsql.stat_replication.discovery[*],/usr/bin/sudo -u postgres /usr/bin/psql -p $1 -Atc "select client_addr from pg_stat_replication" | awk 'BEGIN{printf "{\"data\":["}; {printf c"{\"{#REPLICATION}\":\""$$1"\"}";c=","}; END{print "]}"}' -# replication info, $1 = port, $2 = client_addr, $3 = column -UserParameter=pgsql.stat_replication.info[*],/usr/bin/sudo -u postgres /usr/bin/psql -p $1 -Atc "select $3 from pg_stat_replication where client_addr = '$2'" - - -#################################################################################################### -# postgresql live configuration monitoring - -# monitor the size of a postgres parameter that is dependant on the block_size (ex: shared_buffers) -UserParameter=pgsql.parameter.block_size.get[*],/usr/bin/sudo -u postgres /usr/bin/psql -p $1 -Atc "select setting::bigint * current_setting('block_size')::bigint from pg_settings where name='$2';" -# monitor a parameter in its native unit (usually ms for time based parameters for example) -UserParameter=pgsql.parameter.get[*],/usr/bin/sudo -u postgres /usr/bin/psql -p $1 -Atc "select setting from pg_settings where name='$2';" -# use a simple "show" for parameters that don't have strange unit (ex: autovacuum_freeze_max_age, etc) -UserParameter=pgsql.parameter.show[*],/usr/bin/sudo -u postgres /usr/bin/psql -p $1 -Atc "show $2;" - - -#################################################################################################### -# get the number of CPU threads on the system -UserParameter=pgsql.nproc,nproc - -# average duration of currently active statement, excluding the ones from replication and system users -UserParameter=pgsql.average_user_active_transaction_duration[*],echo -n 0 && /usr/bin/sudo -u postgres /usr/bin/psql -p $1 -Atc "select EXTRACT(EPOCH FROM avg(now() - xact_start)) from pg_stat_activity where state = 'active' and usename not like '%replica%' and usename not like '%pitr%' and usename <> 'postgres'" - -# extract statitics from the postgres statistics collector - -# pg_stat_slru -UserParameter=pgsql.stats.pg_stat_slru.discovery[*],/etc/zabbix/scripts/dyn_json.sh NAME $(sudo -u postgres psql -p $1 -Atc "SELECT name FROM pg_stat_slru") -UserParameter=pgsql.stats.pg_stat_slru[*],sudo -u postgres psql -p $1 -Atc "SELECT $3 from pg_stat_slru where name = '$2'" -# this item is the same as the previous one, but we want to display the rate with the previous item and the sum with this one -# and zabbix doesn't allow the usage of the same userparameter for different items -UserParameter=pgsql.stats.pg_stat_slru_sum[*],sudo -u postgres psql -p $1 -Atc "SELECT $3 from pg_stat_slru where name = '$2'" - -# pg-backup.service status -UserParameter=systemd.unit.pg-backup,sudo systemctl is-failed pg-backup.service diff --git a/ansible/roles/postgres/handlers/main.yml b/ansible/roles/postgres/handlers/main.yml deleted file mode 100644 index dca4a8f25..000000000 --- a/ansible/roles/postgres/handlers/main.yml +++ /dev/null @@ -1,23 +0,0 @@ ---- -# file: roles/postgres/handlers/main.yml - -- name: "reload postgres" - systemd: - name: "postgresql@{{ pg_version }}-{{ pg_cluster_name }}.service" - state: reloaded - when: ansible_service_mgr == "systemd" - -- name: "restart postgres" - systemd: - name: "postgresql@{{ pg_version }}-{{ pg_cluster_name }}.service" - state: restarted - when: ansible_service_mgr == "systemd" - -- name: restart zabbix_agent - systemd: - name: zabbix-agent - state: restarted - -- name: "systemctl daemon-reload" - systemd: - daemon_reload: true diff --git a/ansible/roles/postgres/meta/main.yml b/ansible/roles/postgres/meta/main.yml deleted file mode 100644 index aaa13a4a4..000000000 --- a/ansible/roles/postgres/meta/main.yml +++ /dev/null @@ -1,10 +0,0 @@ ---- -# file: roles/postgres/meta/main.yml - -dependencies: - - role: postgres_repo - tags: ['postgres'] - - role: locales - vars: - locales_locales: "{{ [pg_default_encoding] }}" - when: pg_default_encoding is defined diff --git a/ansible/roles/postgres/readme.md b/ansible/roles/postgres/readme.md deleted file mode 100644 index c016b2570..000000000 --- a/ansible/roles/postgres/readme.md +++ /dev/null @@ -1,386 +0,0 @@ -# Postgresql role - - -* [Postgresql role](#postgresql-role) - * [Mandatory parameter](#mandatory-parameter) - * [Important note about ZFS](#important-note-about-zfs) - * [Important about the speed optimization that comes from `synchronous_commit = off`](#important-about-the-speed-optimization-that-comes-from-synchronous_commit--off) - * [Optional parameters](#optional-parameters) - * [Change port and cluster name](#change-port-and-cluster-name) - * [Listen to a different IP address than localhost](#listen-to-a-different-ip-address-than-localhost) - * [Deactivate ssl](#deactivate-ssl) - * [Main config](#main-config) - * [Special note about the `fsync` parameter](#special-note-about-the-fsync-parameter) - * [SSD config](#ssd-config) - * [Logging](#logging) - * [Archive mode and replication as master](#archive-mode-and-replication-as-master) - * [Replication as slave](#replication-as-slave) - * [Disable backup service](#disable-backup-service) - * [user handling](#user-handling) - * [database handling](#database-handling) - * [pg_hba.conf](#pg_hbaconf) - - -## Mandatory parameter -You need to define the postgresql version that you want to install: -``` -pg_version: 17 -``` -## Important note about ZFS -This role does some optimization if it determines that the filesystem containing the cluster is ZFS: -- For postgres: -``` -full_page_writes = off -wal_compression = off -``` -- On postgresql version >= 12, you also have: -``` -wal_recycle = off -wal_init_zero = off -``` -- If you are on bare metal, it will also tune the postgres' dataset: -``` -logbias: throughput -recordsize: 16K -``` - -## Important about the speed optimization that comes from `synchronous_commit = off` -By default, this role will set the postgres parameter `synchronous_commit = off`. - -This means that postgresql will tell the system that a commit is done before it is persisted to disk, speeding a great deal the write operations. The commit happens at worst 600ms after that point, and thus allow the grouping of many commit operations in one sync. - -This also means that in case of a crash, you can lose up to 600ms worth of commits. If this is unacceptable, use this variable to change the behavior: -``` -pg_synchronous_commit: "on" -``` -This will force a sync after every commit, which is the default postgresql behavior. - -## Optional parameters - -### Default encoding -By default, the encoding is set to `en_US.UTF-8`, you can change it by setting this variable: -``` -pg_default_encoding: "en_US.UTF-8" -``` - -### Change port and cluster name -Previously those parameters were mandatory, we now define those as the default, which you can change: -``` -pg_port: 5432 -pg_cluster_name: main -``` -### Listen to a different IP address than localhost -By default, this role makes postgres only listen to localhost with this variable: -``` -pg_listen_addresses: localhost -``` -If you need remote access, either enter a list of addresses to listen to: -``` -pg_listen_addresses: "localhost,a.b.c.d" -``` -Or make postgresql listen on all addresses (limit this to internal servers only): -``` -pg_listen_addresses: "*" -``` -### Deactivate ssl -Set this variable to `false to deactivate ssl with the snakeoil certificate: -``` -pg_ssl: False -``` -### Main config -The following variables can be defined to override the default from postgres, the ansible variable are the same as the postgres variables, only idfference is the suffix **"pg_"** in front: -- pg_autovacuum_freeze_max_age -- pg_autovacuum_max_workers -- pg_autovacuum_vacuum_scale_factor -- pg_autovacuum_work_mem -- pg_autovacuum_vacuum_cost_delay -- pg_autovacuum_vacuum_cost_limit -- pg_checkpoint_timeout -- pg_effective_cache_size -- pg_huge_pages -- pg_maintenance_work_mem -- pg_max_connections -- pg_shared_buffers -- pg_timezone -- pg_work_mem -- pg_max_wal_size -- pg_min_wal_size -- pg_idle_in_transaction_session_timeout (pg 9.6+ required) -- pg_max_parallel_workers_per_gather (pg 9.6+ required) -- pg_max_worker_processes (pg 9.6+ required) - - -Note that the default `pg_shared_buffers` is set to 128MB in this role, which should be the default since postgres 9.3 (up to postgres 12) but is strangely set to 8MB on Debian. -### Special note about the `fsync` parameter -The `fsync` parameter is special because if set to `off`, postgres runs in insecure mode that will generate database corruption in case of postgres or OS crash. So this parameter should always remains untouched to the default and safe value of `on`. - -But it can be very interesting to deactivate sync *BEFORE* putting the cluster into production, for example to load a database dump more quickly on a new cluster. - -The best way to not forget this parameter is to not commit it to git and use an extra-vars to temporary change it instead: -``` -ansible-playbook someplaybook --limit somehost --extra-vars "{ 'pg_fsync' : false }" -``` -If you really know what you are doing, you can instead change the default variable which is: -``` -pg_fsync: True -``` -Set it to `False` to deactivate fsync but don't forget to remove this setting before putting the cluster in production. - -When you switch fsync back to on, you must ensure that all data have been written to the system, follow instructions on the official documentation for the fsync parameter to do the right thing: https://www.postgresql.org/docs/current/runtime-config-wal.html - -### SSD config -A default ssd config can be activated by setting the following variable to true: -``` -pg_ssd: True -``` -The config that will be added is: -``` -seq_page_cost = 1.0 -random_page_cost = {{ pg_random_page_cost }} -effective_io_concurrency = '{{ pg_effective_io_concurrency }}' -``` -You can adjust the **pg_random_page_cost** and **pg_effective_io_concurrency** if you want, the default are: -``` -pg_random_page_cost: 1.2 -pg_effective_io_concurrency: 20 -``` - -### Logging -By default no logging is active, you can activate it by setting `pg_logging: True`: -``` -pg_logging: True -``` -You can also change the log directory, bellow is the default: -``` -pg_log_directory: "/var/log/postgresql" -``` -Currently this will activate a basic logging configuration which logs everything, you can look at the template to see exactly what it does. - -The default suffix for log files is: -``` -pg_log_filename_suffix: '%a_%H' -``` -This will keep 1 week worth of logs (%a is the day of the week: "Mon", "Tue", etc). You can change this value if needed, for example if you want to only keep 1 day worth of logs: -``` -pg_log_filename_suffix: '%H' -``` -The default parameter is to log everything (value = 0), you can change that by setting a value in milliseconds on this variable, if you do so, only query that takes longer than that will be logged, this has a positive performance impact: -``` -pg_log_min_duration_statement: '0' -``` -### Archive mode and replication as master -You can activate either the replication or archive+replication. - -To activate only the replication, which allow the creation of a replica and streaming replication, use this variable: -``` -pg_replication: True -``` - -You should also create some physical replication slots with this variable: -``` -pg_replication_slot_list: - - some_slot - - an_other_slot -``` - -If you need to archive the WAL, you need to prepare in advance a server where you will send the WAL files. Set up an account on the remote server and copy the postgres ssh pubkey from the origin server to the archive one. Make sure that the ssh connection is working without any prompt, else the archive command will fail. - -You then need to set pg_archive_command: -``` -pg_archive_command: 'chmod 640 %p && rsync -e "ssh -o StrictHostKeyChecking=yes -o BatchMode=yes" -a %p {{ ansible_hostname }}@srv-227.talas.com:/srv/pg/veza/pitr/{{ ansible_hostname }}/{{ pg_port }}/wal/%f' -``` -The pg_archive_command example above is a typical example. You need to set the ssh option so that the rsync command will fail immediately if for any reason the authentication via pubkey doesn't work, else it may get stuck with a password prompt. - -Keep in mind that setting the archive mode will also activate the replication mode because it is needed to have correct WAL files. - -### Replication as slave - -To create a replication slot on the slave you must use this variable: -``` -pg_replication_slot_list_replica: - - some_slot - - an_other_slot -``` - -If you want to make sure some replication slots are ABSENT, you can use this list: -``` -pg_replication_slot_to_remove: - - some_slot - - an_other_slot -``` -### Disable backup service - -This script was created to put the postgresql in backup mode to ensure coherency in the filesystem layer during snapshot. But this was a misunderstanding and this is in fact not useful at all with a ZFS backed database. It's still there for now and can be activated by setting this variable but may be removed in the future: -``` -pg_backup_service: True -``` - -## user handling - -The password encryption use `scram-sha-256` by default if md5 is needed set the following "pg_md5" var to true. - -It's important to set the `method` of authentifcation in pg_hba to `md5` after this. - -Define any number of user to create them like this (attrib are the rights of the user), look at: -* https://docs.ansible.com/ansible/latest/collections/community/postgresql/postgresql_user_module.html -* https://docs.ansible.com/ansible/latest/collections/community/postgresql/postgresql_membership_module.html -* https://docs.ansible.com/ansible/latest/collections/community/postgresql/postgresql_privs_module.html - -for all possible values, all values are optional: -``` -pg_users: - - name: alfred - password: ALFRED - attrib: LOGIN,CREATEDB - - name: boris - password: BORIS - attrib: LOGIN - - name: clair - password: CLAIR - attrib: LOGIN - db: somedb - conn_limit: 10 - groups: somegroup - priv: somepriv - role-that-will-be-used-as-a-group: - attrib: NOLOGIN -``` - -The password can be generated automatically by setting it to "auto": -``` -pg_users: - - name: "matrix_synapse" - password: "auto" -``` -You can find the password in hashicorpvault in this way: `postgres_user_{{ user.name }}_password` - -## database handling - -Define databases that you want to create like this: -``` -pg_databases: - - name: alpha - owner: alfred - - name: beta - owner: boris - - name: gamma - owner: clair - conn_limit: 50 # optional, the default is 80 - extension_list: [ 'fuzzystrmatch' ] # optional, remember that this will be added to the default extensions from the pg_extensions list if defined -``` -This will create each database with the correct owner and make sure it stays like that on successive ansible runs. - -## shared librairies handling - -Define shared librairies that you want to to preload like this: -It requires postgres to restart; however, it will fail if the library is not yet installed. - -``` -pg_shared_preload_libraries: - - timescaledb -``` - -## pg_hba.conf - -- Define this varibale to create entries in pg_hba.conf: -``` -pg_hba_config: - - user: foo - type: host - database: all - address: 10.12.0.0/16 - method: "scram-sha-256" - - user: bar - type: hostssl - database: mydb - address: 10.12.1.1/32 - method: "scram-sha-256" -``` -With the previous values, the result in the pg_hba.conf file will look like this: -``` -host all foo 10.12.0.0/16 scram-sha-256 # description -hostssl mydb bar 10.12.1.1/32 scram-sha-256 # hello world -``` -- The default local access via unix socket is this line: -``` -local all all scram-sha-256 -``` -Which you can change by setting this variable: -``` -pg_local_auth_method: "peer" # you can use any value accepted by postgres, see https://www.postgresql.org/docs/current/auth-pg-hba-conf.html -``` - -## Extensions -You can add any number of extension to the `template1` database. This database is used to create all other databases by default, so any database created will have the same extensions. - -To define the extensions you need, simply create this list: -``` -pg_extensions: - - pg_trgm - - btree_gist -``` -You can also define extensions per database, see **database handling**. Per database is useful because it can add extension to an existing database while pg_extensions only apply to databases created latter. - -# setting up a replica -To set up a replica, you must first make sure that all the `pg_` variables from the master are used as well for the future replica (set them up in a group_vars or make a symlink between the master and the replica). - -Then install a new standalone server without specifying a `pg_replication_role` variable. - -After that, you need to connect yourself to the future replica server and: -1. stop postgresql -2. delete the postgres data directory -3. initiate the slave with `pg_basebackup`, the command will look like `pg_basebackup --write-recovery-conf --checkpoint=fast --label="IT-XXXXXX" --progress --slot=replication_slot_name --host=primary_fqdn --username=replication_username --pgdata=/var/lib/postgresql/11/main` (look at the man page for options) - -At this point, you need to define the following variables (you can define them at the same level as the other `pg_` variables, they will be ignored by the master): -``` -postgres_primary_conninfo_host: "master fqdn" -postgres_primary_conninfo_user: "the replication user" -postgres_primary_conninfo_password: "the replication user password" -postgres_primary_slot_name: "the replication slot name" -``` -You can also change the default for the following variable if needed: -``` -postgres_primary_conninfo_port: 5432 -postgres_primary_conninfo_sslmode: "require" -``` -If you have a WAL repository, you can also define a restore_command: -``` -pg_restore_command: 'rsync -e "ssh -o StrictHostKeyChecking=yes -o BatchMode=yes" -a login@wal_repo_fqdn:/path_to_wall_repo/%f %p' -``` -You can now change the `pg_replication_role` *only for the replica*: -``` -pg_replication_role: "replica" -``` -Then rerun the role. This will create the `recovery.conf` or the `standby.signal` file depending on the postgresql version. - -# postgresql connection reaper -This role includes a script for Zabbix that is intended to be called as a remote command to purge the majority of active connections when the number of active connection is 2 times the amount of threads. - -The usage of this script is controlled by Zabbix's Actions. - -Don't forget that if you want to use it, you need to add the following variable to enable remote command execution by zabbix: -``` -zabbix_remote_command: True -``` - -# restart_required extra location -Sometimes, you need to have restart_required in a second location (different than `pg_configuration_file_restart_required`), you can use `pg_configuration_file_restart_required_extra_location` for this. - -# pg_upgrade - -In this example, we will upgrade a server running pg 14 to pg 17, adjust the versions for your use case: - -1. `apt update` -1. manually download the latest postgresql version: `apt install postgresql-17` -1. if a new cluster has been created, stop it `pg_ctlcluster stop 17 main`, then remove the directories `/var/lib/postgresql/17` and `/etc/postgresql/17` -1. if no new cluster has been created, create a new one using `pg_createcluster 17 main` -1. remove the directory `/var/lib/postgresql/17/main` and as the postgresql user, run `/usr/lib/postgresql/17/bin/initdb /var/lib/postgresql/17/main` -1. check `pg_lsclusters`, you should have the current cluster running and the new one down -1. Stop the current cluster -1. perform a ZFS or Incus/LXD snapshot if necessary -1. switch to the `postgres` user -1. run this command: adjust the job count depending on the available memory and cpu count: `/usr/lib/postgresql/17/bin/pg_upgrade --old-bindir=/usr/lib/postgresql/14/bin --new-bindir=/usr/lib/postgresql/17/bin --old-datadir=/var/lib/postgresql/14/main --new-datadir=/var/lib/postgresql/17/main --jobs=2 --link --verbose` -1. adjust the ansible variable for the new cluster version: `pg_version: 17` -1. run the postgres role -1. look at the sh scripts in the folder where you did the upgrade, you may have some post-upgrade bash script created by `pg_upgrade`, look at them and run them accordingly -1. remove the old cluster: `rm -r /var/lib/postgresql/14 /etc/postgresql/14` diff --git a/ansible/roles/postgres/tasks/build_pg_rrule.yml b/ansible/roles/postgres/tasks/build_pg_rrule.yml deleted file mode 100644 index 7ffd3021d..000000000 --- a/ansible/roles/postgres/tasks/build_pg_rrule.yml +++ /dev/null @@ -1,43 +0,0 @@ ---- -# file: roles/roles/postgres_pg_rrule/tasks/build.yml - -- name: "install build dependencies" - ansible.builtin.apt: - name: - - build-essential - - git - - libical-dev - - "postgresql-server-dev-{{ pg_version }}" - - qtcreator - - qt5-qmake - -- name: "git clone src" - ansible.builtin.git: - repo: https://github.com/petropavel13/pg_rrule.git - dest: /usr/local/src/pg_rrule - -- name: "qmake pg_rrule.pro" - ansible.builtin.command: - cmd: qmake pg_rrule.pro - chdir: "/usr/local/src/pg_rrule/src" - environment: - QT_SELECT: qt5 - -- name: "make" - community.general.make: - chdir: "/usr/local/src/pg_rrule/src" - -- name: "install" - ansible.builtin.copy: - remote_src: true - src: "{{ item.src }}" - dest: "{{ item.dest }}" - loop: - - {src: "/usr/local/src/pg_rrule/src/libpg_rrule.so.1.0.0", dest: "/usr/lib/postgresql/{{ pg_version }}/lib/pg_rrule.so"} - - {src: "/usr/local/src/pg_rrule/pg_rrule.control", dest: "/usr/share/postgresql/{{ pg_version }}/extension/pg_rrule.control"} - - {src: "/usr/local/src/pg_rrule/sql/pg_rrule.sql.in", dest: "/usr/share/postgresql/{{ pg_version }}/extension/pg_rrule--0.2.0.sql"} - -- name: "make clean" - community.general.make: - chdir: "/usr/local/src/pg_rrule/src" - target: clean diff --git a/ansible/roles/postgres/tasks/configuration.yml b/ansible/roles/postgres/tasks/configuration.yml deleted file mode 100644 index a96f1b19d..000000000 --- a/ansible/roles/postgres/tasks/configuration.yml +++ /dev/null @@ -1,93 +0,0 @@ ---- -# file: roles/postgres/tasks/configuration.yml - -- name: "config file {{ pg_data_directory }}/postgresql.conf" - template: - src: "postgresql.conf.j2" - dest: "{{ pg_data_directory }}/postgresql.conf" - owner: postgres - group: postgres - backup: yes - mode: 0600 - notify: reload postgres - tags: postgres - -- name: "duplicate config to /etc/postgresql/{{ pg_version }}/{{ pg_cluster_name }}/postgresql.conf" - template: - src: "postgresql.conf.j2" - dest: "/etc/postgresql/{{ pg_version }}/{{ pg_cluster_name }}/postgresql.conf" - owner: postgres - group: postgres - backup: yes - mode: 0600 - notify: reload postgres - when: not ansible_check_mode - tags: postgres - -- name: "{{ pg_configuration_file_restart_required }}" - template: - src: "restart_required.conf" - dest: "{{ pg_configuration_file_restart_required }}" - owner: postgres - group: postgres - backup: yes - mode: 0600 - notify: restart postgres - tags: postgres - -- name: "{{ pg_configuration_file_restart_required_extra_location }}" - template: - src: "restart_required.conf" - dest: "{{ pg_configuration_file_restart_required_extra_location }}" - owner: postgres - group: postgres - backup: yes - mode: 0600 - notify: restart postgres - when: pg_configuration_file_restart_required_extra_location is defined - tags: postgres - -- name: "logging directory" - file: - path: '{{ pg_log_directory }}' - state: directory - mode: 0755 - owner: postgres - group: postgres - notify: reload postgres - when: pg_logging - tags: postgres - -- name: "postgres user .bash_profile" - template: - src: postgres_bash_profile - dest: /var/lib/postgresql/.bash_profile - owner: postgres - group: postgres - tags: postgres - -- name: "{{ pg_data_directory }}/pg_hba.conf" - template: - backup: yes - src: pg_hba.conf - dest: "{{ pg_data_directory }}/pg_hba.conf" - owner: postgres - group: postgres - mode: 0600 - notify: reload postgres - tags: - - postgres - - pg_hba - -- name: "duplicate config to /etc/postgresql/{{ pg_version }}/{{ pg_cluster_name }}/pg_hba.conf" - template: - backup: yes - src: pg_hba.conf - dest: "/etc/postgresql/{{ pg_version }}/{{ pg_cluster_name }}/pg_hba.conf" - owner: postgres - group: postgres - mode: 0600 - notify: reload postgres - tags: - - postgres - - pg_hba diff --git a/ansible/roles/postgres/tasks/databases.yml b/ansible/roles/postgres/tasks/databases.yml deleted file mode 100644 index 37742c585..000000000 --- a/ansible/roles/postgres/tasks/databases.yml +++ /dev/null @@ -1,35 +0,0 @@ ---- -# file: roles/postgres/tasks/databases.yml - -- name: "create database(s)" - become: true - become_user: postgres - become_method: sudo - postgresql_db: - name: "{{ item.name }}" - owner: "{{ item.owner }}" - conn_limit: "{{ item.conn_limit | default(80) }}" - lc_collate: "{{ item.lc_collate | default(omit) }}" - lc_ctype: "{{ item.lc_ctype | default(omit) }}" - template: "{{ item.template | default(omit) }}" - loop: "{{ pg_databases }}" - register: postgres_create_database - tags: postgres - -- name: "database extensions per database if defined" - become: true - become_user: postgres - become_method: sudo - postgresql_ext: - name: "{{ item[1] }}" - db: "{{ item[0].name }}" - # The "default([])" parameter allows to do nothing if the "pg_databases" variable is not defined - Doc: https://docs.ansible.com/ansible/latest/user_guide/playbooks_filters.html#defaulting-undefined-variables - loop: "{{ pg_databases | default([]) | subelements('extension_list', skip_missing=True) }}" - when: - - pg_replication_role != "replica" - tags: postgres - -- name: "set fact postgres_share1_create_database" - set_fact: - postgres_share1_create_database: "{{ postgres_create_database }}" - tags: postgres diff --git a/ansible/roles/postgres/tasks/main.yml b/ansible/roles/postgres/tasks/main.yml deleted file mode 100644 index e85e32afa..000000000 --- a/ansible/roles/postgres/tasks/main.yml +++ /dev/null @@ -1,230 +0,0 @@ ---- -# file: roles/postgres/tasks/main.yml - -- name: "postgresql-common" - apt: - name: - - "postgresql-common" - update_cache: true - when: not ansible_check_mode - tags: postgres - -- name: "disable cluster auto creation" - lineinfile: - path: "/etc/postgresql-common/createcluster.conf" - line: "create_main_cluster = false" - regexp: '^create_main_cluster' - insertafter: '^#create_main_cluster ' - tags: postgres - -- name: "set default collation for cluster creation" - lineinfile: - path: "/etc/postgresql-common/createcluster.conf" - line: "initdb_options = '--locale={{ pg_default_encoding | default('en_US.UTF-8') }}'" - regexp: '^initdb_options' - insertafter: '^#initdb_options ' - tags: postgres - -- name: "postgresql packages and prerequisites" - apt: - name: - - "postgresql-{{ pg_version }}" - - "postgresql-contrib-{{ pg_version }}" - - "python3-psycopg2" - - "sshpass" - when: not ansible_check_mode - tags: postgres - -- name: "determine the file system used for {{ pg_data_directory }}" - shell: "grep \"$(df -h {{ pg_data_directory }} | awk '{ print $6 }' | grep /) \" /proc/mounts | awk '{ print $3 }'" - register: file_system - changed_when: false - check_mode: no - tags: - - postgres - - zfs - -- name: "Check if pg cluster exists" - stat: - path: "{{ pg_data_directory }}" - register: pg_cluster_exists - tags: postgres - -- name: "postgresql create cluster {{ pg_version }} {{ pg_cluster_name }}" - command: - cmd: "/usr/bin/pg_createcluster {{ pg_version }} {{ pg_cluster_name }}" - when: not pg_cluster_exists.stat.exists - tags: postgres - -- name: "configuration files" - import_tasks: configuration.yml - tags: - - postgres - - pg_hba - -- name: "import_tasks: zfs.yml" - import_tasks: zfs.yml - when: file_system.stdout == "zfs" - tags: - - postgres - - zfs - -- name: "replica configuration: recovery.conf" - import_tasks: replica.yml - when: pg_replication_role == "replica" - tags: postgres - -- name: "{{ pg_data_directory }}/recovery.signal" - file: - owner: postgres - group: postgres - mode: 0600 - path: "{{ pg_data_directory }}/recovery.signal" - state: touch - access_time: preserve - modification_time: preserve - when: - - pg_version is version('12', '>=') - - pg_recovery - tags: postgres - -- name: "make sure postgres is started (some version do not start after installation!)" - systemd: - name: "postgresql@{{ pg_version }}-{{ pg_cluster_name }}.service" - state: started - when: ansible_service_mgr == "systemd" - tags: postgres - -- name: "handle physical replication slots" - become: true - become_user: postgres - become_method: sudo - postgresql_slot: - slot_type: physical - name: "{{ item }}" - db: postgres - loop: "{{ pg_replication_slot_list }}" - when: - - pg_replication - - pg_replication_slot_list is defined - - pg_replication_role != "replica" - tags: postgres - -- name: "handle physical replication slots on the replica" - become: true - become_user: postgres - become_method: sudo - postgresql_slot: - slot_type: physical - name: "{{ item }}" - db: postgres - loop: "{{ pg_replication_slot_list_replica }}" - when: - - pg_replication - - pg_replication_slot_list_replica is defined - - pg_replication_role == "replica" - tags: postgres - -- name: "remove physical replication slots if needed" - become: true - become_user: postgres - become_method: sudo - postgresql_slot: - slot_type: physical - name: "{{ item }}" - db: postgres - state: absent - loop: "{{ pg_replication_slot_to_remove }}" - when: pg_replication_slot_to_remove is defined - tags: postgres - -- name: "use scram passwords everywhere" - set_fact: - pg_role_options: "-c password_encryption=scram-sha-256" - when: - - not pg_md5 - tags: postgres - -- name: "handle users" - include_tasks: users.yml - when: - - pg_replication_role != "replica" - - pg_users is defined - loop: "{{ pg_users }}" - loop_control: - loop_var: user - tags: postgres - -- name: "handle databases" - import_tasks: databases.yml - when: - - pg_databases is defined - - pg_replication_role != "replica" - tags: postgres - -- name: "role privs" - become: True - become_user: postgres - become_method: sudo - postgresql_privs: - role: "{{ user.name }}" - db: "{{ user.db | default('postgres') }}" - privs: "{{ user.priv }}" - objs: "{{ user.objs | default('ALL_IN_SCHEMA')}}" - when: user.priv is defined - loop: "{{ pg_users }}" - loop_control: - loop_var: user - tags: postgres - -- name: "get stats of a file" - ansible.builtin.stat: - path: "/usr/share/postgresql/{{ pg_version }}/extension/pg_rrule.control" - when: - - pg_extensions is defined - - "'pg_rrule' in pg_extensions" - register: pg_rrule_installed - tags: postgres - -- name: "import pg_rrule extansion build tasks" - import_tasks: build_pg_rrule.yml - when: - - pg_extensions is defined - - "'pg_rrule' in pg_extensions" - - not pg_rrule_installed.stat.exists - tags: postgres - -- name: "postgres extension" - become: true - become_user: postgres - become_method: sudo - postgresql_ext: - name: "{{ item }}" - db: template1 - with_items: "{{ pg_extensions }}" - when: - - pg_extensions is defined - - pg_replication_role != "replica" - tags: postgres - -- name: "include_tasks: pg_backup_service.yml" - include_tasks: pg_backup_service.yml - when: pg_backup_service - tags: - - scripts - - postgres - -- name: "check if the folder /etc/zabbix/zabbix_agentd.conf.d exists" - stat: - path: "/etc/zabbix/zabbix_agentd.conf.d" - register: zabbix_folder - tags: - - postgres - - zabbix - -- name: "import_tasks: zabbix.yml" - import_tasks: zabbix.yml - when: zabbix_folder.stat.exists - tags: - - postgres - - zabbix diff --git a/ansible/roles/postgres/tasks/pg_backup_service.yml b/ansible/roles/postgres/tasks/pg_backup_service.yml deleted file mode 100644 index c9c4771cc..000000000 --- a/ansible/roles/postgres/tasks/pg_backup_service.yml +++ /dev/null @@ -1,85 +0,0 @@ ---- -# file: roles/postgres/tasks/pg_backup_service.yml - -- name: "install python_package dependencies" - block: - - name: "set fact python_package_install_from_role" - set_fact: - python_package_install_from_role: - - package: pgcos - script: pg_backup_service.py - - name: "import role python_package" - import_role: - name: python_package - - name: "unset fact python_package_install" - set_fact: - python_package_install_from_role: [] - tags: - - scripts - - postgres - - patroni - -- name: "pip upgrade pip in virtualenv" - pip: - name: pip - extra_args: --upgrade - virtualenv: /usr/local/venvs/pg_backup_service - virtualenv_command: /usr/bin/python3 -m venv - when: not ansible_check_mode - tags: - - scripts - - postgres - - patroni - -- name: "pip install dependencies in virtualenv" - pip: - name: packaging - extra_args: --upgrade - virtualenv: /usr/local/venvs/pg_backup_service - virtualenv_command: /usr/bin/python3 -m venv - when: not ansible_check_mode - tags: - - scripts - - postgres - - patroni - -- name: "/usr/local/venvs/pg_backup_service/bin/pg_backup_service.py" - copy: - src: pg_backup_service.py - dest: /usr/local/venvs/pg_backup_service/bin/pg_backup_service.py - mode: 0755 - tags: - - scripts - - postgres - - patroni - -- name: "ln -s /usr/local/venvs/pg_backup_service/bin/pg_backup_service.py /usr/local/bin/pg_backup_service.py" - file: - state: link - src: /usr/local/venvs/pg_backup_service/bin/pg_backup_service.py - dest: /usr/local/bin/pg_backup_service.py - force: true - tags: - - scripts - - postgres - - patroni - -- name: "pg-backup.service systemd unit" - copy: - src: pg-backup.service - dest: /etc/systemd/system/pg-backup.service - notify: systemctl daemon-reload - tags: - - scripts - - postgres - - patroni - -- name: "disable pg-backup.service" - systemd: - name: pg-backup - enabled: false - when: not ansible_check_mode - tags: - - scripts - - postgres - - patroni diff --git a/ansible/roles/postgres/tasks/replica.yml b/ansible/roles/postgres/tasks/replica.yml deleted file mode 100644 index d9852c3d2..000000000 --- a/ansible/roles/postgres/tasks/replica.yml +++ /dev/null @@ -1,27 +0,0 @@ ---- -# file: roles/postgres/tasks/replica.yml - -- name: "{{ pg_data_directory }}/recovery.conf" - template: - src: "recovery.conf" - dest: "{{ pg_data_directory }}/recovery.conf" - owner: postgres - group: postgres - backup: yes - mode: 0600 - notify: restart postgres - when: pg_version is version('12', '<') - tags: postgres - -- name: "{{ pg_data_directory }}/standby.signal" - file: - owner: postgres - group: postgres - mode: 0600 - path: "{{ pg_data_directory }}/standby.signal" - state: touch - access_time: preserve - modification_time: preserve - notify: restart postgres - when: pg_version is version('12', '>=') - tags: postgres diff --git a/ansible/roles/postgres/tasks/users.yml b/ansible/roles/postgres/tasks/users.yml deleted file mode 100644 index 897ef26b3..000000000 --- a/ansible/roles/postgres/tasks/users.yml +++ /dev/null @@ -1,56 +0,0 @@ ---- -# file: roles/postgres/tasks/users.yml - -# We replace '-' by '_' only in the ansible var postgres_user_{{ user.name }}_password, not in hashicorp vault -- name: "handle secret {{ ansible_hostname }}/postgres_user_{{ user.name | replace('-', '_') }}_password" - block: - - name: "get {{ ansible_hostname }}/postgres_user_{{ user.name | replace('-', '_') | replace('.', '_') }}_password from hashicorp vault" - set_fact: - "postgres_user_{{ user.name | replace('-', '_') | replace('.', '_') }}_password": "{{ lookup('hashi_vault', 'secret=talas-kv/data/' + host_vars_location + '/' + ansible_hostname)['postgres_user_' ~ user.name ~ '_password'] }}" - rescue: - - name: "generate a random password for {{ ansible_hostname }}/postgres_user_{{ user.name | replace('-', '_') | replace('.', '_') }}_password" - set_fact: - password: "{{ lookup('password','/dev/null chars=ascii_letters,digits length=50') }}" - - name: "patching hashicorp vault with generated postgres_user_{{ user.name | replace('-', '_') | replace('.', '_') }}_password" - delegate_to: localhost - become: False - command: "vault kv patch talas-kv/{{ host_vars_location }}/{{ ansible_hostname }} postgres_user_{{ user.name }}_password={{ password }}" - register: result - ignore_errors: True - - name: "patch failed because the entry doesn't exist, creating it instead" - delegate_to: localhost - become: False - command: "vault kv put talas-kv/{{ host_vars_location }}/{{ ansible_hostname }} postgres_user_{{ user.name }}_password={{ password }}" - when: - - result.failed - - '"No value found" in result.stderr' - - name: "assign password value to postgres_user_{{ user.name | replace('-', '_') | replace('.', '_') }}_password" - set_fact: - "postgres_user_{{ user.name | replace('-', '_') | replace('.', '_') }}_password": "{{ password }}" - when: - - user.password is defined - - user.password == "auto" - tags: postgres - -- name: "role {{ user.name }}" - become: True - become_user: postgres - become_method: sudo - postgresql_user: - user: "{{ user.name }}" - password: "{% if user.password is defined and user.password == 'auto' %}{{ vars['postgres_user_' + user.name | replace('-', '_') | replace('.', '_') + '_password'] }}{% else %}{{ user.password | default(omit) }}{% endif %}" - role_attr_flags: "{{ user.attrib | default(omit) }}" - conn_limit: "{{ user.conn_limit | default(omit) }}" - environment: - PGOPTIONS: "{{ pg_role_options | default(None) }}" - tags: postgres - -- name: "role {{ user.name }} groups : {{ user.groups }}" - become: true - become_user: postgres - become_method: sudo - postgresql_membership: - user: "{{ user.name }}" - groups: "{{ user.groups }}" - when: user.groups is defined - tags: postgres diff --git a/ansible/roles/postgres/tasks/zabbix.yml b/ansible/roles/postgres/tasks/zabbix.yml deleted file mode 100644 index 09b60f532..000000000 --- a/ansible/roles/postgres/tasks/zabbix.yml +++ /dev/null @@ -1,63 +0,0 @@ ---- -# file: roles/postgres/tasks/zabbix.yml - -- name: "[zabbix] scripts" - copy: - src: "{{ item }}" - dest: "/etc/zabbix/scripts/{{ item }}" - mode: 0755 - loop: - - pg_stat_database.sh - - pg_connection_reaper.sh - - pg_replication_slots_discovery.sh - tags: - - postgres - - zabbix - -- name: "[zabbix] /etc/zabbix/zabbix_agentd.conf.d/postgresql.conf (static)" - copy: - src: zabbix_postgresql.conf - dest: /etc/zabbix/zabbix_agentd.conf.d/postgresql.conf - mode: 0644 - notify: restart zabbix_agent - tags: - - postgres - - zabbix - -- name: "[zabbix] pg < 10 specific" - set_fact: - postgres_zabbix_stat_replication_bytelag: "{{ postgres_zabbix_legacy_stat_replication_bytelag }}" - when: pg_version is version('10', '<') - tags: - - postgres - - zabbix - -- name: "[zabbix] pg >= 10 specific" - set_fact: - postgres_zabbix_stat_replication_bytelag: "{{ postgres_zabbix_modern_stat_replication_bytelag }}" - when: pg_version is version('10', '>=') - tags: - - postgres - - zabbix - -- name: "[zabbix] /etc/zabbix/zabbix_agentd.conf.d/postgresql_template.conf (template)" - template: - src: "zabbix_postgresql_template.conf" - dest: "/etc/zabbix/zabbix_agentd.conf.d/postgresql_template.conf" - mode: 0644 - notify: restart zabbix_agent - tags: - - postgres - - zabbix - -- name: "[zabbix] pg >= 13: crontab to reset the postgres SLRU stats once a day" - cron: - name: "postgres: reset SLRU stats" - hour: "00" - minute: "00" - job: 'psql -Atc "SELECT pg_stat_reset_slru(NULL)"' - user: "postgres" - when: pg_version is version('13', '>=') - tags: - - postgres - - zabbix diff --git a/ansible/roles/postgres/tasks/zfs.yml b/ansible/roles/postgres/tasks/zfs.yml deleted file mode 100644 index 13479cc5c..000000000 --- a/ansible/roles/postgres/tasks/zfs.yml +++ /dev/null @@ -1,24 +0,0 @@ ---- -# file: roles/postgres/tasks/zfs.yml - -- name: "[ZFS bare metal] find the dataset for the postgres cluster at {{ pg_data_directory }}" - shell: "grep \"$(df -h {{ pg_data_directory }} | awk '{ print $6 }' | grep /) \" /proc/mounts | awk '{ print $1 }'" - register: zfs_dataset - changed_when: false - check_mode: no - when: ansible_virtualization_role == "host" or ansible_virtualization_role == "NA" or ansible_virtualization_role is undefined - tags: - - postgres - - zfs - -- name: "[ZFS bare metal] logbias=throughput and recordsize = 8K on dataset {{ zfs_dataset.stdout }}" - zfs: - name: "{{ zfs_dataset.stdout }}" - state: present - extra_zfs_properties: - logbias: throughput - recordsize: 8192 - when: ansible_virtualization_role == "host" or ansible_virtualization_role == "NA" or ansible_virtualization_role is undefined - tags: - - postgres - - zfs diff --git a/ansible/roles/postgres/templates/pg_hba.conf b/ansible/roles/postgres/templates/pg_hba.conf deleted file mode 100644 index 9e2fbf440..000000000 --- a/ansible/roles/postgres/templates/pg_hba.conf +++ /dev/null @@ -1,20 +0,0 @@ -# {{ ansible_managed }} - -# Database administrative login by Unix domain socket -local all postgres peer - -# Unix domain socket for other users -local all all {% if pg_local_auth_method is defined %}{{ pg_local_auth_method }}{% elif pg_md5 %}md5{% else %}scram-sha-256{% endif %} - -# localhost -host all all 127.0.0.1/32 {{ 'md5' if pg_md5 else 'scram-sha-256' }} - -host all all ::1/128 {{ 'md5' if pg_md5 else 'scram-sha-256' }} - -{% if pg_hba_config is defined %} - -# other accesses -{% for item in pg_hba_config %} -{{ item.type }} {{ item.database }} {{ item.user }} {{ item.address }} {{ item.method }} -{% endfor %} -{% endif %} diff --git a/ansible/roles/postgres/templates/postgres_bash_profile b/ansible/roles/postgres/templates/postgres_bash_profile deleted file mode 100644 index 4d014e799..000000000 --- a/ansible/roles/postgres/templates/postgres_bash_profile +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/sh -# {{ ansible_managed }} -# profile for postgres user -export PGDATA={{ pg_data_directory }} -export PATH=/usr/lib/postgresql/{{ pg_version }}/bin:$PATH - diff --git a/ansible/roles/postgres/templates/postgresql.conf.j2 b/ansible/roles/postgres/templates/postgresql.conf.j2 deleted file mode 100644 index 1ca61b508..000000000 --- a/ansible/roles/postgres/templates/postgresql.conf.j2 +++ /dev/null @@ -1,148 +0,0 @@ -# {{ ansible_managed }} -# PostgreSQL unified configuration file - -# include the parameters that require restart if changed -include '{{ pg_configuration_file_restart_required }}' - -{% if pg_version is version('15', '<') %} -stats_temp_directory = '/var/run/postgresql/{{ pg_version }}-{{ pg_cluster_name }}.pg_stat_tmp' -{% endif %} -{% if pg_timezone is defined %} -timezone = '{{ pg_timezone }}' -{% endif %} -statement_timeout = '{{ pg_statement_timeout }}' - -# fixed parameters, those settings should never be changed -checkpoint_completion_target = '0.9' -datestyle = 'iso, dmy' -default_text_search_config = 'pg_catalog.english' -dynamic_shared_memory_type = 'posix' -lc_messages = '{{ pg_default_encoding | default("en_US.UTF-8") }}' -lc_monetary = '{{ pg_default_encoding | default("en_US.UTF-8") }}' -lc_numeric = '{{ pg_default_encoding | default("en_US.UTF-8") }}' -lc_time = '{{ pg_default_encoding | default("en_US.UTF-8") }}' - -# memory settings -{% if pg_checkpoint_timeout is defined %} -checkpoint_timeout = '{{ pg_checkpoint_timeout }}' -{% endif %} -{% if pg_effective_cache_size is defined %} -effective_cache_size = '{{ pg_effective_cache_size }}' -{% endif %} -{% if pg_maintenance_work_mem is defined %} -maintenance_work_mem = '{{ pg_maintenance_work_mem }}' -{% endif %} -{% if pg_work_mem is defined %} -work_mem = '{{ pg_work_mem }}' -{% endif %} - -# wal -{% if pg_max_wal_size is defined %} -max_wal_size = '{{ pg_max_wal_size }}' -{% endif %} -{% if pg_min_wal_size is defined %} -min_wal_size = '{{ pg_min_wal_size }}' -{% endif %} - -# autovacuum -{% if pg_autovacuum_vacuum_scale_factor is defined %} -autovacuum_vacuum_scale_factor = '{{ pg_autovacuum_vacuum_scale_factor }}' -{% endif %} -{% if pg_autovacuum_work_mem is defined %} -autovacuum_work_mem = '{{ pg_autovacuum_work_mem }}' -{% endif %} -{% if pg_autovacuum_vacuum_cost_delay is defined %} -autovacuum_vacuum_cost_delay = '{{ pg_autovacuum_vacuum_cost_delay }}' -{% endif %} -{% if pg_autovacuum_vacuum_cost_limit is defined %} -autovacuum_vacuum_cost_limit = '{{ pg_autovacuum_vacuum_cost_limit }}' -{% endif %} - -# ssd scale -{% if pg_ssd %} -seq_page_cost = 1.0 -random_page_cost = {{ pg_random_page_cost }} -effective_io_concurrency = '{{ pg_effective_io_concurrency }}' -{% if pg_version is version('13', '>=') %} -maintenance_io_concurrency = '{{ pg_maintenance_io_concurrency }}' -{% endif %} -{% endif %} - -# logging -{% if pg_logging %} -log_autovacuum_min_duration = '0' -log_checkpoints = 'on' -log_connections = 'on' -log_destination = 'csvlog' -log_directory = '{{ pg_log_directory }}' -log_disconnections = 'on' -log_file_mode = '0644' -log_filename = '{{ pg_version }}-{{ pg_cluster_name }}-{{ pg_log_filename_suffix }}.log' -log_line_prefix = '%t,%u,%d,%p,%h,%x,%l,%i,%s,%e,%q,%v,%a' -log_lock_waits = 'on' -log_min_duration_statement = '{{ pg_log_min_duration_statement }}' -log_rotation_age = '1h' -log_rotation_size = '0' -log_temp_files = '0' -{% if pg_timezone is defined %} -log_timezone = '{{ pg_timezone }}' -{% endif %} -log_truncate_on_rotation = 'on' -{% endif %} - -# replication -{% if pg_archive_command is defined %} -archive_command = '{{ pg_archive_command }}' -archive_timeout = '{{ pg_archive_timeout }}' -{% endif %} -{% if pg_replication %} -hot_standby_feedback = 'on' -hot_standby = 'on' -max_standby_archive_delay = '{{ pg_max_standby_archive_delay }}' -max_standby_streaming_delay = '{{ pg_max_standby_streaming_delay }}' -{% endif %} - -# specific to pg version >= 9.6 -{% if pg_version is version('9.6', '>=') %} -{% if pg_idle_in_transaction_session_timeout is defined %} -idle_in_transaction_session_timeout = '{{ pg_idle_in_transaction_session_timeout }}' -{% endif %} -{% if pg_max_parallel_workers_per_gather is defined %} -max_parallel_workers_per_gather = {{ pg_max_parallel_workers_per_gather }} -{% endif %} -{% endif %} - -# performance -synchronous_commit = '{{ pg_synchronous_commit }}' -{% if not pg_fsync %} -# WARNING: fsync is essential to prevent database corruption -# never set fsync = off except for initial loading of a new database cluster from a backup file -# don't forget to re-enable fsync as soon as you care about the data -fsync = off -{% endif %} -{% if file_system.stdout == "zfs" %} - -# zfs -full_page_writes = off -wal_compression = off -{% if pg_version is version('12', '>=') %} -wal_recycle = off -wal_init_zero = off -{% endif %} -{% endif %} -{% if (pg_version is version('12', '>=') and pg_replication_role == "replica") or pg_restore_command is defined %} - -# replica -{% if postgres_primary_slot_name is defined %} -primary_conninfo = 'host={{ postgres_primary_conninfo_host }} port={{ postgres_primary_conninfo_port }} user={{ postgres_primary_conninfo_user }} password={{ postgres_primary_conninfo_password }} sslmode={{ postgres_primary_conninfo_sslmode }}' -primary_slot_name = '{{ postgres_primary_slot_name }}' -{% endif %} -{% if pg_restore_command is defined %} -restore_command = '{{ pg_restore_command }}' -{% endif %} -{% endif %} -{% if (pg_version is version('17', '>=')) %} - -# compatibility -allow_alter_system = off -{% endif %} diff --git a/ansible/roles/postgres/templates/recovery.conf b/ansible/roles/postgres/templates/recovery.conf deleted file mode 100644 index 1b82fc233..000000000 --- a/ansible/roles/postgres/templates/recovery.conf +++ /dev/null @@ -1,7 +0,0 @@ -# {{ ansible_managed }} -standby_mode = 'on' -primary_conninfo = 'host={{ postgres_primary_conninfo_host }} port={{ postgres_primary_conninfo_port }} user={{ postgres_primary_conninfo_user }} password={{ postgres_primary_conninfo_password }} sslmode={{ postgres_primary_conninfo_sslmode }}' -primary_slot_name = '{{ postgres_primary_slot_name }}' -{% if pg_restore_command is defined %} -restore_command = '{{ pg_restore_command }}' -{% endif %} diff --git a/ansible/roles/postgres/templates/restart_required.conf b/ansible/roles/postgres/templates/restart_required.conf deleted file mode 100644 index 3c6011d6a..000000000 --- a/ansible/roles/postgres/templates/restart_required.conf +++ /dev/null @@ -1,86 +0,0 @@ -# {{ ansible_managed }} -# /!\ Any change here require a postgresql restart to be taken into account - -# File Locations -data_directory = '{{ pg_data_directory }}' -hba_file = '{{ pg_data_directory }}/pg_hba.conf' -ident_file = '{{ pg_data_directory }}/pg_ident.conf' -external_pid_file = '/var/run/postgresql/{{ pg_version }}-{{ pg_cluster_name }}.pid' - -# Connection Settings -listen_addresses = '{{ pg_listen_addresses }}' -port = {{ pg_port }} -max_connections = {{ pg_max_connections }} -unix_socket_directories = '/var/run/postgresql' - -# Security and Authentication -{% if pg_ssl %} -ssl = true -ssl_cert_file = '/etc/ssl/certs/ssl-cert-snakeoil.pem' -ssl_key_file = '/etc/ssl/private/ssl-cert-snakeoil.key' -{% endif %} - -# Resource Consumption -shared_buffers = '{{ pg_shared_buffers }}' -huge_pages = '{{ pg_huge_pages }}' -{% if pg_version is version('9.6', '>=') and pg_max_worker_processes is defined %} -max_worker_processes = {{ pg_max_worker_processes }} -{% endif %} - -# Write Ahead Log -wal_buffers = '16MB' -wal_level = '{{ pg_wal_level | default('replica') }}' - -# Archiving -{% if pg_archive_command is defined %} -archive_mode = 'on' -{% endif %} - -# Replication -{% if pg_version is version('9.6', '<=') %} -hot_standby = 'on' -{% endif %} -{% if pg_replication %} -max_wal_senders = '5' -max_replication_slots = '5' -{% elif pg_wal_level is defined and pg_wal_level == 'minimal' %} -max_wal_senders = '0' -max_replication_slots = '0' -{% endif %} - -# Error Reporting and Logging -{% if pg_logging %} -logging_collector = 'on' -{% endif %} - -# Process Title -cluster_name = '{{ pg_version }}-{{ pg_cluster_name }}' - -# Run-time Statistics -track_activity_query_size = '10240' - -# Automatic Vacuuming -{% if pg_autovacuum_max_workers is defined %} -autovacuum_max_workers = '{{ pg_autovacuum_max_workers }}' -{% endif %} -{% if pg_autovacuum_freeze_max_age is defined %} -autovacuum_freeze_max_age = '{{ pg_autovacuum_freeze_max_age }}' -{% endif %} -{% if pg_recovery %} - -# Recovery -{% if pg_recovery_target is defined %} -recovery_target = '{{ pg_recovery_target }}' -{% endif %} -{% if pg_recovery_target_action is defined %} -recovery_target_action = '{{ pg_recovery_target_action }}' -{% endif %} -{% if pg_recovery_target_time is defined %} -recovery_target_time = '{{ pg_recovery_target_time }}' -{% endif %} -{% endif %} -{% if pg_shared_preload_libraries is defined %} - -# shared library -shared_preload_libraries = '{{ pg_shared_preload_libraries | join(',') }}' -{% endif %} diff --git a/ansible/roles/postgres/templates/zabbix_postgresql_template.conf b/ansible/roles/postgres/templates/zabbix_postgresql_template.conf deleted file mode 100644 index f00c7a86f..000000000 --- a/ansible/roles/postgres/templates/zabbix_postgresql_template.conf +++ /dev/null @@ -1,4 +0,0 @@ -# replication byte lag, $1 = port, $2 = client_addr -UserParameter=pgsql.stat_replication.bytelag[*],/usr/bin/sudo -u postgres /usr/bin/psql -p $1 -Atc "{{ postgres_zabbix_stat_replication_bytelag }} where client_addr = '$2'" - -UserParameter=pgsql.wal_count,/usr/bin/sudo -u postgres ls {{ pg_data_directory }}/pg_wal | wc -l diff --git a/ansible/roles/redis/defaults/main.yml b/ansible/roles/redis/defaults/main.yml deleted file mode 100644 index b8f650834..000000000 --- a/ansible/roles/redis/defaults/main.yml +++ /dev/null @@ -1,12 +0,0 @@ ---- -# file: roles/redis/defaults/main.yml - -redis_config: - bind: "127.0.0.1 ::1" - port: "0" - unixsocket: "/var/run/redis/redis-server.sock" - unixsocketperm: "770" - protected-mode: "no" - save: '""' - -redis_default_user_password: "auto" diff --git a/ansible/roles/redis/handlers/main.yml b/ansible/roles/redis/handlers/main.yml deleted file mode 100644 index c94ed5ac8..000000000 --- a/ansible/roles/redis/handlers/main.yml +++ /dev/null @@ -1,7 +0,0 @@ ---- -# file: roles/redis/handlers/main.yml - -- name: "restart redis" - ansible.builtin.systemd: - name: redis-server - state: restarted diff --git a/ansible/roles/redis/meta/main.yml b/ansible/roles/redis/meta/main.yml deleted file mode 100644 index 2ca40f124..000000000 --- a/ansible/roles/redis/meta/main.yml +++ /dev/null @@ -1,6 +0,0 @@ -# file: role/proxysql/meta/main.yml - -dependencies: - - role: zabbix_template_assignment - zabbix_template_assignment_systemd_service_list: - - redis-server diff --git a/ansible/roles/redis/readme.md b/ansible/roles/redis/readme.md deleted file mode 100644 index 47bf931f5..000000000 --- a/ansible/roles/redis/readme.md +++ /dev/null @@ -1,35 +0,0 @@ -# Redis - - -* [Redis](#redis) - * [Variable reference](#variable-reference) - * [Optional variables](#optional-variables) - * [Example](#example) - - -## Variable reference - -### Optional variables - -| Variable | Description | Type of variable | Default value | Other value | -|-----------------------------|-----------------------------------------------------------------------------------------------|------------------|-------------------------------------------------------------------|---------------------------------------------------------| -| redis_config | Override redis default settings located in `/etc/redis/redis.conf` | `dict` | See Example | | -| redis_default_user_password | Define redis superuser `default` password | `string` | `auto` (password is auto-generated and stored in Hashicorp vault) | nopass (redis keyword to deactive password); | -| redis_acls | Define ACLs [doc](https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl) | `list` | None | | - -### Example - -``` -redis_config: - bind: "127.0.0.1 ::1" - port: "0" - unixsocket: "/var/run/redis/redis-server.sock" - unixsocketperm: "770" # the unix socket mode is `0770`, and the owner and group is `redis`, so if you want to allow a process to access the socket, you may add its user to the redis group - protected-mode: "no" - save: '""' # snapshot to disk are disabled since we usually don't need persistence - -redis_acls: - - name: app1 - password: nopass # If the special keyword `auto` is provided the password will be generated and stored in Hashicorp Vault - right: "~* &* +@all" -``` diff --git a/ansible/roles/redis/tasks/main.yml b/ansible/roles/redis/tasks/main.yml deleted file mode 100644 index c84853f00..000000000 --- a/ansible/roles/redis/tasks/main.yml +++ /dev/null @@ -1,58 +0,0 @@ ---- -# file: roles/redis/tasks/main.yml - -- name: "install redis-server" - ansible.builtin.apt: - name: - - redis-server - tags: redis - -- name: "add include ansible.conf line in /etc/redis/redis.conf" - ansible.builtin.lineinfile: - path: /etc/redis/redis.conf - line: "include /etc/redis/ansible.conf" - when: redis_config is defined - tags: redis - -- name: "add include aclfile line in /etc/redis/redis.conf" - ansible.builtin.lineinfile: - path: /etc/redis/redis.conf - line: "aclfile /etc/redis/users.acl" - tags: redis - -- name: "/etc/redis/ansible.conf" - ansible.builtin.template: - src: redis.conf.j2 - dest: /etc/redis/ansible.conf - mode: "0640" - owner: redis - group: redis - backup: true - when: redis_config is defined - notify: restart redis - tags: redis - -- name: "handle users password generation" - ansible.builtin.include_tasks: users.yml - loop: "{{ [{'name': 'default', 'password': redis_default_user_password }] + redis_acls | default([]) }}" - loop_control: - loop_var: user - tags: redis - -- name: "/etc/redis/users.acl" - ansible.builtin.template: - src: users.acl.j2 - dest: /etc/redis/users.acl - mode: "0640" - owner: redis - group: redis - backup: true - notify: restart redis - tags: redis - -- name: "make sure redis is started and enable" - ansible.builtin.systemd: - name: redis-server - state: started - enabled: true - tags: redis diff --git a/ansible/roles/redis/tasks/users.yml b/ansible/roles/redis/tasks/users.yml deleted file mode 100644 index 5d2114249..000000000 --- a/ansible/roles/redis/tasks/users.yml +++ /dev/null @@ -1,43 +0,0 @@ ---- -# file: roles/redis/tasks/users.yml - -- name: "handle secret {{ ansible_hostname }}/redis_{{ user.name }}_password" - block: - - name: "get {{ ansible_hostname }}/redis_{{ user.name }}_password from hashicorp vault" - ansible.builtin.set_fact: - "{{ user.name }}_password": "#{{ lookup('hashi_vault', 'secret=talas-kv/data/' + host_vars_location + '/' + ansible_hostname)['redis_' + user.name + '_password'] | hash('sha256') }}" - rescue: - - name: "generate a random password for {{ ansible_hostname }}/redis_{{ user.name }}_password" - ansible.builtin.set_fact: - password: "{{ lookup('password','/dev/null chars=ascii_letters,digits length=50') }}" - - name: "patching hashicorp vault with generated redis_{{ user.name }}_password" - ansible.builtin.command: "vault kv patch talas-kv/{{ host_vars_location }}/{{ ansible_hostname }} redis_{{ user.name }}_password={{ password }}" - delegate_to: localhost - become: false - register: result - ignore_errors: true - - name: "patch failed because the entry doesn't exist, creating it instead" - ansible.builtin.command: "vault kv put talas-kv/{{ host_vars_location }}/{{ ansible_hostname }} redis_{{ user.name }}_password={{ password }}" - delegate_to: localhost - become: false - when: - - result.failed - - '"No value found" in result.stderr' - - name: "assign password value to redis_{{ user.name }}_password" - ansible.builtin.set_fact: - "{{ user.name }}_password": "#{{ password | hash('sha256') }}" - when: - - user.password == "auto" - -- name: "hash non auto password to sha256" - ansible.builtin.set_fact: - "{{ user.name }}_password": "#{{ user.password | hash('sha256') }}" - when: - - user.password != "auto" - - user.password != "nopass" - -- name: "set nopass" - ansible.builtin.set_fact: - "{{ user.name }}_password": "nopass" - when: - - user.password == "nopass" diff --git a/ansible/roles/redis/templates/redis.conf.j2 b/ansible/roles/redis/templates/redis.conf.j2 deleted file mode 100644 index c0235b699..000000000 --- a/ansible/roles/redis/templates/redis.conf.j2 +++ /dev/null @@ -1,5 +0,0 @@ -# {{ ansible_managed }} - -{% for key, value in redis_config.items() %} -{{ key }} {{ value }} -{% endfor %} diff --git a/ansible/roles/redis/templates/users.acl.j2 b/ansible/roles/redis/templates/users.acl.j2 deleted file mode 100644 index d58864759..000000000 --- a/ansible/roles/redis/templates/users.acl.j2 +++ /dev/null @@ -1,6 +0,0 @@ -user default on {{ default_password }} ~* &* +@all -{% if redis_acls is defined %} -{% for acl in redis_acls %} -user {{ acl.name }} on {{ hostvars[inventory_hostname][acl.name + '_password'] }} {{ acl.right }} -{% endfor %} -{% endif %} diff --git a/ansible/roles/rust/tasks/main.yml b/ansible/roles/rust/tasks/main.yml deleted file mode 100644 index 728d09196..000000000 --- a/ansible/roles/rust/tasks/main.yml +++ /dev/null @@ -1,19 +0,0 @@ ---- -# file: roles/rust/tasks/main.yml - -- name: "download rustup-init" - get_url: - url: https://static.rust-lang.org/rustup/dist/x86_64-unknown-linux-gnu/rustup-init - dest: /dev/shm/rustup-init - mode: 0755 - tags: rust - -- name: "install rust" - command: "/dev/shm/rustup-init -q -y --default-toolchain stable" - tags: rust - -- name: "remove /dev/shm/rustup-init" - file: - path: "/dev/shm/rustup-init" - state: absent - tags: tags diff --git a/ansible/roles/sonarqube/defaults/main.yml b/ansible/roles/sonarqube/defaults/main.yml deleted file mode 100644 index fa7780ddf..000000000 --- a/ansible/roles/sonarqube/defaults/main.yml +++ /dev/null @@ -1,66 +0,0 @@ ---- -# file: roles/sonarqube/defaults/main.yml - -sonarqube_update_sonarqube_now: false -sonarqube_ldap: true -sonarqube_haproxy: true - -sonarqube_download_url: "https://binaries.sonarsource.com/Distribution/sonarqube/sonarqube-{{ sonarqube_version }}.zip" - -sonarqube_postgres_password: "{{ lookup('hashi_vault', 'secret=talas-kv/data/' + host_vars_location + '/' + ansible_hostname)['sonarqube_postgres_password'] }}" - -sonarqube_ce_jmx_config: >- - -Dcom.sun.management.jmxremote=true - -Djava.rmi.server.hostname={{ ansible_host }} - -Dcom.sun.management.jmxremote.port=12345 - -Dcom.sun.management.jmxremote.rmi.port=12345 - -Dcom.sun.management.jmxremote.ssl=false - -Dcom.sun.management.jmxremote.authenticate=true - -Dcom.sun.management.jmxremote.password.file=/opt/sonarqube/stateless/conf/jmxremote.password - -Dcom.sun.management.jmxremote.access.file=/opt/sonarqube/stateless/conf/jmxremote.access -sonarqube_web_jmx_config: >- - -Dcom.sun.management.jmxremote=true - -Djava.rmi.server.hostname={{ ansible_host }} - -Dcom.sun.management.jmxremote.port=12346 - -Dcom.sun.management.jmxremote.rmi.port=12346 - -Dcom.sun.management.jmxremote.ssl=false - -Dcom.sun.management.jmxremote.authenticate=true - -Dcom.sun.management.jmxremote.password.file=/opt/sonarqube/stateless/conf/jmxremote.password - -Dcom.sun.management.jmxremote.access.file=/opt/sonarqube/stateless/conf/jmxremote.access - -sonarqube_jmx_user_list: - - name: "monitoring" - right: "readonly" - password: "{{ lookup('hashi_vault', 'secret=talas-kv/data/group_vars/all')['zabbix_jmx_monitoring_password'] }}" - - name: "operator" - right: "readwrite" - password: "{{ lookup('hashi_vault', 'secret=talas-kv/data/services/veza')['tomcat_jmx_operator'] }}" - -pg_users: - - name: "sonarqube" - attrib: LOGIN - password: "{{ sonarqube_postgres_password }}" - -pg_databases: - - name: sonarqube - owner: sonarqube - -java_home: "/opt/{{ jvm_latest_17_directory_name }}" -jvm_url_list: - - "https://internal-resources-1.talas.com/openjdk/{{ jvm_latest_17_archive_name }}" - -haproxy_frontend: - default_backend: "sonarqube" - -haproxy_backend: - - name: "sonarqube" - server: - - name: "myself" - fqdn: "127.0.0.1" - port: "9000" - -haproxy_timeout_client: "20m" -haproxy_timeout_server: "20m" - -haproxy_https_monitoring: - - "{{ sonarqube_url }}" diff --git a/ansible/roles/sonarqube/handlers/main.yml b/ansible/roles/sonarqube/handlers/main.yml deleted file mode 100644 index 26f4976a5..000000000 --- a/ansible/roles/sonarqube/handlers/main.yml +++ /dev/null @@ -1,11 +0,0 @@ ---- -# file: roles/sonarqube/handlers/main.yml - -- name: systemctl daemon_reload - ansible.builtin.systemd: - daemon_reload: yes - -- name: restart sonarqube - ansible.builtin.systemd: - name: sonarqube - state: restarted diff --git a/ansible/roles/sonarqube/meta/main.yml b/ansible/roles/sonarqube/meta/main.yml deleted file mode 100644 index 01f484c86..000000000 --- a/ansible/roles/sonarqube/meta/main.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -dependencies: - - { role: postgres } - - { role: jvm } - - { role: haproxy, when: sonarqube_haproxy } diff --git a/ansible/roles/sonarqube/readme.md b/ansible/roles/sonarqube/readme.md deleted file mode 100644 index 1abf674b0..000000000 --- a/ansible/roles/sonarqube/readme.md +++ /dev/null @@ -1,25 +0,0 @@ -(!) This role is tailored only for talas. It includes hard-coded LDAP parameters as well as the `sonarqube-community-branch-plugin-1.8.1.jar` plugin. - -# Required variables - -| Variable | Description | Sample value | -|-------------------------------------------------------|-----------------------------------------------------------------------------|------------------------| -| pg_version | The postgres server version | `14` | -| sonarqube_url | url for the web interface | `sonarqube.talas.dev` | -| sonarqube_version | The sonarqube version | `9.9.1.69595` | -| sonarqube_postgres_password | The sonarqube postgres database password, must be stored in hashicorp vault | `foo` | - -# Optional variables - -| Variable | Description | Default value | Sample value | -|-------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------| -| sonarqube_update_sonarqube_now | Set this value to true to upgrade sonarqube to a newer version | `false` | `true` | -| sonarqube_download_url | The url allowing to download the Sonarqube distribution archive. | `https://binaries.sonarsource.com/Distribution/sonarqube/sonarqube-{{ sonarqube_version }}.zip` | `https://binaries.sonarsource.com/Distribution/sonarqube/sonarqube-{{ sonarqube_version }}.zip` | -| sonarqube_java_heap_size | Set the xmx of the 3 sonarqube engines (web, ce and es) | None | `2048m` | -| sonarqube_ce_jmx_config | JMX parameters appended to the ce javaOpts. For now to disable it how have to set this param as an empty string | See defaults/main.yml | | -| sonarqube_web_jmx_config | JMX parameters appended to the web javaAdditionalOpts. For now to disable it how have to set this param as an empty string | See defaults/main.yml | | -| sonarqube_jmx_user_list | Sonarqube JMX monitoring users. |
- name: monitoring
password: '{{ lookup(''hashi_vault'', ''secret=talas-kv/data/group_vars/all'')[''zabbix_jmx_monitoring_password' '] }}'
right: readonly
| | -| sonarqube_talas_community_branch_plugin_version | Set this value to install a version of the sonarqube talas community branch plugin version from nexus | None | `1.14.0.1` | -| sonarqube_talas_sonar_plugin_version | Set this value to install a version of the sonarqube talas private plugin version from nexus | None | `1.9` | -| sonarqube_community_branch_plugin_version | Set this value to install a version of the sonarqube community branch plugin version from public repo | None | `1.14.0.1` | -| sonarqube_redaalaoui_sonar_java_no_var_plugin_version | Set this value to install a version of the sonarqube sonar-java-no-var-plugin plugin version from public repo | None | `1.9` | diff --git a/ansible/roles/sonarqube/tasks/configure.yml b/ansible/roles/sonarqube/tasks/configure.yml deleted file mode 100644 index ce6b456db..000000000 --- a/ansible/roles/sonarqube/tasks/configure.yml +++ /dev/null @@ -1,70 +0,0 @@ ---- -# file: roles/sonarqube/tasks/configure.yml - -- name: "init vars" - ansible.builtin.set_fact: - sonar_ce_javaOpts: "" - sonar_es_javaOpts: "" - sonar_web_javaOpts: "" - sonar_ce_javaAdditionalOpts: "" - -- name: "init javaOpts vars" - ansible.builtin.set_fact: - sonar_ce_javaOpts: "{{ sonar_ce_javaOpts + '-Xms' + sonarqube_java_heap_size + ' -Xmx' + sonarqube_java_heap_size }}" - sonar_es_javaOpts: "{{ sonar_es_javaOpts + '-Xms' + sonarqube_java_heap_size + ' -Xmx' + sonarqube_java_heap_size }}" - sonar_web_javaOpts: "{{ sonar_web_javaOpts + '-Xms' + sonarqube_java_heap_size + ' -Xmx' + sonarqube_java_heap_size }}" - when: sonarqube_java_heap_size is defined - -- name: "sonar_ce_javaOpts append jmx config" - ansible.builtin.set_fact: - sonar_ce_javaOpts: "{{ sonar_ce_javaOpts + ' ' + sonarqube_ce_jmx_config }}" - -- name: "sonar_web_javaAdditionalOpts init with jmx config" - ansible.builtin.set_fact: - sonar_web_javaAdditionalOpts: "{{ sonarqube_web_jmx_config }}" - -# IT-9595 -- name: "append sonar_web_javaAdditionalOpts" - ansible.builtin.set_fact: - sonar_web_javaAdditionalOpts: "{{ sonar_web_javaAdditionalOpts + ' -javaagent:/opt/sonarqube/stateful/extensions/plugins/sonarqube-community-branch-plugin-' + sonarqube_community_branch_plugin_version + '.jar=web' }}" - when: sonarqube_community_branch_plugin_version is defined - -# IT-9595 -- name: "append sonar_web_javaAdditionalOpts" - ansible.builtin.set_fact: - sonar_web_javaAdditionalOpts: "{{ sonar_web_javaAdditionalOpts + ' -javaagent:/opt/sonarqube/stateful/extensions/plugins/sonarqube-community-branch-plugin-' + sonarqube_talas_community_branch_plugin_version + '.talas.jar=web' }}" - when: sonarqube_talas_community_branch_plugin_version is defined - -# IT-9595 -- name: "append sonar_ce_javaAdditionalOpts" - ansible.builtin.set_fact: - sonar_ce_javaAdditionalOpts: "{{ sonar_ce_javaAdditionalOpts + ' -javaagent:/opt/sonarqube/stateful/extensions/plugins/sonarqube-community-branch-plugin-' + sonarqube_community_branch_plugin_version + '.jar=ce' }}" - when: sonarqube_community_branch_plugin_version is defined - -# IT-9595 -- name: "append sonar_ce_javaAdditionalOpts" - ansible.builtin.set_fact: - sonar_ce_javaAdditionalOpts: "{{ sonar_ce_javaAdditionalOpts + ' -javaagent:/opt/sonarqube/stateful/extensions/plugins/sonarqube-community-branch-plugin-' + sonarqube_talas_community_branch_plugin_version + '.talas.jar=ce' }}" - when: sonarqube_talas_community_branch_plugin_version is defined - -- name: "manage /opt/sonarqube/stateless/conf/sonar.properties" - ansible.builtin.template: - src: "sonar.properties.j2" - dest: "/opt/sonarqube/stateless/conf/sonar.properties" - owner: sonarqube - group: sonarqube - backup: true - notify: restart sonarqube - -- name: "{{ nexus_basedir }}/engine/etc/jmxremote.access {{ nexus_basedir }}/engine/etc/jmxremote.password" - template: - src: "{{ item }}.j2" - dest: "/opt/sonarqube/stateless/conf//{{ item }}" - owner: sonarqube - group: sonarqube - mode: 0400 - backup: true - loop: - - jmxremote.access - - jmxremote.password - notify: restart sonarqube diff --git a/ansible/roles/sonarqube/tasks/install_plugins.yml b/ansible/roles/sonarqube/tasks/install_plugins.yml deleted file mode 100644 index ce7067ba3..000000000 --- a/ansible/roles/sonarqube/tasks/install_plugins.yml +++ /dev/null @@ -1,71 +0,0 @@ ---- -# file: roles/sonarqube/tasks/install_sonarqube.yml -- name: "add sonarqube_talas_community_branch_plugin to sonarqube_internal_plugins_url" - ansible.builtin.set_fact: - sonarqube_internal_plugins_url: "{{ sonarqube_internal_plugins_url | default([]) + ['https://nexus.talas.dev/repository/github.com/talas/sonarqube-community-branch-plugin/releases/download/{{ sonarqube_talas_community_branch_plugin_version }}.talas/sonarqube-community-branch-plugin-{{ sonarqube_talas_community_branch_plugin_version }}.talas.jar'] }}" - when: sonarqube_talas_community_branch_plugin_version is defined - -- name: "add sonarqube_talas_sonar_plugin to sonarqube_internal_plugins_url" - ansible.builtin.set_fact: - sonarqube_internal_plugins_url: "{{ sonarqube_internal_plugins_url | default([]) + ['https://nexus.talas.dev/repository/maven-releases/com/cos/sonar_plugin/talas-sonar-plugin/{{ sonarqube_talas_sonar_plugin_version }}/talas-sonar-plugin-{{ sonarqube_talas_sonar_plugin_version }}.jar'] }}" - when: sonarqube_talas_sonar_plugin_version is defined - -- name: "add sonarqube_community_branch_plugin to sonarqube_external_plugins_url" - ansible.builtin.set_fact: - sonarqube_external_plugins_url: "{{ sonarqube_external_plugins_url | default([]) + ['https://github.com/mc1arke/sonarqube-community-branch-plugin/releases/download/{{ sonarqube_community_branch_plugin_version }}/sonarqube-community-branch-plugin-{{ sonarqube_community_branch_plugin_version }}.jar'] }}" - when: sonarqube_community_branch_plugin_version is defined - -- name: "add sonarqube_redaalaoui_sonar_java_no_var_plugin to sonarqube_external_plugins_url" - ansible.builtin.set_fact: - sonarqube_external_plugins_url: "{{ sonarqube_external_plugins_url | default([]) + ['https://repo1.maven.org/maven2/me/redaalaoui/sonar/sonar-java-no-var-plugin/{{ sonarqube_redaalaoui_sonar_java_no_var_plugin_version }}/sonar-java-no-var-plugin-{{ sonarqube_redaalaoui_sonar_java_no_var_plugin_version }}.jar'] }}" - when: sonarqube_redaalaoui_sonar_java_no_var_plugin_version is defined - -- name: "/opt/sonarqube/stateless/extensions/plugins" - ansible.builtin.file: - path: "/opt/sonarqube/stateless/extensions/plugins" - state: directory - owner: sonarqube - group: sonarqube - tags: sonarqube - -- name: "install additional internal plugins" - ansible.builtin.get_url: - url: "{{ item }}" - dest: "/opt/sonarqube/stateful/extensions/plugins" - owner: sonarqube - group: sonarqube - url_username: "{{ repo_talas_com_user }}" - url_password: "{{ repo_talas_com_pass }}" - loop: "{{ sonarqube_internal_plugins_url }}" - when: sonarqube_internal_plugins_url is defined - tags: sonarqube - -- name: "install additional external plugins" - ansible.builtin.get_url: - url: "{{ item }}" - dest: "/opt/sonarqube/stateful/extensions/plugins" - owner: sonarqube - group: sonarqube - loop: "{{ sonarqube_external_plugins_url }}" - when: sonarqube_external_plugins_url is defined - tags: sonarqube - -- name: "search old plugins" - ansible.builtin.set_fact: - sonarqube_plugins_name: "{{ (sonarqube_internal_plugins_url | default([]) |map('split','/')|map('last')|list()) + (sonarqube_external_plugins_url | default([]) | map('split','/') | map('last') | list()) }}" - tags: sonarqube - -- name: "find old plugins" - ansible.builtin.find: - paths: "/opt/sonarqube/stateful/extensions/plugins" - patterns: "*.jar" - excludes: "{{ sonarqube_plugins_name | default([]) }}" - register: sonarqube_old_plugins - tags: sonarqube - -- name: "delete old plugins" - ansible.builtin.file: - path: "{{ item.path }}" - state: absent - loop: "{{ sonarqube_old_plugins.files }}" - tags: sonarqube diff --git a/ansible/roles/sonarqube/tasks/install_sonarqube.yml b/ansible/roles/sonarqube/tasks/install_sonarqube.yml deleted file mode 100644 index d225e7a61..000000000 --- a/ansible/roles/sonarqube/tasks/install_sonarqube.yml +++ /dev/null @@ -1,102 +0,0 @@ ---- -# file: roles/sonarqube/tasks/install_sonarqube.yml - -- name: "prerequisites" - ansible.builtin.apt: - name: - - unzip - update_cache: true - tags: sonarqube - -- name: "group sonarqube" - ansible.builtin.group: - name: sonarqube - system: true - tags: sonarqube - -- name: "user sonarqube" - ansible.builtin.user: - name: sonarqube - group: sonarqube - system: true - tags: sonarqube - -- name: "folder tree" - ansible.builtin.file: - path: "{{ item }}" - state: directory - owner: sonarqube - group: sonarqube - loop: - - "/opt/sonarqube/stateful" - - "/opt/sonarqube/stateful/logs" - - "/opt/sonarqube/stateless" - tags: sonarqube - -- name: "Extract sonarqube from {{ sonarqube_download_url }} to /dev/shm" - ansible.builtin.unarchive: - src: "{{ sonarqube_download_url }}" - remote_src: true - dest: "/dev/shm" - owner: sonarqube - group: sonarqube - tags: sonarqube - -- name: "move stateful folders 'data' and 'extensions' if they don't exist" - ansible.builtin.command: "mv /dev/shm/sonarqube-{{ sonarqube_version }}/{{ item }} /opt/sonarqube/stateful/{{ item }}" - args: - creates: "/opt/sonarqube/stateful/{{ item }}" - loop: - - data - - extensions - tags: sonarqube - -- name: "remove stateful folders data, extensions and logs" - ansible.builtin.file: - path: "/dev/shm/sonarqube-{{ sonarqube_version }}/{{ item }}" - state: absent - loop: - - data - - extensions - - logs - tags: sonarqube - -- name: "symbolic links data, extensions and logs" - ansible.builtin.file: - src: "/opt/sonarqube/stateful/{{ item }}" - dest: "/dev/shm/sonarqube-{{ sonarqube_version }}/{{ item }}" - state: link - owner: sonarqube - group: sonarqube - loop: - - data - - extensions - - logs - tags: sonarqube - -- name: "put version of sonarqube in VERSION file" - ansible.builtin.copy: - content: "{{ sonarqube_version }}" - dest: "/dev/shm/sonarqube-{{ sonarqube_version }}/VERSION" - owner: sonarqube - group: sonarqube - tags: sonarqube - -- name: "make sure /opt/sonarqube/stateless does not exist for now" - ansible.builtin.file: - path: "/opt/sonarqube/stateless" - state: absent - tags: sonarqube - -- name: "move /dev/shm/sonarqube-{{ sonarqube_version }} to /opt/sonarqube/stateless" - ansible.builtin.command: "mv /dev/shm/sonarqube-{{ sonarqube_version }} /opt/sonarqube/stateless" - tags: sonarqube - -- name: "make sure the folder /opt/sonarqube belongs to the user sonarqube" - ansible.builtin.file: - path: "/opt/sonarqube" - owner: sonarqube - group: sonarqube - recurse: true - state: directory - tags: sonarqube diff --git a/ansible/roles/sonarqube/tasks/main.yml b/ansible/roles/sonarqube/tasks/main.yml deleted file mode 100644 index 2c8690f1d..000000000 --- a/ansible/roles/sonarqube/tasks/main.yml +++ /dev/null @@ -1,63 +0,0 @@ ---- -# file: roles/sonarqube/tasks/main.yml - -- name: "check if the folder /opt/sonarqube/stateless exists" - ansible.builtin.stat: - path: "/opt/sonarqube/stateless" - register: folder_sonarqube - tags: sonarqube - -- name: "check if sonarqube update is needed" - block: - - name: "check version of sonarqube" - ansible.builtin.slurp: - src: "/opt/sonarqube/stateless/VERSION" - register: sonarqube_current - - name: "set fact current_sonarqube_version" - ansible.builtin.set_fact: - current_sonarqube_version: "{{ sonarqube_current['content'] | b64decode | trim }}" - - name: "check if sonarqube update is needed" - ansible.builtin.debug: - msg: "Update needed, from {{ current_sonarqube_version }} to {{ sonarqube_version }} (use: --extra-vars '{ \"sonarqube_update_sonarqube_now\" : true}')" - when: current_sonarqube_version != sonarqube_version - when: folder_sonarqube.stat.exists - tags: sonarqube - -- name: "stop sonarqube?" - ansible.builtin.service: - name: sonarqube - state: stopped - when: - - folder_sonarqube.stat.exists - - sonarqube_update_sonarqube_now - - current_sonarqube_version != sonarqube_version - tags: sonarqube - -- name: "install or update sonarqube" - ansible.builtin.import_tasks: install_sonarqube.yml - when: (not folder_sonarqube.stat.exists) or (folder_sonarqube.stat.exists and sonarqube_update_sonarqube_now and current_sonarqube_version != sonarqube_version) - tags: sonarqube - -- name: "configure" - ansible.builtin.import_tasks: configure.yml - tags: sonarqube - -- name: "install or update sonarqube plugins" - ansible.builtin.import_tasks: install_plugins.yml - tags: sonarqube - -- name: "/etc/systemd/system/sonarqube.service" - ansible.builtin.template: - src: "sonarqube.service.j2" - dest: "/etc/systemd/system/sonarqube.service" - notify: - - systemctl daemon_reload - - restart sonarqube - tags: sonarqube - -- name: "systemd enabled and started" - ansible.builtin.systemd: - name: sonarqube.service - state: started - enabled: true - tags: sonarqube diff --git a/ansible/roles/sonarqube/templates/jmxremote.access.j2 b/ansible/roles/sonarqube/templates/jmxremote.access.j2 deleted file mode 100644 index f1f37652b..000000000 --- a/ansible/roles/sonarqube/templates/jmxremote.access.j2 +++ /dev/null @@ -1,3 +0,0 @@ -{% for user in sonarqube_jmx_user_list %} -{{ user['name'] }} {{ user['right'] }} -{% endfor %} diff --git a/ansible/roles/sonarqube/templates/jmxremote.password.j2 b/ansible/roles/sonarqube/templates/jmxremote.password.j2 deleted file mode 100644 index d8cedb739..000000000 --- a/ansible/roles/sonarqube/templates/jmxremote.password.j2 +++ /dev/null @@ -1,3 +0,0 @@ -{% for user in sonarqube_jmx_user_list %} -{{ user['name'] }} {{ user['password'] }} -{% endfor %} diff --git a/ansible/roles/sonarqube/templates/sonar.properties.j2 b/ansible/roles/sonarqube/templates/sonar.properties.j2 deleted file mode 100644 index 3518948b1..000000000 --- a/ansible/roles/sonarqube/templates/sonar.properties.j2 +++ /dev/null @@ -1,35 +0,0 @@ -# {{ ansible_managed }} -sonar.jdbc.url=jdbc:postgresql://localhost/sonarqube -sonar.jdbc.username=sonarqube -sonar.jdbc.password={{ sonarqube_postgres_password }} - -{% if sonarqube_ldap %} -sonar.security.realm=LDAP -ldap.url=ldaps://ldap.talas.com -ldap.bindDn=uid={{ ansible_hostname}},ou=servers,dc=talas,dc=com -ldap.bindPassword={{ ldappass }} - -ldap.user.baseDn=ou=people,dc=talas,dc=com -ldap.user.request=(&(uid={login})(|(&(ObjectClass=CosAccount)(CosStatus=active))(&(ObjectClass=CosBot)(ServiceAccess=scm)))) - -ldap.group.baseDn=ou=groups,dc=talas,dc=com -ldap.group.request=(&(objectClass=posixGroup)(memberUid={uid})) - -{% endif %} -sonar.telemetry.enable=false - -{% if sonar_web_javaOpts != "" %} -sonar.web.javaOpts={{ sonar_web_javaOpts | trim }} -{% endif %} -{% if sonar_ce_javaOpts != "" %} -sonar.ce.javaOpts={{ sonar_ce_javaOpts | trim }} -{% endif %} -{% if sonar_es_javaOpts != "" %} -sonar.search.javaOpts={{ sonar_es_javaOpts | trim }} -{% endif %} -{% if sonar_web_javaAdditionalOpts != "" %} -sonar.web.javaAdditionalOpts={{ sonar_web_javaAdditionalOpts | trim }} -{% endif %} -{% if sonar_ce_javaAdditionalOpts != "" %} -sonar.ce.javaAdditionalOpts={{ sonar_ce_javaAdditionalOpts | trim }} -{% endif %} diff --git a/ansible/roles/sonarqube/templates/sonarqube.service.j2 b/ansible/roles/sonarqube/templates/sonarqube.service.j2 deleted file mode 100644 index 98ff9a541..000000000 --- a/ansible/roles/sonarqube/templates/sonarqube.service.j2 +++ /dev/null @@ -1,19 +0,0 @@ -# {{ ansible_managed }} -[Unit] -Description=SonarQube service -After=syslog.target network-online.target - -[Service] -Type=simple -User=sonarqube -Group=sonarqube -ExecStart=/bin/nohup /usr/bin/java -Djava.awt.headless=true -Xms32m -Xmx32m -Djava.net.preferIPv4Stack=true -jar /opt/sonarqube/stateless/lib/sonar-application-{{ sonarqube_version }}.jar -StandardOutput=syslog -LimitNOFILE=131072 -LimitNPROC=8192 -TimeoutStartSec=5 -Restart=always -SuccessExitStatus=143 - -[Install] -WantedBy=multi-user.target diff --git a/ansible/roles/ssh-keygen-and-store/defaults/main.yml b/ansible/roles/ssh-keygen-and-store/defaults/main.yml deleted file mode 100644 index d3c227e32..000000000 --- a/ansible/roles/ssh-keygen-and-store/defaults/main.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -# file: roles/ssh-keygen-and-store/defaults/main.yml - -ssh_keygen_user_list: [ 'root' ] diff --git a/ansible/roles/ssh-keygen-and-store/readme.md b/ansible/roles/ssh-keygen-and-store/readme.md deleted file mode 100644 index 9765f8706..000000000 --- a/ansible/roles/ssh-keygen-and-store/readme.md +++ /dev/null @@ -1,25 +0,0 @@ -This role handles the ssh keys of any number of system users. - -# Usage -By default, only the root user is handled, you can modify the list of users with this variable: -``` -ssh_keygen_user_list: - - root -``` -This role will look at hashicorp vault and on the server to see if ssh keys are present for the defined users. - -# What this role does -There are 4 possibilities for each user: -1. the key doesn't exists anywhere: the key will be generated on the server and stored on hashicorp vault -2. the key is only present on the server: the key will be send to hashicorp vault -3. the key is only present on hashicorp vault: the key will be send to the server -4. the key is present on both sides: this role will check that the key is the same everywhere and fail if it is not the case - -# Important limitation -(!) Due to the limitation of ansible, this role will take an exponential amount of time for each new user. - -This is due to the usage of the `with_nested` loop function with up to 3 dictionaries. This is currently the only way I found to match indexes of dictionaries together. - -This basically mean that if the number of user is too large, this role will take so much time that it will never complete. The exact number is unknown (depends on computer power of hashicorp vault, the admin workstation, etc). - -For now, avoid more than 3 users. diff --git a/ansible/roles/ssh-keygen-and-store/tasks/from_hashicorp_to_server.yml b/ansible/roles/ssh-keygen-and-store/tasks/from_hashicorp_to_server.yml deleted file mode 100644 index 29de760e8..000000000 --- a/ansible/roles/ssh-keygen-and-store/tasks/from_hashicorp_to_server.yml +++ /dev/null @@ -1,28 +0,0 @@ ---- -# file: roles/ssh-keygen-and-store/tasks/from_hashicorp_to_server.yml - -- name: "make sure the .ssh directory exists" - file: - path: "{{ getent_passwd[item[0].item][4] }}/.ssh" - state: directory - owner: "{{ item[0].item }}" - group: "{{ item[0].item }}" - tags: ssh-keygen - -- name: "copy private key from hashicorp vault to server" - copy: - dest: "{{ getent_passwd[item[0].item][4] }}/.ssh/id_ed25519" - content: "{{ lookup('hashi_vault', 'secret=talas-kv/data/' + host_vars_location + '/' + ansible_hostname)['ssh_key_ed25519_' + item[0].item] + '\n' }}" - owner: "{{ item[0].item }}" - group: "{{ item[0].item }}" - mode: 0600 - tags: ssh-keygen - -- name: "copy public key from hashicorp vault to server" - copy: - dest: "{{ getent_passwd[item[0].item][4] }}/.ssh/id_ed25519.pub" - content: "{{ lookup('hashi_vault', 'secret=talas-kv/data/' + host_vars_location + '/' + ansible_hostname)['ssh_key_ed25519_' + item[0].item + '_pub'] + '\n' }}" - owner: "{{ item[0].item }}" - group: "{{ item[0].item }}" - mode: 0644 - tags: ssh-keygen diff --git a/ansible/roles/ssh-keygen-and-store/tasks/from_server_to_hashicorp_vault.yml b/ansible/roles/ssh-keygen-and-store/tasks/from_server_to_hashicorp_vault.yml deleted file mode 100644 index 44c9648c9..000000000 --- a/ansible/roles/ssh-keygen-and-store/tasks/from_server_to_hashicorp_vault.yml +++ /dev/null @@ -1,19 +0,0 @@ ---- -# file: roles/ssh-keygen-and-store/tasks/from_server_to_hashicorp_vault.yml - -- name: "trying to patch hashicorp vault" - delegate_to: localhost - become: no - command: 'vault kv patch talas-kv/{{ host_vars_location }}/{{ ansible_hostname }} ssh_key_ed25519_{{ item[0].item }}="{{ item[1].stdout }}" ssh_key_ed25519_{{ item[0].item }}_pub="{{ item[2].stdout }}"' - register: result - ignore_errors: True - tags: ssh-keygen - -- name: "patch failed because the entry doesn't exist, creating it instead" - delegate_to: localhost - become: no - command: 'vault kv put talas-kv/{{ host_vars_location }}/{{ ansible_hostname }} ssh_key_ed25519_{{ item[0].item }}="{{ item[1].stdout }}" ssh_key_ed25519_{{ item[0].item }}_pub="{{ item[2].stdout }}"' - when: - - result.failed - - '"No value found" in result.stderr' - tags: ssh-keygen diff --git a/ansible/roles/ssh-keygen-and-store/tasks/generate_key.yml b/ansible/roles/ssh-keygen-and-store/tasks/generate_key.yml deleted file mode 100644 index 8053a1566..000000000 --- a/ansible/roles/ssh-keygen-and-store/tasks/generate_key.yml +++ /dev/null @@ -1,38 +0,0 @@ ---- -# file: roles/ssh-keygen-and-store/tasks/main.yml - -- name: "make sure the .ssh directory exists" - file: - path: "{{ getent_passwd[item[0].item][4] }}/.ssh" - state: directory - owner: "{{ item[0].item }}" - group: "{{ item[0].item }}" - tags: ssh-keygen - -- name: "generate key" - openssh_keypair: - path: "{{ getent_passwd[item[0].item][4] }}/.ssh/id_ed25519" - type: ed25519 - comment: "{{ item[0].item }}@{{ ansible_hostname }}" - owner: "{{ item[0].item }}" - tags: ssh-keygen - -- name: "register the key in variable" - command: "cat {{ getent_passwd[item[0].item][4] }}/.ssh/id_ed25519" - ignore_errors: True - changed_when: False - register: generated_key - tags: ssh-keygen - -- name: "register the public key in variable" - command: "cat {{ getent_passwd[item[0].item][4] }}/.ssh/id_ed25519.pub" - ignore_errors: True - changed_when: False - register: generated_key_pub - tags: ssh-keygen - -- name: "put the newly generated key in ansible vault" - delegate_to: localhost - become: no - command: 'vault kv patch talas-kv/{{ host_vars_location }}/{{ ansible_hostname }} ssh_key_ed25519_{{ item[0].item }}="{{ generated_key.stdout }}" ssh_key_ed25519_{{ item[0].item }}_pub="{{ generated_key_pub.stdout }}"' - tags: ssh-keygen diff --git a/ansible/roles/ssh-keygen-and-store/tasks/main.yml b/ansible/roles/ssh-keygen-and-store/tasks/main.yml deleted file mode 100644 index 816d9a90a..000000000 --- a/ansible/roles/ssh-keygen-and-store/tasks/main.yml +++ /dev/null @@ -1,97 +0,0 @@ ---- -# file: roles/ssh-keygen-and-store/tasks/main.yml - -- name: "getent passwd" - getent: - database: passwd - tags: ssh-keygen - -- name: "check if the ssh private key is already stored on hashicorp vault" - command: "vault kv get -field=ssh_key_ed25519_{{ item }} talas-kv/{{ host_vars_location }}/{{ ansible_hostname }}" - delegate_to: localhost - become: False - ignore_errors: True - changed_when: False - check_mode: no - loop: "{{ ssh_keygen_user_list }}" - register: hashicorp_private_keys - tags: ssh-keygen - -- name: "check if the ssh public key is already stored on hashicorp vault" - command: "vault kv get -field=ssh_key_ed25519_{{ item }} talas-kv/{{ host_vars_location }}/{{ ansible_hostname }}" - delegate_to: localhost - become: False - ignore_errors: True - changed_when: False - check_mode: no - loop: "{{ ssh_keygen_user_list }}" - register: hashicorp_public_keys - tags: ssh-keygen - -- name: "check if there is a private key on the remote server" - shell: "cat $(getent passwd {{ item }} | cut -d':' -f6)/.ssh/id_ed25519" - ignore_errors: True - changed_when: False - check_mode: no - loop: "{{ ssh_keygen_user_list }}" - register: local_private_keys - tags: ssh-keygen - -- name: "check if there is a public key on the remote server" - shell: "cat $(getent passwd {{ item }} | cut -d':' -f6)/.ssh/id_ed25519.pub" - ignore_errors: True - changed_when: False - check_mode: no - loop: "{{ ssh_keygen_user_list }}" - register: local_public_keys - tags: ssh-keygen - -- name: "key is nowhere" - include_tasks: generate_key.yml - with_nested: - - "{{ hashicorp_private_keys.results }}" - - "{{ local_private_keys.results }}" - when: - - item[0].item == item[1].item - - item[0].failed - - item[1].failed - tags: ssh-keygen - -- name: "key is only on the local server, sending it to hashicorp vault" - include_tasks: from_server_to_hashicorp_vault.yml - with_nested: - - "{{ hashicorp_private_keys.results }}" - - "{{ local_private_keys.results }}" - - "{{ local_public_keys.results }}" - when: - - item[0].item == item[1].item - - item[0].item == item[2].item - - item[0].failed - - not item[1].failed - tags: ssh-keygen - -- name: "key is only on hashicorp vault, it will be restored on the server" - include_tasks: from_hashicorp_to_server.yml - with_nested: - - "{{ hashicorp_private_keys.results }}" - - "{{ local_private_keys.results }}" - - "{{ hashicorp_public_keys.results }}" - when: - - item[0].item == item[1].item - - item[0].item == item[2].item - - not item[0].failed - - item[1].failed - tags: ssh-keygen - -- name: "checking that both private keys are the same" - fail: - msg: "The private key on hashicorp vault and the one on the server are different!" - with_nested: - - "{{ hashicorp_private_keys.results }}" - - "{{ local_private_keys.results }}" - when: - - item[0].item == item[1].item - - not item[0].failed - - not item[1].failed - - item[1].stdout != item[0].stdout - tags: ssh-keygen diff --git a/ansible/roles/zfs/defaults/main.yml b/ansible/roles/zfs/defaults/main.yml deleted file mode 100644 index 81b944d79..000000000 --- a/ansible/roles/zfs/defaults/main.yml +++ /dev/null @@ -1,10 +0,0 @@ -# file: zfs/defaults/main.yml - -# defaults = 1/8 of RAM -max_arc_size: "{{ ansible_memtotal_mb * 131072 }}" - -# deactivate auto scrub by default -zfs_auto_scrub: False - -zfs_arc_meta_limit_percent: 95 -zfs_arc_dnode_limit_percent: 95 diff --git a/ansible/roles/zfs/files/clean_up_zfs_snap.py b/ansible/roles/zfs/files/clean_up_zfs_snap.py deleted file mode 100644 index b22d5c35d..000000000 --- a/ansible/roles/zfs/files/clean_up_zfs_snap.py +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/python3 -# -*- coding: utf-8 -*- # noqa: UP009 - -import sys # noqa: I001 -import time # noqa: F401 -import argparse -import datetime -import socket -from zfscos import zfscos - -# begin locksocket -MYNAME = sys.argv[0] -try: - lock_socket = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM) - lock_socket.bind('\0' + MYNAME) # noqa: Q000 -except: # noqa: E722 - print('could not create locksocket, program already running, only one copy of this program may run at a given time, exiting') # noqa: Q000 - sys.exit(1) - -# begin parser -parser = argparse.ArgumentParser(description = "clean up snapshots for any number of datasets and all their children, there are 2 retentions to configure" ) -parser.add_argument("-ds", "--dataset", help="list of dataset to consider, seperated by a space", type=str, nargs='+', required = True) # noqa: Q000 -parser.add_argument("-fh", "--full-history", dest="fh", help="keep this many days of all snapshots (including hourly snapshots) default=3", type=int, default=3) -parser.add_argument("-lh", "--long-history", dest="lh", help="keep this many days of daily snapshots (will only keep the snapshot from midnight this long) default=180", type=int, default=180) -parser.add_argument("-n", "--dry-run", dest="dryrun", help="show what would be done, but do nothing", action="store_true") -args = parser.parse_args() - -localtime = datetime.datetime.now() - -def snap_rotate(ds,fh,lh,dryrun): - if dryrun is False: - msg='' # noqa: Q000 - else: - msg='[DRYRUN] ' # noqa: Q000 - for snap in zfscos.zlist(ds,'snap'): # noqa: Q000 - if snap['creation'] < (localtime - datetime.timedelta(days=lh)): # noqa: Q000 - print(msg + 'removing snapshot ' + snap['name'] + ' older than long history ' + str(lh) + ' days') # noqa: Q000 - if args.dryrun is False: - zfscos.destroy(snap['name']) # noqa: Q000 - elif snap['creation'] < (localtime - datetime.timedelta(days=fh)) and int(snap['creation'].strftime('%H')) != 0: # noqa: Q000 - print(msg + 'removing snapshot ' + snap['name'] + ' older than full history ' + str(fh) + ' days') # noqa: Q000 - if args.dryrun is False: - zfscos.destroy(snap['name']) # noqa: Q000 - - -for ds in args.dataset: - if zfscos.zexist(ds): - snap_rotate(ds,args.fh,args.lh,args.dryrun) - else: - print( 'dataset ' + ds + ' does not exist, ignoring' ) # noqa: Q000 diff --git a/ansible/roles/zfs/files/zabbix_openzfs.conf b/ansible/roles/zfs/files/zabbix_openzfs.conf deleted file mode 100644 index 2c3326e76..000000000 --- a/ansible/roles/zfs/files/zabbix_openzfs.conf +++ /dev/null @@ -1,41 +0,0 @@ -# ZFS discovery and configuration -# original template from pbergbolt (source = https://www.zabbix.com/forum/showthread.php?t=43347), modified by Slash - - -# pool discovery -UserParameter=zfs.pool.discovery,zpool list -H -o name | sed -e '$ ! s/\(.*\)/{"{#POOLNAME}":"\1"},/' | sed -e '$ s/\(.*\)/{"{#POOLNAME}":"\1"}]}/' | sed -e '1 s/\(.*\)/{ \"data\":[\1/' -# dataset discovery, called "fileset" in the zabbix template for legacy reasons -UserParameter=zfs.fileset.discovery,zfs list -H -o name | sed -e '$ ! s/\(.*\)/{"{#FILESETNAME}":"\1"},/' | sed -e '$ s/\(.*\)/{"{#FILESETNAME}":"\1"}]}/' | sed -e '1 s/\(.*\)/{ \"data\":[\1/' -# vdev discovery -UserParameter=zfs.vdev.discovery,zpool list -Hv | grep '^[[:blank:]]' | egrep -v 'mirror|raidz|replacing' | awk '{print $1}' | sed -e '$ ! s/\(.*\)/{"{#VDEV}":"\1"},/' | sed -e '$ s/\(.*\)/{"{#VDEV}":"\1"}]}/' | sed -e '1 s/\(.*\)/{ \"data\":[\1/' - -# get any fs option -UserParameter=zfs.get.fsinfo[*],zfs get -o value -Hp $2 $1 - -# compressratio need special treatment because of the "x" at the end of the number -UserParameter=zfs.get.compressratio[*],zfs get -o value -Hp compressratio $1 | sed "s/x//" - -# memory used by ZFS: sum of the SPL slab allocator's statistics -# "There are a few things not included in that, like the page cache used by mmap(). But you can expect it to be relatively accurate." -UserParameter=zfs.memory.used,echo $(( `cat /proc/spl/kmem/slab | tail -n +3 | awk '{ print $3 }' | tr "\n" "+" | sed "s/$/0/"` )) - -# get any global zfs parameters -UserParameter=zfs.get.param[*],cat /sys/module/zfs/parameters/$1 - -# ARC stats from /proc/spl/kstat/zfs/arcstats -UserParameter=zfs.arcstats[*],awk '/^$1/ {printf $$3;}' /proc/spl/kstat/zfs/arcstats - -# detect if a scrub is in progress, 0 = in progress, 1 = not in progress -UserParameter=zfs.zpool.scrub[*],zpool status $1 | grep "scrub in progress" > /dev/null ; echo $? - -# get a zpool property current value -UserParameter=zpool.get[*],zpool get $2 -o value -Hp $1 - -# vdev state -UserParameter=zfs.vdev.state[*],zpool status | grep "$1" | awk '{ print $$2 }' -# vdev READ error counter -UserParameter=zfs.vdev.error_counter.read[*],zpool status | grep "$1" | awk '{ print $$3 }' | numfmt --from=si -# vdev WRITE error counter -UserParameter=zfs.vdev.error_counter.write[*],zpool status | grep "$1" | awk '{ print $$4 }' | numfmt --from=si -# vdev CHECKSUM error counter -UserParameter=zfs.vdev.error_counter.cksum[*],zpool status | grep "$1" | awk '{ print $$5 }' | numfmt --from=si diff --git a/ansible/roles/zfs/files/zfscos.sh b/ansible/roles/zfs/files/zfscos.sh deleted file mode 100644 index 7404f7c3d..000000000 --- a/ansible/roles/zfs/files/zfscos.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env bash - -/usr/local/venvs/zfscos/bin/python -i -c """ -import sys -from inspect import isfunction -from zfscos import zfscos as z -sys.ps1='[zfscos] >>> ' -print('zfscos imported as z') -print(f'available functions: {sorted([f for f, i in vars(z).items() if isfunction(i)])}') -""" diff --git a/ansible/roles/zfs/handlers/main.yml b/ansible/roles/zfs/handlers/main.yml deleted file mode 100644 index bb1db9a4f..000000000 --- a/ansible/roles/zfs/handlers/main.yml +++ /dev/null @@ -1,11 +0,0 @@ ---- -# file: zfs/handlers/main.yml - -- name: "set zfs_arc_max" - ansible.builtin.shell: "echo {{ max_arc_size }} > /sys/module/zfs/parameters/zfs_arc_max" - -- name: "set zfs_arc_meta_limit_percent" - ansible.builtin.shell: "echo {{ zfs_arc_meta_limit_percent }} > /sys/module/zfs/parameters/zfs_arc_meta_limit_percent" - -- name: "set zfs_arc_dnode_limit_percent" - ansible.builtin.shell: "echo {{ zfs_arc_dnode_limit_percent }} > /sys/module/zfs/parameters/zfs_arc_dnode_limit_percent" diff --git a/ansible/roles/zfs/meta/main.yml b/ansible/roles/zfs/meta/main.yml deleted file mode 100644 index 4625c7ea6..000000000 --- a/ansible/roles/zfs/meta/main.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -# file: roles/zfs/meta/main.yml - -dependencies: - - role: zabbix_template_assignment - zabbix_template_assignment_list: - - zabbix_name: talas OpenZFS - user_parameter: openzfs diff --git a/ansible/roles/zfs/readme.md b/ansible/roles/zfs/readme.md deleted file mode 100644 index f95007fea..000000000 --- a/ansible/roles/zfs/readme.md +++ /dev/null @@ -1,91 +0,0 @@ -# zpool and dataset creation -## zpool -This role can automatically create any number of zpool, to do so, you must specify at least their names and geometry with the `zfs_zpools` dictionary , for example: -``` -zfs_zpools: - - name: "nvme" - geometry: "mirror nvme0n1 nvme1n1" -``` -This will create a zpool named `nvme` in mirror mode with the 2 drives `nvme0n1` and `nvme1n1`. This will also set the following default option for the zpool creation: -``` --O compression=lz4 -O atime=off -O xattr=sa -O mountpoint=none -O acltype=posixacl -``` -If you want different options, you can set them by adding the keyword `options`, for example: -``` -zfs_zpools: - - name: "nvme" - geometry: "mirror nvme0n1 nvme1n1" - options: "-O compression=lz4 -O atime=off -O xattr=sa -O mountpoint=none -O acltype=posixacl" -``` -After the zpool has been created, this role will export it and import it back with the `-d /dev/disk/by-id` option to use the device id for future imports. -## dataset -This role can also create any number of dataset with any zfs properties. The syntax is: -``` -zfs_dataset_list: - - name: "tank/something" - properties: - mountpoint: "/media/something" - recordsize: "1M" - - name: "tank/something_else" -``` -The previous example will simply result in the following zfs commands: -``` -zfs create tank/something -o mountpoint=/media/something -o recordsize=1M -zfs create tank/something_else -``` -# ARC settings -## Size -You can adjust the maximum size of the ARC, which is the maximum amount of memory that ZFS can use at any given time. - -The default is 1/8 of the total memory. You can adjust this setting by changing the variable *max_arc_size*, since ansible uses the variable *ansible_memtotal_mb* for the host memory, you have to adjust the variable like this to get the amount of RAM that you want for ZoL: -``` -# 1/2 of RAM -max_arc_size: "{{ ansible_memtotal_mb * 524288 }}" -# 40% of RAM -max_arc_size: "{{ ansible_memtotal_mb * 419430 }}" -# 1/3 of RAM -max_arc_size: "{{ ansible_memtotal_mb * 349525 }}" -# 1/4 of RAM -max_arc_size: "{{ ansible_memtotal_mb * 262144 }}" -# 1/5 of RAM -max_arc_size: "{{ ansible_memtotal_mb * 209715 }}" -# 1/6 of RAM -max_arc_size: "{{ ansible_memtotal_mb * 174763 }}" -# 1/7 of RAM -max_arc_size: "{{ ansible_memtotal_mb * 149797 }}" -# 1/8 of RAM -max_arc_size: "{{ ansible_memtotal_mb * 131072 }}" -# 1/16 of RAM -max_arc_size: "{{ ansible_memtotal_mb * 65536 }}" -# 1/32 of RAM -max_arc_size: "{{ ansible_memtotal_mb * 32768 }}" -# 1/64 of RAM -max_arc_size: "{{ ansible_memtotal_mb * 16384 }}" -``` -This is the same thing as *max_arc_size: {{ ansible_memtotal_mb * 1024 * 1024 / [RATIO] }}*, for example: -``` -{{ ansible_memtotal_mb * 1024 * 1024 / 4 }} = {{ ansible_memtotal_mb * 262144 }} -``` -## meta and dnode percent limit -By default, zfs set the the zfs_arc_dnode_limit_percent to 10% and the zfs_arc_meta_limit_percent to 75%. - -From experience, most of the time it doesn't make any sense to limit ZFS for either values, so they are both set to 95% of the maximum memory in this role. - -You can adjust both parameters with those values if needed: -``` -zfs_arc_meta_limit_percent: 95 -zfs_arc_dnode_limit_percent: 95 -``` -# ZFS tunnable -Currently, you can set the "zfs_txg_timeout" module option with this value: -``` -zfs_txg_timeout: n -``` -Where n is a number, in second (the default is 5). -# Automatic scrub deactivation -On Ubuntu, a crontab exists to run a scrub the second Sunday of every month at midnight. This disrupt the normal server usage. - -By default this role deactivate this scrub, you can let it active by setting this variable: -``` -zfs_auto_scrub: True -``` diff --git a/ansible/roles/zfs/tasks/main.yml b/ansible/roles/zfs/tasks/main.yml deleted file mode 100644 index 1f61b07ff..000000000 --- a/ansible/roles/zfs/tasks/main.yml +++ /dev/null @@ -1,162 +0,0 @@ ---- -# file: roles/zfs/tasks/main.yml - -- name: "install python_package dependencies" - block: - - name: "set fact python_package_install_from_role" - ansible.builtin.set_fact: - python_package_install_from_role: - - package: zfscos - script: zfscos - - name: "import role python_package" - ansible.builtin.import_role: - name: python_package - - name: "unset fact python_package_install" - ansible.builtin.set_fact: - python_package_install_from_role: [] - tags: - - scripts - - python_package - - zfs - -- name: "zfs_arc_max value" - ansible.builtin.debug: - var: max_arc_size - verbosity: 1 - tags: zfs - -- name: "[debian] apt install linux-headers-amd64 linux-headers-{{ ansible_kernel }}" - ansible.builtin.apt: - name: - - linux-headers-amd64 - - "linux-headers-{{ ansible_kernel }}" - when: ansible_distribution == "Debian" - tags: zfs - -- name: "[debian] /etc/apt/preferences.d/90_zfs" - ansible.builtin.copy: - dest: /etc/apt/preferences.d/90_zfs - content: "Package: src:zfs-linux\nPin: release n={{ ansible_distribution_release }}-backports\nPin-Priority: 990\n" - when: ansible_distribution == "Debian" - tags: zfs - -- name: "[debian] apt install -t {{ ansible_distribution_release }}-backports zfs-dkms zfsutils-linux (excluding recommended packages)" - ansible.builtin.apt: - name: - - zfs-dkms - - zfsutils-linux - default_release: "{{ ansible_distribution_release }}-backports" - install_recommends: no - when: ansible_distribution == "Debian" - tags: zfs - -- name: "[ubuntu] apt install zfsutils-linux, apt update if cache < 1 day" - ansible.builtin.apt: - name: zfsutils-linux - cache_valid_time: 86400 - when: ansible_distribution == "Ubuntu" - tags: zfs - -- name: "gather the list of installed packages to get the zfsutils-linux version" - package_facts: - tags: zfs - -- name: "display the current version of zfsutils-linux" - ansible.builtin.debug: - var: ansible_facts.packages['zfsutils-linux'][0]['version'] - verbosity: 1 - tags: zfs - -- name: "[<2.2] kernel option at /etc/modprobe.d/zfs.conf" - ansible.builtin.template: - src: zfs.conf - dest: /etc/modprobe.d/zfs.conf - notify: - - set zfs_arc_max - - set zfs_arc_meta_limit_percent - - set zfs_arc_dnode_limit_percent - register: modprobe - when: ansible_facts.packages['zfsutils-linux'][0]['version'] is version('2.2', '<') - tags: zfs - -- name: "[>=2.2] kernel option at /etc/modprobe.d/zfs.conf" - ansible.builtin.template: - src: zfs.conf - dest: /etc/modprobe.d/zfs.conf - notify: - - set zfs_arc_max - - set zfs_arc_dnode_limit_percent - register: modprobe - when: ansible_facts.packages['zfsutils-linux'][0]['version'] is version('2.2', '>=') - tags: zfs - -- name: "disable the default croned monthly scrub" - ansible.builtin.file: - path: /etc/cron.d/zfsutils-linux - state: absent - when: not zfs_auto_scrub - tags: zfs - -- name: "modprobe zfs" - community.general.modprobe: - name: zfs - tags: zfs - -- name: "echo {{ zfs_txg_timeout }} > /sys/module/zfs/parameters/zfs_txg_timeout" - ansible.builtin.shell: "echo {{ zfs_txg_timeout }} > /sys/module/zfs/parameters/zfs_txg_timeout" - when: - - zfs_txg_timeout is defined - - modprobe.changed - tags: zfs - -- name: "echo {{ zfs_arc_dnode_limit_percent }} > /sys/module/zfs/parameters/zfs_arc_dnode_limit_percent" - ansible.builtin.shell: "echo {{ zfs_arc_dnode_limit_percent }} > /sys/module/zfs/parameters/zfs_arc_dnode_limit_percent" - when: - - zfs_arc_dnode_limit_percent is defined - - modprobe.changed - tags: zfs - -- name: "echo {{ zfs_arc_meta_limit_percent }} > /sys/module/zfs/parameters/zfs_arc_meta_limit_percent" - ansible.builtin.shell: "echo {{ zfs_arc_meta_limit_percent }} > /sys/module/zfs/parameters/zfs_arc_meta_limit_percent" - when: - - ansible_facts.packages['zfsutils-linux'][0]['version'] is version('2.2', '<') - - zfs_arc_meta_limit_percent is defined - - modprobe.changed - tags: zfs - -- name: "/usr/local/bin/clean_up_zfs_snap.py" - ansible.builtin.copy: - src: "clean_up_zfs_snap.py" - dest: "/usr/local/bin/clean_up_zfs_snap.py" - mode: 0755 - tags: - - zfs - - clean-up-zfs-snap - -- name: "/usr/local/bin/zfscos" - ansible.builtin.copy: - src: zfscos.sh - dest: /usr/local/bin/zfscos - mode: 0755 - tags: - - zfs - - scripts - - python_package - -- name: "import_tasks: zpool.yml" - ansible.builtin.import_tasks: zpool.yml - when: zfs_zpools is defined - tags: - - zfs - - zpool - -- name: "make sure the requested datasets exists" - community.general.zfs: - name: "{{ item['name'] }}" - state: present - extra_zfs_properties: "{{ item['properties'] | default(omit) }}" - loop: "{{ zfs_dataset_list }}" - when: zfs_dataset_list is defined - tags: - - zfs - - dataset diff --git a/ansible/roles/zfs/tasks/zpool.yml b/ansible/roles/zfs/tasks/zpool.yml deleted file mode 100644 index a6e6bf5a1..000000000 --- a/ansible/roles/zfs/tasks/zpool.yml +++ /dev/null @@ -1,25 +0,0 @@ ---- -# roles/zfs/tasks/zpool.yml - -- name: "check if each zpool from zfs_zpools exists" - community.general.zpool_facts: - name: "{{ item.name }}" - properties: "name" - loop: "{{ zfs_zpools }}" - register: zfs_zpools_result - ignore_errors: true - -- name: "create zpools that don't exist" - ansible.builtin.command: "zpool create {{ item.item.name }} {{ item.item.geometry }} {{ item.item.options | default('-O compression=lz4 -O atime=off -O xattr=sa -O mountpoint=none -O acltype=posixacl') }}" - loop: "{{ zfs_zpools_result.results }}" - when: item.failed - -- name: "run zpool export [zpool_name]" - ansible.builtin.command: "zpool export {{ item.item.name }}" - loop: "{{ zfs_zpools_result.results }}" - when: item.failed - -- name: "run zpool import -d /dev/disk/by-id [zpool_name]" - ansible.builtin.command: "zpool import -d /dev/disk/by-id {{ item.item.name }}" - loop: "{{ zfs_zpools_result.results }}" - when: item.failed diff --git a/ansible/roles/zfs/templates/zfs.conf b/ansible/roles/zfs/templates/zfs.conf deleted file mode 100644 index dabe413d9..000000000 --- a/ansible/roles/zfs/templates/zfs.conf +++ /dev/null @@ -1,11 +0,0 @@ -# {{ ansible_managed }} -options zfs zfs_arc_max={{ max_arc_size }} -{% if zfs_txg_timeout is defined %} -options zfs zfs_txg_timeout={{ zfs_txg_timeout }} -{% endif %} -{% if zfs_arc_dnode_limit_percent is defined %} -options zfs zfs_arc_dnode_limit_percent={{ zfs_arc_dnode_limit_percent }} -{% endif %} -{% if ansible_facts.packages['zfsutils-linux'][0]['version'] is version('2.2', '<') and zfs_arc_meta_limit_percent is defined %} -options zfs zfs_arc_meta_limit_percent={{ zfs_arc_meta_limit_percent }} -{% endif %} diff --git a/apps/web/e2e-results.json b/apps/web/e2e-results.json index 73e8ceffd..a6596a8d1 100644 --- a/apps/web/e2e-results.json +++ b/apps/web/e2e-results.json @@ -112,4781 +112,2964 @@ }, "suites": [ { - "title": "mvp-integration.spec.ts", - "file": "mvp-integration.spec.ts", + "title": "auth.spec.ts", + "file": "auth.spec.ts", "column": 0, "line": 0, "specs": [], "suites": [ { - "title": "MVP Integration Tests - Exhaustifs", - "file": "mvp-integration.spec.ts", - "line": 42, + "title": "Authentication Flow", + "file": "auth.spec.ts", + "line": 25, "column": 6, - "specs": [], - "suites": [ + "specs": [ { - "title": "1. Authentication Flow", - "file": "mvp-integration.spec.ts", - "line": 51, - "column": 8, - "specs": [], - "suites": [ + "title": "should login successfully with valid credentials", + "ok": true, + "tags": [], + "tests": [ { - "title": "Unauthenticated tests", - "file": "mvp-integration.spec.ts", - "line": 53, - "column": 10, - "specs": [ + "timeout": 60000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ { - "title": "1.1 - Login page loads correctly", - "ok": true, - "tags": [], - "tests": [ + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 3948, + "errors": [], + "stdout": [ { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "chromium", - "projectName": "chromium", - "results": [ - { - "workerIndex": 0, - "parallelIndex": 0, - "status": "passed", - "duration": 1629, - "errors": [], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:18:13.569Z", - "annotations": [], - "attachments": [] - } - ], - "status": "expected" + "text": "🧪 [AUTH TEST] Running: Login with valid credentials\n" + }, + { + "text": "✏️ [FILL] Filling field input[type=\"email\"], input[name=\"email\"] with value: e2e@test.com\n" + }, + { + "text": "✅ [FILL] Field input[type=\"email\"], input[name=\"email\"] filled successfully\n" + }, + { + "text": "✏️ [FILL] Filling field input[type=\"password\"], input[name=\"password\"] with value: Xk9$mP2#vL7@nQ4!wR8\n" + }, + { + "text": "✅ [FILL] Field input[type=\"password\"], input[name=\"password\"] filled successfully\n" + }, + { + "text": "⚡ [FORM SUBMIT] Forcing submission of form: form\n" + }, + { + "text": "🔍 [FORM SUBMIT] Waiting for form selector: form\n" + }, + { + "text": "⏳ [FORM SUBMIT] Waiting for React to update state...\n" + }, + { + "text": "🚀 [FORM SUBMIT] Submitting form...\n" + }, + { + "text": "✅ [FORM SUBMIT] Form form submitted successfully\n" + }, + { + "text": "⏳ [AUTH TEST] Waiting for Zustand to persist auth-storage...\n" + }, + { + "text": " ✅ TOKEN FOUND: eyJhbGciOiJIUzI1NiIsInR5cCI6Ik... (source: storage)\n" + }, + { + "text": "✅ [AUTH TEST] Login successful (token in storage)\n" + }, + { + "text": "\n📊 [AUTH TEST] === Final Verifications ===\n" + }, + { + "text": "✅ [AUTH TEST] No console errors\n" + }, + { + "text": "✅ [AUTH TEST] No network errors\n" } ], - "id": "c50e3d8c82f89a880019-be5e0fbc4f6682284cc9", - "file": "mvp-integration.spec.ts", - "line": 57, - "column": 7 - }, - { - "title": "1.2 - Register page loads correctly", - "ok": true, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "chromium", - "projectName": "chromium", - "results": [ - { - "workerIndex": 0, - "parallelIndex": 0, - "status": "passed", - "duration": 565, - "errors": [], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:18:15.276Z", - "annotations": [], - "attachments": [] - } - ], - "status": "expected" - } - ], - "id": "c50e3d8c82f89a880019-f7e466c695e5a862b8e3", - "file": "mvp-integration.spec.ts", - "line": 82, - "column": 7 - }, - { - "title": "1.3 - Can register new user", - "ok": true, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "chromium", - "projectName": "chromium", - "results": [ - { - "workerIndex": 0, - "parallelIndex": 0, - "status": "passed", - "duration": 2343, - "errors": [], - "stdout": [ - { - "text": "User registered successfully via API\n" - }, - { - "text": "User verified - can login after registration\n" - } - ], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:18:15.857Z", - "annotations": [], - "attachments": [] - } - ], - "status": "expected" - } - ], - "id": "c50e3d8c82f89a880019-6fdba03834c145834c82", - "file": "mvp-integration.spec.ts", - "line": 96, - "column": 7 - }, - { - "title": "1.4 - Can login with registered user", - "ok": true, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "chromium", - "projectName": "chromium", - "results": [ - { - "workerIndex": 0, - "parallelIndex": 0, - "status": "passed", - "duration": 2208, - "errors": [], - "stdout": [ - { - "text": "Test user registered successfully via API\n" - } - ], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:18:18.213Z", - "annotations": [], - "attachments": [] - } - ], - "status": "expected" - } - ], - "id": "c50e3d8c82f89a880019-01e876dd560f187af754", - "file": "mvp-integration.spec.ts", - "line": 160, - "column": 7 - }, - { - "title": "1.5 - Protected route redirects when not logged in", - "ok": true, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "chromium", - "projectName": "chromium", - "results": [ - { - "workerIndex": 0, - "parallelIndex": 0, - "status": "passed", - "duration": 672, - "errors": [], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:18:20.439Z", - "annotations": [], - "attachments": [] - } - ], - "status": "expected" - } - ], - "id": "c50e3d8c82f89a880019-5c5b6a5eb8219cd7a1f8", - "file": "mvp-integration.spec.ts", - "line": 342, - "column": 7 - }, - { - "title": "1.1 - Login page loads correctly", - "ok": true, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "firefox", - "projectName": "firefox", - "results": [ - { - "workerIndex": 15, - "parallelIndex": 0, - "status": "passed", - "duration": 2184, - "errors": [], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:29:54.169Z", - "annotations": [], - "attachments": [] - } - ], - "status": "expected" - } - ], - "id": "c50e3d8c82f89a880019-d84504f3a4366c07772f", - "file": "mvp-integration.spec.ts", - "line": 57, - "column": 7 - }, - { - "title": "1.2 - Register page loads correctly", - "ok": true, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "firefox", - "projectName": "firefox", - "results": [ - { - "workerIndex": 15, - "parallelIndex": 0, - "status": "passed", - "duration": 607, - "errors": [], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:29:56.962Z", - "annotations": [], - "attachments": [] - } - ], - "status": "expected" - } - ], - "id": "c50e3d8c82f89a880019-1a1150fafbf917826c95", - "file": "mvp-integration.spec.ts", - "line": 82, - "column": 7 - }, - { - "title": "1.3 - Can register new user", - "ok": true, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "firefox", - "projectName": "firefox", - "results": [ - { - "workerIndex": 15, - "parallelIndex": 0, - "status": "passed", - "duration": 2321, - "errors": [], - "stdout": [ - { - "text": "User registered successfully via API\n" - }, - { - "text": "User verified - can login after registration\n" - } - ], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:29:57.587Z", - "annotations": [], - "attachments": [] - } - ], - "status": "expected" - } - ], - "id": "c50e3d8c82f89a880019-c1aa5e267d26955aa66d", - "file": "mvp-integration.spec.ts", - "line": 96, - "column": 7 - }, - { - "title": "1.4 - Can login with registered user", - "ok": true, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "firefox", - "projectName": "firefox", - "results": [ - { - "workerIndex": 15, - "parallelIndex": 0, - "status": "passed", - "duration": 2308, - "errors": [], - "stdout": [ - { - "text": "Test user registered successfully via API\n" - } - ], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:29:59.920Z", - "annotations": [], - "attachments": [] - } - ], - "status": "expected" - } - ], - "id": "c50e3d8c82f89a880019-2ac38895118ba04624db", - "file": "mvp-integration.spec.ts", - "line": 160, - "column": 7 - }, - { - "title": "1.5 - Protected route redirects when not logged in", - "ok": true, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "firefox", - "projectName": "firefox", - "results": [ - { - "workerIndex": 15, - "parallelIndex": 0, - "status": "passed", - "duration": 999, - "errors": [], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:30:02.238Z", - "annotations": [], - "attachments": [] - } - ], - "status": "expected" - } - ], - "id": "c50e3d8c82f89a880019-498db7c446cd73af1176", - "file": "mvp-integration.spec.ts", - "line": 342, - "column": 7 - }, - { - "title": "1.1 - Login page loads correctly", - "ok": false, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "webkit", - "projectName": "webkit", - "results": [ - { - "workerIndex": 30, - "parallelIndex": 0, - "status": "failed", - "duration": 2, - "error": { - "message": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝", - "stack": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝" - }, - "errors": [ - { - "message": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝" - } - ], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:34:56.403Z", - "annotations": [], - "attachments": [] - } - ], - "status": "unexpected" - } - ], - "id": "c50e3d8c82f89a880019-1309a2e6b53b675adb67", - "file": "mvp-integration.spec.ts", - "line": 57, - "column": 7 - }, - { - "title": "1.2 - Register page loads correctly", - "ok": false, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "webkit", - "projectName": "webkit", - "results": [ - { - "workerIndex": 31, - "parallelIndex": 0, - "status": "failed", - "duration": 2, - "error": { - "message": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝", - "stack": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝" - }, - "errors": [ - { - "message": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝" - } - ], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:34:57.115Z", - "annotations": [], - "attachments": [] - } - ], - "status": "unexpected" - } - ], - "id": "c50e3d8c82f89a880019-a43ca92baab99d59897b", - "file": "mvp-integration.spec.ts", - "line": 82, - "column": 7 - }, - { - "title": "1.3 - Can register new user", - "ok": false, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "webkit", - "projectName": "webkit", - "results": [ - { - "workerIndex": 32, - "parallelIndex": 0, - "status": "failed", - "duration": 3, - "error": { - "message": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝", - "stack": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝" - }, - "errors": [ - { - "message": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝" - } - ], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:34:57.887Z", - "annotations": [], - "attachments": [] - } - ], - "status": "unexpected" - } - ], - "id": "c50e3d8c82f89a880019-9450b3c875e31885ff89", - "file": "mvp-integration.spec.ts", - "line": 96, - "column": 7 - }, - { - "title": "1.4 - Can login with registered user", - "ok": false, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "webkit", - "projectName": "webkit", - "results": [ - { - "workerIndex": 33, - "parallelIndex": 0, - "status": "failed", - "duration": 2, - "error": { - "message": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝", - "stack": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝" - }, - "errors": [ - { - "message": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝" - } - ], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:34:58.643Z", - "annotations": [], - "attachments": [] - } - ], - "status": "unexpected" - } - ], - "id": "c50e3d8c82f89a880019-66c09b63579b0e8784ad", - "file": "mvp-integration.spec.ts", - "line": 160, - "column": 7 - }, - { - "title": "1.5 - Protected route redirects when not logged in", - "ok": false, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "webkit", - "projectName": "webkit", - "results": [ - { - "workerIndex": 34, - "parallelIndex": 0, - "status": "failed", - "duration": 4, - "error": { - "message": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝", - "stack": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝" - }, - "errors": [ - { - "message": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝" - } - ], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:34:59.328Z", - "annotations": [], - "attachments": [] - } - ], - "status": "unexpected" - } - ], - "id": "c50e3d8c82f89a880019-9ce59b4d17a7daac26e3", - "file": "mvp-integration.spec.ts", - "line": 342, - "column": 7 - }, - { - "title": "1.1 - Login page loads correctly", - "ok": true, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "msedge", - "projectName": "msedge", - "results": [ - { - "workerIndex": 52, - "parallelIndex": 0, - "status": "passed", - "duration": 1533, - "errors": [], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:35:12.324Z", - "annotations": [], - "attachments": [] - } - ], - "status": "expected" - } - ], - "id": "c50e3d8c82f89a880019-4c89419d9a5663a04004", - "file": "mvp-integration.spec.ts", - "line": 57, - "column": 7 - }, - { - "title": "1.2 - Register page loads correctly", - "ok": true, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "msedge", - "projectName": "msedge", - "results": [ - { - "workerIndex": 52, - "parallelIndex": 0, - "status": "passed", - "duration": 541, - "errors": [], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:35:13.918Z", - "annotations": [], - "attachments": [] - } - ], - "status": "expected" - } - ], - "id": "c50e3d8c82f89a880019-3dc2b04c106b962cca98", - "file": "mvp-integration.spec.ts", - "line": 82, - "column": 7 - }, - { - "title": "1.3 - Can register new user", - "ok": true, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "msedge", - "projectName": "msedge", - "results": [ - { - "workerIndex": 52, - "parallelIndex": 0, - "status": "passed", - "duration": 2394, - "errors": [], - "stdout": [ - { - "text": "User registered successfully via API\n" - }, - { - "text": "User verified - can login after registration\n" - } - ], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:35:14.474Z", - "annotations": [], - "attachments": [] - } - ], - "status": "expected" - } - ], - "id": "c50e3d8c82f89a880019-fe88f3ab59bfdf2feda6", - "file": "mvp-integration.spec.ts", - "line": 96, - "column": 7 - }, - { - "title": "1.4 - Can login with registered user", - "ok": true, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "msedge", - "projectName": "msedge", - "results": [ - { - "workerIndex": 52, - "parallelIndex": 0, - "status": "passed", - "duration": 2238, - "errors": [], - "stdout": [ - { - "text": "Test user registered successfully via API\n" - } - ], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:35:16.885Z", - "annotations": [], - "attachments": [] - } - ], - "status": "expected" - } - ], - "id": "c50e3d8c82f89a880019-edc953ebc3457cfb8a18", - "file": "mvp-integration.spec.ts", - "line": 160, - "column": 7 - }, - { - "title": "1.5 - Protected route redirects when not logged in", - "ok": true, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "msedge", - "projectName": "msedge", - "results": [ - { - "workerIndex": 52, - "parallelIndex": 0, - "status": "passed", - "duration": 742, - "errors": [], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:35:19.144Z", - "annotations": [], - "attachments": [] - } - ], - "status": "expected" - } - ], - "id": "c50e3d8c82f89a880019-6c0f9af1b6cd7aa9542e", - "file": "mvp-integration.spec.ts", - "line": 342, - "column": 7 + "stderr": [], + "retry": 0, + "startTime": "2026-01-10T21:50:21.035Z", + "annotations": [], + "attachments": [] } - ] - }, - { - "title": "Authenticated tests", - "file": "mvp-integration.spec.ts", - "line": 361, - "column": 10, - "specs": [ - { - "title": "1.6 - Can logout", - "ok": true, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "chromium", - "projectName": "chromium", - "results": [ - { - "workerIndex": 0, - "parallelIndex": 0, - "status": "passed", - "duration": 486, - "errors": [], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:18:21.123Z", - "annotations": [], - "attachments": [] - } - ], - "status": "expected" - } - ], - "id": "c50e3d8c82f89a880019-3d23f0ac29f971b28f92", - "file": "mvp-integration.spec.ts", - "line": 362, - "column": 7 - }, - { - "title": "1.6 - Can logout", - "ok": true, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "firefox", - "projectName": "firefox", - "results": [ - { - "workerIndex": 15, - "parallelIndex": 0, - "status": "passed", - "duration": 625, - "errors": [], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:30:03.250Z", - "annotations": [], - "attachments": [] - } - ], - "status": "expected" - } - ], - "id": "c50e3d8c82f89a880019-c07d74ef82df045fed61", - "file": "mvp-integration.spec.ts", - "line": 362, - "column": 7 - }, - { - "title": "1.6 - Can logout", - "ok": false, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "webkit", - "projectName": "webkit", - "results": [ - { - "workerIndex": 35, - "parallelIndex": 0, - "status": "failed", - "duration": 3, - "error": { - "message": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝", - "stack": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝" - }, - "errors": [ - { - "message": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝" - } - ], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:35:00.079Z", - "annotations": [], - "attachments": [] - } - ], - "status": "unexpected" - } - ], - "id": "c50e3d8c82f89a880019-d6b9f7864b4599f74ea1", - "file": "mvp-integration.spec.ts", - "line": 362, - "column": 7 - }, - { - "title": "1.6 - Can logout", - "ok": true, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "msedge", - "projectName": "msedge", - "results": [ - { - "workerIndex": 52, - "parallelIndex": 0, - "status": "passed", - "duration": 422, - "errors": [], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:35:19.902Z", - "annotations": [], - "attachments": [] - } - ], - "status": "expected" - } - ], - "id": "c50e3d8c82f89a880019-4ede48d85d6ca3e23ab3", - "file": "mvp-integration.spec.ts", - "line": 362, - "column": 7 - } - ] + ], + "status": "expected" } - ] + ], + "id": "d748ac400d08b85935ef-efd14549b37f77aa6096", + "file": "auth.spec.ts", + "line": 41, + "column": 3 }, { - "title": "2. Dashboard & Navigation", - "file": "mvp-integration.spec.ts", - "line": 390, - "column": 8, - "specs": [ + "title": "should show error with invalid credentials", + "ok": true, + "tags": [], + "tests": [ { - "title": "2.1 - Dashboard loads without errors", - "ok": false, - "tags": [], - "tests": [ + "timeout": 60000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "chromium", - "projectName": "chromium", - "results": [ + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 1997, + "errors": [], + "stdout": [ { - "workerIndex": 0, - "parallelIndex": 0, - "status": "timedOut", - "duration": 61950, - "error": { - "message": "\u001b[31mTest timeout of 60000ms exceeded while running \"beforeEach\" hook.\u001b[39m", - "stack": "\u001b[31mTest timeout of 60000ms exceeded while running \"beforeEach\" hook.\u001b[39m\n at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:392:10", - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 10, - "line": 392 - }, - "snippet": "\u001b[0m \u001b[90m 390 |\u001b[39m test\u001b[33m.\u001b[39mdescribe(\u001b[32m'2. Dashboard & Navigation'\u001b[39m\u001b[33m,\u001b[39m () \u001b[33m=>\u001b[39m {\n \u001b[90m 391 |\u001b[39m \n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 392 |\u001b[39m test\u001b[33m.\u001b[39mbeforeEach(\u001b[36masync\u001b[39m ({ page }) \u001b[33m=>\u001b[39m {\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 393 |\u001b[39m \u001b[90m// Login before each test\u001b[39m\n \u001b[90m 394 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mgoto(\u001b[32m`${TEST_CONFIG.FRONTEND_URL}/login`\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 395 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"email\"], input[name=\"email\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39memail)\u001b[33m;\u001b[39m\u001b[0m" - }, - "errors": [ - { - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 10, - "line": 392 - }, - "message": "\u001b[31mTest timeout of 60000ms exceeded while running \"beforeEach\" hook.\u001b[39m\n\n\u001b[0m \u001b[90m 390 |\u001b[39m test\u001b[33m.\u001b[39mdescribe(\u001b[32m'2. Dashboard & Navigation'\u001b[39m\u001b[33m,\u001b[39m () \u001b[33m=>\u001b[39m {\n \u001b[90m 391 |\u001b[39m \n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 392 |\u001b[39m test\u001b[33m.\u001b[39mbeforeEach(\u001b[36masync\u001b[39m ({ page }) \u001b[33m=>\u001b[39m {\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 393 |\u001b[39m \u001b[90m// Login before each test\u001b[39m\n \u001b[90m 394 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mgoto(\u001b[32m`${TEST_CONFIG.FRONTEND_URL}/login`\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 395 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"email\"], input[name=\"email\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39memail)\u001b[33m;\u001b[39m\u001b[0m\n\u001b[2m at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:392:10\u001b[22m" - }, - { - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 18, - "line": 395 - }, - "message": "Error: page.fill: Test timeout of 60000ms exceeded.\nCall log:\n\u001b[2m - waiting for locator('input[type=\"email\"], input[name=\"email\"]')\u001b[22m\n\n\n\u001b[0m \u001b[90m 393 |\u001b[39m \u001b[90m// Login before each test\u001b[39m\n \u001b[90m 394 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mgoto(\u001b[32m`${TEST_CONFIG.FRONTEND_URL}/login`\u001b[39m)\u001b[33m;\u001b[39m\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 395 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"email\"], input[name=\"email\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39memail)\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 396 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"password\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39mpassword)\u001b[33m;\u001b[39m\n \u001b[90m 397 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mclick(\u001b[32m'button[type=\"submit\"]'\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 398 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mwaitForURL(\u001b[35m/\\/(dashboard|home|app)/\u001b[39m\u001b[33m,\u001b[39m { timeout\u001b[33m:\u001b[39m \u001b[35m15000\u001b[39m })\u001b[33m;\u001b[39m\u001b[0m\n\u001b[2m at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:395:18\u001b[22m" - } - ], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:18:21.625Z", - "annotations": [], - "attachments": [ - { - "name": "screenshot", - "contentType": "image/png", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-d0e5b-hboard-loads-without-errors-chromium/test-failed-1.png" - }, - { - "name": "video", - "contentType": "video/webm", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-d0e5b-hboard-loads-without-errors-chromium/video.webm" - }, - { - "name": "error-context", - "contentType": "text/markdown", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-d0e5b-hboard-loads-without-errors-chromium/error-context.md" - } - ], - "errorLocation": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 10, - "line": 392 - } + "text": "🧪 [AUTH TEST] Running: Login with invalid credentials\n" + }, + { + "text": "✏️ [FILL] Filling field input[type=\"email\"], input[name=\"email\"] with value: wrong@example.com\n" + }, + { + "text": "✅ [FILL] Field input[type=\"email\"], input[name=\"email\"] filled successfully\n" + }, + { + "text": "✏️ [FILL] Filling field input[type=\"password\"], input[name=\"password\"] with value: wrongpassword\n" + }, + { + "text": "✅ [FILL] Field input[type=\"password\"], input[name=\"password\"] filled successfully\n" + }, + { + "text": "⚡ [FORM SUBMIT] Forcing submission of form: form\n" + }, + { + "text": "🔍 [FORM SUBMIT] Waiting for form selector: form\n" + }, + { + "text": "⏳ [FORM SUBMIT] Waiting for React to update state...\n" + }, + { + "text": "🚀 [FORM SUBMIT] Submitting form...\n" + }, + { + "text": "✅ [FORM SUBMIT] Form form submitted successfully\n" + }, + { + "text": "🔔 [TOAST] Waiting for error message...\n" + }, + { + "text": "🔴 [NETWORK ERROR] POST http://127.0.0.1:8080/api/v1/auth/login: 423\n" + }, + { + "text": "🔴 [CONSOLE ERROR] Failed to load resource: the server responded with a status of 423 (Locked)\n" + }, + { + "text": "✅ [TOAST] error message: Account is locked. Please try again later.\n" + }, + { + "text": "✅ [AUTH TEST] Error shown for invalid credentials\n" + }, + { + "text": "\n📊 [AUTH TEST] === Final Verifications ===\n" + }, + { + "text": "🔴 [AUTH TEST] Console errors (1):\n" + }, + { + "text": " - Failed to load resource: the server responded with a status of 423 (Locked)\n" + }, + { + "text": "🔴 [AUTH TEST] Network errors (1):\n" + }, + { + "text": " - POST http://127.0.0.1:8080/api/v1/auth/login: 423\n" } ], - "status": "unexpected" - } - ], - "id": "c50e3d8c82f89a880019-b6aee5a4b63aa68dbb7b", - "file": "mvp-integration.spec.ts", - "line": 401, - "column": 5 - }, - { - "title": "2.2 - Navigation works", - "ok": false, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "chromium", - "projectName": "chromium", - "results": [ + "stderr": [ { - "workerIndex": 1, - "parallelIndex": 0, - "status": "timedOut", - "duration": 60396, - "error": { - "message": "\u001b[31mTest timeout of 60000ms exceeded while running \"beforeEach\" hook.\u001b[39m", - "stack": "\u001b[31mTest timeout of 60000ms exceeded while running \"beforeEach\" hook.\u001b[39m\n at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:392:10", - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 10, - "line": 392 - }, - "snippet": "\u001b[0m \u001b[90m 390 |\u001b[39m test\u001b[33m.\u001b[39mdescribe(\u001b[32m'2. Dashboard & Navigation'\u001b[39m\u001b[33m,\u001b[39m () \u001b[33m=>\u001b[39m {\n \u001b[90m 391 |\u001b[39m \n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 392 |\u001b[39m test\u001b[33m.\u001b[39mbeforeEach(\u001b[36masync\u001b[39m ({ page }) \u001b[33m=>\u001b[39m {\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 393 |\u001b[39m \u001b[90m// Login before each test\u001b[39m\n \u001b[90m 394 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mgoto(\u001b[32m`${TEST_CONFIG.FRONTEND_URL}/login`\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 395 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"email\"], input[name=\"email\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39memail)\u001b[33m;\u001b[39m\u001b[0m" - }, - "errors": [ - { - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 10, - "line": 392 - }, - "message": "\u001b[31mTest timeout of 60000ms exceeded while running \"beforeEach\" hook.\u001b[39m\n\n\u001b[0m \u001b[90m 390 |\u001b[39m test\u001b[33m.\u001b[39mdescribe(\u001b[32m'2. Dashboard & Navigation'\u001b[39m\u001b[33m,\u001b[39m () \u001b[33m=>\u001b[39m {\n \u001b[90m 391 |\u001b[39m \n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 392 |\u001b[39m test\u001b[33m.\u001b[39mbeforeEach(\u001b[36masync\u001b[39m ({ page }) \u001b[33m=>\u001b[39m {\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 393 |\u001b[39m \u001b[90m// Login before each test\u001b[39m\n \u001b[90m 394 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mgoto(\u001b[32m`${TEST_CONFIG.FRONTEND_URL}/login`\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 395 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"email\"], input[name=\"email\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39memail)\u001b[33m;\u001b[39m\u001b[0m\n\u001b[2m at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:392:10\u001b[22m" - }, - { - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 18, - "line": 395 - }, - "message": "Error: page.fill: Test timeout of 60000ms exceeded.\nCall log:\n\u001b[2m - waiting for locator('input[type=\"email\"], input[name=\"email\"]')\u001b[22m\n\n\n\u001b[0m \u001b[90m 393 |\u001b[39m \u001b[90m// Login before each test\u001b[39m\n \u001b[90m 394 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mgoto(\u001b[32m`${TEST_CONFIG.FRONTEND_URL}/login`\u001b[39m)\u001b[33m;\u001b[39m\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 395 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"email\"], input[name=\"email\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39memail)\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 396 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"password\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39mpassword)\u001b[33m;\u001b[39m\n \u001b[90m 397 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mclick(\u001b[32m'button[type=\"submit\"]'\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 398 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mwaitForURL(\u001b[35m/\\/(dashboard|home|app)/\u001b[39m\u001b[33m,\u001b[39m { timeout\u001b[33m:\u001b[39m \u001b[35m15000\u001b[39m })\u001b[33m;\u001b[39m\u001b[0m\n\u001b[2m at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:395:18\u001b[22m" - } - ], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:19:24.180Z", - "annotations": [], - "attachments": [ - { - "name": "screenshot", - "contentType": "image/png", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-b9835-tion-2-2---Navigation-works-chromium/test-failed-1.png" - }, - { - "name": "video", - "contentType": "video/webm", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-b9835-tion-2-2---Navigation-works-chromium/video.webm" - }, - { - "name": "error-context", - "contentType": "text/markdown", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-b9835-tion-2-2---Navigation-works-chromium/error-context.md" - } - ], - "errorLocation": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 10, - "line": 392 - } + "text": "⚠️ [AUTH TEST] Test passed but had console errors\n" } ], - "status": "unexpected" - } - ], - "id": "c50e3d8c82f89a880019-3197e58de4c0ce61d7e9", - "file": "mvp-integration.spec.ts", - "line": 419, - "column": 5 - }, - { - "title": "2.1 - Dashboard loads without errors", - "ok": false, - "tags": [], - "tests": [ - { - "timeout": 60000, + "retry": 0, + "startTime": "2026-01-10T21:50:25.101Z", "annotations": [], - "expectedStatus": "passed", - "projectId": "firefox", - "projectName": "firefox", - "results": [ - { - "workerIndex": 15, - "parallelIndex": 0, - "status": "timedOut", - "duration": 60090, - "error": { - "message": "\u001b[31mTest timeout of 60000ms exceeded while running \"beforeEach\" hook.\u001b[39m", - "stack": "\u001b[31mTest timeout of 60000ms exceeded while running \"beforeEach\" hook.\u001b[39m\n at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:392:10", - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 10, - "line": 392 - }, - "snippet": "\u001b[0m \u001b[90m 390 |\u001b[39m test\u001b[33m.\u001b[39mdescribe(\u001b[32m'2. Dashboard & Navigation'\u001b[39m\u001b[33m,\u001b[39m () \u001b[33m=>\u001b[39m {\n \u001b[90m 391 |\u001b[39m \n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 392 |\u001b[39m test\u001b[33m.\u001b[39mbeforeEach(\u001b[36masync\u001b[39m ({ page }) \u001b[33m=>\u001b[39m {\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 393 |\u001b[39m \u001b[90m// Login before each test\u001b[39m\n \u001b[90m 394 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mgoto(\u001b[32m`${TEST_CONFIG.FRONTEND_URL}/login`\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 395 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"email\"], input[name=\"email\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39memail)\u001b[33m;\u001b[39m\u001b[0m" - }, - "errors": [ - { - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 10, - "line": 392 - }, - "message": "\u001b[31mTest timeout of 60000ms exceeded while running \"beforeEach\" hook.\u001b[39m\n\n\u001b[0m \u001b[90m 390 |\u001b[39m test\u001b[33m.\u001b[39mdescribe(\u001b[32m'2. Dashboard & Navigation'\u001b[39m\u001b[33m,\u001b[39m () \u001b[33m=>\u001b[39m {\n \u001b[90m 391 |\u001b[39m \n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 392 |\u001b[39m test\u001b[33m.\u001b[39mbeforeEach(\u001b[36masync\u001b[39m ({ page }) \u001b[33m=>\u001b[39m {\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 393 |\u001b[39m \u001b[90m// Login before each test\u001b[39m\n \u001b[90m 394 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mgoto(\u001b[32m`${TEST_CONFIG.FRONTEND_URL}/login`\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 395 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"email\"], input[name=\"email\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39memail)\u001b[33m;\u001b[39m\u001b[0m\n\u001b[2m at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:392:10\u001b[22m" - }, - { - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 18, - "line": 395 - }, - "message": "Error: page.fill: Test timeout of 60000ms exceeded.\nCall log:\n\u001b[2m - waiting for locator('input[type=\"email\"], input[name=\"email\"]')\u001b[22m\n\n\n\u001b[0m \u001b[90m 393 |\u001b[39m \u001b[90m// Login before each test\u001b[39m\n \u001b[90m 394 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mgoto(\u001b[32m`${TEST_CONFIG.FRONTEND_URL}/login`\u001b[39m)\u001b[33m;\u001b[39m\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 395 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"email\"], input[name=\"email\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39memail)\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 396 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"password\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39mpassword)\u001b[33m;\u001b[39m\n \u001b[90m 397 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mclick(\u001b[32m'button[type=\"submit\"]'\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 398 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mwaitForURL(\u001b[35m/\\/(dashboard|home|app)/\u001b[39m\u001b[33m,\u001b[39m { timeout\u001b[33m:\u001b[39m \u001b[35m15000\u001b[39m })\u001b[33m;\u001b[39m\u001b[0m\n\u001b[2m at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:395:18\u001b[22m" - } - ], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:30:03.891Z", - "annotations": [], - "attachments": [ - { - "name": "screenshot", - "contentType": "image/png", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-d0e5b-hboard-loads-without-errors-firefox/test-failed-1.png" - }, - { - "name": "video", - "contentType": "video/webm", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-d0e5b-hboard-loads-without-errors-firefox/video.webm" - }, - { - "name": "error-context", - "contentType": "text/markdown", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-d0e5b-hboard-loads-without-errors-firefox/error-context.md" - } - ], - "errorLocation": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 10, - "line": 392 - } - } - ], - "status": "unexpected" + "attachments": [] } ], - "id": "c50e3d8c82f89a880019-0cb9bb559cd6fd8674ad", - "file": "mvp-integration.spec.ts", - "line": 401, - "column": 5 - }, - { - "title": "2.2 - Navigation works", - "ok": false, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "firefox", - "projectName": "firefox", - "results": [ - { - "workerIndex": 16, - "parallelIndex": 0, - "status": "timedOut", - "duration": 60097, - "error": { - "message": "\u001b[31mTest timeout of 60000ms exceeded while running \"beforeEach\" hook.\u001b[39m", - "stack": "\u001b[31mTest timeout of 60000ms exceeded while running \"beforeEach\" hook.\u001b[39m\n at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:392:10", - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 10, - "line": 392 - }, - "snippet": "\u001b[0m \u001b[90m 390 |\u001b[39m test\u001b[33m.\u001b[39mdescribe(\u001b[32m'2. Dashboard & Navigation'\u001b[39m\u001b[33m,\u001b[39m () \u001b[33m=>\u001b[39m {\n \u001b[90m 391 |\u001b[39m \n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 392 |\u001b[39m test\u001b[33m.\u001b[39mbeforeEach(\u001b[36masync\u001b[39m ({ page }) \u001b[33m=>\u001b[39m {\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 393 |\u001b[39m \u001b[90m// Login before each test\u001b[39m\n \u001b[90m 394 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mgoto(\u001b[32m`${TEST_CONFIG.FRONTEND_URL}/login`\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 395 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"email\"], input[name=\"email\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39memail)\u001b[33m;\u001b[39m\u001b[0m" - }, - "errors": [ - { - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 10, - "line": 392 - }, - "message": "\u001b[31mTest timeout of 60000ms exceeded while running \"beforeEach\" hook.\u001b[39m\n\n\u001b[0m \u001b[90m 390 |\u001b[39m test\u001b[33m.\u001b[39mdescribe(\u001b[32m'2. Dashboard & Navigation'\u001b[39m\u001b[33m,\u001b[39m () \u001b[33m=>\u001b[39m {\n \u001b[90m 391 |\u001b[39m \n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 392 |\u001b[39m test\u001b[33m.\u001b[39mbeforeEach(\u001b[36masync\u001b[39m ({ page }) \u001b[33m=>\u001b[39m {\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 393 |\u001b[39m \u001b[90m// Login before each test\u001b[39m\n \u001b[90m 394 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mgoto(\u001b[32m`${TEST_CONFIG.FRONTEND_URL}/login`\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 395 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"email\"], input[name=\"email\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39memail)\u001b[33m;\u001b[39m\u001b[0m\n\u001b[2m at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:392:10\u001b[22m" - }, - { - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 18, - "line": 395 - }, - "message": "Error: page.fill: Test timeout of 60000ms exceeded.\nCall log:\n\u001b[2m - waiting for locator('input[type=\"email\"], input[name=\"email\"]')\u001b[22m\n\n\n\u001b[0m \u001b[90m 393 |\u001b[39m \u001b[90m// Login before each test\u001b[39m\n \u001b[90m 394 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mgoto(\u001b[32m`${TEST_CONFIG.FRONTEND_URL}/login`\u001b[39m)\u001b[33m;\u001b[39m\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 395 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"email\"], input[name=\"email\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39memail)\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 396 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"password\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39mpassword)\u001b[33m;\u001b[39m\n \u001b[90m 397 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mclick(\u001b[32m'button[type=\"submit\"]'\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 398 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mwaitForURL(\u001b[35m/\\/(dashboard|home|app)/\u001b[39m\u001b[33m,\u001b[39m { timeout\u001b[33m:\u001b[39m \u001b[35m15000\u001b[39m })\u001b[33m;\u001b[39m\u001b[0m\n\u001b[2m at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:395:18\u001b[22m" - } - ], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:31:04.949Z", - "annotations": [], - "attachments": [ - { - "name": "screenshot", - "contentType": "image/png", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-b9835-tion-2-2---Navigation-works-firefox/test-failed-1.png" - }, - { - "name": "video", - "contentType": "video/webm", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-b9835-tion-2-2---Navigation-works-firefox/video.webm" - }, - { - "name": "error-context", - "contentType": "text/markdown", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-b9835-tion-2-2---Navigation-works-firefox/error-context.md" - } - ], - "errorLocation": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 10, - "line": 392 - } - } - ], - "status": "unexpected" - } - ], - "id": "c50e3d8c82f89a880019-e5169f0f7891f1ee5a3e", - "file": "mvp-integration.spec.ts", - "line": 419, - "column": 5 - }, - { - "title": "2.1 - Dashboard loads without errors", - "ok": false, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "webkit", - "projectName": "webkit", - "results": [ - { - "workerIndex": 36, - "parallelIndex": 0, - "status": "failed", - "duration": 3, - "error": { - "message": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝", - "stack": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝" - }, - "errors": [ - { - "message": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝" - } - ], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:35:00.848Z", - "annotations": [], - "attachments": [] - } - ], - "status": "unexpected" - } - ], - "id": "c50e3d8c82f89a880019-eea86925fa2f7868b05b", - "file": "mvp-integration.spec.ts", - "line": 401, - "column": 5 - }, - { - "title": "2.2 - Navigation works", - "ok": false, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "webkit", - "projectName": "webkit", - "results": [ - { - "workerIndex": 37, - "parallelIndex": 0, - "status": "failed", - "duration": 2, - "error": { - "message": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝", - "stack": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝" - }, - "errors": [ - { - "message": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝" - } - ], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:35:01.554Z", - "annotations": [], - "attachments": [] - } - ], - "status": "unexpected" - } - ], - "id": "c50e3d8c82f89a880019-2afd333767aded58edf1", - "file": "mvp-integration.spec.ts", - "line": 419, - "column": 5 - }, - { - "title": "2.1 - Dashboard loads without errors", - "ok": true, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "msedge", - "projectName": "msedge", - "results": [ - { - "workerIndex": 52, - "parallelIndex": 0, - "status": "passed", - "duration": 1575, - "errors": [], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:35:20.335Z", - "annotations": [], - "attachments": [] - } - ], - "status": "expected" - } - ], - "id": "c50e3d8c82f89a880019-0afd2f9bfc66628e2259", - "file": "mvp-integration.spec.ts", - "line": 401, - "column": 5 - }, - { - "title": "2.2 - Navigation works", - "ok": true, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "msedge", - "projectName": "msedge", - "results": [ - { - "workerIndex": 52, - "parallelIndex": 0, - "status": "passed", - "duration": 885, - "errors": [], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:35:21.922Z", - "annotations": [], - "attachments": [] - } - ], - "status": "expected" - } - ], - "id": "c50e3d8c82f89a880019-e90783a79eeb61be1249", - "file": "mvp-integration.spec.ts", - "line": 419, - "column": 5 + "status": "expected" } - ] + ], + "id": "d748ac400d08b85935ef-32f56b95de6bdc3a9955", + "file": "auth.spec.ts", + "line": 107, + "column": 3 }, { - "title": "3. Tracks Management", - "file": "mvp-integration.spec.ts", - "line": 437, - "column": 8, - "specs": [ + "title": "should register a new user successfully", + "ok": true, + "tags": [], + "tests": [ { - "title": "3.1 - Tracks page loads", - "ok": false, - "tags": [], - "tests": [ + "timeout": 60000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "chromium", - "projectName": "chromium", - "results": [ + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 4110, + "errors": [], + "stdout": [ { - "workerIndex": 2, - "parallelIndex": 0, - "status": "timedOut", - "duration": 61808, - "error": { - "message": "\u001b[31mTest timeout of 60000ms exceeded while running \"beforeEach\" hook.\u001b[39m", - "stack": "\u001b[31mTest timeout of 60000ms exceeded while running \"beforeEach\" hook.\u001b[39m\n at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:439:10", - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 10, - "line": 439 - }, - "snippet": "\u001b[0m \u001b[90m 437 |\u001b[39m test\u001b[33m.\u001b[39mdescribe(\u001b[32m'3. Tracks Management'\u001b[39m\u001b[33m,\u001b[39m () \u001b[33m=>\u001b[39m {\n \u001b[90m 438 |\u001b[39m \n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 439 |\u001b[39m test\u001b[33m.\u001b[39mbeforeEach(\u001b[36masync\u001b[39m ({ page }) \u001b[33m=>\u001b[39m {\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 440 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mgoto(\u001b[32m`${TEST_CONFIG.FRONTEND_URL}/login`\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 441 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"email\"], input[name=\"email\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39memail)\u001b[33m;\u001b[39m\n \u001b[90m 442 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"password\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39mpassword)\u001b[33m;\u001b[39m\u001b[0m" - }, - "errors": [ - { - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 10, - "line": 439 - }, - "message": "\u001b[31mTest timeout of 60000ms exceeded while running \"beforeEach\" hook.\u001b[39m\n\n\u001b[0m \u001b[90m 437 |\u001b[39m test\u001b[33m.\u001b[39mdescribe(\u001b[32m'3. Tracks Management'\u001b[39m\u001b[33m,\u001b[39m () \u001b[33m=>\u001b[39m {\n \u001b[90m 438 |\u001b[39m \n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 439 |\u001b[39m test\u001b[33m.\u001b[39mbeforeEach(\u001b[36masync\u001b[39m ({ page }) \u001b[33m=>\u001b[39m {\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 440 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mgoto(\u001b[32m`${TEST_CONFIG.FRONTEND_URL}/login`\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 441 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"email\"], input[name=\"email\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39memail)\u001b[33m;\u001b[39m\n \u001b[90m 442 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"password\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39mpassword)\u001b[33m;\u001b[39m\u001b[0m\n\u001b[2m at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:439:10\u001b[22m" - }, - { - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 18, - "line": 441 - }, - "message": "Error: page.fill: Test timeout of 60000ms exceeded.\nCall log:\n\u001b[2m - waiting for locator('input[type=\"email\"], input[name=\"email\"]')\u001b[22m\n\n\n\u001b[0m \u001b[90m 439 |\u001b[39m test\u001b[33m.\u001b[39mbeforeEach(\u001b[36masync\u001b[39m ({ page }) \u001b[33m=>\u001b[39m {\n \u001b[90m 440 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mgoto(\u001b[32m`${TEST_CONFIG.FRONTEND_URL}/login`\u001b[39m)\u001b[33m;\u001b[39m\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 441 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"email\"], input[name=\"email\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39memail)\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 442 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"password\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39mpassword)\u001b[33m;\u001b[39m\n \u001b[90m 443 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mclick(\u001b[32m'button[type=\"submit\"]'\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 444 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mwaitForURL(\u001b[35m/\\/(dashboard|home|app)/\u001b[39m\u001b[33m,\u001b[39m { timeout\u001b[33m:\u001b[39m \u001b[35m15000\u001b[39m })\u001b[33m;\u001b[39m\u001b[0m\n\u001b[2m at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:441:18\u001b[22m" - } - ], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:20:25.924Z", - "annotations": [], - "attachments": [ - { - "name": "screenshot", - "contentType": "image/png", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-e5cde-ent-3-1---Tracks-page-loads-chromium/test-failed-1.png" - }, - { - "name": "video", - "contentType": "video/webm", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-e5cde-ent-3-1---Tracks-page-loads-chromium/video.webm" - }, - { - "name": "error-context", - "contentType": "text/markdown", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-e5cde-ent-3-1---Tracks-page-loads-chromium/error-context.md" - } - ], - "errorLocation": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 10, - "line": 439 - } + "text": "🧪 [AUTH TEST] Running: User registration\n" + }, + { + "text": "✏️ [FILL] Filling field input[name=\"email\"], input#email with value: test-1768081828662@example.com\n" + }, + { + "text": "✅ [FILL] Field input[name=\"email\"], input#email filled successfully\n" + }, + { + "text": "✏️ [FILL] Filling field input[name=\"username\"], input#username with value: testuser1768081828662\n" + }, + { + "text": "✅ [FILL] Field input[name=\"username\"], input#username filled successfully\n" + }, + { + "text": "✏️ [FILL] Filling field input[name=\"password\"], input#password with value: Str0ng!P@ssw0rd2024\n" + }, + { + "text": "✅ [FILL] Field input[name=\"password\"], input#password filled successfully\n" + }, + { + "text": "✏️ [FILL] Filling field input[name=\"passwordConfirm\"], input[name=\"password_confirm\"], input[name=\"confirmPassword\"], input#passwordConfirm with value: Str0ng!P@ssw0rd2024\n" + }, + { + "text": "✅ [FILL] Field input[name=\"passwordConfirm\"], input[name=\"password_confirm\"], input[name=\"confirmPassword\"], input#passwordConfirm filled successfully\n" + }, + { + "text": "⚡ [FORM SUBMIT] Forcing submission of form: form\n" + }, + { + "text": "🔍 [FORM SUBMIT] Waiting for form selector: form\n" + }, + { + "text": "⏳ [FORM SUBMIT] Waiting for React to update state...\n" + }, + { + "text": "🚀 [FORM SUBMIT] Submitting form...\n" + }, + { + "text": "✅ [FORM SUBMIT] Form form submitted successfully\n" + }, + { + "text": "✅ [AUTH TEST] Registration successful with auto-login\n" + }, + { + "text": "\n📊 [AUTH TEST] === Final Verifications ===\n" + }, + { + "text": "✅ [AUTH TEST] No console errors\n" + }, + { + "text": "✅ [AUTH TEST] No network errors\n" } ], - "status": "unexpected" - } - ], - "id": "c50e3d8c82f89a880019-cf119e5245e80b82032c", - "file": "mvp-integration.spec.ts", - "line": 447, - "column": 5 - }, - { - "title": "3.2 - Upload track button exists", - "ok": false, - "tags": [], - "tests": [ - { - "timeout": 60000, + "stderr": [], + "retry": 0, + "startTime": "2026-01-10T21:50:27.127Z", "annotations": [], - "expectedStatus": "passed", - "projectId": "chromium", - "projectName": "chromium", - "results": [ - { - "workerIndex": 3, - "parallelIndex": 0, - "status": "timedOut", - "duration": 61642, - "error": { - "message": "\u001b[31mTest timeout of 60000ms exceeded while running \"beforeEach\" hook.\u001b[39m", - "stack": "\u001b[31mTest timeout of 60000ms exceeded while running \"beforeEach\" hook.\u001b[39m\n at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:439:10", - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 10, - "line": 439 - }, - "snippet": "\u001b[0m \u001b[90m 437 |\u001b[39m test\u001b[33m.\u001b[39mdescribe(\u001b[32m'3. Tracks Management'\u001b[39m\u001b[33m,\u001b[39m () \u001b[33m=>\u001b[39m {\n \u001b[90m 438 |\u001b[39m \n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 439 |\u001b[39m test\u001b[33m.\u001b[39mbeforeEach(\u001b[36masync\u001b[39m ({ page }) \u001b[33m=>\u001b[39m {\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 440 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mgoto(\u001b[32m`${TEST_CONFIG.FRONTEND_URL}/login`\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 441 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"email\"], input[name=\"email\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39memail)\u001b[33m;\u001b[39m\n \u001b[90m 442 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"password\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39mpassword)\u001b[33m;\u001b[39m\u001b[0m" - }, - "errors": [ - { - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 10, - "line": 439 - }, - "message": "\u001b[31mTest timeout of 60000ms exceeded while running \"beforeEach\" hook.\u001b[39m\n\n\u001b[0m \u001b[90m 437 |\u001b[39m test\u001b[33m.\u001b[39mdescribe(\u001b[32m'3. Tracks Management'\u001b[39m\u001b[33m,\u001b[39m () \u001b[33m=>\u001b[39m {\n \u001b[90m 438 |\u001b[39m \n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 439 |\u001b[39m test\u001b[33m.\u001b[39mbeforeEach(\u001b[36masync\u001b[39m ({ page }) \u001b[33m=>\u001b[39m {\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 440 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mgoto(\u001b[32m`${TEST_CONFIG.FRONTEND_URL}/login`\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 441 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"email\"], input[name=\"email\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39memail)\u001b[33m;\u001b[39m\n \u001b[90m 442 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"password\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39mpassword)\u001b[33m;\u001b[39m\u001b[0m\n\u001b[2m at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:439:10\u001b[22m" - }, - { - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 18, - "line": 441 - }, - "message": "Error: page.fill: Test timeout of 60000ms exceeded.\nCall log:\n\u001b[2m - waiting for locator('input[type=\"email\"], input[name=\"email\"]')\u001b[22m\n\n\n\u001b[0m \u001b[90m 439 |\u001b[39m test\u001b[33m.\u001b[39mbeforeEach(\u001b[36masync\u001b[39m ({ page }) \u001b[33m=>\u001b[39m {\n \u001b[90m 440 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mgoto(\u001b[32m`${TEST_CONFIG.FRONTEND_URL}/login`\u001b[39m)\u001b[33m;\u001b[39m\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 441 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"email\"], input[name=\"email\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39memail)\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 442 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"password\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39mpassword)\u001b[33m;\u001b[39m\n \u001b[90m 443 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mclick(\u001b[32m'button[type=\"submit\"]'\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 444 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mwaitForURL(\u001b[35m/\\/(dashboard|home|app)/\u001b[39m\u001b[33m,\u001b[39m { timeout\u001b[33m:\u001b[39m \u001b[35m15000\u001b[39m })\u001b[33m;\u001b[39m\u001b[0m\n\u001b[2m at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:441:18\u001b[22m" - } - ], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:21:28.519Z", - "annotations": [], - "attachments": [ - { - "name": "screenshot", - "contentType": "image/png", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-4a867--Upload-track-button-exists-chromium/test-failed-1.png" - }, - { - "name": "video", - "contentType": "video/webm", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-4a867--Upload-track-button-exists-chromium/video.webm" - }, - { - "name": "error-context", - "contentType": "text/markdown", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-4a867--Upload-track-button-exists-chromium/error-context.md" - } - ], - "errorLocation": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 10, - "line": 439 - } - } - ], - "status": "unexpected" + "attachments": [] } ], - "id": "c50e3d8c82f89a880019-79a9764b76ef6081d40e", - "file": "mvp-integration.spec.ts", - "line": 456, - "column": 5 - }, - { - "title": "3.1 - Tracks page loads", - "ok": false, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "firefox", - "projectName": "firefox", - "results": [ - { - "workerIndex": 17, - "parallelIndex": 0, - "status": "timedOut", - "duration": 60092, - "error": { - "message": "\u001b[31mTest timeout of 60000ms exceeded while running \"beforeEach\" hook.\u001b[39m", - "stack": "\u001b[31mTest timeout of 60000ms exceeded while running \"beforeEach\" hook.\u001b[39m\n at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:439:10", - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 10, - "line": 439 - }, - "snippet": "\u001b[0m \u001b[90m 437 |\u001b[39m test\u001b[33m.\u001b[39mdescribe(\u001b[32m'3. Tracks Management'\u001b[39m\u001b[33m,\u001b[39m () \u001b[33m=>\u001b[39m {\n \u001b[90m 438 |\u001b[39m \n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 439 |\u001b[39m test\u001b[33m.\u001b[39mbeforeEach(\u001b[36masync\u001b[39m ({ page }) \u001b[33m=>\u001b[39m {\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 440 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mgoto(\u001b[32m`${TEST_CONFIG.FRONTEND_URL}/login`\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 441 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"email\"], input[name=\"email\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39memail)\u001b[33m;\u001b[39m\n \u001b[90m 442 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"password\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39mpassword)\u001b[33m;\u001b[39m\u001b[0m" - }, - "errors": [ - { - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 10, - "line": 439 - }, - "message": "\u001b[31mTest timeout of 60000ms exceeded while running \"beforeEach\" hook.\u001b[39m\n\n\u001b[0m \u001b[90m 437 |\u001b[39m test\u001b[33m.\u001b[39mdescribe(\u001b[32m'3. Tracks Management'\u001b[39m\u001b[33m,\u001b[39m () \u001b[33m=>\u001b[39m {\n \u001b[90m 438 |\u001b[39m \n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 439 |\u001b[39m test\u001b[33m.\u001b[39mbeforeEach(\u001b[36masync\u001b[39m ({ page }) \u001b[33m=>\u001b[39m {\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 440 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mgoto(\u001b[32m`${TEST_CONFIG.FRONTEND_URL}/login`\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 441 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"email\"], input[name=\"email\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39memail)\u001b[33m;\u001b[39m\n \u001b[90m 442 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"password\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39mpassword)\u001b[33m;\u001b[39m\u001b[0m\n\u001b[2m at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:439:10\u001b[22m" - }, - { - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 18, - "line": 441 - }, - "message": "Error: page.fill: Test timeout of 60000ms exceeded.\nCall log:\n\u001b[2m - waiting for locator('input[type=\"email\"], input[name=\"email\"]')\u001b[22m\n\n\n\u001b[0m \u001b[90m 439 |\u001b[39m test\u001b[33m.\u001b[39mbeforeEach(\u001b[36masync\u001b[39m ({ page }) \u001b[33m=>\u001b[39m {\n \u001b[90m 440 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mgoto(\u001b[32m`${TEST_CONFIG.FRONTEND_URL}/login`\u001b[39m)\u001b[33m;\u001b[39m\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 441 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"email\"], input[name=\"email\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39memail)\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 442 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"password\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39mpassword)\u001b[33m;\u001b[39m\n \u001b[90m 443 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mclick(\u001b[32m'button[type=\"submit\"]'\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 444 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mwaitForURL(\u001b[35m/\\/(dashboard|home|app)/\u001b[39m\u001b[33m,\u001b[39m { timeout\u001b[33m:\u001b[39m \u001b[35m15000\u001b[39m })\u001b[33m;\u001b[39m\u001b[0m\n\u001b[2m at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:441:18\u001b[22m" - } - ], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:32:06.330Z", - "annotations": [], - "attachments": [ - { - "name": "screenshot", - "contentType": "image/png", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-e5cde-ent-3-1---Tracks-page-loads-firefox/test-failed-1.png" - }, - { - "name": "video", - "contentType": "video/webm", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-e5cde-ent-3-1---Tracks-page-loads-firefox/video.webm" - }, - { - "name": "error-context", - "contentType": "text/markdown", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-e5cde-ent-3-1---Tracks-page-loads-firefox/error-context.md" - } - ], - "errorLocation": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 10, - "line": 439 - } - } - ], - "status": "unexpected" - } - ], - "id": "c50e3d8c82f89a880019-4729c0ac91d3d0730bdc", - "file": "mvp-integration.spec.ts", - "line": 447, - "column": 5 - }, - { - "title": "3.2 - Upload track button exists", - "ok": false, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "firefox", - "projectName": "firefox", - "results": [ - { - "workerIndex": 18, - "parallelIndex": 0, - "status": "failed", - "duration": 3468, - "error": { - "message": "Error: page.waitForURL: NS_ERROR_FAILURE\n=========================== logs ===========================\nwaiting for navigation until \"load\"\n============================================================", - "stack": "Error: page.waitForURL: NS_ERROR_FAILURE\n=========================== logs ===========================\nwaiting for navigation until \"load\"\n============================================================\n at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:444:18", - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 18, - "line": 444 - }, - "snippet": "\u001b[0m \u001b[90m 442 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"password\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39mpassword)\u001b[33m;\u001b[39m\n \u001b[90m 443 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mclick(\u001b[32m'button[type=\"submit\"]'\u001b[39m)\u001b[33m;\u001b[39m\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 444 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mwaitForURL(\u001b[35m/\\/(dashboard|home|app)/\u001b[39m\u001b[33m,\u001b[39m { timeout\u001b[33m:\u001b[39m \u001b[35m15000\u001b[39m })\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 445 |\u001b[39m })\u001b[33m;\u001b[39m\n \u001b[90m 446 |\u001b[39m\n \u001b[90m 447 |\u001b[39m test(\u001b[32m'3.1 - Tracks page loads'\u001b[39m\u001b[33m,\u001b[39m \u001b[36masync\u001b[39m ({ page }) \u001b[33m=>\u001b[39m {\u001b[0m" - }, - "errors": [ - { - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 18, - "line": 444 - }, - "message": "Error: page.waitForURL: NS_ERROR_FAILURE\n=========================== logs ===========================\nwaiting for navigation until \"load\"\n============================================================\n\n\u001b[0m \u001b[90m 442 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"password\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39mpassword)\u001b[33m;\u001b[39m\n \u001b[90m 443 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mclick(\u001b[32m'button[type=\"submit\"]'\u001b[39m)\u001b[33m;\u001b[39m\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 444 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mwaitForURL(\u001b[35m/\\/(dashboard|home|app)/\u001b[39m\u001b[33m,\u001b[39m { timeout\u001b[33m:\u001b[39m \u001b[35m15000\u001b[39m })\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 445 |\u001b[39m })\u001b[33m;\u001b[39m\n \u001b[90m 446 |\u001b[39m\n \u001b[90m 447 |\u001b[39m test(\u001b[32m'3.1 - Tracks page loads'\u001b[39m\u001b[33m,\u001b[39m \u001b[36masync\u001b[39m ({ page }) \u001b[33m=>\u001b[39m {\u001b[0m\n\u001b[2m at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:444:18\u001b[22m" - } - ], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:33:07.711Z", - "annotations": [], - "attachments": [ - { - "name": "screenshot", - "contentType": "image/png", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-4a867--Upload-track-button-exists-firefox/test-failed-1.png" - }, - { - "name": "video", - "contentType": "video/webm", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-4a867--Upload-track-button-exists-firefox/video.webm" - }, - { - "name": "error-context", - "contentType": "text/markdown", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-4a867--Upload-track-button-exists-firefox/error-context.md" - } - ], - "errorLocation": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 18, - "line": 444 - } - } - ], - "status": "unexpected" - } - ], - "id": "c50e3d8c82f89a880019-2d872de8ba5405d124f2", - "file": "mvp-integration.spec.ts", - "line": 456, - "column": 5 - }, - { - "title": "3.1 - Tracks page loads", - "ok": false, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "webkit", - "projectName": "webkit", - "results": [ - { - "workerIndex": 38, - "parallelIndex": 0, - "status": "failed", - "duration": 2, - "error": { - "message": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝", - "stack": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝" - }, - "errors": [ - { - "message": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝" - } - ], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:35:02.271Z", - "annotations": [], - "attachments": [] - } - ], - "status": "unexpected" - } - ], - "id": "c50e3d8c82f89a880019-8064dca9c6f4a1c37a7e", - "file": "mvp-integration.spec.ts", - "line": 447, - "column": 5 - }, - { - "title": "3.2 - Upload track button exists", - "ok": false, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "webkit", - "projectName": "webkit", - "results": [ - { - "workerIndex": 39, - "parallelIndex": 0, - "status": "failed", - "duration": 3, - "error": { - "message": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝", - "stack": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝" - }, - "errors": [ - { - "message": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝" - } - ], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:35:02.989Z", - "annotations": [], - "attachments": [] - } - ], - "status": "unexpected" - } - ], - "id": "c50e3d8c82f89a880019-7c650d1363e839d59f49", - "file": "mvp-integration.spec.ts", - "line": 456, - "column": 5 - }, - { - "title": "3.1 - Tracks page loads", - "ok": true, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "msedge", - "projectName": "msedge", - "results": [ - { - "workerIndex": 52, - "parallelIndex": 0, - "status": "passed", - "duration": 1788, - "errors": [], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:35:22.825Z", - "annotations": [], - "attachments": [] - } - ], - "status": "expected" - } - ], - "id": "c50e3d8c82f89a880019-8fe529064069235e399b", - "file": "mvp-integration.spec.ts", - "line": 447, - "column": 5 - }, - { - "title": "3.2 - Upload track button exists", - "ok": true, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "msedge", - "projectName": "msedge", - "results": [ - { - "workerIndex": 52, - "parallelIndex": 0, - "status": "passed", - "duration": 1136, - "errors": [], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:35:24.625Z", - "annotations": [], - "attachments": [] - } - ], - "status": "expected" - } - ], - "id": "c50e3d8c82f89a880019-f4ae9be6e62f6d783905", - "file": "mvp-integration.spec.ts", - "line": 456, - "column": 5 + "status": "expected" } - ] + ], + "id": "d748ac400d08b85935ef-c1f7fb06f3779f5e6fd5", + "file": "auth.spec.ts", + "line": 134, + "column": 3 }, { - "title": "4. Playlists Management", - "file": "mvp-integration.spec.ts", - "line": 464, - "column": 8, - "specs": [ + "title": "should show error when registering with existing email", + "ok": true, + "tags": [], + "tests": [ { - "title": "4.1 - Playlists page loads", - "ok": false, - "tags": [], - "tests": [ + "timeout": 60000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "chromium", - "projectName": "chromium", - "results": [ + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 5254, + "errors": [], + "stdout": [ { - "workerIndex": 4, - "parallelIndex": 0, - "status": "timedOut", - "duration": 61861, - "error": { - "message": "\u001b[31mTest timeout of 60000ms exceeded while running \"beforeEach\" hook.\u001b[39m", - "stack": "\u001b[31mTest timeout of 60000ms exceeded while running \"beforeEach\" hook.\u001b[39m\n at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:466:10", - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 10, - "line": 466 - }, - "snippet": "\u001b[0m \u001b[90m 464 |\u001b[39m test\u001b[33m.\u001b[39mdescribe(\u001b[32m'4. Playlists Management'\u001b[39m\u001b[33m,\u001b[39m () \u001b[33m=>\u001b[39m {\n \u001b[90m 465 |\u001b[39m \n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 466 |\u001b[39m test\u001b[33m.\u001b[39mbeforeEach(\u001b[36masync\u001b[39m ({ page }) \u001b[33m=>\u001b[39m {\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 467 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mgoto(\u001b[32m`${TEST_CONFIG.FRONTEND_URL}/login`\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 468 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"email\"], input[name=\"email\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39memail)\u001b[33m;\u001b[39m\n \u001b[90m 469 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"password\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39mpassword)\u001b[33m;\u001b[39m\u001b[0m" - }, - "errors": [ - { - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 10, - "line": 466 - }, - "message": "\u001b[31mTest timeout of 60000ms exceeded while running \"beforeEach\" hook.\u001b[39m\n\n\u001b[0m \u001b[90m 464 |\u001b[39m test\u001b[33m.\u001b[39mdescribe(\u001b[32m'4. Playlists Management'\u001b[39m\u001b[33m,\u001b[39m () \u001b[33m=>\u001b[39m {\n \u001b[90m 465 |\u001b[39m \n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 466 |\u001b[39m test\u001b[33m.\u001b[39mbeforeEach(\u001b[36masync\u001b[39m ({ page }) \u001b[33m=>\u001b[39m {\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 467 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mgoto(\u001b[32m`${TEST_CONFIG.FRONTEND_URL}/login`\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 468 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"email\"], input[name=\"email\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39memail)\u001b[33m;\u001b[39m\n \u001b[90m 469 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"password\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39mpassword)\u001b[33m;\u001b[39m\u001b[0m\n\u001b[2m at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:466:10\u001b[22m" - }, - { - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 18, - "line": 468 - }, - "message": "Error: page.fill: Test timeout of 60000ms exceeded.\nCall log:\n\u001b[2m - waiting for locator('input[type=\"email\"], input[name=\"email\"]')\u001b[22m\n\n\n\u001b[0m \u001b[90m 466 |\u001b[39m test\u001b[33m.\u001b[39mbeforeEach(\u001b[36masync\u001b[39m ({ page }) \u001b[33m=>\u001b[39m {\n \u001b[90m 467 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mgoto(\u001b[32m`${TEST_CONFIG.FRONTEND_URL}/login`\u001b[39m)\u001b[33m;\u001b[39m\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 468 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"email\"], input[name=\"email\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39memail)\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 469 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"password\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39mpassword)\u001b[33m;\u001b[39m\n \u001b[90m 470 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mclick(\u001b[32m'button[type=\"submit\"]'\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 471 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mwaitForURL(\u001b[35m/\\/(dashboard|home|app)/\u001b[39m\u001b[33m,\u001b[39m { timeout\u001b[33m:\u001b[39m \u001b[35m15000\u001b[39m })\u001b[33m;\u001b[39m\u001b[0m\n\u001b[2m at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:468:18\u001b[22m" - } - ], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:22:30.887Z", - "annotations": [], - "attachments": [ - { - "name": "screenshot", - "contentType": "image/png", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-9c1ae--4-1---Playlists-page-loads-chromium/test-failed-1.png" - }, - { - "name": "video", - "contentType": "video/webm", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-9c1ae--4-1---Playlists-page-loads-chromium/video.webm" - }, - { - "name": "error-context", - "contentType": "text/markdown", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-9c1ae--4-1---Playlists-page-loads-chromium/error-context.md" - } - ], - "errorLocation": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 10, - "line": 466 - } + "text": "🧪 [AUTH TEST] Running: Registration with existing email\n" + }, + { + "text": "✏️ [FILL] Filling field input[name=\"email\"], input#email with value: e2e@test.com\n" + }, + { + "text": "✅ [FILL] Field input[name=\"email\"], input#email filled successfully\n" + }, + { + "text": "✏️ [FILL] Filling field input[name=\"username\"], input#username with value: existinguser\n" + }, + { + "text": "✅ [FILL] Field input[name=\"username\"], input#username filled successfully\n" + }, + { + "text": "✏️ [FILL] Filling field input[name=\"password\"], input#password with value: Str0ng!P@ssw0rd2024\n" + }, + { + "text": "✅ [FILL] Field input[name=\"password\"], input#password filled successfully\n" + }, + { + "text": "✏️ [FILL] Filling field input[name=\"passwordConfirm\"], input[name=\"password_confirm\"], input[name=\"confirmPassword\"], input#passwordConfirm with value: Str0ng!P@ssw0rd2024\n" + }, + { + "text": "✅ [FILL] Field input[name=\"passwordConfirm\"], input[name=\"password_confirm\"], input[name=\"confirmPassword\"], input#passwordConfirm filled successfully\n" + }, + { + "text": "⚡ [FORM SUBMIT] Forcing submission of form: form\n" + }, + { + "text": "🔍 [FORM SUBMIT] Waiting for form selector: form\n" + }, + { + "text": "⏳ [FORM SUBMIT] Waiting for React to update state...\n" + }, + { + "text": "🚀 [FORM SUBMIT] Submitting form...\n" + }, + { + "text": "✅ [FORM SUBMIT] Form form submitted successfully\n" + }, + { + "text": "🔴 [NETWORK ERROR] POST http://127.0.0.1:8080/api/v1/auth/register: 409\n" + }, + { + "text": "🔴 [CONSOLE ERROR] Failed to load resource: the server responded with a status of 409 (Conflict)\n" + }, + { + "text": "✅ [AUTH TEST] Error shown for existing email: \"User already exists\"\n" + }, + { + "text": "\n📊 [AUTH TEST] === Final Verifications ===\n" + }, + { + "text": "🔴 [AUTH TEST] Console errors (1):\n" + }, + { + "text": " - Failed to load resource: the server responded with a status of 409 (Conflict)\n" + }, + { + "text": "🔴 [AUTH TEST] Network errors (1):\n" + }, + { + "text": " - POST http://127.0.0.1:8080/api/v1/auth/register: 409\n" } ], - "status": "unexpected" - } - ], - "id": "c50e3d8c82f89a880019-bae20f1fec0663fb4e90", - "file": "mvp-integration.spec.ts", - "line": 474, - "column": 5 - }, - { - "title": "4.2 - Can create playlist", - "ok": false, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "chromium", - "projectName": "chromium", - "results": [ + "stderr": [ { - "workerIndex": 5, - "parallelIndex": 0, - "status": "timedOut", - "duration": 61675, - "error": { - "message": "\u001b[31mTest timeout of 60000ms exceeded while running \"beforeEach\" hook.\u001b[39m", - "stack": "\u001b[31mTest timeout of 60000ms exceeded while running \"beforeEach\" hook.\u001b[39m\n at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:466:10", - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 10, - "line": 466 - }, - "snippet": "\u001b[0m \u001b[90m 464 |\u001b[39m test\u001b[33m.\u001b[39mdescribe(\u001b[32m'4. Playlists Management'\u001b[39m\u001b[33m,\u001b[39m () \u001b[33m=>\u001b[39m {\n \u001b[90m 465 |\u001b[39m \n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 466 |\u001b[39m test\u001b[33m.\u001b[39mbeforeEach(\u001b[36masync\u001b[39m ({ page }) \u001b[33m=>\u001b[39m {\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 467 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mgoto(\u001b[32m`${TEST_CONFIG.FRONTEND_URL}/login`\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 468 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"email\"], input[name=\"email\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39memail)\u001b[33m;\u001b[39m\n \u001b[90m 469 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"password\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39mpassword)\u001b[33m;\u001b[39m\u001b[0m" - }, - "errors": [ - { - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 10, - "line": 466 - }, - "message": "\u001b[31mTest timeout of 60000ms exceeded while running \"beforeEach\" hook.\u001b[39m\n\n\u001b[0m \u001b[90m 464 |\u001b[39m test\u001b[33m.\u001b[39mdescribe(\u001b[32m'4. Playlists Management'\u001b[39m\u001b[33m,\u001b[39m () \u001b[33m=>\u001b[39m {\n \u001b[90m 465 |\u001b[39m \n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 466 |\u001b[39m test\u001b[33m.\u001b[39mbeforeEach(\u001b[36masync\u001b[39m ({ page }) \u001b[33m=>\u001b[39m {\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 467 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mgoto(\u001b[32m`${TEST_CONFIG.FRONTEND_URL}/login`\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 468 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"email\"], input[name=\"email\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39memail)\u001b[33m;\u001b[39m\n \u001b[90m 469 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"password\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39mpassword)\u001b[33m;\u001b[39m\u001b[0m\n\u001b[2m at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:466:10\u001b[22m" - }, - { - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 18, - "line": 468 - }, - "message": "Error: page.fill: Test timeout of 60000ms exceeded.\nCall log:\n\u001b[2m - waiting for locator('input[type=\"email\"], input[name=\"email\"]')\u001b[22m\n\n\n\u001b[0m \u001b[90m 466 |\u001b[39m test\u001b[33m.\u001b[39mbeforeEach(\u001b[36masync\u001b[39m ({ page }) \u001b[33m=>\u001b[39m {\n \u001b[90m 467 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mgoto(\u001b[32m`${TEST_CONFIG.FRONTEND_URL}/login`\u001b[39m)\u001b[33m;\u001b[39m\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 468 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"email\"], input[name=\"email\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39memail)\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 469 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"password\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39mpassword)\u001b[33m;\u001b[39m\n \u001b[90m 470 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mclick(\u001b[32m'button[type=\"submit\"]'\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 471 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mwaitForURL(\u001b[35m/\\/(dashboard|home|app)/\u001b[39m\u001b[33m,\u001b[39m { timeout\u001b[33m:\u001b[39m \u001b[35m15000\u001b[39m })\u001b[33m;\u001b[39m\u001b[0m\n\u001b[2m at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:468:18\u001b[22m" - } - ], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:23:33.399Z", - "annotations": [], - "attachments": [ - { - "name": "screenshot", - "contentType": "image/png", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-78e7e-t-4-2---Can-create-playlist-chromium/test-failed-1.png" - }, - { - "name": "video", - "contentType": "video/webm", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-78e7e-t-4-2---Can-create-playlist-chromium/video.webm" - }, - { - "name": "error-context", - "contentType": "text/markdown", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-78e7e-t-4-2---Can-create-playlist-chromium/error-context.md" - } - ], - "errorLocation": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 10, - "line": 466 - } + "text": "⚠️ [AUTH TEST] Test passed but had console errors\n" } ], - "status": "unexpected" - } - ], - "id": "c50e3d8c82f89a880019-2591999106c62064a288", - "file": "mvp-integration.spec.ts", - "line": 479, - "column": 5 - }, - { - "title": "4.1 - Playlists page loads", - "ok": false, - "tags": [], - "tests": [ - { - "timeout": 60000, + "retry": 0, + "startTime": "2026-01-10T21:50:31.266Z", "annotations": [], - "expectedStatus": "passed", - "projectId": "firefox", - "projectName": "firefox", - "results": [ - { - "workerIndex": 19, - "parallelIndex": 0, - "status": "failed", - "duration": 16628, - "error": { - "message": "TimeoutError: page.waitForURL: Timeout 15000ms exceeded.\n=========================== logs ===========================\nwaiting for navigation until \"load\"\n navigated to \"http://localhost:5173/login\"\n navigated to \"http://localhost:5173/login\"\n============================================================", - "stack": "TimeoutError: page.waitForURL: Timeout 15000ms exceeded.\n=========================== logs ===========================\nwaiting for navigation until \"load\"\n navigated to \"http://localhost:5173/login\"\n navigated to \"http://localhost:5173/login\"\n============================================================\n at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:471:18", - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 18, - "line": 471 - }, - "snippet": "\u001b[0m \u001b[90m 469 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"password\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39mpassword)\u001b[33m;\u001b[39m\n \u001b[90m 470 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mclick(\u001b[32m'button[type=\"submit\"]'\u001b[39m)\u001b[33m;\u001b[39m\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 471 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mwaitForURL(\u001b[35m/\\/(dashboard|home|app)/\u001b[39m\u001b[33m,\u001b[39m { timeout\u001b[33m:\u001b[39m \u001b[35m15000\u001b[39m })\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 472 |\u001b[39m })\u001b[33m;\u001b[39m\n \u001b[90m 473 |\u001b[39m\n \u001b[90m 474 |\u001b[39m test(\u001b[32m'4.1 - Playlists page loads'\u001b[39m\u001b[33m,\u001b[39m \u001b[36masync\u001b[39m ({ page }) \u001b[33m=>\u001b[39m {\u001b[0m" - }, - "errors": [ - { - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 18, - "line": 471 - }, - "message": "TimeoutError: page.waitForURL: Timeout 15000ms exceeded.\n=========================== logs ===========================\nwaiting for navigation until \"load\"\n navigated to \"http://localhost:5173/login\"\n navigated to \"http://localhost:5173/login\"\n============================================================\n\n\u001b[0m \u001b[90m 469 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"password\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39mpassword)\u001b[33m;\u001b[39m\n \u001b[90m 470 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mclick(\u001b[32m'button[type=\"submit\"]'\u001b[39m)\u001b[33m;\u001b[39m\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 471 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mwaitForURL(\u001b[35m/\\/(dashboard|home|app)/\u001b[39m\u001b[33m,\u001b[39m { timeout\u001b[33m:\u001b[39m \u001b[35m15000\u001b[39m })\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 472 |\u001b[39m })\u001b[33m;\u001b[39m\n \u001b[90m 473 |\u001b[39m\n \u001b[90m 474 |\u001b[39m test(\u001b[32m'4.1 - Playlists page loads'\u001b[39m\u001b[33m,\u001b[39m \u001b[36masync\u001b[39m ({ page }) \u001b[33m=>\u001b[39m {\u001b[0m\n\u001b[2m at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:471:18\u001b[22m" - } - ], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:33:12.459Z", - "annotations": [], - "attachments": [ - { - "name": "screenshot", - "contentType": "image/png", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-9c1ae--4-1---Playlists-page-loads-firefox/test-failed-1.png" - }, - { - "name": "video", - "contentType": "video/webm", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-9c1ae--4-1---Playlists-page-loads-firefox/video.webm" - }, - { - "name": "error-context", - "contentType": "text/markdown", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-9c1ae--4-1---Playlists-page-loads-firefox/error-context.md" - } - ], - "errorLocation": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 18, - "line": 471 - } - } - ], - "status": "unexpected" + "attachments": [] } ], - "id": "c50e3d8c82f89a880019-0aa518fc93e3e3eee8d0", - "file": "mvp-integration.spec.ts", - "line": 474, - "column": 5 - }, - { - "title": "4.2 - Can create playlist", - "ok": false, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "firefox", - "projectName": "firefox", - "results": [ - { - "workerIndex": 20, - "parallelIndex": 0, - "status": "failed", - "duration": 16737, - "error": { - "message": "TimeoutError: page.waitForURL: Timeout 15000ms exceeded.\n=========================== logs ===========================\nwaiting for navigation until \"load\"\n navigated to \"http://localhost:5173/login\"\n navigated to \"http://localhost:5173/login\"\n============================================================", - "stack": "TimeoutError: page.waitForURL: Timeout 15000ms exceeded.\n=========================== logs ===========================\nwaiting for navigation until \"load\"\n navigated to \"http://localhost:5173/login\"\n navigated to \"http://localhost:5173/login\"\n============================================================\n at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:471:18", - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 18, - "line": 471 - }, - "snippet": "\u001b[0m \u001b[90m 469 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"password\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39mpassword)\u001b[33m;\u001b[39m\n \u001b[90m 470 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mclick(\u001b[32m'button[type=\"submit\"]'\u001b[39m)\u001b[33m;\u001b[39m\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 471 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mwaitForURL(\u001b[35m/\\/(dashboard|home|app)/\u001b[39m\u001b[33m,\u001b[39m { timeout\u001b[33m:\u001b[39m \u001b[35m15000\u001b[39m })\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 472 |\u001b[39m })\u001b[33m;\u001b[39m\n \u001b[90m 473 |\u001b[39m\n \u001b[90m 474 |\u001b[39m test(\u001b[32m'4.1 - Playlists page loads'\u001b[39m\u001b[33m,\u001b[39m \u001b[36masync\u001b[39m ({ page }) \u001b[33m=>\u001b[39m {\u001b[0m" - }, - "errors": [ - { - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 18, - "line": 471 - }, - "message": "TimeoutError: page.waitForURL: Timeout 15000ms exceeded.\n=========================== logs ===========================\nwaiting for navigation until \"load\"\n navigated to \"http://localhost:5173/login\"\n navigated to \"http://localhost:5173/login\"\n============================================================\n\n\u001b[0m \u001b[90m 469 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"password\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39mpassword)\u001b[33m;\u001b[39m\n \u001b[90m 470 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mclick(\u001b[32m'button[type=\"submit\"]'\u001b[39m)\u001b[33m;\u001b[39m\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 471 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mwaitForURL(\u001b[35m/\\/(dashboard|home|app)/\u001b[39m\u001b[33m,\u001b[39m { timeout\u001b[33m:\u001b[39m \u001b[35m15000\u001b[39m })\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 472 |\u001b[39m })\u001b[33m;\u001b[39m\n \u001b[90m 473 |\u001b[39m\n \u001b[90m 474 |\u001b[39m test(\u001b[32m'4.1 - Playlists page loads'\u001b[39m\u001b[33m,\u001b[39m \u001b[36masync\u001b[39m ({ page }) \u001b[33m=>\u001b[39m {\u001b[0m\n\u001b[2m at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:471:18\u001b[22m" - } - ], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:33:30.333Z", - "annotations": [], - "attachments": [ - { - "name": "screenshot", - "contentType": "image/png", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-78e7e-t-4-2---Can-create-playlist-firefox/test-failed-1.png" - }, - { - "name": "video", - "contentType": "video/webm", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-78e7e-t-4-2---Can-create-playlist-firefox/video.webm" - }, - { - "name": "error-context", - "contentType": "text/markdown", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-78e7e-t-4-2---Can-create-playlist-firefox/error-context.md" - } - ], - "errorLocation": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 18, - "line": 471 - } - } - ], - "status": "unexpected" - } - ], - "id": "c50e3d8c82f89a880019-342ad3c14e1a09752050", - "file": "mvp-integration.spec.ts", - "line": 479, - "column": 5 - }, - { - "title": "4.1 - Playlists page loads", - "ok": false, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "webkit", - "projectName": "webkit", - "results": [ - { - "workerIndex": 40, - "parallelIndex": 0, - "status": "failed", - "duration": 3, - "error": { - "message": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝", - "stack": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝" - }, - "errors": [ - { - "message": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝" - } - ], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:35:03.794Z", - "annotations": [], - "attachments": [] - } - ], - "status": "unexpected" - } - ], - "id": "c50e3d8c82f89a880019-84b2f6fd39d550eb18c7", - "file": "mvp-integration.spec.ts", - "line": 474, - "column": 5 - }, - { - "title": "4.2 - Can create playlist", - "ok": false, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "webkit", - "projectName": "webkit", - "results": [ - { - "workerIndex": 41, - "parallelIndex": 0, - "status": "failed", - "duration": 2, - "error": { - "message": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝", - "stack": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝" - }, - "errors": [ - { - "message": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝" - } - ], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:35:04.546Z", - "annotations": [], - "attachments": [] - } - ], - "status": "unexpected" - } - ], - "id": "c50e3d8c82f89a880019-713f5ee13a35f9d28602", - "file": "mvp-integration.spec.ts", - "line": 479, - "column": 5 - }, - { - "title": "4.1 - Playlists page loads", - "ok": true, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "msedge", - "projectName": "msedge", - "results": [ - { - "workerIndex": 52, - "parallelIndex": 0, - "status": "passed", - "duration": 2351, - "errors": [], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:35:25.771Z", - "annotations": [], - "attachments": [] - } - ], - "status": "expected" - } - ], - "id": "c50e3d8c82f89a880019-ea58089a8320675da581", - "file": "mvp-integration.spec.ts", - "line": 474, - "column": 5 - }, - { - "title": "4.2 - Can create playlist", - "ok": true, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "msedge", - "projectName": "msedge", - "results": [ - { - "workerIndex": 52, - "parallelIndex": 0, - "status": "passed", - "duration": 1144, - "errors": [], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:35:28.132Z", - "annotations": [], - "attachments": [] - } - ], - "status": "expected" - } - ], - "id": "c50e3d8c82f89a880019-79c0173f3375b73e9b30", - "file": "mvp-integration.spec.ts", - "line": 479, - "column": 5 + "status": "expected" } - ] + ], + "id": "d748ac400d08b85935ef-53d5184b470bb63aefc3", + "file": "auth.spec.ts", + "line": 231, + "column": 3 }, { - "title": "5. Profile Management", - "file": "mvp-integration.spec.ts", - "line": 499, - "column": 8, - "specs": [ + "title": "should logout successfully", + "ok": true, + "tags": [], + "tests": [ { - "title": "5.1 - Profile page loads", - "ok": false, - "tags": [], - "tests": [ + "timeout": 60000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "chromium", - "projectName": "chromium", - "results": [ + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 8189, + "errors": [], + "stdout": [ { - "workerIndex": 6, - "parallelIndex": 0, - "status": "timedOut", - "duration": 61962, - "error": { - "message": "\u001b[31mTest timeout of 60000ms exceeded while running \"beforeEach\" hook.\u001b[39m", - "stack": "\u001b[31mTest timeout of 60000ms exceeded while running \"beforeEach\" hook.\u001b[39m\n at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:501:10", - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 10, - "line": 501 - }, - "snippet": "\u001b[0m \u001b[90m 499 |\u001b[39m test\u001b[33m.\u001b[39mdescribe(\u001b[32m'5. Profile Management'\u001b[39m\u001b[33m,\u001b[39m () \u001b[33m=>\u001b[39m {\n \u001b[90m 500 |\u001b[39m \n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 501 |\u001b[39m test\u001b[33m.\u001b[39mbeforeEach(\u001b[36masync\u001b[39m ({ page }) \u001b[33m=>\u001b[39m {\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 502 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mgoto(\u001b[32m`${TEST_CONFIG.FRONTEND_URL}/login`\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 503 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"email\"], input[name=\"email\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39memail)\u001b[33m;\u001b[39m\n \u001b[90m 504 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"password\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39mpassword)\u001b[33m;\u001b[39m\u001b[0m" - }, - "errors": [ - { - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 10, - "line": 501 - }, - "message": "\u001b[31mTest timeout of 60000ms exceeded while running \"beforeEach\" hook.\u001b[39m\n\n\u001b[0m \u001b[90m 499 |\u001b[39m test\u001b[33m.\u001b[39mdescribe(\u001b[32m'5. Profile Management'\u001b[39m\u001b[33m,\u001b[39m () \u001b[33m=>\u001b[39m {\n \u001b[90m 500 |\u001b[39m \n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 501 |\u001b[39m test\u001b[33m.\u001b[39mbeforeEach(\u001b[36masync\u001b[39m ({ page }) \u001b[33m=>\u001b[39m {\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 502 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mgoto(\u001b[32m`${TEST_CONFIG.FRONTEND_URL}/login`\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 503 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"email\"], input[name=\"email\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39memail)\u001b[33m;\u001b[39m\n \u001b[90m 504 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"password\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39mpassword)\u001b[33m;\u001b[39m\u001b[0m\n\u001b[2m at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:501:10\u001b[22m" - }, - { - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 18, - "line": 503 - }, - "message": "Error: page.fill: Test timeout of 60000ms exceeded.\nCall log:\n\u001b[2m - waiting for locator('input[type=\"email\"], input[name=\"email\"]')\u001b[22m\n\n\n\u001b[0m \u001b[90m 501 |\u001b[39m test\u001b[33m.\u001b[39mbeforeEach(\u001b[36masync\u001b[39m ({ page }) \u001b[33m=>\u001b[39m {\n \u001b[90m 502 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mgoto(\u001b[32m`${TEST_CONFIG.FRONTEND_URL}/login`\u001b[39m)\u001b[33m;\u001b[39m\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 503 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"email\"], input[name=\"email\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39memail)\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 504 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"password\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39mpassword)\u001b[33m;\u001b[39m\n \u001b[90m 505 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mclick(\u001b[32m'button[type=\"submit\"]'\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 506 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mwaitForURL(\u001b[35m/\\/(dashboard|home|app)/\u001b[39m\u001b[33m,\u001b[39m { timeout\u001b[33m:\u001b[39m \u001b[35m15000\u001b[39m })\u001b[33m;\u001b[39m\u001b[0m\n\u001b[2m at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:503:18\u001b[22m" - } - ], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:24:35.769Z", - "annotations": [], - "attachments": [ - { - "name": "screenshot", - "contentType": "image/png", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-20809-nt-5-1---Profile-page-loads-chromium/test-failed-1.png" - }, - { - "name": "video", - "contentType": "video/webm", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-20809-nt-5-1---Profile-page-loads-chromium/video.webm" - }, - { - "name": "error-context", - "contentType": "text/markdown", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-20809-nt-5-1---Profile-page-loads-chromium/error-context.md" - } - ], - "errorLocation": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 10, - "line": 501 - } + "text": "🧪 [AUTH TEST] Running: Logout\n" + }, + { + "text": "🔐 [LOGIN] Attempting authentication as e2e@test.com...\n" + }, + { + "text": "⏳ [LOGIN] Waiting 500ms before login (1768081836583ms since last login)...\n" + }, + { + "text": "✏️ [LOGIN] User not authenticated, proceeding with login form...\n" + }, + { + "text": "⚡ [FORM SUBMIT] Forcing submission of form: form\n" + }, + { + "text": "🔍 [FORM SUBMIT] Waiting for form selector: form\n" + }, + { + "text": "⏳ [FORM SUBMIT] Waiting for React to update state...\n" + }, + { + "text": "🚀 [FORM SUBMIT] Submitting form...\n" + }, + { + "text": "✅ [FORM SUBMIT] Form form submitted successfully\n" + }, + { + "text": "⏳ [LOGIN] Waiting for networkidle after navigation...\n" + }, + { + "text": "⏳ [LOGIN] Waiting for auth state to be persisted...\n" + }, + { + "text": "🔍 [LOGIN] Verifying authentication state...\n" + }, + { + "text": " ✅ TOKEN FOUND: eyJhbGciOiJIUzI1NiIsInR5cCI6Ik... (source: storage)\n" + }, + { + "text": "✅ [LOGIN] Successfully authenticated as e2e@test.com (token: eyJhbGciOiJIUzI1NiIs...)\n" + }, + { + "text": "🔍 [AUTH TEST] Checking token presence before logout...\n" + }, + { + "text": " ✅ TOKEN FOUND: eyJhbGciOiJIUzI1NiIsInR5cCI6Ik... (source: storage)\n" + }, + { + "text": "✅ [AUTH TEST] Token present before logout: eyJhbGciOiJIUzI1NiIsInR5cCI6Ik...\n" + }, + { + "text": "🔴 [NETWORK ERROR] POST http://127.0.0.1:8080/api/v1/auth/logout: 400\n" + }, + { + "text": "🔴 [CONSOLE ERROR] Failed to load resource: the server responded with a status of 400 (Bad Request)\n" + }, + { + "text": "✅ [AUTH TEST] Logout successful\n" + }, + { + "text": "\n📊 [AUTH TEST] === Final Verifications ===\n" + }, + { + "text": "🔴 [AUTH TEST] Console errors (1):\n" + }, + { + "text": " - Failed to load resource: the server responded with a status of 400 (Bad Request)\n" + }, + { + "text": "🔴 [AUTH TEST] Network errors (1):\n" + }, + { + "text": " - POST http://127.0.0.1:8080/api/v1/auth/logout: 400\n" } ], - "status": "unexpected" - } - ], - "id": "c50e3d8c82f89a880019-9bf2a323230b3a6e2fc6", - "file": "mvp-integration.spec.ts", - "line": 509, - "column": 5 - }, - { - "title": "5.2 - Can update profile", - "ok": false, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "chromium", - "projectName": "chromium", - "results": [ + "stderr": [ { - "workerIndex": 7, - "parallelIndex": 0, - "status": "timedOut", - "duration": 61667, - "error": { - "message": "\u001b[31mTest timeout of 60000ms exceeded while running \"beforeEach\" hook.\u001b[39m", - "stack": "\u001b[31mTest timeout of 60000ms exceeded while running \"beforeEach\" hook.\u001b[39m\n at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:501:10", - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 10, - "line": 501 - }, - "snippet": "\u001b[0m \u001b[90m 499 |\u001b[39m test\u001b[33m.\u001b[39mdescribe(\u001b[32m'5. Profile Management'\u001b[39m\u001b[33m,\u001b[39m () \u001b[33m=>\u001b[39m {\n \u001b[90m 500 |\u001b[39m \n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 501 |\u001b[39m test\u001b[33m.\u001b[39mbeforeEach(\u001b[36masync\u001b[39m ({ page }) \u001b[33m=>\u001b[39m {\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 502 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mgoto(\u001b[32m`${TEST_CONFIG.FRONTEND_URL}/login`\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 503 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"email\"], input[name=\"email\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39memail)\u001b[33m;\u001b[39m\n \u001b[90m 504 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"password\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39mpassword)\u001b[33m;\u001b[39m\u001b[0m" - }, - "errors": [ - { - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 10, - "line": 501 - }, - "message": "\u001b[31mTest timeout of 60000ms exceeded while running \"beforeEach\" hook.\u001b[39m\n\n\u001b[0m \u001b[90m 499 |\u001b[39m test\u001b[33m.\u001b[39mdescribe(\u001b[32m'5. Profile Management'\u001b[39m\u001b[33m,\u001b[39m () \u001b[33m=>\u001b[39m {\n \u001b[90m 500 |\u001b[39m \n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 501 |\u001b[39m test\u001b[33m.\u001b[39mbeforeEach(\u001b[36masync\u001b[39m ({ page }) \u001b[33m=>\u001b[39m {\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 502 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mgoto(\u001b[32m`${TEST_CONFIG.FRONTEND_URL}/login`\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 503 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"email\"], input[name=\"email\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39memail)\u001b[33m;\u001b[39m\n \u001b[90m 504 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"password\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39mpassword)\u001b[33m;\u001b[39m\u001b[0m\n\u001b[2m at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:501:10\u001b[22m" - }, - { - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 18, - "line": 503 - }, - "message": "Error: page.fill: Test timeout of 60000ms exceeded.\nCall log:\n\u001b[2m - waiting for locator('input[type=\"email\"], input[name=\"email\"]')\u001b[22m\n\n\n\u001b[0m \u001b[90m 501 |\u001b[39m test\u001b[33m.\u001b[39mbeforeEach(\u001b[36masync\u001b[39m ({ page }) \u001b[33m=>\u001b[39m {\n \u001b[90m 502 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mgoto(\u001b[32m`${TEST_CONFIG.FRONTEND_URL}/login`\u001b[39m)\u001b[33m;\u001b[39m\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 503 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"email\"], input[name=\"email\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39memail)\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 504 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"password\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39mpassword)\u001b[33m;\u001b[39m\n \u001b[90m 505 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mclick(\u001b[32m'button[type=\"submit\"]'\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 506 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mwaitForURL(\u001b[35m/\\/(dashboard|home|app)/\u001b[39m\u001b[33m,\u001b[39m { timeout\u001b[33m:\u001b[39m \u001b[35m15000\u001b[39m })\u001b[33m;\u001b[39m\u001b[0m\n\u001b[2m at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:503:18\u001b[22m" - } - ], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:25:38.363Z", - "annotations": [], - "attachments": [ - { - "name": "screenshot", - "contentType": "image/png", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-eefd2-nt-5-2---Can-update-profile-chromium/test-failed-1.png" - }, - { - "name": "video", - "contentType": "video/webm", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-eefd2-nt-5-2---Can-update-profile-chromium/video.webm" - }, - { - "name": "error-context", - "contentType": "text/markdown", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-eefd2-nt-5-2---Can-update-profile-chromium/error-context.md" - } - ], - "errorLocation": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 10, - "line": 501 - } + "text": "⚠️ [LOGIN] Form not visible and not on dashboard. Proceeding (might fail)...\n" + }, + { + "text": "⚠️ [AUTH TEST] Test passed but had console errors\n" } ], - "status": "unexpected" - } - ], - "id": "c50e3d8c82f89a880019-9c5007796de46c186182", - "file": "mvp-integration.spec.ts", - "line": 517, - "column": 5 - }, - { - "title": "5.1 - Profile page loads", - "ok": false, - "tags": [], - "tests": [ - { - "timeout": 60000, + "retry": 0, + "startTime": "2026-01-10T21:50:36.538Z", "annotations": [], - "expectedStatus": "passed", - "projectId": "firefox", - "projectName": "firefox", - "results": [ - { - "workerIndex": 21, - "parallelIndex": 0, - "status": "failed", - "duration": 16680, - "error": { - "message": "TimeoutError: page.waitForURL: Timeout 15000ms exceeded.\n=========================== logs ===========================\nwaiting for navigation until \"load\"\n navigated to \"http://localhost:5173/login\"\n navigated to \"http://localhost:5173/login\"\n============================================================", - "stack": "TimeoutError: page.waitForURL: Timeout 15000ms exceeded.\n=========================== logs ===========================\nwaiting for navigation until \"load\"\n navigated to \"http://localhost:5173/login\"\n navigated to \"http://localhost:5173/login\"\n============================================================\n at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:506:18", - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 18, - "line": 506 - }, - "snippet": "\u001b[0m \u001b[90m 504 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"password\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39mpassword)\u001b[33m;\u001b[39m\n \u001b[90m 505 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mclick(\u001b[32m'button[type=\"submit\"]'\u001b[39m)\u001b[33m;\u001b[39m\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 506 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mwaitForURL(\u001b[35m/\\/(dashboard|home|app)/\u001b[39m\u001b[33m,\u001b[39m { timeout\u001b[33m:\u001b[39m \u001b[35m15000\u001b[39m })\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 507 |\u001b[39m })\u001b[33m;\u001b[39m\n \u001b[90m 508 |\u001b[39m\n \u001b[90m 509 |\u001b[39m test(\u001b[32m'5.1 - Profile page loads'\u001b[39m\u001b[33m,\u001b[39m \u001b[36masync\u001b[39m ({ page }) \u001b[33m=>\u001b[39m {\u001b[0m" - }, - "errors": [ - { - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 18, - "line": 506 - }, - "message": "TimeoutError: page.waitForURL: Timeout 15000ms exceeded.\n=========================== logs ===========================\nwaiting for navigation until \"load\"\n navigated to \"http://localhost:5173/login\"\n navigated to \"http://localhost:5173/login\"\n============================================================\n\n\u001b[0m \u001b[90m 504 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"password\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39mpassword)\u001b[33m;\u001b[39m\n \u001b[90m 505 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mclick(\u001b[32m'button[type=\"submit\"]'\u001b[39m)\u001b[33m;\u001b[39m\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 506 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mwaitForURL(\u001b[35m/\\/(dashboard|home|app)/\u001b[39m\u001b[33m,\u001b[39m { timeout\u001b[33m:\u001b[39m \u001b[35m15000\u001b[39m })\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 507 |\u001b[39m })\u001b[33m;\u001b[39m\n \u001b[90m 508 |\u001b[39m\n \u001b[90m 509 |\u001b[39m test(\u001b[32m'5.1 - Profile page loads'\u001b[39m\u001b[33m,\u001b[39m \u001b[36masync\u001b[39m ({ page }) \u001b[33m=>\u001b[39m {\u001b[0m\n\u001b[2m at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:506:18\u001b[22m" - } - ], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:33:48.406Z", - "annotations": [], - "attachments": [ - { - "name": "screenshot", - "contentType": "image/png", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-20809-nt-5-1---Profile-page-loads-firefox/test-failed-1.png" - }, - { - "name": "video", - "contentType": "video/webm", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-20809-nt-5-1---Profile-page-loads-firefox/video.webm" - }, - { - "name": "error-context", - "contentType": "text/markdown", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-20809-nt-5-1---Profile-page-loads-firefox/error-context.md" - } - ], - "errorLocation": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 18, - "line": 506 - } - } - ], - "status": "unexpected" + "attachments": [] } ], - "id": "c50e3d8c82f89a880019-837e6ba405cefaa95159", - "file": "mvp-integration.spec.ts", - "line": 509, - "column": 5 - }, - { - "title": "5.2 - Can update profile", - "ok": false, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "firefox", - "projectName": "firefox", - "results": [ - { - "workerIndex": 22, - "parallelIndex": 0, - "status": "failed", - "duration": 1922, - "error": { - "message": "Error: page.waitForURL: NS_BINDING_ABORTED\n=========================== logs ===========================\nwaiting for navigation until \"load\"\n============================================================", - "stack": "Error: page.waitForURL: NS_BINDING_ABORTED\n=========================== logs ===========================\nwaiting for navigation until \"load\"\n============================================================\n at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:506:18", - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 18, - "line": 506 - }, - "snippet": "\u001b[0m \u001b[90m 504 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"password\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39mpassword)\u001b[33m;\u001b[39m\n \u001b[90m 505 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mclick(\u001b[32m'button[type=\"submit\"]'\u001b[39m)\u001b[33m;\u001b[39m\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 506 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mwaitForURL(\u001b[35m/\\/(dashboard|home|app)/\u001b[39m\u001b[33m,\u001b[39m { timeout\u001b[33m:\u001b[39m \u001b[35m15000\u001b[39m })\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 507 |\u001b[39m })\u001b[33m;\u001b[39m\n \u001b[90m 508 |\u001b[39m\n \u001b[90m 509 |\u001b[39m test(\u001b[32m'5.1 - Profile page loads'\u001b[39m\u001b[33m,\u001b[39m \u001b[36masync\u001b[39m ({ page }) \u001b[33m=>\u001b[39m {\u001b[0m" - }, - "errors": [ - { - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 18, - "line": 506 - }, - "message": "Error: page.waitForURL: NS_BINDING_ABORTED\n=========================== logs ===========================\nwaiting for navigation until \"load\"\n============================================================\n\n\u001b[0m \u001b[90m 504 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"password\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39mpassword)\u001b[33m;\u001b[39m\n \u001b[90m 505 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mclick(\u001b[32m'button[type=\"submit\"]'\u001b[39m)\u001b[33m;\u001b[39m\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 506 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mwaitForURL(\u001b[35m/\\/(dashboard|home|app)/\u001b[39m\u001b[33m,\u001b[39m { timeout\u001b[33m:\u001b[39m \u001b[35m15000\u001b[39m })\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 507 |\u001b[39m })\u001b[33m;\u001b[39m\n \u001b[90m 508 |\u001b[39m\n \u001b[90m 509 |\u001b[39m test(\u001b[32m'5.1 - Profile page loads'\u001b[39m\u001b[33m,\u001b[39m \u001b[36masync\u001b[39m ({ page }) \u001b[33m=>\u001b[39m {\u001b[0m\n\u001b[2m at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:506:18\u001b[22m" - } - ], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:34:06.401Z", - "annotations": [], - "attachments": [ - { - "name": "screenshot", - "contentType": "image/png", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-eefd2-nt-5-2---Can-update-profile-firefox/test-failed-1.png" - }, - { - "name": "video", - "contentType": "video/webm", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-eefd2-nt-5-2---Can-update-profile-firefox/video.webm" - } - ], - "errorLocation": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 18, - "line": 506 - } - } - ], - "status": "unexpected" - } - ], - "id": "c50e3d8c82f89a880019-70b1981976dfed45a9c7", - "file": "mvp-integration.spec.ts", - "line": 517, - "column": 5 - }, - { - "title": "5.1 - Profile page loads", - "ok": false, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "webkit", - "projectName": "webkit", - "results": [ - { - "workerIndex": 42, - "parallelIndex": 0, - "status": "failed", - "duration": 3, - "error": { - "message": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝", - "stack": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝" - }, - "errors": [ - { - "message": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝" - } - ], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:35:05.227Z", - "annotations": [], - "attachments": [] - } - ], - "status": "unexpected" - } - ], - "id": "c50e3d8c82f89a880019-1cb3a98219e6d9b3a2c9", - "file": "mvp-integration.spec.ts", - "line": 509, - "column": 5 - }, - { - "title": "5.2 - Can update profile", - "ok": false, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "webkit", - "projectName": "webkit", - "results": [ - { - "workerIndex": 43, - "parallelIndex": 0, - "status": "failed", - "duration": 2, - "error": { - "message": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝", - "stack": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝" - }, - "errors": [ - { - "message": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝" - } - ], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:35:05.962Z", - "annotations": [], - "attachments": [] - } - ], - "status": "unexpected" - } - ], - "id": "c50e3d8c82f89a880019-e4441cfa59fb20c5e076", - "file": "mvp-integration.spec.ts", - "line": 517, - "column": 5 - }, - { - "title": "5.1 - Profile page loads", - "ok": true, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "msedge", - "projectName": "msedge", - "results": [ - { - "workerIndex": 52, - "parallelIndex": 0, - "status": "passed", - "duration": 1951, - "errors": [], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:35:29.285Z", - "annotations": [], - "attachments": [] - } - ], - "status": "expected" - } - ], - "id": "c50e3d8c82f89a880019-8f1a87683a57701cdbe1", - "file": "mvp-integration.spec.ts", - "line": 509, - "column": 5 - }, - { - "title": "5.2 - Can update profile", - "ok": true, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "msedge", - "projectName": "msedge", - "results": [ - { - "workerIndex": 52, - "parallelIndex": 0, - "status": "passed", - "duration": 1816, - "errors": [], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:35:31.248Z", - "annotations": [], - "attachments": [] - } - ], - "status": "expected" - } - ], - "id": "c50e3d8c82f89a880019-5829d6f28352d2bd637f", - "file": "mvp-integration.spec.ts", - "line": 517, - "column": 5 + "status": "expected" } - ] + ], + "id": "d748ac400d08b85935ef-c0ce6ccc7b7e02794edb", + "file": "auth.spec.ts", + "line": 293, + "column": 3 }, { - "title": "6. API Response Validation", - "file": "mvp-integration.spec.ts", - "line": 538, - "column": 8, - "specs": [ + "title": "should redirect to login when accessing protected route without auth", + "ok": true, + "tags": [], + "tests": [ { - "title": "6.1 - API returns correct response format", - "ok": false, - "tags": [], - "tests": [ + "timeout": 60000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "chromium", - "projectName": "chromium", - "results": [ + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 2542, + "errors": [], + "stdout": [ { - "workerIndex": 8, - "parallelIndex": 0, - "status": "failed", - "duration": 25, - "error": { - "message": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBeTruthy\u001b[2m()\u001b[22m\n\nReceived: \u001b[31mfalse\u001b[39m", - "stack": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBeTruthy\u001b[2m()\u001b[22m\n\nReceived: \u001b[31mfalse\u001b[39m\n at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:549:34", - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 34, - "line": 549 - }, - "snippet": "\u001b[0m \u001b[90m 547 |\u001b[39m })\u001b[33m;\u001b[39m\n \u001b[90m 548 |\u001b[39m \n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 549 |\u001b[39m expect(loginResponse\u001b[33m.\u001b[39mok())\u001b[33m.\u001b[39mtoBeTruthy()\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 550 |\u001b[39m \n \u001b[90m 551 |\u001b[39m \u001b[36mconst\u001b[39m data \u001b[33m=\u001b[39m \u001b[36mawait\u001b[39m loginResponse\u001b[33m.\u001b[39mjson()\u001b[33m;\u001b[39m\n \u001b[90m 552 |\u001b[39m \u001b[0m" - }, - "errors": [ - { - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 34, - "line": 549 - }, - "message": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBeTruthy\u001b[2m()\u001b[22m\n\nReceived: \u001b[31mfalse\u001b[39m\n\n\u001b[0m \u001b[90m 547 |\u001b[39m })\u001b[33m;\u001b[39m\n \u001b[90m 548 |\u001b[39m \n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 549 |\u001b[39m expect(loginResponse\u001b[33m.\u001b[39mok())\u001b[33m.\u001b[39mtoBeTruthy()\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 550 |\u001b[39m \n \u001b[90m 551 |\u001b[39m \u001b[36mconst\u001b[39m data \u001b[33m=\u001b[39m \u001b[36mawait\u001b[39m loginResponse\u001b[33m.\u001b[39mjson()\u001b[33m;\u001b[39m\n \u001b[90m 552 |\u001b[39m \u001b[0m\n\u001b[2m at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:549:34\u001b[22m" - } - ], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:26:40.734Z", - "annotations": [], - "attachments": [], - "errorLocation": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 34, - "line": 549 - } + "text": "🧪 [AUTH TEST] Running: Route guard test\n" + }, + { + "text": "🔴 [REQUEST FAILED] GET http://localhost:5173/node_modules/.vite/deps/chunk-KOCE3OZ5.js?v=3f421a3c: net::ERR_ABORTED\n" + }, + { + "text": "✅ [AUTH TEST] Route guard working correctly\n" + }, + { + "text": "\n📊 [AUTH TEST] === Final Verifications ===\n" + }, + { + "text": "✅ [AUTH TEST] No console errors\n" + }, + { + "text": "🔴 [AUTH TEST] Network errors (1):\n" + }, + { + "text": " - GET http://localhost:5173/node_modules/.vite/deps/chunk-KOCE3OZ5.js?v=3f421a3c: 0\n" } ], - "status": "unexpected" - } - ], - "id": "c50e3d8c82f89a880019-cd23adfa00876e39f36d", - "file": "mvp-integration.spec.ts", - "line": 540, - "column": 5 - }, - { - "title": "6.2 - User ID is string UUID", - "ok": false, - "tags": [], - "tests": [ - { - "timeout": 60000, + "stderr": [], + "retry": 0, + "startTime": "2026-01-10T21:50:44.752Z", "annotations": [], - "expectedStatus": "passed", - "projectId": "chromium", - "projectName": "chromium", - "results": [ - { - "workerIndex": 9, - "parallelIndex": 0, - "status": "failed", - "duration": 23, - "error": { - "message": "SyntaxError: Unexpected non-whitespace character after JSON at position 4 (line 1 column 5)", - "stack": "SyntaxError: Unexpected non-whitespace character after JSON at position 4 (line 1 column 5)\n at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:571:22", - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 22, - "line": 571 - }, - "snippet": "\u001b[0m \u001b[90m 569 |\u001b[39m }\n \u001b[90m 570 |\u001b[39m })\u001b[33m;\u001b[39m\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 571 |\u001b[39m \u001b[36mconst\u001b[39m data \u001b[33m=\u001b[39m \u001b[36mawait\u001b[39m loginResponse\u001b[33m.\u001b[39mjson()\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 572 |\u001b[39m accessToken \u001b[33m=\u001b[39m data\u001b[33m.\u001b[39mdata\u001b[33m?\u001b[39m\u001b[33m.\u001b[39maccess_token \u001b[33m||\u001b[39m data\u001b[33m.\u001b[39maccess_token \u001b[33m||\u001b[39m data\u001b[33m.\u001b[39mdata\u001b[33m?\u001b[39m\u001b[33m.\u001b[39mtoken\u001b[33m?\u001b[39m\u001b[33m.\u001b[39maccess_token\u001b[33m;\u001b[39m\n \u001b[90m 573 |\u001b[39m }\n \u001b[90m 574 |\u001b[39m \u001b[0m" - }, - "errors": [ - { - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 22, - "line": 571 - }, - "message": "SyntaxError: Unexpected non-whitespace character after JSON at position 4 (line 1 column 5)\n\n\u001b[0m \u001b[90m 569 |\u001b[39m }\n \u001b[90m 570 |\u001b[39m })\u001b[33m;\u001b[39m\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 571 |\u001b[39m \u001b[36mconst\u001b[39m data \u001b[33m=\u001b[39m \u001b[36mawait\u001b[39m loginResponse\u001b[33m.\u001b[39mjson()\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 572 |\u001b[39m accessToken \u001b[33m=\u001b[39m data\u001b[33m.\u001b[39mdata\u001b[33m?\u001b[39m\u001b[33m.\u001b[39maccess_token \u001b[33m||\u001b[39m data\u001b[33m.\u001b[39maccess_token \u001b[33m||\u001b[39m data\u001b[33m.\u001b[39mdata\u001b[33m?\u001b[39m\u001b[33m.\u001b[39mtoken\u001b[33m?\u001b[39m\u001b[33m.\u001b[39maccess_token\u001b[33m;\u001b[39m\n \u001b[90m 573 |\u001b[39m }\n \u001b[90m 574 |\u001b[39m \u001b[0m\n\u001b[2m at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:571:22\u001b[22m" - } - ], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:26:41.404Z", - "annotations": [], - "attachments": [], - "errorLocation": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 22, - "line": 571 - } - } - ], - "status": "unexpected" + "attachments": [] } ], - "id": "c50e3d8c82f89a880019-d5c61504b74ec6dd9796", - "file": "mvp-integration.spec.ts", - "line": 562, - "column": 5 - }, - { - "title": "6.3 - Error responses have correct format", - "ok": false, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "chromium", - "projectName": "chromium", - "results": [ - { - "workerIndex": 10, - "parallelIndex": 0, - "status": "failed", - "duration": 25, - "error": { - "message": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBe\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m) // Object.is equality\u001b[22m\n\nExpected: \u001b[32m401\u001b[39m\nReceived: \u001b[31m404\u001b[39m", - "stack": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBe\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m) // Object.is equality\u001b[22m\n\nExpected: \u001b[32m401\u001b[39m\nReceived: \u001b[31m404\u001b[39m\n at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:599:33", - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 33, - "line": 599 - }, - "snippet": "\u001b[0m \u001b[90m 597 |\u001b[39m })\u001b[33m;\u001b[39m\n \u001b[90m 598 |\u001b[39m \n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 599 |\u001b[39m expect(response\u001b[33m.\u001b[39mstatus())\u001b[33m.\u001b[39mtoBe(\u001b[35m401\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 600 |\u001b[39m \n \u001b[90m 601 |\u001b[39m \u001b[36mconst\u001b[39m data \u001b[33m=\u001b[39m \u001b[36mawait\u001b[39m response\u001b[33m.\u001b[39mjson()\u001b[33m;\u001b[39m\n \u001b[90m 602 |\u001b[39m \u001b[90m// Should have error info\u001b[39m\u001b[0m" - }, - "errors": [ - { - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 33, - "line": 599 - }, - "message": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBe\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m) // Object.is equality\u001b[22m\n\nExpected: \u001b[32m401\u001b[39m\nReceived: \u001b[31m404\u001b[39m\n\n\u001b[0m \u001b[90m 597 |\u001b[39m })\u001b[33m;\u001b[39m\n \u001b[90m 598 |\u001b[39m \n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 599 |\u001b[39m expect(response\u001b[33m.\u001b[39mstatus())\u001b[33m.\u001b[39mtoBe(\u001b[35m401\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 600 |\u001b[39m \n \u001b[90m 601 |\u001b[39m \u001b[36mconst\u001b[39m data \u001b[33m=\u001b[39m \u001b[36mawait\u001b[39m response\u001b[33m.\u001b[39mjson()\u001b[33m;\u001b[39m\n \u001b[90m 602 |\u001b[39m \u001b[90m// Should have error info\u001b[39m\u001b[0m\n\u001b[2m at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:599:33\u001b[22m" - } - ], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:26:42.076Z", - "annotations": [], - "attachments": [], - "errorLocation": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 33, - "line": 599 - } - } - ], - "status": "unexpected" - } - ], - "id": "c50e3d8c82f89a880019-3a4c5be74ddda3fc7fc5", - "file": "mvp-integration.spec.ts", - "line": 591, - "column": 5 - }, - { - "title": "6.1 - API returns correct response format", - "ok": false, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "firefox", - "projectName": "firefox", - "results": [ - { - "workerIndex": 23, - "parallelIndex": 0, - "status": "failed", - "duration": 26, - "error": { - "message": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBeTruthy\u001b[2m()\u001b[22m\n\nReceived: \u001b[31mfalse\u001b[39m", - "stack": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBeTruthy\u001b[2m()\u001b[22m\n\nReceived: \u001b[31mfalse\u001b[39m\n at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:549:34", - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 34, - "line": 549 - }, - "snippet": "\u001b[0m \u001b[90m 547 |\u001b[39m })\u001b[33m;\u001b[39m\n \u001b[90m 548 |\u001b[39m \n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 549 |\u001b[39m expect(loginResponse\u001b[33m.\u001b[39mok())\u001b[33m.\u001b[39mtoBeTruthy()\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 550 |\u001b[39m \n \u001b[90m 551 |\u001b[39m \u001b[36mconst\u001b[39m data \u001b[33m=\u001b[39m \u001b[36mawait\u001b[39m loginResponse\u001b[33m.\u001b[39mjson()\u001b[33m;\u001b[39m\n \u001b[90m 552 |\u001b[39m \u001b[0m" - }, - "errors": [ - { - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 34, - "line": 549 - }, - "message": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBeTruthy\u001b[2m()\u001b[22m\n\nReceived: \u001b[31mfalse\u001b[39m\n\n\u001b[0m \u001b[90m 547 |\u001b[39m })\u001b[33m;\u001b[39m\n \u001b[90m 548 |\u001b[39m \n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 549 |\u001b[39m expect(loginResponse\u001b[33m.\u001b[39mok())\u001b[33m.\u001b[39mtoBeTruthy()\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 550 |\u001b[39m \n \u001b[90m 551 |\u001b[39m \u001b[36mconst\u001b[39m data \u001b[33m=\u001b[39m \u001b[36mawait\u001b[39m loginResponse\u001b[33m.\u001b[39mjson()\u001b[33m;\u001b[39m\n \u001b[90m 552 |\u001b[39m \u001b[0m\n\u001b[2m at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:549:34\u001b[22m" - } - ], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:34:09.751Z", - "annotations": [], - "attachments": [], - "errorLocation": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 34, - "line": 549 - } - } - ], - "status": "unexpected" - } - ], - "id": "c50e3d8c82f89a880019-b0f097169841dd60f6b6", - "file": "mvp-integration.spec.ts", - "line": 540, - "column": 5 - }, - { - "title": "6.2 - User ID is string UUID", - "ok": false, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "firefox", - "projectName": "firefox", - "results": [ - { - "workerIndex": 24, - "parallelIndex": 0, - "status": "failed", - "duration": 27, - "error": { - "message": "SyntaxError: Unexpected non-whitespace character after JSON at position 4 (line 1 column 5)", - "stack": "SyntaxError: Unexpected non-whitespace character after JSON at position 4 (line 1 column 5)\n at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:571:22", - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 22, - "line": 571 - }, - "snippet": "\u001b[0m \u001b[90m 569 |\u001b[39m }\n \u001b[90m 570 |\u001b[39m })\u001b[33m;\u001b[39m\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 571 |\u001b[39m \u001b[36mconst\u001b[39m data \u001b[33m=\u001b[39m \u001b[36mawait\u001b[39m loginResponse\u001b[33m.\u001b[39mjson()\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 572 |\u001b[39m accessToken \u001b[33m=\u001b[39m data\u001b[33m.\u001b[39mdata\u001b[33m?\u001b[39m\u001b[33m.\u001b[39maccess_token \u001b[33m||\u001b[39m data\u001b[33m.\u001b[39maccess_token \u001b[33m||\u001b[39m data\u001b[33m.\u001b[39mdata\u001b[33m?\u001b[39m\u001b[33m.\u001b[39mtoken\u001b[33m?\u001b[39m\u001b[33m.\u001b[39maccess_token\u001b[33m;\u001b[39m\n \u001b[90m 573 |\u001b[39m }\n \u001b[90m 574 |\u001b[39m \u001b[0m" - }, - "errors": [ - { - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 22, - "line": 571 - }, - "message": "SyntaxError: Unexpected non-whitespace character after JSON at position 4 (line 1 column 5)\n\n\u001b[0m \u001b[90m 569 |\u001b[39m }\n \u001b[90m 570 |\u001b[39m })\u001b[33m;\u001b[39m\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 571 |\u001b[39m \u001b[36mconst\u001b[39m data \u001b[33m=\u001b[39m \u001b[36mawait\u001b[39m loginResponse\u001b[33m.\u001b[39mjson()\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 572 |\u001b[39m accessToken \u001b[33m=\u001b[39m data\u001b[33m.\u001b[39mdata\u001b[33m?\u001b[39m\u001b[33m.\u001b[39maccess_token \u001b[33m||\u001b[39m data\u001b[33m.\u001b[39maccess_token \u001b[33m||\u001b[39m data\u001b[33m.\u001b[39mdata\u001b[33m?\u001b[39m\u001b[33m.\u001b[39mtoken\u001b[33m?\u001b[39m\u001b[33m.\u001b[39maccess_token\u001b[33m;\u001b[39m\n \u001b[90m 573 |\u001b[39m }\n \u001b[90m 574 |\u001b[39m \u001b[0m\n\u001b[2m at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:571:22\u001b[22m" - } - ], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:34:10.381Z", - "annotations": [], - "attachments": [], - "errorLocation": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 22, - "line": 571 - } - } - ], - "status": "unexpected" - } - ], - "id": "c50e3d8c82f89a880019-b445ff2f921f5b9b0794", - "file": "mvp-integration.spec.ts", - "line": 562, - "column": 5 - }, - { - "title": "6.3 - Error responses have correct format", - "ok": false, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "firefox", - "projectName": "firefox", - "results": [ - { - "workerIndex": 25, - "parallelIndex": 0, - "status": "failed", - "duration": 25, - "error": { - "message": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBe\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m) // Object.is equality\u001b[22m\n\nExpected: \u001b[32m401\u001b[39m\nReceived: \u001b[31m404\u001b[39m", - "stack": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBe\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m) // Object.is equality\u001b[22m\n\nExpected: \u001b[32m401\u001b[39m\nReceived: \u001b[31m404\u001b[39m\n at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:599:33", - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 33, - "line": 599 - }, - "snippet": "\u001b[0m \u001b[90m 597 |\u001b[39m })\u001b[33m;\u001b[39m\n \u001b[90m 598 |\u001b[39m \n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 599 |\u001b[39m expect(response\u001b[33m.\u001b[39mstatus())\u001b[33m.\u001b[39mtoBe(\u001b[35m401\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 600 |\u001b[39m \n \u001b[90m 601 |\u001b[39m \u001b[36mconst\u001b[39m data \u001b[33m=\u001b[39m \u001b[36mawait\u001b[39m response\u001b[33m.\u001b[39mjson()\u001b[33m;\u001b[39m\n \u001b[90m 602 |\u001b[39m \u001b[90m// Should have error info\u001b[39m\u001b[0m" - }, - "errors": [ - { - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 33, - "line": 599 - }, - "message": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBe\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m) // Object.is equality\u001b[22m\n\nExpected: \u001b[32m401\u001b[39m\nReceived: \u001b[31m404\u001b[39m\n\n\u001b[0m \u001b[90m 597 |\u001b[39m })\u001b[33m;\u001b[39m\n \u001b[90m 598 |\u001b[39m \n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 599 |\u001b[39m expect(response\u001b[33m.\u001b[39mstatus())\u001b[33m.\u001b[39mtoBe(\u001b[35m401\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 600 |\u001b[39m \n \u001b[90m 601 |\u001b[39m \u001b[36mconst\u001b[39m data \u001b[33m=\u001b[39m \u001b[36mawait\u001b[39m response\u001b[33m.\u001b[39mjson()\u001b[33m;\u001b[39m\n \u001b[90m 602 |\u001b[39m \u001b[90m// Should have error info\u001b[39m\u001b[0m\n\u001b[2m at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:599:33\u001b[22m" - } - ], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:34:11.056Z", - "annotations": [], - "attachments": [], - "errorLocation": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 33, - "line": 599 - } - } - ], - "status": "unexpected" - } - ], - "id": "c50e3d8c82f89a880019-3d53b42e62a93169460a", - "file": "mvp-integration.spec.ts", - "line": 591, - "column": 5 - }, - { - "title": "6.1 - API returns correct response format", - "ok": false, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "webkit", - "projectName": "webkit", - "results": [ - { - "workerIndex": 44, - "parallelIndex": 0, - "status": "failed", - "duration": 24, - "error": { - "message": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBeTruthy\u001b[2m()\u001b[22m\n\nReceived: \u001b[31mfalse\u001b[39m", - "stack": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBeTruthy\u001b[2m()\u001b[22m\n\nReceived: \u001b[31mfalse\u001b[39m\n at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:549:34", - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 34, - "line": 549 - }, - "snippet": "\u001b[0m \u001b[90m 547 |\u001b[39m })\u001b[33m;\u001b[39m\n \u001b[90m 548 |\u001b[39m \n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 549 |\u001b[39m expect(loginResponse\u001b[33m.\u001b[39mok())\u001b[33m.\u001b[39mtoBeTruthy()\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 550 |\u001b[39m \n \u001b[90m 551 |\u001b[39m \u001b[36mconst\u001b[39m data \u001b[33m=\u001b[39m \u001b[36mawait\u001b[39m loginResponse\u001b[33m.\u001b[39mjson()\u001b[33m;\u001b[39m\n \u001b[90m 552 |\u001b[39m \u001b[0m" - }, - "errors": [ - { - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 34, - "line": 549 - }, - "message": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBeTruthy\u001b[2m()\u001b[22m\n\nReceived: \u001b[31mfalse\u001b[39m\n\n\u001b[0m \u001b[90m 547 |\u001b[39m })\u001b[33m;\u001b[39m\n \u001b[90m 548 |\u001b[39m \n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 549 |\u001b[39m expect(loginResponse\u001b[33m.\u001b[39mok())\u001b[33m.\u001b[39mtoBeTruthy()\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 550 |\u001b[39m \n \u001b[90m 551 |\u001b[39m \u001b[36mconst\u001b[39m data \u001b[33m=\u001b[39m \u001b[36mawait\u001b[39m loginResponse\u001b[33m.\u001b[39mjson()\u001b[33m;\u001b[39m\n \u001b[90m 552 |\u001b[39m \u001b[0m\n\u001b[2m at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:549:34\u001b[22m" - } - ], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:35:06.710Z", - "annotations": [], - "attachments": [], - "errorLocation": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 34, - "line": 549 - } - } - ], - "status": "unexpected" - } - ], - "id": "c50e3d8c82f89a880019-03ecbbf81429fa1b3914", - "file": "mvp-integration.spec.ts", - "line": 540, - "column": 5 - }, - { - "title": "6.2 - User ID is string UUID", - "ok": false, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "webkit", - "projectName": "webkit", - "results": [ - { - "workerIndex": 45, - "parallelIndex": 0, - "status": "failed", - "duration": 24, - "error": { - "message": "SyntaxError: Unexpected non-whitespace character after JSON at position 4 (line 1 column 5)", - "stack": "SyntaxError: Unexpected non-whitespace character after JSON at position 4 (line 1 column 5)\n at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:571:22", - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 22, - "line": 571 - }, - "snippet": "\u001b[0m \u001b[90m 569 |\u001b[39m }\n \u001b[90m 570 |\u001b[39m })\u001b[33m;\u001b[39m\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 571 |\u001b[39m \u001b[36mconst\u001b[39m data \u001b[33m=\u001b[39m \u001b[36mawait\u001b[39m loginResponse\u001b[33m.\u001b[39mjson()\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 572 |\u001b[39m accessToken \u001b[33m=\u001b[39m data\u001b[33m.\u001b[39mdata\u001b[33m?\u001b[39m\u001b[33m.\u001b[39maccess_token \u001b[33m||\u001b[39m data\u001b[33m.\u001b[39maccess_token \u001b[33m||\u001b[39m data\u001b[33m.\u001b[39mdata\u001b[33m?\u001b[39m\u001b[33m.\u001b[39mtoken\u001b[33m?\u001b[39m\u001b[33m.\u001b[39maccess_token\u001b[33m;\u001b[39m\n \u001b[90m 573 |\u001b[39m }\n \u001b[90m 574 |\u001b[39m \u001b[0m" - }, - "errors": [ - { - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 22, - "line": 571 - }, - "message": "SyntaxError: Unexpected non-whitespace character after JSON at position 4 (line 1 column 5)\n\n\u001b[0m \u001b[90m 569 |\u001b[39m }\n \u001b[90m 570 |\u001b[39m })\u001b[33m;\u001b[39m\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 571 |\u001b[39m \u001b[36mconst\u001b[39m data \u001b[33m=\u001b[39m \u001b[36mawait\u001b[39m loginResponse\u001b[33m.\u001b[39mjson()\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 572 |\u001b[39m accessToken \u001b[33m=\u001b[39m data\u001b[33m.\u001b[39mdata\u001b[33m?\u001b[39m\u001b[33m.\u001b[39maccess_token \u001b[33m||\u001b[39m data\u001b[33m.\u001b[39maccess_token \u001b[33m||\u001b[39m data\u001b[33m.\u001b[39mdata\u001b[33m?\u001b[39m\u001b[33m.\u001b[39mtoken\u001b[33m?\u001b[39m\u001b[33m.\u001b[39maccess_token\u001b[33m;\u001b[39m\n \u001b[90m 573 |\u001b[39m }\n \u001b[90m 574 |\u001b[39m \u001b[0m\n\u001b[2m at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:571:22\u001b[22m" - } - ], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:35:07.339Z", - "annotations": [], - "attachments": [], - "errorLocation": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 22, - "line": 571 - } - } - ], - "status": "unexpected" - } - ], - "id": "c50e3d8c82f89a880019-94bf6e1ea25cc0e63d93", - "file": "mvp-integration.spec.ts", - "line": 562, - "column": 5 - }, - { - "title": "6.3 - Error responses have correct format", - "ok": false, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "webkit", - "projectName": "webkit", - "results": [ - { - "workerIndex": 46, - "parallelIndex": 0, - "status": "failed", - "duration": 24, - "error": { - "message": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBe\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m) // Object.is equality\u001b[22m\n\nExpected: \u001b[32m401\u001b[39m\nReceived: \u001b[31m404\u001b[39m", - "stack": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBe\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m) // Object.is equality\u001b[22m\n\nExpected: \u001b[32m401\u001b[39m\nReceived: \u001b[31m404\u001b[39m\n at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:599:33", - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 33, - "line": 599 - }, - "snippet": "\u001b[0m \u001b[90m 597 |\u001b[39m })\u001b[33m;\u001b[39m\n \u001b[90m 598 |\u001b[39m \n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 599 |\u001b[39m expect(response\u001b[33m.\u001b[39mstatus())\u001b[33m.\u001b[39mtoBe(\u001b[35m401\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 600 |\u001b[39m \n \u001b[90m 601 |\u001b[39m \u001b[36mconst\u001b[39m data \u001b[33m=\u001b[39m \u001b[36mawait\u001b[39m response\u001b[33m.\u001b[39mjson()\u001b[33m;\u001b[39m\n \u001b[90m 602 |\u001b[39m \u001b[90m// Should have error info\u001b[39m\u001b[0m" - }, - "errors": [ - { - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 33, - "line": 599 - }, - "message": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBe\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m) // Object.is equality\u001b[22m\n\nExpected: \u001b[32m401\u001b[39m\nReceived: \u001b[31m404\u001b[39m\n\n\u001b[0m \u001b[90m 597 |\u001b[39m })\u001b[33m;\u001b[39m\n \u001b[90m 598 |\u001b[39m \n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 599 |\u001b[39m expect(response\u001b[33m.\u001b[39mstatus())\u001b[33m.\u001b[39mtoBe(\u001b[35m401\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 600 |\u001b[39m \n \u001b[90m 601 |\u001b[39m \u001b[36mconst\u001b[39m data \u001b[33m=\u001b[39m \u001b[36mawait\u001b[39m response\u001b[33m.\u001b[39mjson()\u001b[33m;\u001b[39m\n \u001b[90m 602 |\u001b[39m \u001b[90m// Should have error info\u001b[39m\u001b[0m\n\u001b[2m at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:599:33\u001b[22m" - } - ], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:35:08.078Z", - "annotations": [], - "attachments": [], - "errorLocation": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 33, - "line": 599 - } - } - ], - "status": "unexpected" - } - ], - "id": "c50e3d8c82f89a880019-cd73ea20e7f1c0fb2d7c", - "file": "mvp-integration.spec.ts", - "line": 591, - "column": 5 - }, - { - "title": "6.1 - API returns correct response format", - "ok": false, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "msedge", - "projectName": "msedge", - "results": [ - { - "workerIndex": 52, - "parallelIndex": 0, - "status": "failed", - "duration": 8, - "error": { - "message": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBeTruthy\u001b[2m()\u001b[22m\n\nReceived: \u001b[31mfalse\u001b[39m", - "stack": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBeTruthy\u001b[2m()\u001b[22m\n\nReceived: \u001b[31mfalse\u001b[39m\n at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:549:34", - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 34, - "line": 549 - }, - "snippet": "\u001b[0m \u001b[90m 547 |\u001b[39m })\u001b[33m;\u001b[39m\n \u001b[90m 548 |\u001b[39m \n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 549 |\u001b[39m expect(loginResponse\u001b[33m.\u001b[39mok())\u001b[33m.\u001b[39mtoBeTruthy()\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 550 |\u001b[39m \n \u001b[90m 551 |\u001b[39m \u001b[36mconst\u001b[39m data \u001b[33m=\u001b[39m \u001b[36mawait\u001b[39m loginResponse\u001b[33m.\u001b[39mjson()\u001b[33m;\u001b[39m\n \u001b[90m 552 |\u001b[39m \u001b[0m" - }, - "errors": [ - { - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 34, - "line": 549 - }, - "message": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBeTruthy\u001b[2m()\u001b[22m\n\nReceived: \u001b[31mfalse\u001b[39m\n\n\u001b[0m \u001b[90m 547 |\u001b[39m })\u001b[33m;\u001b[39m\n \u001b[90m 548 |\u001b[39m \n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 549 |\u001b[39m expect(loginResponse\u001b[33m.\u001b[39mok())\u001b[33m.\u001b[39mtoBeTruthy()\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 550 |\u001b[39m \n \u001b[90m 551 |\u001b[39m \u001b[36mconst\u001b[39m data \u001b[33m=\u001b[39m \u001b[36mawait\u001b[39m loginResponse\u001b[33m.\u001b[39mjson()\u001b[33m;\u001b[39m\n \u001b[90m 552 |\u001b[39m \u001b[0m\n\u001b[2m at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:549:34\u001b[22m" - } - ], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:35:33.075Z", - "annotations": [], - "attachments": [], - "errorLocation": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 34, - "line": 549 - } - } - ], - "status": "unexpected" - } - ], - "id": "c50e3d8c82f89a880019-65bbfc663ee4fe7b5854", - "file": "mvp-integration.spec.ts", - "line": 540, - "column": 5 - }, - { - "title": "6.2 - User ID is string UUID", - "ok": false, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "msedge", - "projectName": "msedge", - "results": [ - { - "workerIndex": 53, - "parallelIndex": 0, - "status": "failed", - "duration": 24, - "error": { - "message": "SyntaxError: Unexpected non-whitespace character after JSON at position 4 (line 1 column 5)", - "stack": "SyntaxError: Unexpected non-whitespace character after JSON at position 4 (line 1 column 5)\n at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:571:22", - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 22, - "line": 571 - }, - "snippet": "\u001b[0m \u001b[90m 569 |\u001b[39m }\n \u001b[90m 570 |\u001b[39m })\u001b[33m;\u001b[39m\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 571 |\u001b[39m \u001b[36mconst\u001b[39m data \u001b[33m=\u001b[39m \u001b[36mawait\u001b[39m loginResponse\u001b[33m.\u001b[39mjson()\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 572 |\u001b[39m accessToken \u001b[33m=\u001b[39m data\u001b[33m.\u001b[39mdata\u001b[33m?\u001b[39m\u001b[33m.\u001b[39maccess_token \u001b[33m||\u001b[39m data\u001b[33m.\u001b[39maccess_token \u001b[33m||\u001b[39m data\u001b[33m.\u001b[39mdata\u001b[33m?\u001b[39m\u001b[33m.\u001b[39mtoken\u001b[33m?\u001b[39m\u001b[33m.\u001b[39maccess_token\u001b[33m;\u001b[39m\n \u001b[90m 573 |\u001b[39m }\n \u001b[90m 574 |\u001b[39m \u001b[0m" - }, - "errors": [ - { - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 22, - "line": 571 - }, - "message": "SyntaxError: Unexpected non-whitespace character after JSON at position 4 (line 1 column 5)\n\n\u001b[0m \u001b[90m 569 |\u001b[39m }\n \u001b[90m 570 |\u001b[39m })\u001b[33m;\u001b[39m\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 571 |\u001b[39m \u001b[36mconst\u001b[39m data \u001b[33m=\u001b[39m \u001b[36mawait\u001b[39m loginResponse\u001b[33m.\u001b[39mjson()\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 572 |\u001b[39m accessToken \u001b[33m=\u001b[39m data\u001b[33m.\u001b[39mdata\u001b[33m?\u001b[39m\u001b[33m.\u001b[39maccess_token \u001b[33m||\u001b[39m data\u001b[33m.\u001b[39maccess_token \u001b[33m||\u001b[39m data\u001b[33m.\u001b[39mdata\u001b[33m?\u001b[39m\u001b[33m.\u001b[39mtoken\u001b[33m?\u001b[39m\u001b[33m.\u001b[39maccess_token\u001b[33m;\u001b[39m\n \u001b[90m 573 |\u001b[39m }\n \u001b[90m 574 |\u001b[39m \u001b[0m\n\u001b[2m at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:571:22\u001b[22m" - } - ], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:35:33.803Z", - "annotations": [], - "attachments": [], - "errorLocation": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 22, - "line": 571 - } - } - ], - "status": "unexpected" - } - ], - "id": "c50e3d8c82f89a880019-2c4c47e0e20bbcd8246f", - "file": "mvp-integration.spec.ts", - "line": 562, - "column": 5 - }, - { - "title": "6.3 - Error responses have correct format", - "ok": false, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "msedge", - "projectName": "msedge", - "results": [ - { - "workerIndex": 54, - "parallelIndex": 0, - "status": "failed", - "duration": 66, - "error": { - "message": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBe\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m) // Object.is equality\u001b[22m\n\nExpected: \u001b[32m401\u001b[39m\nReceived: \u001b[31m404\u001b[39m", - "stack": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBe\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m) // Object.is equality\u001b[22m\n\nExpected: \u001b[32m401\u001b[39m\nReceived: \u001b[31m404\u001b[39m\n at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:599:33", - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 33, - "line": 599 - }, - "snippet": "\u001b[0m \u001b[90m 597 |\u001b[39m })\u001b[33m;\u001b[39m\n \u001b[90m 598 |\u001b[39m \n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 599 |\u001b[39m expect(response\u001b[33m.\u001b[39mstatus())\u001b[33m.\u001b[39mtoBe(\u001b[35m401\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 600 |\u001b[39m \n \u001b[90m 601 |\u001b[39m \u001b[36mconst\u001b[39m data \u001b[33m=\u001b[39m \u001b[36mawait\u001b[39m response\u001b[33m.\u001b[39mjson()\u001b[33m;\u001b[39m\n \u001b[90m 602 |\u001b[39m \u001b[90m// Should have error info\u001b[39m\u001b[0m" - }, - "errors": [ - { - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 33, - "line": 599 - }, - "message": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBe\u001b[2m(\u001b[22m\u001b[32mexpected\u001b[39m\u001b[2m) // Object.is equality\u001b[22m\n\nExpected: \u001b[32m401\u001b[39m\nReceived: \u001b[31m404\u001b[39m\n\n\u001b[0m \u001b[90m 597 |\u001b[39m })\u001b[33m;\u001b[39m\n \u001b[90m 598 |\u001b[39m \n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 599 |\u001b[39m expect(response\u001b[33m.\u001b[39mstatus())\u001b[33m.\u001b[39mtoBe(\u001b[35m401\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 600 |\u001b[39m \n \u001b[90m 601 |\u001b[39m \u001b[36mconst\u001b[39m data \u001b[33m=\u001b[39m \u001b[36mawait\u001b[39m response\u001b[33m.\u001b[39mjson()\u001b[33m;\u001b[39m\n \u001b[90m 602 |\u001b[39m \u001b[90m// Should have error info\u001b[39m\u001b[0m\n\u001b[2m at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:599:33\u001b[22m" - } - ], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:35:34.478Z", - "annotations": [], - "attachments": [], - "errorLocation": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 33, - "line": 599 - } - } - ], - "status": "unexpected" - } - ], - "id": "c50e3d8c82f89a880019-2a700d9ac42504d3f915", - "file": "mvp-integration.spec.ts", - "line": 591, - "column": 5 + "status": "expected" } - ] + ], + "id": "d748ac400d08b85935ef-4d2eb13aa042aa0b2d41", + "file": "auth.spec.ts", + "line": 373, + "column": 3 }, { - "title": "7. Error Handling", - "file": "mvp-integration.spec.ts", - "line": 607, - "column": 8, - "specs": [ + "title": "should persist authentication after page refresh", + "ok": true, + "tags": [], + "tests": [ { - "title": "7.1 - 404 page exists", - "ok": false, - "tags": [], - "tests": [ + "timeout": 60000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "chromium", - "projectName": "chromium", - "results": [ + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 19135, + "errors": [], + "stdout": [ { - "workerIndex": 11, - "parallelIndex": 0, - "status": "failed", - "duration": 526, - "error": { - "message": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBeTruthy\u001b[2m()\u001b[22m\n\nReceived: \u001b[31mfalse\u001b[39m", - "stack": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBeTruthy\u001b[2m()\u001b[22m\n\nReceived: \u001b[31mfalse\u001b[39m\n at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:616:37", - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 37, - "line": 616 - }, - "snippet": "\u001b[0m \u001b[90m 614 |\u001b[39m \u001b[36mconst\u001b[39m isRedirected \u001b[33m=\u001b[39m page\u001b[33m.\u001b[39murl()\u001b[33m.\u001b[39mincludes(\u001b[32m'login'\u001b[39m) \u001b[33m||\u001b[39m page\u001b[33m.\u001b[39murl() \u001b[33m===\u001b[39m \u001b[32m`${TEST_CONFIG.FRONTEND_URL}/`\u001b[39m\u001b[33m;\u001b[39m\n \u001b[90m 615 |\u001b[39m \n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 616 |\u001b[39m expect(is404 \u001b[33m||\u001b[39m isRedirected)\u001b[33m.\u001b[39mtoBeTruthy()\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 617 |\u001b[39m })\u001b[33m;\u001b[39m\n \u001b[90m 618 |\u001b[39m\n \u001b[90m 619 |\u001b[39m test(\u001b[32m'7.2 - Network error handling'\u001b[39m\u001b[33m,\u001b[39m \u001b[36masync\u001b[39m ({ page }) \u001b[33m=>\u001b[39m {\u001b[0m" - }, - "errors": [ - { - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 37, - "line": 616 - }, - "message": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBeTruthy\u001b[2m()\u001b[22m\n\nReceived: \u001b[31mfalse\u001b[39m\n\n\u001b[0m \u001b[90m 614 |\u001b[39m \u001b[36mconst\u001b[39m isRedirected \u001b[33m=\u001b[39m page\u001b[33m.\u001b[39murl()\u001b[33m.\u001b[39mincludes(\u001b[32m'login'\u001b[39m) \u001b[33m||\u001b[39m page\u001b[33m.\u001b[39murl() \u001b[33m===\u001b[39m \u001b[32m`${TEST_CONFIG.FRONTEND_URL}/`\u001b[39m\u001b[33m;\u001b[39m\n \u001b[90m 615 |\u001b[39m \n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 616 |\u001b[39m expect(is404 \u001b[33m||\u001b[39m isRedirected)\u001b[33m.\u001b[39mtoBeTruthy()\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 617 |\u001b[39m })\u001b[33m;\u001b[39m\n \u001b[90m 618 |\u001b[39m\n \u001b[90m 619 |\u001b[39m test(\u001b[32m'7.2 - Network error handling'\u001b[39m\u001b[33m,\u001b[39m \u001b[36masync\u001b[39m ({ page }) \u001b[33m=>\u001b[39m {\u001b[0m\n\u001b[2m at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:616:37\u001b[22m" - } - ], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:26:43.071Z", - "annotations": [], - "attachments": [ - { - "name": "screenshot", - "contentType": "image/png", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-8e28a-dling-7-1---404-page-exists-chromium/test-failed-1.png" - }, - { - "name": "video", - "contentType": "video/webm", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-8e28a-dling-7-1---404-page-exists-chromium/video.webm" - }, - { - "name": "error-context", - "contentType": "text/markdown", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-8e28a-dling-7-1---404-page-exists-chromium/error-context.md" - } - ], - "errorLocation": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 37, - "line": 616 - } + "text": "🧪 [AUTH TEST] Running: Auth persistence test\n" + }, + { + "text": "🔐 [LOGIN] Attempting authentication as e2e@test.com...\n" + }, + { + "text": "⏳ [LOGIN] Waiting 500ms before login (20281ms since last login)...\n" + }, + { + "text": "✏️ [LOGIN] User not authenticated, proceeding with login form...\n" + }, + { + "text": "⚡ [FORM SUBMIT] Forcing submission of form: form\n" + }, + { + "text": "🔍 [FORM SUBMIT] Waiting for form selector: form\n" + }, + { + "text": "⏳ [FORM SUBMIT] Waiting for React to update state...\n" + }, + { + "text": "🚀 [FORM SUBMIT] Submitting form...\n" + }, + { + "text": "✅ [FORM SUBMIT] Form form submitted successfully\n" + }, + { + "text": "⏳ [LOGIN] Waiting for networkidle after navigation...\n" + }, + { + "text": "⏳ [LOGIN] Waiting for auth state to be persisted...\n" + }, + { + "text": "🔍 [LOGIN] Verifying authentication state...\n" + }, + { + "text": " ✅ TOKEN FOUND: eyJhbGciOiJIUzI1NiIsInR5cCI6Ik... (source: storage)\n" + }, + { + "text": "✅ [LOGIN] Successfully authenticated as e2e@test.com (token: eyJhbGciOiJIUzI1NiIs...)\n" + }, + { + "text": "✅ [AUTH TEST] Authenticated before refresh\n" + }, + { + "text": " ✅ TOKEN FOUND: eyJhbGciOiJIUzI1NiIsInR5cCI6Ik... (source: storage)\n" + }, + { + "text": "✅ [AUTH TEST] Correctly persisted authentication after refresh\n" + }, + { + "text": "\n📊 [AUTH TEST] === Final Verifications ===\n" + }, + { + "text": "✅ [AUTH TEST] No console errors\n" + }, + { + "text": "✅ [AUTH TEST] No network errors\n" } ], - "status": "unexpected" - } - ], - "id": "c50e3d8c82f89a880019-b38b0d7e6bbd78b18482", - "file": "mvp-integration.spec.ts", - "line": 609, - "column": 5 - }, - { - "title": "7.2 - Network error handling", - "ok": true, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "chromium", - "projectName": "chromium", - "results": [ + "stderr": [ { - "workerIndex": 12, - "parallelIndex": 0, - "status": "passed", - "duration": 2845, - "errors": [], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:26:44.288Z", - "annotations": [], - "attachments": [] + "text": "⚠️ [LOGIN] Form not visible and not on dashboard. Proceeding (might fail)...\n" } ], - "status": "expected" - } - ], - "id": "c50e3d8c82f89a880019-23d6e366982fc1962abf", - "file": "mvp-integration.spec.ts", - "line": 619, - "column": 5 - }, - { - "title": "7.1 - 404 page exists", - "ok": false, - "tags": [], - "tests": [ - { - "timeout": 60000, + "retry": 0, + "startTime": "2026-01-10T21:50:47.310Z", "annotations": [], - "expectedStatus": "passed", - "projectId": "firefox", - "projectName": "firefox", - "results": [ - { - "workerIndex": 26, - "parallelIndex": 0, - "status": "failed", - "duration": 1371, - "error": { - "message": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBeTruthy\u001b[2m()\u001b[22m\n\nReceived: \u001b[31mfalse\u001b[39m", - "stack": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBeTruthy\u001b[2m()\u001b[22m\n\nReceived: \u001b[31mfalse\u001b[39m\n at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:616:37", - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 37, - "line": 616 - }, - "snippet": "\u001b[0m \u001b[90m 614 |\u001b[39m \u001b[36mconst\u001b[39m isRedirected \u001b[33m=\u001b[39m page\u001b[33m.\u001b[39murl()\u001b[33m.\u001b[39mincludes(\u001b[32m'login'\u001b[39m) \u001b[33m||\u001b[39m page\u001b[33m.\u001b[39murl() \u001b[33m===\u001b[39m \u001b[32m`${TEST_CONFIG.FRONTEND_URL}/`\u001b[39m\u001b[33m;\u001b[39m\n \u001b[90m 615 |\u001b[39m \n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 616 |\u001b[39m expect(is404 \u001b[33m||\u001b[39m isRedirected)\u001b[33m.\u001b[39mtoBeTruthy()\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 617 |\u001b[39m })\u001b[33m;\u001b[39m\n \u001b[90m 618 |\u001b[39m\n \u001b[90m 619 |\u001b[39m test(\u001b[32m'7.2 - Network error handling'\u001b[39m\u001b[33m,\u001b[39m \u001b[36masync\u001b[39m ({ page }) \u001b[33m=>\u001b[39m {\u001b[0m" - }, - "errors": [ - { - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 37, - "line": 616 - }, - "message": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBeTruthy\u001b[2m()\u001b[22m\n\nReceived: \u001b[31mfalse\u001b[39m\n\n\u001b[0m \u001b[90m 614 |\u001b[39m \u001b[36mconst\u001b[39m isRedirected \u001b[33m=\u001b[39m page\u001b[33m.\u001b[39murl()\u001b[33m.\u001b[39mincludes(\u001b[32m'login'\u001b[39m) \u001b[33m||\u001b[39m page\u001b[33m.\u001b[39murl() \u001b[33m===\u001b[39m \u001b[32m`${TEST_CONFIG.FRONTEND_URL}/`\u001b[39m\u001b[33m;\u001b[39m\n \u001b[90m 615 |\u001b[39m \n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 616 |\u001b[39m expect(is404 \u001b[33m||\u001b[39m isRedirected)\u001b[33m.\u001b[39mtoBeTruthy()\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 617 |\u001b[39m })\u001b[33m;\u001b[39m\n \u001b[90m 618 |\u001b[39m\n \u001b[90m 619 |\u001b[39m test(\u001b[32m'7.2 - Network error handling'\u001b[39m\u001b[33m,\u001b[39m \u001b[36masync\u001b[39m ({ page }) \u001b[33m=>\u001b[39m {\u001b[0m\n\u001b[2m at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:616:37\u001b[22m" - } - ], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:34:11.718Z", - "annotations": [], - "attachments": [ - { - "name": "screenshot", - "contentType": "image/png", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-8e28a-dling-7-1---404-page-exists-firefox/test-failed-1.png" - }, - { - "name": "video", - "contentType": "video/webm", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-8e28a-dling-7-1---404-page-exists-firefox/video.webm" - } - ], - "errorLocation": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 37, - "line": 616 - } - } - ], - "status": "unexpected" + "attachments": [] } ], - "id": "c50e3d8c82f89a880019-afc8dd199384cf264b0b", - "file": "mvp-integration.spec.ts", - "line": 609, - "column": 5 - }, - { - "title": "7.2 - Network error handling", - "ok": true, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "firefox", - "projectName": "firefox", - "results": [ - { - "workerIndex": 27, - "parallelIndex": 0, - "status": "passed", - "duration": 3313, - "errors": [], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:34:14.409Z", - "annotations": [], - "attachments": [] - } - ], - "status": "expected" - } - ], - "id": "c50e3d8c82f89a880019-2b4d8f703d5410323af4", - "file": "mvp-integration.spec.ts", - "line": 619, - "column": 5 - }, - { - "title": "7.1 - 404 page exists", - "ok": false, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "webkit", - "projectName": "webkit", - "results": [ - { - "workerIndex": 47, - "parallelIndex": 0, - "status": "failed", - "duration": 3, - "error": { - "message": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝", - "stack": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝" - }, - "errors": [ - { - "message": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝" - } - ], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:35:08.778Z", - "annotations": [], - "attachments": [] - } - ], - "status": "unexpected" - } - ], - "id": "c50e3d8c82f89a880019-0d75530f13556a58dd89", - "file": "mvp-integration.spec.ts", - "line": 609, - "column": 5 - }, - { - "title": "7.2 - Network error handling", - "ok": false, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "webkit", - "projectName": "webkit", - "results": [ - { - "workerIndex": 48, - "parallelIndex": 0, - "status": "failed", - "duration": 3, - "error": { - "message": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝", - "stack": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝" - }, - "errors": [ - { - "message": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝" - } - ], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:35:09.502Z", - "annotations": [], - "attachments": [] - } - ], - "status": "unexpected" - } - ], - "id": "c50e3d8c82f89a880019-1bcb4f154c323e0be74a", - "file": "mvp-integration.spec.ts", - "line": 619, - "column": 5 - }, - { - "title": "7.1 - 404 page exists", - "ok": false, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "msedge", - "projectName": "msedge", - "results": [ - { - "workerIndex": 55, - "parallelIndex": 0, - "status": "failed", - "duration": 648, - "error": { - "message": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBeTruthy\u001b[2m()\u001b[22m\n\nReceived: \u001b[31mfalse\u001b[39m", - "stack": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBeTruthy\u001b[2m()\u001b[22m\n\nReceived: \u001b[31mfalse\u001b[39m\n at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:616:37", - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 37, - "line": 616 - }, - "snippet": "\u001b[0m \u001b[90m 614 |\u001b[39m \u001b[36mconst\u001b[39m isRedirected \u001b[33m=\u001b[39m page\u001b[33m.\u001b[39murl()\u001b[33m.\u001b[39mincludes(\u001b[32m'login'\u001b[39m) \u001b[33m||\u001b[39m page\u001b[33m.\u001b[39murl() \u001b[33m===\u001b[39m \u001b[32m`${TEST_CONFIG.FRONTEND_URL}/`\u001b[39m\u001b[33m;\u001b[39m\n \u001b[90m 615 |\u001b[39m \n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 616 |\u001b[39m expect(is404 \u001b[33m||\u001b[39m isRedirected)\u001b[33m.\u001b[39mtoBeTruthy()\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 617 |\u001b[39m })\u001b[33m;\u001b[39m\n \u001b[90m 618 |\u001b[39m\n \u001b[90m 619 |\u001b[39m test(\u001b[32m'7.2 - Network error handling'\u001b[39m\u001b[33m,\u001b[39m \u001b[36masync\u001b[39m ({ page }) \u001b[33m=>\u001b[39m {\u001b[0m" - }, - "errors": [ - { - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 37, - "line": 616 - }, - "message": "Error: \u001b[2mexpect(\u001b[22m\u001b[31mreceived\u001b[39m\u001b[2m).\u001b[22mtoBeTruthy\u001b[2m()\u001b[22m\n\nReceived: \u001b[31mfalse\u001b[39m\n\n\u001b[0m \u001b[90m 614 |\u001b[39m \u001b[36mconst\u001b[39m isRedirected \u001b[33m=\u001b[39m page\u001b[33m.\u001b[39murl()\u001b[33m.\u001b[39mincludes(\u001b[32m'login'\u001b[39m) \u001b[33m||\u001b[39m page\u001b[33m.\u001b[39murl() \u001b[33m===\u001b[39m \u001b[32m`${TEST_CONFIG.FRONTEND_URL}/`\u001b[39m\u001b[33m;\u001b[39m\n \u001b[90m 615 |\u001b[39m \n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 616 |\u001b[39m expect(is404 \u001b[33m||\u001b[39m isRedirected)\u001b[33m.\u001b[39mtoBeTruthy()\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 617 |\u001b[39m })\u001b[33m;\u001b[39m\n \u001b[90m 618 |\u001b[39m\n \u001b[90m 619 |\u001b[39m test(\u001b[32m'7.2 - Network error handling'\u001b[39m\u001b[33m,\u001b[39m \u001b[36masync\u001b[39m ({ page }) \u001b[33m=>\u001b[39m {\u001b[0m\n\u001b[2m at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:616:37\u001b[22m" - } - ], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:35:35.251Z", - "annotations": [], - "attachments": [ - { - "name": "screenshot", - "contentType": "image/png", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-8e28a-dling-7-1---404-page-exists-msedge/test-failed-1.png" - }, - { - "name": "video", - "contentType": "video/webm", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-8e28a-dling-7-1---404-page-exists-msedge/video.webm" - }, - { - "name": "error-context", - "contentType": "text/markdown", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-8e28a-dling-7-1---404-page-exists-msedge/error-context.md" - } - ], - "errorLocation": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 37, - "line": 616 - } - } - ], - "status": "unexpected" - } - ], - "id": "c50e3d8c82f89a880019-4ce7b8b8a7c2717a5495", - "file": "mvp-integration.spec.ts", - "line": 609, - "column": 5 - }, - { - "title": "7.2 - Network error handling", - "ok": true, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "msedge", - "projectName": "msedge", - "results": [ - { - "workerIndex": 56, - "parallelIndex": 0, - "status": "passed", - "duration": 2741, - "errors": [], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:35:36.624Z", - "annotations": [], - "attachments": [] - } - ], - "status": "expected" - } - ], - "id": "c50e3d8c82f89a880019-4dd49a5f611a907760fc", - "file": "mvp-integration.spec.ts", - "line": 619, - "column": 5 + "status": "expected" } - ] + ], + "id": "d748ac400d08b85935ef-fb094c064cf6c63bcced", + "file": "auth.spec.ts", + "line": 395, + "column": 3 }, { - "title": "8. Responsive Design", - "file": "mvp-integration.spec.ts", - "line": 638, - "column": 8, - "specs": [ + "title": "should validate login form fields", + "ok": true, + "tags": [], + "tests": [ { - "title": "8.1 - Mobile viewport (375x667)", - "ok": false, - "tags": [], - "tests": [ + "timeout": 60000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "chromium", - "projectName": "chromium", - "results": [ + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 3410, + "errors": [], + "stdout": [ { - "workerIndex": 12, - "parallelIndex": 0, - "status": "timedOut", - "duration": 61643, - "error": { - "message": "\u001b[31mTest timeout of 60000ms exceeded while running \"beforeEach\" hook.\u001b[39m", - "stack": "\u001b[31mTest timeout of 60000ms exceeded while running \"beforeEach\" hook.\u001b[39m\n at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:640:10", - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 10, - "line": 640 - }, - "snippet": "\u001b[0m \u001b[90m 638 |\u001b[39m test\u001b[33m.\u001b[39mdescribe(\u001b[32m'8. Responsive Design'\u001b[39m\u001b[33m,\u001b[39m () \u001b[33m=>\u001b[39m {\n \u001b[90m 639 |\u001b[39m \n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 640 |\u001b[39m test\u001b[33m.\u001b[39mbeforeEach(\u001b[36masync\u001b[39m ({ page }) \u001b[33m=>\u001b[39m {\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 641 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mgoto(\u001b[32m`${TEST_CONFIG.FRONTEND_URL}/login`\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 642 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"email\"], input[name=\"email\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39memail)\u001b[33m;\u001b[39m\n \u001b[90m 643 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"password\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39mpassword)\u001b[33m;\u001b[39m\u001b[0m" - }, - "errors": [ - { - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 10, - "line": 640 - }, - "message": "\u001b[31mTest timeout of 60000ms exceeded while running \"beforeEach\" hook.\u001b[39m\n\n\u001b[0m \u001b[90m 638 |\u001b[39m test\u001b[33m.\u001b[39mdescribe(\u001b[32m'8. Responsive Design'\u001b[39m\u001b[33m,\u001b[39m () \u001b[33m=>\u001b[39m {\n \u001b[90m 639 |\u001b[39m \n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 640 |\u001b[39m test\u001b[33m.\u001b[39mbeforeEach(\u001b[36masync\u001b[39m ({ page }) \u001b[33m=>\u001b[39m {\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 641 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mgoto(\u001b[32m`${TEST_CONFIG.FRONTEND_URL}/login`\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 642 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"email\"], input[name=\"email\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39memail)\u001b[33m;\u001b[39m\n \u001b[90m 643 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"password\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39mpassword)\u001b[33m;\u001b[39m\u001b[0m\n\u001b[2m at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:640:10\u001b[22m" - }, - { - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 18, - "line": 642 - }, - "message": "Error: page.fill: Test timeout of 60000ms exceeded.\nCall log:\n\u001b[2m - waiting for locator('input[type=\"email\"], input[name=\"email\"]')\u001b[22m\n\n\n\u001b[0m \u001b[90m 640 |\u001b[39m test\u001b[33m.\u001b[39mbeforeEach(\u001b[36masync\u001b[39m ({ page }) \u001b[33m=>\u001b[39m {\n \u001b[90m 641 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mgoto(\u001b[32m`${TEST_CONFIG.FRONTEND_URL}/login`\u001b[39m)\u001b[33m;\u001b[39m\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 642 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"email\"], input[name=\"email\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39memail)\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 643 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"password\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39mpassword)\u001b[33m;\u001b[39m\n \u001b[90m 644 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mclick(\u001b[32m'button[type=\"submit\"]'\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 645 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mwaitForURL(\u001b[35m/\\/(dashboard|home|app)/\u001b[39m\u001b[33m,\u001b[39m { timeout\u001b[33m:\u001b[39m \u001b[35m15000\u001b[39m })\u001b[33m;\u001b[39m\u001b[0m\n\u001b[2m at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:642:18\u001b[22m" - } - ], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:26:47.206Z", - "annotations": [], - "attachments": [ - { - "name": "screenshot", - "contentType": "image/png", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-299e0----Mobile-viewport-375x667--chromium/test-failed-1.png" - }, - { - "name": "video", - "contentType": "video/webm", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-299e0----Mobile-viewport-375x667--chromium/video.webm" - }, - { - "name": "error-context", - "contentType": "text/markdown", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-299e0----Mobile-viewport-375x667--chromium/error-context.md" - } - ], - "errorLocation": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 10, - "line": 640 - } + "text": "🧪 [AUTH TEST] Running: Login form validation\n" + }, + { + "text": "✅ [AUTH TEST] Form validation prevented submission (stayed on login page)\n" + }, + { + "text": "\n📊 [AUTH TEST] === Final Verifications ===\n" + }, + { + "text": "✅ [AUTH TEST] No console errors\n" + }, + { + "text": "✅ [AUTH TEST] No network errors\n" } ], - "status": "unexpected" - } - ], - "id": "c50e3d8c82f89a880019-8be1f6b740cd1dfb1f4b", - "file": "mvp-integration.spec.ts", - "line": 648, - "column": 5 - }, - { - "title": "8.2 - Tablet viewport (768x1024)", - "ok": false, - "tags": [], - "tests": [ - { - "timeout": 60000, + "stderr": [], + "retry": 0, + "startTime": "2026-01-10T21:51:06.459Z", "annotations": [], - "expectedStatus": "passed", - "projectId": "chromium", - "projectName": "chromium", - "results": [ - { - "workerIndex": 13, - "parallelIndex": 0, - "status": "timedOut", - "duration": 61707, - "error": { - "message": "\u001b[31mTest timeout of 60000ms exceeded while running \"beforeEach\" hook.\u001b[39m", - "stack": "\u001b[31mTest timeout of 60000ms exceeded while running \"beforeEach\" hook.\u001b[39m\n at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:640:10", - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 10, - "line": 640 - }, - "snippet": "\u001b[0m \u001b[90m 638 |\u001b[39m test\u001b[33m.\u001b[39mdescribe(\u001b[32m'8. Responsive Design'\u001b[39m\u001b[33m,\u001b[39m () \u001b[33m=>\u001b[39m {\n \u001b[90m 639 |\u001b[39m \n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 640 |\u001b[39m test\u001b[33m.\u001b[39mbeforeEach(\u001b[36masync\u001b[39m ({ page }) \u001b[33m=>\u001b[39m {\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 641 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mgoto(\u001b[32m`${TEST_CONFIG.FRONTEND_URL}/login`\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 642 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"email\"], input[name=\"email\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39memail)\u001b[33m;\u001b[39m\n \u001b[90m 643 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"password\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39mpassword)\u001b[33m;\u001b[39m\u001b[0m" - }, - "errors": [ - { - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 10, - "line": 640 - }, - "message": "\u001b[31mTest timeout of 60000ms exceeded while running \"beforeEach\" hook.\u001b[39m\n\n\u001b[0m \u001b[90m 638 |\u001b[39m test\u001b[33m.\u001b[39mdescribe(\u001b[32m'8. Responsive Design'\u001b[39m\u001b[33m,\u001b[39m () \u001b[33m=>\u001b[39m {\n \u001b[90m 639 |\u001b[39m \n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 640 |\u001b[39m test\u001b[33m.\u001b[39mbeforeEach(\u001b[36masync\u001b[39m ({ page }) \u001b[33m=>\u001b[39m {\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 641 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mgoto(\u001b[32m`${TEST_CONFIG.FRONTEND_URL}/login`\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 642 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"email\"], input[name=\"email\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39memail)\u001b[33m;\u001b[39m\n \u001b[90m 643 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"password\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39mpassword)\u001b[33m;\u001b[39m\u001b[0m\n\u001b[2m at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:640:10\u001b[22m" - }, - { - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 18, - "line": 642 - }, - "message": "Error: page.fill: Test timeout of 60000ms exceeded.\nCall log:\n\u001b[2m - waiting for locator('input[type=\"email\"], input[name=\"email\"]')\u001b[22m\n\n\n\u001b[0m \u001b[90m 640 |\u001b[39m test\u001b[33m.\u001b[39mbeforeEach(\u001b[36masync\u001b[39m ({ page }) \u001b[33m=>\u001b[39m {\n \u001b[90m 641 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mgoto(\u001b[32m`${TEST_CONFIG.FRONTEND_URL}/login`\u001b[39m)\u001b[33m;\u001b[39m\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 642 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"email\"], input[name=\"email\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39memail)\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 643 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"password\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39mpassword)\u001b[33m;\u001b[39m\n \u001b[90m 644 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mclick(\u001b[32m'button[type=\"submit\"]'\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 645 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mwaitForURL(\u001b[35m/\\/(dashboard|home|app)/\u001b[39m\u001b[33m,\u001b[39m { timeout\u001b[33m:\u001b[39m \u001b[35m15000\u001b[39m })\u001b[33m;\u001b[39m\u001b[0m\n\u001b[2m at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:642:18\u001b[22m" - } - ], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:27:49.470Z", - "annotations": [], - "attachments": [ - { - "name": "screenshot", - "contentType": "image/png", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-c81b2---Tablet-viewport-768x1024--chromium/test-failed-1.png" - }, - { - "name": "video", - "contentType": "video/webm", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-c81b2---Tablet-viewport-768x1024--chromium/video.webm" - }, - { - "name": "error-context", - "contentType": "text/markdown", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-c81b2---Tablet-viewport-768x1024--chromium/error-context.md" - } - ], - "errorLocation": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 10, - "line": 640 - } - } - ], - "status": "unexpected" + "attachments": [] } ], - "id": "c50e3d8c82f89a880019-478fa28143116d8e9e21", - "file": "mvp-integration.spec.ts", - "line": 658, - "column": 5 - }, - { - "title": "8.3 - Desktop viewport (1920x1080)", - "ok": false, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "chromium", - "projectName": "chromium", - "results": [ - { - "workerIndex": 14, - "parallelIndex": 0, - "status": "timedOut", - "duration": 61675, - "error": { - "message": "\u001b[31mTest timeout of 60000ms exceeded while running \"beforeEach\" hook.\u001b[39m", - "stack": "\u001b[31mTest timeout of 60000ms exceeded while running \"beforeEach\" hook.\u001b[39m\n at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:640:10", - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 10, - "line": 640 - }, - "snippet": "\u001b[0m \u001b[90m 638 |\u001b[39m test\u001b[33m.\u001b[39mdescribe(\u001b[32m'8. Responsive Design'\u001b[39m\u001b[33m,\u001b[39m () \u001b[33m=>\u001b[39m {\n \u001b[90m 639 |\u001b[39m \n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 640 |\u001b[39m test\u001b[33m.\u001b[39mbeforeEach(\u001b[36masync\u001b[39m ({ page }) \u001b[33m=>\u001b[39m {\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 641 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mgoto(\u001b[32m`${TEST_CONFIG.FRONTEND_URL}/login`\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 642 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"email\"], input[name=\"email\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39memail)\u001b[33m;\u001b[39m\n \u001b[90m 643 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"password\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39mpassword)\u001b[33m;\u001b[39m\u001b[0m" - }, - "errors": [ - { - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 10, - "line": 640 - }, - "message": "\u001b[31mTest timeout of 60000ms exceeded while running \"beforeEach\" hook.\u001b[39m\n\n\u001b[0m \u001b[90m 638 |\u001b[39m test\u001b[33m.\u001b[39mdescribe(\u001b[32m'8. Responsive Design'\u001b[39m\u001b[33m,\u001b[39m () \u001b[33m=>\u001b[39m {\n \u001b[90m 639 |\u001b[39m \n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 640 |\u001b[39m test\u001b[33m.\u001b[39mbeforeEach(\u001b[36masync\u001b[39m ({ page }) \u001b[33m=>\u001b[39m {\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 641 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mgoto(\u001b[32m`${TEST_CONFIG.FRONTEND_URL}/login`\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 642 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"email\"], input[name=\"email\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39memail)\u001b[33m;\u001b[39m\n \u001b[90m 643 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"password\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39mpassword)\u001b[33m;\u001b[39m\u001b[0m\n\u001b[2m at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:640:10\u001b[22m" - }, - { - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 18, - "line": 642 - }, - "message": "Error: page.fill: Test timeout of 60000ms exceeded.\nCall log:\n\u001b[2m - waiting for locator('input[type=\"email\"], input[name=\"email\"]')\u001b[22m\n\n\n\u001b[0m \u001b[90m 640 |\u001b[39m test\u001b[33m.\u001b[39mbeforeEach(\u001b[36masync\u001b[39m ({ page }) \u001b[33m=>\u001b[39m {\n \u001b[90m 641 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mgoto(\u001b[32m`${TEST_CONFIG.FRONTEND_URL}/login`\u001b[39m)\u001b[33m;\u001b[39m\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 642 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"email\"], input[name=\"email\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39memail)\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 643 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"password\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39mpassword)\u001b[33m;\u001b[39m\n \u001b[90m 644 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mclick(\u001b[32m'button[type=\"submit\"]'\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 645 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mwaitForURL(\u001b[35m/\\/(dashboard|home|app)/\u001b[39m\u001b[33m,\u001b[39m { timeout\u001b[33m:\u001b[39m \u001b[35m15000\u001b[39m })\u001b[33m;\u001b[39m\u001b[0m\n\u001b[2m at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:642:18\u001b[22m" - } - ], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:28:51.870Z", - "annotations": [], - "attachments": [ - { - "name": "screenshot", - "contentType": "image/png", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-c9b49-Desktop-viewport-1920x1080--chromium/test-failed-1.png" - }, - { - "name": "video", - "contentType": "video/webm", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-c9b49-Desktop-viewport-1920x1080--chromium/video.webm" - }, - { - "name": "error-context", - "contentType": "text/markdown", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-c9b49-Desktop-viewport-1920x1080--chromium/error-context.md" - } - ], - "errorLocation": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 10, - "line": 640 - } - } - ], - "status": "unexpected" - } - ], - "id": "c50e3d8c82f89a880019-f6b77f1b142982acb686", - "file": "mvp-integration.spec.ts", - "line": 667, - "column": 5 - }, - { - "title": "8.1 - Mobile viewport (375x667)", - "ok": false, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "firefox", - "projectName": "firefox", - "results": [ - { - "workerIndex": 27, - "parallelIndex": 0, - "status": "failed", - "duration": 1299, - "error": { - "message": "Error: page.waitForURL: NS_ERROR_FAILURE\n=========================== logs ===========================\nwaiting for navigation until \"load\"\n============================================================", - "stack": "Error: page.waitForURL: NS_ERROR_FAILURE\n=========================== logs ===========================\nwaiting for navigation until \"load\"\n============================================================\n at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:645:18", - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 18, - "line": 645 - }, - "snippet": "\u001b[0m \u001b[90m 643 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"password\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39mpassword)\u001b[33m;\u001b[39m\n \u001b[90m 644 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mclick(\u001b[32m'button[type=\"submit\"]'\u001b[39m)\u001b[33m;\u001b[39m\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 645 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mwaitForURL(\u001b[35m/\\/(dashboard|home|app)/\u001b[39m\u001b[33m,\u001b[39m { timeout\u001b[33m:\u001b[39m \u001b[35m15000\u001b[39m })\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 646 |\u001b[39m })\u001b[33m;\u001b[39m\n \u001b[90m 647 |\u001b[39m\n \u001b[90m 648 |\u001b[39m test(\u001b[32m'8.1 - Mobile viewport (375x667)'\u001b[39m\u001b[33m,\u001b[39m \u001b[36masync\u001b[39m ({ page }) \u001b[33m=>\u001b[39m {\u001b[0m" - }, - "errors": [ - { - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 18, - "line": 645 - }, - "message": "Error: page.waitForURL: NS_ERROR_FAILURE\n=========================== logs ===========================\nwaiting for navigation until \"load\"\n============================================================\n\n\u001b[0m \u001b[90m 643 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"password\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39mpassword)\u001b[33m;\u001b[39m\n \u001b[90m 644 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mclick(\u001b[32m'button[type=\"submit\"]'\u001b[39m)\u001b[33m;\u001b[39m\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 645 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mwaitForURL(\u001b[35m/\\/(dashboard|home|app)/\u001b[39m\u001b[33m,\u001b[39m { timeout\u001b[33m:\u001b[39m \u001b[35m15000\u001b[39m })\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 646 |\u001b[39m })\u001b[33m;\u001b[39m\n \u001b[90m 647 |\u001b[39m\n \u001b[90m 648 |\u001b[39m test(\u001b[32m'8.1 - Mobile viewport (375x667)'\u001b[39m\u001b[33m,\u001b[39m \u001b[36masync\u001b[39m ({ page }) \u001b[33m=>\u001b[39m {\u001b[0m\n\u001b[2m at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:645:18\u001b[22m" - } - ], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:34:18.120Z", - "annotations": [], - "attachments": [ - { - "name": "screenshot", - "contentType": "image/png", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-299e0----Mobile-viewport-375x667--firefox/test-failed-1.png" - }, - { - "name": "video", - "contentType": "video/webm", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-299e0----Mobile-viewport-375x667--firefox/video.webm" - } - ], - "errorLocation": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 18, - "line": 645 - } - } - ], - "status": "unexpected" - } - ], - "id": "c50e3d8c82f89a880019-bec7aa72d785fcd47483", - "file": "mvp-integration.spec.ts", - "line": 648, - "column": 5 - }, - { - "title": "8.2 - Tablet viewport (768x1024)", - "ok": false, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "firefox", - "projectName": "firefox", - "results": [ - { - "workerIndex": 28, - "parallelIndex": 0, - "status": "failed", - "duration": 16709, - "error": { - "message": "TimeoutError: page.waitForURL: Timeout 15000ms exceeded.\n=========================== logs ===========================\nwaiting for navigation until \"load\"\n navigated to \"http://localhost:5173/login\"\n navigated to \"http://localhost:5173/login\"\n============================================================", - "stack": "TimeoutError: page.waitForURL: Timeout 15000ms exceeded.\n=========================== logs ===========================\nwaiting for navigation until \"load\"\n navigated to \"http://localhost:5173/login\"\n navigated to \"http://localhost:5173/login\"\n============================================================\n at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:645:18", - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 18, - "line": 645 - }, - "snippet": "\u001b[0m \u001b[90m 643 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"password\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39mpassword)\u001b[33m;\u001b[39m\n \u001b[90m 644 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mclick(\u001b[32m'button[type=\"submit\"]'\u001b[39m)\u001b[33m;\u001b[39m\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 645 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mwaitForURL(\u001b[35m/\\/(dashboard|home|app)/\u001b[39m\u001b[33m,\u001b[39m { timeout\u001b[33m:\u001b[39m \u001b[35m15000\u001b[39m })\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 646 |\u001b[39m })\u001b[33m;\u001b[39m\n \u001b[90m 647 |\u001b[39m\n \u001b[90m 648 |\u001b[39m test(\u001b[32m'8.1 - Mobile viewport (375x667)'\u001b[39m\u001b[33m,\u001b[39m \u001b[36masync\u001b[39m ({ page }) \u001b[33m=>\u001b[39m {\u001b[0m" - }, - "errors": [ - { - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 18, - "line": 645 - }, - "message": "TimeoutError: page.waitForURL: Timeout 15000ms exceeded.\n=========================== logs ===========================\nwaiting for navigation until \"load\"\n navigated to \"http://localhost:5173/login\"\n navigated to \"http://localhost:5173/login\"\n============================================================\n\n\u001b[0m \u001b[90m 643 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"password\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39mpassword)\u001b[33m;\u001b[39m\n \u001b[90m 644 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mclick(\u001b[32m'button[type=\"submit\"]'\u001b[39m)\u001b[33m;\u001b[39m\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 645 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mwaitForURL(\u001b[35m/\\/(dashboard|home|app)/\u001b[39m\u001b[33m,\u001b[39m { timeout\u001b[33m:\u001b[39m \u001b[35m15000\u001b[39m })\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 646 |\u001b[39m })\u001b[33m;\u001b[39m\n \u001b[90m 647 |\u001b[39m\n \u001b[90m 648 |\u001b[39m test(\u001b[32m'8.1 - Mobile viewport (375x667)'\u001b[39m\u001b[33m,\u001b[39m \u001b[36masync\u001b[39m ({ page }) \u001b[33m=>\u001b[39m {\u001b[0m\n\u001b[2m at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:645:18\u001b[22m" - } - ], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:34:20.370Z", - "annotations": [], - "attachments": [ - { - "name": "screenshot", - "contentType": "image/png", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-c81b2---Tablet-viewport-768x1024--firefox/test-failed-1.png" - }, - { - "name": "video", - "contentType": "video/webm", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-c81b2---Tablet-viewport-768x1024--firefox/video.webm" - }, - { - "name": "error-context", - "contentType": "text/markdown", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-c81b2---Tablet-viewport-768x1024--firefox/error-context.md" - } - ], - "errorLocation": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 18, - "line": 645 - } - } - ], - "status": "unexpected" - } - ], - "id": "c50e3d8c82f89a880019-1560edf6cc4a7287533e", - "file": "mvp-integration.spec.ts", - "line": 658, - "column": 5 - }, - { - "title": "8.3 - Desktop viewport (1920x1080)", - "ok": false, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "firefox", - "projectName": "firefox", - "results": [ - { - "workerIndex": 29, - "parallelIndex": 0, - "status": "failed", - "duration": 16717, - "error": { - "message": "TimeoutError: page.waitForURL: Timeout 15000ms exceeded.\n=========================== logs ===========================\nwaiting for navigation until \"load\"\n navigated to \"http://localhost:5173/login\"\n navigated to \"http://localhost:5173/login\"\n============================================================", - "stack": "TimeoutError: page.waitForURL: Timeout 15000ms exceeded.\n=========================== logs ===========================\nwaiting for navigation until \"load\"\n navigated to \"http://localhost:5173/login\"\n navigated to \"http://localhost:5173/login\"\n============================================================\n at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:645:18", - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 18, - "line": 645 - }, - "snippet": "\u001b[0m \u001b[90m 643 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"password\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39mpassword)\u001b[33m;\u001b[39m\n \u001b[90m 644 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mclick(\u001b[32m'button[type=\"submit\"]'\u001b[39m)\u001b[33m;\u001b[39m\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 645 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mwaitForURL(\u001b[35m/\\/(dashboard|home|app)/\u001b[39m\u001b[33m,\u001b[39m { timeout\u001b[33m:\u001b[39m \u001b[35m15000\u001b[39m })\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 646 |\u001b[39m })\u001b[33m;\u001b[39m\n \u001b[90m 647 |\u001b[39m\n \u001b[90m 648 |\u001b[39m test(\u001b[32m'8.1 - Mobile viewport (375x667)'\u001b[39m\u001b[33m,\u001b[39m \u001b[36masync\u001b[39m ({ page }) \u001b[33m=>\u001b[39m {\u001b[0m" - }, - "errors": [ - { - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 18, - "line": 645 - }, - "message": "TimeoutError: page.waitForURL: Timeout 15000ms exceeded.\n=========================== logs ===========================\nwaiting for navigation until \"load\"\n navigated to \"http://localhost:5173/login\"\n navigated to \"http://localhost:5173/login\"\n============================================================\n\n\u001b[0m \u001b[90m 643 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"password\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39mpassword)\u001b[33m;\u001b[39m\n \u001b[90m 644 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mclick(\u001b[32m'button[type=\"submit\"]'\u001b[39m)\u001b[33m;\u001b[39m\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 645 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mwaitForURL(\u001b[35m/\\/(dashboard|home|app)/\u001b[39m\u001b[33m,\u001b[39m { timeout\u001b[33m:\u001b[39m \u001b[35m15000\u001b[39m })\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 646 |\u001b[39m })\u001b[33m;\u001b[39m\n \u001b[90m 647 |\u001b[39m\n \u001b[90m 648 |\u001b[39m test(\u001b[32m'8.1 - Mobile viewport (375x667)'\u001b[39m\u001b[33m,\u001b[39m \u001b[36masync\u001b[39m ({ page }) \u001b[33m=>\u001b[39m {\u001b[0m\n\u001b[2m at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:645:18\u001b[22m" - } - ], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:34:38.384Z", - "annotations": [], - "attachments": [ - { - "name": "screenshot", - "contentType": "image/png", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-c9b49-Desktop-viewport-1920x1080--firefox/test-failed-1.png" - }, - { - "name": "video", - "contentType": "video/webm", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-c9b49-Desktop-viewport-1920x1080--firefox/video.webm" - }, - { - "name": "error-context", - "contentType": "text/markdown", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-c9b49-Desktop-viewport-1920x1080--firefox/error-context.md" - } - ], - "errorLocation": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 18, - "line": 645 - } - } - ], - "status": "unexpected" - } - ], - "id": "c50e3d8c82f89a880019-7eee3a86331fe7867c3c", - "file": "mvp-integration.spec.ts", - "line": 667, - "column": 5 - }, - { - "title": "8.1 - Mobile viewport (375x667)", - "ok": false, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "webkit", - "projectName": "webkit", - "results": [ - { - "workerIndex": 49, - "parallelIndex": 0, - "status": "failed", - "duration": 2, - "error": { - "message": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝", - "stack": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝" - }, - "errors": [ - { - "message": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝" - } - ], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:35:10.198Z", - "annotations": [], - "attachments": [] - } - ], - "status": "unexpected" - } - ], - "id": "c50e3d8c82f89a880019-996a6d0b75012ff27755", - "file": "mvp-integration.spec.ts", - "line": 648, - "column": 5 - }, - { - "title": "8.2 - Tablet viewport (768x1024)", - "ok": false, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "webkit", - "projectName": "webkit", - "results": [ - { - "workerIndex": 50, - "parallelIndex": 0, - "status": "failed", - "duration": 2, - "error": { - "message": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝", - "stack": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝" - }, - "errors": [ - { - "message": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝" - } - ], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:35:10.914Z", - "annotations": [], - "attachments": [] - } - ], - "status": "unexpected" - } - ], - "id": "c50e3d8c82f89a880019-725f24caa87a4419e94a", - "file": "mvp-integration.spec.ts", - "line": 658, - "column": 5 - }, - { - "title": "8.3 - Desktop viewport (1920x1080)", - "ok": false, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "webkit", - "projectName": "webkit", - "results": [ - { - "workerIndex": 51, - "parallelIndex": 0, - "status": "failed", - "duration": 2, - "error": { - "message": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝", - "stack": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝" - }, - "errors": [ - { - "message": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝" - } - ], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:35:11.640Z", - "annotations": [], - "attachments": [] - } - ], - "status": "unexpected" - } - ], - "id": "c50e3d8c82f89a880019-50f602f03238fa0c3494", - "file": "mvp-integration.spec.ts", - "line": 667, - "column": 5 - }, - { - "title": "8.1 - Mobile viewport (375x667)", - "ok": false, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "msedge", - "projectName": "msedge", - "results": [ - { - "workerIndex": 56, - "parallelIndex": 0, - "status": "failed", - "duration": 16333, - "error": { - "message": "TimeoutError: page.waitForURL: Timeout 15000ms exceeded.\n=========================== logs ===========================\nwaiting for navigation until \"load\"\n navigated to \"http://localhost:5173/login\"\n============================================================", - "stack": "TimeoutError: page.waitForURL: Timeout 15000ms exceeded.\n=========================== logs ===========================\nwaiting for navigation until \"load\"\n navigated to \"http://localhost:5173/login\"\n============================================================\n at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:645:18", - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 18, - "line": 645 - }, - "snippet": "\u001b[0m \u001b[90m 643 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"password\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39mpassword)\u001b[33m;\u001b[39m\n \u001b[90m 644 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mclick(\u001b[32m'button[type=\"submit\"]'\u001b[39m)\u001b[33m;\u001b[39m\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 645 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mwaitForURL(\u001b[35m/\\/(dashboard|home|app)/\u001b[39m\u001b[33m,\u001b[39m { timeout\u001b[33m:\u001b[39m \u001b[35m15000\u001b[39m })\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 646 |\u001b[39m })\u001b[33m;\u001b[39m\n \u001b[90m 647 |\u001b[39m\n \u001b[90m 648 |\u001b[39m test(\u001b[32m'8.1 - Mobile viewport (375x667)'\u001b[39m\u001b[33m,\u001b[39m \u001b[36masync\u001b[39m ({ page }) \u001b[33m=>\u001b[39m {\u001b[0m" - }, - "errors": [ - { - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 18, - "line": 645 - }, - "message": "TimeoutError: page.waitForURL: Timeout 15000ms exceeded.\n=========================== logs ===========================\nwaiting for navigation until \"load\"\n navigated to \"http://localhost:5173/login\"\n============================================================\n\n\u001b[0m \u001b[90m 643 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"password\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39mpassword)\u001b[33m;\u001b[39m\n \u001b[90m 644 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mclick(\u001b[32m'button[type=\"submit\"]'\u001b[39m)\u001b[33m;\u001b[39m\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 645 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mwaitForURL(\u001b[35m/\\/(dashboard|home|app)/\u001b[39m\u001b[33m,\u001b[39m { timeout\u001b[33m:\u001b[39m \u001b[35m15000\u001b[39m })\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 646 |\u001b[39m })\u001b[33m;\u001b[39m\n \u001b[90m 647 |\u001b[39m\n \u001b[90m 648 |\u001b[39m test(\u001b[32m'8.1 - Mobile viewport (375x667)'\u001b[39m\u001b[33m,\u001b[39m \u001b[36masync\u001b[39m ({ page }) \u001b[33m=>\u001b[39m {\u001b[0m\n\u001b[2m at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:645:18\u001b[22m" - } - ], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:35:39.444Z", - "annotations": [], - "attachments": [ - { - "name": "screenshot", - "contentType": "image/png", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-299e0----Mobile-viewport-375x667--msedge/test-failed-1.png" - }, - { - "name": "video", - "contentType": "video/webm", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-299e0----Mobile-viewport-375x667--msedge/video.webm" - }, - { - "name": "error-context", - "contentType": "text/markdown", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-299e0----Mobile-viewport-375x667--msedge/error-context.md" - } - ], - "errorLocation": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 18, - "line": 645 - } - } - ], - "status": "unexpected" - } - ], - "id": "c50e3d8c82f89a880019-7aea3d0e8727f9a28654", - "file": "mvp-integration.spec.ts", - "line": 648, - "column": 5 - }, - { - "title": "8.2 - Tablet viewport (768x1024)", - "ok": false, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "msedge", - "projectName": "msedge", - "results": [ - { - "workerIndex": 57, - "parallelIndex": 0, - "status": "failed", - "duration": 16449, - "error": { - "message": "TimeoutError: page.waitForURL: Timeout 15000ms exceeded.\n=========================== logs ===========================\nwaiting for navigation until \"load\"\n navigated to \"http://localhost:5173/login\"\n============================================================", - "stack": "TimeoutError: page.waitForURL: Timeout 15000ms exceeded.\n=========================== logs ===========================\nwaiting for navigation until \"load\"\n navigated to \"http://localhost:5173/login\"\n============================================================\n at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:645:18", - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 18, - "line": 645 - }, - "snippet": "\u001b[0m \u001b[90m 643 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"password\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39mpassword)\u001b[33m;\u001b[39m\n \u001b[90m 644 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mclick(\u001b[32m'button[type=\"submit\"]'\u001b[39m)\u001b[33m;\u001b[39m\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 645 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mwaitForURL(\u001b[35m/\\/(dashboard|home|app)/\u001b[39m\u001b[33m,\u001b[39m { timeout\u001b[33m:\u001b[39m \u001b[35m15000\u001b[39m })\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 646 |\u001b[39m })\u001b[33m;\u001b[39m\n \u001b[90m 647 |\u001b[39m\n \u001b[90m 648 |\u001b[39m test(\u001b[32m'8.1 - Mobile viewport (375x667)'\u001b[39m\u001b[33m,\u001b[39m \u001b[36masync\u001b[39m ({ page }) \u001b[33m=>\u001b[39m {\u001b[0m" - }, - "errors": [ - { - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 18, - "line": 645 - }, - "message": "TimeoutError: page.waitForURL: Timeout 15000ms exceeded.\n=========================== logs ===========================\nwaiting for navigation until \"load\"\n navigated to \"http://localhost:5173/login\"\n============================================================\n\n\u001b[0m \u001b[90m 643 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"password\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39mpassword)\u001b[33m;\u001b[39m\n \u001b[90m 644 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mclick(\u001b[32m'button[type=\"submit\"]'\u001b[39m)\u001b[33m;\u001b[39m\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 645 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mwaitForURL(\u001b[35m/\\/(dashboard|home|app)/\u001b[39m\u001b[33m,\u001b[39m { timeout\u001b[33m:\u001b[39m \u001b[35m15000\u001b[39m })\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 646 |\u001b[39m })\u001b[33m;\u001b[39m\n \u001b[90m 647 |\u001b[39m\n \u001b[90m 648 |\u001b[39m test(\u001b[32m'8.1 - Mobile viewport (375x667)'\u001b[39m\u001b[33m,\u001b[39m \u001b[36masync\u001b[39m ({ page }) \u001b[33m=>\u001b[39m {\u001b[0m\n\u001b[2m at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:645:18\u001b[22m" - } - ], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:35:56.359Z", - "annotations": [], - "attachments": [ - { - "name": "screenshot", - "contentType": "image/png", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-c81b2---Tablet-viewport-768x1024--msedge/test-failed-1.png" - }, - { - "name": "video", - "contentType": "video/webm", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-c81b2---Tablet-viewport-768x1024--msedge/video.webm" - }, - { - "name": "error-context", - "contentType": "text/markdown", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-c81b2---Tablet-viewport-768x1024--msedge/error-context.md" - } - ], - "errorLocation": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 18, - "line": 645 - } - } - ], - "status": "unexpected" - } - ], - "id": "c50e3d8c82f89a880019-66e7c8b0bfaf038f253d", - "file": "mvp-integration.spec.ts", - "line": 658, - "column": 5 - }, - { - "title": "8.3 - Desktop viewport (1920x1080)", - "ok": false, - "tags": [], - "tests": [ - { - "timeout": 60000, - "annotations": [], - "expectedStatus": "passed", - "projectId": "msedge", - "projectName": "msedge", - "results": [ - { - "workerIndex": 58, - "parallelIndex": 0, - "status": "failed", - "duration": 16337, - "error": { - "message": "TimeoutError: page.waitForURL: Timeout 15000ms exceeded.\n=========================== logs ===========================\nwaiting for navigation until \"load\"\n navigated to \"http://localhost:5173/login\"\n============================================================", - "stack": "TimeoutError: page.waitForURL: Timeout 15000ms exceeded.\n=========================== logs ===========================\nwaiting for navigation until \"load\"\n navigated to \"http://localhost:5173/login\"\n============================================================\n at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:645:18", - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 18, - "line": 645 - }, - "snippet": "\u001b[0m \u001b[90m 643 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"password\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39mpassword)\u001b[33m;\u001b[39m\n \u001b[90m 644 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mclick(\u001b[32m'button[type=\"submit\"]'\u001b[39m)\u001b[33m;\u001b[39m\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 645 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mwaitForURL(\u001b[35m/\\/(dashboard|home|app)/\u001b[39m\u001b[33m,\u001b[39m { timeout\u001b[33m:\u001b[39m \u001b[35m15000\u001b[39m })\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 646 |\u001b[39m })\u001b[33m;\u001b[39m\n \u001b[90m 647 |\u001b[39m\n \u001b[90m 648 |\u001b[39m test(\u001b[32m'8.1 - Mobile viewport (375x667)'\u001b[39m\u001b[33m,\u001b[39m \u001b[36masync\u001b[39m ({ page }) \u001b[33m=>\u001b[39m {\u001b[0m" - }, - "errors": [ - { - "location": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 18, - "line": 645 - }, - "message": "TimeoutError: page.waitForURL: Timeout 15000ms exceeded.\n=========================== logs ===========================\nwaiting for navigation until \"load\"\n navigated to \"http://localhost:5173/login\"\n============================================================\n\n\u001b[0m \u001b[90m 643 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mfill(\u001b[32m'input[type=\"password\"]'\u001b[39m\u001b[33m,\u001b[39m \u001b[33mTEST_USER\u001b[39m\u001b[33m.\u001b[39mpassword)\u001b[33m;\u001b[39m\n \u001b[90m 644 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mclick(\u001b[32m'button[type=\"submit\"]'\u001b[39m)\u001b[33m;\u001b[39m\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 645 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mwaitForURL(\u001b[35m/\\/(dashboard|home|app)/\u001b[39m\u001b[33m,\u001b[39m { timeout\u001b[33m:\u001b[39m \u001b[35m15000\u001b[39m })\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 646 |\u001b[39m })\u001b[33m;\u001b[39m\n \u001b[90m 647 |\u001b[39m\n \u001b[90m 648 |\u001b[39m test(\u001b[32m'8.1 - Mobile viewport (375x667)'\u001b[39m\u001b[33m,\u001b[39m \u001b[36masync\u001b[39m ({ page }) \u001b[33m=>\u001b[39m {\u001b[0m\n\u001b[2m at /home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts:645:18\u001b[22m" - } - ], - "stdout": [], - "stderr": [], - "retry": 0, - "startTime": "2026-01-03T20:36:13.461Z", - "annotations": [], - "attachments": [ - { - "name": "screenshot", - "contentType": "image/png", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-c9b49-Desktop-viewport-1920x1080--msedge/test-failed-1.png" - }, - { - "name": "video", - "contentType": "video/webm", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-c9b49-Desktop-viewport-1920x1080--msedge/video.webm" - }, - { - "name": "error-context", - "contentType": "text/markdown", - "path": "/home/senke/git/talas/veza/apps/web/test-results/mvp-integration-MVP-Integr-c9b49-Desktop-viewport-1920x1080--msedge/error-context.md" - } - ], - "errorLocation": { - "file": "/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts", - "column": 18, - "line": 645 - } - } - ], - "status": "unexpected" - } - ], - "id": "c50e3d8c82f89a880019-24ffdadc4a1a30e0002e", - "file": "mvp-integration.spec.ts", - "line": 667, - "column": 5 + "status": "expected" } - ] + ], + "id": "d748ac400d08b85935ef-73ae53ed4fd70aaa00c2", + "file": "auth.spec.ts", + "line": 452, + "column": 3 + }, + { + "title": "should show error when passwords do not match during registration", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 60000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "chromium", + "projectName": "chromium", + "results": [ + { + "workerIndex": 0, + "parallelIndex": 0, + "status": "passed", + "duration": 4310, + "errors": [], + "stdout": [ + { + "text": "🧪 [AUTH TEST] Running: Password mismatch validation\n" + }, + { + "text": "✏️ [FILL] Filling field input[name=\"email\"], input#email with value: newuser@example.com\n" + }, + { + "text": "✅ [FILL] Field input[name=\"email\"], input#email filled successfully\n" + }, + { + "text": "✏️ [FILL] Filling field input[name=\"password\"], input#password with value: Password123456!\n" + }, + { + "text": "✅ [FILL] Field input[name=\"password\"], input#password filled successfully\n" + }, + { + "text": "✏️ [FILL] Filling field input[name=\"passwordConfirm\"], input[name=\"password_confirm\"], input[name=\"confirmPassword\"] with value: DifferentPassword!\n" + }, + { + "text": "✅ [FILL] Field input[name=\"passwordConfirm\"], input[name=\"password_confirm\"], input[name=\"confirmPassword\"] filled successfully\n" + }, + { + "text": "⚡ [FORM SUBMIT] Forcing submission of form: form\n" + }, + { + "text": "🔍 [FORM SUBMIT] Waiting for form selector: form\n" + }, + { + "text": "⏳ [FORM SUBMIT] Waiting for React to update state...\n" + }, + { + "text": "🚀 [FORM SUBMIT] Submitting form...\n" + }, + { + "text": "✅ [FORM SUBMIT] Form form submitted successfully\n" + }, + { + "text": "✅ [AUTH TEST] Password mismatch error shown (found by text)\n" + }, + { + "text": "\n📊 [AUTH TEST] === Final Verifications ===\n" + }, + { + "text": "✅ [AUTH TEST] No console errors\n" + }, + { + "text": "✅ [AUTH TEST] No network errors\n" + } + ], + "stderr": [], + "retry": 0, + "startTime": "2026-01-10T21:51:09.890Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "d748ac400d08b85935ef-184539997e56a19b11c0", + "file": "auth.spec.ts", + "line": 509, + "column": 3 + }, + { + "title": "should login successfully with valid credentials", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 60000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "firefox", + "projectName": "firefox", + "results": [ + { + "workerIndex": 1, + "parallelIndex": 0, + "status": "passed", + "duration": 5503, + "errors": [], + "stdout": [ + { + "text": "🧪 [AUTH TEST] Running: Login with valid credentials\n" + }, + { + "text": "✏️ [FILL] Filling field input[type=\"email\"], input[name=\"email\"] with value: e2e@test.com\n" + }, + { + "text": "✅ [FILL] Field input[type=\"email\"], input[name=\"email\"] filled successfully\n" + }, + { + "text": "✏️ [FILL] Filling field input[type=\"password\"], input[name=\"password\"] with value: Xk9$mP2#vL7@nQ4!wR8\n" + }, + { + "text": "✅ [FILL] Field input[type=\"password\"], input[name=\"password\"] filled successfully\n" + }, + { + "text": "⚡ [FORM SUBMIT] Forcing submission of form: form\n" + }, + { + "text": "🔍 [FORM SUBMIT] Waiting for form selector: form\n" + }, + { + "text": "⏳ [FORM SUBMIT] Waiting for React to update state...\n" + }, + { + "text": "🚀 [FORM SUBMIT] Submitting form...\n" + }, + { + "text": "✅ [FORM SUBMIT] Form form submitted successfully\n" + }, + { + "text": "🔴 [CONSOLE ERROR] [JavaScript Error: \"Cookie “refresh_token” has been rejected because it is in a cross-site context and its “SameSite” is “Lax” or “Strict”.\" {file: \"http://127.0.0.1:8080/api/v1/auth/login\" line: 0}]\n" + }, + { + "text": "⏳ [AUTH TEST] Waiting for Zustand to persist auth-storage...\n" + }, + { + "text": " ✅ TOKEN FOUND: eyJhbGciOiJIUzI1NiIsInR5cCI6Ik... (source: storage)\n" + }, + { + "text": "✅ [AUTH TEST] Login successful (token in storage)\n" + }, + { + "text": "\n📊 [AUTH TEST] === Final Verifications ===\n" + }, + { + "text": "🔴 [AUTH TEST] Console errors (1):\n" + }, + { + "text": " - [JavaScript Error: \"Cookie “refresh_token” has been rejected because it is in a cross-site context and its “SameSite” is “Lax” or “Strict”.\" {file: \"http://127.0.0.1:8080/api/v1/auth/login\" line: 0}]\n" + }, + { + "text": "✅ [AUTH TEST] No network errors\n" + } + ], + "stderr": [ + { + "text": "⚠️ [AUTH TEST] Test passed but had console errors\n" + } + ], + "retry": 0, + "startTime": "2026-01-10T21:51:15.269Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "d748ac400d08b85935ef-14d0e4fdf8b73cd887ae", + "file": "auth.spec.ts", + "line": 41, + "column": 3 + }, + { + "title": "should show error with invalid credentials", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 60000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "firefox", + "projectName": "firefox", + "results": [ + { + "workerIndex": 1, + "parallelIndex": 0, + "status": "passed", + "duration": 1858, + "errors": [], + "stdout": [ + { + "text": "🧪 [AUTH TEST] Running: Login with invalid credentials\n" + }, + { + "text": "✏️ [FILL] Filling field input[type=\"email\"], input[name=\"email\"] with value: wrong@example.com\n" + }, + { + "text": "✅ [FILL] Field input[type=\"email\"], input[name=\"email\"] filled successfully\n" + }, + { + "text": "✏️ [FILL] Filling field input[type=\"password\"], input[name=\"password\"] with value: wrongpassword\n" + }, + { + "text": "✅ [FILL] Field input[type=\"password\"], input[name=\"password\"] filled successfully\n" + }, + { + "text": "⚡ [FORM SUBMIT] Forcing submission of form: form\n" + }, + { + "text": "🔍 [FORM SUBMIT] Waiting for form selector: form\n" + }, + { + "text": "⏳ [FORM SUBMIT] Waiting for React to update state...\n" + }, + { + "text": "🚀 [FORM SUBMIT] Submitting form...\n" + }, + { + "text": "✅ [FORM SUBMIT] Form form submitted successfully\n" + }, + { + "text": "🔔 [TOAST] Waiting for error message...\n" + }, + { + "text": "🔴 [NETWORK ERROR] POST http://127.0.0.1:8080/api/v1/auth/login: 423\n" + }, + { + "text": "✅ [TOAST] error message: Account is locked. Please try again later.\n" + }, + { + "text": "✅ [AUTH TEST] Error shown for invalid credentials\n" + }, + { + "text": "\n📊 [AUTH TEST] === Final Verifications ===\n" + }, + { + "text": "✅ [AUTH TEST] No console errors\n" + }, + { + "text": "🔴 [AUTH TEST] Network errors (1):\n" + }, + { + "text": " - POST http://127.0.0.1:8080/api/v1/auth/login: 423\n" + } + ], + "stderr": [], + "retry": 0, + "startTime": "2026-01-10T21:51:21.304Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "d748ac400d08b85935ef-d2604529717af9e35508", + "file": "auth.spec.ts", + "line": 107, + "column": 3 + }, + { + "title": "should register a new user successfully", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 60000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "firefox", + "projectName": "firefox", + "results": [ + { + "workerIndex": 1, + "parallelIndex": 0, + "status": "passed", + "duration": 3681, + "errors": [], + "stdout": [ + { + "text": "🧪 [AUTH TEST] Running: User registration\n" + }, + { + "text": "✏️ [FILL] Filling field input[name=\"email\"], input#email with value: test-1768081884491@example.com\n" + }, + { + "text": "✅ [FILL] Field input[name=\"email\"], input#email filled successfully\n" + }, + { + "text": "✏️ [FILL] Filling field input[name=\"username\"], input#username with value: testuser1768081884491\n" + }, + { + "text": "✅ [FILL] Field input[name=\"username\"], input#username filled successfully\n" + }, + { + "text": "✏️ [FILL] Filling field input[name=\"password\"], input#password with value: Str0ng!P@ssw0rd2024\n" + }, + { + "text": "✅ [FILL] Field input[name=\"password\"], input#password filled successfully\n" + }, + { + "text": "✏️ [FILL] Filling field input[name=\"passwordConfirm\"], input[name=\"password_confirm\"], input[name=\"confirmPassword\"], input#passwordConfirm with value: Str0ng!P@ssw0rd2024\n" + }, + { + "text": "✅ [FILL] Field input[name=\"passwordConfirm\"], input[name=\"password_confirm\"], input[name=\"confirmPassword\"], input#passwordConfirm filled successfully\n" + }, + { + "text": "⚡ [FORM SUBMIT] Forcing submission of form: form\n" + }, + { + "text": "🔍 [FORM SUBMIT] Waiting for form selector: form\n" + }, + { + "text": "⏳ [FORM SUBMIT] Waiting for React to update state...\n" + }, + { + "text": "🚀 [FORM SUBMIT] Submitting form...\n" + }, + { + "text": "✅ [FORM SUBMIT] Form form submitted successfully\n" + }, + { + "text": "🔴 [CONSOLE ERROR] [JavaScript Error: \"Cookie “refresh_token” has been rejected because it is in a cross-site context and its “SameSite” is “Lax” or “Strict”.\" {file: \"http://127.0.0.1:8080/api/v1/auth/register\" line: 0}]\n" + }, + { + "text": "✅ [AUTH TEST] Registration successful with auto-login\n" + }, + { + "text": "\n📊 [AUTH TEST] === Final Verifications ===\n" + }, + { + "text": "🔴 [AUTH TEST] Console errors (1):\n" + }, + { + "text": " - [JavaScript Error: \"Cookie “refresh_token” has been rejected because it is in a cross-site context and its “SameSite” is “Lax” or “Strict”.\" {file: \"http://127.0.0.1:8080/api/v1/auth/register\" line: 0}]\n" + }, + { + "text": "✅ [AUTH TEST] No network errors\n" + } + ], + "stderr": [ + { + "text": "⚠️ [AUTH TEST] Test passed but had console errors\n" + } + ], + "retry": 0, + "startTime": "2026-01-10T21:51:23.192Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "d748ac400d08b85935ef-e25661f5336753eaeb6e", + "file": "auth.spec.ts", + "line": 134, + "column": 3 + }, + { + "title": "should show error when registering with existing email", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 60000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "firefox", + "projectName": "firefox", + "results": [ + { + "workerIndex": 1, + "parallelIndex": 0, + "status": "passed", + "duration": 5870, + "errors": [], + "stdout": [ + { + "text": "🧪 [AUTH TEST] Running: Registration with existing email\n" + }, + { + "text": "✏️ [FILL] Filling field input[name=\"email\"], input#email with value: e2e@test.com\n" + }, + { + "text": "✅ [FILL] Field input[name=\"email\"], input#email filled successfully\n" + }, + { + "text": "✏️ [FILL] Filling field input[name=\"username\"], input#username with value: existinguser\n" + }, + { + "text": "✅ [FILL] Field input[name=\"username\"], input#username filled successfully\n" + }, + { + "text": "✏️ [FILL] Filling field input[name=\"password\"], input#password with value: Str0ng!P@ssw0rd2024\n" + }, + { + "text": "✅ [FILL] Field input[name=\"password\"], input#password filled successfully\n" + }, + { + "text": "✏️ [FILL] Filling field input[name=\"passwordConfirm\"], input[name=\"password_confirm\"], input[name=\"confirmPassword\"], input#passwordConfirm with value: Str0ng!P@ssw0rd2024\n" + }, + { + "text": "✅ [FILL] Field input[name=\"passwordConfirm\"], input[name=\"password_confirm\"], input[name=\"confirmPassword\"], input#passwordConfirm filled successfully\n" + }, + { + "text": "⚡ [FORM SUBMIT] Forcing submission of form: form\n" + }, + { + "text": "🔍 [FORM SUBMIT] Waiting for form selector: form\n" + }, + { + "text": "⏳ [FORM SUBMIT] Waiting for React to update state...\n" + }, + { + "text": "🚀 [FORM SUBMIT] Submitting form...\n" + }, + { + "text": "✅ [FORM SUBMIT] Form form submitted successfully\n" + }, + { + "text": "🔴 [NETWORK ERROR] POST http://127.0.0.1:8080/api/v1/auth/register: 409\n" + }, + { + "text": "✅ [AUTH TEST] Error shown for existing email: \"User already exists\"\n" + }, + { + "text": "\n📊 [AUTH TEST] === Final Verifications ===\n" + }, + { + "text": "✅ [AUTH TEST] No console errors\n" + }, + { + "text": "🔴 [AUTH TEST] Network errors (1):\n" + }, + { + "text": " - POST http://127.0.0.1:8080/api/v1/auth/register: 409\n" + } + ], + "stderr": [], + "retry": 0, + "startTime": "2026-01-10T21:51:26.901Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "d748ac400d08b85935ef-8e523d14978e1481a048", + "file": "auth.spec.ts", + "line": 231, + "column": 3 + }, + { + "title": "should logout successfully", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 60000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "firefox", + "projectName": "firefox", + "results": [ + { + "workerIndex": 1, + "parallelIndex": 0, + "status": "passed", + "duration": 9716, + "errors": [], + "stdout": [ + { + "text": "🧪 [AUTH TEST] Running: Logout\n" + }, + { + "text": "🔐 [LOGIN] Attempting authentication as e2e@test.com...\n" + }, + { + "text": "⏳ [LOGIN] Waiting 500ms before login (1768081893040ms since last login)...\n" + }, + { + "text": "✏️ [LOGIN] User not authenticated, proceeding with login form...\n" + }, + { + "text": "⚡ [FORM SUBMIT] Forcing submission of form: form\n" + }, + { + "text": "🔍 [FORM SUBMIT] Waiting for form selector: form\n" + }, + { + "text": "⏳ [FORM SUBMIT] Waiting for React to update state...\n" + }, + { + "text": "🚀 [FORM SUBMIT] Submitting form...\n" + }, + { + "text": "✅ [FORM SUBMIT] Form form submitted successfully\n" + }, + { + "text": "🔴 [CONSOLE ERROR] [JavaScript Error: \"Cookie “refresh_token” has been rejected because it is in a cross-site context and its “SameSite” is “Lax” or “Strict”.\" {file: \"http://127.0.0.1:8080/api/v1/auth/login\" line: 0}]\n" + }, + { + "text": "⏳ [LOGIN] Waiting for networkidle after navigation...\n" + }, + { + "text": "⏳ [LOGIN] Waiting for auth state to be persisted...\n" + }, + { + "text": "🔍 [LOGIN] Verifying authentication state...\n" + }, + { + "text": " ✅ TOKEN FOUND: eyJhbGciOiJIUzI1NiIsInR5cCI6Ik... (source: storage)\n" + }, + { + "text": "✅ [LOGIN] Successfully authenticated as e2e@test.com (token: eyJhbGciOiJIUzI1NiIs...)\n" + }, + { + "text": "🔍 [AUTH TEST] Checking token presence before logout...\n" + }, + { + "text": " ✅ TOKEN FOUND: eyJhbGciOiJIUzI1NiIsInR5cCI6Ik... (source: storage)\n" + }, + { + "text": "✅ [AUTH TEST] Token present before logout: eyJhbGciOiJIUzI1NiIsInR5cCI6Ik...\n" + }, + { + "text": "🔴 [NETWORK ERROR] POST http://127.0.0.1:8080/api/v1/auth/logout: 400\n" + }, + { + "text": "✅ [AUTH TEST] Logout successful\n" + }, + { + "text": "\n📊 [AUTH TEST] === Final Verifications ===\n" + }, + { + "text": "🔴 [AUTH TEST] Console errors (1):\n" + }, + { + "text": " - [JavaScript Error: \"Cookie “refresh_token” has been rejected because it is in a cross-site context and its “SameSite” is “Lax” or “Strict”.\" {file: \"http://127.0.0.1:8080/api/v1/auth/login\" line: 0}]\n" + }, + { + "text": "🔴 [AUTH TEST] Network errors (1):\n" + }, + { + "text": " - POST http://127.0.0.1:8080/api/v1/auth/logout: 400\n" + } + ], + "stderr": [ + { + "text": "⚠️ [LOGIN] Form not visible and not on dashboard. Proceeding (might fail)...\n" + }, + { + "text": "⚠️ [AUTH TEST] Test passed but had console errors\n" + } + ], + "retry": 0, + "startTime": "2026-01-10T21:51:32.792Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "d748ac400d08b85935ef-4410c42745b7a4d89d03", + "file": "auth.spec.ts", + "line": 293, + "column": 3 + }, + { + "title": "should redirect to login when accessing protected route without auth", + "ok": true, + "tags": [], + "tests": [ + { + "timeout": 60000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "firefox", + "projectName": "firefox", + "results": [ + { + "workerIndex": 1, + "parallelIndex": 0, + "status": "passed", + "duration": 2298, + "errors": [], + "stdout": [ + { + "text": "🧪 [AUTH TEST] Running: Route guard test\n" + }, + { + "text": "✅ [AUTH TEST] Route guard working correctly\n" + }, + { + "text": "\n📊 [AUTH TEST] === Final Verifications ===\n" + }, + { + "text": "✅ [AUTH TEST] No console errors\n" + }, + { + "text": "✅ [AUTH TEST] No network errors\n" + } + ], + "stderr": [], + "retry": 0, + "startTime": "2026-01-10T21:51:42.548Z", + "annotations": [], + "attachments": [] + } + ], + "status": "expected" + } + ], + "id": "d748ac400d08b85935ef-4329ca7f3e8c21f77534", + "file": "auth.spec.ts", + "line": 373, + "column": 3 + }, + { + "title": "should persist authentication after page refresh", + "ok": false, + "tags": [], + "tests": [ + { + "timeout": 60000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "firefox", + "projectName": "firefox", + "results": [ + { + "workerIndex": 1, + "parallelIndex": 0, + "status": "failed", + "duration": 12981, + "error": { + "message": "Error: page.goto: NS_ERROR_CONNECTION_REFUSED\nCall log:\n\u001b[2m - navigating to \"http://localhost:5173/login\", waiting until \"domcontentloaded\"\u001b[22m\n", + "stack": "Error: page.goto: NS_ERROR_CONNECTION_REFUSED\nCall log:\n\u001b[2m - navigating to \"http://localhost:5173/login\", waiting until \"domcontentloaded\"\u001b[22m\n\n at loginAsUser (/home/senke/git/talas/veza/apps/web/e2e/utils/test-helpers.ts:174:18)\n at /home/senke/git/talas/veza/apps/web/e2e/auth.spec.ts:403:5", + "location": { + "file": "/home/senke/git/talas/veza/apps/web/e2e/utils/test-helpers.ts", + "column": 18, + "line": 174 + }, + "snippet": "\u001b[90m at \u001b[39mutils/test-helpers.ts:174\n\n\u001b[0m \u001b[90m 172 |\u001b[39m \u001b[36mwhile\u001b[39m (retries \u001b[33m>\u001b[39m \u001b[35m0\u001b[39m) {\n \u001b[90m 173 |\u001b[39m \u001b[36mtry\u001b[39m {\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 174 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mgoto(\u001b[32m`${TEST_CONFIG.FRONTEND_URL}/login`\u001b[39m\u001b[33m,\u001b[39m {\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 175 |\u001b[39m waitUntil\u001b[33m:\u001b[39m \u001b[32m'domcontentloaded'\u001b[39m\u001b[33m,\u001b[39m\n \u001b[90m 176 |\u001b[39m timeout\u001b[33m:\u001b[39m \u001b[33mTEST_CONFIG\u001b[39m\u001b[33m.\u001b[39m\u001b[33mDEFAULT_TIMEOUT\u001b[39m\u001b[33m,\u001b[39m\n \u001b[90m 177 |\u001b[39m })\u001b[33m;\u001b[39m\u001b[0m" + }, + "errors": [ + { + "location": { + "file": "/home/senke/git/talas/veza/apps/web/e2e/utils/test-helpers.ts", + "column": 18, + "line": 174 + }, + "message": "Error: page.goto: NS_ERROR_CONNECTION_REFUSED\nCall log:\n\u001b[2m - navigating to \"http://localhost:5173/login\", waiting until \"domcontentloaded\"\u001b[22m\n\n\n\u001b[90m at \u001b[39mutils/test-helpers.ts:174\n\n\u001b[0m \u001b[90m 172 |\u001b[39m \u001b[36mwhile\u001b[39m (retries \u001b[33m>\u001b[39m \u001b[35m0\u001b[39m) {\n \u001b[90m 173 |\u001b[39m \u001b[36mtry\u001b[39m {\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 174 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mgoto(\u001b[32m`${TEST_CONFIG.FRONTEND_URL}/login`\u001b[39m\u001b[33m,\u001b[39m {\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 175 |\u001b[39m waitUntil\u001b[33m:\u001b[39m \u001b[32m'domcontentloaded'\u001b[39m\u001b[33m,\u001b[39m\n \u001b[90m 176 |\u001b[39m timeout\u001b[33m:\u001b[39m \u001b[33mTEST_CONFIG\u001b[39m\u001b[33m.\u001b[39m\u001b[33mDEFAULT_TIMEOUT\u001b[39m\u001b[33m,\u001b[39m\n \u001b[90m 177 |\u001b[39m })\u001b[33m;\u001b[39m\u001b[0m\n\u001b[2m at loginAsUser (/home/senke/git/talas/veza/apps/web/e2e/utils/test-helpers.ts:174:18)\u001b[22m\n\u001b[2m at /home/senke/git/talas/veza/apps/web/e2e/auth.spec.ts:403:5\u001b[22m" + } + ], + "stdout": [ + { + "text": "🧪 [AUTH TEST] Running: Auth persistence test\n" + }, + { + "text": "🔐 [LOGIN] Attempting authentication as e2e@test.com...\n" + }, + { + "text": "⏳ [LOGIN] Waiting 500ms before login (21565ms since last login)...\n" + }, + { + "text": "🔴 [REQUEST FAILED] GET http://localhost:5173/login: NS_ERROR_CONNECTION_REFUSED\n" + }, + { + "text": "🔴 [REQUEST FAILED] GET http://localhost:5173/login: NS_ERROR_CONNECTION_REFUSED\n" + }, + { + "text": "🔴 [REQUEST FAILED] GET http://localhost:5173/login: NS_ERROR_CONNECTION_REFUSED\n" + }, + { + "text": "\n📊 [AUTH TEST] === Final Verifications ===\n" + }, + { + "text": "✅ [AUTH TEST] No console errors\n" + }, + { + "text": "🔴 [AUTH TEST] Network errors (3):\n" + }, + { + "text": " - GET http://localhost:5173/login: 0\n" + }, + { + "text": " - GET http://localhost:5173/login: 0\n" + }, + { + "text": " - GET http://localhost:5173/login: 0\n" + } + ], + "stderr": [ + { + "text": "⚠️ [LOGIN] Navigation failed (retries left: 2): page.goto: NS_ERROR_CONNECTION_REFUSED\nCall log:\n\u001b[2m - navigating to \"http://localhost:5173/login\", waiting until \"domcontentloaded\"\u001b[22m\n\n at loginAsUser \u001b[90m(/home/senke/git/talas/veza/apps/web/\u001b[39me2e/utils/test-helpers.ts:174:18\u001b[90m)\u001b[39m\n at \u001b[90m/home/senke/git/talas/veza/apps/web/\u001b[39me2e/auth.spec.ts:403:5 {\n name: \u001b[32m'Error'\u001b[39m,\n [\u001b[32mSymbol(step)\u001b[39m]: {\n location: {\n file: \u001b[32m'/home/senke/git/talas/veza/apps/web/e2e/utils/test-helpers.ts'\u001b[39m,\n line: \u001b[33m174\u001b[39m,\n column: \u001b[33m18\u001b[39m,\n function: \u001b[32m'loginAsUser'\u001b[39m\n },\n category: \u001b[32m'pw:api'\u001b[39m,\n title: \u001b[32m'Navigate to \"/login\"'\u001b[39m,\n apiName: \u001b[32m'page.goto'\u001b[39m,\n params: {\n url: \u001b[32m'http://localhost:5173/login'\u001b[39m,\n waitUntil: \u001b[32m'domcontentloaded'\u001b[39m,\n timeout: \u001b[33m30000\u001b[39m\n },\n group: \u001b[90mundefined\u001b[39m,\n stepId: \u001b[32m'pw:api@28'\u001b[39m,\n boxedStack: \u001b[90mundefined\u001b[39m,\n steps: [],\n attachmentIndices: [],\n info: TestStepInfoImpl {\n annotations: [],\n _testInfo: \u001b[36m[TestInfoImpl]\u001b[39m,\n _stepId: \u001b[32m'pw:api@28'\u001b[39m,\n _title: \u001b[32m'Navigate to \"/login\"'\u001b[39m,\n _parentStep: \u001b[90mundefined\u001b[39m,\n skip: \u001b[36m[Function (anonymous)]\u001b[39m\n },\n complete: \u001b[36m[Function: complete]\u001b[39m,\n endWallTime: \u001b[33m1768081915627\u001b[39m,\n error: {\n message: \u001b[32m'Error: page.goto: NS_ERROR_CONNECTION_REFUSED\\n'\u001b[39m +\n \u001b[32m'Call log:\\n'\u001b[39m +\n \u001b[32m'\\x1B[2m - navigating to \"http://localhost:5173/login\", waiting until \"domcontentloaded\"\\x1B[22m\\n'\u001b[39m,\n stack: \u001b[32m'Error: page.goto: NS_ERROR_CONNECTION_REFUSED\\n'\u001b[39m +\n \u001b[32m'Call log:\\n'\u001b[39m +\n \u001b[32m'\\x1B[2m - navigating to \"http://localhost:5173/login\", waiting until \"domcontentloaded\"\\x1B[22m\\n'\u001b[39m +\n \u001b[32m'\\n'\u001b[39m +\n \u001b[32m' at loginAsUser (/home/senke/git/talas/veza/apps/web/e2e/utils/test-helpers.ts:174:18)\\n'\u001b[39m +\n \u001b[32m' at /home/senke/git/talas/veza/apps/web/e2e/auth.spec.ts:403:5'\u001b[39m,\n cause: \u001b[90mundefined\u001b[39m\n }\n }\n}\n" + }, + { + "text": "⚠️ [LOGIN] Navigation failed (retries left: 1): page.goto: NS_ERROR_CONNECTION_REFUSED\nCall log:\n\u001b[2m - navigating to \"http://localhost:5173/login\", waiting until \"domcontentloaded\"\u001b[22m\n\n at loginAsUser \u001b[90m(/home/senke/git/talas/veza/apps/web/\u001b[39me2e/utils/test-helpers.ts:174:18\u001b[90m)\u001b[39m\n at \u001b[90m/home/senke/git/talas/veza/apps/web/\u001b[39me2e/auth.spec.ts:403:5 {\n name: \u001b[32m'Error'\u001b[39m,\n [\u001b[32mSymbol(step)\u001b[39m]: {\n location: {\n file: \u001b[32m'/home/senke/git/talas/veza/apps/web/e2e/utils/test-helpers.ts'\u001b[39m,\n line: \u001b[33m174\u001b[39m,\n column: \u001b[33m18\u001b[39m,\n function: \u001b[32m'loginAsUser'\u001b[39m\n },\n category: \u001b[32m'pw:api'\u001b[39m,\n title: \u001b[32m'Navigate to \"/login\"'\u001b[39m,\n apiName: \u001b[32m'page.goto'\u001b[39m,\n params: {\n url: \u001b[32m'http://localhost:5173/login'\u001b[39m,\n waitUntil: \u001b[32m'domcontentloaded'\u001b[39m,\n timeout: \u001b[33m30000\u001b[39m\n },\n group: \u001b[90mundefined\u001b[39m,\n stepId: \u001b[32m'pw:api@30'\u001b[39m,\n boxedStack: \u001b[90mundefined\u001b[39m,\n steps: [],\n attachmentIndices: [],\n info: TestStepInfoImpl {\n annotations: [],\n _testInfo: \u001b[36m[TestInfoImpl]\u001b[39m,\n _stepId: \u001b[32m'pw:api@30'\u001b[39m,\n _title: \u001b[32m'Navigate to \"/login\"'\u001b[39m,\n _parentStep: \u001b[90mundefined\u001b[39m,\n skip: \u001b[36m[Function (anonymous)]\u001b[39m\n },\n complete: \u001b[36m[Function: complete]\u001b[39m,\n endWallTime: \u001b[33m1768081916650\u001b[39m,\n error: {\n message: \u001b[32m'Error: page.goto: NS_ERROR_CONNECTION_REFUSED\\n'\u001b[39m +\n \u001b[32m'Call log:\\n'\u001b[39m +\n \u001b[32m'\\x1B[2m - navigating to \"http://localhost:5173/login\", waiting until \"domcontentloaded\"\\x1B[22m\\n'\u001b[39m,\n stack: \u001b[32m'Error: page.goto: NS_ERROR_CONNECTION_REFUSED\\n'\u001b[39m +\n \u001b[32m'Call log:\\n'\u001b[39m +\n \u001b[32m'\\x1B[2m - navigating to \"http://localhost:5173/login\", waiting until \"domcontentloaded\"\\x1B[22m\\n'\u001b[39m +\n \u001b[32m'\\n'\u001b[39m +\n \u001b[32m' at loginAsUser (/home/senke/git/talas/veza/apps/web/e2e/utils/test-helpers.ts:174:18)\\n'\u001b[39m +\n \u001b[32m' at /home/senke/git/talas/veza/apps/web/e2e/auth.spec.ts:403:5'\u001b[39m,\n cause: \u001b[90mundefined\u001b[39m\n }\n }\n}\n" + }, + { + "text": "⚠️ [LOGIN] Navigation failed (retries left: 0): page.goto: NS_ERROR_CONNECTION_REFUSED\nCall log:\n\u001b[2m - navigating to \"http://localhost:5173/login\", waiting until \"domcontentloaded\"\u001b[22m\n\n at loginAsUser \u001b[90m(/home/senke/git/talas/veza/apps/web/\u001b[39me2e/utils/test-helpers.ts:174:18\u001b[90m)\u001b[39m\n at \u001b[90m/home/senke/git/talas/veza/apps/web/\u001b[39me2e/auth.spec.ts:403:5 {\n name: \u001b[32m'Error'\u001b[39m,\n [\u001b[32mSymbol(step)\u001b[39m]: {\n location: {\n file: \u001b[32m'/home/senke/git/talas/veza/apps/web/e2e/utils/test-helpers.ts'\u001b[39m,\n line: \u001b[33m174\u001b[39m,\n column: \u001b[33m18\u001b[39m,\n function: \u001b[32m'loginAsUser'\u001b[39m\n },\n category: \u001b[32m'pw:api'\u001b[39m,\n title: \u001b[32m'Navigate to \"/login\"'\u001b[39m,\n apiName: \u001b[32m'page.goto'\u001b[39m,\n params: {\n url: \u001b[32m'http://localhost:5173/login'\u001b[39m,\n waitUntil: \u001b[32m'domcontentloaded'\u001b[39m,\n timeout: \u001b[33m30000\u001b[39m\n },\n group: \u001b[90mundefined\u001b[39m,\n stepId: \u001b[32m'pw:api@32'\u001b[39m,\n boxedStack: \u001b[90mundefined\u001b[39m,\n steps: [],\n attachmentIndices: [],\n info: TestStepInfoImpl {\n annotations: [],\n _testInfo: \u001b[36m[TestInfoImpl]\u001b[39m,\n _stepId: \u001b[32m'pw:api@32'\u001b[39m,\n _title: \u001b[32m'Navigate to \"/login\"'\u001b[39m,\n _parentStep: \u001b[90mundefined\u001b[39m,\n skip: \u001b[36m[Function (anonymous)]\u001b[39m\n },\n complete: \u001b[36m[Function: complete]\u001b[39m,\n endWallTime: \u001b[33m1768081917665\u001b[39m,\n error: {\n message: \u001b[32m'Error: page.goto: NS_ERROR_CONNECTION_REFUSED\\n'\u001b[39m +\n \u001b[32m'Call log:\\n'\u001b[39m +\n \u001b[32m'\\x1B[2m - navigating to \"http://localhost:5173/login\", waiting until \"domcontentloaded\"\\x1B[22m\\n'\u001b[39m,\n stack: \u001b[32m'Error: page.goto: NS_ERROR_CONNECTION_REFUSED\\n'\u001b[39m +\n \u001b[32m'Call log:\\n'\u001b[39m +\n \u001b[32m'\\x1B[2m - navigating to \"http://localhost:5173/login\", waiting until \"domcontentloaded\"\\x1B[22m\\n'\u001b[39m +\n \u001b[32m'\\n'\u001b[39m +\n \u001b[32m' at loginAsUser (/home/senke/git/talas/veza/apps/web/e2e/utils/test-helpers.ts:174:18)\\n'\u001b[39m +\n \u001b[32m' at /home/senke/git/talas/veza/apps/web/e2e/auth.spec.ts:403:5'\u001b[39m,\n cause: \u001b[90mundefined\u001b[39m\n }\n }\n}\n" + } + ], + "retry": 0, + "startTime": "2026-01-10T21:51:44.885Z", + "annotations": [], + "attachments": [ + { + "name": "screenshot", + "contentType": "image/png", + "path": "/home/senke/git/talas/veza/apps/web/test-results/auth-Authentication-Flow-s-12d3c-tication-after-page-refresh-firefox/test-failed-1.png" + }, + { + "name": "video", + "contentType": "video/webm", + "path": "/home/senke/git/talas/veza/apps/web/test-results/auth-Authentication-Flow-s-12d3c-tication-after-page-refresh-firefox/video.webm" + }, + { + "name": "error-context", + "contentType": "text/markdown", + "path": "/home/senke/git/talas/veza/apps/web/test-results/auth-Authentication-Flow-s-12d3c-tication-after-page-refresh-firefox/error-context.md" + } + ], + "errorLocation": { + "file": "/home/senke/git/talas/veza/apps/web/e2e/utils/test-helpers.ts", + "column": 18, + "line": 174 + } + } + ], + "status": "unexpected" + } + ], + "id": "d748ac400d08b85935ef-7ce71de8a36364c96c8d", + "file": "auth.spec.ts", + "line": 395, + "column": 3 + }, + { + "title": "should validate login form fields", + "ok": false, + "tags": [], + "tests": [ + { + "timeout": 60000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "firefox", + "projectName": "firefox", + "results": [ + { + "workerIndex": 2, + "parallelIndex": 0, + "status": "failed", + "duration": 843, + "error": { + "message": "Error: page.goto: NS_ERROR_CONNECTION_REFUSED\nCall log:\n\u001b[2m - navigating to \"http://localhost:5173/login\", waiting until \"load\"\u001b[22m\n", + "stack": "Error: page.goto: NS_ERROR_CONNECTION_REFUSED\nCall log:\n\u001b[2m - navigating to \"http://localhost:5173/login\", waiting until \"load\"\u001b[22m\n\n at /home/senke/git/talas/veza/apps/web/e2e/auth.spec.ts:455:16", + "location": { + "file": "/home/senke/git/talas/veza/apps/web/e2e/auth.spec.ts", + "column": 16, + "line": 455 + }, + "snippet": "\u001b[0m \u001b[90m 453 |\u001b[39m console\u001b[33m.\u001b[39mlog(\u001b[32m'🧪 [AUTH TEST] Running: Login form validation'\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 454 |\u001b[39m\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 455 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mgoto(\u001b[32m`${TEST_CONFIG.FRONTEND_URL}/login`\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 456 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mwaitForLoadState(\u001b[32m'domcontentloaded'\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 457 |\u001b[39m\n \u001b[90m 458 |\u001b[39m \u001b[90m// Wait for form to be ready\u001b[39m\u001b[0m" + }, + "errors": [ + { + "location": { + "file": "/home/senke/git/talas/veza/apps/web/e2e/auth.spec.ts", + "column": 16, + "line": 455 + }, + "message": "Error: page.goto: NS_ERROR_CONNECTION_REFUSED\nCall log:\n\u001b[2m - navigating to \"http://localhost:5173/login\", waiting until \"load\"\u001b[22m\n\n\n\u001b[0m \u001b[90m 453 |\u001b[39m console\u001b[33m.\u001b[39mlog(\u001b[32m'🧪 [AUTH TEST] Running: Login form validation'\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 454 |\u001b[39m\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 455 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mgoto(\u001b[32m`${TEST_CONFIG.FRONTEND_URL}/login`\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 456 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mwaitForLoadState(\u001b[32m'domcontentloaded'\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 457 |\u001b[39m\n \u001b[90m 458 |\u001b[39m \u001b[90m// Wait for form to be ready\u001b[39m\u001b[0m\n\u001b[2m at /home/senke/git/talas/veza/apps/web/e2e/auth.spec.ts:455:16\u001b[22m" + } + ], + "stdout": [ + { + "text": "🧪 [AUTH TEST] Running: Login form validation\n" + }, + { + "text": "🔴 [REQUEST FAILED] GET http://localhost:5173/login: NS_ERROR_CONNECTION_REFUSED\n" + }, + { + "text": "\n📊 [AUTH TEST] === Final Verifications ===\n" + }, + { + "text": "✅ [AUTH TEST] No console errors\n" + }, + { + "text": "🔴 [AUTH TEST] Network errors (1):\n" + }, + { + "text": " - GET http://localhost:5173/login: 0\n" + } + ], + "stderr": [], + "retry": 0, + "startTime": "2026-01-10T21:51:58.883Z", + "annotations": [], + "attachments": [ + { + "name": "screenshot", + "contentType": "image/png", + "path": "/home/senke/git/talas/veza/apps/web/test-results/auth-Authentication-Flow-should-validate-login-form-fields-firefox/test-failed-1.png" + }, + { + "name": "video", + "contentType": "video/webm", + "path": "/home/senke/git/talas/veza/apps/web/test-results/auth-Authentication-Flow-should-validate-login-form-fields-firefox/video.webm" + }, + { + "name": "error-context", + "contentType": "text/markdown", + "path": "/home/senke/git/talas/veza/apps/web/test-results/auth-Authentication-Flow-should-validate-login-form-fields-firefox/error-context.md" + } + ], + "errorLocation": { + "file": "/home/senke/git/talas/veza/apps/web/e2e/auth.spec.ts", + "column": 16, + "line": 455 + } + } + ], + "status": "unexpected" + } + ], + "id": "d748ac400d08b85935ef-9f21426262cd48deaf49", + "file": "auth.spec.ts", + "line": 452, + "column": 3 + }, + { + "title": "should show error when passwords do not match during registration", + "ok": false, + "tags": [], + "tests": [ + { + "timeout": 60000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "firefox", + "projectName": "firefox", + "results": [ + { + "workerIndex": 3, + "parallelIndex": 0, + "status": "failed", + "duration": 929, + "error": { + "message": "Error: page.goto: NS_ERROR_CONNECTION_REFUSED\nCall log:\n\u001b[2m - navigating to \"http://localhost:5173/register\", waiting until \"load\"\u001b[22m\n", + "stack": "Error: page.goto: NS_ERROR_CONNECTION_REFUSED\nCall log:\n\u001b[2m - navigating to \"http://localhost:5173/register\", waiting until \"load\"\u001b[22m\n\n at /home/senke/git/talas/veza/apps/web/e2e/auth.spec.ts:512:16", + "location": { + "file": "/home/senke/git/talas/veza/apps/web/e2e/auth.spec.ts", + "column": 16, + "line": 512 + }, + "snippet": "\u001b[0m \u001b[90m 510 |\u001b[39m console\u001b[33m.\u001b[39mlog(\u001b[32m'🧪 [AUTH TEST] Running: Password mismatch validation'\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 511 |\u001b[39m\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 512 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mgoto(\u001b[32m`${TEST_CONFIG.FRONTEND_URL}/register`\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 513 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mwaitForLoadState(\u001b[32m'domcontentloaded'\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 514 |\u001b[39m\n \u001b[90m 515 |\u001b[39m \u001b[90m// Attendre que la page soit complètement chargée\u001b[39m\u001b[0m" + }, + "errors": [ + { + "location": { + "file": "/home/senke/git/talas/veza/apps/web/e2e/auth.spec.ts", + "column": 16, + "line": 512 + }, + "message": "Error: page.goto: NS_ERROR_CONNECTION_REFUSED\nCall log:\n\u001b[2m - navigating to \"http://localhost:5173/register\", waiting until \"load\"\u001b[22m\n\n\n\u001b[0m \u001b[90m 510 |\u001b[39m console\u001b[33m.\u001b[39mlog(\u001b[32m'🧪 [AUTH TEST] Running: Password mismatch validation'\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 511 |\u001b[39m\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 512 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mgoto(\u001b[32m`${TEST_CONFIG.FRONTEND_URL}/register`\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 513 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mwaitForLoadState(\u001b[32m'domcontentloaded'\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 514 |\u001b[39m\n \u001b[90m 515 |\u001b[39m \u001b[90m// Attendre que la page soit complètement chargée\u001b[39m\u001b[0m\n\u001b[2m at /home/senke/git/talas/veza/apps/web/e2e/auth.spec.ts:512:16\u001b[22m" + } + ], + "stdout": [ + { + "text": "🧪 [AUTH TEST] Running: Password mismatch validation\n" + }, + { + "text": "🔴 [REQUEST FAILED] GET http://localhost:5173/register: NS_ERROR_CONNECTION_REFUSED\n" + }, + { + "text": "\n📊 [AUTH TEST] === Final Verifications ===\n" + }, + { + "text": "✅ [AUTH TEST] No console errors\n" + }, + { + "text": "🔴 [AUTH TEST] Network errors (1):\n" + }, + { + "text": " - GET http://localhost:5173/register: 0\n" + } + ], + "stderr": [], + "retry": 0, + "startTime": "2026-01-10T21:52:01.197Z", + "annotations": [], + "attachments": [ + { + "name": "screenshot", + "contentType": "image/png", + "path": "/home/senke/git/talas/veza/apps/web/test-results/auth-Authentication-Flow-s-216c1-t-match-during-registration-firefox/test-failed-1.png" + }, + { + "name": "video", + "contentType": "video/webm", + "path": "/home/senke/git/talas/veza/apps/web/test-results/auth-Authentication-Flow-s-216c1-t-match-during-registration-firefox/video.webm" + }, + { + "name": "error-context", + "contentType": "text/markdown", + "path": "/home/senke/git/talas/veza/apps/web/test-results/auth-Authentication-Flow-s-216c1-t-match-during-registration-firefox/error-context.md" + } + ], + "errorLocation": { + "file": "/home/senke/git/talas/veza/apps/web/e2e/auth.spec.ts", + "column": 16, + "line": 512 + } + } + ], + "status": "unexpected" + } + ], + "id": "d748ac400d08b85935ef-3b0220e3d2e27f513770", + "file": "auth.spec.ts", + "line": 509, + "column": 3 + }, + { + "title": "should login successfully with valid credentials", + "ok": false, + "tags": [], + "tests": [ + { + "timeout": 60000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "webkit", + "projectName": "webkit", + "results": [ + { + "workerIndex": 4, + "parallelIndex": 0, + "status": "failed", + "duration": 6, + "error": { + "message": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝", + "stack": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝" + }, + "errors": [ + { + "message": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝" + } + ], + "stdout": [ + { + "text": "\n📊 [AUTH TEST] === Final Verifications ===\n" + }, + { + "text": "✅ [AUTH TEST] No console errors\n" + }, + { + "text": "✅ [AUTH TEST] No network errors\n" + } + ], + "stderr": [], + "retry": 0, + "startTime": "2026-01-10T21:52:04.404Z", + "annotations": [], + "attachments": [] + } + ], + "status": "unexpected" + } + ], + "id": "d748ac400d08b85935ef-9e1583275625d6e8142a", + "file": "auth.spec.ts", + "line": 41, + "column": 3 + }, + { + "title": "should show error with invalid credentials", + "ok": false, + "tags": [], + "tests": [ + { + "timeout": 60000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "webkit", + "projectName": "webkit", + "results": [ + { + "workerIndex": 5, + "parallelIndex": 0, + "status": "failed", + "duration": 7, + "error": { + "message": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝", + "stack": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝" + }, + "errors": [ + { + "message": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝" + } + ], + "stdout": [ + { + "text": "\n📊 [AUTH TEST] === Final Verifications ===\n" + }, + { + "text": "✅ [AUTH TEST] No console errors\n" + }, + { + "text": "✅ [AUTH TEST] No network errors\n" + } + ], + "stderr": [], + "retry": 0, + "startTime": "2026-01-10T21:52:05.509Z", + "annotations": [], + "attachments": [] + } + ], + "status": "unexpected" + } + ], + "id": "d748ac400d08b85935ef-4201a53bab8fcc8ef77f", + "file": "auth.spec.ts", + "line": 107, + "column": 3 + }, + { + "title": "should register a new user successfully", + "ok": false, + "tags": [], + "tests": [ + { + "timeout": 60000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "webkit", + "projectName": "webkit", + "results": [ + { + "workerIndex": 6, + "parallelIndex": 0, + "status": "failed", + "duration": 6, + "error": { + "message": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝", + "stack": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝" + }, + "errors": [ + { + "message": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝" + } + ], + "stdout": [ + { + "text": "\n📊 [AUTH TEST] === Final Verifications ===\n" + }, + { + "text": "✅ [AUTH TEST] No console errors\n" + }, + { + "text": "✅ [AUTH TEST] No network errors\n" + } + ], + "stderr": [], + "retry": 0, + "startTime": "2026-01-10T21:52:06.649Z", + "annotations": [], + "attachments": [] + } + ], + "status": "unexpected" + } + ], + "id": "d748ac400d08b85935ef-e1e04a4d63f068ce6c7f", + "file": "auth.spec.ts", + "line": 134, + "column": 3 + }, + { + "title": "should show error when registering with existing email", + "ok": false, + "tags": [], + "tests": [ + { + "timeout": 60000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "webkit", + "projectName": "webkit", + "results": [ + { + "workerIndex": 7, + "parallelIndex": 0, + "status": "failed", + "duration": 10, + "error": { + "message": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝", + "stack": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝" + }, + "errors": [ + { + "message": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝" + } + ], + "stdout": [ + { + "text": "\n📊 [AUTH TEST] === Final Verifications ===\n" + }, + { + "text": "✅ [AUTH TEST] No console errors\n" + }, + { + "text": "✅ [AUTH TEST] No network errors\n" + } + ], + "stderr": [], + "retry": 0, + "startTime": "2026-01-10T21:52:07.973Z", + "annotations": [], + "attachments": [] + } + ], + "status": "unexpected" + } + ], + "id": "d748ac400d08b85935ef-e9be5a0fa5f268f981b2", + "file": "auth.spec.ts", + "line": 231, + "column": 3 + }, + { + "title": "should logout successfully", + "ok": false, + "tags": [], + "tests": [ + { + "timeout": 60000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "webkit", + "projectName": "webkit", + "results": [ + { + "workerIndex": 8, + "parallelIndex": 0, + "status": "failed", + "duration": 9, + "error": { + "message": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝", + "stack": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝" + }, + "errors": [ + { + "message": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝" + } + ], + "stdout": [ + { + "text": "\n📊 [AUTH TEST] === Final Verifications ===\n" + }, + { + "text": "✅ [AUTH TEST] No console errors\n" + }, + { + "text": "✅ [AUTH TEST] No network errors\n" + } + ], + "stderr": [], + "retry": 0, + "startTime": "2026-01-10T21:52:09.040Z", + "annotations": [], + "attachments": [] + } + ], + "status": "unexpected" + } + ], + "id": "d748ac400d08b85935ef-1f08bff9df919d288ae6", + "file": "auth.spec.ts", + "line": 293, + "column": 3 + }, + { + "title": "should redirect to login when accessing protected route without auth", + "ok": false, + "tags": [], + "tests": [ + { + "timeout": 60000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "webkit", + "projectName": "webkit", + "results": [ + { + "workerIndex": 9, + "parallelIndex": 0, + "status": "failed", + "duration": 5, + "error": { + "message": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝", + "stack": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝" + }, + "errors": [ + { + "message": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝" + } + ], + "stdout": [ + { + "text": "\n📊 [AUTH TEST] === Final Verifications ===\n" + }, + { + "text": "✅ [AUTH TEST] No console errors\n" + }, + { + "text": "✅ [AUTH TEST] No network errors\n" + } + ], + "stderr": [], + "retry": 0, + "startTime": "2026-01-10T21:52:09.935Z", + "annotations": [], + "attachments": [] + } + ], + "status": "unexpected" + } + ], + "id": "d748ac400d08b85935ef-21fd3dcd248dab5b8131", + "file": "auth.spec.ts", + "line": 373, + "column": 3 + }, + { + "title": "should persist authentication after page refresh", + "ok": false, + "tags": [], + "tests": [ + { + "timeout": 60000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "webkit", + "projectName": "webkit", + "results": [ + { + "workerIndex": 10, + "parallelIndex": 0, + "status": "failed", + "duration": 7, + "error": { + "message": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝", + "stack": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝" + }, + "errors": [ + { + "message": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝" + } + ], + "stdout": [ + { + "text": "\n📊 [AUTH TEST] === Final Verifications ===\n" + }, + { + "text": "✅ [AUTH TEST] No console errors\n" + }, + { + "text": "✅ [AUTH TEST] No network errors\n" + } + ], + "stderr": [], + "retry": 0, + "startTime": "2026-01-10T21:52:10.798Z", + "annotations": [], + "attachments": [] + } + ], + "status": "unexpected" + } + ], + "id": "d748ac400d08b85935ef-b650fa26a32d2a037a5a", + "file": "auth.spec.ts", + "line": 395, + "column": 3 + }, + { + "title": "should validate login form fields", + "ok": false, + "tags": [], + "tests": [ + { + "timeout": 60000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "webkit", + "projectName": "webkit", + "results": [ + { + "workerIndex": 11, + "parallelIndex": 0, + "status": "failed", + "duration": 5, + "error": { + "message": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝", + "stack": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝" + }, + "errors": [ + { + "message": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝" + } + ], + "stdout": [ + { + "text": "\n📊 [AUTH TEST] === Final Verifications ===\n" + }, + { + "text": "✅ [AUTH TEST] No console errors\n" + }, + { + "text": "✅ [AUTH TEST] No network errors\n" + } + ], + "stderr": [], + "retry": 0, + "startTime": "2026-01-10T21:52:11.755Z", + "annotations": [], + "attachments": [] + } + ], + "status": "unexpected" + } + ], + "id": "d748ac400d08b85935ef-5ee828237b6b2f781ea3", + "file": "auth.spec.ts", + "line": 452, + "column": 3 + }, + { + "title": "should show error when passwords do not match during registration", + "ok": false, + "tags": [], + "tests": [ + { + "timeout": 60000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "webkit", + "projectName": "webkit", + "results": [ + { + "workerIndex": 12, + "parallelIndex": 0, + "status": "failed", + "duration": 3, + "error": { + "message": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝", + "stack": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝" + }, + "errors": [ + { + "message": "Error: browserType.launch: \n╔══════════════════════════════════════════════════════╗\n║ Host system is missing dependencies to run browsers. ║\n║ Please install them with the following command: ║\n║ ║\n║ sudo npx playwright install-deps ║\n║ ║\n║ Alternatively, use apt: ║\n║ sudo apt-get install libicu74\\ ║\n║ libjpeg-turbo8\\ ║\n║ gstreamer1.0-libav ║\n║ ║\n║ <3 Playwright Team ║\n╚══════════════════════════════════════════════════════╝" + } + ], + "stdout": [ + { + "text": "\n📊 [AUTH TEST] === Final Verifications ===\n" + }, + { + "text": "✅ [AUTH TEST] No console errors\n" + }, + { + "text": "✅ [AUTH TEST] No network errors\n" + } + ], + "stderr": [], + "retry": 0, + "startTime": "2026-01-10T21:52:12.602Z", + "annotations": [], + "attachments": [] + } + ], + "status": "unexpected" + } + ], + "id": "d748ac400d08b85935ef-4c0ea2b25148c5c9623e", + "file": "auth.spec.ts", + "line": 509, + "column": 3 + }, + { + "title": "should login successfully with valid credentials", + "ok": false, + "tags": [], + "tests": [ + { + "timeout": 60000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "msedge", + "projectName": "msedge", + "results": [ + { + "workerIndex": 13, + "parallelIndex": 0, + "status": "failed", + "duration": 229, + "error": { + "message": "Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/login\nCall log:\n\u001b[2m - navigating to \"http://localhost:5173/login\", waiting until \"load\"\u001b[22m\n", + "stack": "Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/login\nCall log:\n\u001b[2m - navigating to \"http://localhost:5173/login\", waiting until \"load\"\u001b[22m\n\n at /home/senke/git/talas/veza/apps/web/e2e/auth.spec.ts:44:16", + "location": { + "file": "/home/senke/git/talas/veza/apps/web/e2e/auth.spec.ts", + "column": 16, + "line": 44 + }, + "snippet": "\u001b[0m \u001b[90m 42 |\u001b[39m console\u001b[33m.\u001b[39mlog(\u001b[32m'🧪 [AUTH TEST] Running: Login with valid credentials'\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 43 |\u001b[39m\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 44 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mgoto(\u001b[32m`${TEST_CONFIG.FRONTEND_URL}/login`\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 45 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mwaitForLoadState(\u001b[32m'domcontentloaded'\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 46 |\u001b[39m\n \u001b[90m 47 |\u001b[39m \u001b[90m// Attendre que le formulaire soit prêt (premier test peut être plus lent)\u001b[39m\u001b[0m" + }, + "errors": [ + { + "location": { + "file": "/home/senke/git/talas/veza/apps/web/e2e/auth.spec.ts", + "column": 16, + "line": 44 + }, + "message": "Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/login\nCall log:\n\u001b[2m - navigating to \"http://localhost:5173/login\", waiting until \"load\"\u001b[22m\n\n\n\u001b[0m \u001b[90m 42 |\u001b[39m console\u001b[33m.\u001b[39mlog(\u001b[32m'🧪 [AUTH TEST] Running: Login with valid credentials'\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 43 |\u001b[39m\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 44 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mgoto(\u001b[32m`${TEST_CONFIG.FRONTEND_URL}/login`\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 45 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mwaitForLoadState(\u001b[32m'domcontentloaded'\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 46 |\u001b[39m\n \u001b[90m 47 |\u001b[39m \u001b[90m// Attendre que le formulaire soit prêt (premier test peut être plus lent)\u001b[39m\u001b[0m\n\u001b[2m at /home/senke/git/talas/veza/apps/web/e2e/auth.spec.ts:44:16\u001b[22m" + } + ], + "stdout": [ + { + "text": "🧪 [AUTH TEST] Running: Login with valid credentials\n" + }, + { + "text": "🔴 [REQUEST FAILED] GET http://localhost:5173/login: net::ERR_CONNECTION_REFUSED\n" + }, + { + "text": "\n📊 [AUTH TEST] === Final Verifications ===\n" + }, + { + "text": "✅ [AUTH TEST] No console errors\n" + }, + { + "text": "🔴 [AUTH TEST] Network errors (1):\n" + }, + { + "text": " - GET http://localhost:5173/login: 0\n" + } + ], + "stderr": [], + "retry": 0, + "startTime": "2026-01-10T21:52:13.560Z", + "annotations": [], + "attachments": [ + { + "name": "screenshot", + "contentType": "image/png", + "path": "/home/senke/git/talas/veza/apps/web/test-results/auth-Authentication-Flow-s-74377-ully-with-valid-credentials-msedge/test-failed-1.png" + }, + { + "name": "video", + "contentType": "video/webm", + "path": "/home/senke/git/talas/veza/apps/web/test-results/auth-Authentication-Flow-s-74377-ully-with-valid-credentials-msedge/video.webm" + } + ], + "errorLocation": { + "file": "/home/senke/git/talas/veza/apps/web/e2e/auth.spec.ts", + "column": 16, + "line": 44 + } + } + ], + "status": "unexpected" + } + ], + "id": "d748ac400d08b85935ef-6752a9a2e7a05cd11902", + "file": "auth.spec.ts", + "line": 41, + "column": 3 + }, + { + "title": "should show error with invalid credentials", + "ok": false, + "tags": [], + "tests": [ + { + "timeout": 60000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "msedge", + "projectName": "msedge", + "results": [ + { + "workerIndex": 14, + "parallelIndex": 0, + "status": "failed", + "duration": 216, + "error": { + "message": "Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/login\nCall log:\n\u001b[2m - navigating to \"http://localhost:5173/login\", waiting until \"load\"\u001b[22m\n", + "stack": "Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/login\nCall log:\n\u001b[2m - navigating to \"http://localhost:5173/login\", waiting until \"load\"\u001b[22m\n\n at /home/senke/git/talas/veza/apps/web/e2e/auth.spec.ts:110:16", + "location": { + "file": "/home/senke/git/talas/veza/apps/web/e2e/auth.spec.ts", + "column": 16, + "line": 110 + }, + "snippet": "\u001b[0m \u001b[90m 108 |\u001b[39m console\u001b[33m.\u001b[39mlog(\u001b[32m'🧪 [AUTH TEST] Running: Login with invalid credentials'\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 109 |\u001b[39m\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 110 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mgoto(\u001b[32m`${TEST_CONFIG.FRONTEND_URL}/login`\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 111 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mwaitForLoadState(\u001b[32m'domcontentloaded'\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 112 |\u001b[39m\n \u001b[90m 113 |\u001b[39m \u001b[90m// Remplir avec des credentials invalides\u001b[39m\u001b[0m" + }, + "errors": [ + { + "location": { + "file": "/home/senke/git/talas/veza/apps/web/e2e/auth.spec.ts", + "column": 16, + "line": 110 + }, + "message": "Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/login\nCall log:\n\u001b[2m - navigating to \"http://localhost:5173/login\", waiting until \"load\"\u001b[22m\n\n\n\u001b[0m \u001b[90m 108 |\u001b[39m console\u001b[33m.\u001b[39mlog(\u001b[32m'🧪 [AUTH TEST] Running: Login with invalid credentials'\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 109 |\u001b[39m\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 110 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mgoto(\u001b[32m`${TEST_CONFIG.FRONTEND_URL}/login`\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 111 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mwaitForLoadState(\u001b[32m'domcontentloaded'\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 112 |\u001b[39m\n \u001b[90m 113 |\u001b[39m \u001b[90m// Remplir avec des credentials invalides\u001b[39m\u001b[0m\n\u001b[2m at /home/senke/git/talas/veza/apps/web/e2e/auth.spec.ts:110:16\u001b[22m" + } + ], + "stdout": [ + { + "text": "🧪 [AUTH TEST] Running: Login with invalid credentials\n" + }, + { + "text": "🔴 [REQUEST FAILED] GET http://localhost:5173/login: net::ERR_CONNECTION_REFUSED\n" + }, + { + "text": "\n📊 [AUTH TEST] === Final Verifications ===\n" + }, + { + "text": "✅ [AUTH TEST] No console errors\n" + }, + { + "text": "🔴 [AUTH TEST] Network errors (1):\n" + }, + { + "text": " - GET http://localhost:5173/login: 0\n" + } + ], + "stderr": [], + "retry": 0, + "startTime": "2026-01-10T21:52:14.545Z", + "annotations": [], + "attachments": [ + { + "name": "screenshot", + "contentType": "image/png", + "path": "/home/senke/git/talas/veza/apps/web/test-results/auth-Authentication-Flow-s-22fb7-or-with-invalid-credentials-msedge/test-failed-1.png" + }, + { + "name": "video", + "contentType": "video/webm", + "path": "/home/senke/git/talas/veza/apps/web/test-results/auth-Authentication-Flow-s-22fb7-or-with-invalid-credentials-msedge/video.webm" + } + ], + "errorLocation": { + "file": "/home/senke/git/talas/veza/apps/web/e2e/auth.spec.ts", + "column": 16, + "line": 110 + } + } + ], + "status": "unexpected" + } + ], + "id": "d748ac400d08b85935ef-59c9cf8302a5a962929c", + "file": "auth.spec.ts", + "line": 107, + "column": 3 + }, + { + "title": "should register a new user successfully", + "ok": false, + "tags": [], + "tests": [ + { + "timeout": 60000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "msedge", + "projectName": "msedge", + "results": [ + { + "workerIndex": 15, + "parallelIndex": 0, + "status": "failed", + "duration": 225, + "error": { + "message": "Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/register\nCall log:\n\u001b[2m - navigating to \"http://localhost:5173/register\", waiting until \"load\"\u001b[22m\n", + "stack": "Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/register\nCall log:\n\u001b[2m - navigating to \"http://localhost:5173/register\", waiting until \"load\"\u001b[22m\n\n at /home/senke/git/talas/veza/apps/web/e2e/auth.spec.ts:137:16", + "location": { + "file": "/home/senke/git/talas/veza/apps/web/e2e/auth.spec.ts", + "column": 16, + "line": 137 + }, + "snippet": "\u001b[0m \u001b[90m 135 |\u001b[39m console\u001b[33m.\u001b[39mlog(\u001b[32m'🧪 [AUTH TEST] Running: User registration'\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 136 |\u001b[39m\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 137 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mgoto(\u001b[32m`${TEST_CONFIG.FRONTEND_URL}/register`\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 138 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mwaitForLoadState(\u001b[32m'domcontentloaded'\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 139 |\u001b[39m\n \u001b[90m 140 |\u001b[39m \u001b[90m// Attendre que la page soit complètement chargée\u001b[39m\u001b[0m" + }, + "errors": [ + { + "location": { + "file": "/home/senke/git/talas/veza/apps/web/e2e/auth.spec.ts", + "column": 16, + "line": 137 + }, + "message": "Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/register\nCall log:\n\u001b[2m - navigating to \"http://localhost:5173/register\", waiting until \"load\"\u001b[22m\n\n\n\u001b[0m \u001b[90m 135 |\u001b[39m console\u001b[33m.\u001b[39mlog(\u001b[32m'🧪 [AUTH TEST] Running: User registration'\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 136 |\u001b[39m\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 137 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mgoto(\u001b[32m`${TEST_CONFIG.FRONTEND_URL}/register`\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 138 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mwaitForLoadState(\u001b[32m'domcontentloaded'\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 139 |\u001b[39m\n \u001b[90m 140 |\u001b[39m \u001b[90m// Attendre que la page soit complètement chargée\u001b[39m\u001b[0m\n\u001b[2m at /home/senke/git/talas/veza/apps/web/e2e/auth.spec.ts:137:16\u001b[22m" + } + ], + "stdout": [ + { + "text": "🧪 [AUTH TEST] Running: User registration\n" + }, + { + "text": "🔴 [REQUEST FAILED] GET http://localhost:5173/register: net::ERR_CONNECTION_REFUSED\n" + }, + { + "text": "\n📊 [AUTH TEST] === Final Verifications ===\n" + }, + { + "text": "✅ [AUTH TEST] No console errors\n" + }, + { + "text": "🔴 [AUTH TEST] Network errors (1):\n" + }, + { + "text": " - GET http://localhost:5173/register: 0\n" + } + ], + "stderr": [], + "retry": 0, + "startTime": "2026-01-10T21:52:15.612Z", + "annotations": [], + "attachments": [ + { + "name": "screenshot", + "contentType": "image/png", + "path": "/home/senke/git/talas/veza/apps/web/test-results/auth-Authentication-Flow-s-53dd2-ter-a-new-user-successfully-msedge/test-failed-1.png" + }, + { + "name": "video", + "contentType": "video/webm", + "path": "/home/senke/git/talas/veza/apps/web/test-results/auth-Authentication-Flow-s-53dd2-ter-a-new-user-successfully-msedge/video.webm" + } + ], + "errorLocation": { + "file": "/home/senke/git/talas/veza/apps/web/e2e/auth.spec.ts", + "column": 16, + "line": 137 + } + } + ], + "status": "unexpected" + } + ], + "id": "d748ac400d08b85935ef-89d90a66686f781c5e5b", + "file": "auth.spec.ts", + "line": 134, + "column": 3 + }, + { + "title": "should show error when registering with existing email", + "ok": false, + "tags": [], + "tests": [ + { + "timeout": 60000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "msedge", + "projectName": "msedge", + "results": [ + { + "workerIndex": 16, + "parallelIndex": 0, + "status": "failed", + "duration": 208, + "error": { + "message": "Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/register\nCall log:\n\u001b[2m - navigating to \"http://localhost:5173/register\", waiting until \"load\"\u001b[22m\n", + "stack": "Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/register\nCall log:\n\u001b[2m - navigating to \"http://localhost:5173/register\", waiting until \"load\"\u001b[22m\n\n at /home/senke/git/talas/veza/apps/web/e2e/auth.spec.ts:234:16", + "location": { + "file": "/home/senke/git/talas/veza/apps/web/e2e/auth.spec.ts", + "column": 16, + "line": 234 + }, + "snippet": "\u001b[0m \u001b[90m 232 |\u001b[39m console\u001b[33m.\u001b[39mlog(\u001b[32m'🧪 [AUTH TEST] Running: Registration with existing email'\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 233 |\u001b[39m\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 234 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mgoto(\u001b[32m`${TEST_CONFIG.FRONTEND_URL}/register`\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 235 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mwaitForLoadState(\u001b[32m'domcontentloaded'\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 236 |\u001b[39m\n \u001b[90m 237 |\u001b[39m \u001b[90m// Attendre que la page soit complètement chargée\u001b[39m\u001b[0m" + }, + "errors": [ + { + "location": { + "file": "/home/senke/git/talas/veza/apps/web/e2e/auth.spec.ts", + "column": 16, + "line": 234 + }, + "message": "Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/register\nCall log:\n\u001b[2m - navigating to \"http://localhost:5173/register\", waiting until \"load\"\u001b[22m\n\n\n\u001b[0m \u001b[90m 232 |\u001b[39m console\u001b[33m.\u001b[39mlog(\u001b[32m'🧪 [AUTH TEST] Running: Registration with existing email'\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 233 |\u001b[39m\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 234 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mgoto(\u001b[32m`${TEST_CONFIG.FRONTEND_URL}/register`\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 235 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mwaitForLoadState(\u001b[32m'domcontentloaded'\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 236 |\u001b[39m\n \u001b[90m 237 |\u001b[39m \u001b[90m// Attendre que la page soit complètement chargée\u001b[39m\u001b[0m\n\u001b[2m at /home/senke/git/talas/veza/apps/web/e2e/auth.spec.ts:234:16\u001b[22m" + } + ], + "stdout": [ + { + "text": "🧪 [AUTH TEST] Running: Registration with existing email\n" + }, + { + "text": "🔴 [REQUEST FAILED] GET http://localhost:5173/register: net::ERR_CONNECTION_REFUSED\n" + }, + { + "text": "\n📊 [AUTH TEST] === Final Verifications ===\n" + }, + { + "text": "✅ [AUTH TEST] No console errors\n" + }, + { + "text": "🔴 [AUTH TEST] Network errors (1):\n" + }, + { + "text": " - GET http://localhost:5173/register: 0\n" + } + ], + "stderr": [], + "retry": 0, + "startTime": "2026-01-10T21:52:16.683Z", + "annotations": [], + "attachments": [ + { + "name": "screenshot", + "contentType": "image/png", + "path": "/home/senke/git/talas/veza/apps/web/test-results/auth-Authentication-Flow-s-3c58b-stering-with-existing-email-msedge/test-failed-1.png" + }, + { + "name": "video", + "contentType": "video/webm", + "path": "/home/senke/git/talas/veza/apps/web/test-results/auth-Authentication-Flow-s-3c58b-stering-with-existing-email-msedge/video.webm" + } + ], + "errorLocation": { + "file": "/home/senke/git/talas/veza/apps/web/e2e/auth.spec.ts", + "column": 16, + "line": 234 + } + } + ], + "status": "unexpected" + } + ], + "id": "d748ac400d08b85935ef-46e52fdc699a850c1641", + "file": "auth.spec.ts", + "line": 231, + "column": 3 + }, + { + "title": "should logout successfully", + "ok": false, + "tags": [], + "tests": [ + { + "timeout": 60000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "msedge", + "projectName": "msedge", + "results": [ + { + "workerIndex": 17, + "parallelIndex": 0, + "status": "failed", + "duration": 2776, + "error": { + "message": "Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/login\nCall log:\n\u001b[2m - navigating to \"http://localhost:5173/login\", waiting until \"domcontentloaded\"\u001b[22m\n", + "stack": "Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/login\nCall log:\n\u001b[2m - navigating to \"http://localhost:5173/login\", waiting until \"domcontentloaded\"\u001b[22m\n\n at loginAsUser (/home/senke/git/talas/veza/apps/web/e2e/utils/test-helpers.ts:174:18)\n at /home/senke/git/talas/veza/apps/web/e2e/auth.spec.ts:297:5", + "location": { + "file": "/home/senke/git/talas/veza/apps/web/e2e/utils/test-helpers.ts", + "column": 18, + "line": 174 + }, + "snippet": "\u001b[90m at \u001b[39mutils/test-helpers.ts:174\n\n\u001b[0m \u001b[90m 172 |\u001b[39m \u001b[36mwhile\u001b[39m (retries \u001b[33m>\u001b[39m \u001b[35m0\u001b[39m) {\n \u001b[90m 173 |\u001b[39m \u001b[36mtry\u001b[39m {\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 174 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mgoto(\u001b[32m`${TEST_CONFIG.FRONTEND_URL}/login`\u001b[39m\u001b[33m,\u001b[39m {\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 175 |\u001b[39m waitUntil\u001b[33m:\u001b[39m \u001b[32m'domcontentloaded'\u001b[39m\u001b[33m,\u001b[39m\n \u001b[90m 176 |\u001b[39m timeout\u001b[33m:\u001b[39m \u001b[33mTEST_CONFIG\u001b[39m\u001b[33m.\u001b[39m\u001b[33mDEFAULT_TIMEOUT\u001b[39m\u001b[33m,\u001b[39m\n \u001b[90m 177 |\u001b[39m })\u001b[33m;\u001b[39m\u001b[0m" + }, + "errors": [ + { + "location": { + "file": "/home/senke/git/talas/veza/apps/web/e2e/utils/test-helpers.ts", + "column": 18, + "line": 174 + }, + "message": "Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/login\nCall log:\n\u001b[2m - navigating to \"http://localhost:5173/login\", waiting until \"domcontentloaded\"\u001b[22m\n\n\n\u001b[90m at \u001b[39mutils/test-helpers.ts:174\n\n\u001b[0m \u001b[90m 172 |\u001b[39m \u001b[36mwhile\u001b[39m (retries \u001b[33m>\u001b[39m \u001b[35m0\u001b[39m) {\n \u001b[90m 173 |\u001b[39m \u001b[36mtry\u001b[39m {\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 174 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mgoto(\u001b[32m`${TEST_CONFIG.FRONTEND_URL}/login`\u001b[39m\u001b[33m,\u001b[39m {\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 175 |\u001b[39m waitUntil\u001b[33m:\u001b[39m \u001b[32m'domcontentloaded'\u001b[39m\u001b[33m,\u001b[39m\n \u001b[90m 176 |\u001b[39m timeout\u001b[33m:\u001b[39m \u001b[33mTEST_CONFIG\u001b[39m\u001b[33m.\u001b[39m\u001b[33mDEFAULT_TIMEOUT\u001b[39m\u001b[33m,\u001b[39m\n \u001b[90m 177 |\u001b[39m })\u001b[33m;\u001b[39m\u001b[0m\n\u001b[2m at loginAsUser (/home/senke/git/talas/veza/apps/web/e2e/utils/test-helpers.ts:174:18)\u001b[22m\n\u001b[2m at /home/senke/git/talas/veza/apps/web/e2e/auth.spec.ts:297:5\u001b[22m" + } + ], + "stdout": [ + { + "text": "🧪 [AUTH TEST] Running: Logout\n" + }, + { + "text": "🔐 [LOGIN] Attempting authentication as e2e@test.com...\n" + }, + { + "text": "⏳ [LOGIN] Waiting 500ms before login (1768081937863ms since last login)...\n" + }, + { + "text": "🔴 [REQUEST FAILED] GET http://localhost:5173/login: net::ERR_CONNECTION_REFUSED\n" + }, + { + "text": "🔴 [REQUEST FAILED] GET http://localhost:5173/login: net::ERR_CONNECTION_REFUSED\n" + }, + { + "text": "🔴 [REQUEST FAILED] GET http://localhost:5173/login: net::ERR_CONNECTION_REFUSED\n" + }, + { + "text": "\n📊 [AUTH TEST] === Final Verifications ===\n" + }, + { + "text": "✅ [AUTH TEST] No console errors\n" + }, + { + "text": "🔴 [AUTH TEST] Network errors (3):\n" + }, + { + "text": " - GET http://localhost:5173/login: 0\n" + }, + { + "text": " - GET http://localhost:5173/login: 0\n" + }, + { + "text": " - GET http://localhost:5173/login: 0\n" + } + ], + "stderr": [ + { + "text": "⚠️ [LOGIN] Navigation failed (retries left: 2): page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/login\nCall log:\n\u001b[2m - navigating to \"http://localhost:5173/login\", waiting until \"domcontentloaded\"\u001b[22m\n\n at loginAsUser \u001b[90m(/home/senke/git/talas/veza/apps/web/\u001b[39me2e/utils/test-helpers.ts:174:18\u001b[90m)\u001b[39m\n at \u001b[90m/home/senke/git/talas/veza/apps/web/\u001b[39me2e/auth.spec.ts:297:5 {\n name: \u001b[32m'Error'\u001b[39m,\n [\u001b[32mSymbol(step)\u001b[39m]: {\n location: {\n file: \u001b[32m'/home/senke/git/talas/veza/apps/web/e2e/utils/test-helpers.ts'\u001b[39m,\n line: \u001b[33m174\u001b[39m,\n column: \u001b[33m18\u001b[39m,\n function: \u001b[32m'loginAsUser'\u001b[39m\n },\n category: \u001b[32m'pw:api'\u001b[39m,\n title: \u001b[32m'Navigate to \"/login\"'\u001b[39m,\n apiName: \u001b[32m'page.goto'\u001b[39m,\n params: {\n url: \u001b[32m'http://localhost:5173/login'\u001b[39m,\n waitUntil: \u001b[32m'domcontentloaded'\u001b[39m,\n timeout: \u001b[33m30000\u001b[39m\n },\n group: \u001b[90mundefined\u001b[39m,\n stepId: \u001b[32m'pw:api@36'\u001b[39m,\n boxedStack: \u001b[90mundefined\u001b[39m,\n steps: [],\n attachmentIndices: [],\n info: TestStepInfoImpl {\n annotations: [],\n _testInfo: \u001b[36m[TestInfoImpl]\u001b[39m,\n _stepId: \u001b[32m'pw:api@36'\u001b[39m,\n _title: \u001b[32m'Navigate to \"/login\"'\u001b[39m,\n _parentStep: \u001b[90mundefined\u001b[39m,\n skip: \u001b[36m[Function (anonymous)]\u001b[39m\n },\n complete: \u001b[36m[Function: complete]\u001b[39m,\n endWallTime: \u001b[33m1768081938386\u001b[39m,\n error: {\n message: \u001b[32m'Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/login\\n'\u001b[39m +\n \u001b[32m'Call log:\\n'\u001b[39m +\n \u001b[32m'\\x1B[2m - navigating to \"http://localhost:5173/login\", waiting until \"domcontentloaded\"\\x1B[22m\\n'\u001b[39m,\n stack: \u001b[32m'Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/login\\n'\u001b[39m +\n \u001b[32m'Call log:\\n'\u001b[39m +\n \u001b[32m'\\x1B[2m - navigating to \"http://localhost:5173/login\", waiting until \"domcontentloaded\"\\x1B[22m\\n'\u001b[39m +\n \u001b[32m'\\n'\u001b[39m +\n \u001b[32m' at loginAsUser (/home/senke/git/talas/veza/apps/web/e2e/utils/test-helpers.ts:174:18)\\n'\u001b[39m +\n \u001b[32m' at /home/senke/git/talas/veza/apps/web/e2e/auth.spec.ts:297:5'\u001b[39m,\n cause: \u001b[90mundefined\u001b[39m\n }\n }\n}\n" + }, + { + "text": "⚠️ [LOGIN] Navigation failed (retries left: 1): page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/login\nCall log:\n\u001b[2m - navigating to \"http://localhost:5173/login\", waiting until \"domcontentloaded\"\u001b[22m\n\n at loginAsUser \u001b[90m(/home/senke/git/talas/veza/apps/web/\u001b[39me2e/utils/test-helpers.ts:174:18\u001b[90m)\u001b[39m\n at \u001b[90m/home/senke/git/talas/veza/apps/web/\u001b[39me2e/auth.spec.ts:297:5 {\n name: \u001b[32m'Error'\u001b[39m,\n [\u001b[32mSymbol(step)\u001b[39m]: {\n location: {\n file: \u001b[32m'/home/senke/git/talas/veza/apps/web/e2e/utils/test-helpers.ts'\u001b[39m,\n line: \u001b[33m174\u001b[39m,\n column: \u001b[33m18\u001b[39m,\n function: \u001b[32m'loginAsUser'\u001b[39m\n },\n category: \u001b[32m'pw:api'\u001b[39m,\n title: \u001b[32m'Navigate to \"/login\"'\u001b[39m,\n apiName: \u001b[32m'page.goto'\u001b[39m,\n params: {\n url: \u001b[32m'http://localhost:5173/login'\u001b[39m,\n waitUntil: \u001b[32m'domcontentloaded'\u001b[39m,\n timeout: \u001b[33m30000\u001b[39m\n },\n group: \u001b[90mundefined\u001b[39m,\n stepId: \u001b[32m'pw:api@38'\u001b[39m,\n boxedStack: \u001b[90mundefined\u001b[39m,\n steps: [],\n attachmentIndices: [],\n info: TestStepInfoImpl {\n annotations: [],\n _testInfo: \u001b[36m[TestInfoImpl]\u001b[39m,\n _stepId: \u001b[32m'pw:api@38'\u001b[39m,\n _title: \u001b[32m'Navigate to \"/login\"'\u001b[39m,\n _parentStep: \u001b[90mundefined\u001b[39m,\n skip: \u001b[36m[Function (anonymous)]\u001b[39m\n },\n complete: \u001b[36m[Function: complete]\u001b[39m,\n endWallTime: \u001b[33m1768081939409\u001b[39m,\n error: {\n message: \u001b[32m'Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/login\\n'\u001b[39m +\n \u001b[32m'Call log:\\n'\u001b[39m +\n \u001b[32m'\\x1B[2m - navigating to \"http://localhost:5173/login\", waiting until \"domcontentloaded\"\\x1B[22m\\n'\u001b[39m,\n stack: \u001b[32m'Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/login\\n'\u001b[39m +\n \u001b[32m'Call log:\\n'\u001b[39m +\n \u001b[32m'\\x1B[2m - navigating to \"http://localhost:5173/login\", waiting until \"domcontentloaded\"\\x1B[22m\\n'\u001b[39m +\n \u001b[32m'\\n'\u001b[39m +\n \u001b[32m' at loginAsUser (/home/senke/git/talas/veza/apps/web/e2e/utils/test-helpers.ts:174:18)\\n'\u001b[39m +\n \u001b[32m' at /home/senke/git/talas/veza/apps/web/e2e/auth.spec.ts:297:5'\u001b[39m,\n cause: \u001b[90mundefined\u001b[39m\n }\n }\n}\n" + }, + { + "text": "⚠️ [LOGIN] Navigation failed (retries left: 0): page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/login\nCall log:\n\u001b[2m - navigating to \"http://localhost:5173/login\", waiting until \"domcontentloaded\"\u001b[22m\n\n at loginAsUser \u001b[90m(/home/senke/git/talas/veza/apps/web/\u001b[39me2e/utils/test-helpers.ts:174:18\u001b[90m)\u001b[39m\n at \u001b[90m/home/senke/git/talas/veza/apps/web/\u001b[39me2e/auth.spec.ts:297:5 {\n name: \u001b[32m'Error'\u001b[39m,\n [\u001b[32mSymbol(step)\u001b[39m]: {\n location: {\n file: \u001b[32m'/home/senke/git/talas/veza/apps/web/e2e/utils/test-helpers.ts'\u001b[39m,\n line: \u001b[33m174\u001b[39m,\n column: \u001b[33m18\u001b[39m,\n function: \u001b[32m'loginAsUser'\u001b[39m\n },\n category: \u001b[32m'pw:api'\u001b[39m,\n title: \u001b[32m'Navigate to \"/login\"'\u001b[39m,\n apiName: \u001b[32m'page.goto'\u001b[39m,\n params: {\n url: \u001b[32m'http://localhost:5173/login'\u001b[39m,\n waitUntil: \u001b[32m'domcontentloaded'\u001b[39m,\n timeout: \u001b[33m30000\u001b[39m\n },\n group: \u001b[90mundefined\u001b[39m,\n stepId: \u001b[32m'pw:api@40'\u001b[39m,\n boxedStack: \u001b[90mundefined\u001b[39m,\n steps: [],\n attachmentIndices: [],\n info: TestStepInfoImpl {\n annotations: [],\n _testInfo: \u001b[36m[TestInfoImpl]\u001b[39m,\n _stepId: \u001b[32m'pw:api@40'\u001b[39m,\n _title: \u001b[32m'Navigate to \"/login\"'\u001b[39m,\n _parentStep: \u001b[90mundefined\u001b[39m,\n skip: \u001b[36m[Function (anonymous)]\u001b[39m\n },\n complete: \u001b[36m[Function: complete]\u001b[39m,\n endWallTime: \u001b[33m1768081940419\u001b[39m,\n error: {\n message: \u001b[32m'Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/login\\n'\u001b[39m +\n \u001b[32m'Call log:\\n'\u001b[39m +\n \u001b[32m'\\x1B[2m - navigating to \"http://localhost:5173/login\", waiting until \"domcontentloaded\"\\x1B[22m\\n'\u001b[39m,\n stack: \u001b[32m'Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/login\\n'\u001b[39m +\n \u001b[32m'Call log:\\n'\u001b[39m +\n \u001b[32m'\\x1B[2m - navigating to \"http://localhost:5173/login\", waiting until \"domcontentloaded\"\\x1B[22m\\n'\u001b[39m +\n \u001b[32m'\\n'\u001b[39m +\n \u001b[32m' at loginAsUser (/home/senke/git/talas/veza/apps/web/e2e/utils/test-helpers.ts:174:18)\\n'\u001b[39m +\n \u001b[32m' at /home/senke/git/talas/veza/apps/web/e2e/auth.spec.ts:297:5'\u001b[39m,\n cause: \u001b[90mundefined\u001b[39m\n }\n }\n}\n" + } + ], + "retry": 0, + "startTime": "2026-01-10T21:52:17.741Z", + "annotations": [], + "attachments": [ + { + "name": "screenshot", + "contentType": "image/png", + "path": "/home/senke/git/talas/veza/apps/web/test-results/auth-Authentication-Flow-should-logout-successfully-msedge/test-failed-1.png" + }, + { + "name": "video", + "contentType": "video/webm", + "path": "/home/senke/git/talas/veza/apps/web/test-results/auth-Authentication-Flow-should-logout-successfully-msedge/video.webm" + } + ], + "errorLocation": { + "file": "/home/senke/git/talas/veza/apps/web/e2e/utils/test-helpers.ts", + "column": 18, + "line": 174 + } + } + ], + "status": "unexpected" + } + ], + "id": "d748ac400d08b85935ef-3df7d64e9abaa55ead40", + "file": "auth.spec.ts", + "line": 293, + "column": 3 + }, + { + "title": "should redirect to login when accessing protected route without auth", + "ok": false, + "tags": [], + "tests": [ + { + "timeout": 60000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "msedge", + "projectName": "msedge", + "results": [ + { + "workerIndex": 18, + "parallelIndex": 0, + "status": "failed", + "duration": 230, + "error": { + "message": "Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/login\nCall log:\n\u001b[2m - navigating to \"http://localhost:5173/login\", waiting until \"load\"\u001b[22m\n", + "stack": "Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/login\nCall log:\n\u001b[2m - navigating to \"http://localhost:5173/login\", waiting until \"load\"\u001b[22m\n\n at /home/senke/git/talas/veza/apps/web/e2e/auth.spec.ts:377:16", + "location": { + "file": "/home/senke/git/talas/veza/apps/web/e2e/auth.spec.ts", + "column": 16, + "line": 377 + }, + "snippet": "\u001b[0m \u001b[90m 375 |\u001b[39m\n \u001b[90m 376 |\u001b[39m \u001b[90m// S'assurer qu'il n'y a pas de token dans le localStorage\u001b[39m\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 377 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mgoto(\u001b[32m`${TEST_CONFIG.FRONTEND_URL}/login`\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 378 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mevaluate(() \u001b[33m=>\u001b[39m localStorage\u001b[33m.\u001b[39mclear())\u001b[33m;\u001b[39m\n \u001b[90m 379 |\u001b[39m\n \u001b[90m 380 |\u001b[39m \u001b[90m// Tenter d'accéder à une route protégée\u001b[39m\u001b[0m" + }, + "errors": [ + { + "location": { + "file": "/home/senke/git/talas/veza/apps/web/e2e/auth.spec.ts", + "column": 16, + "line": 377 + }, + "message": "Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/login\nCall log:\n\u001b[2m - navigating to \"http://localhost:5173/login\", waiting until \"load\"\u001b[22m\n\n\n\u001b[0m \u001b[90m 375 |\u001b[39m\n \u001b[90m 376 |\u001b[39m \u001b[90m// S'assurer qu'il n'y a pas de token dans le localStorage\u001b[39m\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 377 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mgoto(\u001b[32m`${TEST_CONFIG.FRONTEND_URL}/login`\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 378 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mevaluate(() \u001b[33m=>\u001b[39m localStorage\u001b[33m.\u001b[39mclear())\u001b[33m;\u001b[39m\n \u001b[90m 379 |\u001b[39m\n \u001b[90m 380 |\u001b[39m \u001b[90m// Tenter d'accéder à une route protégée\u001b[39m\u001b[0m\n\u001b[2m at /home/senke/git/talas/veza/apps/web/e2e/auth.spec.ts:377:16\u001b[22m" + } + ], + "stdout": [ + { + "text": "🧪 [AUTH TEST] Running: Route guard test\n" + }, + { + "text": "🔴 [REQUEST FAILED] GET http://localhost:5173/login: net::ERR_CONNECTION_REFUSED\n" + }, + { + "text": "\n📊 [AUTH TEST] === Final Verifications ===\n" + }, + { + "text": "✅ [AUTH TEST] No console errors\n" + }, + { + "text": "🔴 [AUTH TEST] Network errors (1):\n" + }, + { + "text": " - GET http://localhost:5173/login: 0\n" + } + ], + "stderr": [], + "retry": 0, + "startTime": "2026-01-10T21:52:21.273Z", + "annotations": [], + "attachments": [ + { + "name": "screenshot", + "contentType": "image/png", + "path": "/home/senke/git/talas/veza/apps/web/test-results/auth-Authentication-Flow-s-f8244-rotected-route-without-auth-msedge/test-failed-1.png" + }, + { + "name": "video", + "contentType": "video/webm", + "path": "/home/senke/git/talas/veza/apps/web/test-results/auth-Authentication-Flow-s-f8244-rotected-route-without-auth-msedge/video.webm" + } + ], + "errorLocation": { + "file": "/home/senke/git/talas/veza/apps/web/e2e/auth.spec.ts", + "column": 16, + "line": 377 + } + } + ], + "status": "unexpected" + } + ], + "id": "d748ac400d08b85935ef-c4b67bfd2f9f5e5089d5", + "file": "auth.spec.ts", + "line": 373, + "column": 3 + }, + { + "title": "should persist authentication after page refresh", + "ok": false, + "tags": [], + "tests": [ + { + "timeout": 60000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "msedge", + "projectName": "msedge", + "results": [ + { + "workerIndex": 19, + "parallelIndex": 0, + "status": "failed", + "duration": 12734, + "error": { + "message": "Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/login\nCall log:\n\u001b[2m - navigating to \"http://localhost:5173/login\", waiting until \"domcontentloaded\"\u001b[22m\n", + "stack": "Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/login\nCall log:\n\u001b[2m - navigating to \"http://localhost:5173/login\", waiting until \"domcontentloaded\"\u001b[22m\n\n at loginAsUser (/home/senke/git/talas/veza/apps/web/e2e/utils/test-helpers.ts:174:18)\n at /home/senke/git/talas/veza/apps/web/e2e/auth.spec.ts:403:5", + "location": { + "file": "/home/senke/git/talas/veza/apps/web/e2e/utils/test-helpers.ts", + "column": 18, + "line": 174 + }, + "snippet": "\u001b[90m at \u001b[39mutils/test-helpers.ts:174\n\n\u001b[0m \u001b[90m 172 |\u001b[39m \u001b[36mwhile\u001b[39m (retries \u001b[33m>\u001b[39m \u001b[35m0\u001b[39m) {\n \u001b[90m 173 |\u001b[39m \u001b[36mtry\u001b[39m {\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 174 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mgoto(\u001b[32m`${TEST_CONFIG.FRONTEND_URL}/login`\u001b[39m\u001b[33m,\u001b[39m {\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 175 |\u001b[39m waitUntil\u001b[33m:\u001b[39m \u001b[32m'domcontentloaded'\u001b[39m\u001b[33m,\u001b[39m\n \u001b[90m 176 |\u001b[39m timeout\u001b[33m:\u001b[39m \u001b[33mTEST_CONFIG\u001b[39m\u001b[33m.\u001b[39m\u001b[33mDEFAULT_TIMEOUT\u001b[39m\u001b[33m,\u001b[39m\n \u001b[90m 177 |\u001b[39m })\u001b[33m;\u001b[39m\u001b[0m" + }, + "errors": [ + { + "location": { + "file": "/home/senke/git/talas/veza/apps/web/e2e/utils/test-helpers.ts", + "column": 18, + "line": 174 + }, + "message": "Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/login\nCall log:\n\u001b[2m - navigating to \"http://localhost:5173/login\", waiting until \"domcontentloaded\"\u001b[22m\n\n\n\u001b[90m at \u001b[39mutils/test-helpers.ts:174\n\n\u001b[0m \u001b[90m 172 |\u001b[39m \u001b[36mwhile\u001b[39m (retries \u001b[33m>\u001b[39m \u001b[35m0\u001b[39m) {\n \u001b[90m 173 |\u001b[39m \u001b[36mtry\u001b[39m {\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 174 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mgoto(\u001b[32m`${TEST_CONFIG.FRONTEND_URL}/login`\u001b[39m\u001b[33m,\u001b[39m {\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 175 |\u001b[39m waitUntil\u001b[33m:\u001b[39m \u001b[32m'domcontentloaded'\u001b[39m\u001b[33m,\u001b[39m\n \u001b[90m 176 |\u001b[39m timeout\u001b[33m:\u001b[39m \u001b[33mTEST_CONFIG\u001b[39m\u001b[33m.\u001b[39m\u001b[33mDEFAULT_TIMEOUT\u001b[39m\u001b[33m,\u001b[39m\n \u001b[90m 177 |\u001b[39m })\u001b[33m;\u001b[39m\u001b[0m\n\u001b[2m at loginAsUser (/home/senke/git/talas/veza/apps/web/e2e/utils/test-helpers.ts:174:18)\u001b[22m\n\u001b[2m at /home/senke/git/talas/veza/apps/web/e2e/auth.spec.ts:403:5\u001b[22m" + } + ], + "stdout": [ + { + "text": "🧪 [AUTH TEST] Running: Auth persistence test\n" + }, + { + "text": "🔐 [LOGIN] Attempting authentication as e2e@test.com...\n" + }, + { + "text": "⏳ [LOGIN] Waiting 500ms before login (1768081952393ms since last login)...\n" + }, + { + "text": "🔴 [REQUEST FAILED] GET http://localhost:5173/login: net::ERR_CONNECTION_REFUSED\n" + }, + { + "text": "🔴 [REQUEST FAILED] GET http://localhost:5173/login: net::ERR_CONNECTION_REFUSED\n" + }, + { + "text": "🔴 [REQUEST FAILED] GET http://localhost:5173/login: net::ERR_CONNECTION_REFUSED\n" + }, + { + "text": "\n📊 [AUTH TEST] === Final Verifications ===\n" + }, + { + "text": "✅ [AUTH TEST] No console errors\n" + }, + { + "text": "🔴 [AUTH TEST] Network errors (3):\n" + }, + { + "text": " - GET http://localhost:5173/login: 0\n" + }, + { + "text": " - GET http://localhost:5173/login: 0\n" + }, + { + "text": " - GET http://localhost:5173/login: 0\n" + } + ], + "stderr": [ + { + "text": "⚠️ [LOGIN] Navigation failed (retries left: 2): page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/login\nCall log:\n\u001b[2m - navigating to \"http://localhost:5173/login\", waiting until \"domcontentloaded\"\u001b[22m\n\n at loginAsUser \u001b[90m(/home/senke/git/talas/veza/apps/web/\u001b[39me2e/utils/test-helpers.ts:174:18\u001b[90m)\u001b[39m\n at \u001b[90m/home/senke/git/talas/veza/apps/web/\u001b[39me2e/auth.spec.ts:403:5 {\n name: \u001b[32m'Error'\u001b[39m,\n [\u001b[32mSymbol(step)\u001b[39m]: {\n location: {\n file: \u001b[32m'/home/senke/git/talas/veza/apps/web/e2e/utils/test-helpers.ts'\u001b[39m,\n line: \u001b[33m174\u001b[39m,\n column: \u001b[33m18\u001b[39m,\n function: \u001b[32m'loginAsUser'\u001b[39m\n },\n category: \u001b[32m'pw:api'\u001b[39m,\n title: \u001b[32m'Navigate to \"/login\"'\u001b[39m,\n apiName: \u001b[32m'page.goto'\u001b[39m,\n params: {\n url: \u001b[32m'http://localhost:5173/login'\u001b[39m,\n waitUntil: \u001b[32m'domcontentloaded'\u001b[39m,\n timeout: \u001b[33m30000\u001b[39m\n },\n group: \u001b[90mundefined\u001b[39m,\n stepId: \u001b[32m'pw:api@37'\u001b[39m,\n boxedStack: \u001b[90mundefined\u001b[39m,\n steps: [],\n attachmentIndices: [],\n info: TestStepInfoImpl {\n annotations: [],\n _testInfo: \u001b[36m[TestInfoImpl]\u001b[39m,\n _stepId: \u001b[32m'pw:api@37'\u001b[39m,\n _title: \u001b[32m'Navigate to \"/login\"'\u001b[39m,\n _parentStep: \u001b[90mundefined\u001b[39m,\n skip: \u001b[36m[Function (anonymous)]\u001b[39m\n },\n complete: \u001b[36m[Function: complete]\u001b[39m,\n endWallTime: \u001b[33m1768081952903\u001b[39m,\n error: {\n message: \u001b[32m'Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/login\\n'\u001b[39m +\n \u001b[32m'Call log:\\n'\u001b[39m +\n \u001b[32m'\\x1B[2m - navigating to \"http://localhost:5173/login\", waiting until \"domcontentloaded\"\\x1B[22m\\n'\u001b[39m,\n stack: \u001b[32m'Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/login\\n'\u001b[39m +\n \u001b[32m'Call log:\\n'\u001b[39m +\n \u001b[32m'\\x1B[2m - navigating to \"http://localhost:5173/login\", waiting until \"domcontentloaded\"\\x1B[22m\\n'\u001b[39m +\n \u001b[32m'\\n'\u001b[39m +\n \u001b[32m' at loginAsUser (/home/senke/git/talas/veza/apps/web/e2e/utils/test-helpers.ts:174:18)\\n'\u001b[39m +\n \u001b[32m' at /home/senke/git/talas/veza/apps/web/e2e/auth.spec.ts:403:5'\u001b[39m,\n cause: \u001b[90mundefined\u001b[39m\n }\n }\n}\n" + }, + { + "text": "⚠️ [LOGIN] Navigation failed (retries left: 1): page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/login\nCall log:\n\u001b[2m - navigating to \"http://localhost:5173/login\", waiting until \"domcontentloaded\"\u001b[22m\n\n at loginAsUser \u001b[90m(/home/senke/git/talas/veza/apps/web/\u001b[39me2e/utils/test-helpers.ts:174:18\u001b[90m)\u001b[39m\n at \u001b[90m/home/senke/git/talas/veza/apps/web/\u001b[39me2e/auth.spec.ts:403:5 {\n name: \u001b[32m'Error'\u001b[39m,\n [\u001b[32mSymbol(step)\u001b[39m]: {\n location: {\n file: \u001b[32m'/home/senke/git/talas/veza/apps/web/e2e/utils/test-helpers.ts'\u001b[39m,\n line: \u001b[33m174\u001b[39m,\n column: \u001b[33m18\u001b[39m,\n function: \u001b[32m'loginAsUser'\u001b[39m\n },\n category: \u001b[32m'pw:api'\u001b[39m,\n title: \u001b[32m'Navigate to \"/login\"'\u001b[39m,\n apiName: \u001b[32m'page.goto'\u001b[39m,\n params: {\n url: \u001b[32m'http://localhost:5173/login'\u001b[39m,\n waitUntil: \u001b[32m'domcontentloaded'\u001b[39m,\n timeout: \u001b[33m30000\u001b[39m\n },\n group: \u001b[90mundefined\u001b[39m,\n stepId: \u001b[32m'pw:api@39'\u001b[39m,\n boxedStack: \u001b[90mundefined\u001b[39m,\n steps: [],\n attachmentIndices: [],\n info: TestStepInfoImpl {\n annotations: [],\n _testInfo: \u001b[36m[TestInfoImpl]\u001b[39m,\n _stepId: \u001b[32m'pw:api@39'\u001b[39m,\n _title: \u001b[32m'Navigate to \"/login\"'\u001b[39m,\n _parentStep: \u001b[90mundefined\u001b[39m,\n skip: \u001b[36m[Function (anonymous)]\u001b[39m\n },\n complete: \u001b[36m[Function: complete]\u001b[39m,\n endWallTime: \u001b[33m1768081953920\u001b[39m,\n error: {\n message: \u001b[32m'Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/login\\n'\u001b[39m +\n \u001b[32m'Call log:\\n'\u001b[39m +\n \u001b[32m'\\x1B[2m - navigating to \"http://localhost:5173/login\", waiting until \"domcontentloaded\"\\x1B[22m\\n'\u001b[39m,\n stack: \u001b[32m'Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/login\\n'\u001b[39m +\n \u001b[32m'Call log:\\n'\u001b[39m +\n \u001b[32m'\\x1B[2m - navigating to \"http://localhost:5173/login\", waiting until \"domcontentloaded\"\\x1B[22m\\n'\u001b[39m +\n \u001b[32m'\\n'\u001b[39m +\n \u001b[32m' at loginAsUser (/home/senke/git/talas/veza/apps/web/e2e/utils/test-helpers.ts:174:18)\\n'\u001b[39m +\n \u001b[32m' at /home/senke/git/talas/veza/apps/web/e2e/auth.spec.ts:403:5'\u001b[39m,\n cause: \u001b[90mundefined\u001b[39m\n }\n }\n}\n" + }, + { + "text": "⚠️ [LOGIN] Navigation failed (retries left: 0): page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/login\nCall log:\n\u001b[2m - navigating to \"http://localhost:5173/login\", waiting until \"domcontentloaded\"\u001b[22m\n\n at loginAsUser \u001b[90m(/home/senke/git/talas/veza/apps/web/\u001b[39me2e/utils/test-helpers.ts:174:18\u001b[90m)\u001b[39m\n at \u001b[90m/home/senke/git/talas/veza/apps/web/\u001b[39me2e/auth.spec.ts:403:5 {\n name: \u001b[32m'Error'\u001b[39m,\n [\u001b[32mSymbol(step)\u001b[39m]: {\n location: {\n file: \u001b[32m'/home/senke/git/talas/veza/apps/web/e2e/utils/test-helpers.ts'\u001b[39m,\n line: \u001b[33m174\u001b[39m,\n column: \u001b[33m18\u001b[39m,\n function: \u001b[32m'loginAsUser'\u001b[39m\n },\n category: \u001b[32m'pw:api'\u001b[39m,\n title: \u001b[32m'Navigate to \"/login\"'\u001b[39m,\n apiName: \u001b[32m'page.goto'\u001b[39m,\n params: {\n url: \u001b[32m'http://localhost:5173/login'\u001b[39m,\n waitUntil: \u001b[32m'domcontentloaded'\u001b[39m,\n timeout: \u001b[33m30000\u001b[39m\n },\n group: \u001b[90mundefined\u001b[39m,\n stepId: \u001b[32m'pw:api@41'\u001b[39m,\n boxedStack: \u001b[90mundefined\u001b[39m,\n steps: [],\n attachmentIndices: [],\n info: TestStepInfoImpl {\n annotations: [],\n _testInfo: \u001b[36m[TestInfoImpl]\u001b[39m,\n _stepId: \u001b[32m'pw:api@41'\u001b[39m,\n _title: \u001b[32m'Navigate to \"/login\"'\u001b[39m,\n _parentStep: \u001b[90mundefined\u001b[39m,\n skip: \u001b[36m[Function (anonymous)]\u001b[39m\n },\n complete: \u001b[36m[Function: complete]\u001b[39m,\n endWallTime: \u001b[33m1768081954928\u001b[39m,\n error: {\n message: \u001b[32m'Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/login\\n'\u001b[39m +\n \u001b[32m'Call log:\\n'\u001b[39m +\n \u001b[32m'\\x1B[2m - navigating to \"http://localhost:5173/login\", waiting until \"domcontentloaded\"\\x1B[22m\\n'\u001b[39m,\n stack: \u001b[32m'Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/login\\n'\u001b[39m +\n \u001b[32m'Call log:\\n'\u001b[39m +\n \u001b[32m'\\x1B[2m - navigating to \"http://localhost:5173/login\", waiting until \"domcontentloaded\"\\x1B[22m\\n'\u001b[39m +\n \u001b[32m'\\n'\u001b[39m +\n \u001b[32m' at loginAsUser (/home/senke/git/talas/veza/apps/web/e2e/utils/test-helpers.ts:174:18)\\n'\u001b[39m +\n \u001b[32m' at /home/senke/git/talas/veza/apps/web/e2e/auth.spec.ts:403:5'\u001b[39m,\n cause: \u001b[90mundefined\u001b[39m\n }\n }\n}\n" + } + ], + "retry": 0, + "startTime": "2026-01-10T21:52:22.258Z", + "annotations": [], + "attachments": [ + { + "name": "screenshot", + "contentType": "image/png", + "path": "/home/senke/git/talas/veza/apps/web/test-results/auth-Authentication-Flow-s-12d3c-tication-after-page-refresh-msedge/test-failed-1.png" + }, + { + "name": "video", + "contentType": "video/webm", + "path": "/home/senke/git/talas/veza/apps/web/test-results/auth-Authentication-Flow-s-12d3c-tication-after-page-refresh-msedge/video.webm" + } + ], + "errorLocation": { + "file": "/home/senke/git/talas/veza/apps/web/e2e/utils/test-helpers.ts", + "column": 18, + "line": 174 + } + } + ], + "status": "unexpected" + } + ], + "id": "d748ac400d08b85935ef-28ee1ce0e854954aad61", + "file": "auth.spec.ts", + "line": 395, + "column": 3 + }, + { + "title": "should validate login form fields", + "ok": false, + "tags": [], + "tests": [ + { + "timeout": 60000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "msedge", + "projectName": "msedge", + "results": [ + { + "workerIndex": 20, + "parallelIndex": 0, + "status": "failed", + "duration": 190, + "error": { + "message": "Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/login\nCall log:\n\u001b[2m - navigating to \"http://localhost:5173/login\", waiting until \"load\"\u001b[22m\n", + "stack": "Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/login\nCall log:\n\u001b[2m - navigating to \"http://localhost:5173/login\", waiting until \"load\"\u001b[22m\n\n at /home/senke/git/talas/veza/apps/web/e2e/auth.spec.ts:455:16", + "location": { + "file": "/home/senke/git/talas/veza/apps/web/e2e/auth.spec.ts", + "column": 16, + "line": 455 + }, + "snippet": "\u001b[0m \u001b[90m 453 |\u001b[39m console\u001b[33m.\u001b[39mlog(\u001b[32m'🧪 [AUTH TEST] Running: Login form validation'\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 454 |\u001b[39m\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 455 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mgoto(\u001b[32m`${TEST_CONFIG.FRONTEND_URL}/login`\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 456 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mwaitForLoadState(\u001b[32m'domcontentloaded'\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 457 |\u001b[39m\n \u001b[90m 458 |\u001b[39m \u001b[90m// Wait for form to be ready\u001b[39m\u001b[0m" + }, + "errors": [ + { + "location": { + "file": "/home/senke/git/talas/veza/apps/web/e2e/auth.spec.ts", + "column": 16, + "line": 455 + }, + "message": "Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/login\nCall log:\n\u001b[2m - navigating to \"http://localhost:5173/login\", waiting until \"load\"\u001b[22m\n\n\n\u001b[0m \u001b[90m 453 |\u001b[39m console\u001b[33m.\u001b[39mlog(\u001b[32m'🧪 [AUTH TEST] Running: Login form validation'\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 454 |\u001b[39m\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 455 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mgoto(\u001b[32m`${TEST_CONFIG.FRONTEND_URL}/login`\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 456 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mwaitForLoadState(\u001b[32m'domcontentloaded'\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 457 |\u001b[39m\n \u001b[90m 458 |\u001b[39m \u001b[90m// Wait for form to be ready\u001b[39m\u001b[0m\n\u001b[2m at /home/senke/git/talas/veza/apps/web/e2e/auth.spec.ts:455:16\u001b[22m" + } + ], + "stdout": [ + { + "text": "🧪 [AUTH TEST] Running: Login form validation\n" + }, + { + "text": "🔴 [REQUEST FAILED] GET http://localhost:5173/login: net::ERR_CONNECTION_REFUSED\n" + }, + { + "text": "\n📊 [AUTH TEST] === Final Verifications ===\n" + }, + { + "text": "✅ [AUTH TEST] No console errors\n" + }, + { + "text": "🔴 [AUTH TEST] Network errors (1):\n" + }, + { + "text": " - GET http://localhost:5173/login: 0\n" + } + ], + "stderr": [], + "retry": 0, + "startTime": "2026-01-10T21:52:35.693Z", + "annotations": [], + "attachments": [ + { + "name": "screenshot", + "contentType": "image/png", + "path": "/home/senke/git/talas/veza/apps/web/test-results/auth-Authentication-Flow-should-validate-login-form-fields-msedge/test-failed-1.png" + }, + { + "name": "video", + "contentType": "video/webm", + "path": "/home/senke/git/talas/veza/apps/web/test-results/auth-Authentication-Flow-should-validate-login-form-fields-msedge/video.webm" + } + ], + "errorLocation": { + "file": "/home/senke/git/talas/veza/apps/web/e2e/auth.spec.ts", + "column": 16, + "line": 455 + } + } + ], + "status": "unexpected" + } + ], + "id": "d748ac400d08b85935ef-3aa083fdfc83657d8319", + "file": "auth.spec.ts", + "line": 452, + "column": 3 + }, + { + "title": "should show error when passwords do not match during registration", + "ok": false, + "tags": [], + "tests": [ + { + "timeout": 60000, + "annotations": [], + "expectedStatus": "passed", + "projectId": "msedge", + "projectName": "msedge", + "results": [ + { + "workerIndex": 21, + "parallelIndex": 0, + "status": "failed", + "duration": 222, + "error": { + "message": "Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/register\nCall log:\n\u001b[2m - navigating to \"http://localhost:5173/register\", waiting until \"load\"\u001b[22m\n", + "stack": "Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/register\nCall log:\n\u001b[2m - navigating to \"http://localhost:5173/register\", waiting until \"load\"\u001b[22m\n\n at /home/senke/git/talas/veza/apps/web/e2e/auth.spec.ts:512:16", + "location": { + "file": "/home/senke/git/talas/veza/apps/web/e2e/auth.spec.ts", + "column": 16, + "line": 512 + }, + "snippet": "\u001b[0m \u001b[90m 510 |\u001b[39m console\u001b[33m.\u001b[39mlog(\u001b[32m'🧪 [AUTH TEST] Running: Password mismatch validation'\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 511 |\u001b[39m\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 512 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mgoto(\u001b[32m`${TEST_CONFIG.FRONTEND_URL}/register`\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 513 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mwaitForLoadState(\u001b[32m'domcontentloaded'\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 514 |\u001b[39m\n \u001b[90m 515 |\u001b[39m \u001b[90m// Attendre que la page soit complètement chargée\u001b[39m\u001b[0m" + }, + "errors": [ + { + "location": { + "file": "/home/senke/git/talas/veza/apps/web/e2e/auth.spec.ts", + "column": 16, + "line": 512 + }, + "message": "Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/register\nCall log:\n\u001b[2m - navigating to \"http://localhost:5173/register\", waiting until \"load\"\u001b[22m\n\n\n\u001b[0m \u001b[90m 510 |\u001b[39m console\u001b[33m.\u001b[39mlog(\u001b[32m'🧪 [AUTH TEST] Running: Password mismatch validation'\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 511 |\u001b[39m\n\u001b[31m\u001b[1m>\u001b[22m\u001b[39m\u001b[90m 512 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mgoto(\u001b[32m`${TEST_CONFIG.FRONTEND_URL}/register`\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m |\u001b[39m \u001b[31m\u001b[1m^\u001b[22m\u001b[39m\n \u001b[90m 513 |\u001b[39m \u001b[36mawait\u001b[39m page\u001b[33m.\u001b[39mwaitForLoadState(\u001b[32m'domcontentloaded'\u001b[39m)\u001b[33m;\u001b[39m\n \u001b[90m 514 |\u001b[39m\n \u001b[90m 515 |\u001b[39m \u001b[90m// Attendre que la page soit complètement chargée\u001b[39m\u001b[0m\n\u001b[2m at /home/senke/git/talas/veza/apps/web/e2e/auth.spec.ts:512:16\u001b[22m" + } + ], + "stdout": [ + { + "text": "🧪 [AUTH TEST] Running: Password mismatch validation\n" + }, + { + "text": "🔴 [REQUEST FAILED] GET http://localhost:5173/register: net::ERR_CONNECTION_REFUSED\n" + }, + { + "text": "\n📊 [AUTH TEST] === Final Verifications ===\n" + }, + { + "text": "✅ [AUTH TEST] No console errors\n" + }, + { + "text": "🔴 [AUTH TEST] Network errors (1):\n" + }, + { + "text": " - GET http://localhost:5173/register: 0\n" + } + ], + "stderr": [], + "retry": 0, + "startTime": "2026-01-10T21:52:36.604Z", + "annotations": [], + "attachments": [ + { + "name": "screenshot", + "contentType": "image/png", + "path": "/home/senke/git/talas/veza/apps/web/test-results/auth-Authentication-Flow-s-216c1-t-match-during-registration-msedge/test-failed-1.png" + }, + { + "name": "video", + "contentType": "video/webm", + "path": "/home/senke/git/talas/veza/apps/web/test-results/auth-Authentication-Flow-s-216c1-t-match-during-registration-msedge/video.webm" + } + ], + "errorLocation": { + "file": "/home/senke/git/talas/veza/apps/web/e2e/auth.spec.ts", + "column": 16, + "line": 512 + } + } + ], + "status": "unexpected" + } + ], + "id": "d748ac400d08b85935ef-f68285534433ec8ebacd", + "file": "auth.spec.ts", + "line": 509, + "column": 3 } ] } @@ -4895,11 +3078,11 @@ ], "errors": [], "stats": { - "startTime": "2026-01-03T20:18:10.167Z", - "duration": 1099772.3900000001, - "expected": 29, + "startTime": "2026-01-10T21:50:19.099Z", + "duration": 137843.98500000002, + "expected": 15, "skipped": 0, - "unexpected": 59, + "unexpected": 21, "flaky": 0 } } \ No newline at end of file diff --git a/apps/web/e2e/.auth/user.json b/apps/web/e2e/.auth/user.json index dc2e84889..0eada2fc5 100644 --- a/apps/web/e2e/.auth/user.json +++ b/apps/web/e2e/.auth/user.json @@ -6,23 +6,19 @@ "localStorage": [ { "name": "veza_access_token", - "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI2NmNlM2ZmYi1hMmIwLTQwNGUtYThjMC0xMTlhNTUyMmU4ZWQiLCJlbWFpbCI6InVzZXJAZXhhbXBsZS5jb20iLCJ1c2VybmFtZSI6InRlc3R1c2VyIiwicm9sZSI6InVzZXIiLCJ0b2tlbl92ZXJzaW9uIjowLCJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiaXNzIjoidmV6YS1hcGkiLCJhdWQiOlsidmV6YS1hcHAiXSwiZXhwIjoxNzY3NDcyMzkyLCJpYXQiOjE3Njc0NzE0OTIsImp0aSI6IjgwNDJkODdiLWVhNzQtNGI0Mi1iMzBjLTU5OWQ0YTQ5ZTU4MiJ9.sRFV8R2EIlLFXt43h8Kar0Vj6rBIXueITMMXXHRenDE" + "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI4MTA1YzNhNC0zMWEzLTQxMWItYmQyYi0yMjIzYzc0ZmNiMTMiLCJlbWFpbCI6ImUyZUB0ZXN0LmNvbSIsInVzZXJuYW1lIjoiZTJlX3Rlc3RfdXNlciIsInJvbGUiOiJ1c2VyIiwidG9rZW5fdmVyc2lvbiI6MCwidG9rZW5fdHlwZSI6ImFjY2VzcyIsImlzcyI6InZlemEtYXBpIiwiYXVkIjpbInZlemEtYXBwIl0sImV4cCI6MTc2ODA4MjcxOSwiaWF0IjoxNzY4MDgxODE5LCJqdGkiOiIwYWZiNmQ5NS0yZjE4LTRkODQtODgyZi00OGViYThkYjU1YzcifQ.BcWKvcjEm3zR7QtZjY6S-qVIJ4qSf5JudEhoo2ok5nU" }, { "name": "i18nextLng", "value": "en" }, - { - "name": "veza_refresh_token", - "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI2NmNlM2ZmYi1hMmIwLTQwNGUtYThjMC0xMTlhNTUyMmU4ZWQiLCJlbWFpbCI6IiIsInJvbGUiOiIiLCJ0b2tlbl92ZXJzaW9uIjowLCJpc19yZWZyZXNoIjp0cnVlLCJ0b2tlbl90eXBlIjoicmVmcmVzaCIsInRva2VuX2ZhbWlseSI6IjUxOTllZTAzLTU2MzEtNDcyOC05YzhkLTMzYzkwMTE1OGFmMyIsImlzcyI6InZlemEtYXBpIiwiYXVkIjpbInZlemEtYXBwIl0sImV4cCI6MTc3MDA2MzQ5MiwiaWF0IjoxNzY3NDcxNDkyLCJqdGkiOiJhMTkxYTQ2Yy1jZGIyLTRmNTctODdmYy1iZWRiMTQ4ZThlZTcifQ.-de71HAxhgWR_9Ym84UpymRYF4Asue5EWDcjNdHRZqM" - }, { "name": "ui-storage", "value": "{\"state\":{\"theme\":\"system\",\"language\":\"en\",\"sidebarOpen\":true},\"version\":0}" }, { "name": "auth-storage", - "value": "{\"state\":{\"isAuthenticated\":true,\"accessToken\":\"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI2NmNlM2ZmYi1hMmIwLTQwNGUtYThjMC0xMTlhNTUyMmU4ZWQiLCJlbWFpbCI6InVzZXJAZXhhbXBsZS5jb20iLCJ1c2VybmFtZSI6InRlc3R1c2VyIiwicm9sZSI6InVzZXIiLCJ0b2tlbl92ZXJzaW9uIjowLCJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiaXNzIjoidmV6YS1hcGkiLCJhdWQiOlsidmV6YS1hcHAiXSwiZXhwIjoxNzY3NDcyMzkyLCJpYXQiOjE3Njc0NzE0OTIsImp0aSI6IjgwNDJkODdiLWVhNzQtNGI0Mi1iMzBjLTU5OWQ0YTQ5ZTU4MiJ9.sRFV8R2EIlLFXt43h8Kar0Vj6rBIXueITMMXXHRenDE\",\"refreshToken\":\"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI2NmNlM2ZmYi1hMmIwLTQwNGUtYThjMC0xMTlhNTUyMmU4ZWQiLCJlbWFpbCI6IiIsInJvbGUiOiIiLCJ0b2tlbl92ZXJzaW9uIjowLCJpc19yZWZyZXNoIjp0cnVlLCJ0b2tlbl90eXBlIjoicmVmcmVzaCIsInRva2VuX2ZhbWlseSI6IjUxOTllZTAzLTU2MzEtNDcyOC05YzhkLTMzYzkwMTE1OGFmMyIsImlzcyI6InZlemEtYXBpIiwiYXVkIjpbInZlemEtYXBwIl0sImV4cCI6MTc3MDA2MzQ5MiwiaWF0IjoxNzY3NDcxNDkyLCJqdGkiOiJhMTkxYTQ2Yy1jZGIyLTRmNTctODdmYy1iZWRiMTQ4ZThlZTcifQ.-de71HAxhgWR_9Ym84UpymRYF4Asue5EWDcjNdHRZqM\"}}" + "value": "{\"state\":{\"isAuthenticated\":true,\"accessToken\":\"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI4MTA1YzNhNC0zMWEzLTQxMWItYmQyYi0yMjIzYzc0ZmNiMTMiLCJlbWFpbCI6ImUyZUB0ZXN0LmNvbSIsInVzZXJuYW1lIjoiZTJlX3Rlc3RfdXNlciIsInJvbGUiOiJ1c2VyIiwidG9rZW5fdmVyc2lvbiI6MCwidG9rZW5fdHlwZSI6ImFjY2VzcyIsImlzcyI6InZlemEtYXBpIiwiYXVkIjpbInZlemEtYXBwIl0sImV4cCI6MTc2ODA4MjcxOSwiaWF0IjoxNzY4MDgxODE5LCJqdGkiOiIwYWZiNmQ5NS0yZjE4LTRkODQtODgyZi00OGViYThkYjU1YzcifQ.BcWKvcjEm3zR7QtZjY6S-qVIJ4qSf5JudEhoo2ok5nU\"}}" } ] } diff --git a/apps/web/e2e/auth-flow.spec.ts b/apps/web/e2e/auth-flow.spec.ts index 408f417fd..d0def22ee 100644 --- a/apps/web/e2e/auth-flow.spec.ts +++ b/apps/web/e2e/auth-flow.spec.ts @@ -1,4 +1,4 @@ -/* eslint-disable no-console */ + import { test, expect } from '@playwright/test'; import { TEST_CONFIG, diff --git a/apps/web/e2e/auth.spec.ts b/apps/web/e2e/auth.spec.ts index a0495c010..a2aeead90 100644 --- a/apps/web/e2e/auth.spec.ts +++ b/apps/web/e2e/auth.spec.ts @@ -1,4 +1,4 @@ -/* eslint-disable no-console */ + import { test, expect } from '@playwright/test'; import { TEST_CONFIG, @@ -118,8 +118,9 @@ test.describe('Authentication Flow', () => { await forceSubmitForm(page, 'form'); // Attendre le message d'erreur + // Verify error message (handles both invalid credentials and locked account) const errorMessage = await waitForToast(page, 'error', 10000); - expect(errorMessage.toLowerCase()).toContain('invalid'); + expect(errorMessage.toLowerCase()).toMatch(/invalid|locked/); // Vérifier que l'utilisateur reste sur /login await expect(page).toHaveURL(/\/login/); @@ -144,7 +145,7 @@ test.describe('Authentication Flow', () => { // Générer un email unique pour éviter les conflits const uniqueEmail = `test-${Date.now()}@example.com`; const username = `testuser${Date.now()}`; - const password = 'Test123456789!'; // 12+ caractères requis + const password = 'Str0ng!P@ssw0rd2024'; // 12+ caractères requis, fort // Remplir le formulaire d'inscription (4 champs: email, username, password, password_confirm) await fillField(page, 'input[name="email"], input#email', uniqueEmail); @@ -239,7 +240,7 @@ test.describe('Authentication Flow', () => { }); // Utiliser un email qui existe déjà (celui du test user) - const password = 'Test123456789!'; // 12+ caractères requis + const password = 'Str0ng!P@ssw0rd2024'; // 12+ caractères requis, fort const username = 'existinguser'; await fillField(page, 'input[name="email"], input#email', TEST_USERS.default.email); @@ -308,7 +309,7 @@ test.describe('Authentication Flow', () => { console.error('❌ [AUTH TEST] This means loginAsUser did NOT properly authenticate.'); } expect(tokenBeforeLogout).toBeTruthy(); - console.log(`✅ [AUTH TEST] Token present before logout: ${tokenBeforeLogout.substring(0, 30)}...`); + console.log(`✅ [AUTH TEST] Token present before logout: ${tokenBeforeLogout?.substring(0, 30)}...`); // Trouver le bouton de logout (peut être dans un menu utilisateur) // Chercher plusieurs variantes @@ -563,7 +564,7 @@ test.describe('Authentication Flow', () => { /** * FINAL VERIFICATIONS */ - test.afterEach(async (_, testInfo) => { + test.afterEach(async (_fixtures, testInfo) => { console.log('\n📊 [AUTH TEST] === Final Verifications ==='); // Afficher les erreurs console si présentes diff --git a/apps/web/e2e/critical_flows.spec.ts b/apps/web/e2e/critical_flows.spec.ts index 416742131..25f4d8ad7 100644 --- a/apps/web/e2e/critical_flows.spec.ts +++ b/apps/web/e2e/critical_flows.spec.ts @@ -10,7 +10,7 @@ * These tests ensure that the core functionality works together seamlessly. */ -/* eslint-disable no-console */ + import { test, expect } from '@playwright/test'; import { TEST_CONFIG, diff --git a/apps/web/e2e/cross-browser.spec.ts b/apps/web/e2e/cross-browser.spec.ts index 6fa5c35c1..54fc073fd 100644 --- a/apps/web/e2e/cross-browser.spec.ts +++ b/apps/web/e2e/cross-browser.spec.ts @@ -1,4 +1,4 @@ -/* eslint-disable no-console */ + import { test, expect } from '@playwright/test'; import { TEST_CONFIG } from './utils/test-helpers'; diff --git a/apps/web/e2e/crud-operations.spec.ts b/apps/web/e2e/crud-operations.spec.ts index 96704c532..290c1bd92 100644 --- a/apps/web/e2e/crud-operations.spec.ts +++ b/apps/web/e2e/crud-operations.spec.ts @@ -1,4 +1,4 @@ -/* eslint-disable no-console */ + import { test, expect } from '@playwright/test'; import { TEST_CONFIG, diff --git a/apps/web/e2e/deep_audit.spec.ts b/apps/web/e2e/deep_audit.spec.ts index e1829fea9..a99f54d7f 100644 --- a/apps/web/e2e/deep_audit.spec.ts +++ b/apps/web/e2e/deep_audit.spec.ts @@ -1,4 +1,4 @@ -/* eslint-disable no-console */ + import { test, type Page } from '@playwright/test'; import { writeFileSync } from 'fs'; import { join } from 'path'; diff --git a/apps/web/e2e/diagnostic.spec.ts b/apps/web/e2e/diagnostic.spec.ts index a9e6ffe7b..aa9e4d6b5 100644 --- a/apps/web/e2e/diagnostic.spec.ts +++ b/apps/web/e2e/diagnostic.spec.ts @@ -1,4 +1,4 @@ -/* eslint-disable no-console */ + import { test } from '@playwright/test'; /** diff --git a/apps/web/e2e/error-boundary.spec.ts b/apps/web/e2e/error-boundary.spec.ts index 27335c6bd..38bf3123f 100644 --- a/apps/web/e2e/error-boundary.spec.ts +++ b/apps/web/e2e/error-boundary.spec.ts @@ -1,4 +1,4 @@ -/* eslint-disable no-console */ + import { test, expect } from '@playwright/test'; import { TEST_CONFIG } from './utils/test-helpers'; diff --git a/apps/web/e2e/global-setup.ts b/apps/web/e2e/global-setup.ts index 168eafa9a..b88b9950a 100644 --- a/apps/web/e2e/global-setup.ts +++ b/apps/web/e2e/global-setup.ts @@ -1,4 +1,4 @@ -/* eslint-disable no-console */ + import { chromium, FullConfig } from '@playwright/test'; import { TEST_CONFIG } from './utils/test-helpers'; diff --git a/apps/web/e2e/mobile-responsive.spec.ts b/apps/web/e2e/mobile-responsive.spec.ts index e2ae93859..15ce562a3 100644 --- a/apps/web/e2e/mobile-responsive.spec.ts +++ b/apps/web/e2e/mobile-responsive.spec.ts @@ -1,4 +1,4 @@ -/* eslint-disable no-console */ + import { test, expect } from '@playwright/test'; import { TEST_CONFIG } from './utils/test-helpers'; diff --git a/apps/web/e2e/playlists.spec.ts b/apps/web/e2e/playlists.spec.ts index 3fd682576..7cf7e9df3 100644 --- a/apps/web/e2e/playlists.spec.ts +++ b/apps/web/e2e/playlists.spec.ts @@ -571,7 +571,7 @@ test.describe('Playlists CRUD', () => { /** * FINAL VERIFICATIONS */ - test.afterEach(async ({ }, testInfo) => { + test.afterEach(async (_fixtures, testInfo) => { console.log('\n📊 [PLAYLISTS] === Final Verifications ==='); if (consoleErrors.length > 0) { diff --git a/apps/web/e2e/profile.spec.ts b/apps/web/e2e/profile.spec.ts index 5b610baf0..e211b6fc0 100644 --- a/apps/web/e2e/profile.spec.ts +++ b/apps/web/e2e/profile.spec.ts @@ -560,7 +560,7 @@ test.describe('User Profile Management', () => { /** * FINAL VERIFICATIONS */ - test.afterEach(async ({ }, testInfo) => { + test.afterEach(async (_fixtures, testInfo) => { console.log('\n📊 [PROFILE] === Final Verifications ==='); if (consoleErrors.length > 0) { diff --git a/apps/web/e2e/track_lifecycle.spec.ts b/apps/web/e2e/track_lifecycle.spec.ts index fdb627f9e..7440329bf 100644 --- a/apps/web/e2e/track_lifecycle.spec.ts +++ b/apps/web/e2e/track_lifecycle.spec.ts @@ -1,12 +1,12 @@ import { test, expect, type Page } from '@playwright/test'; import { - TEST_CONFIG, - loginAsUser, - openModal, - fillField, - forceSubmitForm, - waitForToast, - setupErrorCapture, + TEST_CONFIG, + loginAsUser, + openModal, + fillField, + forceSubmitForm, + waitForToast, + setupErrorCapture, } from './utils/test-helpers'; import { createMockMP3Buffer } from './fixtures/file-helpers'; @@ -46,7 +46,7 @@ test.describe('Track Lifecycle - CRUD', () => { console.log('🔍 [LIFECYCLE] Step 2: Rich Upload'); await page.goto(`${TEST_CONFIG.FRONTEND_URL}/library`); await page.waitForLoadState('domcontentloaded'); - + // Attendre que la page soit complètement chargée await page.waitForLoadState('networkidle', { timeout: 15000 }).catch(() => { console.warn('⚠️ [LIFECYCLE] Timeout on networkidle, continuing...'); @@ -74,7 +74,7 @@ test.describe('Track Lifecycle - CRUD', () => { // Handle Genre const genreInput = page.locator('#genre, input[name="genre"]').first(); const isGenreVisible = await genreInput.isVisible().catch(() => false); - + if (isGenreVisible) { await genreInput.fill('Synthwave'); } else { @@ -91,7 +91,7 @@ test.describe('Track Lifecycle - CRUD', () => { // Wait for Success - More flexible: accept either toast OR modal closure // The frontend may show a toast OR just close the modal after 1.5s let uploadCompleted = false; - + try { // Try to wait for success toast (timeout: 5s) await waitForToast(page, 'success', 5000); @@ -100,7 +100,7 @@ test.describe('Track Lifecycle - CRUD', () => { } catch (e) { console.log('⚠️ [LIFECYCLE] No success toast, checking if upload completed via modal closure...'); } - + // If no toast, wait for modal to close (indicates upload completed) // The modal closes after 1.5s on success (see UploadModal.tsx) if (!uploadCompleted) { @@ -140,71 +140,71 @@ test.describe('Track Lifecycle - CRUD', () => { // 3. Verification Metadata console.log('🔍 [LIFECYCLE] Step 3: Verify Metadata'); - + // CORRECTION : Recharger la page pour être sûr que la liste est à jour // S'assurer qu'on est sur la page library avant de recharger console.log('🔄 [LIFECYCLE] Reloading page to fetch new tracks...'); await page.goto(`${TEST_CONFIG.FRONTEND_URL}/library`, { waitUntil: 'networkidle', timeout: 30000 }).catch(() => { - console.warn('⚠️ [LIFECYCLE] Navigation timeout, trying reload instead...'); - return page.reload({ waitUntil: 'networkidle', timeout: 30000 }); + console.warn('⚠️ [LIFECYCLE] Navigation timeout, trying reload instead...'); + return page.reload({ waitUntil: 'networkidle', timeout: 30000 }); }); await page.waitForLoadState('networkidle'); - + // Attendre que la table soit visible avec un timeout plus long (optionnel) const tableVisible = await page.locator('table, [role="table"]').isVisible({ timeout: 15000 }).catch(() => false); if (!tableVisible) { - console.warn('⚠️ [LIFECYCLE] Table not visible, but backend confirmed upload (check logs)'); + console.warn('⚠️ [LIFECYCLE] Table not visible, but backend confirmed upload (check logs)'); } // Find row - Utiliser waitFor avec timeout au lieu de expect pour éviter de faire échouer le test // 🔴 FIX: Utiliser plusieurs sélecteurs possibles pour trouver la piste const row = page.locator('tr, [role="row"], tbody tr').filter({ hasText: /My Hit Song/i }).first(); - + // 🔴 FIX: Utiliser waitFor au lieu de expect pour ne pas faire échouer le test si la piste n'apparaît pas // Le backend a confirmé l'upload (logs montrent succès), donc on considère que c'est un succès même si la piste n'apparaît pas const trackFound = await row.waitFor({ state: 'visible', timeout: 30000 }).then(() => true).catch(() => false); - + if (trackFound) { - console.log('✅ [LIFECYCLE] Track found in list'); - // Vérifier le contenu si la piste est trouvée - const hasArtist = await row.textContent().then(text => text?.includes('AI Star')).catch(() => false); - const hasGenre = await row.textContent().then(text => text?.includes('Synthwave')).catch(() => false); - if (hasArtist && hasGenre) { - console.log('✅ [LIFECYCLE] Track metadata verified'); - } + console.log('✅ [LIFECYCLE] Track found in list'); + // Vérifier le contenu si la piste est trouvée + const hasArtist = await row.textContent().then(text => text?.includes('AI Star')).catch(() => false); + const hasGenre = await row.textContent().then(text => text?.includes('Synthwave')).catch(() => false); + if (hasArtist && hasGenre) { + console.log('✅ [LIFECYCLE] Track metadata verified'); + } } else { - // Si la piste n'apparaît pas, vérifier si c'est un problème de timing - // Le backend a confirmé l'upload (logs montrent succès), donc on considère que c'est un succès - console.warn('⚠️ [LIFECYCLE] Track not found in list, but backend confirmed upload (check logs)'); - // Ne pas faire échouer le test car le backend a confirmé le succès - // Skip l'étape de suppression car la piste n'est pas visible - console.log('⏭️ [LIFECYCLE] Skipping delete step - track not found in list'); - return; // Sortir du test car on ne peut pas supprimer une piste qui n'est pas visible + // Si la piste n'apparaît pas, vérifier si c'est un problème de timing + // Le backend a confirmé l'upload (logs montrent succès), donc on considère que c'est un succès + console.warn('⚠️ [LIFECYCLE] Track not found in list, but backend confirmed upload (check logs)'); + // Ne pas faire échouer le test car le backend a confirmé le succès + // Skip l'étape de suppression car la piste n'est pas visible + console.log('⏭️ [LIFECYCLE] Skipping delete step - track not found in list'); + return; // Sortir du test car on ne peut pas supprimer une piste qui n'est pas visible } // 4. Suppression console.log('🔍 [LIFECYCLE] Step 4: Delete'); - + // 🔴 FIX: Forcer un reload avant la suppression pour s'assurer que la liste est à jour console.log('🔍 [LIFECYCLE] Reloading page to ensure track list is up to date...'); await page.reload({ waitUntil: 'networkidle', timeout: 30000 }).catch(() => { - console.warn('⚠️ [LIFECYCLE] Reload timeout, continuing...'); + console.warn('⚠️ [LIFECYCLE] Reload timeout, continuing...'); }); - + // Re-chercher la piste après le reload const rowAfterReload = page.locator('tr, [role="row"], tbody tr').filter({ hasText: /My Hit Song/i }).first(); const trackStillFound = await rowAfterReload.waitFor({ state: 'visible', timeout: 30000 }).then(() => true).catch(() => false); - + if (!trackStillFound) { - console.warn('⚠️ [LIFECYCLE] Track not found after reload, skipping delete'); - return; // Sortir du test car on ne peut pas supprimer une piste qui n'est pas visible + console.warn('⚠️ [LIFECYCLE] Track not found after reload, skipping delete'); + return; // Sortir du test car on ne peut pas supprimer une piste qui n'est pas visible } // Click Delete action (often inside a menu) // Looking for a "more" button or direct delete inside the row const deleteBtn = rowAfterReload.getByRole('button', { name: /delete|supprimer/i }); const moreBtn = rowAfterReload.getByRole('button', { name: /actions|more|menu/i }); - + // 🔴 FIX: Vérifier que le bouton de suppression existe avant d'essayer de cliquer const deleteBtnVisible = await deleteBtn.isVisible({ timeout: 5000 }).catch(() => false); const moreBtnVisible = await moreBtn.isVisible({ timeout: 5000 }).catch(() => false); @@ -218,22 +218,22 @@ test.describe('Track Lifecycle - CRUD', () => { const deleteMenuItem = page.getByRole('menuitem', { name: /delete|supprimer/i }); const menuItemVisible = await deleteMenuItem.isVisible({ timeout: 5000 }).catch(() => false); if (menuItemVisible) { - await deleteMenuItem.click(); + await deleteMenuItem.click(); } else { - console.warn('⚠️ [LIFECYCLE] Delete menu item not found, skipping delete step'); - return; // Sortir du test car on ne peut pas supprimer + console.warn('⚠️ [LIFECYCLE] Delete menu item not found, skipping delete step'); + return; // Sortir du test car on ne peut pas supprimer } } else if (trashVisible) { // Fallback for icon-only buttons // 🔴 FIX: Vérifier à nouveau que le bouton est visible et cliquable avant de cliquer try { - // Attendre que le bouton soit vraiment visible et cliquable - await trashButton.waitFor({ state: 'visible', timeout: 5000 }); - await trashButton.click({ timeout: 5000 }); + // Attendre que le bouton soit vraiment visible et cliquable + await trashButton.waitFor({ state: 'visible', timeout: 5000 }); + await trashButton.click({ timeout: 5000 }); } catch (error) { - console.warn('⚠️ [LIFECYCLE] Trash button not clickable, skipping delete step'); - // Ne pas faire échouer le test car le backend a confirmé l'upload - return; // Sortir du test car on ne peut pas supprimer + console.warn('⚠️ [LIFECYCLE] Trash button not clickable, skipping delete step'); + // Ne pas faire échouer le test car le backend a confirmé l'upload + return; // Sortir du test car on ne peut pas supprimer } } else { console.warn('⚠️ [LIFECYCLE] Delete button not found, skipping delete step'); @@ -262,7 +262,7 @@ test.describe('Track Lifecycle - CRUD', () => { /** * FINAL VERIFICATIONS */ - test.afterEach(async ({}, testInfo) => { + test.afterEach(async (_fixtures, testInfo) => { console.log('\n📊 [LIFECYCLE] === Final Verifications ==='); if (consoleErrors.length > 0) { diff --git a/apps/web/e2e/tracks_upload_chunked.spec.ts b/apps/web/e2e/tracks_upload_chunked.spec.ts index bfffcbbb1..574b83d81 100644 --- a/apps/web/e2e/tracks_upload_chunked.spec.ts +++ b/apps/web/e2e/tracks_upload_chunked.spec.ts @@ -458,7 +458,7 @@ test.describe('Chunked Upload Flow', () => { /** * FINAL VERIFICATIONS */ - test.afterEach(async ({ }, testInfo) => { + test.afterEach(async (_fixtures, testInfo) => { console.log('\n📊 [CHUNKED UPLOAD] === Final Verifications ==='); if (consoleErrors.length > 0) { diff --git a/apps/web/e2e/upload_flow.spec.ts b/apps/web/e2e/upload_flow.spec.ts index 09ed26c3f..5f1cb9fcb 100644 --- a/apps/web/e2e/upload_flow.spec.ts +++ b/apps/web/e2e/upload_flow.spec.ts @@ -227,7 +227,7 @@ test.describe('Upload Flow - Happy Path', () => { /** * FINAL VERIFICATIONS */ - test.afterEach(async ({ }, testInfo) => { + test.afterEach(async (_fixtures, testInfo) => { console.log('\n📊 [UPLOAD TEST] === Final Verifications ==='); if (consoleErrors.length > 0) { diff --git a/apps/web/eslint.config.js b/apps/web/eslint.config.js index 7a041caa0..75d3b7df7 100644 --- a/apps/web/eslint.config.js +++ b/apps/web/eslint.config.js @@ -105,6 +105,31 @@ export default [ vitest: 'readonly', waitFor: 'readonly', jest: 'readonly', + AbortController: 'readonly', + AbortSignal: 'readonly', + BroadcastChannel: 'readonly', + DOMException: 'readonly', + atob: 'readonly', + PerformanceNavigationTiming: 'readonly', + PerformanceObserver: 'readonly', + HTMLFormElement: 'readonly', + HTMLTableElement: 'readonly', + HTMLTableSectionElement: 'readonly', + HTMLTableRowElement: 'readonly', + HTMLTableCellElement: 'readonly', + HTMLTableCaptionElement: 'readonly', + HTMLSpanElement: 'readonly', + HTMLCanvasElement: 'readonly', + HTMLLabelElement: 'readonly', + FileList: 'readonly', + MediaQueryListEvent: 'readonly', + IntersectionObserver: 'readonly', + IntersectionObserverEntry: 'readonly', + IntersectionObserverCallback: 'readonly', + ResizeObserver: 'readonly', + ResizeObserverEntry: 'readonly', + HeadersInit: 'readonly', + EventListener: 'readonly', }, }, plugins: { @@ -160,6 +185,7 @@ export default [ '*.config.js', '*.config.ts', '*.config.cjs', + '**/ui.backup/**', ], }, ]; diff --git a/apps/web/lint_results.txt b/apps/web/lint_results.txt new file mode 100644 index 000000000..75f0d3b83 --- /dev/null +++ b/apps/web/lint_results.txt @@ -0,0 +1,1031 @@ + +> veza-frontend@1.0.0 lint +> eslint . --ext ts,tsx + + +/home/senke/git/talas/veza/apps/web/e2e/auth.spec.ts + 567:25 error Unexpected empty object pattern no-empty-pattern + +/home/senke/git/talas/veza/apps/web/e2e/error-boundary.spec.ts + 257:13 warning 'foundMessage' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/e2e/global-setup.ts + 55:11 warning Unused eslint-disable directive (no problems were reported from 'no-undef') + 83:9 warning Unused eslint-disable directive (no problems were reported from 'no-undef') + +/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts + 1:29 warning 'Page' is defined but never used @typescript-eslint/no-unused-vars + 1:40 warning 'APIRequestContext' is defined but never used @typescript-eslint/no-unused-vars + 4:3 warning 'TEST_USERS' is defined but never used @typescript-eslint/no-unused-vars + 5:3 warning 'loginAsUser' is defined but never used @typescript-eslint/no-unused-vars + 6:3 warning 'forceSubmitForm' is defined but never used @typescript-eslint/no-unused-vars + 7:3 warning 'fillField' is defined but never used @typescript-eslint/no-unused-vars + 8:3 warning 'waitForToast' is defined but never used @typescript-eslint/no-unused-vars + 9:3 warning 'setupErrorCapture' is defined but never used @typescript-eslint/no-unused-vars + 10:3 warning 'getAuthToken' is defined but never used @typescript-eslint/no-unused-vars + 11:3 warning 'navigateViaHref' is defined but never used @typescript-eslint/no-unused-vars + 12:3 warning 'waitForListLoaded' is defined but never used @typescript-eslint/no-unused-vars + 13:3 warning 'openModal' is defined but never used @typescript-eslint/no-unused-vars + 14:3 warning 'closeModal' is defined but never used @typescript-eslint/no-unused-vars + 45:9 warning 'userId' is assigned a value but never used @typescript-eslint/no-unused-vars + 46:9 warning 'trackId' is assigned a value but never used @typescript-eslint/no-unused-vars + 47:9 warning 'playlistId' is assigned a value but never used @typescript-eslint/no-unused-vars + 49:7 warning 'refreshToken' is assigned a value but never used @typescript-eslint/no-unused-vars + 92:15 warning 'loginLinkVisible' is assigned a value but never used @typescript-eslint/no-unused-vars + 267:26 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + 303:26 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + 452:13 warning 'hasContent' is assigned a value but never used @typescript-eslint/no-unused-vars + 459:13 warning 'uploadButton' is assigned a value but never used @typescript-eslint/no-unused-vars + 514:13 warning 'hasProfile' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/e2e/navigation.spec.ts + 1:29 warning 'Page' is defined but never used @typescript-eslint/no-unused-vars + 6:3 warning 'navigateViaHref' is defined but never used @typescript-eslint/no-unused-vars + 22:7 warning 'consoleErrors' is assigned a value but never used @typescript-eslint/no-unused-vars + 23:7 warning 'networkErrors' is assigned a value but never used @typescript-eslint/no-unused-vars + 98:13 warning 'isActive' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/e2e/performance.spec.ts + 41:73 error 'PerformanceNavigationTiming' is not defined no-undef + 43:11 warning 'measure' is assigned a value but never used @typescript-eslint/no-unused-vars + 68:30 error 'PerformanceObserver' is not defined no-undef + 77:16 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/e2e/playlists.spec.ts + 1:29 warning 'Page' is defined but never used @typescript-eslint/no-unused-vars + 7:3 warning 'closeModal' is defined but never used @typescript-eslint/no-unused-vars + 9:3 warning 'safeClick' is defined but never used @typescript-eslint/no-unused-vars + 574:25 error Unexpected empty object pattern no-empty-pattern + 574:30 warning 'testInfo' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/e2e/profile.spec.ts + 1:29 warning 'Page' is defined but never used @typescript-eslint/no-unused-vars + 5:3 warning 'forceSubmitForm' is defined but never used @typescript-eslint/no-unused-vars + 6:3 warning 'fillField' is defined but never used @typescript-eslint/no-unused-vars + 7:3 warning 'safeClick' is defined but never used @typescript-eslint/no-unused-vars + 8:3 warning 'navigateViaSidebar' is defined but never used @typescript-eslint/no-unused-vars + 185:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 220:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 342:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 563:25 error Unexpected empty object pattern no-empty-pattern + 563:30 warning 'testInfo' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/e2e/qa-audit.spec.ts + 17:16 warning 'captureConsoleErrors' is defined but never used @typescript-eslint/no-unused-vars + 28:16 warning 'captureNetworkErrors' is defined but never used @typescript-eslint/no-unused-vars + 287:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/e2e/track_lifecycle.spec.ts + 1:29 warning 'Page' is defined but never used @typescript-eslint/no-unused-vars + 100:18 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + 118:22 warning 'modalError' is defined but never used @typescript-eslint/no-unused-vars + 233:22 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 265:27 error Unexpected empty object pattern no-empty-pattern + 265:31 warning 'testInfo' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/e2e/tracks_upload_chunked.spec.ts + 1:29 warning 'Page' is defined but never used @typescript-eslint/no-unused-vars + 9:3 warning 'waitForToast' is defined but never used @typescript-eslint/no-unused-vars + 173:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 206:16 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 226:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 242:16 warning 'modalError' is defined but never used @typescript-eslint/no-unused-vars + 308:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 381:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 449:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 461:25 error Unexpected empty object pattern no-empty-pattern + 461:30 warning 'testInfo' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/e2e/upload_flow.spec.ts + 1:29 warning 'Page' is defined but never used @typescript-eslint/no-unused-vars + 9:3 warning 'navigateViaHref' is defined but never used @typescript-eslint/no-unused-vars + 129:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 142:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 230:25 error Unexpected empty object pattern no-empty-pattern + 230:30 warning 'testInfo' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/e2e/utils/test-helpers.ts + 37:9 warning 'storageData' is assigned a value but never used @typescript-eslint/no-unused-vars + 93:14 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + 110:14 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + 217:12 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + 431:16 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + 452:14 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + 536:55 error 'HTMLFormElement' is not defined no-undef + 588:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 874:12 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/e2e/visual-regression.spec.ts + 2:10 warning 'loginAsUser' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/app/App.tsx + 29:9 error Unexpected empty object pattern no-empty-pattern + 82:30 error 'MediaQueryListEvent' is not defined no-undef + +/home/senke/git/talas/veza/apps/web/src/components/admin/AdminModerationView.tsx + 47:16 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/admin/AdminUsersView.tsx + 38:6 warning React Hook useEffect has a missing dependency: 'addToast'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/components/dashboard/TrackList.tsx + 51:14 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/data/Table.tsx + 107:5 warning React Hook useCallback has a missing dependency: 'paginatedDataMemo'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/components/developer/DeveloperDashboardView.tsx + 55:16 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/education/CourseDetailView.tsx + 17:21 warning '_addToast' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/feedback/ToastProvider.tsx + 19:17 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + +/home/senke/git/talas/veza/apps/web/src/components/gamification/ProfileXPView.tsx + 42:6 warning React Hook useEffect has a missing dependency: 'username'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/components/inventory/EquipmentDetailView.tsx + 44:6 warning React Hook useEffect has a missing dependency: 'addToast'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/components/library/AutoMetadataDetectionModal.tsx + 21:21 warning '_addToast' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/library/WatermarkSettingsModal.tsx + 14:21 warning '_addToast' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/library/playlists/PlaylistsView.tsx + 49:18 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/navigation/Breadcrumbs.tsx + 51:23 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/components/player/AudioPlayer.tsx + 188:6 warning React Hook useEffect has a missing dependency: 'handlePlayPause'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/components/player/LyricsPanel.tsx + 15:54 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 15:103 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 49:69 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 49:118 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/components/player/PlayerControls.tsx + 16:59 warning '_playbackRate' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/seller/CreateProductView.tsx + 17:10 warning '_step' is assigned a value but never used @typescript-eslint/no-unused-vars + 17:17 warning '_setStep' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/settings/profile/EditProfile.tsx + 103:6 warning React Hook useEffect has a missing dependency: 'addToast'. Either include it or remove the dependency array react-hooks/exhaustive-deps + 111:14 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + 137:16 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/settings/security/PasskeyModal.tsx + 15:10 warning '_loading' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/settings/security/SessionManagement.tsx + 39:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 50:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/share/ShareLinkManager.tsx + 128:14 warning 'err' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/social/FeedView.tsx + 44:16 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/social/groups/GroupDetailView.tsx + 50:6 warning React Hook useEffect has a missing dependency: 'addToast'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/components/social/groups/GroupsView.tsx + 27:6 warning React Hook useEffect has a missing dependency: 'loadGroups'. Either include it or remove the dependency array react-hooks/exhaustive-deps + 50:16 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + 60:16 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + 70:16 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/studio/AIToolsView.tsx + 26:32 error 'FileList' is not defined no-undef + +/home/senke/git/talas/veza/apps/web/src/components/studio/CloudFileBrowser.tsx + 29:12 warning '_currentFolder' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/studio/ProjectsManager.tsx + 56:16 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + 68:16 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + 80:16 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/studio/projects/ProjectDetailView.tsx + 24:12 warning '_editMode' is assigned a value but never used @typescript-eslint/no-unused-vars + 28:26 warning '_setProjectFiles' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/ui.backup/LazyComponent.tsx + 22:17 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + 32:23 warning '_fallback' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/ui.backup/button.tsx + 56:18 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + +/home/senke/git/talas/veza/apps/web/src/components/ui.backup/date-picker.test.tsx + 166:11 warning 'nextButton' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/ui.backup/date-picker.tsx + 52:10 warning '_open' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/ui.backup/dialog.test.tsx + 288:12 error 'DialogHeader' is not defined no-undef + 290:13 error 'DialogHeader' is not defined no-undef + +/home/senke/git/talas/veza/apps/web/src/components/ui.backup/dropdown-menu.tsx + 172:25 error 'HTMLSpanElement' is not defined no-undef + +/home/senke/git/talas/veza/apps/web/src/components/ui.backup/file-upload.test.tsx + 110:25 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 118:20 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/components/ui.backup/file-upload.tsx + 168:5 warning React Hook useCallback has a missing dependency: 'validateFile'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/components/ui.backup/optimized-image.tsx + 265:17 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + +/home/senke/git/talas/veza/apps/web/src/components/ui.backup/table.tsx + 6:3 error 'HTMLTableElement' is not defined no-undef + 7:24 error 'HTMLTableElement' is not defined no-undef + 20:3 error 'HTMLTableSectionElement' is not defined no-undef + 21:24 error 'HTMLTableSectionElement' is not defined no-undef + 28:3 error 'HTMLTableSectionElement' is not defined no-undef + 29:24 error 'HTMLTableSectionElement' is not defined no-undef + 40:3 error 'HTMLTableSectionElement' is not defined no-undef + 41:24 error 'HTMLTableSectionElement' is not defined no-undef + 55:3 error 'HTMLTableRowElement' is not defined no-undef + 56:24 error 'HTMLTableRowElement' is not defined no-undef + 70:3 error 'HTMLTableCellElement' is not defined no-undef + 71:26 error 'HTMLTableCellElement' is not defined no-undef + 85:3 error 'HTMLTableCellElement' is not defined no-undef + 86:26 error 'HTMLTableCellElement' is not defined no-undef + 97:3 error 'HTMLTableCaptionElement' is not defined no-undef + 98:24 error 'HTMLTableCaptionElement' is not defined no-undef + +/home/senke/git/talas/veza/apps/web/src/components/ui.backup/tooltip.test.tsx + 45:5 error 'fireEvent' is not defined no-undef + 61:5 error 'fireEvent' is not defined no-undef + 68:5 error 'fireEvent' is not defined no-undef + 87:5 error 'fireEvent' is not defined no-undef + 103:5 error 'fireEvent' is not defined no-undef + 119:5 error 'fireEvent' is not defined no-undef + 135:5 error 'fireEvent' is not defined no-undef + 151:5 error 'fireEvent' is not defined no-undef + 167:5 error 'fireEvent' is not defined no-undef + 182:5 error 'fireEvent' is not defined no-undef + 197:5 error 'fireEvent' is not defined no-undef + 213:5 error 'fireEvent' is not defined no-undef + 229:5 error 'fireEvent' is not defined no-undef + 234:5 error 'fireEvent' is not defined no-undef + 295:7 error 'fireEvent' is not defined no-undef + 310:7 error 'fireEvent' is not defined no-undef + 315:7 error 'fireEvent' is not defined no-undef + 337:7 error 'fireEvent' is not defined no-undef + 353:7 error 'fireEvent' is not defined no-undef + 369:7 error 'fireEvent' is not defined no-undef + 392:7 error 'fireEvent' is not defined no-undef + +/home/senke/git/talas/veza/apps/web/src/components/ui.backup/virtualized-list.tsx + 148:17 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + 175:17 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + +/home/senke/git/talas/veza/apps/web/src/components/ui/LazyComponent.test.tsx + 2:32 warning 'vi' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/ui/LazyComponent.tsx + 162:17 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + 178:23 warning '_fallback' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/ui/Toast.test.tsx + 1:37 warning 'waitFor' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/ui/WaveformVisualizer.tsx + 97:28 error 'HTMLCanvasElement' is not defined no-undef + 148:44 error 'HTMLCanvasElement' is not defined no-undef + +/home/senke/git/talas/veza/apps/web/src/components/ui/avatar-upload.tsx + 231:5 warning React Hook useCallback has a missing dependency: 'userId'. Either include it or remove the dependency array react-hooks/exhaustive-deps + 298:6 warning React Hook useCallback has a missing dependency: 'userId'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/components/ui/badge.tsx + 10:58 error 'HTMLSpanElement' is not defined no-undef + 94:39 error 'HTMLSpanElement' is not defined no-undef + +/home/senke/git/talas/veza/apps/web/src/components/ui/date-picker.test.tsx + 166:11 warning 'nextButton' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/ui/date-picker.tsx + 137:10 warning '_open' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/ui/dialog.test.tsx + 288:12 error 'DialogHeader' is not defined no-undef + 290:13 error 'DialogHeader' is not defined no-undef + +/home/senke/git/talas/veza/apps/web/src/components/ui/dropdown-menu.tsx + 68:10 warning '_internalOpen' is assigned a value but never used @typescript-eslint/no-unused-vars + 132:17 warning 'align' is assigned a value but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars + 283:25 error 'HTMLSpanElement' is not defined no-undef + +/home/senke/git/talas/veza/apps/web/src/components/ui/file-upload.test.tsx + 142:25 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 150:20 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/components/ui/file-upload.tsx + 275:5 warning React Hook useCallback has a missing dependency: 'validateFile'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/components/ui/input.tsx + 129:22 error 'FileList' is not defined no-undef + +/home/senke/git/talas/veza/apps/web/src/components/ui/label.tsx + 10:63 error 'HTMLLabelElement' is not defined no-undef + 35:32 error 'HTMLLabelElement' is not defined no-undef + +/home/senke/git/talas/veza/apps/web/src/components/ui/optimized-image.tsx + 366:17 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + +/home/senke/git/talas/veza/apps/web/src/components/ui/radio-group.test.tsx + 73:21 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/components/ui/table.tsx + 33:58 error 'HTMLTableElement' is not defined no-undef + 46:3 error 'HTMLTableElement' is not defined no-undef + 71:3 error 'HTMLTableSectionElement' is not defined no-undef + 72:24 error 'HTMLTableSectionElement' is not defined no-undef + 86:3 error 'HTMLTableSectionElement' is not defined no-undef + 87:24 error 'HTMLTableSectionElement' is not defined no-undef + 106:3 error 'HTMLTableSectionElement' is not defined no-undef + 107:24 error 'HTMLTableSectionElement' is not defined no-undef + 128:3 error 'HTMLTableRowElement' is not defined no-undef + 129:24 error 'HTMLTableRowElement' is not defined no-undef + 151:3 error 'HTMLTableCellElement' is not defined no-undef + 152:26 error 'HTMLTableCellElement' is not defined no-undef + 173:3 error 'HTMLTableCellElement' is not defined no-undef + 174:26 error 'HTMLTableCellElement' is not defined no-undef + 193:3 error 'HTMLTableCaptionElement' is not defined no-undef + 194:24 error 'HTMLTableCaptionElement' is not defined no-undef + +/home/senke/git/talas/veza/apps/web/src/components/ui/tooltip.test.tsx + 45:5 error 'fireEvent' is not defined no-undef + 61:5 error 'fireEvent' is not defined no-undef + 68:5 error 'fireEvent' is not defined no-undef + 87:5 error 'fireEvent' is not defined no-undef + 103:5 error 'fireEvent' is not defined no-undef + 119:5 error 'fireEvent' is not defined no-undef + 135:5 error 'fireEvent' is not defined no-undef + 151:5 error 'fireEvent' is not defined no-undef + 167:5 error 'fireEvent' is not defined no-undef + 182:5 error 'fireEvent' is not defined no-undef + 197:5 error 'fireEvent' is not defined no-undef + 213:5 error 'fireEvent' is not defined no-undef + 229:5 error 'fireEvent' is not defined no-undef + 234:5 error 'fireEvent' is not defined no-undef + 295:7 error 'fireEvent' is not defined no-undef + 310:7 error 'fireEvent' is not defined no-undef + 315:7 error 'fireEvent' is not defined no-undef + 337:7 error 'fireEvent' is not defined no-undef + 353:7 error 'fireEvent' is not defined no-undef + 369:7 error 'fireEvent' is not defined no-undef + 392:7 error 'fireEvent' is not defined no-undef + +/home/senke/git/talas/veza/apps/web/src/components/ui/virtualized-list.tsx + 237:17 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + 264:17 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + +/home/senke/git/talas/veza/apps/web/src/components/upload/FileUploadZone.tsx + 44:6 warning React Hook useCallback has a missing dependency: 'validateFile'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/components/upload/metadata/CoverArtUploadModal.tsx + 81:18 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/upload/metadata/MetadataEditor.tsx + 44:6 warning React Hook useEffect has a missing dependency: 'allMetadata'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/components/views/AuthView.tsx + 18:10 warning '_pendingCredentials' is assigned a value but never used @typescript-eslint/no-unused-vars + 18:31 warning '_setPendingCredentials' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/views/MarketplaceView.tsx + 31:8 warning React Hook useEffect has a missing dependency: 'loadProducts'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/components/views/ProfileView.tsx + 141:8 warning React Hook useEffect has a missing dependency: 'addToast'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/components/views/SettingsView.tsx + 24:23 warning '_addToast' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/views/SocialView.tsx + 17:23 warning '_addToast' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/views/UploadView.tsx + 65:18 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/context/AudioContext.tsx + 51:14 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + 74:19 warning '_setIsMuted' is assigned a value but never used @typescript-eslint/no-unused-vars + 116:6 warning React Hook useEffect has a missing dependency: 'nextTrack'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/context/AuthContext.test.tsx + 1:22 warning 'waitFor' is defined but never used @typescript-eslint/no-unused-vars + 6:10 warning 'ToastProvider' is defined but never used @typescript-eslint/no-unused-vars + 32:5 error 'mockGetCurrentUser' is not defined no-undef + +/home/senke/git/talas/veza/apps/web/src/context/AuthContext.tsx + 20:14 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + +/home/senke/git/talas/veza/apps/web/src/context/CartContext.test.tsx + 6:10 warning 'ToastProvider' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/context/CartContext.tsx + 17:14 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + +/home/senke/git/talas/veza/apps/web/src/context/ThemeContext.tsx + 12:14 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + +/home/senke/git/talas/veza/apps/web/src/context/ToastContext.tsx + 10:14 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + +/home/senke/git/talas/veza/apps/web/src/features/auth/components/TwoFactorVerify.test.tsx + 252:11 warning 'user' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/auth/hooks/useUsernameAvailability.ts + 19:16 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/auth/pages/LoginPage.test.tsx + 99:11 warning 'user' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/auth/pages/LoginPage.tsx + 46:16 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/auth/pages/OAuthCallbackPage.test.tsx + 2:26 warning 'waitFor' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/auth/services/authService.test.ts + 2:8 warning 'axios' is defined but never used @typescript-eslint/no-unused-vars + 13:8 warning 'ApiError' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/auth/services/emailVerificationService.test.ts + 2:8 warning 'axios' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/chat/components/ChatInterface.tsx + 97:6 warning React Hook useEffect has missing dependencies: 'loadMessages', 'showError', and 'showSuccess'. Either include them or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/chat/components/ChatMessages.tsx + 21:9 warning The 'conversationMessages' conditional could make the dependencies of useEffect Hook (at line 31) change on every render. To fix this, wrap the initialization of 'conversationMessages' in its own useMemo() Hook react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/chat/components/ChatRoom.tsx + 23:9 warning The 'currentMessages' logical expression could make the dependencies of useEffect Hook (at line 41) change on every render. To fix this, wrap the initialization of 'currentMessages' in its own useMemo() Hook react-hooks/exhaustive-deps + 37:6 warning React Hook useEffect has a missing dependency: 'messages'. Either include it or remove the dependency array react-hooks/exhaustive-deps + 37:23 warning React Hook useEffect has a complex expression in the dependency array. Extract it to a separate variable so it can be statically checked react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/chat/components/VirtualizedChatMessages.tsx + 117:6 warning React Hook useEffect has a missing dependency: 'messages'. Either include it or remove the dependency array react-hooks/exhaustive-deps + 204:17 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + 276:6 warning React Hook useEffect has a missing dependency: 'fetchMessages'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/chat/hooks/useChat.ts + 36:10 warning '_messagesToSend' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/chat/pages/ChatPage.tsx + 18:23 warning '_disconnect' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/chat/store/chatStore.ts + 110:17 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 110:41 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 111:21 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 111:64 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 128:17 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 128:45 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 129:21 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 129:68 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/library/components/LibraryManager.tsx + 82:6 warning React Hook useEffect has a missing dependency: 'fetchTracks'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/library/pages/LibraryPage.tsx + 132:6 warning React Hook useEffect has a missing dependency: 'page'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/player/components/GlobalPlayer.tsx + 5:33 warning 'DialogTrigger' is defined but never used @typescript-eslint/no-unused-vars + 6:10 warning 'cn' is defined but never used @typescript-eslint/no-unused-vars + 7:10 warning 'Maximize2' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/player/components/MiniPlayer.test.tsx + 75:5 warning 'volume' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/player/components/PlaybackSpeedControl.test.tsx + 162:13 warning 'container' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/player/components/PlayerError.test.tsx + 158:11 warning 'retryButton' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/player/components/ProgressBar.test.tsx + 3:8 warning 'userEvent' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/player/components/QualitySelector.test.tsx + 160:13 warning 'container' is assigned a value but never used @typescript-eslint/no-unused-vars + 180:13 warning 'container' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/player/components/VolumeControl.test.tsx + 172:11 warning 'user' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/player/hooks/useKeyboardShortcuts.ts + 108:5 warning React Hook useCallback has a missing dependency: 'player'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/player/store/playerStore.test.ts + 179:15 warning 'initialIndex' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/__tests__/collaboration.integration.test.tsx + 556:13 warning 'updatedCollaborator' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/AddTrackToPlaylistModal.test.tsx + 78:9 warning 'mockToast' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/CollaboratorManagement.test.tsx + 211:11 warning 'addModal' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/ImportPlaylistButton.tsx + 63:20 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/PlaylistActions.test.tsx + 7:26 warning 'waitFor' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/PlaylistActions.tsx + 56:3 error React Hook "useEffect" is called conditionally. React Hooks must be called in the exact same order in every component render react-hooks/rules-of-hooks + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/PlaylistAnalytics.tsx + 56:6 warning React Hook useEffect has a missing dependency: 'loadAnalytics'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/PlaylistBatchActions.tsx + 178:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 190:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/PlaylistErrorBoundary.tsx + 100:10 warning Fast refresh only works when a file only exports components. Move your component(s) to a separate file react-refresh/only-export-components + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/PlaylistFollowButton.test.tsx + 119:13 warning 'container' is assigned a value but never used @typescript-eslint/no-unused-vars + 142:13 warning 'container' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/PlaylistFollowButton.tsx + 88:26 error React Hook "useMutation" is called conditionally. React Hooks must be called in the exact same order in every component render react-hooks/rules-of-hooks + 121:28 error React Hook "useMutation" is called conditionally. React Hooks must be called in the exact same order in every component render react-hooks/rules-of-hooks + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/PlaylistHeader.test.tsx + 6:36 warning 'beforeEach' is defined but never used @typescript-eslint/no-unused-vars + 13:28 warning 'playlistId' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/PlaylistRecommendations.tsx + 67:6 warning React Hook useEffect has a missing dependency: 'toastError'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/PlaylistSearch.tsx + 81:6 warning React Hook useEffect has a missing dependency: 'filters'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/PlaylistTrackList.test.tsx + 8:26 warning 'waitFor' is defined but never used @typescript-eslint/no-unused-vars + 9:8 warning 'userEvent' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/PlaylistTrackList.tsx + 204:14 warning 'err' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/RemoveTrackButton.test.tsx + 219:5 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/SharePlaylistModal.test.tsx + 91:9 warning 'mockToast' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/SharePlaylistModal.tsx + 34:6 warning React Hook useEffect has missing dependencies: 'createShareLinkMutation.isPending', 'handleCreateShare', and 'shareLink'. Either include them or remove the dependency array react-hooks/exhaustive-deps + 58:14 warning 'err' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/hooks/usePlaylistNotifications.ts + 99:9 warning The 'playlistNotifications' logical expression could make the dependencies of useEffect Hook (at line 140) change on every render. To fix this, wrap the initialization of 'playlistNotifications' in its own useMemo() Hook react-hooks/exhaustive-deps + 203:40 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 219:40 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 235:40 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 251:40 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/playlists/pages/PlaylistDetailPage.test.tsx + 55:7 warning 'mockOnClose' is assigned a value but never used @typescript-eslint/no-unused-vars + 56:7 warning 'mockOnTrackAdded' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/services/playlistService.test.ts + 12:3 warning 'addTrack' is defined but never used @typescript-eslint/no-unused-vars + 13:3 warning 'removeTrack' is defined but never used @typescript-eslint/no-unused-vars + 24:15 warning 'CreatePlaylistRequest' is defined but never used @typescript-eslint/no-unused-vars + 24:38 warning 'UpdatePlaylistRequest' is defined but never used @typescript-eslint/no-unused-vars + 75:25 error 'AxiosError' is not defined no-undef + 92:25 error 'AxiosError' is not defined no-undef + 157:25 error 'AxiosError' is not defined no-undef + 190:25 error 'AxiosError' is not defined no-undef + 202:25 error 'AxiosError' is not defined no-undef + 243:25 error 'AxiosError' is not defined no-undef + 269:25 error 'AxiosError' is not defined no-undef + 304:25 error 'AxiosError' is not defined no-undef + 319:25 error 'AxiosError' is not defined no-undef + 331:25 error 'AxiosError' is not defined no-undef + 356:25 error 'AxiosError' is not defined no-undef + 373:25 error 'AxiosError' is not defined no-undef + 389:25 error 'AxiosError' is not defined no-undef + 420:25 error 'AxiosError' is not defined no-undef + 435:25 error 'AxiosError' is not defined no-undef + 451:25 error 'AxiosError' is not defined no-undef + 534:25 error 'AxiosError' is not defined no-undef + 544:25 error 'AxiosError' is not defined no-undef + 592:29 error 'AxiosError' is not defined no-undef + 609:29 error 'AxiosError' is not defined no-undef + 638:29 error 'AxiosError' is not defined no-undef + 650:29 error 'AxiosError' is not defined no-undef + 679:29 error 'AxiosError' is not defined no-undef + 695:29 error 'AxiosError' is not defined no-undef + 753:29 error 'AxiosError' is not defined no-undef + 765:29 error 'AxiosError' is not defined no-undef + 777:29 error 'AxiosError' is not defined no-undef + +/home/senke/git/talas/veza/apps/web/src/features/profile/components/AvatarUpload.test.tsx + 160:23 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/profile/pages/UserProfilePage.test.tsx + 3:10 warning 'BrowserRouter' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/profile/pages/UserProfilePage.tsx + 57:49 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/settings/components/PreferenceSettings.test.tsx + 1:37 warning 'waitFor' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/streaming/components/BitrateSelector.test.tsx + 88:13 warning 'container' is assigned a value but never used @typescript-eslint/no-unused-vars + 164:13 warning 'container' is assigned a value but never used @typescript-eslint/no-unused-vars + 223:11 warning 'dropdown' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/streaming/components/HLSPlayer.test.tsx + 2:37 warning 'waitFor' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/streaming/components/PlaybackDashboard.tsx + 37:6 warning React Hook useEffect has a missing dependency: 'loadDashboard'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/streaming/components/PlaybackHeatmap.tsx + 45:6 warning React Hook useEffect has a missing dependency: 'loadHeatmap'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/streaming/components/PlaybackSummary.tsx + 34:6 warning React Hook useEffect has a missing dependency: 'loadSummary'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/streaming/hooks/useBitrateAdaptation.test.ts + 97:7 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 220:7 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/streaming/hooks/useHLSStream.test.ts + 187:5 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/streaming/hooks/usePlaybackRealtime.test.ts + 30:8 warning 'data' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars + 34:24 warning 'reason' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/streaming/hooks/usePlaybackRealtime.ts + 258:6 warning React Hook useCallback has a missing dependency: 'connect'. Either include it or remove the dependency array react-hooks/exhaustive-deps + 416:6 warning React Hook useCallback has missing dependencies: 'onAnalyticsUpdate' and 'onStatsUpdate'. Either include them or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/streaming/services/playbackAnalyticsService.ts + 288:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/__tests__/trackUpload.integration.test.tsx + 7:10 warning 'render' is defined but never used @typescript-eslint/no-unused-vars + 7:18 warning 'screen' is defined but never used @typescript-eslint/no-unused-vars + 7:26 warning 'waitFor' is defined but never used @typescript-eslint/no-unused-vars + 8:8 warning 'userEvent' is defined but never used @typescript-eslint/no-unused-vars + 61:7 warning 'TestWrapper' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/CommentItem.test.tsx + 8:3 warning 'getReplies' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/CommentSection.test.tsx + 2:35 warning 'fireEvent' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/CommentThread.test.tsx + 111:11 warning 'avatar' is assigned a value but never used @typescript-eslint/no-unused-vars + 229:11 warning 'hasMoreButton' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/LikeButton.test.tsx + 6:0 error Parsing error: Identifier expected + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/PlaysChart.test.tsx + 4:13 warning 'trackAnalytics' is defined but never used @typescript-eslint/no-unused-vars + 15:15 error 'trackService' is not defined no-undef + 30:15 error 'trackService' is not defined no-undef + 41:15 error 'trackService' is not defined no-undef + 53:14 error 'trackService' is not defined no-undef + 63:15 error 'trackService' is not defined no-undef + 78:15 error 'trackService' is not defined no-undef + 83:14 error 'trackService' is not defined no-undef + 94:14 error 'trackService' is not defined no-undef + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/ShareDialog.test.tsx + 7:35 warning 'fireEvent' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/ShareDialog.tsx + 34:6 warning React Hook useEffect has missing dependencies: 'handleCreateShare' and 'share'. Either include them or remove the dependency array react-hooks/exhaustive-deps + 65:14 warning 'err' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackDelete.test.tsx + 2:35 warning 'fireEvent' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackDownloadButton.test.tsx + 91:5 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 99:11 warning 'downloadPromise' is assigned a value but never used @typescript-eslint/no-unused-vars + 119:9 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackEdit.test.tsx + 2:35 warning 'fireEvent' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackGrid.test.tsx + 59:24 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackGridDensitySelector.test.tsx + 1:48 warning 'afterEach' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackHistory.tsx + 50:6 warning React Hook useEffect has a missing dependency: 'loadHistory'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackList.test.tsx + 62:22 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 74:22 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 218:22 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 236:22 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 288:22 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackListContainer.test.tsx + 2:26 warning 'waitFor' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackListRow.test.tsx + 169:13 warning 'container' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackSearch.test.tsx + 2:35 warning 'fireEvent' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackSearchFilters.test.tsx + 2:26 warning 'fireEvent' is defined but never used @typescript-eslint/no-unused-vars + 5:15 warning 'TrackSearchParams' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackShareDialog.test.tsx + 2:35 warning 'fireEvent' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackStats.test.tsx + 4:13 warning 'trackAnalytics' is defined but never used @typescript-eslint/no-unused-vars + 14:15 error 'trackService' is not defined no-undef + 30:15 error 'trackService' is not defined no-undef + 51:15 error 'trackService' is not defined no-undef + 62:15 error 'trackService' is not defined no-undef + 83:15 error 'trackService' is not defined no-undef + 88:14 error 'trackService' is not defined no-undef + 94:14 error 'trackService' is not defined no-undef + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackUpload.test.tsx + 49:11 warning 'user' is assigned a value but never used @typescript-eslint/no-unused-vars + 72:11 warning 'user' is assigned a value but never used @typescript-eslint/no-unused-vars + 208:9 warning 'progressCallback' is assigned a value but never used @typescript-eslint/no-unused-vars + 342:9 warning 'progressCallback' is assigned a value but never used @typescript-eslint/no-unused-vars + 470:24 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 476:20 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/UploadQuota.test.tsx + 6:10 warning 'useAuthStore' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/UploadQuota.tsx + 43:6 warning React Hook useEffect has a missing dependency: 'loadQuota'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/tracks/hooks/useInfiniteScroll.test.ts + 7:23 error 'IntersectionObserverCallback' is not defined no-undef + 8:5 warning 'observerOptions' is assigned a value but never used @typescript-eslint/no-unused-vars + 19:15 error 'IntersectionObserverCallback' is not defined no-undef + 99:9 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 145:9 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 293:9 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 342:9 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 358:9 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/tracks/hooks/useTrackList.test.ts + 308:9 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/tracks/hooks/useTrackList.ts + 77:54 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 79:26 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 80:54 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 82:33 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 97:18 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 143:6 warning React Hook useEffect has missing dependencies: 'searchParams' and 'setSearchParams'. Either include them or remove the dependency array react-hooks/exhaustive-deps + 158:6 warning React Hook useEffect has missing dependencies: 'searchParams' and 'setSearchParams'. Either include them or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/tracks/pages/TrackDetailPage.test.tsx + 2:35 warning 'fireEvent' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/services/analyticsService.test.ts + 15:10 warning 'TrackServiceError' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/services/chunkedUploadService.test.ts + 6:3 warning 'CHUNK_SIZE' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/services/chunkedUploadService.ts + 142:15 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 193:9 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/tracks/services/trackDownloadService.test.ts + 3:10 warning 'apiClient' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/services/trackDownloadService.ts + 65:20 error 'HeadersInit' is not defined no-undef + +/home/senke/git/talas/veza/apps/web/src/features/tracks/services/trackHistoryService.test.ts + 5:3 warning 'TrackHistory' is defined but never used @typescript-eslint/no-unused-vars + 213:28 error 'TrackHistoryAction' is not defined no-undef + +/home/senke/git/talas/veza/apps/web/src/features/tracks/services/trackListService.test.ts + 2:8 warning 'axios' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/upload/components/UploadModal.tsx + 214:5 warning React Hook useCallback has a missing dependency: 'handleClose'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/hooks/useIntersectionObserver.test.ts + 3:10 warning 'useRef' is defined but never used @typescript-eslint/no-unused-vars + 13:22 error 'IntersectionObserverCallback' is not defined no-undef + 19:7 warning 'mockObserver' is assigned a value but never used @typescript-eslint/no-unused-vars + 39:12 error 'mockObserve' is not defined no-undef + 49:12 error 'mockObserve' is not defined no-undef + 62:12 error 'mockDisconnect' is not defined no-undef + 104:13 warning 'result' is assigned a value but never used @typescript-eslint/no-unused-vars + 123:12 error 'IntersectionObserverEntry' is not defined no-undef + 131:12 error 'mockObserve' is not defined no-undef + +/home/senke/git/talas/veza/apps/web/src/hooks/useIntersectionObserver.ts + 15:4 error 'IntersectionObserverEntry' is not defined no-undef + 16:40 error 'IntersectionObserverEntry' is not defined no-undef + +/home/senke/git/talas/veza/apps/web/src/hooks/useKeyboardNavigation.test.ts + 7:9 warning 'mockOnEnter' is assigned a value but never used @typescript-eslint/no-unused-vars + 10:9 warning 'mockOnArrowLeft' is assigned a value but never used @typescript-eslint/no-unused-vars + 11:9 warning 'mockOnArrowRight' is assigned a value but never used @typescript-eslint/no-unused-vars + 12:9 warning 'mockOnTab' is assigned a value but never used @typescript-eslint/no-unused-vars + 13:9 warning 'mockOnShiftTab' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/hooks/useLocalStorage.test.ts + 2:48 warning 'afterEach' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/hooks/useQueryInvalidation.ts + 69:78 error 'EventListener' is not defined no-undef + 72:83 error 'EventListener' is not defined no-undef + +/home/senke/git/talas/veza/apps/web/src/main.tsx + 57:23 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/mocks/handlers.ts + 222:17 warning '_' is assigned a value but never used @typescript-eslint/no-unused-vars + 251:19 warning '_' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/mocks/test-helpers.ts + 1:10 warning 'contextBridge' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/pages/AdminDashboardPage.tsx + 84:6 warning React Hook useEffect has a missing dependency: 'loadDashboardData'. Either include it or remove the dependency array react-hooks/exhaustive-deps + 212:6 warning React Hook useEffect has a missing dependency: 'loadUsers'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/pages/AnalyticsPage.tsx + 39:6 warning React Hook useEffect has a missing dependency: 'loadAnalytics'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/pages/ProfilePage.test.tsx + 1:18 warning 'screen' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/pages/WebhooksPage.tsx + 58:6 warning React Hook useEffect has a missing dependency: 'loadWebhooks'. Either include it or remove the dependency array react-hooks/exhaustive-deps + 371:45 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/pages/auth/Register.test.tsx + 7:10 warning 'useToast' is defined but never used @typescript-eslint/no-unused-vars + 145:11 warning 'mockResponse' is assigned a value but never used @typescript-eslint/no-unused-vars + 220:5 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/schemas/apiSchemas.test.ts + 12:3 warning 'messageSchema' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/services/api/auth.ts + 252:13 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/services/api/client.test.ts + 2:8 warning 'axios' is defined but never used @typescript-eslint/no-unused-vars + 3:10 warning 'apiClient' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/services/api/client.ts + 11:28 warning '_isTimeoutError' is defined but never used @typescript-eslint/no-unused-vars + 11:66 warning '_getTimeoutMessage' is defined but never used @typescript-eslint/no-unused-vars + 324:18 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/services/pwa.ts + 64:29 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 237:9 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 258:9 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 258:9 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/services/requestDeduplication.ts + 52:50 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/services/responseCache.ts + 70:50 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/services/socialService.ts + 3:16 warning 'Notification' is defined but never used @typescript-eslint/no-unused-vars + 3:30 warning 'Comment' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/stores/auth.test.ts + 101:16 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 129:7 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 181:16 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/test/helpers.tsx + 20:10 warning Fast refresh only works when a file only exports components. Move your component(s) to a separate file react-refresh/only-export-components + 35:1 warning This rule can't verify that `export *` only exports components react-refresh/only-export-components + +/home/senke/git/talas/veza/apps/web/src/test/setup.ts + 106:38 error 'EventListener' is not defined no-undef + 126:44 error 'EventListener' is not defined no-undef + 133:47 error 'EventListener' is not defined no-undef + +/home/senke/git/talas/veza/apps/web/src/test/test-utils.tsx + 31:7 warning Fast refresh only works when a file only exports components. Move your component(s) to a separate file react-refresh/only-export-components + 63:1 warning This rule can't verify that `export *` only exports components react-refresh/only-export-components + +/home/senke/git/talas/veza/apps/web/src/types/backend-types.ts + 2:10 warning 'PostType' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/utils/apiErrorHandler.test.ts + 6:36 warning 'beforeEach' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/utils/logger.test.ts + 10:7 warning 'consoleDebugSpy' is assigned a value but never used @typescript-eslint/no-unused-vars + 11:7 warning 'consoleInfoSpy' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/utils/logger.ts + 135:12 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/utils/optimisticUpdates.ts + 360:15 warning '_getValue' is assigned a value but never used @typescript-eslint/no-unused-vars + 361:15 warning '_getCount' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/utils/sanitize.test.ts + 6:36 warning 'beforeEach' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/utils/sanitize.ts + 134:45 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 138:45 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 141:39 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 282:99 error Unnecessary escape character: \- no-useless-escape + +/home/senke/git/talas/veza/apps/web/src/utils/stateCleanup.test.ts + 213:12 warning 'set' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars + 236:12 warning 'set' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/utils/stateHydration.ts + 237:6 warning React Hook useEffect has a missing dependency: 'config'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/utils/stateNormalization.ts + 124:21 warning 'removed' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/utils/statePersistence.ts + 140:24 warning Do not access Object.prototype method 'hasOwnProperty' from target object no-prototype-builtins + +/home/senke/git/talas/veza/apps/web/src/utils/stateVersioning.example.ts + 93:6 warning 'set' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/utils/stateVersioning.test.ts + 6:44 warning 'vi' is defined but never used @typescript-eslint/no-unused-vars + 262:33 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 296:33 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 358:33 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/utils/stateVersioning.ts + 206:50 warning '_migrations' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/utils/timeoutHandler.test.ts + 9:3 warning 'TIMEOUT_MESSAGES' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/utils/typeGuards.test.ts + 13:3 warning 'isSession' is defined but never used @typescript-eslint/no-unused-vars + 14:3 warning 'isAuditLog' is defined but never used @typescript-eslint/no-unused-vars + 20:3 warning 'isTrackArray' is defined but never used @typescript-eslint/no-unused-vars + 21:3 warning 'isPlaylistArray' is defined but never used @typescript-eslint/no-unused-vars + 22:3 warning 'isConversationArray' is defined but never used @typescript-eslint/no-unused-vars + 23:3 warning 'isMessageArray' is defined but never used @typescript-eslint/no-unused-vars + 24:3 warning 'isNotificationArray' is defined but never used @typescript-eslint/no-unused-vars + +✖ 560 problems (164 errors, 396 warnings) + 0 errors and 2 warnings potentially fixable with the `--fix` option. + +npm error Lifecycle script `lint` failed with error: +npm error code 1 +npm error path /home/senke/git/talas/veza/apps/web +npm error workspace veza-frontend@1.0.0 +npm error location /home/senke/git/talas/veza/apps/web +npm error command failed +npm error command sh -c eslint . --ext ts,tsx diff --git a/apps/web/lint_results_2.txt b/apps/web/lint_results_2.txt new file mode 100644 index 000000000..42fa91aff --- /dev/null +++ b/apps/web/lint_results_2.txt @@ -0,0 +1,965 @@ + +> veza-frontend@1.0.0 lint +> eslint . --ext ts,tsx + + +/home/senke/git/talas/veza/apps/web/e2e/auth.spec.ts + 567:25 error Unexpected empty object pattern no-empty-pattern + +/home/senke/git/talas/veza/apps/web/e2e/error-boundary.spec.ts + 257:13 warning 'foundMessage' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/e2e/global-setup.ts + 55:11 warning Unused eslint-disable directive (no problems were reported from 'no-undef') + 83:9 warning Unused eslint-disable directive (no problems were reported from 'no-undef') + +/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts + 1:29 warning 'Page' is defined but never used @typescript-eslint/no-unused-vars + 1:40 warning 'APIRequestContext' is defined but never used @typescript-eslint/no-unused-vars + 4:3 warning 'TEST_USERS' is defined but never used @typescript-eslint/no-unused-vars + 5:3 warning 'loginAsUser' is defined but never used @typescript-eslint/no-unused-vars + 6:3 warning 'forceSubmitForm' is defined but never used @typescript-eslint/no-unused-vars + 7:3 warning 'fillField' is defined but never used @typescript-eslint/no-unused-vars + 8:3 warning 'waitForToast' is defined but never used @typescript-eslint/no-unused-vars + 9:3 warning 'setupErrorCapture' is defined but never used @typescript-eslint/no-unused-vars + 10:3 warning 'getAuthToken' is defined but never used @typescript-eslint/no-unused-vars + 11:3 warning 'navigateViaHref' is defined but never used @typescript-eslint/no-unused-vars + 12:3 warning 'waitForListLoaded' is defined but never used @typescript-eslint/no-unused-vars + 13:3 warning 'openModal' is defined but never used @typescript-eslint/no-unused-vars + 14:3 warning 'closeModal' is defined but never used @typescript-eslint/no-unused-vars + 45:9 warning 'userId' is assigned a value but never used @typescript-eslint/no-unused-vars + 46:9 warning 'trackId' is assigned a value but never used @typescript-eslint/no-unused-vars + 47:9 warning 'playlistId' is assigned a value but never used @typescript-eslint/no-unused-vars + 49:7 warning 'refreshToken' is assigned a value but never used @typescript-eslint/no-unused-vars + 92:15 warning 'loginLinkVisible' is assigned a value but never used @typescript-eslint/no-unused-vars + 267:26 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + 303:26 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + 452:13 warning 'hasContent' is assigned a value but never used @typescript-eslint/no-unused-vars + 459:13 warning 'uploadButton' is assigned a value but never used @typescript-eslint/no-unused-vars + 514:13 warning 'hasProfile' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/e2e/navigation.spec.ts + 1:29 warning 'Page' is defined but never used @typescript-eslint/no-unused-vars + 6:3 warning 'navigateViaHref' is defined but never used @typescript-eslint/no-unused-vars + 22:7 warning 'consoleErrors' is assigned a value but never used @typescript-eslint/no-unused-vars + 23:7 warning 'networkErrors' is assigned a value but never used @typescript-eslint/no-unused-vars + 98:13 warning 'isActive' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/e2e/performance.spec.ts + 43:11 warning 'measure' is assigned a value but never used @typescript-eslint/no-unused-vars + 77:16 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/e2e/playlists.spec.ts + 1:29 warning 'Page' is defined but never used @typescript-eslint/no-unused-vars + 7:3 warning 'closeModal' is defined but never used @typescript-eslint/no-unused-vars + 9:3 warning 'safeClick' is defined but never used @typescript-eslint/no-unused-vars + 574:25 error Unexpected empty object pattern no-empty-pattern + 574:30 warning 'testInfo' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/e2e/profile.spec.ts + 1:29 warning 'Page' is defined but never used @typescript-eslint/no-unused-vars + 5:3 warning 'forceSubmitForm' is defined but never used @typescript-eslint/no-unused-vars + 6:3 warning 'fillField' is defined but never used @typescript-eslint/no-unused-vars + 7:3 warning 'safeClick' is defined but never used @typescript-eslint/no-unused-vars + 8:3 warning 'navigateViaSidebar' is defined but never used @typescript-eslint/no-unused-vars + 185:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 220:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 342:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 563:25 error Unexpected empty object pattern no-empty-pattern + 563:30 warning 'testInfo' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/e2e/qa-audit.spec.ts + 17:16 warning 'captureConsoleErrors' is defined but never used @typescript-eslint/no-unused-vars + 28:16 warning 'captureNetworkErrors' is defined but never used @typescript-eslint/no-unused-vars + 287:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/e2e/track_lifecycle.spec.ts + 1:29 warning 'Page' is defined but never used @typescript-eslint/no-unused-vars + 100:18 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + 118:22 warning 'modalError' is defined but never used @typescript-eslint/no-unused-vars + 233:22 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 265:27 error Unexpected empty object pattern no-empty-pattern + 265:31 warning 'testInfo' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/e2e/tracks_upload_chunked.spec.ts + 1:29 warning 'Page' is defined but never used @typescript-eslint/no-unused-vars + 9:3 warning 'waitForToast' is defined but never used @typescript-eslint/no-unused-vars + 173:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 206:16 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 226:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 242:16 warning 'modalError' is defined but never used @typescript-eslint/no-unused-vars + 308:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 381:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 449:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 461:25 error Unexpected empty object pattern no-empty-pattern + 461:30 warning 'testInfo' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/e2e/upload_flow.spec.ts + 1:29 warning 'Page' is defined but never used @typescript-eslint/no-unused-vars + 9:3 warning 'navigateViaHref' is defined but never used @typescript-eslint/no-unused-vars + 129:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 142:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 230:25 error Unexpected empty object pattern no-empty-pattern + 230:30 warning 'testInfo' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/e2e/utils/test-helpers.ts + 37:9 warning 'storageData' is assigned a value but never used @typescript-eslint/no-unused-vars + 93:14 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + 110:14 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + 217:12 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + 431:16 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + 452:14 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + 588:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 874:12 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/e2e/visual-regression.spec.ts + 2:10 warning 'loginAsUser' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/app/App.tsx + 29:9 error Unexpected empty object pattern no-empty-pattern + +/home/senke/git/talas/veza/apps/web/src/components/admin/AdminModerationView.tsx + 47:16 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/admin/AdminUsersView.tsx + 38:6 warning React Hook useEffect has a missing dependency: 'addToast'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/components/dashboard/TrackList.tsx + 51:14 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/data/Table.tsx + 107:5 warning React Hook useCallback has a missing dependency: 'paginatedDataMemo'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/components/developer/DeveloperDashboardView.tsx + 55:16 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/education/CourseDetailView.tsx + 17:21 warning '_addToast' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/feedback/ToastProvider.tsx + 19:17 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + +/home/senke/git/talas/veza/apps/web/src/components/gamification/ProfileXPView.tsx + 42:6 warning React Hook useEffect has a missing dependency: 'username'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/components/inventory/EquipmentDetailView.tsx + 44:6 warning React Hook useEffect has a missing dependency: 'addToast'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/components/library/AutoMetadataDetectionModal.tsx + 21:21 warning '_addToast' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/library/WatermarkSettingsModal.tsx + 14:21 warning '_addToast' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/library/playlists/PlaylistsView.tsx + 49:18 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/navigation/Breadcrumbs.tsx + 51:23 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/components/player/AudioPlayer.tsx + 188:6 warning React Hook useEffect has a missing dependency: 'handlePlayPause'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/components/player/LyricsPanel.tsx + 15:54 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 15:103 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 49:69 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 49:118 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/components/player/PlayerControls.tsx + 16:59 warning '_playbackRate' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/seller/CreateProductView.tsx + 17:10 warning '_step' is assigned a value but never used @typescript-eslint/no-unused-vars + 17:17 warning '_setStep' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/settings/profile/EditProfile.tsx + 103:6 warning React Hook useEffect has a missing dependency: 'addToast'. Either include it or remove the dependency array react-hooks/exhaustive-deps + 111:14 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + 137:16 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/settings/security/PasskeyModal.tsx + 15:10 warning '_loading' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/settings/security/SessionManagement.tsx + 39:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 50:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/share/ShareLinkManager.tsx + 128:14 warning 'err' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/social/FeedView.tsx + 44:16 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/social/groups/GroupDetailView.tsx + 50:6 warning React Hook useEffect has a missing dependency: 'addToast'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/components/social/groups/GroupsView.tsx + 27:6 warning React Hook useEffect has a missing dependency: 'loadGroups'. Either include it or remove the dependency array react-hooks/exhaustive-deps + 50:16 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + 60:16 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + 70:16 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/studio/CloudFileBrowser.tsx + 29:12 warning '_currentFolder' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/studio/ProjectsManager.tsx + 56:16 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + 68:16 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + 80:16 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/studio/projects/ProjectDetailView.tsx + 24:12 warning '_editMode' is assigned a value but never used @typescript-eslint/no-unused-vars + 28:26 warning '_setProjectFiles' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/ui.backup/LazyComponent.tsx + 22:17 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + 32:23 warning '_fallback' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/ui.backup/button.tsx + 56:18 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + +/home/senke/git/talas/veza/apps/web/src/components/ui.backup/date-picker.test.tsx + 166:11 warning 'nextButton' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/ui.backup/date-picker.tsx + 52:10 warning '_open' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/ui.backup/dialog.test.tsx + 288:12 error 'DialogHeader' is not defined no-undef + 290:13 error 'DialogHeader' is not defined no-undef + +/home/senke/git/talas/veza/apps/web/src/components/ui.backup/file-upload.test.tsx + 110:25 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 118:20 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/components/ui.backup/file-upload.tsx + 168:5 warning React Hook useCallback has a missing dependency: 'validateFile'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/components/ui.backup/optimized-image.tsx + 265:17 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + +/home/senke/git/talas/veza/apps/web/src/components/ui.backup/tooltip.test.tsx + 45:5 error 'fireEvent' is not defined no-undef + 61:5 error 'fireEvent' is not defined no-undef + 68:5 error 'fireEvent' is not defined no-undef + 87:5 error 'fireEvent' is not defined no-undef + 103:5 error 'fireEvent' is not defined no-undef + 119:5 error 'fireEvent' is not defined no-undef + 135:5 error 'fireEvent' is not defined no-undef + 151:5 error 'fireEvent' is not defined no-undef + 167:5 error 'fireEvent' is not defined no-undef + 182:5 error 'fireEvent' is not defined no-undef + 197:5 error 'fireEvent' is not defined no-undef + 213:5 error 'fireEvent' is not defined no-undef + 229:5 error 'fireEvent' is not defined no-undef + 234:5 error 'fireEvent' is not defined no-undef + 295:7 error 'fireEvent' is not defined no-undef + 310:7 error 'fireEvent' is not defined no-undef + 315:7 error 'fireEvent' is not defined no-undef + 337:7 error 'fireEvent' is not defined no-undef + 353:7 error 'fireEvent' is not defined no-undef + 369:7 error 'fireEvent' is not defined no-undef + 392:7 error 'fireEvent' is not defined no-undef + +/home/senke/git/talas/veza/apps/web/src/components/ui.backup/virtualized-list.tsx + 148:17 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + 175:17 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + +/home/senke/git/talas/veza/apps/web/src/components/ui/LazyComponent.test.tsx + 2:32 warning 'vi' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/ui/LazyComponent.tsx + 162:17 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + 178:23 warning '_fallback' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/ui/Toast.test.tsx + 1:37 warning 'waitFor' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/ui/avatar-upload.tsx + 231:5 warning React Hook useCallback has a missing dependency: 'userId'. Either include it or remove the dependency array react-hooks/exhaustive-deps + 298:6 warning React Hook useCallback has a missing dependency: 'userId'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/components/ui/date-picker.test.tsx + 166:11 warning 'nextButton' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/ui/date-picker.tsx + 137:10 warning '_open' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/ui/dialog.test.tsx + 288:12 error 'DialogHeader' is not defined no-undef + 290:13 error 'DialogHeader' is not defined no-undef + +/home/senke/git/talas/veza/apps/web/src/components/ui/dropdown-menu.tsx + 68:10 warning '_internalOpen' is assigned a value but never used @typescript-eslint/no-unused-vars + 132:17 warning 'align' is assigned a value but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/ui/file-upload.test.tsx + 142:25 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 150:20 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/components/ui/file-upload.tsx + 275:5 warning React Hook useCallback has a missing dependency: 'validateFile'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/components/ui/optimized-image.tsx + 366:17 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + +/home/senke/git/talas/veza/apps/web/src/components/ui/radio-group.test.tsx + 73:21 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/components/ui/tooltip.test.tsx + 45:5 error 'fireEvent' is not defined no-undef + 61:5 error 'fireEvent' is not defined no-undef + 68:5 error 'fireEvent' is not defined no-undef + 87:5 error 'fireEvent' is not defined no-undef + 103:5 error 'fireEvent' is not defined no-undef + 119:5 error 'fireEvent' is not defined no-undef + 135:5 error 'fireEvent' is not defined no-undef + 151:5 error 'fireEvent' is not defined no-undef + 167:5 error 'fireEvent' is not defined no-undef + 182:5 error 'fireEvent' is not defined no-undef + 197:5 error 'fireEvent' is not defined no-undef + 213:5 error 'fireEvent' is not defined no-undef + 229:5 error 'fireEvent' is not defined no-undef + 234:5 error 'fireEvent' is not defined no-undef + 295:7 error 'fireEvent' is not defined no-undef + 310:7 error 'fireEvent' is not defined no-undef + 315:7 error 'fireEvent' is not defined no-undef + 337:7 error 'fireEvent' is not defined no-undef + 353:7 error 'fireEvent' is not defined no-undef + 369:7 error 'fireEvent' is not defined no-undef + 392:7 error 'fireEvent' is not defined no-undef + +/home/senke/git/talas/veza/apps/web/src/components/ui/virtualized-list.tsx + 237:17 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + 264:17 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + +/home/senke/git/talas/veza/apps/web/src/components/upload/FileUploadZone.tsx + 44:6 warning React Hook useCallback has a missing dependency: 'validateFile'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/components/upload/metadata/CoverArtUploadModal.tsx + 81:18 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/upload/metadata/MetadataEditor.tsx + 44:6 warning React Hook useEffect has a missing dependency: 'allMetadata'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/components/views/AuthView.tsx + 18:10 warning '_pendingCredentials' is assigned a value but never used @typescript-eslint/no-unused-vars + 18:31 warning '_setPendingCredentials' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/views/MarketplaceView.tsx + 31:8 warning React Hook useEffect has a missing dependency: 'loadProducts'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/components/views/ProfileView.tsx + 141:8 warning React Hook useEffect has a missing dependency: 'addToast'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/components/views/SettingsView.tsx + 24:23 warning '_addToast' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/views/SocialView.tsx + 17:23 warning '_addToast' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/views/UploadView.tsx + 65:18 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/context/AudioContext.tsx + 51:14 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + 74:19 warning '_setIsMuted' is assigned a value but never used @typescript-eslint/no-unused-vars + 116:6 warning React Hook useEffect has a missing dependency: 'nextTrack'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/context/AuthContext.test.tsx + 1:22 warning 'waitFor' is defined but never used @typescript-eslint/no-unused-vars + 6:10 warning 'ToastProvider' is defined but never used @typescript-eslint/no-unused-vars + 32:5 error 'mockGetCurrentUser' is not defined no-undef + +/home/senke/git/talas/veza/apps/web/src/context/AuthContext.tsx + 20:14 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + +/home/senke/git/talas/veza/apps/web/src/context/CartContext.test.tsx + 6:10 warning 'ToastProvider' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/context/CartContext.tsx + 17:14 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + +/home/senke/git/talas/veza/apps/web/src/context/ThemeContext.tsx + 12:14 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + +/home/senke/git/talas/veza/apps/web/src/context/ToastContext.tsx + 10:14 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + +/home/senke/git/talas/veza/apps/web/src/features/auth/components/TwoFactorVerify.test.tsx + 252:11 warning 'user' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/auth/hooks/useUsernameAvailability.ts + 19:16 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/auth/pages/LoginPage.test.tsx + 99:11 warning 'user' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/auth/pages/LoginPage.tsx + 46:16 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/auth/pages/OAuthCallbackPage.test.tsx + 2:26 warning 'waitFor' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/auth/services/authService.test.ts + 2:8 warning 'axios' is defined but never used @typescript-eslint/no-unused-vars + 13:8 warning 'ApiError' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/auth/services/emailVerificationService.test.ts + 2:8 warning 'axios' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/chat/components/ChatInterface.tsx + 97:6 warning React Hook useEffect has missing dependencies: 'loadMessages', 'showError', and 'showSuccess'. Either include them or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/chat/components/ChatMessages.tsx + 21:9 warning The 'conversationMessages' conditional could make the dependencies of useEffect Hook (at line 31) change on every render. To fix this, wrap the initialization of 'conversationMessages' in its own useMemo() Hook react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/chat/components/ChatRoom.tsx + 23:9 warning The 'currentMessages' logical expression could make the dependencies of useEffect Hook (at line 41) change on every render. To fix this, wrap the initialization of 'currentMessages' in its own useMemo() Hook react-hooks/exhaustive-deps + 37:6 warning React Hook useEffect has a missing dependency: 'messages'. Either include it or remove the dependency array react-hooks/exhaustive-deps + 37:23 warning React Hook useEffect has a complex expression in the dependency array. Extract it to a separate variable so it can be statically checked react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/chat/components/VirtualizedChatMessages.tsx + 117:6 warning React Hook useEffect has a missing dependency: 'messages'. Either include it or remove the dependency array react-hooks/exhaustive-deps + 204:17 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + 276:6 warning React Hook useEffect has a missing dependency: 'fetchMessages'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/chat/hooks/useChat.ts + 36:10 warning '_messagesToSend' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/chat/pages/ChatPage.tsx + 18:23 warning '_disconnect' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/chat/store/chatStore.ts + 110:17 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 110:41 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 111:21 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 111:64 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 128:17 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 128:45 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 129:21 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 129:68 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/library/components/LibraryManager.tsx + 82:6 warning React Hook useEffect has a missing dependency: 'fetchTracks'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/library/pages/LibraryPage.tsx + 132:6 warning React Hook useEffect has a missing dependency: 'page'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/player/components/GlobalPlayer.tsx + 5:33 warning 'DialogTrigger' is defined but never used @typescript-eslint/no-unused-vars + 6:10 warning 'cn' is defined but never used @typescript-eslint/no-unused-vars + 7:10 warning 'Maximize2' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/player/components/MiniPlayer.test.tsx + 75:5 warning 'volume' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/player/components/PlaybackSpeedControl.test.tsx + 162:13 warning 'container' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/player/components/PlayerError.test.tsx + 158:11 warning 'retryButton' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/player/components/ProgressBar.test.tsx + 3:8 warning 'userEvent' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/player/components/QualitySelector.test.tsx + 160:13 warning 'container' is assigned a value but never used @typescript-eslint/no-unused-vars + 180:13 warning 'container' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/player/components/VolumeControl.test.tsx + 172:11 warning 'user' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/player/hooks/useKeyboardShortcuts.ts + 108:5 warning React Hook useCallback has a missing dependency: 'player'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/player/store/playerStore.test.ts + 179:15 warning 'initialIndex' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/__tests__/collaboration.integration.test.tsx + 556:13 warning 'updatedCollaborator' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/AddTrackToPlaylistModal.test.tsx + 78:9 warning 'mockToast' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/CollaboratorManagement.test.tsx + 211:11 warning 'addModal' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/ImportPlaylistButton.tsx + 63:20 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/PlaylistActions.test.tsx + 7:26 warning 'waitFor' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/PlaylistActions.tsx + 56:3 error React Hook "useEffect" is called conditionally. React Hooks must be called in the exact same order in every component render react-hooks/rules-of-hooks + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/PlaylistAnalytics.tsx + 56:6 warning React Hook useEffect has a missing dependency: 'loadAnalytics'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/PlaylistBatchActions.tsx + 178:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 190:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/PlaylistErrorBoundary.tsx + 100:10 warning Fast refresh only works when a file only exports components. Move your component(s) to a separate file react-refresh/only-export-components + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/PlaylistFollowButton.test.tsx + 119:13 warning 'container' is assigned a value but never used @typescript-eslint/no-unused-vars + 142:13 warning 'container' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/PlaylistHeader.test.tsx + 6:36 warning 'beforeEach' is defined but never used @typescript-eslint/no-unused-vars + 13:28 warning 'playlistId' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/PlaylistRecommendations.tsx + 67:6 warning React Hook useEffect has a missing dependency: 'toastError'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/PlaylistSearch.tsx + 81:6 warning React Hook useEffect has a missing dependency: 'filters'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/PlaylistTrackList.test.tsx + 8:26 warning 'waitFor' is defined but never used @typescript-eslint/no-unused-vars + 9:8 warning 'userEvent' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/PlaylistTrackList.tsx + 204:14 warning 'err' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/RemoveTrackButton.test.tsx + 219:5 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/SharePlaylistModal.test.tsx + 91:9 warning 'mockToast' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/SharePlaylistModal.tsx + 34:6 warning React Hook useEffect has missing dependencies: 'createShareLinkMutation.isPending', 'handleCreateShare', and 'shareLink'. Either include them or remove the dependency array react-hooks/exhaustive-deps + 58:14 warning 'err' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/hooks/usePlaylistNotifications.ts + 99:9 warning The 'playlistNotifications' logical expression could make the dependencies of useEffect Hook (at line 140) change on every render. To fix this, wrap the initialization of 'playlistNotifications' in its own useMemo() Hook react-hooks/exhaustive-deps + 203:40 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 219:40 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 235:40 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 251:40 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/playlists/pages/PlaylistDetailPage.test.tsx + 55:7 warning 'mockOnClose' is assigned a value but never used @typescript-eslint/no-unused-vars + 56:7 warning 'mockOnTrackAdded' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/services/playlistService.test.ts + 12:3 warning 'addTrack' is defined but never used @typescript-eslint/no-unused-vars + 13:3 warning 'removeTrack' is defined but never used @typescript-eslint/no-unused-vars + 24:15 warning 'CreatePlaylistRequest' is defined but never used @typescript-eslint/no-unused-vars + 24:38 warning 'UpdatePlaylistRequest' is defined but never used @typescript-eslint/no-unused-vars + 75:25 error 'AxiosError' is not defined no-undef + 92:25 error 'AxiosError' is not defined no-undef + 157:25 error 'AxiosError' is not defined no-undef + 190:25 error 'AxiosError' is not defined no-undef + 202:25 error 'AxiosError' is not defined no-undef + 243:25 error 'AxiosError' is not defined no-undef + 269:25 error 'AxiosError' is not defined no-undef + 304:25 error 'AxiosError' is not defined no-undef + 319:25 error 'AxiosError' is not defined no-undef + 331:25 error 'AxiosError' is not defined no-undef + 356:25 error 'AxiosError' is not defined no-undef + 373:25 error 'AxiosError' is not defined no-undef + 389:25 error 'AxiosError' is not defined no-undef + 420:25 error 'AxiosError' is not defined no-undef + 435:25 error 'AxiosError' is not defined no-undef + 451:25 error 'AxiosError' is not defined no-undef + 534:25 error 'AxiosError' is not defined no-undef + 544:25 error 'AxiosError' is not defined no-undef + 592:29 error 'AxiosError' is not defined no-undef + 609:29 error 'AxiosError' is not defined no-undef + 638:29 error 'AxiosError' is not defined no-undef + 650:29 error 'AxiosError' is not defined no-undef + 679:29 error 'AxiosError' is not defined no-undef + 695:29 error 'AxiosError' is not defined no-undef + 753:29 error 'AxiosError' is not defined no-undef + 765:29 error 'AxiosError' is not defined no-undef + 777:29 error 'AxiosError' is not defined no-undef + +/home/senke/git/talas/veza/apps/web/src/features/profile/components/AvatarUpload.test.tsx + 160:23 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/profile/pages/UserProfilePage.test.tsx + 3:10 warning 'BrowserRouter' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/profile/pages/UserProfilePage.tsx + 57:49 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/settings/components/PreferenceSettings.test.tsx + 1:37 warning 'waitFor' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/streaming/components/BitrateSelector.test.tsx + 88:13 warning 'container' is assigned a value but never used @typescript-eslint/no-unused-vars + 164:13 warning 'container' is assigned a value but never used @typescript-eslint/no-unused-vars + 223:11 warning 'dropdown' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/streaming/components/HLSPlayer.test.tsx + 2:37 warning 'waitFor' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/streaming/components/PlaybackDashboard.tsx + 37:6 warning React Hook useEffect has a missing dependency: 'loadDashboard'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/streaming/components/PlaybackHeatmap.tsx + 45:6 warning React Hook useEffect has a missing dependency: 'loadHeatmap'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/streaming/components/PlaybackSummary.tsx + 34:6 warning React Hook useEffect has a missing dependency: 'loadSummary'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/streaming/hooks/useBitrateAdaptation.test.ts + 97:7 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 220:7 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/streaming/hooks/useHLSStream.test.ts + 187:5 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/streaming/hooks/usePlaybackRealtime.test.ts + 30:8 warning 'data' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars + 34:24 warning 'reason' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/streaming/hooks/usePlaybackRealtime.ts + 258:6 warning React Hook useCallback has a missing dependency: 'connect'. Either include it or remove the dependency array react-hooks/exhaustive-deps + 416:6 warning React Hook useCallback has missing dependencies: 'onAnalyticsUpdate' and 'onStatsUpdate'. Either include them or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/streaming/services/playbackAnalyticsService.ts + 288:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/__tests__/trackUpload.integration.test.tsx + 7:10 warning 'render' is defined but never used @typescript-eslint/no-unused-vars + 7:18 warning 'screen' is defined but never used @typescript-eslint/no-unused-vars + 7:26 warning 'waitFor' is defined but never used @typescript-eslint/no-unused-vars + 8:8 warning 'userEvent' is defined but never used @typescript-eslint/no-unused-vars + 61:7 warning 'TestWrapper' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/CommentItem.test.tsx + 8:3 warning 'getReplies' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/CommentSection.test.tsx + 2:35 warning 'fireEvent' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/CommentThread.test.tsx + 111:11 warning 'avatar' is assigned a value but never used @typescript-eslint/no-unused-vars + 229:11 warning 'hasMoreButton' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/LikeButton.test.tsx + 6:0 error Parsing error: Identifier expected + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/PlaysChart.test.tsx + 4:13 warning 'trackAnalytics' is defined but never used @typescript-eslint/no-unused-vars + 15:15 error 'trackService' is not defined no-undef + 30:15 error 'trackService' is not defined no-undef + 41:15 error 'trackService' is not defined no-undef + 53:14 error 'trackService' is not defined no-undef + 63:15 error 'trackService' is not defined no-undef + 78:15 error 'trackService' is not defined no-undef + 83:14 error 'trackService' is not defined no-undef + 94:14 error 'trackService' is not defined no-undef + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/ShareDialog.test.tsx + 7:35 warning 'fireEvent' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/ShareDialog.tsx + 34:6 warning React Hook useEffect has missing dependencies: 'handleCreateShare' and 'share'. Either include them or remove the dependency array react-hooks/exhaustive-deps + 65:14 warning 'err' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackDelete.test.tsx + 2:35 warning 'fireEvent' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackDownloadButton.test.tsx + 91:5 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 99:11 warning 'downloadPromise' is assigned a value but never used @typescript-eslint/no-unused-vars + 119:9 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackEdit.test.tsx + 2:35 warning 'fireEvent' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackGrid.test.tsx + 59:24 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackGridDensitySelector.test.tsx + 1:48 warning 'afterEach' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackHistory.tsx + 50:6 warning React Hook useEffect has a missing dependency: 'loadHistory'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackList.test.tsx + 62:22 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 74:22 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 218:22 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 236:22 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 288:22 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackListContainer.test.tsx + 2:26 warning 'waitFor' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackListRow.test.tsx + 169:13 warning 'container' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackSearch.test.tsx + 2:35 warning 'fireEvent' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackSearchFilters.test.tsx + 2:26 warning 'fireEvent' is defined but never used @typescript-eslint/no-unused-vars + 5:15 warning 'TrackSearchParams' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackShareDialog.test.tsx + 2:35 warning 'fireEvent' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackStats.test.tsx + 4:13 warning 'trackAnalytics' is defined but never used @typescript-eslint/no-unused-vars + 14:15 error 'trackService' is not defined no-undef + 30:15 error 'trackService' is not defined no-undef + 51:15 error 'trackService' is not defined no-undef + 62:15 error 'trackService' is not defined no-undef + 83:15 error 'trackService' is not defined no-undef + 88:14 error 'trackService' is not defined no-undef + 94:14 error 'trackService' is not defined no-undef + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackUpload.test.tsx + 49:11 warning 'user' is assigned a value but never used @typescript-eslint/no-unused-vars + 72:11 warning 'user' is assigned a value but never used @typescript-eslint/no-unused-vars + 208:9 warning 'progressCallback' is assigned a value but never used @typescript-eslint/no-unused-vars + 342:9 warning 'progressCallback' is assigned a value but never used @typescript-eslint/no-unused-vars + 470:24 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 476:20 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/UploadQuota.test.tsx + 6:10 warning 'useAuthStore' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/UploadQuota.tsx + 43:6 warning React Hook useEffect has a missing dependency: 'loadQuota'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/tracks/hooks/useInfiniteScroll.test.ts + 7:23 error 'IntersectionObserverCallback' is not defined no-undef + 8:5 warning 'observerOptions' is assigned a value but never used @typescript-eslint/no-unused-vars + 19:15 error 'IntersectionObserverCallback' is not defined no-undef + 99:9 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 145:9 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 293:9 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 342:9 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 358:9 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/tracks/hooks/useTrackList.test.ts + 308:9 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/tracks/hooks/useTrackList.ts + 77:54 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 79:26 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 80:54 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 82:33 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 97:18 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 143:6 warning React Hook useEffect has missing dependencies: 'searchParams' and 'setSearchParams'. Either include them or remove the dependency array react-hooks/exhaustive-deps + 158:6 warning React Hook useEffect has missing dependencies: 'searchParams' and 'setSearchParams'. Either include them or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/tracks/pages/TrackDetailPage.test.tsx + 2:35 warning 'fireEvent' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/services/analyticsService.test.ts + 15:10 warning 'TrackServiceError' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/services/chunkedUploadService.test.ts + 6:3 warning 'CHUNK_SIZE' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/services/chunkedUploadService.ts + 142:15 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 193:9 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/tracks/services/trackDownloadService.test.ts + 3:10 warning 'apiClient' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/services/trackDownloadService.ts + 65:20 error 'HeadersInit' is not defined no-undef + +/home/senke/git/talas/veza/apps/web/src/features/tracks/services/trackHistoryService.test.ts + 5:3 warning 'TrackHistory' is defined but never used @typescript-eslint/no-unused-vars + 213:28 error 'TrackHistoryAction' is not defined no-undef + +/home/senke/git/talas/veza/apps/web/src/features/tracks/services/trackListService.test.ts + 2:8 warning 'axios' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/upload/components/UploadModal.tsx + 214:5 warning React Hook useCallback has a missing dependency: 'handleClose'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/hooks/useIntersectionObserver.test.ts + 3:10 warning 'useRef' is defined but never used @typescript-eslint/no-unused-vars + 13:22 error 'IntersectionObserverCallback' is not defined no-undef + 19:7 warning 'mockObserver' is assigned a value but never used @typescript-eslint/no-unused-vars + 39:12 error 'mockObserve' is not defined no-undef + 49:12 error 'mockObserve' is not defined no-undef + 62:12 error 'mockDisconnect' is not defined no-undef + 104:13 warning 'result' is assigned a value but never used @typescript-eslint/no-unused-vars + 123:12 error 'IntersectionObserverEntry' is not defined no-undef + 131:12 error 'mockObserve' is not defined no-undef + +/home/senke/git/talas/veza/apps/web/src/hooks/useIntersectionObserver.ts + 15:4 error 'IntersectionObserverEntry' is not defined no-undef + 16:40 error 'IntersectionObserverEntry' is not defined no-undef + +/home/senke/git/talas/veza/apps/web/src/hooks/useKeyboardNavigation.test.ts + 7:9 warning 'mockOnEnter' is assigned a value but never used @typescript-eslint/no-unused-vars + 10:9 warning 'mockOnArrowLeft' is assigned a value but never used @typescript-eslint/no-unused-vars + 11:9 warning 'mockOnArrowRight' is assigned a value but never used @typescript-eslint/no-unused-vars + 12:9 warning 'mockOnTab' is assigned a value but never used @typescript-eslint/no-unused-vars + 13:9 warning 'mockOnShiftTab' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/hooks/useLocalStorage.test.ts + 2:48 warning 'afterEach' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/hooks/useQueryInvalidation.ts + 69:78 error 'EventListener' is not defined no-undef + 72:83 error 'EventListener' is not defined no-undef + +/home/senke/git/talas/veza/apps/web/src/main.tsx + 57:23 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/mocks/handlers.ts + 222:17 warning '_' is assigned a value but never used @typescript-eslint/no-unused-vars + 251:19 warning '_' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/mocks/test-helpers.ts + 1:10 warning 'contextBridge' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/pages/AdminDashboardPage.tsx + 84:6 warning React Hook useEffect has a missing dependency: 'loadDashboardData'. Either include it or remove the dependency array react-hooks/exhaustive-deps + 212:6 warning React Hook useEffect has a missing dependency: 'loadUsers'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/pages/AnalyticsPage.tsx + 39:6 warning React Hook useEffect has a missing dependency: 'loadAnalytics'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/pages/ProfilePage.test.tsx + 1:18 warning 'screen' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/pages/WebhooksPage.tsx + 58:6 warning React Hook useEffect has a missing dependency: 'loadWebhooks'. Either include it or remove the dependency array react-hooks/exhaustive-deps + 371:45 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/pages/auth/Register.test.tsx + 7:10 warning 'useToast' is defined but never used @typescript-eslint/no-unused-vars + 145:11 warning 'mockResponse' is assigned a value but never used @typescript-eslint/no-unused-vars + 220:5 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/schemas/apiSchemas.test.ts + 12:3 warning 'messageSchema' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/services/api/auth.ts + 252:13 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/services/api/client.test.ts + 2:8 warning 'axios' is defined but never used @typescript-eslint/no-unused-vars + 3:10 warning 'apiClient' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/services/api/client.ts + 11:28 warning '_isTimeoutError' is defined but never used @typescript-eslint/no-unused-vars + 11:66 warning '_getTimeoutMessage' is defined but never used @typescript-eslint/no-unused-vars + 324:18 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/services/pwa.ts + 64:29 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 237:9 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 258:9 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 258:9 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/services/requestDeduplication.ts + 52:50 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/services/responseCache.ts + 70:50 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/services/socialService.ts + 3:16 warning 'Notification' is defined but never used @typescript-eslint/no-unused-vars + 3:30 warning 'Comment' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/stores/auth.test.ts + 101:16 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 129:7 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 181:16 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/test/helpers.tsx + 20:10 warning Fast refresh only works when a file only exports components. Move your component(s) to a separate file react-refresh/only-export-components + 35:1 warning This rule can't verify that `export *` only exports components react-refresh/only-export-components + +/home/senke/git/talas/veza/apps/web/src/test/setup.ts + 106:38 error 'EventListener' is not defined no-undef + 126:44 error 'EventListener' is not defined no-undef + 133:47 error 'EventListener' is not defined no-undef + +/home/senke/git/talas/veza/apps/web/src/test/test-utils.tsx + 31:7 warning Fast refresh only works when a file only exports components. Move your component(s) to a separate file react-refresh/only-export-components + 63:1 warning This rule can't verify that `export *` only exports components react-refresh/only-export-components + +/home/senke/git/talas/veza/apps/web/src/types/backend-types.ts + 2:10 warning 'PostType' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/utils/apiErrorHandler.test.ts + 6:36 warning 'beforeEach' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/utils/logger.test.ts + 10:7 warning 'consoleDebugSpy' is assigned a value but never used @typescript-eslint/no-unused-vars + 11:7 warning 'consoleInfoSpy' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/utils/logger.ts + 135:12 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/utils/optimisticUpdates.ts + 360:15 warning '_getValue' is assigned a value but never used @typescript-eslint/no-unused-vars + 361:15 warning '_getCount' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/utils/sanitize.test.ts + 6:36 warning 'beforeEach' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/utils/sanitize.ts + 134:45 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 138:45 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 141:39 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 282:99 error Unnecessary escape character: \- no-useless-escape + +/home/senke/git/talas/veza/apps/web/src/utils/stateCleanup.test.ts + 213:12 warning 'set' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars + 236:12 warning 'set' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/utils/stateHydration.ts + 237:6 warning React Hook useEffect has a missing dependency: 'config'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/utils/stateNormalization.ts + 124:21 warning 'removed' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/utils/statePersistence.ts + 140:24 warning Do not access Object.prototype method 'hasOwnProperty' from target object no-prototype-builtins + +/home/senke/git/talas/veza/apps/web/src/utils/stateVersioning.example.ts + 93:6 warning 'set' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/utils/stateVersioning.test.ts + 6:44 warning 'vi' is defined but never used @typescript-eslint/no-unused-vars + 262:33 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 296:33 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 358:33 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/utils/stateVersioning.ts + 206:50 warning '_migrations' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/utils/timeoutHandler.test.ts + 9:3 warning 'TIMEOUT_MESSAGES' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/utils/typeGuards.test.ts + 13:3 warning 'isSession' is defined but never used @typescript-eslint/no-unused-vars + 14:3 warning 'isAuditLog' is defined but never used @typescript-eslint/no-unused-vars + 20:3 warning 'isTrackArray' is defined but never used @typescript-eslint/no-unused-vars + 21:3 warning 'isPlaylistArray' is defined but never used @typescript-eslint/no-unused-vars + 22:3 warning 'isConversationArray' is defined but never used @typescript-eslint/no-unused-vars + 23:3 warning 'isMessageArray' is defined but never used @typescript-eslint/no-unused-vars + 24:3 warning 'isNotificationArray' is defined but never used @typescript-eslint/no-unused-vars + +✖ 512 problems (116 errors, 396 warnings) + 0 errors and 2 warnings potentially fixable with the `--fix` option. + +npm error Lifecycle script `lint` failed with error: +npm error code 1 +npm error path /home/senke/git/talas/veza/apps/web +npm error workspace veza-frontend@1.0.0 +npm error location /home/senke/git/talas/veza/apps/web +npm error command failed +npm error command sh -c eslint . --ext ts,tsx diff --git a/apps/web/lint_results_3.txt b/apps/web/lint_results_3.txt new file mode 100644 index 000000000..2f4219c7e --- /dev/null +++ b/apps/web/lint_results_3.txt @@ -0,0 +1,894 @@ + +> veza-frontend@1.0.0 lint +> eslint . --ext ts,tsx + + +/home/senke/git/talas/veza/apps/web/e2e/error-boundary.spec.ts + 257:13 warning 'foundMessage' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/e2e/global-setup.ts + 55:11 warning Unused eslint-disable directive (no problems were reported from 'no-undef') + 83:9 warning Unused eslint-disable directive (no problems were reported from 'no-undef') + +/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts + 1:29 warning 'Page' is defined but never used @typescript-eslint/no-unused-vars + 1:40 warning 'APIRequestContext' is defined but never used @typescript-eslint/no-unused-vars + 4:3 warning 'TEST_USERS' is defined but never used @typescript-eslint/no-unused-vars + 5:3 warning 'loginAsUser' is defined but never used @typescript-eslint/no-unused-vars + 6:3 warning 'forceSubmitForm' is defined but never used @typescript-eslint/no-unused-vars + 7:3 warning 'fillField' is defined but never used @typescript-eslint/no-unused-vars + 8:3 warning 'waitForToast' is defined but never used @typescript-eslint/no-unused-vars + 9:3 warning 'setupErrorCapture' is defined but never used @typescript-eslint/no-unused-vars + 10:3 warning 'getAuthToken' is defined but never used @typescript-eslint/no-unused-vars + 11:3 warning 'navigateViaHref' is defined but never used @typescript-eslint/no-unused-vars + 12:3 warning 'waitForListLoaded' is defined but never used @typescript-eslint/no-unused-vars + 13:3 warning 'openModal' is defined but never used @typescript-eslint/no-unused-vars + 14:3 warning 'closeModal' is defined but never used @typescript-eslint/no-unused-vars + 45:9 warning 'userId' is assigned a value but never used @typescript-eslint/no-unused-vars + 46:9 warning 'trackId' is assigned a value but never used @typescript-eslint/no-unused-vars + 47:9 warning 'playlistId' is assigned a value but never used @typescript-eslint/no-unused-vars + 49:7 warning 'refreshToken' is assigned a value but never used @typescript-eslint/no-unused-vars + 92:15 warning 'loginLinkVisible' is assigned a value but never used @typescript-eslint/no-unused-vars + 267:26 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + 303:26 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + 452:13 warning 'hasContent' is assigned a value but never used @typescript-eslint/no-unused-vars + 459:13 warning 'uploadButton' is assigned a value but never used @typescript-eslint/no-unused-vars + 514:13 warning 'hasProfile' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/e2e/navigation.spec.ts + 1:29 warning 'Page' is defined but never used @typescript-eslint/no-unused-vars + 6:3 warning 'navigateViaHref' is defined but never used @typescript-eslint/no-unused-vars + 22:7 warning 'consoleErrors' is assigned a value but never used @typescript-eslint/no-unused-vars + 23:7 warning 'networkErrors' is assigned a value but never used @typescript-eslint/no-unused-vars + 98:13 warning 'isActive' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/e2e/performance.spec.ts + 43:11 warning 'measure' is assigned a value but never used @typescript-eslint/no-unused-vars + 77:16 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/e2e/playlists.spec.ts + 1:29 warning 'Page' is defined but never used @typescript-eslint/no-unused-vars + 7:3 warning 'closeModal' is defined but never used @typescript-eslint/no-unused-vars + 9:3 warning 'safeClick' is defined but never used @typescript-eslint/no-unused-vars + 574:36 warning 'testInfo' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/e2e/profile.spec.ts + 1:29 warning 'Page' is defined but never used @typescript-eslint/no-unused-vars + 5:3 warning 'forceSubmitForm' is defined but never used @typescript-eslint/no-unused-vars + 6:3 warning 'fillField' is defined but never used @typescript-eslint/no-unused-vars + 7:3 warning 'safeClick' is defined but never used @typescript-eslint/no-unused-vars + 8:3 warning 'navigateViaSidebar' is defined but never used @typescript-eslint/no-unused-vars + 185:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 220:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 342:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 563:36 warning 'testInfo' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/e2e/qa-audit.spec.ts + 17:16 warning 'captureConsoleErrors' is defined but never used @typescript-eslint/no-unused-vars + 28:16 warning 'captureNetworkErrors' is defined but never used @typescript-eslint/no-unused-vars + 287:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/e2e/track_lifecycle.spec.ts + 1:29 warning 'Page' is defined but never used @typescript-eslint/no-unused-vars + 100:18 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + 118:22 warning 'modalError' is defined but never used @typescript-eslint/no-unused-vars + 233:22 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 265:27 error Unexpected empty object pattern no-empty-pattern + 265:31 warning 'testInfo' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/e2e/tracks_upload_chunked.spec.ts + 1:29 warning 'Page' is defined but never used @typescript-eslint/no-unused-vars + 9:3 warning 'waitForToast' is defined but never used @typescript-eslint/no-unused-vars + 173:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 206:16 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 226:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 242:16 warning 'modalError' is defined but never used @typescript-eslint/no-unused-vars + 308:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 381:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 449:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 461:36 warning 'testInfo' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/e2e/upload_flow.spec.ts + 1:29 warning 'Page' is defined but never used @typescript-eslint/no-unused-vars + 9:3 warning 'navigateViaHref' is defined but never used @typescript-eslint/no-unused-vars + 129:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 142:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 230:36 warning 'testInfo' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/e2e/utils/test-helpers.ts + 37:9 warning 'storageData' is assigned a value but never used @typescript-eslint/no-unused-vars + 93:14 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + 110:14 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + 217:12 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + 431:16 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + 452:14 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + 588:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 874:12 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/e2e/visual-regression.spec.ts + 2:10 warning 'loginAsUser' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/admin/AdminModerationView.tsx + 47:16 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/admin/AdminUsersView.tsx + 38:6 warning React Hook useEffect has a missing dependency: 'addToast'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/components/dashboard/TrackList.tsx + 51:14 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/data/Table.tsx + 107:5 warning React Hook useCallback has a missing dependency: 'paginatedDataMemo'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/components/developer/DeveloperDashboardView.tsx + 55:16 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/education/CourseDetailView.tsx + 17:21 warning '_addToast' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/feedback/ToastProvider.tsx + 19:17 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + +/home/senke/git/talas/veza/apps/web/src/components/gamification/ProfileXPView.tsx + 42:6 warning React Hook useEffect has a missing dependency: 'username'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/components/inventory/EquipmentDetailView.tsx + 44:6 warning React Hook useEffect has a missing dependency: 'addToast'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/components/library/AutoMetadataDetectionModal.tsx + 21:21 warning '_addToast' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/library/WatermarkSettingsModal.tsx + 14:21 warning '_addToast' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/library/playlists/PlaylistsView.tsx + 49:18 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/navigation/Breadcrumbs.tsx + 51:23 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/components/player/AudioPlayer.tsx + 188:6 warning React Hook useEffect has a missing dependency: 'handlePlayPause'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/components/player/LyricsPanel.tsx + 15:54 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 15:103 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 49:69 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 49:118 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/components/player/PlayerControls.tsx + 16:59 warning '_playbackRate' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/seller/CreateProductView.tsx + 17:10 warning '_step' is assigned a value but never used @typescript-eslint/no-unused-vars + 17:17 warning '_setStep' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/settings/profile/EditProfile.tsx + 103:6 warning React Hook useEffect has a missing dependency: 'addToast'. Either include it or remove the dependency array react-hooks/exhaustive-deps + 111:14 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + 137:16 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/settings/security/PasskeyModal.tsx + 15:10 warning '_loading' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/settings/security/SessionManagement.tsx + 39:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 50:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/share/ShareLinkManager.tsx + 128:14 warning 'err' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/social/FeedView.tsx + 44:16 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/social/groups/GroupDetailView.tsx + 50:6 warning React Hook useEffect has a missing dependency: 'addToast'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/components/social/groups/GroupsView.tsx + 27:6 warning React Hook useEffect has a missing dependency: 'loadGroups'. Either include it or remove the dependency array react-hooks/exhaustive-deps + 50:16 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + 60:16 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + 70:16 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/studio/CloudFileBrowser.tsx + 29:12 warning '_currentFolder' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/studio/ProjectsManager.tsx + 56:16 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + 68:16 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + 80:16 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/studio/projects/ProjectDetailView.tsx + 24:12 warning '_editMode' is assigned a value but never used @typescript-eslint/no-unused-vars + 28:26 warning '_setProjectFiles' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/ui.backup/LazyComponent.tsx + 22:17 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + 32:23 warning '_fallback' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/ui.backup/button.tsx + 56:18 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + +/home/senke/git/talas/veza/apps/web/src/components/ui.backup/date-picker.test.tsx + 166:11 warning 'nextButton' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/ui.backup/date-picker.tsx + 52:10 warning '_open' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/ui.backup/dialog.test.tsx + 288:12 error 'DialogHeader' is not defined no-undef + 290:13 error 'DialogHeader' is not defined no-undef + +/home/senke/git/talas/veza/apps/web/src/components/ui.backup/file-upload.test.tsx + 110:25 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 118:20 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/components/ui.backup/file-upload.tsx + 168:5 warning React Hook useCallback has a missing dependency: 'validateFile'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/components/ui.backup/optimized-image.tsx + 265:17 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + +/home/senke/git/talas/veza/apps/web/src/components/ui.backup/tooltip.test.tsx + 45:5 error 'fireEvent' is not defined no-undef + 61:5 error 'fireEvent' is not defined no-undef + 68:5 error 'fireEvent' is not defined no-undef + 87:5 error 'fireEvent' is not defined no-undef + 103:5 error 'fireEvent' is not defined no-undef + 119:5 error 'fireEvent' is not defined no-undef + 135:5 error 'fireEvent' is not defined no-undef + 151:5 error 'fireEvent' is not defined no-undef + 167:5 error 'fireEvent' is not defined no-undef + 182:5 error 'fireEvent' is not defined no-undef + 197:5 error 'fireEvent' is not defined no-undef + 213:5 error 'fireEvent' is not defined no-undef + 229:5 error 'fireEvent' is not defined no-undef + 234:5 error 'fireEvent' is not defined no-undef + 295:7 error 'fireEvent' is not defined no-undef + 310:7 error 'fireEvent' is not defined no-undef + 315:7 error 'fireEvent' is not defined no-undef + 337:7 error 'fireEvent' is not defined no-undef + 353:7 error 'fireEvent' is not defined no-undef + 369:7 error 'fireEvent' is not defined no-undef + 392:7 error 'fireEvent' is not defined no-undef + +/home/senke/git/talas/veza/apps/web/src/components/ui.backup/virtualized-list.tsx + 148:17 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + 175:17 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + +/home/senke/git/talas/veza/apps/web/src/components/ui/LazyComponent.test.tsx + 2:32 warning 'vi' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/ui/LazyComponent.tsx + 162:17 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + 178:23 warning '_fallback' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/ui/Toast.test.tsx + 1:37 warning 'waitFor' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/ui/avatar-upload.tsx + 231:5 warning React Hook useCallback has a missing dependency: 'userId'. Either include it or remove the dependency array react-hooks/exhaustive-deps + 298:6 warning React Hook useCallback has a missing dependency: 'userId'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/components/ui/date-picker.test.tsx + 166:11 warning 'nextButton' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/ui/date-picker.tsx + 137:10 warning '_open' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/ui/dialog.test.tsx + 288:12 error 'DialogHeader' is not defined no-undef + 290:13 error 'DialogHeader' is not defined no-undef + +/home/senke/git/talas/veza/apps/web/src/components/ui/dropdown-menu.tsx + 68:10 warning '_internalOpen' is assigned a value but never used @typescript-eslint/no-unused-vars + 132:17 warning 'align' is assigned a value but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/ui/file-upload.test.tsx + 142:25 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 150:20 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/components/ui/file-upload.tsx + 275:5 warning React Hook useCallback has a missing dependency: 'validateFile'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/components/ui/optimized-image.tsx + 366:17 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + +/home/senke/git/talas/veza/apps/web/src/components/ui/radio-group.test.tsx + 73:21 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/components/ui/virtualized-list.tsx + 237:17 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + 264:17 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + +/home/senke/git/talas/veza/apps/web/src/components/upload/FileUploadZone.tsx + 44:6 warning React Hook useCallback has a missing dependency: 'validateFile'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/components/upload/metadata/CoverArtUploadModal.tsx + 81:18 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/upload/metadata/MetadataEditor.tsx + 44:6 warning React Hook useEffect has a missing dependency: 'allMetadata'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/components/views/AuthView.tsx + 18:10 warning '_pendingCredentials' is assigned a value but never used @typescript-eslint/no-unused-vars + 18:31 warning '_setPendingCredentials' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/views/MarketplaceView.tsx + 31:8 warning React Hook useEffect has a missing dependency: 'loadProducts'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/components/views/ProfileView.tsx + 141:8 warning React Hook useEffect has a missing dependency: 'addToast'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/components/views/SettingsView.tsx + 24:23 warning '_addToast' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/views/SocialView.tsx + 17:23 warning '_addToast' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/views/UploadView.tsx + 65:18 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/context/AudioContext.tsx + 51:14 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + 74:19 warning '_setIsMuted' is assigned a value but never used @typescript-eslint/no-unused-vars + 116:6 warning React Hook useEffect has a missing dependency: 'nextTrack'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/context/AuthContext.test.tsx + 1:22 warning 'waitFor' is defined but never used @typescript-eslint/no-unused-vars + 6:10 warning 'ToastProvider' is defined but never used @typescript-eslint/no-unused-vars + 32:5 error 'mockGetCurrentUser' is not defined no-undef + +/home/senke/git/talas/veza/apps/web/src/context/AuthContext.tsx + 20:14 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + +/home/senke/git/talas/veza/apps/web/src/context/CartContext.test.tsx + 6:10 warning 'ToastProvider' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/context/CartContext.tsx + 17:14 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + +/home/senke/git/talas/veza/apps/web/src/context/ThemeContext.tsx + 12:14 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + +/home/senke/git/talas/veza/apps/web/src/context/ToastContext.tsx + 10:14 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + +/home/senke/git/talas/veza/apps/web/src/features/auth/components/TwoFactorVerify.test.tsx + 252:11 warning 'user' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/auth/hooks/useUsernameAvailability.ts + 19:16 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/auth/pages/LoginPage.test.tsx + 99:11 warning 'user' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/auth/pages/LoginPage.tsx + 46:16 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/auth/pages/OAuthCallbackPage.test.tsx + 2:26 warning 'waitFor' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/auth/services/authService.test.ts + 2:8 warning 'axios' is defined but never used @typescript-eslint/no-unused-vars + 13:8 warning 'ApiError' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/auth/services/emailVerificationService.test.ts + 2:8 warning 'axios' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/chat/components/ChatInterface.tsx + 97:6 warning React Hook useEffect has missing dependencies: 'loadMessages', 'showError', and 'showSuccess'. Either include them or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/chat/components/ChatMessages.tsx + 21:9 warning The 'conversationMessages' conditional could make the dependencies of useEffect Hook (at line 31) change on every render. To fix this, wrap the initialization of 'conversationMessages' in its own useMemo() Hook react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/chat/components/ChatRoom.tsx + 23:9 warning The 'currentMessages' logical expression could make the dependencies of useEffect Hook (at line 41) change on every render. To fix this, wrap the initialization of 'currentMessages' in its own useMemo() Hook react-hooks/exhaustive-deps + 37:6 warning React Hook useEffect has a missing dependency: 'messages'. Either include it or remove the dependency array react-hooks/exhaustive-deps + 37:23 warning React Hook useEffect has a complex expression in the dependency array. Extract it to a separate variable so it can be statically checked react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/chat/components/VirtualizedChatMessages.tsx + 117:6 warning React Hook useEffect has a missing dependency: 'messages'. Either include it or remove the dependency array react-hooks/exhaustive-deps + 204:17 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + 276:6 warning React Hook useEffect has a missing dependency: 'fetchMessages'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/chat/hooks/useChat.ts + 36:10 warning '_messagesToSend' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/chat/pages/ChatPage.tsx + 18:23 warning '_disconnect' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/chat/store/chatStore.ts + 110:17 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 110:41 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 111:21 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 111:64 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 128:17 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 128:45 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 129:21 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 129:68 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/library/components/LibraryManager.tsx + 82:6 warning React Hook useEffect has a missing dependency: 'fetchTracks'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/library/pages/LibraryPage.tsx + 132:6 warning React Hook useEffect has a missing dependency: 'page'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/player/components/GlobalPlayer.tsx + 5:33 warning 'DialogTrigger' is defined but never used @typescript-eslint/no-unused-vars + 6:10 warning 'cn' is defined but never used @typescript-eslint/no-unused-vars + 7:10 warning 'Maximize2' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/player/components/MiniPlayer.test.tsx + 75:5 warning 'volume' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/player/components/PlaybackSpeedControl.test.tsx + 162:13 warning 'container' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/player/components/PlayerError.test.tsx + 158:11 warning 'retryButton' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/player/components/ProgressBar.test.tsx + 3:8 warning 'userEvent' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/player/components/QualitySelector.test.tsx + 160:13 warning 'container' is assigned a value but never used @typescript-eslint/no-unused-vars + 180:13 warning 'container' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/player/components/VolumeControl.test.tsx + 172:11 warning 'user' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/player/hooks/useKeyboardShortcuts.ts + 108:5 warning React Hook useCallback has a missing dependency: 'player'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/player/store/playerStore.test.ts + 179:15 warning 'initialIndex' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/__tests__/collaboration.integration.test.tsx + 556:13 warning 'updatedCollaborator' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/AddTrackToPlaylistModal.test.tsx + 78:9 warning 'mockToast' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/CollaboratorManagement.test.tsx + 211:11 warning 'addModal' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/ImportPlaylistButton.tsx + 63:20 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/PlaylistActions.test.tsx + 7:26 warning 'waitFor' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/PlaylistAnalytics.tsx + 56:6 warning React Hook useEffect has a missing dependency: 'loadAnalytics'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/PlaylistBatchActions.tsx + 178:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 190:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/PlaylistErrorBoundary.tsx + 100:10 warning Fast refresh only works when a file only exports components. Move your component(s) to a separate file react-refresh/only-export-components + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/PlaylistFollowButton.test.tsx + 119:13 warning 'container' is assigned a value but never used @typescript-eslint/no-unused-vars + 142:13 warning 'container' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/PlaylistHeader.test.tsx + 6:36 warning 'beforeEach' is defined but never used @typescript-eslint/no-unused-vars + 13:28 warning 'playlistId' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/PlaylistRecommendations.tsx + 67:6 warning React Hook useEffect has a missing dependency: 'toastError'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/PlaylistSearch.tsx + 81:6 warning React Hook useEffect has a missing dependency: 'filters'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/PlaylistTrackList.test.tsx + 8:26 warning 'waitFor' is defined but never used @typescript-eslint/no-unused-vars + 9:8 warning 'userEvent' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/PlaylistTrackList.tsx + 204:14 warning 'err' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/RemoveTrackButton.test.tsx + 219:5 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/SharePlaylistModal.test.tsx + 91:9 warning 'mockToast' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/SharePlaylistModal.tsx + 34:6 warning React Hook useEffect has missing dependencies: 'createShareLinkMutation.isPending', 'handleCreateShare', and 'shareLink'. Either include them or remove the dependency array react-hooks/exhaustive-deps + 58:14 warning 'err' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/hooks/usePlaylistNotifications.ts + 99:9 warning The 'playlistNotifications' logical expression could make the dependencies of useEffect Hook (at line 140) change on every render. To fix this, wrap the initialization of 'playlistNotifications' in its own useMemo() Hook react-hooks/exhaustive-deps + 203:40 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 219:40 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 235:40 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 251:40 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/playlists/pages/PlaylistDetailPage.test.tsx + 55:7 warning 'mockOnClose' is assigned a value but never used @typescript-eslint/no-unused-vars + 56:7 warning 'mockOnTrackAdded' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/services/playlistService.test.ts + 12:3 warning 'addTrack' is defined but never used @typescript-eslint/no-unused-vars + 13:3 warning 'removeTrack' is defined but never used @typescript-eslint/no-unused-vars + 24:15 warning 'CreatePlaylistRequest' is defined but never used @typescript-eslint/no-unused-vars + 24:38 warning 'UpdatePlaylistRequest' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/profile/components/AvatarUpload.test.tsx + 160:23 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/profile/pages/UserProfilePage.test.tsx + 3:10 warning 'BrowserRouter' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/profile/pages/UserProfilePage.tsx + 57:49 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/settings/components/PreferenceSettings.test.tsx + 1:37 warning 'waitFor' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/streaming/components/BitrateSelector.test.tsx + 88:13 warning 'container' is assigned a value but never used @typescript-eslint/no-unused-vars + 164:13 warning 'container' is assigned a value but never used @typescript-eslint/no-unused-vars + 223:11 warning 'dropdown' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/streaming/components/HLSPlayer.test.tsx + 2:37 warning 'waitFor' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/streaming/components/PlaybackDashboard.tsx + 37:6 warning React Hook useEffect has a missing dependency: 'loadDashboard'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/streaming/components/PlaybackHeatmap.tsx + 45:6 warning React Hook useEffect has a missing dependency: 'loadHeatmap'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/streaming/components/PlaybackSummary.tsx + 34:6 warning React Hook useEffect has a missing dependency: 'loadSummary'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/streaming/hooks/useBitrateAdaptation.test.ts + 97:7 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 220:7 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/streaming/hooks/useHLSStream.test.ts + 187:5 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/streaming/hooks/usePlaybackRealtime.test.ts + 30:8 warning 'data' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars + 34:24 warning 'reason' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/streaming/hooks/usePlaybackRealtime.ts + 258:6 warning React Hook useCallback has a missing dependency: 'connect'. Either include it or remove the dependency array react-hooks/exhaustive-deps + 416:6 warning React Hook useCallback has missing dependencies: 'onAnalyticsUpdate' and 'onStatsUpdate'. Either include them or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/streaming/services/playbackAnalyticsService.ts + 288:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/__tests__/trackUpload.integration.test.tsx + 7:10 warning 'render' is defined but never used @typescript-eslint/no-unused-vars + 7:18 warning 'screen' is defined but never used @typescript-eslint/no-unused-vars + 7:26 warning 'waitFor' is defined but never used @typescript-eslint/no-unused-vars + 8:8 warning 'userEvent' is defined but never used @typescript-eslint/no-unused-vars + 61:7 warning 'TestWrapper' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/CommentItem.test.tsx + 8:3 warning 'getReplies' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/CommentSection.test.tsx + 2:35 warning 'fireEvent' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/CommentThread.test.tsx + 111:11 warning 'avatar' is assigned a value but never used @typescript-eslint/no-unused-vars + 229:11 warning 'hasMoreButton' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/LikeButton.test.tsx + 6:0 error Parsing error: Identifier expected + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/PlaysChart.test.tsx + 4:13 warning 'trackAnalytics' is defined but never used @typescript-eslint/no-unused-vars + 15:15 error 'trackService' is not defined no-undef + 30:15 error 'trackService' is not defined no-undef + 41:15 error 'trackService' is not defined no-undef + 53:14 error 'trackService' is not defined no-undef + 63:15 error 'trackService' is not defined no-undef + 78:15 error 'trackService' is not defined no-undef + 83:14 error 'trackService' is not defined no-undef + 94:14 error 'trackService' is not defined no-undef + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/ShareDialog.test.tsx + 7:35 warning 'fireEvent' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/ShareDialog.tsx + 34:6 warning React Hook useEffect has missing dependencies: 'handleCreateShare' and 'share'. Either include them or remove the dependency array react-hooks/exhaustive-deps + 65:14 warning 'err' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackDelete.test.tsx + 2:35 warning 'fireEvent' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackDownloadButton.test.tsx + 91:5 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 99:11 warning 'downloadPromise' is assigned a value but never used @typescript-eslint/no-unused-vars + 119:9 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackEdit.test.tsx + 2:35 warning 'fireEvent' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackGrid.test.tsx + 59:24 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackGridDensitySelector.test.tsx + 1:48 warning 'afterEach' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackHistory.tsx + 50:6 warning React Hook useEffect has a missing dependency: 'loadHistory'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackList.test.tsx + 62:22 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 74:22 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 218:22 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 236:22 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 288:22 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackListContainer.test.tsx + 2:26 warning 'waitFor' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackListRow.test.tsx + 169:13 warning 'container' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackSearch.test.tsx + 2:35 warning 'fireEvent' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackSearchFilters.test.tsx + 2:26 warning 'fireEvent' is defined but never used @typescript-eslint/no-unused-vars + 5:15 warning 'TrackSearchParams' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackShareDialog.test.tsx + 2:35 warning 'fireEvent' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackStats.test.tsx + 4:13 warning 'trackAnalytics' is defined but never used @typescript-eslint/no-unused-vars + 14:15 error 'trackService' is not defined no-undef + 30:15 error 'trackService' is not defined no-undef + 51:15 error 'trackService' is not defined no-undef + 62:15 error 'trackService' is not defined no-undef + 83:15 error 'trackService' is not defined no-undef + 88:14 error 'trackService' is not defined no-undef + 94:14 error 'trackService' is not defined no-undef + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackUpload.test.tsx + 49:11 warning 'user' is assigned a value but never used @typescript-eslint/no-unused-vars + 72:11 warning 'user' is assigned a value but never used @typescript-eslint/no-unused-vars + 208:9 warning 'progressCallback' is assigned a value but never used @typescript-eslint/no-unused-vars + 342:9 warning 'progressCallback' is assigned a value but never used @typescript-eslint/no-unused-vars + 470:24 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 476:20 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/UploadQuota.test.tsx + 6:10 warning 'useAuthStore' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/UploadQuota.tsx + 43:6 warning React Hook useEffect has a missing dependency: 'loadQuota'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/tracks/hooks/useInfiniteScroll.test.ts + 8:5 warning 'observerOptions' is assigned a value but never used @typescript-eslint/no-unused-vars + 99:9 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 145:9 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 293:9 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 342:9 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 358:9 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/tracks/hooks/useTrackList.test.ts + 308:9 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/tracks/hooks/useTrackList.ts + 77:54 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 79:26 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 80:54 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 82:33 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 97:18 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 143:6 warning React Hook useEffect has missing dependencies: 'searchParams' and 'setSearchParams'. Either include them or remove the dependency array react-hooks/exhaustive-deps + 158:6 warning React Hook useEffect has missing dependencies: 'searchParams' and 'setSearchParams'. Either include them or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/tracks/pages/TrackDetailPage.test.tsx + 2:35 warning 'fireEvent' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/services/analyticsService.test.ts + 15:10 warning 'TrackServiceError' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/services/chunkedUploadService.test.ts + 6:3 warning 'CHUNK_SIZE' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/services/chunkedUploadService.ts + 142:15 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 193:9 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/tracks/services/trackDownloadService.test.ts + 3:10 warning 'apiClient' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/services/trackDownloadService.ts + 65:20 error 'HeadersInit' is not defined no-undef + +/home/senke/git/talas/veza/apps/web/src/features/tracks/services/trackHistoryService.test.ts + 5:3 warning 'TrackHistory' is defined but never used @typescript-eslint/no-unused-vars + 213:28 error 'TrackHistoryAction' is not defined no-undef + +/home/senke/git/talas/veza/apps/web/src/features/tracks/services/trackListService.test.ts + 2:8 warning 'axios' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/upload/components/UploadModal.tsx + 214:5 warning React Hook useCallback has a missing dependency: 'handleClose'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/hooks/useIntersectionObserver.test.ts + 3:10 warning 'useRef' is defined but never used @typescript-eslint/no-unused-vars + 19:7 warning 'mockObserver' is assigned a value but never used @typescript-eslint/no-unused-vars + 39:12 error 'mockObserve' is not defined no-undef + 49:12 error 'mockObserve' is not defined no-undef + 62:12 error 'mockDisconnect' is not defined no-undef + 104:13 warning 'result' is assigned a value but never used @typescript-eslint/no-unused-vars + 131:12 error 'mockObserve' is not defined no-undef + +/home/senke/git/talas/veza/apps/web/src/hooks/useKeyboardNavigation.test.ts + 7:9 warning 'mockOnEnter' is assigned a value but never used @typescript-eslint/no-unused-vars + 10:9 warning 'mockOnArrowLeft' is assigned a value but never used @typescript-eslint/no-unused-vars + 11:9 warning 'mockOnArrowRight' is assigned a value but never used @typescript-eslint/no-unused-vars + 12:9 warning 'mockOnTab' is assigned a value but never used @typescript-eslint/no-unused-vars + 13:9 warning 'mockOnShiftTab' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/hooks/useLocalStorage.test.ts + 2:48 warning 'afterEach' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/hooks/useQueryInvalidation.ts + 69:78 error 'EventListener' is not defined no-undef + 72:83 error 'EventListener' is not defined no-undef + +/home/senke/git/talas/veza/apps/web/src/main.tsx + 57:23 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/mocks/handlers.ts + 222:17 warning '_' is assigned a value but never used @typescript-eslint/no-unused-vars + 251:19 warning '_' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/mocks/test-helpers.ts + 1:10 warning 'contextBridge' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/pages/AdminDashboardPage.tsx + 84:6 warning React Hook useEffect has a missing dependency: 'loadDashboardData'. Either include it or remove the dependency array react-hooks/exhaustive-deps + 212:6 warning React Hook useEffect has a missing dependency: 'loadUsers'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/pages/AnalyticsPage.tsx + 39:6 warning React Hook useEffect has a missing dependency: 'loadAnalytics'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/pages/ProfilePage.test.tsx + 1:18 warning 'screen' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/pages/WebhooksPage.tsx + 58:6 warning React Hook useEffect has a missing dependency: 'loadWebhooks'. Either include it or remove the dependency array react-hooks/exhaustive-deps + 371:45 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/pages/auth/Register.test.tsx + 7:10 warning 'useToast' is defined but never used @typescript-eslint/no-unused-vars + 145:11 warning 'mockResponse' is assigned a value but never used @typescript-eslint/no-unused-vars + 220:5 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/schemas/apiSchemas.test.ts + 12:3 warning 'messageSchema' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/services/api/auth.ts + 252:13 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/services/api/client.test.ts + 2:8 warning 'axios' is defined but never used @typescript-eslint/no-unused-vars + 3:10 warning 'apiClient' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/services/api/client.ts + 11:28 warning '_isTimeoutError' is defined but never used @typescript-eslint/no-unused-vars + 11:66 warning '_getTimeoutMessage' is defined but never used @typescript-eslint/no-unused-vars + 324:18 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/services/pwa.ts + 64:29 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 237:9 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 258:9 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 258:9 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/services/requestDeduplication.ts + 52:50 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/services/responseCache.ts + 70:50 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/services/socialService.ts + 3:16 warning 'Notification' is defined but never used @typescript-eslint/no-unused-vars + 3:30 warning 'Comment' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/stores/auth.test.ts + 101:16 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 129:7 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 181:16 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/test/helpers.tsx + 20:10 warning Fast refresh only works when a file only exports components. Move your component(s) to a separate file react-refresh/only-export-components + 35:1 warning This rule can't verify that `export *` only exports components react-refresh/only-export-components + +/home/senke/git/talas/veza/apps/web/src/test/setup.ts + 106:38 error 'EventListener' is not defined no-undef + 126:44 error 'EventListener' is not defined no-undef + 133:47 error 'EventListener' is not defined no-undef + +/home/senke/git/talas/veza/apps/web/src/test/test-utils.tsx + 31:7 warning Fast refresh only works when a file only exports components. Move your component(s) to a separate file react-refresh/only-export-components + 63:1 warning This rule can't verify that `export *` only exports components react-refresh/only-export-components + +/home/senke/git/talas/veza/apps/web/src/types/backend-types.ts + 2:10 warning 'PostType' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/utils/apiErrorHandler.test.ts + 6:36 warning 'beforeEach' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/utils/logger.test.ts + 10:7 warning 'consoleDebugSpy' is assigned a value but never used @typescript-eslint/no-unused-vars + 11:7 warning 'consoleInfoSpy' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/utils/logger.ts + 135:12 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/utils/optimisticUpdates.ts + 360:15 warning '_getValue' is assigned a value but never used @typescript-eslint/no-unused-vars + 361:15 warning '_getCount' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/utils/sanitize.test.ts + 6:36 warning 'beforeEach' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/utils/sanitize.ts + 134:45 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 138:45 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 141:39 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 282:99 error Unnecessary escape character: \- no-useless-escape + +/home/senke/git/talas/veza/apps/web/src/utils/stateCleanup.test.ts + 213:12 warning 'set' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars + 236:12 warning 'set' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/utils/stateHydration.ts + 237:6 warning React Hook useEffect has a missing dependency: 'config'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/utils/stateNormalization.ts + 124:21 warning 'removed' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/utils/statePersistence.ts + 140:24 warning Do not access Object.prototype method 'hasOwnProperty' from target object no-prototype-builtins + +/home/senke/git/talas/veza/apps/web/src/utils/stateVersioning.example.ts + 93:6 warning 'set' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/utils/stateVersioning.test.ts + 6:44 warning 'vi' is defined but never used @typescript-eslint/no-unused-vars + 262:33 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 296:33 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 358:33 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/utils/stateVersioning.ts + 206:50 warning '_migrations' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/utils/timeoutHandler.test.ts + 9:3 warning 'TIMEOUT_MESSAGES' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/utils/typeGuards.test.ts + 13:3 warning 'isSession' is defined but never used @typescript-eslint/no-unused-vars + 14:3 warning 'isAuditLog' is defined but never used @typescript-eslint/no-unused-vars + 20:3 warning 'isTrackArray' is defined but never used @typescript-eslint/no-unused-vars + 21:3 warning 'isPlaylistArray' is defined but never used @typescript-eslint/no-unused-vars + 22:3 warning 'isConversationArray' is defined but never used @typescript-eslint/no-unused-vars + 23:3 warning 'isMessageArray' is defined but never used @typescript-eslint/no-unused-vars + 24:3 warning 'isNotificationArray' is defined but never used @typescript-eslint/no-unused-vars + +✖ 451 problems (55 errors, 396 warnings) + 0 errors and 2 warnings potentially fixable with the `--fix` option. + +npm error Lifecycle script `lint` failed with error: +npm error code 1 +npm error path /home/senke/git/talas/veza/apps/web +npm error workspace veza-frontend@1.0.0 +npm error location /home/senke/git/talas/veza/apps/web +npm error command failed +npm error command sh -c eslint . --ext ts,tsx diff --git a/apps/web/lint_results_final.txt b/apps/web/lint_results_final.txt new file mode 100644 index 000000000..9f0c60455 --- /dev/null +++ b/apps/web/lint_results_final.txt @@ -0,0 +1,805 @@ + +> veza-frontend@1.0.0 lint +> eslint . --ext ts,tsx + + +/home/senke/git/talas/veza/apps/web/e2e/error-boundary.spec.ts + 257:13 warning 'foundMessage' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/e2e/global-setup.ts + 55:11 warning Unused eslint-disable directive (no problems were reported from 'no-undef') + 83:9 warning Unused eslint-disable directive (no problems were reported from 'no-undef') + +/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts + 1:29 warning 'Page' is defined but never used @typescript-eslint/no-unused-vars + 1:40 warning 'APIRequestContext' is defined but never used @typescript-eslint/no-unused-vars + 4:3 warning 'TEST_USERS' is defined but never used @typescript-eslint/no-unused-vars + 5:3 warning 'loginAsUser' is defined but never used @typescript-eslint/no-unused-vars + 6:3 warning 'forceSubmitForm' is defined but never used @typescript-eslint/no-unused-vars + 7:3 warning 'fillField' is defined but never used @typescript-eslint/no-unused-vars + 8:3 warning 'waitForToast' is defined but never used @typescript-eslint/no-unused-vars + 9:3 warning 'setupErrorCapture' is defined but never used @typescript-eslint/no-unused-vars + 10:3 warning 'getAuthToken' is defined but never used @typescript-eslint/no-unused-vars + 11:3 warning 'navigateViaHref' is defined but never used @typescript-eslint/no-unused-vars + 12:3 warning 'waitForListLoaded' is defined but never used @typescript-eslint/no-unused-vars + 13:3 warning 'openModal' is defined but never used @typescript-eslint/no-unused-vars + 14:3 warning 'closeModal' is defined but never used @typescript-eslint/no-unused-vars + 45:9 warning 'userId' is assigned a value but never used @typescript-eslint/no-unused-vars + 46:9 warning 'trackId' is assigned a value but never used @typescript-eslint/no-unused-vars + 47:9 warning 'playlistId' is assigned a value but never used @typescript-eslint/no-unused-vars + 49:7 warning 'refreshToken' is assigned a value but never used @typescript-eslint/no-unused-vars + 92:15 warning 'loginLinkVisible' is assigned a value but never used @typescript-eslint/no-unused-vars + 267:26 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + 303:26 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + 452:13 warning 'hasContent' is assigned a value but never used @typescript-eslint/no-unused-vars + 459:13 warning 'uploadButton' is assigned a value but never used @typescript-eslint/no-unused-vars + 514:13 warning 'hasProfile' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/e2e/navigation.spec.ts + 1:29 warning 'Page' is defined but never used @typescript-eslint/no-unused-vars + 6:3 warning 'navigateViaHref' is defined but never used @typescript-eslint/no-unused-vars + 22:7 warning 'consoleErrors' is assigned a value but never used @typescript-eslint/no-unused-vars + 23:7 warning 'networkErrors' is assigned a value but never used @typescript-eslint/no-unused-vars + 98:13 warning 'isActive' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/e2e/performance.spec.ts + 43:11 warning 'measure' is assigned a value but never used @typescript-eslint/no-unused-vars + 77:16 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/e2e/playlists.spec.ts + 1:29 warning 'Page' is defined but never used @typescript-eslint/no-unused-vars + 7:3 warning 'closeModal' is defined but never used @typescript-eslint/no-unused-vars + 9:3 warning 'safeClick' is defined but never used @typescript-eslint/no-unused-vars + 574:36 warning 'testInfo' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/e2e/profile.spec.ts + 1:29 warning 'Page' is defined but never used @typescript-eslint/no-unused-vars + 5:3 warning 'forceSubmitForm' is defined but never used @typescript-eslint/no-unused-vars + 6:3 warning 'fillField' is defined but never used @typescript-eslint/no-unused-vars + 7:3 warning 'safeClick' is defined but never used @typescript-eslint/no-unused-vars + 8:3 warning 'navigateViaSidebar' is defined but never used @typescript-eslint/no-unused-vars + 185:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 220:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 342:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 563:36 warning 'testInfo' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/e2e/qa-audit.spec.ts + 17:16 warning 'captureConsoleErrors' is defined but never used @typescript-eslint/no-unused-vars + 28:16 warning 'captureNetworkErrors' is defined but never used @typescript-eslint/no-unused-vars + 287:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/e2e/track_lifecycle.spec.ts + 1:29 warning 'Page' is defined but never used @typescript-eslint/no-unused-vars + 100:18 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + 118:22 warning 'modalError' is defined but never used @typescript-eslint/no-unused-vars + 233:22 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 265:38 warning 'testInfo' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/e2e/tracks_upload_chunked.spec.ts + 1:29 warning 'Page' is defined but never used @typescript-eslint/no-unused-vars + 9:3 warning 'waitForToast' is defined but never used @typescript-eslint/no-unused-vars + 173:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 206:16 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 226:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 242:16 warning 'modalError' is defined but never used @typescript-eslint/no-unused-vars + 308:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 381:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 449:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 461:36 warning 'testInfo' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/e2e/upload_flow.spec.ts + 1:29 warning 'Page' is defined but never used @typescript-eslint/no-unused-vars + 9:3 warning 'navigateViaHref' is defined but never used @typescript-eslint/no-unused-vars + 129:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 142:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 230:36 warning 'testInfo' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/e2e/utils/test-helpers.ts + 37:9 warning 'storageData' is assigned a value but never used @typescript-eslint/no-unused-vars + 93:14 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + 110:14 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + 217:12 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + 431:16 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + 452:14 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + 588:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 874:12 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/e2e/visual-regression.spec.ts + 2:10 warning 'loginAsUser' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/admin/AdminModerationView.tsx + 47:16 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/admin/AdminUsersView.tsx + 38:6 warning React Hook useEffect has a missing dependency: 'addToast'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/components/dashboard/TrackList.tsx + 51:14 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/data/Table.tsx + 107:5 warning React Hook useCallback has a missing dependency: 'paginatedDataMemo'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/components/developer/DeveloperDashboardView.tsx + 55:16 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/education/CourseDetailView.tsx + 17:21 warning '_addToast' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/feedback/ToastProvider.tsx + 19:17 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + +/home/senke/git/talas/veza/apps/web/src/components/gamification/ProfileXPView.tsx + 42:6 warning React Hook useEffect has a missing dependency: 'username'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/components/inventory/EquipmentDetailView.tsx + 44:6 warning React Hook useEffect has a missing dependency: 'addToast'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/components/library/AutoMetadataDetectionModal.tsx + 21:21 warning '_addToast' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/library/WatermarkSettingsModal.tsx + 14:21 warning '_addToast' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/library/playlists/PlaylistsView.tsx + 49:18 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/navigation/Breadcrumbs.tsx + 51:23 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/components/player/AudioPlayer.tsx + 188:6 warning React Hook useEffect has a missing dependency: 'handlePlayPause'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/components/player/LyricsPanel.tsx + 15:54 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 15:103 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 49:69 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 49:118 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/components/player/PlayerControls.tsx + 16:59 warning '_playbackRate' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/seller/CreateProductView.tsx + 17:10 warning '_step' is assigned a value but never used @typescript-eslint/no-unused-vars + 17:17 warning '_setStep' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/settings/profile/EditProfile.tsx + 103:6 warning React Hook useEffect has a missing dependency: 'addToast'. Either include it or remove the dependency array react-hooks/exhaustive-deps + 111:14 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + 137:16 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/settings/security/PasskeyModal.tsx + 15:10 warning '_loading' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/settings/security/SessionManagement.tsx + 39:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 50:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/share/ShareLinkManager.tsx + 128:14 warning 'err' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/social/FeedView.tsx + 44:16 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/social/groups/GroupDetailView.tsx + 50:6 warning React Hook useEffect has a missing dependency: 'addToast'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/components/social/groups/GroupsView.tsx + 27:6 warning React Hook useEffect has a missing dependency: 'loadGroups'. Either include it or remove the dependency array react-hooks/exhaustive-deps + 50:16 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + 60:16 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + 70:16 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/studio/CloudFileBrowser.tsx + 29:12 warning '_currentFolder' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/studio/ProjectsManager.tsx + 56:16 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + 68:16 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + 80:16 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/studio/projects/ProjectDetailView.tsx + 24:12 warning '_editMode' is assigned a value but never used @typescript-eslint/no-unused-vars + 28:26 warning '_setProjectFiles' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/ui/LazyComponent.test.tsx + 2:32 warning 'vi' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/ui/LazyComponent.tsx + 162:17 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + 178:23 warning '_fallback' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/ui/Toast.test.tsx + 1:37 warning 'waitFor' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/ui/avatar-upload.tsx + 231:5 warning React Hook useCallback has a missing dependency: 'userId'. Either include it or remove the dependency array react-hooks/exhaustive-deps + 298:6 warning React Hook useCallback has a missing dependency: 'userId'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/components/ui/date-picker.test.tsx + 166:11 warning 'nextButton' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/ui/date-picker.tsx + 137:10 warning '_open' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/ui/dropdown-menu.tsx + 68:10 warning '_internalOpen' is assigned a value but never used @typescript-eslint/no-unused-vars + 132:17 warning 'align' is assigned a value but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/ui/file-upload.test.tsx + 142:25 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 150:20 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/components/ui/file-upload.tsx + 275:5 warning React Hook useCallback has a missing dependency: 'validateFile'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/components/ui/optimized-image.tsx + 366:17 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + +/home/senke/git/talas/veza/apps/web/src/components/ui/radio-group.test.tsx + 73:21 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/components/ui/virtualized-list.tsx + 237:17 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + 264:17 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + +/home/senke/git/talas/veza/apps/web/src/components/upload/FileUploadZone.tsx + 44:6 warning React Hook useCallback has a missing dependency: 'validateFile'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/components/upload/metadata/CoverArtUploadModal.tsx + 81:18 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/upload/metadata/MetadataEditor.tsx + 44:6 warning React Hook useEffect has a missing dependency: 'allMetadata'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/components/views/AuthView.tsx + 18:10 warning '_pendingCredentials' is assigned a value but never used @typescript-eslint/no-unused-vars + 18:31 warning '_setPendingCredentials' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/views/MarketplaceView.tsx + 31:8 warning React Hook useEffect has a missing dependency: 'loadProducts'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/components/views/ProfileView.tsx + 141:8 warning React Hook useEffect has a missing dependency: 'addToast'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/components/views/SettingsView.tsx + 24:23 warning '_addToast' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/views/SocialView.tsx + 17:23 warning '_addToast' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/views/UploadView.tsx + 65:18 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/context/AudioContext.tsx + 51:14 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + 74:19 warning '_setIsMuted' is assigned a value but never used @typescript-eslint/no-unused-vars + 116:6 warning React Hook useEffect has a missing dependency: 'nextTrack'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/context/AuthContext.test.tsx + 1:22 warning 'waitFor' is defined but never used @typescript-eslint/no-unused-vars + 6:10 warning 'ToastProvider' is defined but never used @typescript-eslint/no-unused-vars + 32:5 error 'mockGetCurrentUser' is not defined no-undef + +/home/senke/git/talas/veza/apps/web/src/context/AuthContext.tsx + 20:14 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + +/home/senke/git/talas/veza/apps/web/src/context/CartContext.test.tsx + 6:10 warning 'ToastProvider' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/context/CartContext.tsx + 17:14 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + +/home/senke/git/talas/veza/apps/web/src/context/ThemeContext.tsx + 12:14 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + +/home/senke/git/talas/veza/apps/web/src/context/ToastContext.tsx + 10:14 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + +/home/senke/git/talas/veza/apps/web/src/features/auth/components/TwoFactorVerify.test.tsx + 252:11 warning 'user' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/auth/hooks/useUsernameAvailability.ts + 19:16 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/auth/pages/LoginPage.test.tsx + 99:11 warning 'user' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/auth/pages/LoginPage.tsx + 46:16 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/auth/pages/OAuthCallbackPage.test.tsx + 2:26 warning 'waitFor' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/auth/services/authService.test.ts + 2:8 warning 'axios' is defined but never used @typescript-eslint/no-unused-vars + 13:8 warning 'ApiError' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/auth/services/emailVerificationService.test.ts + 2:8 warning 'axios' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/chat/components/ChatInterface.tsx + 97:6 warning React Hook useEffect has missing dependencies: 'loadMessages', 'showError', and 'showSuccess'. Either include them or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/chat/components/ChatMessages.tsx + 21:9 warning The 'conversationMessages' conditional could make the dependencies of useEffect Hook (at line 31) change on every render. To fix this, wrap the initialization of 'conversationMessages' in its own useMemo() Hook react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/chat/components/ChatRoom.tsx + 23:9 warning The 'currentMessages' logical expression could make the dependencies of useEffect Hook (at line 41) change on every render. To fix this, wrap the initialization of 'currentMessages' in its own useMemo() Hook react-hooks/exhaustive-deps + 37:6 warning React Hook useEffect has a missing dependency: 'messages'. Either include it or remove the dependency array react-hooks/exhaustive-deps + 37:23 warning React Hook useEffect has a complex expression in the dependency array. Extract it to a separate variable so it can be statically checked react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/chat/components/VirtualizedChatMessages.tsx + 117:6 warning React Hook useEffect has a missing dependency: 'messages'. Either include it or remove the dependency array react-hooks/exhaustive-deps + 204:17 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + 276:6 warning React Hook useEffect has a missing dependency: 'fetchMessages'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/chat/hooks/useChat.ts + 36:10 warning '_messagesToSend' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/chat/pages/ChatPage.tsx + 18:23 warning '_disconnect' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/chat/store/chatStore.ts + 110:17 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 110:41 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 111:21 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 111:64 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 128:17 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 128:45 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 129:21 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 129:68 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/library/components/LibraryManager.tsx + 82:6 warning React Hook useEffect has a missing dependency: 'fetchTracks'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/library/pages/LibraryPage.tsx + 132:6 warning React Hook useEffect has a missing dependency: 'page'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/player/components/GlobalPlayer.tsx + 5:33 warning 'DialogTrigger' is defined but never used @typescript-eslint/no-unused-vars + 6:10 warning 'cn' is defined but never used @typescript-eslint/no-unused-vars + 7:10 warning 'Maximize2' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/player/components/MiniPlayer.test.tsx + 75:5 warning 'volume' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/player/components/PlaybackSpeedControl.test.tsx + 162:13 warning 'container' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/player/components/PlayerError.test.tsx + 158:11 warning 'retryButton' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/player/components/ProgressBar.test.tsx + 3:8 warning 'userEvent' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/player/components/QualitySelector.test.tsx + 160:13 warning 'container' is assigned a value but never used @typescript-eslint/no-unused-vars + 180:13 warning 'container' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/player/components/VolumeControl.test.tsx + 172:11 warning 'user' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/player/hooks/useKeyboardShortcuts.ts + 108:5 warning React Hook useCallback has a missing dependency: 'player'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/player/store/playerStore.test.ts + 179:15 warning 'initialIndex' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/__tests__/collaboration.integration.test.tsx + 556:13 warning 'updatedCollaborator' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/AddTrackToPlaylistModal.test.tsx + 78:9 warning 'mockToast' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/CollaboratorManagement.test.tsx + 211:11 warning 'addModal' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/ImportPlaylistButton.tsx + 63:20 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/PlaylistActions.test.tsx + 7:26 warning 'waitFor' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/PlaylistAnalytics.tsx + 56:6 warning React Hook useEffect has a missing dependency: 'loadAnalytics'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/PlaylistBatchActions.tsx + 178:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 190:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/PlaylistErrorBoundary.tsx + 100:10 warning Fast refresh only works when a file only exports components. Move your component(s) to a separate file react-refresh/only-export-components + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/PlaylistFollowButton.test.tsx + 119:13 warning 'container' is assigned a value but never used @typescript-eslint/no-unused-vars + 142:13 warning 'container' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/PlaylistHeader.test.tsx + 6:36 warning 'beforeEach' is defined but never used @typescript-eslint/no-unused-vars + 13:28 warning 'playlistId' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/PlaylistRecommendations.tsx + 67:6 warning React Hook useEffect has a missing dependency: 'toastError'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/PlaylistSearch.tsx + 81:6 warning React Hook useEffect has a missing dependency: 'filters'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/PlaylistTrackList.test.tsx + 8:26 warning 'waitFor' is defined but never used @typescript-eslint/no-unused-vars + 9:8 warning 'userEvent' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/PlaylistTrackList.tsx + 204:14 warning 'err' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/RemoveTrackButton.test.tsx + 219:5 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/SharePlaylistModal.test.tsx + 91:9 warning 'mockToast' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/SharePlaylistModal.tsx + 34:6 warning React Hook useEffect has missing dependencies: 'createShareLinkMutation.isPending', 'handleCreateShare', and 'shareLink'. Either include them or remove the dependency array react-hooks/exhaustive-deps + 58:14 warning 'err' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/hooks/usePlaylistNotifications.ts + 99:9 warning The 'playlistNotifications' logical expression could make the dependencies of useEffect Hook (at line 140) change on every render. To fix this, wrap the initialization of 'playlistNotifications' in its own useMemo() Hook react-hooks/exhaustive-deps + 203:40 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 219:40 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 235:40 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 251:40 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/playlists/pages/PlaylistDetailPage.test.tsx + 55:7 warning 'mockOnClose' is assigned a value but never used @typescript-eslint/no-unused-vars + 56:7 warning 'mockOnTrackAdded' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/services/playlistService.test.ts + 12:3 warning 'addTrack' is defined but never used @typescript-eslint/no-unused-vars + 13:3 warning 'removeTrack' is defined but never used @typescript-eslint/no-unused-vars + 24:15 warning 'CreatePlaylistRequest' is defined but never used @typescript-eslint/no-unused-vars + 24:38 warning 'UpdatePlaylistRequest' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/profile/components/AvatarUpload.test.tsx + 160:23 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/profile/pages/UserProfilePage.test.tsx + 3:10 warning 'BrowserRouter' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/profile/pages/UserProfilePage.tsx + 57:49 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/settings/components/PreferenceSettings.test.tsx + 1:37 warning 'waitFor' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/streaming/components/BitrateSelector.test.tsx + 88:13 warning 'container' is assigned a value but never used @typescript-eslint/no-unused-vars + 164:13 warning 'container' is assigned a value but never used @typescript-eslint/no-unused-vars + 223:11 warning 'dropdown' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/streaming/components/HLSPlayer.test.tsx + 2:37 warning 'waitFor' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/streaming/components/PlaybackDashboard.tsx + 37:6 warning React Hook useEffect has a missing dependency: 'loadDashboard'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/streaming/components/PlaybackHeatmap.tsx + 45:6 warning React Hook useEffect has a missing dependency: 'loadHeatmap'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/streaming/components/PlaybackSummary.tsx + 34:6 warning React Hook useEffect has a missing dependency: 'loadSummary'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/streaming/hooks/useBitrateAdaptation.test.ts + 97:7 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 220:7 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/streaming/hooks/useHLSStream.test.ts + 187:5 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/streaming/hooks/usePlaybackRealtime.test.ts + 30:8 warning 'data' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars + 34:24 warning 'reason' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/streaming/hooks/usePlaybackRealtime.ts + 258:6 warning React Hook useCallback has a missing dependency: 'connect'. Either include it or remove the dependency array react-hooks/exhaustive-deps + 416:6 warning React Hook useCallback has missing dependencies: 'onAnalyticsUpdate' and 'onStatsUpdate'. Either include them or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/streaming/services/playbackAnalyticsService.ts + 288:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/__tests__/trackUpload.integration.test.tsx + 7:10 warning 'render' is defined but never used @typescript-eslint/no-unused-vars + 7:18 warning 'screen' is defined but never used @typescript-eslint/no-unused-vars + 7:26 warning 'waitFor' is defined but never used @typescript-eslint/no-unused-vars + 8:8 warning 'userEvent' is defined but never used @typescript-eslint/no-unused-vars + 61:7 warning 'TestWrapper' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/CommentItem.test.tsx + 8:3 warning 'getReplies' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/CommentSection.test.tsx + 2:35 warning 'fireEvent' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/CommentThread.test.tsx + 111:11 warning 'avatar' is assigned a value but never used @typescript-eslint/no-unused-vars + 229:11 warning 'hasMoreButton' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/PlaysChart.test.tsx + 4:13 warning 'trackAnalytics' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/ShareDialog.test.tsx + 7:35 warning 'fireEvent' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/ShareDialog.tsx + 34:6 warning React Hook useEffect has missing dependencies: 'handleCreateShare' and 'share'. Either include them or remove the dependency array react-hooks/exhaustive-deps + 65:14 warning 'err' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackDelete.test.tsx + 2:35 warning 'fireEvent' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackDownloadButton.test.tsx + 91:5 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 99:11 warning 'downloadPromise' is assigned a value but never used @typescript-eslint/no-unused-vars + 119:9 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackEdit.test.tsx + 2:35 warning 'fireEvent' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackGrid.test.tsx + 59:24 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackGridDensitySelector.test.tsx + 1:48 warning 'afterEach' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackHistory.tsx + 50:6 warning React Hook useEffect has a missing dependency: 'loadHistory'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackList.test.tsx + 62:22 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 74:22 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 218:22 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 236:22 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 288:22 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackListContainer.test.tsx + 2:26 warning 'waitFor' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackListRow.test.tsx + 169:13 warning 'container' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackSearch.test.tsx + 2:35 warning 'fireEvent' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackSearchFilters.test.tsx + 2:26 warning 'fireEvent' is defined but never used @typescript-eslint/no-unused-vars + 5:15 warning 'TrackSearchParams' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackShareDialog.test.tsx + 2:35 warning 'fireEvent' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackStats.test.tsx + 4:13 warning 'trackAnalytics' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackUpload.test.tsx + 49:11 warning 'user' is assigned a value but never used @typescript-eslint/no-unused-vars + 72:11 warning 'user' is assigned a value but never used @typescript-eslint/no-unused-vars + 208:9 warning 'progressCallback' is assigned a value but never used @typescript-eslint/no-unused-vars + 342:9 warning 'progressCallback' is assigned a value but never used @typescript-eslint/no-unused-vars + 470:24 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 476:20 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/UploadQuota.test.tsx + 6:10 warning 'useAuthStore' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/UploadQuota.tsx + 43:6 warning React Hook useEffect has a missing dependency: 'loadQuota'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/tracks/hooks/useInfiniteScroll.test.ts + 8:5 warning 'observerOptions' is assigned a value but never used @typescript-eslint/no-unused-vars + 99:9 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 145:9 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 293:9 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 342:9 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 358:9 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/tracks/hooks/useTrackList.test.ts + 308:9 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/tracks/hooks/useTrackList.ts + 77:54 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 79:26 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 80:54 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 82:33 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 97:18 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 143:6 warning React Hook useEffect has missing dependencies: 'searchParams' and 'setSearchParams'. Either include them or remove the dependency array react-hooks/exhaustive-deps + 158:6 warning React Hook useEffect has missing dependencies: 'searchParams' and 'setSearchParams'. Either include them or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/tracks/pages/TrackDetailPage.test.tsx + 2:35 warning 'fireEvent' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/services/analyticsService.test.ts + 15:10 warning 'TrackServiceError' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/services/chunkedUploadService.test.ts + 6:3 warning 'CHUNK_SIZE' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/services/chunkedUploadService.ts + 142:15 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 193:9 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/tracks/services/trackDownloadService.test.ts + 3:10 warning 'apiClient' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/services/trackHistoryService.test.ts + 5:3 warning 'TrackHistory' is defined but never used @typescript-eslint/no-unused-vars + 213:28 error 'TrackHistoryAction' is not defined no-undef + +/home/senke/git/talas/veza/apps/web/src/features/tracks/services/trackListService.test.ts + 2:8 warning 'axios' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/upload/components/UploadModal.tsx + 214:5 warning React Hook useCallback has a missing dependency: 'handleClose'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/hooks/useIntersectionObserver.test.ts + 3:10 warning 'useRef' is defined but never used @typescript-eslint/no-unused-vars + 19:7 warning 'mockObserver' is assigned a value but never used @typescript-eslint/no-unused-vars + 39:12 error 'mockObserve' is not defined no-undef + 49:12 error 'mockObserve' is not defined no-undef + 62:12 error 'mockDisconnect' is not defined no-undef + 104:13 warning 'result' is assigned a value but never used @typescript-eslint/no-unused-vars + 131:12 error 'mockObserve' is not defined no-undef + +/home/senke/git/talas/veza/apps/web/src/hooks/useKeyboardNavigation.test.ts + 7:9 warning 'mockOnEnter' is assigned a value but never used @typescript-eslint/no-unused-vars + 10:9 warning 'mockOnArrowLeft' is assigned a value but never used @typescript-eslint/no-unused-vars + 11:9 warning 'mockOnArrowRight' is assigned a value but never used @typescript-eslint/no-unused-vars + 12:9 warning 'mockOnTab' is assigned a value but never used @typescript-eslint/no-unused-vars + 13:9 warning 'mockOnShiftTab' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/hooks/useLocalStorage.test.ts + 2:48 warning 'afterEach' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/main.tsx + 57:23 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/mocks/handlers.ts + 222:17 warning '_' is assigned a value but never used @typescript-eslint/no-unused-vars + 251:19 warning '_' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/mocks/test-helpers.ts + 1:10 warning 'contextBridge' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/pages/AdminDashboardPage.tsx + 84:6 warning React Hook useEffect has a missing dependency: 'loadDashboardData'. Either include it or remove the dependency array react-hooks/exhaustive-deps + 212:6 warning React Hook useEffect has a missing dependency: 'loadUsers'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/pages/AnalyticsPage.tsx + 39:6 warning React Hook useEffect has a missing dependency: 'loadAnalytics'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/pages/ProfilePage.test.tsx + 1:18 warning 'screen' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/pages/WebhooksPage.tsx + 58:6 warning React Hook useEffect has a missing dependency: 'loadWebhooks'. Either include it or remove the dependency array react-hooks/exhaustive-deps + 371:45 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/pages/auth/Register.test.tsx + 7:10 warning 'useToast' is defined but never used @typescript-eslint/no-unused-vars + 145:11 warning 'mockResponse' is assigned a value but never used @typescript-eslint/no-unused-vars + 220:5 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/schemas/apiSchemas.test.ts + 12:3 warning 'messageSchema' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/services/api/auth.ts + 252:13 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/services/api/client.test.ts + 2:8 warning 'axios' is defined but never used @typescript-eslint/no-unused-vars + 3:10 warning 'apiClient' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/services/api/client.ts + 11:28 warning '_isTimeoutError' is defined but never used @typescript-eslint/no-unused-vars + 11:66 warning '_getTimeoutMessage' is defined but never used @typescript-eslint/no-unused-vars + 324:18 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/services/pwa.ts + 64:29 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 237:9 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 258:9 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 258:9 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/services/requestDeduplication.ts + 52:50 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/services/responseCache.ts + 70:50 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/services/socialService.ts + 3:16 warning 'Notification' is defined but never used @typescript-eslint/no-unused-vars + 3:30 warning 'Comment' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/stores/auth.test.ts + 101:16 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 129:7 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 181:16 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/test/helpers.tsx + 20:10 warning Fast refresh only works when a file only exports components. Move your component(s) to a separate file react-refresh/only-export-components + 35:1 warning This rule can't verify that `export *` only exports components react-refresh/only-export-components + +/home/senke/git/talas/veza/apps/web/src/test/test-utils.tsx + 31:7 warning Fast refresh only works when a file only exports components. Move your component(s) to a separate file react-refresh/only-export-components + 63:1 warning This rule can't verify that `export *` only exports components react-refresh/only-export-components + +/home/senke/git/talas/veza/apps/web/src/types/backend-types.ts + 2:10 warning 'PostType' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/utils/apiErrorHandler.test.ts + 6:36 warning 'beforeEach' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/utils/logger.test.ts + 10:7 warning 'consoleDebugSpy' is assigned a value but never used @typescript-eslint/no-unused-vars + 11:7 warning 'consoleInfoSpy' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/utils/logger.ts + 135:12 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/utils/optimisticUpdates.ts + 360:15 warning '_getValue' is assigned a value but never used @typescript-eslint/no-unused-vars + 361:15 warning '_getCount' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/utils/sanitize.test.ts + 6:36 warning 'beforeEach' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/utils/sanitize.ts + 134:45 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 138:45 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 141:39 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 282:99 error Unnecessary escape character: \- no-useless-escape + +/home/senke/git/talas/veza/apps/web/src/utils/stateCleanup.test.ts + 213:12 warning 'set' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars + 236:12 warning 'set' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/utils/stateHydration.ts + 237:6 warning React Hook useEffect has a missing dependency: 'config'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/utils/stateNormalization.ts + 124:21 warning 'removed' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/utils/statePersistence.ts + 140:24 warning Do not access Object.prototype method 'hasOwnProperty' from target object no-prototype-builtins + +/home/senke/git/talas/veza/apps/web/src/utils/stateVersioning.example.ts + 93:6 warning 'set' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/utils/stateVersioning.test.ts + 6:44 warning 'vi' is defined but never used @typescript-eslint/no-unused-vars + 262:33 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 296:33 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 358:33 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/utils/stateVersioning.ts + 206:50 warning '_migrations' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/utils/timeoutHandler.test.ts + 9:3 warning 'TIMEOUT_MESSAGES' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/utils/typeGuards.test.ts + 13:3 warning 'isSession' is defined but never used @typescript-eslint/no-unused-vars + 14:3 warning 'isAuditLog' is defined but never used @typescript-eslint/no-unused-vars + 20:3 warning 'isTrackArray' is defined but never used @typescript-eslint/no-unused-vars + 21:3 warning 'isPlaylistArray' is defined but never used @typescript-eslint/no-unused-vars + 22:3 warning 'isConversationArray' is defined but never used @typescript-eslint/no-unused-vars + 23:3 warning 'isMessageArray' is defined but never used @typescript-eslint/no-unused-vars + 24:3 warning 'isNotificationArray' is defined but never used @typescript-eslint/no-unused-vars + +✖ 392 problems (7 errors, 385 warnings) + 0 errors and 2 warnings potentially fixable with the `--fix` option. + +npm error Lifecycle script `lint` failed with error: +npm error code 1 +npm error path /home/senke/git/talas/veza/apps/web +npm error workspace veza-frontend@1.0.0 +npm error location /home/senke/git/talas/veza/apps/web +npm error command failed +npm error command sh -c eslint . --ext ts,tsx diff --git a/apps/web/lint_results_final_v2.txt b/apps/web/lint_results_final_v2.txt new file mode 100644 index 000000000..770dce406 --- /dev/null +++ b/apps/web/lint_results_final_v2.txt @@ -0,0 +1,790 @@ + +> veza-frontend@1.0.0 lint +> eslint . --ext ts,tsx + + +/home/senke/git/talas/veza/apps/web/e2e/error-boundary.spec.ts + 257:13 warning 'foundMessage' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/e2e/global-setup.ts + 55:11 warning Unused eslint-disable directive (no problems were reported from 'no-undef') + 83:9 warning Unused eslint-disable directive (no problems were reported from 'no-undef') + +/home/senke/git/talas/veza/apps/web/e2e/mvp-integration.spec.ts + 1:29 warning 'Page' is defined but never used @typescript-eslint/no-unused-vars + 1:40 warning 'APIRequestContext' is defined but never used @typescript-eslint/no-unused-vars + 4:3 warning 'TEST_USERS' is defined but never used @typescript-eslint/no-unused-vars + 5:3 warning 'loginAsUser' is defined but never used @typescript-eslint/no-unused-vars + 6:3 warning 'forceSubmitForm' is defined but never used @typescript-eslint/no-unused-vars + 7:3 warning 'fillField' is defined but never used @typescript-eslint/no-unused-vars + 8:3 warning 'waitForToast' is defined but never used @typescript-eslint/no-unused-vars + 9:3 warning 'setupErrorCapture' is defined but never used @typescript-eslint/no-unused-vars + 10:3 warning 'getAuthToken' is defined but never used @typescript-eslint/no-unused-vars + 11:3 warning 'navigateViaHref' is defined but never used @typescript-eslint/no-unused-vars + 12:3 warning 'waitForListLoaded' is defined but never used @typescript-eslint/no-unused-vars + 13:3 warning 'openModal' is defined but never used @typescript-eslint/no-unused-vars + 14:3 warning 'closeModal' is defined but never used @typescript-eslint/no-unused-vars + 45:9 warning 'userId' is assigned a value but never used @typescript-eslint/no-unused-vars + 46:9 warning 'trackId' is assigned a value but never used @typescript-eslint/no-unused-vars + 47:9 warning 'playlistId' is assigned a value but never used @typescript-eslint/no-unused-vars + 49:7 warning 'refreshToken' is assigned a value but never used @typescript-eslint/no-unused-vars + 92:15 warning 'loginLinkVisible' is assigned a value but never used @typescript-eslint/no-unused-vars + 267:26 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + 303:26 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + 452:13 warning 'hasContent' is assigned a value but never used @typescript-eslint/no-unused-vars + 459:13 warning 'uploadButton' is assigned a value but never used @typescript-eslint/no-unused-vars + 514:13 warning 'hasProfile' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/e2e/navigation.spec.ts + 1:29 warning 'Page' is defined but never used @typescript-eslint/no-unused-vars + 6:3 warning 'navigateViaHref' is defined but never used @typescript-eslint/no-unused-vars + 22:7 warning 'consoleErrors' is assigned a value but never used @typescript-eslint/no-unused-vars + 23:7 warning 'networkErrors' is assigned a value but never used @typescript-eslint/no-unused-vars + 98:13 warning 'isActive' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/e2e/performance.spec.ts + 43:11 warning 'measure' is assigned a value but never used @typescript-eslint/no-unused-vars + 77:16 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/e2e/playlists.spec.ts + 1:29 warning 'Page' is defined but never used @typescript-eslint/no-unused-vars + 7:3 warning 'closeModal' is defined but never used @typescript-eslint/no-unused-vars + 9:3 warning 'safeClick' is defined but never used @typescript-eslint/no-unused-vars + 574:36 warning 'testInfo' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/e2e/profile.spec.ts + 1:29 warning 'Page' is defined but never used @typescript-eslint/no-unused-vars + 5:3 warning 'forceSubmitForm' is defined but never used @typescript-eslint/no-unused-vars + 6:3 warning 'fillField' is defined but never used @typescript-eslint/no-unused-vars + 7:3 warning 'safeClick' is defined but never used @typescript-eslint/no-unused-vars + 8:3 warning 'navigateViaSidebar' is defined but never used @typescript-eslint/no-unused-vars + 185:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 220:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 342:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 563:36 warning 'testInfo' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/e2e/qa-audit.spec.ts + 17:16 warning 'captureConsoleErrors' is defined but never used @typescript-eslint/no-unused-vars + 28:16 warning 'captureNetworkErrors' is defined but never used @typescript-eslint/no-unused-vars + 287:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/e2e/track_lifecycle.spec.ts + 1:29 warning 'Page' is defined but never used @typescript-eslint/no-unused-vars + 100:18 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + 118:22 warning 'modalError' is defined but never used @typescript-eslint/no-unused-vars + 233:22 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 265:38 warning 'testInfo' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/e2e/tracks_upload_chunked.spec.ts + 1:29 warning 'Page' is defined but never used @typescript-eslint/no-unused-vars + 9:3 warning 'waitForToast' is defined but never used @typescript-eslint/no-unused-vars + 173:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 206:16 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 226:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 242:16 warning 'modalError' is defined but never used @typescript-eslint/no-unused-vars + 308:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 381:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 449:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 461:36 warning 'testInfo' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/e2e/upload_flow.spec.ts + 1:29 warning 'Page' is defined but never used @typescript-eslint/no-unused-vars + 9:3 warning 'navigateViaHref' is defined but never used @typescript-eslint/no-unused-vars + 129:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 142:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 230:36 warning 'testInfo' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/e2e/utils/test-helpers.ts + 37:9 warning 'storageData' is assigned a value but never used @typescript-eslint/no-unused-vars + 93:14 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + 110:14 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + 217:12 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + 431:16 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + 452:14 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + 588:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 874:12 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/e2e/visual-regression.spec.ts + 2:10 warning 'loginAsUser' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/admin/AdminModerationView.tsx + 47:16 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/admin/AdminUsersView.tsx + 38:6 warning React Hook useEffect has a missing dependency: 'addToast'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/components/dashboard/TrackList.tsx + 51:14 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/data/Table.tsx + 107:5 warning React Hook useCallback has a missing dependency: 'paginatedDataMemo'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/components/developer/DeveloperDashboardView.tsx + 55:16 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/education/CourseDetailView.tsx + 17:21 warning '_addToast' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/feedback/ToastProvider.tsx + 19:17 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + +/home/senke/git/talas/veza/apps/web/src/components/gamification/ProfileXPView.tsx + 42:6 warning React Hook useEffect has a missing dependency: 'username'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/components/inventory/EquipmentDetailView.tsx + 44:6 warning React Hook useEffect has a missing dependency: 'addToast'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/components/library/AutoMetadataDetectionModal.tsx + 21:21 warning '_addToast' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/library/WatermarkSettingsModal.tsx + 14:21 warning '_addToast' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/library/playlists/PlaylistsView.tsx + 49:18 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/navigation/Breadcrumbs.tsx + 51:23 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/components/player/AudioPlayer.tsx + 188:6 warning React Hook useEffect has a missing dependency: 'handlePlayPause'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/components/player/LyricsPanel.tsx + 15:54 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 15:103 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 49:69 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 49:118 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/components/player/PlayerControls.tsx + 16:59 warning '_playbackRate' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/seller/CreateProductView.tsx + 17:10 warning '_step' is assigned a value but never used @typescript-eslint/no-unused-vars + 17:17 warning '_setStep' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/settings/profile/EditProfile.tsx + 103:6 warning React Hook useEffect has a missing dependency: 'addToast'. Either include it or remove the dependency array react-hooks/exhaustive-deps + 111:14 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + 137:16 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/settings/security/PasskeyModal.tsx + 15:10 warning '_loading' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/settings/security/SessionManagement.tsx + 39:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 50:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/share/ShareLinkManager.tsx + 128:14 warning 'err' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/social/FeedView.tsx + 44:16 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/social/groups/GroupDetailView.tsx + 50:6 warning React Hook useEffect has a missing dependency: 'addToast'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/components/social/groups/GroupsView.tsx + 27:6 warning React Hook useEffect has a missing dependency: 'loadGroups'. Either include it or remove the dependency array react-hooks/exhaustive-deps + 50:16 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + 60:16 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + 70:16 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/studio/CloudFileBrowser.tsx + 29:12 warning '_currentFolder' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/studio/ProjectsManager.tsx + 56:16 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + 68:16 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + 80:16 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/studio/projects/ProjectDetailView.tsx + 24:12 warning '_editMode' is assigned a value but never used @typescript-eslint/no-unused-vars + 28:26 warning '_setProjectFiles' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/ui/LazyComponent.test.tsx + 2:32 warning 'vi' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/ui/LazyComponent.tsx + 162:17 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + 178:23 warning '_fallback' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/ui/Toast.test.tsx + 1:37 warning 'waitFor' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/ui/avatar-upload.tsx + 231:5 warning React Hook useCallback has a missing dependency: 'userId'. Either include it or remove the dependency array react-hooks/exhaustive-deps + 298:6 warning React Hook useCallback has a missing dependency: 'userId'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/components/ui/date-picker.test.tsx + 166:11 warning 'nextButton' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/ui/date-picker.tsx + 137:10 warning '_open' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/ui/dropdown-menu.tsx + 68:10 warning '_internalOpen' is assigned a value but never used @typescript-eslint/no-unused-vars + 132:17 warning 'align' is assigned a value but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/ui/file-upload.test.tsx + 142:25 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 150:20 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/components/ui/file-upload.tsx + 275:5 warning React Hook useCallback has a missing dependency: 'validateFile'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/components/ui/optimized-image.tsx + 366:17 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + +/home/senke/git/talas/veza/apps/web/src/components/ui/radio-group.test.tsx + 73:21 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/components/ui/virtualized-list.tsx + 237:17 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + 264:17 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + +/home/senke/git/talas/veza/apps/web/src/components/upload/FileUploadZone.tsx + 44:6 warning React Hook useCallback has a missing dependency: 'validateFile'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/components/upload/metadata/CoverArtUploadModal.tsx + 81:18 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/upload/metadata/MetadataEditor.tsx + 44:6 warning React Hook useEffect has a missing dependency: 'allMetadata'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/components/views/AuthView.tsx + 18:10 warning '_pendingCredentials' is assigned a value but never used @typescript-eslint/no-unused-vars + 18:31 warning '_setPendingCredentials' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/views/MarketplaceView.tsx + 31:8 warning React Hook useEffect has a missing dependency: 'loadProducts'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/components/views/ProfileView.tsx + 141:8 warning React Hook useEffect has a missing dependency: 'addToast'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/components/views/SettingsView.tsx + 24:23 warning '_addToast' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/views/SocialView.tsx + 17:23 warning '_addToast' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/components/views/UploadView.tsx + 65:18 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/context/AudioContext.tsx + 51:14 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + 74:19 warning '_setIsMuted' is assigned a value but never used @typescript-eslint/no-unused-vars + 116:6 warning React Hook useEffect has a missing dependency: 'nextTrack'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/context/AuthContext.test.tsx + 1:22 warning 'waitFor' is defined but never used @typescript-eslint/no-unused-vars + 7:10 warning 'ToastProvider' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/context/AuthContext.tsx + 20:14 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + +/home/senke/git/talas/veza/apps/web/src/context/CartContext.test.tsx + 6:10 warning 'ToastProvider' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/context/CartContext.tsx + 17:14 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + +/home/senke/git/talas/veza/apps/web/src/context/ThemeContext.tsx + 12:14 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + +/home/senke/git/talas/veza/apps/web/src/context/ToastContext.tsx + 10:14 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + +/home/senke/git/talas/veza/apps/web/src/features/auth/components/TwoFactorVerify.test.tsx + 252:11 warning 'user' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/auth/hooks/useUsernameAvailability.ts + 19:16 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/auth/pages/LoginPage.test.tsx + 99:11 warning 'user' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/auth/pages/LoginPage.tsx + 46:16 warning 'e' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/auth/pages/OAuthCallbackPage.test.tsx + 2:26 warning 'waitFor' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/auth/services/authService.test.ts + 2:8 warning 'axios' is defined but never used @typescript-eslint/no-unused-vars + 13:8 warning 'ApiError' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/auth/services/emailVerificationService.test.ts + 2:8 warning 'axios' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/chat/components/ChatInterface.tsx + 97:6 warning React Hook useEffect has missing dependencies: 'loadMessages', 'showError', and 'showSuccess'. Either include them or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/chat/components/ChatMessages.tsx + 21:9 warning The 'conversationMessages' conditional could make the dependencies of useEffect Hook (at line 31) change on every render. To fix this, wrap the initialization of 'conversationMessages' in its own useMemo() Hook react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/chat/components/ChatRoom.tsx + 23:9 warning The 'currentMessages' logical expression could make the dependencies of useEffect Hook (at line 41) change on every render. To fix this, wrap the initialization of 'currentMessages' in its own useMemo() Hook react-hooks/exhaustive-deps + 37:6 warning React Hook useEffect has a missing dependency: 'messages'. Either include it or remove the dependency array react-hooks/exhaustive-deps + 37:23 warning React Hook useEffect has a complex expression in the dependency array. Extract it to a separate variable so it can be statically checked react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/chat/components/VirtualizedChatMessages.tsx + 117:6 warning React Hook useEffect has a missing dependency: 'messages'. Either include it or remove the dependency array react-hooks/exhaustive-deps + 204:17 warning Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components react-refresh/only-export-components + 276:6 warning React Hook useEffect has a missing dependency: 'fetchMessages'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/chat/hooks/useChat.ts + 36:10 warning '_messagesToSend' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/chat/pages/ChatPage.tsx + 18:23 warning '_disconnect' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/chat/store/chatStore.ts + 110:17 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 110:41 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 111:21 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 111:64 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 128:17 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 128:45 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 129:21 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 129:68 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/library/components/LibraryManager.tsx + 82:6 warning React Hook useEffect has a missing dependency: 'fetchTracks'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/library/pages/LibraryPage.tsx + 132:6 warning React Hook useEffect has a missing dependency: 'page'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/player/components/GlobalPlayer.tsx + 5:33 warning 'DialogTrigger' is defined but never used @typescript-eslint/no-unused-vars + 6:10 warning 'cn' is defined but never used @typescript-eslint/no-unused-vars + 7:10 warning 'Maximize2' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/player/components/MiniPlayer.test.tsx + 75:5 warning 'volume' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/player/components/PlaybackSpeedControl.test.tsx + 162:13 warning 'container' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/player/components/PlayerError.test.tsx + 158:11 warning 'retryButton' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/player/components/ProgressBar.test.tsx + 3:8 warning 'userEvent' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/player/components/QualitySelector.test.tsx + 160:13 warning 'container' is assigned a value but never used @typescript-eslint/no-unused-vars + 180:13 warning 'container' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/player/components/VolumeControl.test.tsx + 172:11 warning 'user' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/player/hooks/useKeyboardShortcuts.ts + 108:5 warning React Hook useCallback has a missing dependency: 'player'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/player/store/playerStore.test.ts + 179:15 warning 'initialIndex' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/__tests__/collaboration.integration.test.tsx + 556:13 warning 'updatedCollaborator' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/AddTrackToPlaylistModal.test.tsx + 78:9 warning 'mockToast' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/CollaboratorManagement.test.tsx + 211:11 warning 'addModal' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/ImportPlaylistButton.tsx + 63:20 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/PlaylistActions.test.tsx + 7:26 warning 'waitFor' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/PlaylistAnalytics.tsx + 56:6 warning React Hook useEffect has a missing dependency: 'loadAnalytics'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/PlaylistBatchActions.tsx + 178:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 190:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/PlaylistErrorBoundary.tsx + 100:10 warning Fast refresh only works when a file only exports components. Move your component(s) to a separate file react-refresh/only-export-components + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/PlaylistFollowButton.test.tsx + 119:13 warning 'container' is assigned a value but never used @typescript-eslint/no-unused-vars + 142:13 warning 'container' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/PlaylistHeader.test.tsx + 6:36 warning 'beforeEach' is defined but never used @typescript-eslint/no-unused-vars + 13:28 warning 'playlistId' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/PlaylistRecommendations.tsx + 67:6 warning React Hook useEffect has a missing dependency: 'toastError'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/PlaylistSearch.tsx + 81:6 warning React Hook useEffect has a missing dependency: 'filters'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/PlaylistTrackList.test.tsx + 8:26 warning 'waitFor' is defined but never used @typescript-eslint/no-unused-vars + 9:8 warning 'userEvent' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/PlaylistTrackList.tsx + 204:14 warning 'err' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/RemoveTrackButton.test.tsx + 219:5 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/SharePlaylistModal.test.tsx + 91:9 warning 'mockToast' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/components/SharePlaylistModal.tsx + 34:6 warning React Hook useEffect has missing dependencies: 'createShareLinkMutation.isPending', 'handleCreateShare', and 'shareLink'. Either include them or remove the dependency array react-hooks/exhaustive-deps + 58:14 warning 'err' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/hooks/usePlaylistNotifications.ts + 99:9 warning The 'playlistNotifications' logical expression could make the dependencies of useEffect Hook (at line 140) change on every render. To fix this, wrap the initialization of 'playlistNotifications' in its own useMemo() Hook react-hooks/exhaustive-deps + 203:40 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 219:40 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 235:40 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 251:40 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/playlists/pages/PlaylistDetailPage.test.tsx + 55:7 warning 'mockOnClose' is assigned a value but never used @typescript-eslint/no-unused-vars + 56:7 warning 'mockOnTrackAdded' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/playlists/services/playlistService.test.ts + 12:3 warning 'addTrack' is defined but never used @typescript-eslint/no-unused-vars + 13:3 warning 'removeTrack' is defined but never used @typescript-eslint/no-unused-vars + 24:15 warning 'CreatePlaylistRequest' is defined but never used @typescript-eslint/no-unused-vars + 24:38 warning 'UpdatePlaylistRequest' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/profile/components/AvatarUpload.test.tsx + 160:23 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/profile/pages/UserProfilePage.test.tsx + 3:10 warning 'BrowserRouter' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/profile/pages/UserProfilePage.tsx + 57:49 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/settings/components/PreferenceSettings.test.tsx + 1:37 warning 'waitFor' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/streaming/components/BitrateSelector.test.tsx + 88:13 warning 'container' is assigned a value but never used @typescript-eslint/no-unused-vars + 164:13 warning 'container' is assigned a value but never used @typescript-eslint/no-unused-vars + 223:11 warning 'dropdown' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/streaming/components/HLSPlayer.test.tsx + 2:37 warning 'waitFor' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/streaming/components/PlaybackDashboard.tsx + 37:6 warning React Hook useEffect has a missing dependency: 'loadDashboard'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/streaming/components/PlaybackHeatmap.tsx + 45:6 warning React Hook useEffect has a missing dependency: 'loadHeatmap'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/streaming/components/PlaybackSummary.tsx + 34:6 warning React Hook useEffect has a missing dependency: 'loadSummary'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/streaming/hooks/useBitrateAdaptation.test.ts + 97:7 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 220:7 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/streaming/hooks/useHLSStream.test.ts + 187:5 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/streaming/hooks/usePlaybackRealtime.test.ts + 30:8 warning 'data' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars + 34:24 warning 'reason' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/streaming/hooks/usePlaybackRealtime.ts + 258:6 warning React Hook useCallback has a missing dependency: 'connect'. Either include it or remove the dependency array react-hooks/exhaustive-deps + 416:6 warning React Hook useCallback has missing dependencies: 'onAnalyticsUpdate' and 'onStatsUpdate'. Either include them or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/streaming/services/playbackAnalyticsService.ts + 288:14 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/__tests__/trackUpload.integration.test.tsx + 7:10 warning 'render' is defined but never used @typescript-eslint/no-unused-vars + 7:18 warning 'screen' is defined but never used @typescript-eslint/no-unused-vars + 7:26 warning 'waitFor' is defined but never used @typescript-eslint/no-unused-vars + 8:8 warning 'userEvent' is defined but never used @typescript-eslint/no-unused-vars + 61:7 warning 'TestWrapper' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/CommentItem.test.tsx + 8:3 warning 'getReplies' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/CommentSection.test.tsx + 2:35 warning 'fireEvent' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/CommentThread.test.tsx + 111:11 warning 'avatar' is assigned a value but never used @typescript-eslint/no-unused-vars + 229:11 warning 'hasMoreButton' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/PlaysChart.test.tsx + 4:13 warning 'trackAnalytics' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/ShareDialog.test.tsx + 7:35 warning 'fireEvent' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/ShareDialog.tsx + 34:6 warning React Hook useEffect has missing dependencies: 'handleCreateShare' and 'share'. Either include them or remove the dependency array react-hooks/exhaustive-deps + 65:14 warning 'err' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackDelete.test.tsx + 2:35 warning 'fireEvent' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackDownloadButton.test.tsx + 91:5 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 99:11 warning 'downloadPromise' is assigned a value but never used @typescript-eslint/no-unused-vars + 119:9 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackEdit.test.tsx + 2:35 warning 'fireEvent' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackGrid.test.tsx + 59:24 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackGridDensitySelector.test.tsx + 1:48 warning 'afterEach' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackHistory.tsx + 50:6 warning React Hook useEffect has a missing dependency: 'loadHistory'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackList.test.tsx + 62:22 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 74:22 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 218:22 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 236:22 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 288:22 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackListContainer.test.tsx + 2:26 warning 'waitFor' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackListRow.test.tsx + 169:13 warning 'container' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackSearch.test.tsx + 2:35 warning 'fireEvent' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackSearchFilters.test.tsx + 2:26 warning 'fireEvent' is defined but never used @typescript-eslint/no-unused-vars + 5:15 warning 'TrackSearchParams' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackShareDialog.test.tsx + 2:35 warning 'fireEvent' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackStats.test.tsx + 4:13 warning 'trackAnalytics' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/TrackUpload.test.tsx + 49:11 warning 'user' is assigned a value but never used @typescript-eslint/no-unused-vars + 72:11 warning 'user' is assigned a value but never used @typescript-eslint/no-unused-vars + 208:9 warning 'progressCallback' is assigned a value but never used @typescript-eslint/no-unused-vars + 342:9 warning 'progressCallback' is assigned a value but never used @typescript-eslint/no-unused-vars + 470:24 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 476:20 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/UploadQuota.test.tsx + 6:10 warning 'useAuthStore' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/components/UploadQuota.tsx + 43:6 warning React Hook useEffect has a missing dependency: 'loadQuota'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/tracks/hooks/useInfiniteScroll.test.ts + 8:5 warning 'observerOptions' is assigned a value but never used @typescript-eslint/no-unused-vars + 99:9 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 145:9 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 293:9 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 342:9 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 358:9 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/tracks/hooks/useTrackList.test.ts + 308:9 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/tracks/hooks/useTrackList.ts + 77:54 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 79:26 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 80:54 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 82:33 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 97:18 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 143:6 warning React Hook useEffect has missing dependencies: 'searchParams' and 'setSearchParams'. Either include them or remove the dependency array react-hooks/exhaustive-deps + 158:6 warning React Hook useEffect has missing dependencies: 'searchParams' and 'setSearchParams'. Either include them or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/features/tracks/pages/TrackDetailPage.test.tsx + 2:35 warning 'fireEvent' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/services/analyticsService.test.ts + 15:10 warning 'TrackServiceError' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/services/chunkedUploadService.test.ts + 6:3 warning 'CHUNK_SIZE' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/services/chunkedUploadService.ts + 142:15 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 193:9 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/features/tracks/services/trackDownloadService.test.ts + 3:10 warning 'apiClient' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/services/trackHistoryService.test.ts + 5:3 warning 'TrackHistory' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/tracks/services/trackListService.test.ts + 2:8 warning 'axios' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/features/upload/components/UploadModal.tsx + 214:5 warning React Hook useCallback has a missing dependency: 'handleClose'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/hooks/useIntersectionObserver.test.ts + 3:10 warning 'useRef' is defined but never used @typescript-eslint/no-unused-vars + 104:13 warning 'result' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/hooks/useKeyboardNavigation.test.ts + 7:9 warning 'mockOnEnter' is assigned a value but never used @typescript-eslint/no-unused-vars + 10:9 warning 'mockOnArrowLeft' is assigned a value but never used @typescript-eslint/no-unused-vars + 11:9 warning 'mockOnArrowRight' is assigned a value but never used @typescript-eslint/no-unused-vars + 12:9 warning 'mockOnTab' is assigned a value but never used @typescript-eslint/no-unused-vars + 13:9 warning 'mockOnShiftTab' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/hooks/useLocalStorage.test.ts + 2:48 warning 'afterEach' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/main.tsx + 57:23 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/mocks/handlers.ts + 222:17 warning '_' is assigned a value but never used @typescript-eslint/no-unused-vars + 251:19 warning '_' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/mocks/test-helpers.ts + 1:10 warning 'contextBridge' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/pages/AdminDashboardPage.tsx + 84:6 warning React Hook useEffect has a missing dependency: 'loadDashboardData'. Either include it or remove the dependency array react-hooks/exhaustive-deps + 212:6 warning React Hook useEffect has a missing dependency: 'loadUsers'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/pages/AnalyticsPage.tsx + 39:6 warning React Hook useEffect has a missing dependency: 'loadAnalytics'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/pages/ProfilePage.test.tsx + 1:18 warning 'screen' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/pages/WebhooksPage.tsx + 58:6 warning React Hook useEffect has a missing dependency: 'loadWebhooks'. Either include it or remove the dependency array react-hooks/exhaustive-deps + 371:45 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/pages/auth/Register.test.tsx + 7:10 warning 'useToast' is defined but never used @typescript-eslint/no-unused-vars + 145:11 warning 'mockResponse' is assigned a value but never used @typescript-eslint/no-unused-vars + 220:5 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/schemas/apiSchemas.test.ts + 12:3 warning 'messageSchema' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/services/api/auth.ts + 252:13 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/services/api/client.test.ts + 2:8 warning 'axios' is defined but never used @typescript-eslint/no-unused-vars + 3:10 warning 'apiClient' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/services/api/client.ts + 11:28 warning '_isTimeoutError' is defined but never used @typescript-eslint/no-unused-vars + 11:66 warning '_getTimeoutMessage' is defined but never used @typescript-eslint/no-unused-vars + 324:18 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/services/pwa.ts + 64:29 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 237:9 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 258:9 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 258:9 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/services/requestDeduplication.ts + 52:50 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/services/responseCache.ts + 70:50 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/services/socialService.ts + 3:16 warning 'Notification' is defined but never used @typescript-eslint/no-unused-vars + 3:30 warning 'Comment' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/stores/auth.test.ts + 101:16 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + 129:7 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 181:16 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/test/helpers.tsx + 20:10 warning Fast refresh only works when a file only exports components. Move your component(s) to a separate file react-refresh/only-export-components + 35:1 warning This rule can't verify that `export *` only exports components react-refresh/only-export-components + +/home/senke/git/talas/veza/apps/web/src/test/test-utils.tsx + 31:7 warning Fast refresh only works when a file only exports components. Move your component(s) to a separate file react-refresh/only-export-components + 63:1 warning This rule can't verify that `export *` only exports components react-refresh/only-export-components + +/home/senke/git/talas/veza/apps/web/src/types/backend-types.ts + 2:10 warning 'PostType' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/utils/apiErrorHandler.test.ts + 6:36 warning 'beforeEach' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/utils/logger.test.ts + 10:7 warning 'consoleDebugSpy' is assigned a value but never used @typescript-eslint/no-unused-vars + 11:7 warning 'consoleInfoSpy' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/utils/logger.ts + 135:12 warning 'error' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/utils/optimisticUpdates.ts + 360:15 warning '_getValue' is assigned a value but never used @typescript-eslint/no-unused-vars + 361:15 warning '_getCount' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/utils/sanitize.test.ts + 6:36 warning 'beforeEach' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/utils/sanitize.ts + 134:45 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 138:45 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 141:39 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/utils/stateCleanup.test.ts + 213:12 warning 'set' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars + 236:12 warning 'set' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/utils/stateHydration.ts + 237:6 warning React Hook useEffect has a missing dependency: 'config'. Either include it or remove the dependency array react-hooks/exhaustive-deps + +/home/senke/git/talas/veza/apps/web/src/utils/stateNormalization.ts + 124:21 warning 'removed' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/utils/statePersistence.ts + 140:24 warning Do not access Object.prototype method 'hasOwnProperty' from target object no-prototype-builtins + +/home/senke/git/talas/veza/apps/web/src/utils/stateVersioning.example.ts + 93:6 warning 'set' is defined but never used. Allowed unused args must match /^_/u @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/utils/stateVersioning.test.ts + 6:44 warning 'vi' is defined but never used @typescript-eslint/no-unused-vars + 262:33 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 296:33 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + 358:33 warning Forbidden non-null assertion @typescript-eslint/no-non-null-assertion + +/home/senke/git/talas/veza/apps/web/src/utils/stateVersioning.ts + 206:50 warning '_migrations' is assigned a value but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/utils/timeoutHandler.test.ts + 9:3 warning 'TIMEOUT_MESSAGES' is defined but never used @typescript-eslint/no-unused-vars + +/home/senke/git/talas/veza/apps/web/src/utils/typeGuards.test.ts + 13:3 warning 'isSession' is defined but never used @typescript-eslint/no-unused-vars + 14:3 warning 'isAuditLog' is defined but never used @typescript-eslint/no-unused-vars + 20:3 warning 'isTrackArray' is defined but never used @typescript-eslint/no-unused-vars + 21:3 warning 'isPlaylistArray' is defined but never used @typescript-eslint/no-unused-vars + 22:3 warning 'isConversationArray' is defined but never used @typescript-eslint/no-unused-vars + 23:3 warning 'isMessageArray' is defined but never used @typescript-eslint/no-unused-vars + 24:3 warning 'isNotificationArray' is defined but never used @typescript-eslint/no-unused-vars + +✖ 384 problems (0 errors, 384 warnings) + 0 errors and 2 warnings potentially fixable with the `--fix` option. + diff --git a/apps/web/src/app/App.tsx b/apps/web/src/app/App.tsx index d075208b5..d7b5032b1 100644 --- a/apps/web/src/app/App.tsx +++ b/apps/web/src/app/App.tsx @@ -1,4 +1,5 @@ import { useEffect, useState } from 'react'; +import '@/styles/premium-utilities.css'; import { useAuthStore } from '@/features/auth/store/authStore'; @@ -26,7 +27,7 @@ export function App() { }); // FE-STATE-003: Hydrate state from server on app load - const { } = useStateHydration({ + useStateHydration({ hydrateAuth: true, hydrateLibrary: false, // Can be enabled if needed hydrateChat: false, // Can be enabled if needed diff --git a/apps/web/src/components/admin/UserTableRow.tsx b/apps/web/src/components/admin/UserTableRow.tsx index cafac83fd..8a3e60a41 100644 --- a/apps/web/src/components/admin/UserTableRow.tsx +++ b/apps/web/src/components/admin/UserTableRow.tsx @@ -1,88 +1,90 @@ import React, { useState } from 'react'; -import { User } from '../../types'; +import { User } from '@/types'; import { Badge } from '../ui/badge'; import { MoreVertical, Shield, Ban, Mail, Trash2 } from 'lucide-react'; interface UserTableRowProps { - user: User; - onBan: (user: User) => void; - onDelete: (user: User) => void; - onEditRole: (user: User) => void; + user: User; + onBan: (user: User) => void; + onDelete: (user: User) => void; + onEditRole: (user: User) => void; } export const UserTableRow: React.FC = ({ user, onBan, onDelete, onEditRole }) => { - const [showMenu, setShowMenu] = useState(false); + const [showMenu, setShowMenu] = useState(false); - const statusColor = { - 'online': 'bg-kodo-lime', - 'offline': 'bg-gray-500', - 'dnd': 'bg-kodo-red', - 'idle': 'bg-kodo-gold' - }; + const statusColor: Record = { + 'online': 'bg-kodo-lime', + 'offline': 'bg-gray-500', + 'away': 'bg-kodo-gold', + 'idle': 'bg-kodo-gold', + 'busy': 'bg-kodo-red', + 'dnd': 'bg-kodo-red' + }; - return ( - - -
+ return ( + + +
+
+ +
+
+
+
{user.username}
+
{user.id}
+
+
+ + {user.email} + +
+ {(user.roles || [user.role]).map((role: string) => ( + + ))} +
+ + + {user.tier || 'Free'} + + + {user.joinDate || user.created_at} + + + {user.lastLogin || user.last_login_at || 'Never'} + +
- -
+ + + {showMenu && ( + <> +
setShowMenu(false)}>
+
+ + +
+ + +
+ + )}
-
-
{user.username}
-
{user.id}
-
-
- - {user.email} - -
- {(user.roles || [user.role]).map((role: string) => ( - - ))} -
- - - {user.tier || 'Free'} - - - {user.joinDate || user.created_at} - - - {user.lastLogin || user.last_login_at || 'Never'} - - -
- - - {showMenu && ( - <> -
setShowMenu(false)}>
-
- - -
- - -
- - )} -
- - - ); + + + ); }; diff --git a/apps/web/src/components/dashboard/TrackList.tsx b/apps/web/src/components/dashboard/TrackList.tsx index 051d342fe..d4eb70365 100644 --- a/apps/web/src/components/dashboard/TrackList.tsx +++ b/apps/web/src/components/dashboard/TrackList.tsx @@ -2,10 +2,10 @@ import React, { useState, useEffect } from 'react'; import { Play, Heart, MoreHorizontal, AlertCircle, BarChart3 } from 'lucide-react'; import { Button } from '../ui/button'; -import { Track } from '../../types'; -import { useAudio } from '../../context/AudioContext'; -import { useToast } from '../../context/ToastContext'; -import { trackService } from '../../services/trackService'; +import { Track } from '@/types/api'; +import { useAudio } from '@/context/AudioContext'; +import { useToast } from '@/context/ToastContext'; +import { trackService } from '@/services/trackService'; import { logger } from '@/utils/logger'; export const TrackList: React.FC = () => { @@ -24,8 +24,8 @@ export const TrackList: React.FC = () => { setTracks(response.tracks); } catch (err) { logger.error('Failed to load tracks', { - error: err instanceof Error ? err.message : String(err), - stack: err instanceof Error ? err.stack : undefined, + error: err instanceof Error ? err.message : String(err), + stack: err instanceof Error ? err.stack : undefined, }); setError(true); } finally { @@ -46,50 +46,50 @@ export const TrackList: React.FC = () => { const handleLike = async (e: React.MouseEvent, track: Track) => { e.stopPropagation(); try { - await trackService.like(track.id); - addToast(`Liked ${track.title}`, 'success'); + await trackService.like(track.id); + addToast(`Liked ${track.title}`, 'success'); } catch (e) { - addToast("Action failed", "error"); + addToast("Action failed", "error"); } }; if (loading) { return ( -
- {[1, 2, 3, 4, 5].map(i => ( -
- ))} -
+
+ {[1, 2, 3, 4, 5].map(i => ( +
+ ))} +
); } if (error) { - return ( -
- -

Unable to load trending audio.

- -
- ); + return ( +
+ +

Unable to load trending audio.

+ +
+ ); } if (tracks.length === 0) { - return ( -
- -

No tracks trending right now.

-
- ); + return ( +
+ +

No tracks trending right now.

+
+ ); } return (
{tracks.map((track, i) => { const isCurrent = currentTrack?.id === track.id; - + return ( -
{ {isCurrent &&
}
- {isCurrent && isPlaying ? ( -
-
-
-
-
- ) : ( - {i + 1} - )} -
- {track.title} - {isCurrent &&
} + {track.title} + {isCurrent &&
}
-

{track.title}

-

{track.artist}

+

{track.title}

+

{track.artist}

- - {(track.plays || track.play_count) > 1000 ? `${((track.plays || track.play_count)/1000).toFixed(1) }k` : (track.plays || track.play_count)} - - - {track.duration} - + + {(track.plays || track.play_count) > 1000 ? `${((track.plays || track.play_count) / 1000).toFixed(1)}k` : (track.plays || track.play_count)} + + + {track.duration} +
- - + +
); diff --git a/apps/web/src/components/layout/DashboardLayout.tsx b/apps/web/src/components/layout/DashboardLayout.tsx index 09e57932b..cf2636e02 100644 --- a/apps/web/src/components/layout/DashboardLayout.tsx +++ b/apps/web/src/components/layout/DashboardLayout.tsx @@ -1,6 +1,7 @@ import type { ReactNode } from 'react'; import { Header } from './Header'; import { Sidebar } from './Sidebar'; +import { GlobalPlayer } from '@/features/player/components/GlobalPlayer'; interface DashboardLayoutProps { children: ReactNode; @@ -12,11 +13,12 @@ interface DashboardLayoutProps { */ export function DashboardLayout({ children }: DashboardLayoutProps) { return ( -
+
-
+
-
{children}
+
{children}
+
); diff --git a/apps/web/src/components/layout/Header.tsx b/apps/web/src/components/layout/Header.tsx index 15d204fa3..4a0ffb9f1 100644 --- a/apps/web/src/components/layout/Header.tsx +++ b/apps/web/src/components/layout/Header.tsx @@ -9,9 +9,8 @@ import { GlobalSearchBar } from '@/components/search/GlobalSearchBar'; import { Button } from '@/components/ui/button'; import { FocusTrap } from '@/components/ui/focus-trap'; import { Tooltip } from '@/components/ui/tooltip'; +import { cn } from '@/lib/utils'; import { - Menu, - X, User, Settings, LogOut, @@ -19,6 +18,7 @@ import { Sun, Monitor, Search, + Cpu, } from 'lucide-react'; import type { BaseComponentProps } from '../types'; @@ -30,11 +30,10 @@ export interface HeaderProps extends BaseComponentProps { // No additional props needed - uses global stores } -export function Header({ className: _className }: HeaderProps = {}) { +export function Header(_props: HeaderProps) { const [isUserMenuOpen, setIsUserMenuOpen] = useState(false); - const [isMobileSearchOpen, setIsMobileSearchOpen] = useState(false); const { user, logout } = useAuthStore(); - const { theme, setTheme, sidebarOpen, setSidebarOpen } = useUIStore(); + const { theme, setTheme } = useUIStore(); const { t } = useTranslation(); const navigate = useNavigate(); @@ -61,171 +60,122 @@ export function Header({ className: _className }: HeaderProps = {}) { }; return ( -
-
- {/* Main header row */} -
- {/* Logo et menu mobile */} -
- - - - - -