# Services Homepage - Features Documentation

## Overview

This services homepage provides a modern, feature-rich interface for managing and monitoring your self-hosted services. All features include persistent storage using browser localStorage.

---

## 🎯 Core Features

### 1. **Custom Health Check Endpoints**

Health checks now support custom endpoints for services that don't respond on the root path.

#### Configuration

Add the `health-path` attribute to any service in `services.xml`:

```xml
<service id="jellyfin" 
         name="Jellyfin" 
         proto="http" 
         port="8096" 
         health-path="/health"
         logo="jellyfin.svg" />
```

**Supported attributes:**
- `health-path`: Custom path to ping (e.g., `/api/health`, `/ping`, `/status`)
- If not specified, defaults to root path (`/`)

#### How It Works

The health-proxy service:
1. Parses `services.xml` and finds the service by `id`
2. Uses `local-ip` > `tailscale-ip` > `host` for the target
3. Appends the custom `health-path` to the URL
4. Performs a HEAD request to check availability

---

### 2. **Drag-and-Drop Service Reordering** 🖱️

Reorganize your services within each group by dragging and dropping cards.

#### Usage

1. Click and hold any service card
2. Drag it to the desired position within the same group
3. Release to drop
4. Order is automatically saved to localStorage

#### Features

- ✅ Reordering within groups only (maintains organization)
- ✅ Visual feedback during drag (opacity, border highlight)
- ✅ Persistent across browser sessions
- ✅ Per-group order tracking

#### Reset Order

To reset to default XML order:
```javascript
localStorage.removeItem('services-order');
location.reload();
```

---

### 3. **Collapsible Service Groups** 📁

Collapse and expand service groups to focus on what matters.

#### Usage

1. Click on any group header (or the ▼ icon)
2. The group will collapse/expand
3. State is saved automatically

#### Features

- ✅ Animated collapse/expand
- ✅ Persistent state per group
- ✅ Visual indicator (rotating arrow)
- ✅ Keyboard accessible

#### Reset State

```javascript
localStorage.removeItem('collapsed-groups');
location.reload();
```

---

### 4. **Theme System** 🎨

Choose from 5 built-in themes or customize your own.

#### Available Themes

1. **Dark** (Default) - Deep blue-gray tones
2. **Light** - Clean white and blue
3. **Ocean** - Deep teal and aqua
4. **Sunset** - Purple and pink gradient
5. **Forest** - Green and earth tones

#### Usage

1. Click the 🎨 icon in the top-right corner
2. Select a theme from the menu
3. Theme is applied immediately and saved

#### Custom Themes

Themes are defined in `js/themes.js`. To add a custom theme:

```javascript
customTheme: {
  name: 'My Theme',
  primary: '#hexcolor',
  secondary: '#hexcolor',
  accent: '#hexcolor',
  text: '#hexcolor',
  textMuted: '#hexcolor',
  border: '#hexcolor',
  cardBg: 'rgba(...)',
  headerBg: 'rgba(...)',
  overlayBg: 'rgba(...)'
}
```

Add your theme to the `themes` object and rebuild.

---

### 5. **Export/Import Configuration** 💾

Backup and restore your service configurations with JSON export/import.

#### Export Configuration

1. Click **📥 Export** button in the top-left
2. Downloads `services-config-YYYY-MM-DD.json` file
3. Contains all services, groups, and settings

#### Import Configuration

1. Click **📤 Import** button
2. Select a previously exported JSON file
3. Downloads a new `services.xml` file
4. Replace your existing `services.xml` and rebuild containers

#### Export Format

```json
{
  "version": "1.0",
  "exportDate": "2025-11-24T...",
  "tailscaleIp": "100.124.17.41",
  "groups": [
    {
      "name": "Management",
      "services": [
        {
          "id": "portainer",
          "name": "Portainer",
          "proto": "https",
          "port": "9443",
          "logo": "portainer.svg"
        }
      ]
    }
  ]
}
```

---

### 6. **Dashboard Widgets** 📊

Add live widgets to your homepage header.

#### Available Widgets

##### ⏰ Clock Widget
- Real-time clock with date
- Updates every second
- Always enabled by default

##### 🌤️ Weather Widget
- Current weather for your location
- Temperature, conditions, wind, humidity
- Requires **OpenWeatherMap API key** (free)
- Auto-location or custom city

##### 💭 Daily Quote Widget
- Random inspirational quotes
- Fetched from quotable.io API
- Changes on each page load

#### Configuration

