diff --git a/app/src/routes/menu.svelte b/app/src/routes/menu.svelte index e16a10e..d7dc740 100644 --- a/app/src/routes/menu.svelte +++ b/app/src/routes/menu.svelte @@ -5,6 +5,7 @@ import Settings from '~icons/mdi/settings'; import MdiController from '~icons/mdi/controller'; import Health from '~icons/mdi/stethoscope'; + import Folder from '~icons/mdi/folder-outline'; import Update from '~icons/mdi/reload'; import WiFi from '~icons/mdi/wifi'; import Router from '~icons/mdi/router'; @@ -111,6 +112,13 @@ feature: true, }, + { + title: 'File System', + icon: Folder, + href: '/system/filesystem', + feature: true, + + }, { title: 'System Metrics', icon: Metrics, diff --git a/app/src/routes/system/filesystem/+page.svelte b/app/src/routes/system/filesystem/+page.svelte new file mode 100644 index 0000000..96f6ac0 --- /dev/null +++ b/app/src/routes/system/filesystem/+page.svelte @@ -0,0 +1,10 @@ + + +
+ +
diff --git a/app/src/routes/system/filesystem/+page.ts b/app/src/routes/system/filesystem/+page.ts new file mode 100644 index 0000000..d6444c1 --- /dev/null +++ b/app/src/routes/system/filesystem/+page.ts @@ -0,0 +1,5 @@ +import type { PageLoad } from './$types'; + +export const load = (async () => { + return { title: 'File System' }; +}) satisfies PageLoad; diff --git a/app/src/routes/system/filesystem/File.svelte b/app/src/routes/system/filesystem/File.svelte new file mode 100644 index 0000000..e72d78a --- /dev/null +++ b/app/src/routes/system/filesystem/File.svelte @@ -0,0 +1,7 @@ + + +{name} diff --git a/app/src/routes/system/filesystem/FileSystem.svelte b/app/src/routes/system/filesystem/FileSystem.svelte new file mode 100644 index 0000000..34f70eb --- /dev/null +++ b/app/src/routes/system/filesystem/FileSystem.svelte @@ -0,0 +1,24 @@ + + + + File System +
+ {#await getFiles()} + + {:then files} + + {/await} +
+
\ No newline at end of file diff --git a/app/src/routes/system/filesystem/Folder.svelte b/app/src/routes/system/filesystem/Folder.svelte new file mode 100644 index 0000000..dc6f1ad --- /dev/null +++ b/app/src/routes/system/filesystem/Folder.svelte @@ -0,0 +1,36 @@ + + + + +{#if expanded} + +{/if} diff --git a/esp32/lib/ESP32-sveltekit/ESP32SvelteKit.cpp b/esp32/lib/ESP32-sveltekit/ESP32SvelteKit.cpp index e4f6969..18f77d2 100644 --- a/esp32/lib/ESP32-sveltekit/ESP32SvelteKit.cpp +++ b/esp32/lib/ESP32-sveltekit/ESP32SvelteKit.cpp @@ -52,7 +52,8 @@ ESP32SvelteKit::ESP32SvelteKit(PsychicHttpServer *server, unsigned int numberEnd #endif _restartService(server, &_securitySettingsService), _factoryResetService(server, &ESPFS, &_securitySettingsService), - _systemStatus(server, &_securitySettingsService) { + _systemStatus(server, &_securitySettingsService), + _fileExplorer(server, &_securitySettingsService){ } void ESP32SvelteKit::begin() { @@ -178,8 +179,8 @@ void ESP32SvelteKit::startServices() { #if FT_ENABLED(FT_BATTERY) _batteryService.begin(); #endif - _taskManager.begin(); + _fileExplorer.begin(); } void ESP32SvelteKit::_loop() { diff --git a/esp32/lib/ESP32-sveltekit/ESP32SvelteKit.h b/esp32/lib/ESP32-sveltekit/ESP32SvelteKit.h index 2e4e746..e65fd09 100644 --- a/esp32/lib/ESP32-sveltekit/ESP32SvelteKit.h +++ b/esp32/lib/ESP32-sveltekit/ESP32SvelteKit.h @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -153,6 +154,11 @@ public: return &_taskManager; } + FileExplorer *getFileExplorer() + { + return &_fileExplorer; + } + void factoryReset() { _factoryResetService.factoryReset(); @@ -209,6 +215,7 @@ private: FactoryResetService _factoryResetService; SystemStatus _systemStatus; TaskManager _taskManager; + FileExplorer _fileExplorer; String _appName = APP_NAME; diff --git a/esp32/lib/ESP32-sveltekit/FileExplorerService.h b/esp32/lib/ESP32-sveltekit/FileExplorerService.h new file mode 100644 index 0000000..3194a9f --- /dev/null +++ b/esp32/lib/ESP32-sveltekit/FileExplorerService.h @@ -0,0 +1,72 @@ +#ifndef FileExplorer_h +#define FileExplorer_h + +#include +#include +#include + +#define FILE_EXPLORER_SERVICE_PATH "/api/files/list" + +class FileExplorer +{ + public: + FileExplorer(PsychicHttpServer *server, SecurityManager *securityManager) + : _server(server), _securityManager(securityManager) + { + } + + void begin() + { + _server->on(FILE_EXPLORER_SERVICE_PATH, HTTP_GET, + _securityManager->wrapRequest(std::bind(&FileExplorer::explore, this, std::placeholders::_1), + AuthenticationPredicates::IS_AUTHENTICATED)); + + ESP_LOGV("APStatus", "Registered GET endpoint: %s", FILE_EXPLORER_SERVICE_PATH); + } + + private: + PsychicHttpServer *_server; + SecurityManager *_securityManager; + esp_err_t explore(PsychicRequest *request) + { + return request->reply(200, "application/json", listFiles("/").c_str()); + } + + String listFiles(const String &directory, bool isRoot = true) + { + File root = ESPFS.open(directory.startsWith("/") ? directory : "/" + directory); + if (!root.isDirectory()) + { + return ""; + } + + File file = root.openNextFile(); + String output = isRoot ? "{ \"root\": {" : "{"; + + while (file) + { + if (file.isDirectory()) + { + output += "\"" + String(file.name()) + "\": " + listFiles(file.name(), false) + ", "; + } + else + { + output += "\"" + String(file.name()) + "\": " + String(file.size()) + ", "; + } + file = root.openNextFile(); + } + + if (output.endsWith(", ")) + { + output.remove(output.length() - 2); + } + output += "}"; + if (isRoot) + { + output += "}"; + } + return output; + } +}; + +#endif // end FileExplorer_h