web server tweaks
This commit is contained in:
parent
af58eb1987
commit
cda8a5ec6d
262
docs/webserver-api-reference.md
Normal file
262
docs/webserver-api-reference.md
Normal file
@ -0,0 +1,262 @@
|
||||
# CrossPointWebServer API Reference
|
||||
|
||||
Source: `src/network/CrossPointWebServer.cpp` and `CrossPointWebServer.h`
|
||||
|
||||
## Server Configuration
|
||||
|
||||
- HTTP port: 80 (default)
|
||||
- WebSocket port: 81 (default)
|
||||
- WiFi sleep disabled for responsiveness
|
||||
- Supports both STA (station) and AP (access point) modes
|
||||
|
||||
## HTTP Endpoints
|
||||
|
||||
### GET /
|
||||
**Handler:** `handleRoot()`
|
||||
**Response:** HTML homepage from `HomePageHtml` (generated from `html/HomePage.html`)
|
||||
**Content-Type:** text/html
|
||||
|
||||
### GET /files
|
||||
**Handler:** `handleFileList()`
|
||||
**Response:** HTML file browser page from `FilesPageHtml` (generated from `html/FilesPage.html`)
|
||||
**Content-Type:** text/html
|
||||
|
||||
### GET /api/status
|
||||
**Handler:** `handleStatus()`
|
||||
**Response:** JSON device status
|
||||
**Content-Type:** application/json
|
||||
```json
|
||||
{
|
||||
"version": "CROSSPOINT_VERSION",
|
||||
"ip": "192.168.x.x",
|
||||
"mode": "AP" | "STA",
|
||||
"rssi": -50, // 0 in AP mode
|
||||
"freeHeap": 123456,
|
||||
"uptime": 3600 // seconds
|
||||
}
|
||||
```
|
||||
|
||||
### GET /api/files
|
||||
**Handler:** `handleFileListData()`
|
||||
**Query params:**
|
||||
- `path` (optional): Directory path, defaults to "/"
|
||||
- `showHidden` (optional): "true" to show dot-files (except .crosspoint)
|
||||
**Response:** JSON array of files
|
||||
**Content-Type:** application/json
|
||||
```json
|
||||
[
|
||||
{"name": "book.epub", "size": 123456, "isDirectory": false, "isEpub": true},
|
||||
{"name": "folder", "size": 0, "isDirectory": true, "isEpub": false}
|
||||
]
|
||||
```
|
||||
**Notes:**
|
||||
- Hidden by default: files starting with ".", "System Volume Information", "XTCache"
|
||||
- Always hidden: ".crosspoint" (internal cache folder)
|
||||
- Streamed response (chunked encoding) to reduce memory usage
|
||||
|
||||
### GET /api/archived
|
||||
**Handler:** `handleArchivedList()`
|
||||
**Response:** JSON array of archived books
|
||||
**Content-Type:** application/json
|
||||
```json
|
||||
[
|
||||
{"filename": "archived_file.epub", "originalPath": "/Books/archived_file.epub"}
|
||||
]
|
||||
```
|
||||
**Notes:** Uses `BookManager::listArchivedBooks()` and `BookManager::getArchivedBookOriginalPath()`
|
||||
|
||||
### GET /download
|
||||
**Handler:** `handleDownload()`
|
||||
**Query params:**
|
||||
- `path` (required): File path to download
|
||||
**Response:** File binary with Content-Disposition attachment header
|
||||
**Content-Type:** application/octet-stream
|
||||
**Errors:**
|
||||
- 400: Missing path, path is directory
|
||||
- 403: Hidden/system file, protected item
|
||||
- 404: File not found
|
||||
**Notes:**
|
||||
- Streams in 4KB chunks
|
||||
- Updates `totalBytesDownloaded` and `totalFilesDownloaded` stats
|
||||
- Security: rejects paths with "..", files starting with ".", protected items
|
||||
|
||||
### POST /upload
|
||||
**Handler:** `handleUpload()` (multipart handler), `handleUploadPost()` (response handler)
|
||||
**Query params:**
|
||||
- `path` (optional): Upload directory, defaults to "/"
|
||||
**Form data:** multipart/form-data with file
|
||||
**Response:** "File uploaded successfully: filename" or error message
|
||||
**Notes:**
|
||||
- Uses 4KB write buffer for SD card efficiency
|
||||
- Overwrites existing files
|
||||
- Clears epub cache after upload via `clearEpubCacheIfNeeded()`
|
||||
- Updates `totalBytesUploaded` and `totalFilesUploaded` stats
|
||||
- Logs progress every 100KB
|
||||
|
||||
### POST /mkdir
|
||||
**Handler:** `handleCreateFolder()`
|
||||
**Form params:**
|
||||
- `name` (required): Folder name
|
||||
- `path` (optional): Parent directory, defaults to "/"
|
||||
**Response:** "Folder created: foldername" or error
|
||||
**Errors:**
|
||||
- 400: Missing name, empty name, folder exists
|
||||
|
||||
### POST /delete
|
||||
**Handler:** `handleDelete()`
|
||||
**Form params:**
|
||||
- `path` (required): Item path to delete
|
||||
- `type` (optional): "file" (default) or "folder"
|
||||
- `archived` (optional): "true" for archived books
|
||||
**Response:** "Deleted successfully" or error
|
||||
**Errors:**
|
||||
- 400: Missing path, root directory, folder not empty
|
||||
- 403: Hidden/system file, protected item
|
||||
- 404: Item not found
|
||||
- 500: Delete failed
|
||||
**Notes:**
|
||||
- For files: uses `BookManager::deleteBook()` which handles cache and recent books cleanup
|
||||
- For folders: must be empty first
|
||||
- For archived: passes filename to `BookManager::deleteBook(filename, true)`
|
||||
|
||||
### POST /archive
|
||||
**Handler:** `handleArchive()`
|
||||
**Form params:**
|
||||
- `path` (required): Book path to archive
|
||||
**Response:** "Book archived successfully" or error
|
||||
**Notes:** Uses `BookManager::archiveBook()`
|
||||
|
||||
### POST /unarchive
|
||||
**Handler:** `handleUnarchive()`
|
||||
**Form params:**
|
||||
- `filename` (required): Archived book filename
|
||||
**Response:** JSON with original path
|
||||
**Content-Type:** application/json
|
||||
```json
|
||||
{"success": true, "originalPath": "/Books/book.epub"}
|
||||
```
|
||||
**Notes:** Uses `BookManager::unarchiveBook()` and `BookManager::getArchivedBookOriginalPath()`
|
||||
|
||||
### POST /rename
|
||||
**Handler:** `handleRename()`
|
||||
**Form params:**
|
||||
- `path` (required): Current item path
|
||||
- `newName` (required): New name (filename only, no path separators)
|
||||
**Response:** "Renamed successfully" or error
|
||||
**Errors:**
|
||||
- 400: Missing params, empty name, name contains "/" or "\\", root directory, destination exists
|
||||
- 403: System file, protected item
|
||||
- 404: Source not found
|
||||
- 500: Rename failed
|
||||
**Notes:**
|
||||
- Renames in place (same directory, new name)
|
||||
- Uses `SdMan.rename()`
|
||||
- Clears epub cache after rename via `clearEpubCacheIfNeeded()`
|
||||
|
||||
### POST /copy
|
||||
**Handler:** `handleCopy()`
|
||||
**Form params:**
|
||||
- `srcPath` (required): Source path
|
||||
- `destPath` (required): Full destination path (including new name)
|
||||
**Response:** "Copied successfully" or error
|
||||
**Errors:**
|
||||
- 400: Missing params, root directory, destination exists, copy into self
|
||||
- 403: System file, protected item
|
||||
- 404: Source not found
|
||||
- 500: Copy failed
|
||||
**Notes:**
|
||||
- Uses `copyFile()` for files (4KB buffer chunks)
|
||||
- Uses `copyFolder()` for recursive directory copy
|
||||
- Skips hidden files in folder copy
|
||||
|
||||
### POST /move
|
||||
**Handler:** `handleMove()`
|
||||
**Form params:**
|
||||
- `srcPath` (required): Source path
|
||||
- `destPath` (required): Full destination path (including new name)
|
||||
**Response:** "Moved successfully" or error
|
||||
**Errors:** Same as copy
|
||||
**Notes:**
|
||||
- First attempts atomic `SdMan.rename()` (fast)
|
||||
- Falls back to copy+delete if rename fails
|
||||
- Uses `deleteFolderRecursive()` for folder cleanup
|
||||
|
||||
## WebSocket Protocol (port 81)
|
||||
|
||||
**Handler:** `onWebSocketEvent()` via `wsEventCallback()` trampoline
|
||||
|
||||
### Upload Protocol
|
||||
|
||||
1. Client connects
|
||||
2. Server: (implicit connection acknowledgment)
|
||||
3. Client TEXT: `START:<filename>:<size>:<path>`
|
||||
4. Server TEXT: `READY` or `ERROR:<message>`
|
||||
5. Client BIN: file data chunks (any size, recommend 64KB)
|
||||
6. Server TEXT: `PROGRESS:<received>:<total>` (every 64KB or at end)
|
||||
7. Server TEXT: `DONE` or `ERROR:<message>`
|
||||
|
||||
### Events
|
||||
- `WStype_CONNECTED`: Client connected, logs connection
|
||||
- `WStype_DISCONNECTED`: Cleanup incomplete upload, delete partial file
|
||||
- `WStype_TEXT`: Parse control messages (START)
|
||||
- `WStype_BIN`: Write file data, send progress, complete upload
|
||||
|
||||
### Notes
|
||||
- Faster than HTTP multipart for large files
|
||||
- Direct binary writes to SD card
|
||||
- Clears epub cache after upload
|
||||
- Updates traffic statistics
|
||||
|
||||
## Security
|
||||
|
||||
### Protected Items (HIDDEN_ITEMS[])
|
||||
- "System Volume Information"
|
||||
- "XTCache"
|
||||
|
||||
### Always Hidden
|
||||
- ".crosspoint" (internal cache)
|
||||
|
||||
### Security Checks Applied To
|
||||
- `/delete`: Rejects dot-files (unless archived), protected items
|
||||
- `/download`: Rejects dot-files, protected items, path traversal (..)
|
||||
- `/rename`: Rejects dot-files, protected items
|
||||
- `/copy`: Rejects dot-files, protected items
|
||||
- `/move`: Rejects dot-files, protected items
|
||||
|
||||
## Helper Functions
|
||||
|
||||
### clearEpubCacheIfNeeded(filePath)
|
||||
- Location: anonymous namespace at top of file
|
||||
- Clears epub cache if file ends with ".epub"
|
||||
- Uses `Epub(filePath, "/.crosspoint").clearCache()`
|
||||
- Called by: upload, WebSocket upload, rename
|
||||
|
||||
### scanFiles(path, callback, showHidden)
|
||||
- Iterates directory, calls callback for each FileInfo
|
||||
- Yields and resets watchdog during iteration
|
||||
- Filters hidden items based on showHidden flag
|
||||
|
||||
### copyFile(srcPath, destPath) / copyFolder(srcPath, destPath)
|
||||
- 4KB buffer for file copy
|
||||
- Recursive for folders
|
||||
- Returns bool success
|
||||
|
||||
### deleteFolderRecursive(path)
|
||||
- Static helper for move fallback
|
||||
- Recursively deletes contents then directory
|
||||
|
||||
## Traffic Statistics (mutable, updated from const handlers)
|
||||
- `totalBytesUploaded`
|
||||
- `totalBytesDownloaded`
|
||||
- `totalFilesUploaded`
|
||||
- `totalFilesDownloaded`
|
||||
- `serverStartTime` (for uptime calculation)
|
||||
|
||||
## Dependencies
|
||||
- `<WebServer.h>` - ESP32 HTTP server
|
||||
- `<WebSocketsServer.h>` - WebSocket support
|
||||
- `<ArduinoJson.h>` - JSON serialization
|
||||
- `<SDCardManager.h>` - SD card operations (SdMan singleton)
|
||||
- `<Epub.h>` - Epub cache management
|
||||
- `BookManager.h` - Book deletion, archiving, recent books
|
||||
- `StringUtils.h` - File extension checking
|
||||
@ -77,7 +77,7 @@ void CrossPointWebServerActivity::onEnter() {
|
||||
updateRequired = true;
|
||||
|
||||
xTaskCreate(&CrossPointWebServerActivity::taskTrampoline, "WebServerActivityTask",
|
||||
2048, // Stack size
|
||||
6144, // Stack size (increased: QR code + string ops need ~4KB)
|
||||
this, // Parameters
|
||||
1, // Priority
|
||||
&displayTaskHandle // Task handle
|
||||
|
||||
@ -1143,6 +1143,11 @@ void CrossPointWebServer::handleRename() const {
|
||||
esp_task_wdt_reset();
|
||||
if (SdMan.rename(itemPath.c_str(), newPath.c_str())) {
|
||||
Serial.printf("[%lu] [WEB] Rename successful\n", millis());
|
||||
|
||||
// Clear epub cache for both old and new paths to prevent stale metadata
|
||||
clearEpubCacheIfNeeded(itemPath); // Old path cache is now invalid
|
||||
clearEpubCacheIfNeeded(newPath); // Ensure clean cache for new path
|
||||
|
||||
server->send(200, "text/plain", "Renamed successfully");
|
||||
} else {
|
||||
Serial.printf("[%lu] [WEB] Rename failed\n", millis());
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user