ESP8266-Firmware/src/WebConsole.cpp
2022-03-23 19:20:26 +01:00

451 lines
14 KiB
C++

#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<capacity> 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<userdb::linenumber>());
}
if (!updated.match && doc.containsKey("uid"))
{
updated = userdb->user_by_uid(doc["uid"].as<unsigned long>());
}
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<uint32_t>();
if (doc.containsKey("uid"))
updated.uid = doc["uid"].as<unsigned long>();
if (doc.containsKey("first_name"))
updated.first_name = doc["first_name"].as<String>();
if (doc.containsKey("last_name"))
updated.last_name = doc["last_name"].as<String>();
if (doc.containsKey("rfid_uid"))
updated.rfid_uid = doc["rfid_uid"].as<String>();
if (doc.containsKey("user_pin"))
updated.user_pin = doc["user_pin"].as<String>();
if (doc.containsKey("enabled"))
updated.enabled = doc["enabled"].as<bool>();
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<capacity> 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<unsigned long>();
else
{
_server->send(500, "text/json", "{\"error\":\"UserId not provided.\"}");
return;
}
created.first_name = doc["first_name"].as<String>();
created.last_name = doc["last_name"].as<String>();
created.rfid_uid = doc["rfid_uid"].as<String>();
created.user_pin = doc["user_pin"].as<String>();
created.enabled = doc["enabled"].as<bool>();
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()
{
}