# Virtual Filesystem (`/acq/`) Guide

The Acequia service worker provides a virtual filesystem that maps `/acq/` prefixed URLs to remote WebDAV servers. This allows apps to access remote data using simple fetch requests to local-looking paths.

## How It Works

When the service worker intercepts a request starting with `/acq/`:

1. Looks up mount points from IndexedDB (`acequiaMounts` store)
2. Finds the mount with the longest matching path
3. Rewrites the URL to the actual WebDAV server
4. Uses stored credentials to authenticate
5. Returns the response with paths rewritten back to `/acq/` format

**If no mount point is found, the service worker returns a 404 error.**

## Two Types of Mount Points

There are two distinct mount point systems - don't confuse them:

### 1. Server-Side Mount Points (`config.mjs`)

These configure how the **WebDAV server** maps incoming paths to filesystem locations:

```javascript
// config.mjs - SERVER side
mountPoints: {
  '/': {
    sourceRef: './acequia',
    type: 'fs',
    auth: { type: 'jwt' },
  },
}
```

### 2. Client-Side Mount Points (IndexedDB)

These configure how the **service worker** maps `/acq/` URLs to remote servers:

```javascript
// Client-side JavaScript - BROWSER side
const mountStore = acequia.utils.localforage.createInstance({
    name: 'acequiaMounts',
})

await mountStore.setItem('/customerData', {
    path: '/customerData',
    serverUrl: 'https://webdav.example.com',
    subPath: '/accounts',  // optional
})
```

## Registering Mount Points

Before using `/acq/` URLs, your app must register mount points in IndexedDB:

### Step 1: Register Server Credentials

```javascript
import acequia from '/acequia.js'

const credentialsStore = acequia.utils.localforage.createInstance({
    name: 'acequiaCredentials',
})

// For Acequia token auth (recommended)
await credentialsStore.setItem('https://webdav.example.com', {
    serverUrl: 'https://webdav.example.com',
    type: 'acequiaToken',
})

// OR for simple username/password auth
await credentialsStore.setItem('https://webdav.example.com', {
    serverUrl: 'https://webdav.example.com',
    type: 'simple',
    username: 'user',
    password: 'password',
})
```

### Step 2: Register Mount Point

```javascript
const mountStore = acequia.utils.localforage.createInstance({
    name: 'acequiaMounts',
})

await mountStore.setItem('/mydata', {
    path: '/mydata',                        // Virtual path (without /acq prefix)
    serverUrl: 'https://webdav.example.com', // Must match credentials key
    subPath: '/some/server/path',           // Optional: path on the server
})
```

### Step 3: Use the Virtual Path

```javascript
// Now you can fetch using the /acq/ prefix
const response = await fetch('/acq/mydata/file.json')
const data = await response.json()

// This request becomes:
// GET https://webdav.example.com/some/server/path/file.json
```

## URL Mapping Example

Given this mount configuration:

```javascript
// Credentials
{ serverUrl: 'https://data.example.com', type: 'acequiaToken' }

// Mount
{ path: '/events', serverUrl: 'https://data.example.com', subPath: '/v2/events' }
```

URLs are mapped as follows:

| Virtual URL | Actual Request |
|-------------|----------------|
| `/acq/events/` | `GET https://data.example.com/v2/events/` |
| `/acq/events/2024/fire.json` | `GET https://data.example.com/v2/events/2024/fire.json` |

## Helper Functions

For cleaner code, create helper functions (see [alert.live/src/models/mountPoints.js](../../alert.live/src/models/mountPoints.js) for a full example):

```javascript
import acequia from '/acequia.js'

const mountStore = acequia.utils.localforage.createInstance({
    name: 'acequiaMounts',
})

const credentialsStore = acequia.utils.localforage.createInstance({
    name: 'acequiaCredentials',
})

export async function addMountPoint(virtualPath, subPath, serverUrl) {
    await mountStore.setItem(virtualPath, {
        path: virtualPath,
        serverUrl,
        subPath,
    })
}

export async function addServer(serverUrl, type, username, password) {
    await credentialsStore.setItem(serverUrl, {
        serverUrl,
        type,
        username,
        password,
    })
}

export async function getMountPoint(virtualPath) {
    // Strip /acq prefix if present
    const path = virtualPath.startsWith('/acq')
        ? virtualPath.slice(4)
        : virtualPath
    return await mountStore.getItem(path)
}

export async function removeMountPoint(virtualPath) {
    return await mountStore.removeItem(virtualPath)
}
```

## Troubleshooting

### 404 Errors on `/acq/` URLs

If you get a 404 when fetching `/acq/` URLs:

1. **Check mount registration** - Open browser DevTools, go to Application > IndexedDB > acequiaMounts and verify your mount exists

2. **Check credentials** - Verify credentials exist in Application > IndexedDB > acequiaCredentials

3. **Check service worker** - Ensure the service worker is active (Application > Service Workers)

4. **Check console for "no mount found"** - The service worker logs this when it can't find a matching mount

5. **Verify path matching** - The mount path must be a prefix of your URL path (without the `/acq` prefix)

### Service Worker Not Intercepting

If requests bypass the service worker entirely:

1. **Service worker not registered** - Check Application > Service Workers
2. **Wrong scope** - Service worker must control the page making requests
3. **Request before SW ready** - Wait for `acequia.acequiaReady()` before making requests

### Credential Errors

If authentication fails:

1. **serverUrl mismatch** - The `serverUrl` in mount must exactly match the key in credentials store
2. **Expired token** - For `acequiaToken` type, the service worker will auto-generate a new token if missing
3. **Wrong auth type** - Ensure the server expects the auth type you configured

## Service Worker Handler Chain

The service worker processes requests through a handler chain. Understanding this helps debug issues:

```
1. handleGroupRouteRequest  → Handles /groups/* routes
2. handleWebDAVRequest      → Handles /acq/* URLs (returns 404 if no mount)
3. alwaysFetchHandler       → URLs in config.alwaysFetch bypass cache
4. cacheHandler             → Serves from cache if available
5. (fallback)               → Direct network fetch
```

**Important:** If `handleWebDAVRequest` finds a URL starting with `/acq` but no mount, it returns 404 immediately. The request does NOT fall through to network.
