#include static const char *TAG = "FileService"; namespace FileSystem { PsychicUploadHandler *uploadHandler; class Initializer { public: Initializer() { uploadHandler = new PsychicUploadHandler(); uploadHandler->onUpload([](PsychicRequest *request, const String &filename, uint64_t index, uint8_t *data, size_t len, bool last) -> esp_err_t { return uploadFile(request, std::string(filename.c_str()), index, data, len, last); }); uploadHandler->onRequest([](PsychicRequest *request) { return request->reply(200); }); } }; static Initializer initializer; esp_err_t getFiles(PsychicRequest *request) { return request->reply(200, "application/json", listFiles("/").c_str()); } esp_err_t getConfigFile(PsychicRequest *request) { String path = "/config" + request->uri().substring(11); if (!ESP_FS.exists(path)) { return request->reply(404, "text/plain", "File not found"); } File file = ESP_FS.open(path, "r"); if (!file) { return request->reply(500, "text/plain", "Failed to open file"); } String content = file.readString(); file.close(); return request->reply(200, "application/json", content.c_str()); } esp_err_t handleDelete(PsychicRequest *request, JsonVariant &json) { if (json.is()) { const char *filename = json["file"].as(); ESP_LOGI(TAG, "Deleting file: %s", filename); return deleteFile(filename) ? request->reply(200) : request->reply(500); } return request->reply(400); } esp_err_t handleEdit(PsychicRequest *request, JsonVariant &json) { if (json.is()) { const char *filename = json["file"].as(); const char *content = json["content"].as(); ESP_LOGI(TAG, "Editing file: %s", filename); return editFile(filename, content) ? request->reply(200) : request->reply(500); } return request->reply(400); } /* Helpers */ bool deleteFile(const char *filename) { return ESP_FS.remove(filename); } std::string listFiles(const std::string &directory, bool isRoot) { File root = ESP_FS.open(directory.find("/") == 0 ? directory.c_str() : ("/" + directory).c_str()); if (!root.isDirectory()) return "{}"; File file = root.openNextFile(); if (!file) { return isRoot ? "{ \"root\": {} }" : "{}"; } std::string output = isRoot ? "{ \"root\": {" : "{"; while (file) { std::string name = std::string(file.name()); if (file.isDirectory()) { output += "\"" + name + "\": " + listFiles(name, false); } else { output += "\"" + name + "\": " + std::to_string(file.size()); } File next = root.openNextFile(); if (next) output += ", "; file = next; } output += "}"; if (isRoot) output += "}"; return output; } esp_err_t uploadFile(PsychicRequest *request, const std::string &filename, uint64_t index, uint8_t *data, size_t len, bool last) { File file; std::string path = "/www/" + filename; ESP_LOGI(TAG, "Writing %d/%d bytes to: %s\n", (int)index + (int)len, request->contentLength(), path.c_str()); if (last) ESP_LOGI(TAG, "%s is finished. Total bytes: %d\n", path.c_str(), (int)index + (int)len); file = ESP_FS.open(path.c_str(), !index ? FILE_WRITE : FILE_APPEND); if (!file) { ESP_LOGE(TAG, "Failed to open file"); return ESP_FAIL; } if (!file.write(data, len)) { ESP_LOGE(TAG, "Write failed"); return ESP_FAIL; } return ESP_OK; } bool editFile(const char *filename, const char *content) { File file = ESP_FS.open(filename, FILE_WRITE); if (!file) return false; file.print(content); file.close(); return true; } esp_err_t mkdir(PsychicRequest *request, JsonVariant &json) { const char *path = json["path"].as(); ESP_LOGI(TAG, "Creating directory: %s", path); return ESP_FS.mkdir(path) ? request->reply(200) : request->reply(500); } } // namespace FileSystem