Implemented user-update command for REST-API
This commit is contained in:
		
							parent
							
								
									f2fd5bc3b6
								
							
						
					
					
						commit
						74da018c99
					
				| @ -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) | ||||
|  | ||||
| @ -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; | ||||
|  | ||||
							
								
								
									
										118
									
								
								src/UserDb.cpp
									
									
									
									
									
								
							
							
						
						
									
										118
									
								
								src/UserDb.cpp
									
									
									
									
									
								
							| @ -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}; | ||||
|  | ||||
							
								
								
									
										80
									
								
								src/UserDb.h
									
									
									
									
									
								
							
							
						
						
									
										80
									
								
								src/UserDb.h
									
									
									
									
									
								
							| @ -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); } | ||||
|     }; | ||||
| } | ||||
| @ -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\"}"); | ||||
|     } | ||||
|  | ||||
| @ -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; | ||||
|  | ||||
| @ -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()); | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user