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())
{
this->_inputTimeout = millis();
if (this->_keyboard->getLastChr() == 'X')
{
this->_keyboard->clear();
@ -79,7 +80,12 @@ void Interface::render()
//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)
{
switch (this->_display_state)

View File

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

View File

@ -1,14 +1,74 @@
#include "UserDb.h"
#define DEBUG
//#define DEBUG
using namespace userdb;
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)
{
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)
{
@ -36,8 +96,14 @@ User UserDb::user_by_pin(String cmp)
do
{
User temp = *it;
if (temp.match)
return temp;
if ((*it).match && (*it).enabled)
{
#ifdef DEBUG
Serial.println(" -> Found match");
#endif
it.close_file();
return *it;
}
++it;
} while (it.has_next());
return User{.enabled = false};
@ -50,9 +116,14 @@ User UserDb::user_by_rfid(String cmp)
Iterator it = begin_with_filter(&cmp, USERATTRIBUTES::RFID_UID);
do
{
User temp = *it;
if (temp.match)
return temp;
if ((*it).match && (*it).enabled)
{
#ifdef DEBUG
Serial.println(" -> Found match");
#endif
it.close_file();
return *it;
}
++it;
} while (it.has_next());
return User{.enabled = false};
@ -71,9 +142,34 @@ User UserDb::user_by_uid(String cmp)
Iterator it = begin_with_filter(&cmp, USERATTRIBUTES::USER_ID);
do
{
User temp = *it;
if (temp.match)
return temp;
if ((*it).match)
{
#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;
} while (it.has_next());
return User{.enabled = false};

View File

@ -7,6 +7,7 @@
namespace userdb
{
typedef uint16_t linenumber;
enum USERATTRIBUTES
{
LINE_ID,
@ -22,7 +23,7 @@ namespace userdb
{
// User(unsigned long new_uid): uid(new_uid){}
unsigned long uid = -1;
uint32_t line;
linenumber line = UINT16_MAX;
mutable String first_name;
mutable String last_name;
mutable String rfid_uid;
@ -39,8 +40,9 @@ namespace userdb
{
return String(uid) + ";" + first_name + ";" + last_name + ";" + rfid_uid + ";" + user_pin + ";" + (enabled ? "1" : "0");
}
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")+"}";
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") + ",\"line\":\"" + line + "\"}";
}
};
@ -58,15 +60,16 @@ namespace userdb
User user_by_rfid(String);
User user_by_uid(unsigned long);
User user_by_uid(String cmp);
bool drop(){
User user_by_line(linenumber cmp);
bool drop()
{
LittleFS.remove(filename);
File n = LittleFS.open(filename, "a");
n.close();
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(';');
if (attr == USER_ID && !uid.equals(*match))
return res;
@ -84,39 +87,11 @@ namespace userdb
res.match = true;
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())
{
if (curr_line == line)
while (old_db.available()&&old_db.read() != '\n')
{
}
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;
}
void inline static coursor_to_end_of_line(File &f);
bool remove_user(User &user);
bool update_user(User *user);
static bool update_db_line(const char *file, linenumber line, User *replace = nullptr);
bool add_user(User &user);
struct Iterator
{
@ -129,6 +104,17 @@ namespace userdb
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)
{
#ifdef DEBUG
@ -160,6 +146,9 @@ namespace userdb
}
~Iterator()
{
close_file();
}
void close_file(){
db_file.close();
}
bool has_next()
@ -170,7 +159,16 @@ namespace userdb
{
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
while (db_file.available())
{
@ -195,9 +193,11 @@ namespace userdb
USERATTRIBUTES filter_attr;
String *match;
bool available = true;
uint32_t line = 0;
linenumber line = 0;
linenumber matchline = 0;
};
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(linenumber match, USERATTRIBUTES attr) { return Iterator(LittleFS.open(filename, "r"), match, attr); }
};
}

View File

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

View File

@ -21,14 +21,25 @@ namespace webconsole
void attachRfid(Rfid *rfid);
void serve();
bool isInterceptingRfid();
private:
void _handleStatic();
void _getUserDb();
void _deleteUser();
void _getUser();
void _updateUser();
void _createUser();
void _dropUserDb();
void _catchRFID();
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;
bool catch_rfid = false;

View File

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