#include "WebConsole.h" using namespace webconsole; WebConsole::WebConsole() { } WebConsole::~WebConsole() { _server->close(); delete (_server); } bool WebConsole::init(Config *config, userdb::UserDb *userdb) { this->userdb = userdb; this->_config = config; this->userdb = userdb; // Wifi Setup this->_dnsServer = new DNSServer; WiFi.mode(WIFI_AP); Serial.print("\t1. Network config... "); Serial.println(WiFi.softAPConfig(_config->ip, _config->gw, _config->subnet) ? "Ready" : "Failed!"); Serial.print("\t2. DNS config... "); Serial.println(_dnsServer->start(53, "*", _config->ip) ? "Ready" : "Failed!"); Serial.print("\t3 AP setup SSID:\"" + String(_config->SSID) + "\"... "); if (strlen(_config->PASS) > 0) Serial.println(WiFi.softAP(_config->SSID, _config->PASS) ? "Ready" : "Failed!"); else Serial.println(WiFi.softAP(_config->SSID) ? "Ready" : "Failed!"); WiFi.hostname("Doorlock"); Serial.println("Please connect via http://" + WiFi.softAPIP().toString() + "/"); // Webserver Setup this->_server = new ESP8266WebServer(80); const char *headerkeys[] = {"Authentification"}; size_t headerkeyssize = sizeof(headerkeys) / sizeof(char *); _server->collectHeaders(headerkeys, headerkeyssize); _server->begin(); _server->on("/api/auth", HTTPMethod::HTTP_POST, std::bind(&WebConsole::_auth, this)); _server->on("/api/userdb", HTTPMethod::HTTP_DELETE, std::bind(&WebConsole::_dropUserDb, this)); _server->on("/api/userdb", HTTPMethod::HTTP_GET, std::bind(&WebConsole::_getUserDb, this)); _server->on("/api/rfid", HTTPMethod::HTTP_GET, std::bind(&WebConsole::_catchRFID, this)); _server->on("/api/debug/printfile", std::bind(&WebConsole::_print_db_raw, this)); _server->on(UriBraces("/api/user/{}"), HTTPMethod::HTTP_DELETE, std::bind(&WebConsole::_deleteUser, this)); _server->on(UriBraces("/api/user/{}"), HTTPMethod::HTTP_GET, std::bind(&WebConsole::_getUser, this)); _server->on(UriBraces("/api/user/{}"), HTTPMethod::HTTP_PUT, std::bind(&WebConsole::_createUser, this)); _server->on(UriBraces("/api/user/{}"), HTTPMethod::HTTP_POST, std::bind(&WebConsole::_updateUser, this)); _server->on(UriBraces("/api/config"), HTTPMethod::HTTP_POST, std::bind(&WebConsole::_settings, this)); _server->serveStatic("/", LittleFS, "/s/"); _server->onNotFound(std::bind(&WebConsole::_handleUnknown, this)); return true; } void WebConsole::attachRfid(Rfid *rfid) { this->rfid = rfid; } void WebConsole::serve() { _dnsServer->processNextRequest(); if (catch_rfid) { if (millis() - catch_rfid_millis > 2000) { catch_rfid = 0; } else if (catch_rfid > 1 && rfid != nullptr && rfid->available()) { rfid_buffer = rfid->getID(); catch_rfid = 1; } } _server->handleClient(); } uint8_t WebConsole::isInterceptingRfid() { if (catch_rfid == 3) { catch_rfid = 2; return 3; } return catch_rfid; } bool WebConsole::_isAuth() { if (_server->method() == HTTPMethod::HTTP_OPTIONS) { _server->send(204); return false; } if (!_server->hasHeader("Authentification")) { _server->send(401, "text/plain", "Error 401: Unauthorized (missing auth token)"); return false; } const char *token = _server->header("Authentification").c_str(); bool res = auth.isAuth(token); if (!res) _server->send(401, "text/plain", "Error 401: Unauthorized (missing auth token)"); return res; } void WebConsole::_auth() { _sendCORS(); String action = _server->arg("action"); if (action.equals("check")) { const char *token = _server->arg("token").c_str(); bool res = auth.isAuth(token); _server->send(200, "text/plain", res ? "valid" : "invalid"); } else if (action.equals("login")) { char *token = auth.login(_server->arg("username"), _server->arg("password")); if (token == nullptr) _server->send(401, "text/plain", "login_failed"); else _server->send(200, "text/plain", token); } else if (action.equals("logout")) { const char *token = _server->arg("token").c_str(); bool res = auth.logout(token); _server->send(200, "text/plain", res ? "success" : "failed"); } else if (action.equals("update")) { // if (!_isAuth()) // return; bool res = auth.setAuth(_server->arg("username"), _server->arg("password")); _server->send(200, "text/plain", res ? "success" : "failed"); } else _server->send(404, "text/plain", "unknown action"); } void WebConsole::_settings() { _sendCORS(); if (!_isAuth()) return; String action = _server->arg("action"); if (action.equals("update")) { uint8_t error = 0b00000000; uint8_t sucess = 0b00000000; if (_server->hasArg("SSID")) { _config->setSSID(_server->arg("SSID").c_str()); int length = strlen(_config->SSID); if (length == 0 || length > 31) error |= 0b00000001; else sucess |= 0b00000001; } if (_server->hasArg("PASS")) { _config->setSSID(_server->arg("PASS").c_str()); int length = strlen(_config->SSID); if (length == 0 || length > 31) error |= 0b00000010; else sucess |= 0b00000010; } if (_server->hasArg("ip")) { IPAddress temp; if (temp.fromString(_server->arg("ip"))) { _config->ip = temp.v4(); sucess |= 0b00000100; } else error |= 0b00000100; } if (_server->hasArg("subnet")) { IPAddress temp; if (temp.fromString(_server->arg("subnet"))) { _config->subnet = temp.v4(); sucess |= 0b00001000; } else error |= 0b00001000; } if (_server->hasArg("gw")) { IPAddress temp; if (temp.fromString(_server->arg("gw"))) { _config->gw = temp.v4(); sucess |= 0b00010000; } else error |= 0b00010000; } if (_server->hasArg("mode")) { _config->mode = _server->arg("mode").toInt(); if (_config->mode >= 0 && _config->mode <= 1) sucess |= 0b00100000; else error |= 0b00100000; } if (_server->hasArg("fail_timeout")) { _config->fail_timeout = _server->arg("fail_timeout").toInt(); if (_config->fail_timeout >= 0 && _config->fail_timeout <= 250) sucess |= 0b01000000; else error |= 0b01000000; } if (_server->hasArg("hold_time")) { _config->hold_time = _server->arg("hold_time").toInt(); if (_config->hold_time > 0 && _config->hold_time <= 250) sucess |= 0b10000000; else error |= 0b10000000; } if (error == 0 && sucess > 0) { // Save _config->saveBin(); _server->send(200, "text/json", "{\"status\":\"ok\", \"sucess\":\"" + String(sucess) + "\"}"); } else if (error > 0) { // Rollback _config->loadBin(); _server->send(200, "text/json", "{\"status\":\"failed\", \"sucess\":\"" + String(sucess) + "\", \"error\":\"" + String(error) + "\"}"); } else _server->send(200, "text/json", "{\"status\":\"noting_todo\"}"); } else if (action.equals("get")) { _server->send(200, "text/json", "{\"SSID\":\"" + String(_config->SSID) + "\", \"PASS\":\"" + String(_config->PASS) + "\", \"ip\":\"" + IPAddress(_config->ip).toString() + "\", \"subnet\":\"" + IPAddress(_config->subnet).toString() + "\", \"gw\":\"" + IPAddress(_config->gw).toString() + "\", \"mode\":\"" + String(_config->mode) + "\", \"fail_timeout\":\"" + String(_config->fail_timeout) + "\", \"hold_time\":\"" + String(_config->hold_time) + "\"}"); } else if (action.equals("apply")) { _server->send(200, "text/json", "{\"status\":\"restarting\"}"); ESP.restart(); } else _server->send(404, "text/plain", "unknown action"); } void WebConsole::_sendCORS() { _server->sendHeader("Access-Control-Allow-Origin", "*"); _server->sendHeader("Access-Control-Max-Age", "10000"); _server->sendHeader("Access-Control-Allow-Methods", "PUT,POST,GET,OPTIONS,DELETE"); _server->sendHeader("Access-Control-Allow-Headers", "*"); } void WebConsole::_handleUnknown() { _sendCORS(); if (_server->method() == HTTP_OPTIONS) { _server->send(204); } else { File src = LittleFS.open("s/index.html", "r"); _server->streamFile(src, "text/html"); src.close(); } } void WebConsole::_getUserDb() { _sendCORS(); if (!_isAuth()) return; File src = LittleFS.open("userdb.csv", "r"); if (src) { _server->streamFile(src, "text/csv"); } src.close(); } void WebConsole::_deleteUser() { _sendCORS(); if (!_isAuth()) return; if (userdb == nullptr) { _server->send(500, "text/json", "{\"error\":\"UserDb not initialized\"}"); return; } String uid = _server->pathArg(0); userdb::User del = userdb->user_by_uid(uid); if (del.match == true) { userdb->remove_user(del); _server->send(200, "text/plain", del.toJSONString()); } else { _server->send(500, "text/json", "{\"error\":\"User not found.\"}"); } } void WebConsole::_getUser() { _sendCORS(); if (!_isAuth()) return; if (userdb == nullptr) { _server->send(500, "text/json", "{\"error\":\"UserDb not initialized\"}"); return; } String uid = _server->pathArg(0); userdb::User res = userdb->user_by_uid(uid); if (res.match == true) { _server->send(200, "text/json", res.toJSONString()); } else { _server->send(500, "text/json", "{\"error\":\"User not found.\"}"); } } void WebConsole::_updateUser() { _sendCORS(); if (!_isAuth()) return; userdb::User updated; String body = _server->arg("plain"); const int capacity = 256; StaticJsonDocument doc; DeserializationError err = deserializeJson(doc, body); if (err.code() != err.Ok) { Serial.println(err.c_str()); Serial.println(body); String response = "{\"error\":\""; response += err.c_str(); response += "\"}"; _server->send(500, "text/json", response); return; } if (doc.containsKey("line")) { updated = userdb->user_by_line(doc["line"].as()); } if (!updated.match && doc.containsKey("uid")) { updated = userdb->user_by_uid(doc["uid"].as()); } if (!updated.match && !_server->pathArg(0).isEmpty()) { updated = userdb->user_by_uid(_server->pathArg(0).toInt()); } if (updated.match == false) { _server->send(500, "text/json", "{\"error\":\"user could not be found.\"}"); return; } // created.line = doc["line"].as(); if (doc.containsKey("uid")) updated.uid = doc["uid"].as(); if (doc.containsKey("first_name")) updated.first_name = doc["first_name"].as(); if (doc.containsKey("last_name")) updated.last_name = doc["last_name"].as(); if (doc.containsKey("rfid_uid")) updated.rfid_uid = doc["rfid_uid"].as(); if (doc.containsKey("user_pin")) updated.user_pin = doc["user_pin"].as(); if (doc.containsKey("enabled")) updated.enabled = doc["enabled"].as(); userdb->update_user(&updated); _server->send(200, "text/json", updated.toJSONString().c_str()); } void WebConsole::_createUser() { _sendCORS(); if (!_isAuth()) return; userdb::User created; String body = _server->arg("plain"); const int capacity = 1024; StaticJsonDocument doc; DeserializationError err = deserializeJson(doc, body); if (err.code() != err.Ok) { Serial.println(err.c_str()); Serial.println(body); String response = "{\"error\":\""; response += err.c_str(); response += "\"}"; _server->send(500, "text/json", response); return; } if (!_server->pathArg(0).isEmpty()) created.uid = _server->pathArg(0).toInt(); else if (!doc["uid"].isNull()) created.uid = doc["uid"].as(); else { _server->send(500, "text/json", "{\"error\":\"UserId not provided.\"}"); return; } created.first_name = doc["first_name"].as(); created.last_name = doc["last_name"].as(); created.rfid_uid = doc["rfid_uid"].as(); created.user_pin = doc["user_pin"].as(); created.enabled = doc["enabled"].as(); userdb->add_user(created); _server->send(200, "text/json", created.toJSONString()); } void WebConsole::_dropUserDb() { _sendCORS(); if (!_isAuth()) return; if (userdb->drop()) _server->send(500, "text/json", "{\"ok\":\"UserDb dropped.\"}"); else _server->send(500, "text/json", "{\"error\":\"UserDb could not be dropped.\"}"); } void WebConsole::_catchRFID() { _sendCORS(); if (!_isAuth()) return; if (rfid == nullptr) { _server->send(500, "text/json", "{\"error\":\"RFID not attached.\",\"state\":\"" + String(catch_rfid) + "\"}"); return; } if (catch_rfid == 1) { String response = "{\"rfid_uid\":\"" + rfid_buffer + "\",\"state\":\"" + String(catch_rfid) + "\"}"; _server->send(200, "text/json", response); catch_rfid = 0; } else { catch_rfid = 3; // 3 - Start listening catch_rfid_millis = millis(); _server->send(200, "text/json", "{\"ok\":\"listening\",\"state\":\"" + String(catch_rfid) + "\"}"); } } void WebConsole::_updateAdmin() { }