1. Click **⚙️ Widgets** button
2. Enable/disable widgets with checkboxes
3. For weather:
   - Get API key from [OpenWeatherMap](https://openweathermap.org/api)
   - Enter API key
   - Set location to `auto` or city name (e.g., `Toronto`)
4. Click **Save & Reload**

#### Settings Storage

All widget settings stored in localStorage:
```javascript
{
  "clock": {"enabled": true},
  "weather": {
    "enabled": true,
    "apiKey": "your-api-key",
    "location": "auto"
  },
  "quote": {"enabled": false}
}
```

---

## 🛠️ Technical Details

### Architecture

```
┌─────────────────┐
│  Browser        │
│  (HTTPS)        │
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│  Nginx          │◄── Serves static files
│  (Port 8088)    │◄── Reverse proxy /healthcheck
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│  Health Proxy   │
│  (Flask:8081)   │◄── Server-side health checks
│                 │◄── Parses services.xml
└─────────────────┘
```

### Files Structure

```
services-homepage/
├── index.html                 # Main HTML
├── styles.css                 # Styles with CSS variables
├── services.xml               # Service configuration
├── nginx.conf                 # Nginx config with resolver
├── docker-compose.yml         # Multi-container setup
├── backend/
│   └── health-proxy.py        # Flask health check service
└── js/
    ├── galaxy-background.js   # 3D background
    ├── services-loader.js     # Core service rendering
    ├── drag-drop.js           # Drag-and-drop module
    ├── collapsible-groups.js  # Group collapse module
    ├── themes.js              # Theme system
    ├── export-import.js       # Config backup/restore
    ├── widgets.js             # Dashboard widgets
    ├── search.js              # Search functionality
    ├── keyboard-nav.js        # Arrow key navigation
    └── readme-loader.js       # Markdown rendering
```

### localStorage Keys

| Key | Purpose | Format |
|-----|---------|--------|
| `services-order` | Drag-drop order | `{"GroupName": ["id1", "id2"]}` |
| `collapsed-groups` | Collapsed state | `{"GroupName": true/false}` |
| `selected-theme` | Active theme | `"dark"/"light"/"ocean"/...` |
| `enabled-widgets` | Widget settings | `{clock: {...}, weather: {...}}` |

---

## 📋 services.xml Reference

### Root Element

```xml
<services tailscale-ip="100.124.17.41">
```

**Attributes:**
- `tailscale-ip`: Default IP for local services (optional)

### Service Element

```xml
<service 
  id="unique-id"
  name="Display Name"
  proto="http|https"
  port="8080"
  host="domain.com"
  logo="logo.svg"
  status="maintenance"
  check-health="true|false"
  health-path="/custom/path"
  local-ip="192.168.1.100"
/>
```

**Attributes:**

| Attribute | Required | Description | Example |
|-----------|----------|-------------|---------|
| `id` | Recommended | Unique identifier | `portainer` |
| `name` | **Required** | Display name | `Portainer` |
| `proto` | Optional | Protocol (default: `http`) | `https` |
| `port` | Optional | Port number | `9443` |
| `host` | Optional | Custom hostname/URL | `cloud.example.com` |
| `logo` | Optional | Logo filename in `/logos/` | `portainer.svg` |
| `status` | Optional | Manual status override | `maintenance` |
| `check-health` | Optional | Enable health check (default: `true`) | `false` |
| `health-path` | Optional | Custom health endpoint | `/api/health` |
| `local-ip` | Optional | Override IP for health checks | `192.168.1.100` |

---

## 🔧 Troubleshooting

### Health Checks Return 502

**Problem:** Nginx can't resolve the health-proxy container.

**Solution:** Ensure nginx.conf contains:
```nginx
resolver 127.0.0.11 valid=30s;
```

### Widgets Not Appearing

**Problem:** Widgets enabled but not showing.

**Solutions:**
1. Check browser console for errors
2. For weather: Verify API key is correct
3. Clear localStorage and reconfigure:
   ```javascript
   localStorage.removeItem('enabled-widgets');
   location.reload();
   ```

### Drag-and-Drop Not Working

**Problem:** Cards won't drag.

**Solutions:**
1. Ensure JavaScript is enabled
2. Check browser console for errors
3. Verify cards have `draggable="true"` attribute
4. Try different browser

### Theme Not Persisting

**Problem:** Theme resets after reload.

**Solutions:**
1. Check browser allows localStorage
2. Try incognito/private mode to test
3. Clear site data and reconfigure

---

## 🚀 Future Enhancement Ideas

- [ ] Custom widget creation API
- [ ] Service groups reordering (drag groups)
- [ ] Dark mode auto-switch (time-based)
- [ ] Service uptime statistics
- [ ] Notification system for service outages
- [ ] Mobile app (PWA)
- [ ] Multi-user configurations
- [ ] Service tags and filtering
- [ ] Global search across all services
- [ ] Service dependencies visualization

---

## 📝 Changelog

### v2.0.0 - 2025-11-24

**Added:**
- ✨ Custom health check endpoints support
- ✨ Drag-and-drop service reordering
- ✨ Collapsible service groups
- ✨ Theme system (5 themes)
- ✨ Export/import configuration
- ✨ Dashboard widgets (clock, weather, quote)
- 🐛 Fixed nginx DNS resolver for health-proxy
- 🐛 Fixed services.xml mount in health-proxy container

### v1.0.0 - Previous

- ✅ Basic service listing from XML
- ✅ Automatic health checks
- ✅ Search functionality
- ✅ Keyboard navigation
- ✅ 3D galaxy background
- ✅ README rendering

---

## 🤝 Contributing

To add features:

1. Create new module in `/js/`
2. Export functions via `window.moduleName`
3. Import in `index.html`
4. Initialize in DOMContentLoaded listener
5. Add styles to `styles.css`
6. Document in this file

---

## 📄 License

This project is open source. See LICENSE file for details.

---

**Questions or Issues?** Check the README.md or open an issue.
