Implemented user-update command for REST-API

This commit is contained in:
Jean Jacques Avril 2022-02-23 16:57:06 +01:00
parent f2fd5bc3b6
commit 74da018c99
7 changed files with 248 additions and 70 deletions

View File

@ -49,6 +49,7 @@ void Interface::render()
{ {
if (this->_keyboard->available()) if (this->_keyboard->available())
{ {
this->_inputTimeout = millis();
if (this->_keyboard->getLastChr() == 'X') if (this->_keyboard->getLastChr() == 'X')
{ {
this->_keyboard->clear(); this->_keyboard->clear();
@ -79,7 +80,12 @@ void Interface::render()
//this->setState(states::VALIDATE_PIN); //this->setState(states::VALIDATE_PIN);
} }
} }
if(this->_display_state==states::ENTER_PIN_ADD_NUM||this->_display_state==states::ENTER_PIN_START){
if(millis()-_inputTimeout>10000){
this->_keyboard->clear();
this->setState(states::ABORT);
}
}
if (this->_displayUpdate) if (this->_displayUpdate)
{ {
switch (this->_display_state) switch (this->_display_state)

View File

@ -12,6 +12,7 @@ private:
unsigned long _lastDisplayUpdate = 0; unsigned long _lastDisplayUpdate = 0;
unsigned long _displayTimer1 = 0; unsigned long _displayTimer1 = 0;
unsigned long _displayTimer2 = 0; unsigned long _displayTimer2 = 0;
unsigned long _inputTimeout = 0;
bool _displayUpdate = true; bool _displayUpdate = true;
String _username = ""; String _username = "";
unsigned int _scroll_index = 0; unsigned int _scroll_index = 0;

View File

@ -1,14 +1,74 @@
#include "UserDb.h" #include "UserDb.h"
#define DEBUG //#define DEBUG
using namespace userdb; using namespace userdb;
UserDb::UserDb(const char *filename) : filename(filename) UserDb::UserDb(const char *filename) : filename(filename)
{ {
} }
void UserDb::coursor_to_end_of_line(File &f)
{
while (f.available() && f.read() != '\n')
{
}
}
bool UserDb::remove_user(User &user) bool UserDb::remove_user(User &user)
{ {
return remove_user_by_line(filename, user.line); return update_db_line(filename, user.line);
}
bool UserDb::update_user(User *user)
{
return update_db_line(filename, user->line, user);;
}
bool UserDb::update_db_line(const char *file, linenumber line, User *replace)
{
const char *temp_file = ".tmp.csv";
File old_db = LittleFS.open(file, "r");
File new_db = LittleFS.open(temp_file, "w+");
linenumber curr_line = 0;
bool line_found = false;
while (old_db.available())
{
if (curr_line == line)
{
#ifdef DEBUG
Serial.println("Found line: " + String(line));
#endif
if (replace != nullptr)
{
String insert = replace->toCSVString();
#ifdef DEBUG
Serial.println("Inserting: " + insert);
#endif
if (new_db.position() > 0)
new_db.write('\n');
new_db.write(insert.c_str());
}
coursor_to_end_of_line(old_db);
line_found = true;
}
else
{
if (new_db.position() > 0)
new_db.write('\n');
old_db.sendUntil(new_db, '\n');
}
curr_line++;
}
old_db.close();
#ifdef DEBUG
Serial.println("NewDB:");
new_db.seek(0);
while (new_db.available())
new_db.sendAvailable(Serial);
#endif
new_db.close();
if (LittleFS.remove(file) && LittleFS.rename(temp_file, file))
Serial.println("UserDb updated");
else
Serial.println("Could not delete old userdb");
return line_found;
} }
bool UserDb::add_user(User &user) bool UserDb::add_user(User &user)
{ {
@ -36,8 +96,14 @@ User UserDb::user_by_pin(String cmp)
do do
{ {
User temp = *it; User temp = *it;
if (temp.match) if ((*it).match && (*it).enabled)
return temp; {
#ifdef DEBUG
Serial.println(" -> Found match");
#endif
it.close_file();
return *it;
}
++it; ++it;
} while (it.has_next()); } while (it.has_next());
return User{.enabled = false}; return User{.enabled = false};
@ -50,9 +116,14 @@ User UserDb::user_by_rfid(String cmp)
Iterator it = begin_with_filter(&cmp, USERATTRIBUTES::RFID_UID); Iterator it = begin_with_filter(&cmp, USERATTRIBUTES::RFID_UID);
do do
{ {
User temp = *it; if ((*it).match && (*it).enabled)
if (temp.match) {
return temp; #ifdef DEBUG
Serial.println(" -> Found match");
#endif
it.close_file();
return *it;
}
++it; ++it;
} while (it.has_next()); } while (it.has_next());
return User{.enabled = false}; return User{.enabled = false};
@ -71,9 +142,34 @@ User UserDb::user_by_uid(String cmp)
Iterator it = begin_with_filter(&cmp, USERATTRIBUTES::USER_ID); Iterator it = begin_with_filter(&cmp, USERATTRIBUTES::USER_ID);
do do
{ {
User temp = *it; if ((*it).match)
if (temp.match) {
return temp; #ifdef DEBUG
Serial.println(" -> Found match");
#endif
it.close_file();
return *it;
}
++it;
} while (it.has_next());
return User{.enabled = false};
}
User UserDb::user_by_line(linenumber cmp)
{
#ifdef DEBUG
Serial.println("Searching for user with line number " + String(cmp));
#endif
Iterator it = begin_with_filter(cmp, USERATTRIBUTES::LINE_ID);
do
{
if ((*it).match)
{
#ifdef DEBUG
Serial.println(" -> Found match");
#endif
it.close_file();
return (*it);
}
++it; ++it;
} while (it.has_next()); } while (it.has_next());
return User{.enabled = false}; return User{.enabled = false};

View File

@ -7,6 +7,7 @@
namespace userdb namespace userdb
{ {
typedef uint16_t linenumber;
enum USERATTRIBUTES enum USERATTRIBUTES
{ {
LINE_ID, LINE_ID,
@ -22,7 +23,7 @@ namespace userdb
{ {
// User(unsigned long new_uid): uid(new_uid){} // User(unsigned long new_uid): uid(new_uid){}
unsigned long uid = -1; unsigned long uid = -1;
uint32_t line; linenumber line = UINT16_MAX;
mutable String first_name; mutable String first_name;
mutable String last_name; mutable String last_name;
mutable String rfid_uid; mutable String rfid_uid;
@ -39,8 +40,9 @@ namespace userdb
{ {
return String(uid) + ";" + first_name + ";" + last_name + ";" + rfid_uid + ";" + user_pin + ";" + (enabled ? "1" : "0"); return String(uid) + ";" + first_name + ";" + last_name + ";" + rfid_uid + ";" + user_pin + ";" + (enabled ? "1" : "0");
} }
String toJSONString(){ String toJSONString()
return "{\"uid\":\""+String(uid) + "\",\"first_name\":\"" + first_name + "\",\"last_name\":\"" + last_name + "\",\"rfid_uid\":\"" + rfid_uid + "\",\"user_pin\":\"" + user_pin + "\",\"enabled\":" + (enabled ? "true" : "false")+"}"; {
return "{\"uid\":\"" + String(uid) + "\",\"first_name\":\"" + first_name + "\",\"last_name\":\"" + last_name + "\",\"rfid_uid\":\"" + rfid_uid + "\",\"user_pin\":\"" + user_pin + "\",\"enabled\":" + (enabled ? "true" : "false") + ",\"line\":\"" + line + "\"}";
} }
}; };
@ -58,15 +60,16 @@ namespace userdb
User user_by_rfid(String); User user_by_rfid(String);
User user_by_uid(unsigned long); User user_by_uid(unsigned long);
User user_by_uid(String cmp); User user_by_uid(String cmp);
bool drop(){ User user_by_line(linenumber cmp);
bool drop()
{
LittleFS.remove(filename); LittleFS.remove(filename);
File n = LittleFS.open(filename,"a"); File n = LittleFS.open(filename, "a");
n.close(); n.close();
return true; return true;
} }
static inline User read_csv_line(File &instream, uint32_t &line, String *match, USERATTRIBUTES attr) static inline User read_csv_line(User &res, File &instream, linenumber &line, String *match, USERATTRIBUTES attr)
{ {
User res;
String uid = instream.readStringUntil(';'); String uid = instream.readStringUntil(';');
if (attr == USER_ID && !uid.equals(*match)) if (attr == USER_ID && !uid.equals(*match))
return res; return res;
@ -84,39 +87,11 @@ namespace userdb
res.match = true; res.match = true;
return res; return res;
} }
bool remove_user(User &user);
static bool remove_user_by_line(const char *file, uint32_t line)
{
const char *temp_file = ".tmp.csv";
File old_db = LittleFS.open(file, "r");
File new_db = LittleFS.open(temp_file, "w+");
uint32_t curr_line = 0;
while (old_db.available()) void inline static coursor_to_end_of_line(File &f);
{ bool remove_user(User &user);
if (curr_line == line) bool update_user(User *user);
while (old_db.available()&&old_db.read() != '\n') static bool update_db_line(const char *file, linenumber line, User *replace = nullptr);
{
}
else
{
if (new_db.position() > 0)
new_db.write('\n');
old_db.sendUntil(new_db, '\n');
}
curr_line++;
}
old_db.close();
#ifdef DEBUG
new_db.seek(0);
while (new_db.available())
new_db.sendAvailable(Serial);
#endif
new_db.close();
LittleFS.remove(file);
LittleFS.rename(temp_file, file);
return false;
}
bool add_user(User &user); bool add_user(User &user);
struct Iterator struct Iterator
{ {
@ -129,6 +104,17 @@ namespace userdb
Iterator(File data) : Iterator(data, nullptr, NONE) Iterator(File data) : Iterator(data, nullptr, NONE)
{ {
} }
Iterator(File data, linenumber m, USERATTRIBUTES attr) : db_file(data), filter_attr(attr), matchline(m)
{
if (!data)
{
Serial.println("Failed to open userdb while creating an iterator");
}
else
{
next();
}
}
Iterator(File data, String *m, USERATTRIBUTES attr) : db_file(data), filter_attr(attr), match(m) Iterator(File data, String *m, USERATTRIBUTES attr) : db_file(data), filter_attr(attr), match(m)
{ {
#ifdef DEBUG #ifdef DEBUG
@ -160,6 +146,9 @@ namespace userdb
} }
~Iterator() ~Iterator()
{ {
close_file();
}
void close_file(){
db_file.close(); db_file.close();
} }
bool has_next() bool has_next()
@ -170,7 +159,16 @@ namespace userdb
{ {
if (db_file.available()) if (db_file.available())
{ {
current = read_csv_line(db_file, line, match, filter_attr); if (filter_attr == USERATTRIBUTES::LINE_ID)
{
if (matchline == line)
{
current = read_csv_line(current, db_file, line, match, filter_attr);
current.match=true;
}
}
else
current = read_csv_line(current, db_file, line, match, filter_attr);
// Position stream pointer to beginning of the next line // Position stream pointer to beginning of the next line
while (db_file.available()) while (db_file.available())
{ {
@ -195,9 +193,11 @@ namespace userdb
USERATTRIBUTES filter_attr; USERATTRIBUTES filter_attr;
String *match; String *match;
bool available = true; bool available = true;
uint32_t line = 0; linenumber line = 0;
linenumber matchline = 0;
}; };
Iterator begin() { return Iterator(LittleFS.open(filename, "r")); } Iterator begin() { return Iterator(LittleFS.open(filename, "r")); }
Iterator begin_with_filter(String *match, USERATTRIBUTES attr) { return Iterator(LittleFS.open(filename, "r"), match, attr); } Iterator begin_with_filter(String *match, USERATTRIBUTES attr) { return Iterator(LittleFS.open(filename, "r"), match, attr); }
Iterator begin_with_filter(linenumber match, USERATTRIBUTES attr) { return Iterator(LittleFS.open(filename, "r"), match, attr); }
}; };
} }

View File

@ -13,19 +13,23 @@ bool WebConsole::init(userdb::UserDb *userdb)
_server = new ESP8266WebServer(80); _server = new ESP8266WebServer(80);
this->userdb = userdb; this->userdb = userdb;
_server->begin(); _server->begin();
_server->on("/userdb", HTTPMethod::HTTP_DELETE, std::bind(&WebConsole::_dropUserDb, this)); _server->on("/api/userdb", HTTPMethod::HTTP_DELETE, std::bind(&WebConsole::_dropUserDb, this));
_server->on("/userdb", HTTPMethod::HTTP_GET, std::bind(&WebConsole::_getUserDb, this)); _server->on("/api/userdb", HTTPMethod::HTTP_GET, std::bind(&WebConsole::_getUserDb, this));
_server->on("/rfid", std::bind(&WebConsole::_catchRFID, this)); _server->on("/api/rfid", std::bind(&WebConsole::_catchRFID, this));
_server->on(UriBraces("/user/{}"), HTTPMethod::HTTP_DELETE, std::bind(&WebConsole::_deleteUser, this)); _server->on("/api/debug/printfile", std::bind(&WebConsole::_print_db_raw, this));
_server->on(UriBraces("/user/{}"), HTTPMethod::HTTP_GET, std::bind(&WebConsole::_getUser, this)); _server->on(UriBraces("/api/user/{}"), HTTPMethod::HTTP_DELETE, std::bind(&WebConsole::_deleteUser, this));
_server->on(UriBraces("/user/{}"), HTTPMethod::HTTP_PUT, std::bind(&WebConsole::_createUser, this)); _server->on(UriBraces("/api/user/{}"), HTTPMethod::HTTP_GET, std::bind(&WebConsole::_getUser, this));
_server->on(UriBraces("/get/{}"), std::bind(&WebConsole::_deleteUser, 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/{}"), std::bind(&WebConsole::_deleteUser, this));
//_server->on("/bypin",std::bind(&WebConsole::_findPin,this)); //_server->on("/bypin",std::bind(&WebConsole::_findPin,this));
_server->onNotFound(std::bind(&WebConsole::_handleStatic, this)); _server->serveStatic("/", LittleFS, "/static/index.html");
//_server->onNotFound(std::bind(&WebConsole::_handleStatic, this));
return true; return true;
} }
void WebConsole::attachRfid(Rfid *rfid){ void WebConsole::attachRfid(Rfid *rfid)
this->rfid=rfid; {
this->rfid = rfid;
} }
void WebConsole::serve() void WebConsole::serve()
{ {
@ -37,7 +41,8 @@ void WebConsole::serve()
} }
_server->handleClient(); _server->handleClient();
} }
bool WebConsole::isInterceptingRfid(){ bool WebConsole::isInterceptingRfid()
{
return catch_rfid; return catch_rfid;
} }
void WebConsole::_handleStatic() void WebConsole::_handleStatic()
@ -99,6 +104,7 @@ void WebConsole::_deleteUser()
_server->send(500, "text/json", "{\"error\":\"User not found.\"}"); _server->send(500, "text/json", "{\"error\":\"User not found.\"}");
} }
} }
void WebConsole::_getUser() void WebConsole::_getUser()
{ {
if (userdb == nullptr) if (userdb == nullptr)
@ -117,6 +123,57 @@ void WebConsole::_getUser()
_server->send(500, "text/json", "{\"error\":\"User not found.\"}"); _server->send(500, "text/json", "{\"error\":\"User not found.\"}");
} }
} }
void WebConsole::_updateUser()
{
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>();
bool res = userdb->update_user(&updated);
_server->send(200, "text/json", updated.toJSONString().c_str());
}
void WebConsole::_createUser() void WebConsole::_createUser()
{ {
userdb::User created; userdb::User created;
@ -160,16 +217,23 @@ void WebConsole::_dropUserDb()
} }
void WebConsole::_catchRFID() void WebConsole::_catchRFID()
{ {
if (rfid == nullptr)
{
_server->send(500, "text/json", "{\"error\":\"RFID not attached.\"}");
return;
}
if (catch_rfid_updated) if (catch_rfid_updated)
{ {
String response = "{\"rfid_uid\":\"" + rfid_buffer + "\"}"; String response = "{\"rfid_uid\":\"" + rfid_buffer + "\"}";
_server->send(500, "text/json", response); _server->send(500, "text/json", response);
catch_rfid_updated = false; catch_rfid_updated = false;
} }
else if(catch_rfid){ else if (catch_rfid)
{
_server->send(500, "text/json", "{\"ok\":\"already activated\"}"); _server->send(500, "text/json", "{\"ok\":\"already activated\"}");
} }
else{ else
{
catch_rfid = true; catch_rfid = true;
_server->send(500, "text/json", "{\"ok\":\"now activated\"}"); _server->send(500, "text/json", "{\"ok\":\"now activated\"}");
} }

View File

@ -16,20 +16,31 @@ namespace webconsole
public: public:
WebConsole(); WebConsole();
~WebConsole(); ~WebConsole();
bool init(userdb::UserDb *userdb); bool init(userdb::UserDb *userdb);
void attachRfid(Rfid *rfid); void attachRfid(Rfid *rfid);
void serve(); void serve();
bool isInterceptingRfid(); bool isInterceptingRfid();
private: private:
void _handleStatic(); void _handleStatic();
void _getUserDb(); void _getUserDb();
void _deleteUser(); void _deleteUser();
void _getUser(); void _getUser();
void _updateUser();
void _createUser(); void _createUser();
void _dropUserDb(); void _dropUserDb();
void _catchRFID(); void _catchRFID();
ESP8266WebServer* _server; void _print_db_raw()
{
File f = LittleFS.open("userdb.csv", "r");
while (f.available()){
f.sendAvailable(Serial);
}
_server->send(200,"text/plain","printed to serial");
f.close();
}
ESP8266WebServer *_server;
userdb::UserDb *userdb = nullptr; userdb::UserDb *userdb = nullptr;
bool catch_rfid = false; bool catch_rfid = false;
bool catch_rfid_updated = false; bool catch_rfid_updated = false;

View File

@ -61,7 +61,7 @@ void loop()
if(web.isInterceptingRfid()&&iface.getState()==0){ if(web.isInterceptingRfid()&&iface.getState()==0){
iface.showMessage("WebUI connected", "Awaiting RFID", 3000); iface.showMessage("WebUI connected", "Awaiting RFID", 3000);
} }
else if (iface.pinAvailable()) else if (iface.pinAvailable()&&iface.getState()!=Interface::GREET)
{ {
unsigned long delta = millis(); unsigned long delta = millis();
login_user = userdatabase.user_by_pin(iface.getPin()); login_user = userdatabase.user_by_pin(iface.getPin());
@ -72,7 +72,7 @@ void loop()
return; return;
} }
} }
else if (rfid.available()) else if (rfid.available()&&iface.getState()!=Interface::GREET)
{ {
unsigned long delta = millis(); unsigned long delta = millis();
login_user = userdatabase.user_by_rfid(rfid.getID()); login_user = userdatabase.user_by_rfid(rfid.getID());