Admin web deployment

Build the frontend and publish the generated dist directory as the Nginx document root:

npm run build

The admin web app uses Vue Router HTML5 history mode. The active Nginx server block must therefore contain:

location / {
    try_files $uri $uri/ /index.html;
}

Use nginx.conf as the site template. If the host already terminates HTTPS in another server block, merge the location blocks into that HTTPS block instead of creating a second TLS listener. Keep /api/ and /uploads/ proxied to the Spring Boot service; only client-side page routes should fall back to index.html.

The backend maps uploaded files as follows:

/uploads/avatars/**             -> AVATAR_STORAGE_DIR (default: ./data/avatars)
/uploads/guide-applications/**  -> GUIDE_APPLICATION_STORAGE_DIR (default: ./data/guide-applications)

The recommended Nginx configuration is already included in nginx.conf:

location ^~ /uploads/ {
    proxy_pass http://127.0.0.1:8011;
}

This preserves the backend authorization check for guide application documents. If avatars must be served directly by Nginx, only expose the avatar directory and keep guide applications proxied:

location ^~ /uploads/avatars/ {
    alias /opt/tourguide/data/avatars/;
    autoindex off;
}

location ^~ /uploads/guide-applications/ {
    proxy_pass http://127.0.0.1:8011;
}

Replace /opt/tourguide/data/... with the actual absolute server paths. Configure the same absolute paths through AVATAR_STORAGE_DIR and GUIDE_APPLICATION_STORAGE_DIR for the backend, so storage does not depend on the Java process working directory.

After reloading Nginx, verify all three request types:

GET /                         -> frontend index.html (200)
GET /login                    -> frontend index.html (200)
GET /api/health               -> backend JSON (200)