Make /api/files/... to proto endpoints

This commit is contained in:
Niklas Jensen
2026-01-25 19:54:59 +01:00
committed by nikguin04
parent bd012046f2
commit f9a99ce013
5 changed files with 58 additions and 34 deletions
@@ -6,6 +6,8 @@
import { modals } from 'svelte-modals' import { modals } from 'svelte-modals'
import NewFolderDialog from './NewFolderDialog.svelte' import NewFolderDialog from './NewFolderDialog.svelte'
import NewFileDialog from './NewFileDialog.svelte' import NewFileDialog from './NewFileDialog.svelte'
import { api } from '$lib/api'
import type { Response } from '$lib/platform_shared/api'
let currentPath = $state('/') let currentPath = $state('/')
let files = $state<Array<{ name: string; size: number }>>([]) let files = $state<Array<{ name: string; size: number }>>([])
@@ -87,16 +89,17 @@
error = '' error = ''
try { try {
const filePath = currentPath === '/' ? `/${selectedFile}` : `${currentPath}/${selectedFile}` const filePath = currentPath === '/' ? `/${selectedFile}` : `${currentPath}/${selectedFile}`
const encoder = new TextEncoder() const content = new TextEncoder().encode(fileContent)
const data = encoder.encode(fileContent)
const result = await fileSystemClient.uploadFile(filePath, data) const result = await api.post_proto<Response>('/api/files/edit', {
fileEditRequest: { path: filePath, content }
})
if (result.success) { if (result.ok && result.value.statusCode === 200) {
isEditing = false isEditing = false
await loadDirectory() // Refresh to update file sizes await loadDirectory() // Refresh to update file sizes
} else { } else {
error = result.error || 'Failed to save file' error = result.ok ? result.value.errorMessage || 'Failed to save file' : 'Failed to save file'
} }
} catch (e) { } catch (e) {
error = e instanceof Error ? e.message : 'Failed to save file' error = e instanceof Error ? e.message : 'Failed to save file'
@@ -189,11 +192,14 @@
const path = currentPath === '/' ? `/${folderName}` : `${currentPath}/${folderName}` const path = currentPath === '/' ? `/${folderName}` : `${currentPath}/${folderName}`
try { try {
const result = await fileSystemClient.createDirectory(path) const result = await api.post_proto<Response>('/api/files/mkdir', {
if (result.success) { fileMkdirRequest: { path }
})
if (result.ok && result.value.statusCode === 200) {
await loadDirectory() await loadDirectory()
} else { } else {
error = result.error || 'Failed to create directory' error = result.ok ? result.value.errorMessage || 'Failed to create directory' : 'Failed to create directory'
} }
} catch (e) { } catch (e) {
error = e instanceof Error ? e.message : 'Error creating directory' error = e instanceof Error ? e.message : 'Error creating directory'
@@ -207,15 +213,15 @@
const path = currentPath === '/' ? `/${fileName}` : `${currentPath}/${fileName}` const path = currentPath === '/' ? `/${fileName}` : `${currentPath}/${fileName}`
try { try {
const encoder = new TextEncoder() const result = await api.post_proto<Response>('/api/files/edit', {
const data = encoder.encode('{}') // Default empty JSON fileEditRequest: { path, content: new Uint8Array(0) }
})
const result = await fileSystemClient.uploadFile(path, data) if (result.ok && result.value.statusCode === 200) {
if (result.success) {
await loadDirectory() await loadDirectory()
await loadFileContent(fileName) await loadFileContent(fileName)
} else { } else {
error = result.error || 'Failed to create file' error = result.ok ? result.value.errorMessage || 'Failed to create file' : 'Failed to create file'
} }
} catch (e) { } catch (e) {
error = e instanceof Error ? e.message : 'Error creating file' error = e instanceof Error ? e.message : 'Error creating file'
+3 -3
View File
@@ -21,13 +21,13 @@ namespace FileSystem {
void listFilesProto(const std::string &directory, api_FileEntry *entry); void listFilesProto(const std::string &directory, api_FileEntry *entry);
std::string listFiles(const std::string &directory, bool isRoot = true); std::string listFiles(const std::string &directory, bool isRoot = true);
bool deleteFile(const char *filename); bool deleteFile(const char *filename);
bool editFile(const char *filename, const char *content); bool editFile(const char *filename, const uint8_t *content, size_t size);
esp_err_t getFilesProto(httpd_req_t *request); esp_err_t getFilesProto(httpd_req_t *request);
esp_err_t getFiles(httpd_req_t *request); esp_err_t getFiles(httpd_req_t *request);
esp_err_t getConfigFile(httpd_req_t *request); esp_err_t getConfigFile(httpd_req_t *request);
esp_err_t handleDelete(httpd_req_t *request, const api_FileDeleteRequest &req); esp_err_t handleDelete(httpd_req_t *request, const api_FileDeleteRequest &req);
esp_err_t handleEdit(httpd_req_t *request, JsonVariant &json); esp_err_t handleEdit(httpd_req_t *request, const api_FileEditRequest &req);
esp_err_t mkdir(httpd_req_t *request, JsonVariant &json); esp_err_t mkdir(httpd_req_t *request, const api_FileMkdirRequest &req);
} // namespace FileSystem } // namespace FileSystem
+23 -14
View File
@@ -124,15 +124,17 @@ esp_err_t handleDelete(httpd_req_t *request, const api_FileDeleteRequest &req) {
} }
} }
esp_err_t handleEdit(httpd_req_t *request, JsonVariant &json) { esp_err_t handleEdit(httpd_req_t *request, const api_FileEditRequest &req) {
if (json.is<JsonObject>()) { ESP_LOGI(TAG, "Editing file: %s", req.path);
const char *filename = json["file"].as<const char *>();
const char *content = json["content"].as<const char *>(); api_Response res = api_Response_init_zero;
ESP_LOGI(TAG, "Editing file: %s", filename); if (editFile(req.path, req.content->bytes, req.content->size)) {
return editFile(filename, content) ? WebServer::sendOk(request) res.status_code = 200;
: WebServer::sendError(request, 500, "Edit failed"); res.which_payload = api_Response_empty_message_tag;
return WebServer::sendProto(request, 200, res, api_Response_fields);
} else {
return WebServer::sendError(request, 500, "Edit failed");
} }
return WebServer::sendError(request, 400, "Invalid request");
} }
bool deleteFile(const char *filename) { return ESP_FS.remove(filename); } bool deleteFile(const char *filename) { return ESP_FS.remove(filename); }
@@ -167,19 +169,26 @@ std::string listFiles(const std::string &directory, bool isRoot) {
return output; return output;
} }
bool editFile(const char *filename, const char *content) { bool editFile(const char *filename, const uint8_t *content, size_t size) {
File file = ESP_FS.open(filename, FILE_WRITE); File file = ESP_FS.open(filename, FILE_WRITE);
if (!file) return false; if (!file) return false;
file.print(content); file.write(content, size);
file.close(); file.close();
return true; return true;
} }
esp_err_t mkdir(httpd_req_t *request, JsonVariant &json) { esp_err_t mkdir(httpd_req_t *request, const api_FileMkdirRequest &req) {
const char *path = json["path"].as<const char *>(); ESP_LOGI(TAG, "Creating directory: %s", req.path);
ESP_LOGI(TAG, "Creating directory: %s", path);
return ESP_FS.mkdir(path) ? WebServer::sendOk(request) : WebServer::sendError(request, 500, "mkdir failed"); api_Response res = api_Response_init_zero;
if (ESP_FS.mkdir(req.path)) {
res.status_code = 200;
res.which_payload = api_Response_empty_message_tag;
return WebServer::sendProto(request, 200, res, api_Response_fields);
} else {
return WebServer::sendError(request, 500, "mkdir failed");
}
} }
} // namespace FileSystem } // namespace FileSystem
+2 -4
View File
@@ -106,10 +106,8 @@ void setupServer() {
server.on("/api/config/*", HTTP_GET, [](httpd_req_t *request) { return FileSystem::getConfigFile(request); }); server.on("/api/config/*", HTTP_GET, [](httpd_req_t *request) { return FileSystem::getConfigFile(request); });
server.on("/api/files", HTTP_GET, [&](httpd_req_t *request) { return FileSystem::getFilesProto(request); }); server.on("/api/files", HTTP_GET, [&](httpd_req_t *request) { return FileSystem::getFilesProto(request); });
PROTO_ENDPOINT(server, "/api/files/delete", file_delete_request, FileSystem::handleDelete); PROTO_ENDPOINT(server, "/api/files/delete", file_delete_request, FileSystem::handleDelete);
server.on("/api/files/edit", HTTP_POST, PROTO_ENDPOINT(server, "/api/files/edit", file_edit_request, FileSystem::handleEdit);
[&](httpd_req_t *request, JsonVariant &json) { return FileSystem::handleEdit(request, json); }); PROTO_ENDPOINT(server, "/api/files/mkdir", file_mkdir_request, FileSystem::mkdir);
server.on("/api/files/mkdir", HTTP_POST,
[&](httpd_req_t *request, JsonVariant &json) { return FileSystem::mkdir(request, json); });
#if EMBED_WEBAPP #if EMBED_WEBAPP
mountStaticAssets(server); mountStaticAssets(server);
#endif #endif
+11
View File
@@ -84,6 +84,15 @@ message FileDeleteRequest {
string path = 1; string path = 1;
} }
message FileEditRequest {
string path = 1;
bytes content = 2;
}
message FileMkdirRequest {
string path = 1;
}
// ============================================================================= // =============================================================================
// REST API wrappers - used by HTTP endpoints // REST API wrappers - used by HTTP endpoints
// ============================================================================= // =============================================================================
@@ -98,6 +107,8 @@ message Request {
ServoSettingsRequest servo_settings_request = 21; ServoSettingsRequest servo_settings_request = 21;
FileListRequest file_list_request = 30; FileListRequest file_list_request = 30;
FileDeleteRequest file_delete_request = 31; FileDeleteRequest file_delete_request = 31;
FileEditRequest file_edit_request = 32;
FileMkdirRequest file_mkdir_request = 33;
} }
} }