From 06bdb06dab7e52de3f5cdd3a45b083646e4f99b8 Mon Sep 17 00:00:00 2001 From: Derek Holloway Date: Sun, 13 Jul 2025 20:42:26 -0700 Subject: [PATCH 01/37] Removed Unneccesary Controllers --- src/Server/Controllers/CartController.cs | 69 ---------- src/Server/Controllers/ProductController.cs | 143 -------------------- 2 files changed, 212 deletions(-) delete mode 100644 src/Server/Controllers/CartController.cs delete mode 100755 src/Server/Controllers/ProductController.cs diff --git a/src/Server/Controllers/CartController.cs b/src/Server/Controllers/CartController.cs deleted file mode 100644 index 597d369..0000000 --- a/src/Server/Controllers/CartController.cs +++ /dev/null @@ -1,69 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using BoredCareers.Entities; -using BoredCareers.Services.DatabaseService; - -namespace BoredCareers.Controllers { - [ApiController] - [Route("api/cart/[controller]")] - public class CartController : MistoxControllerBase { - - CartController(DatabaseService db) : base(db) { } - - [Route("get")] - [HttpPost] - public async Task> GetCart() { - try { - if (isLoggedIn()) { - return Ok(await _databaseService.GetCart(getLoggedInUserID())); - } - return StatusCode(500); - } catch { - return StatusCode(500); - } - } - - [Route("add")] - [HttpPost] - public async Task AddCart([FromBody] Cart cart) { - try { - if (isLoggedIn()) { - cart.AccountID = getLoggedInUserID(); - await _databaseService.AddToCart(cart); - return Ok(); - } - return StatusCode(500); - } catch { - return StatusCode(500); - } - } - - [Route("remove")] - [HttpPost] - public async Task RemoveCart([FromBody] Cart cart) { - try { - if (isLoggedIn()) { - cart.AccountID = getLoggedInUserID(); - await _databaseService.RemoveFromCart(cart); - return Ok(); - } - return StatusCode(500); - } catch { - return StatusCode(500); - } - } - - [Route("clear")] - [HttpPost] - public async Task ClearCart() { - try { - if (isLoggedIn()) { - await _databaseService.ClearCart(getLoggedInUserID()); - return Ok(); - } - return StatusCode(500); - } catch { - return StatusCode(500); - } - } - } -} \ No newline at end of file diff --git a/src/Server/Controllers/ProductController.cs b/src/Server/Controllers/ProductController.cs deleted file mode 100755 index 1903df2..0000000 --- a/src/Server/Controllers/ProductController.cs +++ /dev/null @@ -1,143 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using BoredCareers.Services.DatabaseService; -using BoredCareers.Entities; - -namespace BoredCareers.Controllers { - [ApiController] - [Route("api/product/[controller]")] - public class ProductController : MistoxControllerBase { - - public ProductController(DatabaseService db) : base(db) { } - - [Route("set")] - [HttpPost] - public async Task> CreateProduct([FromForm] Product obj, [FromForm] IFormFile[] images) { - try { - if (isLoggedIn()) { - Account user = await getLoggedInUser(); - if (user.Role == "Admin") { - List building = new List(); - foreach (var file in images) { - using (var stream = new MemoryStream()) { - await file.CopyToAsync(stream); - var bytes = stream.ToArray(); - - // Convert to your image model or whatever your logic is - ProductImage img = new ProductImage { Image = bytes, Name = file.FileName }; - building.Add(img); - } - } - obj.Images = building.ToArray(); - await _databaseService.SetProduct(obj); - return true; - } - } - return false; - } catch (Exception e) { - Console.WriteLine(e); - return false; - } - } - - [Route("get")] - [HttpPost] - public async Task> GetProduct([FromForm] int productID) { - try { - Product? product = await _databaseService.GetProduct(productID); - if (product != null) { - return product; - } - else { - return NotFound(); - } - } catch { - return NotFound(); - } - } - - [Route("getall")] - [HttpPost] - public async Task GetAllProducts() { - try { - return await _databaseService.GetAllProducts(); - } catch { - return Array.Empty(); - } - } - - [Route("delete")] - [HttpPost] - public async Task> DeleteProduct([FromForm] int productID) { - try { - if (isLoggedIn()) { - Account user = await getLoggedInUser(); - if (user.Role == "Admin") { - await _databaseService.DeleteProduct(productID); - return true; - } - } - return false; - } catch { - return false; - } - } - - [Route("getimage")] - [HttpPost] - public async Task GetProductImage([FromForm] int ProductID, [FromForm] int ImageID) { - try { - ProductImage? img = await _databaseService.GetImage(ProductID, ImageID); - if (img != null) { - return File(img.Image, "Image/*"); - } - else { - return NotFound(); - } - } catch { - return NotFound(); - } - } - - [Route("getowned")] - [HttpPost] - public async Task> GetOwnedProduct() { - try { - if (isLoggedIn()) { - Receipt[] returned = await _databaseService.GetAllReceipts(getLoggedInUserID()); - return returned; - } - return new Receipt[0]; - } catch { - return new Receipt[0]; - } - } - - [Route("download")] - [HttpGet] - public async Task Download([FromQuery] string Product) { - try { - if (isLoggedIn()) { - Product[] games = await _databaseService.GetAllProducts(); - foreach (Product product in games) { - if (contains(Product, product.URL)) { - Receipt? receipt = await _databaseService.GetReceipt(getLoggedInUserID(), product.ID); - if (receipt != null) { - //FileStream fileStream = new FileStream(_FolderRoot + Product, FileMode.Open, FileAccess.Read); - //return new FileStreamResult( fileStream, "application/octet-stream" ) { - // FileDownloadName = fileStream.Name - //}; - } - break; - } - } - return Unauthorized(); - } - return Unauthorized(); - } catch { - return NotFound(); - } - } - - } - -} From ccd7914dfc5a1aabf9c26832e4ddcfa6fbfc444d Mon Sep 17 00:00:00 2001 From: Derek Holloway Date: Sun, 13 Jul 2025 20:42:32 -0700 Subject: [PATCH 02/37] Update Auth Controller --- .../Controllers/AuthenticationController.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Server/Controllers/AuthenticationController.cs b/src/Server/Controllers/AuthenticationController.cs index ef2ac3a..e5dfdbb 100755 --- a/src/Server/Controllers/AuthenticationController.cs +++ b/src/Server/Controllers/AuthenticationController.cs @@ -26,7 +26,7 @@ namespace BoredCareers.Controllers { if (test.EmailVerified == true) { if (test.FailedPasswordLock) { if (test.CurrentPasswordAttempts >= test.PasswordAttempts) { - return new Account() { Error = "Too many failed password attempts. Please reset your password" }; + return new Account() { ID = -1, UserName = "Too many failed password attempts. Please reset your password" }; } } if (BCrypt.Net.BCrypt.Verify(PasswordHash, test.PasswordHash)) { @@ -50,17 +50,17 @@ namespace BoredCareers.Controllers { else { test.CurrentPasswordAttempts += 1; await _databaseService.SetAccount(test); - return new Account() { Error = "Wrong password" }; + return new Account() { ID = -1, UserName = "Wrong Password" }; } } else { await SendVerify(test.UserName); - return new Account() { Error = "A new verify email has been sent. \n Note only 1 email send every 5 mintes" }; + return new Account() { ID = -1, UserName = "A new verify email has been sent. \n Note only 1 email send every 5 mintes" }; } } - return new Account() { Error = "User doesn't exist" }; + return new Account() { ID = -1, UserName = "User doesn't exist" }; } catch (Exception ex) { - return new Account() { Error = ex.Message }; + return new Account() { ID = -1, UserName = ex.Message }; } } @@ -82,18 +82,18 @@ namespace BoredCareers.Controllers { await SendVerify(created.UserName); return created; } - return new Account() { Error = "Unknown Error" }; + return new Account() { ID = -1, UserName = "Unknown Error" }; } else { - return new Account() { Error = "Email is already in use" }; + return new Account() { ID = -1, UserName = "Email is already in use" }; } } else { - return new Account() { Error = "UserName is taken" }; + return new Account() { ID = -1, UserName = "UserName is taken" }; } } catch (Exception ex) { Console.WriteLine("Error: " + ex.Message); - return new Account() { Error = ex.Message }; + return new Account() { ID = -1, UserName = ex.Message }; } } From ccc3bbc9b6ddda47fea799fa9d3137eda218ce31 Mon Sep 17 00:00:00 2001 From: Derek Holloway Date: Sun, 13 Jul 2025 21:30:38 -0700 Subject: [PATCH 03/37] Update Data Types to fit DbService --- src/Server/Entities/Resume.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/Server/Entities/Resume.cs b/src/Server/Entities/Resume.cs index ae1d175..6d41c47 100644 --- a/src/Server/Entities/Resume.cs +++ b/src/Server/Entities/Resume.cs @@ -12,6 +12,13 @@ namespace BoredCareers.Entities { public string StateOrRegion { get; set; } = ""; public string City { get; set; } = ""; public bool IsActive { get; set; } = false; + public ResumeExperience[] Experience { get; set; } = []; + public ResumeMillitary Millitary { get; set; } = new ResumeMillitary(); + public ResumeEducation[] Educations { get; set; } = []; + public ResumeSkill[] Skills { get; set; } = []; + public ResumeLanguage[] Languages { get; set; } = []; + public ResumeCertification[] Certification { get; set; } = []; + public ResumeProject[] Projects { get; set; } = []; } public class ResumeExperience { @@ -26,6 +33,7 @@ namespace BoredCareers.Entities { public DateTime DateStarted { get; set; } = new DateTime(); public bool StillEmployed { get; set; } = false; public DateTime DateEnded { get; set; } = new DateTime(); + public ResumeExperienceBullet[] ExperienceBullets { get; set; } = []; } public class ResumeExperienceBullet { @@ -42,6 +50,7 @@ namespace BoredCareers.Entities { public DateTime DateStarted { get; set; } = new DateTime(); public bool StillServing { get; set; } = false; public DateTime DateEnded { get; set; } = new DateTime(); + public ResumeMillitaryBullet[] MillitaryBullets = []; } public class ResumeMillitaryBullet { From 84fb9f14b10b9f02c7635c477f508be682c7539f Mon Sep 17 00:00:00 2001 From: Derek Holloway Date: Sun, 13 Jul 2025 21:31:01 -0700 Subject: [PATCH 04/37] Start work on crazy SQL requests. with multithreading --- src/Server/Services/DatabaseService/Resume.cs | 215 ++++++++++++++++++ 1 file changed, 215 insertions(+) create mode 100644 src/Server/Services/DatabaseService/Resume.cs diff --git a/src/Server/Services/DatabaseService/Resume.cs b/src/Server/Services/DatabaseService/Resume.cs new file mode 100644 index 0000000..d322c48 --- /dev/null +++ b/src/Server/Services/DatabaseService/Resume.cs @@ -0,0 +1,215 @@ +using BoredCareers.Entities; +using MySql.Data.MySqlClient; +using System.Data; +using System.Data.Common; + +namespace BoredCareers.Services.DatabaseService { + public partial class DatabaseService { + + public async Task GetResumes(int AccountID) { + List resumes = new List(); + using (MySqlConnection connection = GetConnection()) { + connection.Open(); + string command = @" + SELECT * + FROM Resume + WHERE AccountID = @AccountID; + "; + + MySqlCommand cmd = new MySqlCommand(command, connection); + cmd.Parameters.AddWithValue("@AccountID", AccountID); + + using (DbDataReader reader = await cmd.ExecuteReaderAsync()) { + while (await reader.ReadAsync()) { + if (reader == null) { + break; + } + + int _id = reader.GetInt32("ID"); + int _accountid = reader.GetInt32("AccountID"); + string _name = reader.GetString("Name"); + string _field = reader.GetString("Field"); + string _email = reader.GetString("Email"); + string _phonenumber = reader.GetString("PhoneNumber"); + string _postalcode = reader.GetString("PostalCode"); + string _country = reader.GetString("Country"); + string _state = reader.GetString("StateOrRegion"); + string _city = reader.GetString("City"); + bool _isactive = reader.GetBoolean("IsActive"); + + resumes.Add( new Resume() { + ID = _id, + AccountID = _accountid, + Name = _name, + Field = _field, + Email = _email, + PhoneNumber = _phonenumber, + PostalCode = _postalcode, + Country = _country, + StateOrRegion = _state, + City = _city, + IsActive = _isactive + } ); + } + } + } + return resumes.ToArray(); + } + + public async Task GetResume(int ResumeID) { + Resume? resume = null; + using (MySqlConnection connection = GetConnection()) { + + connection.Open(); + MySqlCommand resumeCommand = new MySqlCommand("SELECT * FROM Resume WHERE ID = @ResumeID;", connection); + MySqlCommand ResumeExperienceCommand = new MySqlCommand("SELECT * FROM ResumeExperience WHERE ResumeID = @ResumeID;", connection); + MySqlCommand ResumeMillitaryCommand = new MySqlCommand("SELECT * FROM ResumeMillitary WHERE ResumeID = @ResumeID;", connection); + MySqlCommand ResumeEducationCommand = new MySqlCommand("SELECT * FROM ResumeEducation WHERE ResumeID = @ResumeID;", connection); + MySqlCommand ResumeSkillCommand = new MySqlCommand("SELECT * FROM ResumeSkill WHERE ResumeID = @ResumeID;", connection); + MySqlCommand ResumeLanguageCommand = new MySqlCommand("SELECT * FROM ResumeLanguage WHERE ResumeID = @ResumeID;", connection); + MySqlCommand ResumeCertificationCommand = new MySqlCommand("SELECT * FROM ResumeCertification WHERE ResumeID = @ResumeID;", connection); + MySqlCommand ResumeProjectCommand = new MySqlCommand("SELECT * FROM ResumeProject WHERE ResumeID = @ResumeID;", connection); + + resumeCommand.Parameters.AddWithValue("@ResumeID", ResumeID); + ResumeExperienceCommand.Parameters.AddWithValue("@ResumeID", ResumeID); + ResumeMillitaryCommand.Parameters.AddWithValue("@ResumeID", ResumeID); + ResumeEducationCommand.Parameters.AddWithValue("@ResumeID", ResumeID); + ResumeSkillCommand.Parameters.AddWithValue("@ResumeID", ResumeID); + ResumeLanguageCommand.Parameters.AddWithValue("@ResumeID", ResumeID); + ResumeCertificationCommand.Parameters.AddWithValue("@ResumeID", ResumeID); + ResumeProjectCommand.Parameters.AddWithValue("@ResumeID", ResumeID); + + Task resumeReader = resumeCommand.ExecuteReaderAsync(); + Task ResumeExperienceReader = ResumeExperienceCommand.ExecuteReaderAsync(); + Task ResumeMillitaryReader = ResumeMillitaryCommand.ExecuteReaderAsync(); + Task ResumeEducationReader = ResumeEducationCommand.ExecuteReaderAsync(); + Task ResumeSkillReader = ResumeSkillCommand.ExecuteReaderAsync(); + Task ResumeLanguageReader = ResumeLanguageCommand.ExecuteReaderAsync(); + Task ResumeCertificationReader = ResumeCertificationCommand.ExecuteReaderAsync(); + Task ResumeProjectnReader = ResumeProjectCommand.ExecuteReaderAsync(); + + await Task.WhenAll(resumeReader, ResumeExperienceReader, ResumeMillitaryReader, ResumeEducationReader, ResumeSkillReader, ResumeLanguageReader, ResumeCertificationReader, ResumeProjectnReader); + + using (DbDataReader reader = await resumeReader) { + while (await reader.ReadAsync()) { + if (reader == null) { + break; + } + + int _id = reader.GetInt32("ID"); + int _accountid = reader.GetInt32("AccountID"); + string _name = reader.GetString("Name"); + string _field = reader.GetString("Field"); + string _email = reader.GetString("Email"); + string _phonenumber = reader.GetString("PhoneNumber"); + string _postalcode = reader.GetString("PostalCode"); + string _country = reader.GetString("Country"); + string _state = reader.GetString("StateOrRegion"); + string _city = reader.GetString("City"); + bool _isactive = reader.GetBoolean("IsActive"); + + resume = new Resume() { + ID = _id, + AccountID = _accountid, + Name = _name, + Field = _field, + Email = _email, + PhoneNumber = _phonenumber, + PostalCode = _postalcode, + Country = _country, + StateOrRegion = _state, + City = _city, + IsActive = _isactive + }; + } + } + + using (DbDataReader reader = await ResumeExperienceReader) { + // Need to run ResumeExperienceBullet after + + } + + using (DbDataReader reader = await ResumeMillitaryReader) { + // Need to run ResumeMillitaryBullet after + + } + + using (DbDataReader reader = await ResumeEducationReader) { + + } + + using (DbDataReader reader = await ResumeSkillReader) { + + } + + using (DbDataReader reader = await ResumeLanguageReader) { + + } + + using (DbDataReader reader = await ResumeCertificationReader) { + + } + + using (DbDataReader reader = await ResumeProjectnReader) { + + } + } + return resume; + } + + public async Task SetResume( Resume resume ) { + using( MySqlConnection connection = GetConnection() ) { + connection.Open(); + + string command = @" + INSERT INTO Account + (ID,UserName,Email,EmailVerified,PasswordHash,FailedPasswordLock,PasswordAttempts,CurrentPasswordAttempts,Role,EmailToken,DataServer) + VALUES + (@ID,@UserName,@Email,@EmailVerified,@PasswordHash,@FailedPasswordLock,@PasswordAttempts,@CurrentPasswordAttempts,@Role,@EmailToken,@DataServer); + ON DUPLICATE KEY UPDATE + UserName = @UserName, + Email = @Email, + EmailVerified = @EmailVerified, + PasswordHash = @PasswordHash, + FailedPasswordLock = @FailedPasswordLock, + PasswordAttempts = @PasswordAttempts, + CurrentPasswordAttempts = @CurrentPasswordAttempts, + Role = @Role, + EmailToken = @EmailToken; + DataServer = @DataServer; + "; + + MySqlCommand cmd = new MySqlCommand( command , connection); + cmd.Parameters.AddWithValue("@ID", Profile.ID); + cmd.Parameters.AddWithValue("@UserName", Profile.UserName); + cmd.Parameters.AddWithValue("@Email", Profile.Email); + cmd.Parameters.AddWithValue("@EmailVerified", Profile.EmailVerified); + cmd.Parameters.AddWithValue("@PasswordHash", Profile.PasswordHash); + cmd.Parameters.AddWithValue("@FailedPasswordLock", Profile.FailedPasswordLock); + cmd.Parameters.AddWithValue("@PasswordAttempts", Profile.PasswordAttempts); + cmd.Parameters.AddWithValue("@CurrentPasswordAttempts", Profile.CurrentPasswordAttempts); + cmd.Parameters.AddWithValue("@Role", Profile.Role); + cmd.Parameters.AddWithValue("@EmailToken", Profile.EmailToken); + cmd.Parameters.AddWithValue("@DataServer", Profile.DataServer); + + await cmd.ExecuteNonQueryAsync(); + } + } + + public async Task DeleteResume( int ID ) { + using( MySqlConnection connection = GetConnection() ) { + MySqlCommand cmd; + connection.Open(); + + string command = @" + DELETE FROM Resume WHERE ID = @ID; + "; + cmd = new MySqlCommand( command, connection ); + cmd.Parameters.AddWithValue("@ID", ID); + + await cmd.ExecuteNonQueryAsync(); + } + } + + } +} From b7462329c1e826c031a94eb0c5d6f920f7711433 Mon Sep 17 00:00:00 2001 From: "derek.holloway" Date: Mon, 14 Jul 2025 08:03:32 -0700 Subject: [PATCH 05/37] Add Foreign ID for faster retreival --- database/mistox.sql | 4 ++++ src/Server/Services/DatabaseService/Resume.cs | 18 +++++++++++++++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/database/mistox.sql b/database/mistox.sql index a571c29..0f1a37c 100755 --- a/database/mistox.sql +++ b/database/mistox.sql @@ -57,9 +57,11 @@ CREATE TABLE IF NOT EXISTS `Resume` ( CREATE TABLE IF NOT EXISTS `ResumeExperienceBullet` ( `ID` int NOT NULL AUTO_INCREMENT, + `ResumeID` int NOT NULL, `ResumeExperienceID` int NOT NULL, `JobFunction` text NOT NULL, PRIMARY KEY (`ID`), + FOREIGN KEY (`ResumeID`) REFERENCES `Resume`(`ID`) ON DELETE CASCADE, FOREIGN KEY (`ResumeExperienceID`) REFERENCES `ResumeExperience`(`ID`) ON DELETE CASCADE ) AUTO_INCREMENT=1; @@ -77,10 +79,12 @@ CREATE TABLE IF NOT EXISTS `Resume` ( CREATE TABLE IF NOT EXISTS `ResumeMillitaryBullet` ( `ID` int NOT NULL AUTO_INCREMENT, + `ResumeID` int NOT NULL, `ResumeMillitaryID` int NOT NULL, `Achevement` varchar(100) NOT NULL, `Description` text DEFAULT NULL, PRIMARY KEY (`ID`), + FOREIGN KEY (`ResumeID`) REFERENCES `Resume`(`ID`) ON DELETE CASCADE, FOREIGN KEY (`ResumeMillitaryID`) REFERENCES `ResumeMillitary`(`ID`) ON DELETE CASCADE ) AUTO_INCREMENT=1; diff --git a/src/Server/Services/DatabaseService/Resume.cs b/src/Server/Services/DatabaseService/Resume.cs index d322c48..b8c6723 100644 --- a/src/Server/Services/DatabaseService/Resume.cs +++ b/src/Server/Services/DatabaseService/Resume.cs @@ -63,7 +63,9 @@ namespace BoredCareers.Services.DatabaseService { connection.Open(); MySqlCommand resumeCommand = new MySqlCommand("SELECT * FROM Resume WHERE ID = @ResumeID;", connection); MySqlCommand ResumeExperienceCommand = new MySqlCommand("SELECT * FROM ResumeExperience WHERE ResumeID = @ResumeID;", connection); + MySqlCommand ResumeExperienceBulletCommand = new MySqlCommand("SELECT * FROM ResumeExperienceBullet WHERE ResumeID = @ResumeID;"); MySqlCommand ResumeMillitaryCommand = new MySqlCommand("SELECT * FROM ResumeMillitary WHERE ResumeID = @ResumeID;", connection); + MySqlCommand ResumeMillitaryBulletCommand = new MySqlCommand("SELECT * FROM ResumeMillitaryBullet WHERE ResumeID = @ResumeID", connection); MySqlCommand ResumeEducationCommand = new MySqlCommand("SELECT * FROM ResumeEducation WHERE ResumeID = @ResumeID;", connection); MySqlCommand ResumeSkillCommand = new MySqlCommand("SELECT * FROM ResumeSkill WHERE ResumeID = @ResumeID;", connection); MySqlCommand ResumeLanguageCommand = new MySqlCommand("SELECT * FROM ResumeLanguage WHERE ResumeID = @ResumeID;", connection); @@ -72,7 +74,9 @@ namespace BoredCareers.Services.DatabaseService { resumeCommand.Parameters.AddWithValue("@ResumeID", ResumeID); ResumeExperienceCommand.Parameters.AddWithValue("@ResumeID", ResumeID); + ResumeExperienceBulletCommand.Parameters.AddWithValue("@ResumeID", ResumeID); ResumeMillitaryCommand.Parameters.AddWithValue("@ResumeID", ResumeID); + ResumeMillitaryBulletCommand.Parameters.AddWithValue("@ResumeID", ResumeID); ResumeEducationCommand.Parameters.AddWithValue("@ResumeID", ResumeID); ResumeSkillCommand.Parameters.AddWithValue("@ResumeID", ResumeID); ResumeLanguageCommand.Parameters.AddWithValue("@ResumeID", ResumeID); @@ -81,14 +85,16 @@ namespace BoredCareers.Services.DatabaseService { Task resumeReader = resumeCommand.ExecuteReaderAsync(); Task ResumeExperienceReader = ResumeExperienceCommand.ExecuteReaderAsync(); + Task ResumeExperienceBulletReader = ResumeExperienceBulletCommand.ExecuteReaderAsync(); Task ResumeMillitaryReader = ResumeMillitaryCommand.ExecuteReaderAsync(); + Task ResumeMillitaryBulletReader = ResumeMillitaryBulletCommand.ExecuteReaderAsync(); Task ResumeEducationReader = ResumeEducationCommand.ExecuteReaderAsync(); Task ResumeSkillReader = ResumeSkillCommand.ExecuteReaderAsync(); Task ResumeLanguageReader = ResumeLanguageCommand.ExecuteReaderAsync(); Task ResumeCertificationReader = ResumeCertificationCommand.ExecuteReaderAsync(); Task ResumeProjectnReader = ResumeProjectCommand.ExecuteReaderAsync(); - await Task.WhenAll(resumeReader, ResumeExperienceReader, ResumeMillitaryReader, ResumeEducationReader, ResumeSkillReader, ResumeLanguageReader, ResumeCertificationReader, ResumeProjectnReader); + await Task.WhenAll(resumeReader, ResumeExperienceReader, ResumeExperienceBulletReader, ResumeMillitaryReader, ResumeMillitaryBulletReader, ResumeEducationReader, ResumeSkillReader, ResumeLanguageReader, ResumeCertificationReader, ResumeProjectnReader); using (DbDataReader reader = await resumeReader) { while (await reader.ReadAsync()) { @@ -125,15 +131,21 @@ namespace BoredCareers.Services.DatabaseService { } using (DbDataReader reader = await ResumeExperienceReader) { - // Need to run ResumeExperienceBullet after + + } + + using (DbDataReader reader = await ResumeExperienceBulletReader) { } using (DbDataReader reader = await ResumeMillitaryReader) { - // Need to run ResumeMillitaryBullet after } + using (DbDataReader reader = await ResumeMillitaryBulletReader) { + + } + using (DbDataReader reader = await ResumeEducationReader) { } From 3875622d3148b9a13cce8472338a6b4970324fac Mon Sep 17 00:00:00 2001 From: "derek.holloway" Date: Mon, 14 Jul 2025 08:14:47 -0700 Subject: [PATCH 06/37] Fix misspelling --- database/mistox.sql | 8 ++++---- src/Server/Entities/Resume.cs | 8 ++++---- src/Server/Services/DatabaseService/Resume.cs | 18 +++++++++--------- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/database/mistox.sql b/database/mistox.sql index 0f1a37c..f305686 100755 --- a/database/mistox.sql +++ b/database/mistox.sql @@ -65,7 +65,7 @@ CREATE TABLE IF NOT EXISTS `Resume` ( FOREIGN KEY (`ResumeExperienceID`) REFERENCES `ResumeExperience`(`ID`) ON DELETE CASCADE ) AUTO_INCREMENT=1; - CREATE TABLE IF NOT EXISTS `ResumeMillitary` ( + CREATE TABLE IF NOT EXISTS `ResumeMilitary` ( `ID` int NOT NULL AUTO_INCREMENT, `ResumeID` int NOT NULL, `Country` char(2) NOT NULL, @@ -77,15 +77,15 @@ CREATE TABLE IF NOT EXISTS `Resume` ( FOREIGN KEY (`ResumeID`) REFERENCES `Resume`(`ID`) ON DELETE CASCADE ) AUTO_INCREMENT=1; - CREATE TABLE IF NOT EXISTS `ResumeMillitaryBullet` ( + CREATE TABLE IF NOT EXISTS `ResumeMilitaryBullet` ( `ID` int NOT NULL AUTO_INCREMENT, `ResumeID` int NOT NULL, - `ResumeMillitaryID` int NOT NULL, + `ResumeMilitaryID` int NOT NULL, `Achevement` varchar(100) NOT NULL, `Description` text DEFAULT NULL, PRIMARY KEY (`ID`), FOREIGN KEY (`ResumeID`) REFERENCES `Resume`(`ID`) ON DELETE CASCADE, - FOREIGN KEY (`ResumeMillitaryID`) REFERENCES `ResumeMillitary`(`ID`) ON DELETE CASCADE + FOREIGN KEY (`ResumeMilitaryID`) REFERENCES `ResumeMilitary`(`ID`) ON DELETE CASCADE ) AUTO_INCREMENT=1; CREATE TABLE IF NOT EXISTS `ResumeEducation` ( diff --git a/src/Server/Entities/Resume.cs b/src/Server/Entities/Resume.cs index 6d41c47..7f61a65 100644 --- a/src/Server/Entities/Resume.cs +++ b/src/Server/Entities/Resume.cs @@ -42,7 +42,7 @@ namespace BoredCareers.Entities { public string JobFunction { get; set; } = ""; } - public class ResumeMillitary { + public class ResumeMilitary { public int ID { get; set; } // PK public int ResumeID { get; set; } // FK public string Country { get; set; } = ""; // 2 Letter Country Code @@ -50,12 +50,12 @@ namespace BoredCareers.Entities { public DateTime DateStarted { get; set; } = new DateTime(); public bool StillServing { get; set; } = false; public DateTime DateEnded { get; set; } = new DateTime(); - public ResumeMillitaryBullet[] MillitaryBullets = []; + public ResumeMilitaryBullet[] MillitaryBullets = []; } - public class ResumeMillitaryBullet { + public class ResumeMilitaryBullet { public int ID { get; set; } // PK - public int ResumeMillitaryID { get; set; } // FK + public int ResumeMilitaryID { get; set; } // FK public string Achevement { get; set; } = ""; public string Description { get; set; } = ""; } diff --git a/src/Server/Services/DatabaseService/Resume.cs b/src/Server/Services/DatabaseService/Resume.cs index b8c6723..f10c787 100644 --- a/src/Server/Services/DatabaseService/Resume.cs +++ b/src/Server/Services/DatabaseService/Resume.cs @@ -64,8 +64,8 @@ namespace BoredCareers.Services.DatabaseService { MySqlCommand resumeCommand = new MySqlCommand("SELECT * FROM Resume WHERE ID = @ResumeID;", connection); MySqlCommand ResumeExperienceCommand = new MySqlCommand("SELECT * FROM ResumeExperience WHERE ResumeID = @ResumeID;", connection); MySqlCommand ResumeExperienceBulletCommand = new MySqlCommand("SELECT * FROM ResumeExperienceBullet WHERE ResumeID = @ResumeID;"); - MySqlCommand ResumeMillitaryCommand = new MySqlCommand("SELECT * FROM ResumeMillitary WHERE ResumeID = @ResumeID;", connection); - MySqlCommand ResumeMillitaryBulletCommand = new MySqlCommand("SELECT * FROM ResumeMillitaryBullet WHERE ResumeID = @ResumeID", connection); + MySqlCommand ResumeMilitaryCommand = new MySqlCommand("SELECT * FROM ResumeMilitary WHERE ResumeID = @ResumeID;", connection); + MySqlCommand ResumeMilitaryBulletCommand = new MySqlCommand("SELECT * FROM ResumeMilitaryBullet WHERE ResumeID = @ResumeID", connection); MySqlCommand ResumeEducationCommand = new MySqlCommand("SELECT * FROM ResumeEducation WHERE ResumeID = @ResumeID;", connection); MySqlCommand ResumeSkillCommand = new MySqlCommand("SELECT * FROM ResumeSkill WHERE ResumeID = @ResumeID;", connection); MySqlCommand ResumeLanguageCommand = new MySqlCommand("SELECT * FROM ResumeLanguage WHERE ResumeID = @ResumeID;", connection); @@ -75,8 +75,8 @@ namespace BoredCareers.Services.DatabaseService { resumeCommand.Parameters.AddWithValue("@ResumeID", ResumeID); ResumeExperienceCommand.Parameters.AddWithValue("@ResumeID", ResumeID); ResumeExperienceBulletCommand.Parameters.AddWithValue("@ResumeID", ResumeID); - ResumeMillitaryCommand.Parameters.AddWithValue("@ResumeID", ResumeID); - ResumeMillitaryBulletCommand.Parameters.AddWithValue("@ResumeID", ResumeID); + ResumeMilitaryCommand.Parameters.AddWithValue("@ResumeID", ResumeID); + ResumeMilitaryBulletCommand.Parameters.AddWithValue("@ResumeID", ResumeID); ResumeEducationCommand.Parameters.AddWithValue("@ResumeID", ResumeID); ResumeSkillCommand.Parameters.AddWithValue("@ResumeID", ResumeID); ResumeLanguageCommand.Parameters.AddWithValue("@ResumeID", ResumeID); @@ -86,15 +86,15 @@ namespace BoredCareers.Services.DatabaseService { Task resumeReader = resumeCommand.ExecuteReaderAsync(); Task ResumeExperienceReader = ResumeExperienceCommand.ExecuteReaderAsync(); Task ResumeExperienceBulletReader = ResumeExperienceBulletCommand.ExecuteReaderAsync(); - Task ResumeMillitaryReader = ResumeMillitaryCommand.ExecuteReaderAsync(); - Task ResumeMillitaryBulletReader = ResumeMillitaryBulletCommand.ExecuteReaderAsync(); + Task ResumeMilitaryReader = ResumeMilitaryCommand.ExecuteReaderAsync(); + Task ResumeMilitaryBulletReader = ResumeMilitaryBulletCommand.ExecuteReaderAsync(); Task ResumeEducationReader = ResumeEducationCommand.ExecuteReaderAsync(); Task ResumeSkillReader = ResumeSkillCommand.ExecuteReaderAsync(); Task ResumeLanguageReader = ResumeLanguageCommand.ExecuteReaderAsync(); Task ResumeCertificationReader = ResumeCertificationCommand.ExecuteReaderAsync(); Task ResumeProjectnReader = ResumeProjectCommand.ExecuteReaderAsync(); - await Task.WhenAll(resumeReader, ResumeExperienceReader, ResumeExperienceBulletReader, ResumeMillitaryReader, ResumeMillitaryBulletReader, ResumeEducationReader, ResumeSkillReader, ResumeLanguageReader, ResumeCertificationReader, ResumeProjectnReader); + await Task.WhenAll(resumeReader, ResumeExperienceReader, ResumeExperienceBulletReader, ResumeMilitaryReader, ResumeMilitaryBulletReader, ResumeEducationReader, ResumeSkillReader, ResumeLanguageReader, ResumeCertificationReader, ResumeProjectnReader); using (DbDataReader reader = await resumeReader) { while (await reader.ReadAsync()) { @@ -138,11 +138,11 @@ namespace BoredCareers.Services.DatabaseService { } - using (DbDataReader reader = await ResumeMillitaryReader) { + using (DbDataReader reader = await ResumeMilitaryReader) { } - using (DbDataReader reader = await ResumeMillitaryBulletReader) { + using (DbDataReader reader = await ResumeMilitaryBulletReader) { } From 6f0b26d97156a3f4945eecffe54475e6b3440ed0 Mon Sep 17 00:00:00 2001 From: "derek.holloway" Date: Mon, 14 Jul 2025 08:58:33 -0700 Subject: [PATCH 07/37] Fix misspelling --- database/mistox.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database/mistox.sql b/database/mistox.sql index f305686..4189264 100755 --- a/database/mistox.sql +++ b/database/mistox.sql @@ -81,7 +81,7 @@ CREATE TABLE IF NOT EXISTS `Resume` ( `ID` int NOT NULL AUTO_INCREMENT, `ResumeID` int NOT NULL, `ResumeMilitaryID` int NOT NULL, - `Achevement` varchar(100) NOT NULL, + `Achievement` varchar(100) NOT NULL, `Description` text DEFAULT NULL, PRIMARY KEY (`ID`), FOREIGN KEY (`ResumeID`) REFERENCES `Resume`(`ID`) ON DELETE CASCADE, From c0a9142d101df055f1b261edc3f3df37ebc07b79 Mon Sep 17 00:00:00 2001 From: "derek.holloway" Date: Mon, 14 Jul 2025 08:59:25 -0700 Subject: [PATCH 08/37] Add resumeID FK --- src/Server/Entities/Resume.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Server/Entities/Resume.cs b/src/Server/Entities/Resume.cs index 7f61a65..d2b9eba 100644 --- a/src/Server/Entities/Resume.cs +++ b/src/Server/Entities/Resume.cs @@ -13,7 +13,7 @@ namespace BoredCareers.Entities { public string City { get; set; } = ""; public bool IsActive { get; set; } = false; public ResumeExperience[] Experience { get; set; } = []; - public ResumeMillitary Millitary { get; set; } = new ResumeMillitary(); + public ResumeMilitary Millitary { get; set; } = new ResumeMilitary(); public ResumeEducation[] Educations { get; set; } = []; public ResumeSkill[] Skills { get; set; } = []; public ResumeLanguage[] Languages { get; set; } = []; @@ -38,6 +38,7 @@ namespace BoredCareers.Entities { public class ResumeExperienceBullet { public int ID { get; set; } // PK + public int ResumeID { get; set; } // FK public int ResumeExperienceID { get; set; } // FK public string JobFunction { get; set; } = ""; } @@ -55,6 +56,7 @@ namespace BoredCareers.Entities { public class ResumeMilitaryBullet { public int ID { get; set; } // PK + public int ResumeID { get; set; } // FK public int ResumeMilitaryID { get; set; } // FK public string Achevement { get; set; } = ""; public string Description { get; set; } = ""; From 5f6fcf5b9639d4f84fd61a76e8d27c2012055eae Mon Sep 17 00:00:00 2001 From: "derek.holloway" Date: Mon, 14 Jul 2025 08:59:58 -0700 Subject: [PATCH 09/37] Work on DbDriver --- src/Server/Services/DatabaseService/Resume.cs | 236 +++++++++++------- 1 file changed, 151 insertions(+), 85 deletions(-) diff --git a/src/Server/Services/DatabaseService/Resume.cs b/src/Server/Services/DatabaseService/Resume.cs index f10c787..3429f1b 100644 --- a/src/Server/Services/DatabaseService/Resume.cs +++ b/src/Server/Services/DatabaseService/Resume.cs @@ -58,113 +58,179 @@ namespace BoredCareers.Services.DatabaseService { public async Task GetResume(int ResumeID) { Resume? resume = null; - using (MySqlConnection connection = GetConnection()) { - connection.Open(); - MySqlCommand resumeCommand = new MySqlCommand("SELECT * FROM Resume WHERE ID = @ResumeID;", connection); - MySqlCommand ResumeExperienceCommand = new MySqlCommand("SELECT * FROM ResumeExperience WHERE ResumeID = @ResumeID;", connection); - MySqlCommand ResumeExperienceBulletCommand = new MySqlCommand("SELECT * FROM ResumeExperienceBullet WHERE ResumeID = @ResumeID;"); - MySqlCommand ResumeMilitaryCommand = new MySqlCommand("SELECT * FROM ResumeMilitary WHERE ResumeID = @ResumeID;", connection); - MySqlCommand ResumeMilitaryBulletCommand = new MySqlCommand("SELECT * FROM ResumeMilitaryBullet WHERE ResumeID = @ResumeID", connection); - MySqlCommand ResumeEducationCommand = new MySqlCommand("SELECT * FROM ResumeEducation WHERE ResumeID = @ResumeID;", connection); - MySqlCommand ResumeSkillCommand = new MySqlCommand("SELECT * FROM ResumeSkill WHERE ResumeID = @ResumeID;", connection); - MySqlCommand ResumeLanguageCommand = new MySqlCommand("SELECT * FROM ResumeLanguage WHERE ResumeID = @ResumeID;", connection); - MySqlCommand ResumeCertificationCommand = new MySqlCommand("SELECT * FROM ResumeCertification WHERE ResumeID = @ResumeID;", connection); - MySqlCommand ResumeProjectCommand = new MySqlCommand("SELECT * FROM ResumeProject WHERE ResumeID = @ResumeID;", connection); + MySqlConnection resumeConnection = GetConnection(); + MySqlConnection ResumeExperienceConnection = GetConnection(); + MySqlConnection ResumeExperienceBulletConnection = GetConnection(); + MySqlConnection ResumeMilitaryConnection = GetConnection(); + MySqlConnection ResumeMilitaryBulletConnection = GetConnection(); + MySqlConnection ResumeEducationConnection = GetConnection(); + MySqlConnection ResumeSkillConnection = GetConnection(); + MySqlConnection ResumeLanguageConnection = GetConnection(); + MySqlConnection ResumeCertificationConnection = GetConnection(); + MySqlConnection ResumeProjectConnection = GetConnection(); - resumeCommand.Parameters.AddWithValue("@ResumeID", ResumeID); - ResumeExperienceCommand.Parameters.AddWithValue("@ResumeID", ResumeID); - ResumeExperienceBulletCommand.Parameters.AddWithValue("@ResumeID", ResumeID); - ResumeMilitaryCommand.Parameters.AddWithValue("@ResumeID", ResumeID); - ResumeMilitaryBulletCommand.Parameters.AddWithValue("@ResumeID", ResumeID); - ResumeEducationCommand.Parameters.AddWithValue("@ResumeID", ResumeID); - ResumeSkillCommand.Parameters.AddWithValue("@ResumeID", ResumeID); - ResumeLanguageCommand.Parameters.AddWithValue("@ResumeID", ResumeID); - ResumeCertificationCommand.Parameters.AddWithValue("@ResumeID", ResumeID); - ResumeProjectCommand.Parameters.AddWithValue("@ResumeID", ResumeID); + Task resumeopen = resumeConnection.OpenAsync(); + Task resumeexperienceopen = ResumeExperienceConnection.OpenAsync(); + Task resumeexperiencebulletopen = ResumeExperienceBulletConnection.OpenAsync(); + Task resumemilitaryopen = ResumeMilitaryConnection.OpenAsync(); + Task resumemilitarybulletopen = ResumeMilitaryBulletConnection.OpenAsync(); + Task resumeeducationopen = ResumeEducationConnection.OpenAsync(); + Task resumeskillopen = ResumeSkillConnection.OpenAsync(); + Task resumelanguageopen = ResumeLanguageConnection.OpenAsync(); + Task resumecertifcationopen = ResumeCertificationConnection.OpenAsync(); + Task resumeprojectopen = ResumeProjectConnection.OpenAsync(); - Task resumeReader = resumeCommand.ExecuteReaderAsync(); - Task ResumeExperienceReader = ResumeExperienceCommand.ExecuteReaderAsync(); - Task ResumeExperienceBulletReader = ResumeExperienceBulletCommand.ExecuteReaderAsync(); - Task ResumeMilitaryReader = ResumeMilitaryCommand.ExecuteReaderAsync(); - Task ResumeMilitaryBulletReader = ResumeMilitaryBulletCommand.ExecuteReaderAsync(); - Task ResumeEducationReader = ResumeEducationCommand.ExecuteReaderAsync(); - Task ResumeSkillReader = ResumeSkillCommand.ExecuteReaderAsync(); - Task ResumeLanguageReader = ResumeLanguageCommand.ExecuteReaderAsync(); - Task ResumeCertificationReader = ResumeCertificationCommand.ExecuteReaderAsync(); - Task ResumeProjectnReader = ResumeProjectCommand.ExecuteReaderAsync(); + await Task.WhenAll(resumeopen, resumeexperienceopen, resumeexperiencebulletopen, resumemilitaryopen, resumemilitarybulletopen, + resumeeducationopen, resumeskillopen, resumelanguageopen, resumecertifcationopen, resumeprojectopen); - await Task.WhenAll(resumeReader, ResumeExperienceReader, ResumeExperienceBulletReader, ResumeMilitaryReader, ResumeMilitaryBulletReader, ResumeEducationReader, ResumeSkillReader, ResumeLanguageReader, ResumeCertificationReader, ResumeProjectnReader); + MySqlCommand resumeCommand = new MySqlCommand("SELECT * FROM Resume WHERE ID = @ResumeID;", resumeConnection); + MySqlCommand ResumeExperienceCommand = new MySqlCommand("SELECT * FROM ResumeExperience WHERE ResumeID = @ResumeID;", ResumeExperienceConnection); + MySqlCommand ResumeExperienceBulletCommand = new MySqlCommand("SELECT * FROM ResumeExperienceBullet WHERE ResumeID = @ResumeID;", ResumeExperienceBulletConnection); + MySqlCommand ResumeMilitaryCommand = new MySqlCommand("SELECT * FROM ResumeMilitary WHERE ResumeID = @ResumeID;", ResumeMilitaryConnection); + MySqlCommand ResumeMilitaryBulletCommand = new MySqlCommand("SELECT * FROM ResumeMilitaryBullet WHERE ResumeID = @ResumeID", ResumeMilitaryBulletConnection); + MySqlCommand ResumeEducationCommand = new MySqlCommand("SELECT * FROM ResumeEducation WHERE ResumeID = @ResumeID;", ResumeEducationConnection); + MySqlCommand ResumeSkillCommand = new MySqlCommand("SELECT * FROM ResumeSkill WHERE ResumeID = @ResumeID;", ResumeSkillConnection); + MySqlCommand ResumeLanguageCommand = new MySqlCommand("SELECT * FROM ResumeLanguage WHERE ResumeID = @ResumeID;", ResumeLanguageConnection); + MySqlCommand ResumeCertificationCommand = new MySqlCommand("SELECT * FROM ResumeCertification WHERE ResumeID = @ResumeID;", ResumeCertificationConnection); + MySqlCommand ResumeProjectCommand = new MySqlCommand("SELECT * FROM ResumeProject WHERE ResumeID = @ResumeID;", ResumeProjectConnection); - using (DbDataReader reader = await resumeReader) { - while (await reader.ReadAsync()) { - if (reader == null) { - break; - } + resumeCommand.Parameters.AddWithValue("@ResumeID", ResumeID); + ResumeExperienceCommand.Parameters.AddWithValue("@ResumeID", ResumeID); + ResumeExperienceBulletCommand.Parameters.AddWithValue("@ResumeID", ResumeID); + ResumeMilitaryCommand.Parameters.AddWithValue("@ResumeID", ResumeID); + ResumeMilitaryBulletCommand.Parameters.AddWithValue("@ResumeID", ResumeID); + ResumeEducationCommand.Parameters.AddWithValue("@ResumeID", ResumeID); + ResumeSkillCommand.Parameters.AddWithValue("@ResumeID", ResumeID); + ResumeLanguageCommand.Parameters.AddWithValue("@ResumeID", ResumeID); + ResumeCertificationCommand.Parameters.AddWithValue("@ResumeID", ResumeID); + ResumeProjectCommand.Parameters.AddWithValue("@ResumeID", ResumeID); - int _id = reader.GetInt32("ID"); - int _accountid = reader.GetInt32("AccountID"); - string _name = reader.GetString("Name"); - string _field = reader.GetString("Field"); - string _email = reader.GetString("Email"); - string _phonenumber = reader.GetString("PhoneNumber"); - string _postalcode = reader.GetString("PostalCode"); - string _country = reader.GetString("Country"); - string _state = reader.GetString("StateOrRegion"); - string _city = reader.GetString("City"); - bool _isactive = reader.GetBoolean("IsActive"); + Task resumeReader = resumeCommand.ExecuteReaderAsync(); + Task ResumeExperienceReader = ResumeExperienceCommand.ExecuteReaderAsync(); + Task ResumeExperienceBulletReader = ResumeExperienceBulletCommand.ExecuteReaderAsync(); + Task ResumeMilitaryReader = ResumeMilitaryCommand.ExecuteReaderAsync(); + Task ResumeMilitaryBulletReader = ResumeMilitaryBulletCommand.ExecuteReaderAsync(); + Task ResumeEducationReader = ResumeEducationCommand.ExecuteReaderAsync(); + Task ResumeSkillReader = ResumeSkillCommand.ExecuteReaderAsync(); + Task ResumeLanguageReader = ResumeLanguageCommand.ExecuteReaderAsync(); + Task ResumeCertificationReader = ResumeCertificationCommand.ExecuteReaderAsync(); + Task ResumeProjectnReader = ResumeProjectCommand.ExecuteReaderAsync(); - resume = new Resume() { - ID = _id, - AccountID = _accountid, - Name = _name, - Field = _field, - Email = _email, - PhoneNumber = _phonenumber, - PostalCode = _postalcode, - Country = _country, - StateOrRegion = _state, - City = _city, - IsActive = _isactive - }; + await Task.WhenAll(resumeReader, ResumeExperienceReader, ResumeExperienceBulletReader, ResumeMilitaryReader, ResumeMilitaryBulletReader, + ResumeEducationReader, ResumeSkillReader, ResumeLanguageReader, ResumeCertificationReader, ResumeProjectnReader); + + using (DbDataReader reader = await resumeReader) { + while (await reader.ReadAsync()) { + if (reader == null) { break; } + int _id = reader.GetInt32("ID"); + int _accountid = reader.GetInt32("AccountID"); + string _name = reader.GetString("Name"); + string _field = reader.GetString("Field"); + string _email = reader.GetString("Email"); + string _phonenumber = reader.GetString("PhoneNumber"); + string _postalcode = reader.GetString("PostalCode"); + string _country = reader.GetString("Country"); + string _state = reader.GetString("StateOrRegion"); + string _city = reader.GetString("City"); + bool _isactive = reader.GetBoolean("IsActive"); + resume = new Resume() { + ID = _id, + AccountID = _accountid, + Name = _name, + Field = _field, + Email = _email, + PhoneNumber = _phonenumber, + PostalCode = _postalcode, + Country = _country, + StateOrRegion = _state, + City = _city, + IsActive = _isactive + }; + } + } + + List experienceBullets = new List(); + using (DbDataReader reader = await ResumeExperienceBulletReader) { + while (await reader.ReadAsync()) { + if (reader == null) { break; } + int _id = reader.GetInt32("ID"); + int _resumeid = reader.GetInt32("ResumeID"); + int _experienceid = reader.GetInt32("ResumeExperienceID"); + string _jobfunction = reader.GetString("JobFunction"); + experienceBullets.Add( new ResumeExperienceBullet() { + ID = _id, + ResumeID = _resumeid, + ResumeExperienceID = _experienceid, + JobFunction = _jobfunction + }); + } + } + Dictionary> groupedExperienceBullets = experienceBullets + .GroupBy(b => b.ResumeExperienceID) + .ToDictionary(g => g.Key, g => g.ToList()); + + using (DbDataReader reader = await ResumeExperienceReader) { + List experience = new List(); + while (await reader.ReadAsync()) { + if (reader == null) { + break; } + int _id = reader.GetInt32("ID"); + int _resumeid = reader.GetInt32("ResumeID"); + string _jobtitle = reader.GetString("JobTitle"); + string _company = reader.GetString("Company"); + string _postalcode = reader.GetString("PostalCode"); + string _country = reader.GetString("Country"); + string _state = reader.GetString("StateOrRegion"); + string _city = reader.GetString("City"); + DateTime _datestarted = reader.GetDateTime("DateStarted"); + bool _stillempoyed = reader.GetBoolean("StillEmployed"); + DateTime _dateended = reader.GetDateTime("DateEnded"); + + experience.Add( new ResumeExperience() { + ID = _id, + ResumeID = _resumeid, + JobTitle = _jobtitle, + Company = _company, + PostalCode = _postalcode, + Country = _country, + StateOrRegion = _state, + City = _city, + DateStarted = _datestarted, + StillEmployed = _stillempoyed, + DateEnded = _dateended, + ExperienceBullets = groupedExperienceBullets[_id].ToArray() + } ); } + } - using (DbDataReader reader = await ResumeExperienceReader) { - - } + using (DbDataReader reader = await ResumeMilitaryBulletReader) { - using (DbDataReader reader = await ResumeExperienceBulletReader) { + } - } + using (DbDataReader reader = await ResumeMilitaryReader) { - using (DbDataReader reader = await ResumeMilitaryReader) { - - } + } - using (DbDataReader reader = await ResumeMilitaryBulletReader) { + using (DbDataReader reader = await ResumeEducationReader) { - } + } - using (DbDataReader reader = await ResumeEducationReader) { + using (DbDataReader reader = await ResumeSkillReader) { - } + } - using (DbDataReader reader = await ResumeSkillReader) { + using (DbDataReader reader = await ResumeLanguageReader) { - } + } - using (DbDataReader reader = await ResumeLanguageReader) { + using (DbDataReader reader = await ResumeCertificationReader) { - } + } - using (DbDataReader reader = await ResumeCertificationReader) { - - } - - using (DbDataReader reader = await ResumeProjectnReader) { - - } + using (DbDataReader reader = await ResumeProjectnReader) { + } return resume; } From 8d5dbcabff136d00adee856181db379139db3a32 Mon Sep 17 00:00:00 2001 From: "derek.holloway" Date: Mon, 14 Jul 2025 12:22:31 -0700 Subject: [PATCH 10/37] Add military to resume db --- src/Server/Services/DatabaseService/Resume.cs | 67 +++++++++++++++---- 1 file changed, 55 insertions(+), 12 deletions(-) diff --git a/src/Server/Services/DatabaseService/Resume.cs b/src/Server/Services/DatabaseService/Resume.cs index 3429f1b..4df0349 100644 --- a/src/Server/Services/DatabaseService/Resume.cs +++ b/src/Server/Services/DatabaseService/Resume.cs @@ -150,6 +150,10 @@ namespace BoredCareers.Services.DatabaseService { } } + if (resume == null) { + return resume; + } + List experienceBullets = new List(); using (DbDataReader reader = await ResumeExperienceBulletReader) { while (await reader.ReadAsync()) { @@ -167,9 +171,7 @@ namespace BoredCareers.Services.DatabaseService { } } Dictionary> groupedExperienceBullets = experienceBullets - .GroupBy(b => b.ResumeExperienceID) - .ToDictionary(g => g.Key, g => g.ToList()); - + .GroupBy(b => b.ResumeExperienceID).ToDictionary(g => g.Key, g => g.ToList()); using (DbDataReader reader = await ResumeExperienceReader) { List experience = new List(); while (await reader.ReadAsync()) { @@ -187,8 +189,7 @@ namespace BoredCareers.Services.DatabaseService { DateTime _datestarted = reader.GetDateTime("DateStarted"); bool _stillempoyed = reader.GetBoolean("StillEmployed"); DateTime _dateended = reader.GetDateTime("DateEnded"); - - experience.Add( new ResumeExperience() { + experience.Add(new ResumeExperience() { ID = _id, ResumeID = _resumeid, JobTitle = _jobtitle, @@ -201,16 +202,58 @@ namespace BoredCareers.Services.DatabaseService { StillEmployed = _stillempoyed, DateEnded = _dateended, ExperienceBullets = groupedExperienceBullets[_id].ToArray() - } ); + }); + } + resume.Experience = experience.ToArray(); + } + + List militaryBullets = new List(); + using (DbDataReader reader = await ResumeMilitaryBulletReader) { + while (await reader.ReadAsync()) { + if (reader == null) { break; } + int _id = reader.GetInt32("ID"); + int _resumeid = reader.GetInt32("ResumeID"); + int _experienceid = reader.GetInt32("ResumeMilitaryID"); + string _achievement = reader.GetString("Achevement"); + string _description = reader.GetString("Description"); + militaryBullets.Add(new ResumeMilitaryBullet() { + ID = _id, + ResumeID = _resumeid, + ResumeMilitaryID = _experienceid, + Achevement = _achievement, + Description = _description + }); } } - - using (DbDataReader reader = await ResumeMilitaryBulletReader) { - - } - + Dictionary> groupedMilitaryBullets = militaryBullets + .GroupBy(b => b.ResumeMilitaryID).ToDictionary(g => g.Key, g => g.ToList()); using (DbDataReader reader = await ResumeMilitaryReader) { - + ResumeMilitary? military = null; + while (await reader.ReadAsync()) { + if (reader == null) { + break; + } + int _id = reader.GetInt32("ID"); + int _resumeid = reader.GetInt32("ResumeID"); + string _country = reader.GetString("Country"); + string _rank = reader.GetString("Rank"); + DateTime _datestarted = reader.GetDateTime("DateStarted"); + bool _stillServing = reader.GetBoolean("StillServing"); + DateTime _dateended = reader.GetDateTime("DateEnded"); + military = new ResumeMilitary() { + ID = _id, + ResumeID = _resumeid, + Country = _country, + Rank = _rank, + DateStarted = _datestarted, + StillServing = _stillServing, + DateEnded = _dateended, + MillitaryBullets = groupedMilitaryBullets[_id].ToArray() + }; + } + if (military != null) { + resume.Millitary = military; + } } using (DbDataReader reader = await ResumeEducationReader) { From 4fc9eab39f3bc611b509846595df3bdbdc3b182d Mon Sep 17 00:00:00 2001 From: "derek.holloway" Date: Mon, 14 Jul 2025 12:39:44 -0700 Subject: [PATCH 11/37] Add skill and education --- src/Server/Services/DatabaseService/Resume.cs | 52 +++++++++++++++++-- 1 file changed, 47 insertions(+), 5 deletions(-) diff --git a/src/Server/Services/DatabaseService/Resume.cs b/src/Server/Services/DatabaseService/Resume.cs index 4df0349..8a152dc 100644 --- a/src/Server/Services/DatabaseService/Resume.cs +++ b/src/Server/Services/DatabaseService/Resume.cs @@ -230,9 +230,7 @@ namespace BoredCareers.Services.DatabaseService { using (DbDataReader reader = await ResumeMilitaryReader) { ResumeMilitary? military = null; while (await reader.ReadAsync()) { - if (reader == null) { - break; - } + if (reader == null) { break; } int _id = reader.GetInt32("ID"); int _resumeid = reader.GetInt32("ResumeID"); string _country = reader.GetString("Country"); @@ -257,11 +255,55 @@ namespace BoredCareers.Services.DatabaseService { } using (DbDataReader reader = await ResumeEducationReader) { - + List? education = new List(); + while (await reader.ReadAsync()) { + if (reader == null) { break; } + int _id = reader.GetInt32("ID"); + int _resumeid = reader.GetInt32("ResumeID"); + string _degreetype = reader.GetString("DegreeType"); + string _degreefield = reader.GetString("DegreeField"); + string _school = reader.GetString("School"); + string _postalcode = reader.GetString("PostalCode"); + string _country = reader.GetString("Country"); + string _state = reader.GetString("StateOrRegion"); + string _city = reader.GetString("City"); + DateTime _datestarted = reader.GetDateTime("DateStarted"); + bool _stillstudying = reader.GetBoolean("StillStudying"); + DateTime _dateended = reader.GetDateTime("DateEnded"); + education.Add( new ResumeEducation { + ID = _id, + ResumeID = _resumeid, + DegreeType = _degreetype, + DegreeField = _degreefield, + School = _school, + PostalCode = _postalcode, + Country = _country, + StateOrRegion = _state, + City = _city, + DateStarted = _datestarted, + StillStudying = _stillstudying, + DateEnded = _dateended + } ); + } + resume.Educations = education.ToArray(); } using (DbDataReader reader = await ResumeSkillReader) { - + List? skills = new List(); + while (await reader.ReadAsync()) { + if (reader == null) { break; } + int _id = reader.GetInt32("ID"); + int _resumeid = reader.GetInt32("ResumeID"); + string _name = reader.GetString("Name"); + string _description = reader.GetString("Description"); + skills.Add( new ResumeSkill { + ID = _id, + ResumeID = _resumeid, + Name = _name, + Description = _description + } ); + } + resume.Skills = skills.ToArray(); } using (DbDataReader reader = await ResumeLanguageReader) { From b06230b5458eb094593e70a54fb87da16d33369b Mon Sep 17 00:00:00 2001 From: "derek.holloway" Date: Mon, 14 Jul 2025 12:45:38 -0700 Subject: [PATCH 12/37] Finish resume get driver --- src/Server/Services/DatabaseService/Resume.cs | 59 +++++++++++++++++-- 1 file changed, 53 insertions(+), 6 deletions(-) diff --git a/src/Server/Services/DatabaseService/Resume.cs b/src/Server/Services/DatabaseService/Resume.cs index 8a152dc..2a88464 100644 --- a/src/Server/Services/DatabaseService/Resume.cs +++ b/src/Server/Services/DatabaseService/Resume.cs @@ -115,10 +115,10 @@ namespace BoredCareers.Services.DatabaseService { Task ResumeSkillReader = ResumeSkillCommand.ExecuteReaderAsync(); Task ResumeLanguageReader = ResumeLanguageCommand.ExecuteReaderAsync(); Task ResumeCertificationReader = ResumeCertificationCommand.ExecuteReaderAsync(); - Task ResumeProjectnReader = ResumeProjectCommand.ExecuteReaderAsync(); + Task ResumeProjectReader = ResumeProjectCommand.ExecuteReaderAsync(); await Task.WhenAll(resumeReader, ResumeExperienceReader, ResumeExperienceBulletReader, ResumeMilitaryReader, ResumeMilitaryBulletReader, - ResumeEducationReader, ResumeSkillReader, ResumeLanguageReader, ResumeCertificationReader, ResumeProjectnReader); + ResumeEducationReader, ResumeSkillReader, ResumeLanguageReader, ResumeCertificationReader, ResumeProjectReader); using (DbDataReader reader = await resumeReader) { while (await reader.ReadAsync()) { @@ -307,16 +307,63 @@ namespace BoredCareers.Services.DatabaseService { } using (DbDataReader reader = await ResumeLanguageReader) { - + List? languages = new List(); + while (await reader.ReadAsync()) { + if (reader == null) { break; } + int _id = reader.GetInt32("ID"); + int _resumeid = reader.GetInt32("ResumeID"); + string _language = reader.GetString("Language"); + string _proficiency = reader.GetString("Proficiency"); + languages.Add( new ResumeLanguage { + ID = _id, + ResumeID = _resumeid, + Language = _language, + Proficiency = _proficiency + } ); + } + resume.Languages = languages.ToArray(); } using (DbDataReader reader = await ResumeCertificationReader) { - + List? certs = new List(); + while (await reader.ReadAsync()) { + if (reader == null) { break; } + int _id = reader.GetInt32("ID"); + int _resumeid = reader.GetInt32("ResumeID"); + string _name = reader.GetString("Name"); + string _url = reader.GetString("VerificationURL"); + string _description = reader.GetString("Description"); + certs.Add( new ResumeCertification { + ID = _id, + ResumeID = _resumeid, + Name = _name, + VerificationURL = _url, + Description = _description + } ); + } + resume.Certification = certs.ToArray(); } - using (DbDataReader reader = await ResumeProjectnReader) { - + using (DbDataReader reader = await ResumeProjectReader) { + List? projects = new List(); + while (await reader.ReadAsync()) { + if (reader == null) { break; } + int _id = reader.GetInt32("ID"); + int _resumeid = reader.GetInt32("ResumeID"); + string _name = reader.GetString("Name"); + string _url = reader.GetString("URL"); + string _description = reader.GetString("Description"); + projects.Add( new ResumeProject { + ID = _id, + ResumeID = _resumeid, + Name = _name, + URL = _url, + Description = _description + } ); + } + resume.Projects = projects.ToArray(); } + return resume; } From 9b25671707c16bbaaf8eda1b53f651fdfed6363c Mon Sep 17 00:00:00 2001 From: Derek Holloway Date: Mon, 14 Jul 2025 16:55:20 -0700 Subject: [PATCH 13/37] split out functions for readability --- src/Server/Services/DatabaseService/Resume.cs | 358 +++++------------- 1 file changed, 85 insertions(+), 273 deletions(-) diff --git a/src/Server/Services/DatabaseService/Resume.cs b/src/Server/Services/DatabaseService/Resume.cs index 2a88464..f7155f3 100644 --- a/src/Server/Services/DatabaseService/Resume.cs +++ b/src/Server/Services/DatabaseService/Resume.cs @@ -1,5 +1,6 @@ using BoredCareers.Entities; using MySql.Data.MySqlClient; +using Stripe.Terminal; using System.Data; using System.Data.Common; @@ -57,8 +58,7 @@ namespace BoredCareers.Services.DatabaseService { } public async Task GetResume(int ResumeID) { - Resume? resume = null; - + // Open connections for multi-threaded request MySqlConnection resumeConnection = GetConnection(); MySqlConnection ResumeExperienceConnection = GetConnection(); MySqlConnection ResumeExperienceBulletConnection = GetConnection(); @@ -70,6 +70,7 @@ namespace BoredCareers.Services.DatabaseService { MySqlConnection ResumeCertificationConnection = GetConnection(); MySqlConnection ResumeProjectConnection = GetConnection(); + // Open the connections Task resumeopen = resumeConnection.OpenAsync(); Task resumeexperienceopen = ResumeExperienceConnection.OpenAsync(); Task resumeexperiencebulletopen = ResumeExperienceBulletConnection.OpenAsync(); @@ -81,9 +82,11 @@ namespace BoredCareers.Services.DatabaseService { Task resumecertifcationopen = ResumeCertificationConnection.OpenAsync(); Task resumeprojectopen = ResumeProjectConnection.OpenAsync(); + // Wait for all the connections to open await Task.WhenAll(resumeopen, resumeexperienceopen, resumeexperiencebulletopen, resumemilitaryopen, resumemilitarybulletopen, resumeeducationopen, resumeskillopen, resumelanguageopen, resumecertifcationopen, resumeprojectopen); + // Setup the commands for the connections MySqlCommand resumeCommand = new MySqlCommand("SELECT * FROM Resume WHERE ID = @ResumeID;", resumeConnection); MySqlCommand ResumeExperienceCommand = new MySqlCommand("SELECT * FROM ResumeExperience WHERE ResumeID = @ResumeID;", ResumeExperienceConnection); MySqlCommand ResumeExperienceBulletCommand = new MySqlCommand("SELECT * FROM ResumeExperienceBullet WHERE ResumeID = @ResumeID;", ResumeExperienceBulletConnection); @@ -95,6 +98,7 @@ namespace BoredCareers.Services.DatabaseService { MySqlCommand ResumeCertificationCommand = new MySqlCommand("SELECT * FROM ResumeCertification WHERE ResumeID = @ResumeID;", ResumeCertificationConnection); MySqlCommand ResumeProjectCommand = new MySqlCommand("SELECT * FROM ResumeProject WHERE ResumeID = @ResumeID;", ResumeProjectConnection); + // Add parameters to the commands resumeCommand.Parameters.AddWithValue("@ResumeID", ResumeID); ResumeExperienceCommand.Parameters.AddWithValue("@ResumeID", ResumeID); ResumeExperienceBulletCommand.Parameters.AddWithValue("@ResumeID", ResumeID); @@ -106,7 +110,8 @@ namespace BoredCareers.Services.DatabaseService { ResumeCertificationCommand.Parameters.AddWithValue("@ResumeID", ResumeID); ResumeProjectCommand.Parameters.AddWithValue("@ResumeID", ResumeID); - Task resumeReader = resumeCommand.ExecuteReaderAsync(); + // Run the commands + Task ResumeReader = resumeCommand.ExecuteReaderAsync(); Task ResumeExperienceReader = ResumeExperienceCommand.ExecuteReaderAsync(); Task ResumeExperienceBulletReader = ResumeExperienceBulletCommand.ExecuteReaderAsync(); Task ResumeMilitaryReader = ResumeMilitaryCommand.ExecuteReaderAsync(); @@ -117,293 +122,100 @@ namespace BoredCareers.Services.DatabaseService { Task ResumeCertificationReader = ResumeCertificationCommand.ExecuteReaderAsync(); Task ResumeProjectReader = ResumeProjectCommand.ExecuteReaderAsync(); - await Task.WhenAll(resumeReader, ResumeExperienceReader, ResumeExperienceBulletReader, ResumeMilitaryReader, ResumeMilitaryBulletReader, + // Wait for all the commands to process + await Task.WhenAll(ResumeReader, ResumeExperienceReader, ResumeExperienceBulletReader, ResumeMilitaryReader, ResumeMilitaryBulletReader, ResumeEducationReader, ResumeSkillReader, ResumeLanguageReader, ResumeCertificationReader, ResumeProjectReader); - using (DbDataReader reader = await resumeReader) { - while (await reader.ReadAsync()) { - if (reader == null) { break; } - int _id = reader.GetInt32("ID"); - int _accountid = reader.GetInt32("AccountID"); - string _name = reader.GetString("Name"); - string _field = reader.GetString("Field"); - string _email = reader.GetString("Email"); - string _phonenumber = reader.GetString("PhoneNumber"); - string _postalcode = reader.GetString("PostalCode"); - string _country = reader.GetString("Country"); - string _state = reader.GetString("StateOrRegion"); - string _city = reader.GetString("City"); - bool _isactive = reader.GetBoolean("IsActive"); - resume = new Resume() { - ID = _id, - AccountID = _accountid, - Name = _name, - Field = _field, - Email = _email, - PhoneNumber = _phonenumber, - PostalCode = _postalcode, - Country = _country, - StateOrRegion = _state, - City = _city, - IsActive = _isactive - }; - } - } + // Get Resume + Resume? resume = await GetResume( await ResumeReader ); + if (resume != null) { - if (resume == null) { - return resume; - } + // Get Resume Parts + ResumeExperience[] experience = await GetResumeExperience(await ResumeExperienceReader); + ResumeExperienceBullet[] experienceBullets = await GetResumeExperienceBullets(await ResumeExperienceBulletReader); + ResumeMilitary? military = await GetResumeMilitary(await ResumeMilitaryReader); + ResumeMilitaryBullet[] militaryBullets = await GetResumeMilitaryBullets(await ResumeMilitaryBulletReader); + ResumeEducation[] education = await GetResumeEducation(await ResumeEducationReader); + ResumeSkill[] skills = await GetResumeSkills(await ResumeSkillReader); + ResumeLanguage[] languages = await GetResumeLanguages(await ResumeLanguageReader); + ResumeCertification[] certs = await GetResumeCertification(await ResumeCertificationReader); + ResumeProject[] projects = await GetResumeProjects(await ResumeProjectReader); - List experienceBullets = new List(); - using (DbDataReader reader = await ResumeExperienceBulletReader) { - while (await reader.ReadAsync()) { - if (reader == null) { break; } - int _id = reader.GetInt32("ID"); - int _resumeid = reader.GetInt32("ResumeID"); - int _experienceid = reader.GetInt32("ResumeExperienceID"); - string _jobfunction = reader.GetString("JobFunction"); - experienceBullets.Add( new ResumeExperienceBullet() { - ID = _id, - ResumeID = _resumeid, - ResumeExperienceID = _experienceid, - JobFunction = _jobfunction - }); + // Split into grouped lists and add to experience + Dictionary groupedExperienceBullets = experienceBullets.GroupBy(b => b.ResumeExperienceID).ToDictionary(g => g.Key, g => g.ToArray()); + foreach (ResumeExperience cur in experience) { + cur.ExperienceBullets = groupedExperienceBullets[cur.ID]; } - } - Dictionary> groupedExperienceBullets = experienceBullets - .GroupBy(b => b.ResumeExperienceID).ToDictionary(g => g.Key, g => g.ToList()); - using (DbDataReader reader = await ResumeExperienceReader) { - List experience = new List(); - while (await reader.ReadAsync()) { - if (reader == null) { - break; - } - int _id = reader.GetInt32("ID"); - int _resumeid = reader.GetInt32("ResumeID"); - string _jobtitle = reader.GetString("JobTitle"); - string _company = reader.GetString("Company"); - string _postalcode = reader.GetString("PostalCode"); - string _country = reader.GetString("Country"); - string _state = reader.GetString("StateOrRegion"); - string _city = reader.GetString("City"); - DateTime _datestarted = reader.GetDateTime("DateStarted"); - bool _stillempoyed = reader.GetBoolean("StillEmployed"); - DateTime _dateended = reader.GetDateTime("DateEnded"); - experience.Add(new ResumeExperience() { - ID = _id, - ResumeID = _resumeid, - JobTitle = _jobtitle, - Company = _company, - PostalCode = _postalcode, - Country = _country, - StateOrRegion = _state, - City = _city, - DateStarted = _datestarted, - StillEmployed = _stillempoyed, - DateEnded = _dateended, - ExperienceBullets = groupedExperienceBullets[_id].ToArray() - }); - } - resume.Experience = experience.ToArray(); - } - List militaryBullets = new List(); - using (DbDataReader reader = await ResumeMilitaryBulletReader) { - while (await reader.ReadAsync()) { - if (reader == null) { break; } - int _id = reader.GetInt32("ID"); - int _resumeid = reader.GetInt32("ResumeID"); - int _experienceid = reader.GetInt32("ResumeMilitaryID"); - string _achievement = reader.GetString("Achevement"); - string _description = reader.GetString("Description"); - militaryBullets.Add(new ResumeMilitaryBullet() { - ID = _id, - ResumeID = _resumeid, - ResumeMilitaryID = _experienceid, - Achevement = _achievement, - Description = _description - }); - } - } - Dictionary> groupedMilitaryBullets = militaryBullets - .GroupBy(b => b.ResumeMilitaryID).ToDictionary(g => g.Key, g => g.ToList()); - using (DbDataReader reader = await ResumeMilitaryReader) { - ResumeMilitary? military = null; - while (await reader.ReadAsync()) { - if (reader == null) { break; } - int _id = reader.GetInt32("ID"); - int _resumeid = reader.GetInt32("ResumeID"); - string _country = reader.GetString("Country"); - string _rank = reader.GetString("Rank"); - DateTime _datestarted = reader.GetDateTime("DateStarted"); - bool _stillServing = reader.GetBoolean("StillServing"); - DateTime _dateended = reader.GetDateTime("DateEnded"); - military = new ResumeMilitary() { - ID = _id, - ResumeID = _resumeid, - Country = _country, - Rank = _rank, - DateStarted = _datestarted, - StillServing = _stillServing, - DateEnded = _dateended, - MillitaryBullets = groupedMilitaryBullets[_id].ToArray() - }; - } + // Add the parts to the resume if (military != null) { + military.MillitaryBullets = militaryBullets; resume.Millitary = military; } - } + resume.Experience = experience; + resume.Educations = education; + resume.Skills = skills; + resume.Languages = languages; + resume.Certification = certs; + resume.Projects = projects; - using (DbDataReader reader = await ResumeEducationReader) { - List? education = new List(); - while (await reader.ReadAsync()) { - if (reader == null) { break; } - int _id = reader.GetInt32("ID"); - int _resumeid = reader.GetInt32("ResumeID"); - string _degreetype = reader.GetString("DegreeType"); - string _degreefield = reader.GetString("DegreeField"); - string _school = reader.GetString("School"); - string _postalcode = reader.GetString("PostalCode"); - string _country = reader.GetString("Country"); - string _state = reader.GetString("StateOrRegion"); - string _city = reader.GetString("City"); - DateTime _datestarted = reader.GetDateTime("DateStarted"); - bool _stillstudying = reader.GetBoolean("StillStudying"); - DateTime _dateended = reader.GetDateTime("DateEnded"); - education.Add( new ResumeEducation { - ID = _id, - ResumeID = _resumeid, - DegreeType = _degreetype, - DegreeField = _degreefield, - School = _school, - PostalCode = _postalcode, - Country = _country, - StateOrRegion = _state, - City = _city, - DateStarted = _datestarted, - StillStudying = _stillstudying, - DateEnded = _dateended - } ); - } - resume.Educations = education.ToArray(); + return resume; } - - using (DbDataReader reader = await ResumeSkillReader) { - List? skills = new List(); - while (await reader.ReadAsync()) { - if (reader == null) { break; } - int _id = reader.GetInt32("ID"); - int _resumeid = reader.GetInt32("ResumeID"); - string _name = reader.GetString("Name"); - string _description = reader.GetString("Description"); - skills.Add( new ResumeSkill { - ID = _id, - ResumeID = _resumeid, - Name = _name, - Description = _description - } ); - } - resume.Skills = skills.ToArray(); - } - - using (DbDataReader reader = await ResumeLanguageReader) { - List? languages = new List(); - while (await reader.ReadAsync()) { - if (reader == null) { break; } - int _id = reader.GetInt32("ID"); - int _resumeid = reader.GetInt32("ResumeID"); - string _language = reader.GetString("Language"); - string _proficiency = reader.GetString("Proficiency"); - languages.Add( new ResumeLanguage { - ID = _id, - ResumeID = _resumeid, - Language = _language, - Proficiency = _proficiency - } ); - } - resume.Languages = languages.ToArray(); - } - - using (DbDataReader reader = await ResumeCertificationReader) { - List? certs = new List(); - while (await reader.ReadAsync()) { - if (reader == null) { break; } - int _id = reader.GetInt32("ID"); - int _resumeid = reader.GetInt32("ResumeID"); - string _name = reader.GetString("Name"); - string _url = reader.GetString("VerificationURL"); - string _description = reader.GetString("Description"); - certs.Add( new ResumeCertification { - ID = _id, - ResumeID = _resumeid, - Name = _name, - VerificationURL = _url, - Description = _description - } ); - } - resume.Certification = certs.ToArray(); - } - - using (DbDataReader reader = await ResumeProjectReader) { - List? projects = new List(); - while (await reader.ReadAsync()) { - if (reader == null) { break; } - int _id = reader.GetInt32("ID"); - int _resumeid = reader.GetInt32("ResumeID"); - string _name = reader.GetString("Name"); - string _url = reader.GetString("URL"); - string _description = reader.GetString("Description"); - projects.Add( new ResumeProject { - ID = _id, - ResumeID = _resumeid, - Name = _name, - URL = _url, - Description = _description - } ); - } - resume.Projects = projects.ToArray(); - } - - return resume; + return null; } - public async Task SetResume( Resume resume ) { - using( MySqlConnection connection = GetConnection() ) { - connection.Open(); + public async Task SetResume(Resume resume) { - string command = @" - INSERT INTO Account - (ID,UserName,Email,EmailVerified,PasswordHash,FailedPasswordLock,PasswordAttempts,CurrentPasswordAttempts,Role,EmailToken,DataServer) - VALUES - (@ID,@UserName,@Email,@EmailVerified,@PasswordHash,@FailedPasswordLock,@PasswordAttempts,@CurrentPasswordAttempts,@Role,@EmailToken,@DataServer); - ON DUPLICATE KEY UPDATE - UserName = @UserName, - Email = @Email, - EmailVerified = @EmailVerified, - PasswordHash = @PasswordHash, - FailedPasswordLock = @FailedPasswordLock, - PasswordAttempts = @PasswordAttempts, - CurrentPasswordAttempts = @CurrentPasswordAttempts, - Role = @Role, - EmailToken = @EmailToken; - DataServer = @DataServer; - "; + // Open connections for multi-threaded request + MySqlConnection resumeConnection = GetConnection(); + MySqlConnection ResumeExperienceConnection = GetConnection(); + MySqlConnection ResumeExperienceBulletConnection = GetConnection(); + MySqlConnection ResumeMilitaryConnection = GetConnection(); + MySqlConnection ResumeMilitaryBulletConnection = GetConnection(); + MySqlConnection ResumeEducationConnection = GetConnection(); + MySqlConnection ResumeSkillConnection = GetConnection(); + MySqlConnection ResumeLanguageConnection = GetConnection(); + MySqlConnection ResumeCertificationConnection = GetConnection(); + MySqlConnection ResumeProjectConnection = GetConnection(); - MySqlCommand cmd = new MySqlCommand( command , connection); - cmd.Parameters.AddWithValue("@ID", Profile.ID); - cmd.Parameters.AddWithValue("@UserName", Profile.UserName); - cmd.Parameters.AddWithValue("@Email", Profile.Email); - cmd.Parameters.AddWithValue("@EmailVerified", Profile.EmailVerified); - cmd.Parameters.AddWithValue("@PasswordHash", Profile.PasswordHash); - cmd.Parameters.AddWithValue("@FailedPasswordLock", Profile.FailedPasswordLock); - cmd.Parameters.AddWithValue("@PasswordAttempts", Profile.PasswordAttempts); - cmd.Parameters.AddWithValue("@CurrentPasswordAttempts", Profile.CurrentPasswordAttempts); - cmd.Parameters.AddWithValue("@Role", Profile.Role); - cmd.Parameters.AddWithValue("@EmailToken", Profile.EmailToken); - cmd.Parameters.AddWithValue("@DataServer", Profile.DataServer); + // Open the connections + Task resumeopen = resumeConnection.OpenAsync(); + Task resumeexperienceopen = ResumeExperienceConnection.OpenAsync(); + Task resumeexperiencebulletopen = ResumeExperienceBulletConnection.OpenAsync(); + Task resumemilitaryopen = ResumeMilitaryConnection.OpenAsync(); + Task resumemilitarybulletopen = ResumeMilitaryBulletConnection.OpenAsync(); + Task resumeeducationopen = ResumeEducationConnection.OpenAsync(); + Task resumeskillopen = ResumeSkillConnection.OpenAsync(); + Task resumelanguageopen = ResumeLanguageConnection.OpenAsync(); + Task resumecertifcationopen = ResumeCertificationConnection.OpenAsync(); + Task resumeprojectopen = ResumeProjectConnection.OpenAsync(); - await cmd.ExecuteNonQueryAsync(); + // Wait for all the connections to open + await Task.WhenAll(resumeopen, resumeexperienceopen, resumeexperiencebulletopen, resumemilitaryopen, resumemilitarybulletopen, + resumeeducationopen, resumeskillopen, resumelanguageopen, resumecertifcationopen, resumeprojectopen); + + // Get all the experience bullets and run all the updates + List bullets = new List(); + foreach (ResumeExperience cur in resume.Experience) { + foreach (ResumeExperienceBullet bullet in cur.ExperienceBullets) { + bullets.Add(bullet); + } } + Task ResumeExperienceBulletTask = SetResumeExperienceBullets(ResumeExperienceBulletConnection, bullets.ToArray()); + Task ResumeTask = SetResume(resumeConnection, resume); + Task ResumeExperienceTask = SetResumeExperience(ResumeExperienceConnection, resume.Experience); + Task ResumeMilitaryTask = SetResumeMilitary(ResumeMilitaryConnection, resume.Millitary); + Task ResumeMilitaryBulletTask = SetResumeMilitaryBullets(ResumeMilitaryBulletConnection, resume.Millitary.MillitaryBullets); + Task ResumeEducationTask = SetResumeEducation(ResumeEducationConnection, resume.Educations); + Task ResumeSkllTask = SetResumeSkills(ResumeSkillConnection, resume.Skills); + Task ResumeLanguageTask = SetResumeLanguages(ResumeLanguageConnection, resume.Languages); + Task ResumeCertTask = SetResumeCertification(ResumeCertificationConnection, resume.Certification); + Task ResumeProjectTask = SetResumeProjects(ResumeProjectConnection, resume.Projects); + + // Allow update to finish before closing process + await Task.WhenAll(ResumeTask, ResumeExperienceTask, ResumeExperienceBulletTask, ResumeMilitaryTask, ResumeMilitaryBulletTask, + ResumeEducationTask, ResumeSkllTask, ResumeLanguageTask, ResumeCertTask, ResumeProjectTask); + } public async Task DeleteResume( int ID ) { From 53877643f6a53103c5f1533b2bf358e2cd2b5456 Mon Sep 17 00:00:00 2001 From: Derek Holloway Date: Mon, 14 Jul 2025 16:55:39 -0700 Subject: [PATCH 14/37] Create split out functions --- .../ResumeParts/GetResumeParts.cs | 244 ++++++++++++++++++ 1 file changed, 244 insertions(+) create mode 100644 src/Server/Services/DatabaseService/ResumeParts/GetResumeParts.cs diff --git a/src/Server/Services/DatabaseService/ResumeParts/GetResumeParts.cs b/src/Server/Services/DatabaseService/ResumeParts/GetResumeParts.cs new file mode 100644 index 0000000..b471f52 --- /dev/null +++ b/src/Server/Services/DatabaseService/ResumeParts/GetResumeParts.cs @@ -0,0 +1,244 @@ +using BoredCareers.Entities; +using System.Data; +using System.Data.Common; + +namespace BoredCareers.Services.DatabaseService { + public partial class DatabaseService { + + public async Task GetResume(DbDataReader reader) { + while (await reader.ReadAsync()) { + if (reader == null) { break; } + int _id = reader.GetInt32("ID"); + int _accountid = reader.GetInt32("AccountID"); + string _name = reader.GetString("Name"); + string _field = reader.GetString("Field"); + string _email = reader.GetString("Email"); + string _phonenumber = reader.GetString("PhoneNumber"); + string _postalcode = reader.GetString("PostalCode"); + string _country = reader.GetString("Country"); + string _state = reader.GetString("StateOrRegion"); + string _city = reader.GetString("City"); + bool _isactive = reader.GetBoolean("IsActive"); + return new Resume() { + ID = _id, + AccountID = _accountid, + Name = _name, + Field = _field, + Email = _email, + PhoneNumber = _phonenumber, + PostalCode = _postalcode, + Country = _country, + StateOrRegion = _state, + City = _city, + IsActive = _isactive + }; + } + return null; + } + + public async Task GetResumeExperienceBullets(DbDataReader reader) { + List experienceBullets = new List(); + while (await reader.ReadAsync()) { + if (reader == null) { break; } + int _id = reader.GetInt32("ID"); + int _resumeid = reader.GetInt32("ResumeID"); + int _experienceid = reader.GetInt32("ResumeExperienceID"); + string _jobfunction = reader.GetString("JobFunction"); + experienceBullets.Add(new ResumeExperienceBullet() { + ID = _id, + ResumeID = _resumeid, + ResumeExperienceID = _experienceid, + JobFunction = _jobfunction + }); + } + return experienceBullets.ToArray(); + } + + public async Task GetResumeExperience(DbDataReader reader) { + List experience = new List(); + while (await reader.ReadAsync()) { + if (reader == null) { break; } + int _id = reader.GetInt32("ID"); + int _resumeid = reader.GetInt32("ResumeID"); + string _jobtitle = reader.GetString("JobTitle"); + string _company = reader.GetString("Company"); + string _postalcode = reader.GetString("PostalCode"); + string _country = reader.GetString("Country"); + string _state = reader.GetString("StateOrRegion"); + string _city = reader.GetString("City"); + DateTime _datestarted = reader.GetDateTime("DateStarted"); + bool _stillempoyed = reader.GetBoolean("StillEmployed"); + DateTime _dateended = reader.GetDateTime("DateEnded"); + experience.Add(new ResumeExperience() { + ID = _id, + ResumeID = _resumeid, + JobTitle = _jobtitle, + Company = _company, + PostalCode = _postalcode, + Country = _country, + StateOrRegion = _state, + City = _city, + DateStarted = _datestarted, + StillEmployed = _stillempoyed, + DateEnded = _dateended, + }); + } + return experience.ToArray(); + } + + public async Task GetResumeMilitaryBullets(DbDataReader reader) { + List militaryBullets = new List(); + while (await reader.ReadAsync()) { + if (reader == null) { break; } + int _id = reader.GetInt32("ID"); + int _resumeid = reader.GetInt32("ResumeID"); + int _experienceid = reader.GetInt32("ResumeMilitaryID"); + string _achievement = reader.GetString("Achevement"); + string _description = reader.GetString("Description"); + militaryBullets.Add(new ResumeMilitaryBullet() { + ID = _id, + ResumeID = _resumeid, + ResumeMilitaryID = _experienceid, + Achevement = _achievement, + Description = _description + }); + } + return militaryBullets.ToArray(); + } + + public async Task GetResumeMilitary(DbDataReader reader) { + ResumeMilitary? military = null; + while (await reader.ReadAsync()) { + if (reader == null) { break; } + int _id = reader.GetInt32("ID"); + int _resumeid = reader.GetInt32("ResumeID"); + string _country = reader.GetString("Country"); + string _rank = reader.GetString("Rank"); + DateTime _datestarted = reader.GetDateTime("DateStarted"); + bool _stillServing = reader.GetBoolean("StillServing"); + DateTime _dateended = reader.GetDateTime("DateEnded"); + military = new ResumeMilitary() { + ID = _id, + ResumeID = _resumeid, + Country = _country, + Rank = _rank, + DateStarted = _datestarted, + StillServing = _stillServing, + DateEnded = _dateended, + }; + } + return military; + } + + public async Task GetResumeEducation(DbDataReader reader) { + List? education = new List(); + while (await reader.ReadAsync()) { + if (reader == null) { break; } + int _id = reader.GetInt32("ID"); + int _resumeid = reader.GetInt32("ResumeID"); + string _degreetype = reader.GetString("DegreeType"); + string _degreefield = reader.GetString("DegreeField"); + string _school = reader.GetString("School"); + string _postalcode = reader.GetString("PostalCode"); + string _country = reader.GetString("Country"); + string _state = reader.GetString("StateOrRegion"); + string _city = reader.GetString("City"); + DateTime _datestarted = reader.GetDateTime("DateStarted"); + bool _stillstudying = reader.GetBoolean("StillStudying"); + DateTime _dateended = reader.GetDateTime("DateEnded"); + education.Add(new ResumeEducation { + ID = _id, + ResumeID = _resumeid, + DegreeType = _degreetype, + DegreeField = _degreefield, + School = _school, + PostalCode = _postalcode, + Country = _country, + StateOrRegion = _state, + City = _city, + DateStarted = _datestarted, + StillStudying = _stillstudying, + DateEnded = _dateended + }); + } + return education.ToArray(); + } + + public async Task GetResumeSkills(DbDataReader reader) { + List skills = new List(); + while (await reader.ReadAsync()) { + if (reader == null) { break; } + int _id = reader.GetInt32("ID"); + int _resumeid = reader.GetInt32("ResumeID"); + string _name = reader.GetString("Name"); + string _description = reader.GetString("Description"); + skills.Add( new ResumeSkill { + ID = _id, + ResumeID = _resumeid, + Name = _name, + Description = _description + } ); + } + return skills.ToArray(); + } + + public async Task GetResumeLanguages(DbDataReader reader) { + List? languages = new List(); + while (await reader.ReadAsync()) { + if (reader == null) { break; } + int _id = reader.GetInt32("ID"); + int _resumeid = reader.GetInt32("ResumeID"); + string _language = reader.GetString("Language"); + string _proficiency = reader.GetString("Proficiency"); + languages.Add( new ResumeLanguage { + ID = _id, + ResumeID = _resumeid, + Language = _language, + Proficiency = _proficiency + } ); + } + return languages.ToArray(); + } + + public async Task GetResumeCertification(DbDataReader reader) { + List certs = new List(); + while (await reader.ReadAsync()) { + if (reader == null) { break; } + int _id = reader.GetInt32("ID"); + int _resumeid = reader.GetInt32("ResumeID"); + string _name = reader.GetString("Name"); + string _url = reader.GetString("VerificationURL"); + string _description = reader.GetString("Description"); + certs.Add( new ResumeCertification { + ID = _id, + ResumeID = _resumeid, + Name = _name, + VerificationURL = _url, + Description = _description + } ); + } + return certs.ToArray(); + } + + public async Task GetResumeProjects(DbDataReader reader) { + List? projects = new List(); + while (await reader.ReadAsync()) { + if (reader == null) { break; } + int _id = reader.GetInt32("ID"); + int _resumeid = reader.GetInt32("ResumeID"); + string _name = reader.GetString("Name"); + string _url = reader.GetString("URL"); + string _description = reader.GetString("Description"); + projects.Add( new ResumeProject { + ID = _id, + ResumeID = _resumeid, + Name = _name, + URL = _url, + Description = _description + } ); + } + return projects.ToArray(); + } + + } +} From 956d700cd729c966c2a46803f40503de06a5cc30 Mon Sep 17 00:00:00 2001 From: Derek Holloway Date: Mon, 14 Jul 2025 16:55:48 -0700 Subject: [PATCH 15/37] Start work on Set resume --- .../ResumeParts/SetResumeParts.cs | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 src/Server/Services/DatabaseService/ResumeParts/SetResumeParts.cs diff --git a/src/Server/Services/DatabaseService/ResumeParts/SetResumeParts.cs b/src/Server/Services/DatabaseService/ResumeParts/SetResumeParts.cs new file mode 100644 index 0000000..19c9fc1 --- /dev/null +++ b/src/Server/Services/DatabaseService/ResumeParts/SetResumeParts.cs @@ -0,0 +1,79 @@ +using BoredCareers.Entities; +using MySql.Data.MySqlClient; + +namespace BoredCareers.Services.DatabaseService { + public partial class DatabaseService { + + public async Task SetResume(MySqlConnection connection, Resume resume) { + string command = @" + INSERT INTO Resume + (ID,UserName,Email,EmailVerified,PasswordHash,FailedPasswordLock,PasswordAttempts,CurrentPasswordAttempts,Role,EmailToken,DataServer) + VALUES + (@ID,@UserName,@Email,@EmailVerified,@PasswordHash,@FailedPasswordLock,@PasswordAttempts,@CurrentPasswordAttempts,@Role,@EmailToken,@DataServer); + ON DUPLICATE KEY UPDATE + UserName = @UserName, + Email = @Email, + EmailVerified = @EmailVerified, + PasswordHash = @PasswordHash, + FailedPasswordLock = @FailedPasswordLock, + PasswordAttempts = @PasswordAttempts, + CurrentPasswordAttempts = @CurrentPasswordAttempts, + Role = @Role, + EmailToken = @EmailToken; + DataServer = @DataServer; + "; + + MySqlCommand cmd = new MySqlCommand(command, connection); + cmd.Parameters.AddWithValue("@ID", Profile.ID); + cmd.Parameters.AddWithValue("@UserName", Profile.UserName); + cmd.Parameters.AddWithValue("@Email", Profile.Email); + cmd.Parameters.AddWithValue("@EmailVerified", Profile.EmailVerified); + cmd.Parameters.AddWithValue("@PasswordHash", Profile.PasswordHash); + cmd.Parameters.AddWithValue("@FailedPasswordLock", Profile.FailedPasswordLock); + cmd.Parameters.AddWithValue("@PasswordAttempts", Profile.PasswordAttempts); + cmd.Parameters.AddWithValue("@CurrentPasswordAttempts", Profile.CurrentPasswordAttempts); + cmd.Parameters.AddWithValue("@Role", Profile.Role); + cmd.Parameters.AddWithValue("@EmailToken", Profile.EmailToken); + cmd.Parameters.AddWithValue("@DataServer", Profile.DataServer); + + await cmd.ExecuteNonQueryAsync(); + } + + public async Task SetResumeExperienceBullets(MySqlConnection connection, ResumeExperienceBullet[] bullets) { + + } + + public async Task SetResumeExperience(MySqlConnection connection, ResumeExperience[] experiences) { + + } + + public async Task SetResumeMilitaryBullets(MySqlConnection connection, ResumeMilitaryBullet[] bullets) { + + } + + public async Task SetResumeMilitary(MySqlConnection connection, ResumeMilitary? military) { + + } + + public async Task SetResumeEducation(MySqlConnection connection, ResumeEducation[] educations) { + + } + + public async Task SetResumeSkills(MySqlConnection connection, ResumeSkill[] skills) { + + } + + public async Task SetResumeLanguages(MySqlConnection connection, ResumeLanguage[] languages) { + + } + + public async Task SetResumeCertification(MySqlConnection connection, ResumeCertification[] certifications) { + + } + + public async Task SetResumeProjects(MySqlConnection connection, ResumeProject[] projects) { + + } + + } +} From 93d9aa31f8d2ab61050216466d9a969317f4ca4b Mon Sep 17 00:00:00 2001 From: Derek Holloway Date: Mon, 14 Jul 2025 19:54:26 -0700 Subject: [PATCH 16/37] Correct Database --- database/mistox.sql | 1 - 1 file changed, 1 deletion(-) diff --git a/database/mistox.sql b/database/mistox.sql index 4189264..9e77991 100755 --- a/database/mistox.sql +++ b/database/mistox.sql @@ -15,7 +15,6 @@ CREATE TABLE IF NOT EXISTS `Account` ( `Role` varchar(45) DEFAULT NULL, `EmailToken` varchar(45) DEFAULT NULL, `DataServer` varchar(200) DEFAULT NULL, - `ServerRegion` varchar(10) DEFAULT NULL, UNIQUE(`Email`), UNIQUE(`UserName`), PRIMARY KEY (`ID`) From 14282a2eb240c5a67b37641ce81d724c86cd4b27 Mon Sep 17 00:00:00 2001 From: Derek Holloway Date: Mon, 14 Jul 2025 19:54:35 -0700 Subject: [PATCH 17/37] Fix misspelling --- src/Server/Entities/Resume.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Server/Entities/Resume.cs b/src/Server/Entities/Resume.cs index d2b9eba..55b58c0 100644 --- a/src/Server/Entities/Resume.cs +++ b/src/Server/Entities/Resume.cs @@ -58,7 +58,7 @@ namespace BoredCareers.Entities { public int ID { get; set; } // PK public int ResumeID { get; set; } // FK public int ResumeMilitaryID { get; set; } // FK - public string Achevement { get; set; } = ""; + public string Achievement { get; set; } = ""; public string Description { get; set; } = ""; } From c8f595729ba362d4279778733ff63ea8132920ad Mon Sep 17 00:00:00 2001 From: Derek Holloway Date: Mon, 14 Jul 2025 19:54:45 -0700 Subject: [PATCH 18/37] Fix misspelling --- .../Services/DatabaseService/ResumeParts/GetResumeParts.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Server/Services/DatabaseService/ResumeParts/GetResumeParts.cs b/src/Server/Services/DatabaseService/ResumeParts/GetResumeParts.cs index b471f52..603bdcc 100644 --- a/src/Server/Services/DatabaseService/ResumeParts/GetResumeParts.cs +++ b/src/Server/Services/DatabaseService/ResumeParts/GetResumeParts.cs @@ -93,13 +93,13 @@ namespace BoredCareers.Services.DatabaseService { int _id = reader.GetInt32("ID"); int _resumeid = reader.GetInt32("ResumeID"); int _experienceid = reader.GetInt32("ResumeMilitaryID"); - string _achievement = reader.GetString("Achevement"); + string _achievement = reader.GetString("Achievement"); string _description = reader.GetString("Description"); militaryBullets.Add(new ResumeMilitaryBullet() { ID = _id, ResumeID = _resumeid, ResumeMilitaryID = _experienceid, - Achevement = _achievement, + Achievement = _achievement, Description = _description }); } From e7bb9326977bb00edb5092ab54d9980a87d1dd5d Mon Sep 17 00:00:00 2001 From: Derek Holloway Date: Mon, 14 Jul 2025 19:55:07 -0700 Subject: [PATCH 19/37] Create Set methods for DbDriver --- .../ResumeParts/SetResumeParts.cs | 298 +++++++++++++++--- 1 file changed, 259 insertions(+), 39 deletions(-) diff --git a/src/Server/Services/DatabaseService/ResumeParts/SetResumeParts.cs b/src/Server/Services/DatabaseService/ResumeParts/SetResumeParts.cs index 19c9fc1..e127c51 100644 --- a/src/Server/Services/DatabaseService/ResumeParts/SetResumeParts.cs +++ b/src/Server/Services/DatabaseService/ResumeParts/SetResumeParts.cs @@ -1,78 +1,298 @@ using BoredCareers.Entities; using MySql.Data.MySqlClient; +// TODO: Fix Data type mangling. Such As DateTime + namespace BoredCareers.Services.DatabaseService { public partial class DatabaseService { public async Task SetResume(MySqlConnection connection, Resume resume) { string command = @" - INSERT INTO Resume - (ID,UserName,Email,EmailVerified,PasswordHash,FailedPasswordLock,PasswordAttempts,CurrentPasswordAttempts,Role,EmailToken,DataServer) - VALUES - (@ID,@UserName,@Email,@EmailVerified,@PasswordHash,@FailedPasswordLock,@PasswordAttempts,@CurrentPasswordAttempts,@Role,@EmailToken,@DataServer); - ON DUPLICATE KEY UPDATE - UserName = @UserName, - Email = @Email, - EmailVerified = @EmailVerified, - PasswordHash = @PasswordHash, - FailedPasswordLock = @FailedPasswordLock, - PasswordAttempts = @PasswordAttempts, - CurrentPasswordAttempts = @CurrentPasswordAttempts, - Role = @Role, - EmailToken = @EmailToken; - DataServer = @DataServer; - "; + INSERT INTO Resume + (ID,AccountID,Name,Field,Email,PhoneNumber,PostalCode,Country,StateOrRegion,City,IsActive) + VALUES + (@ID,@AccountID,@Name,@Field,@Email,@PhoneNumber,@PostalCode,@Country,@StateOrRegion,@City,@IsActive); + ON DUPLICATE KEY UPDATE + ID = @ID, + AccountID = @AccountID, + Name = @Name, + Field = @Field, + Email = @Email, + PhoneNumber = @PhoneNumber, + PostalCode = @PostalCode, + Country = @Country, + StateOrRegion = @StateOrRegion, + City = @City, + IsActive = @IsActive; + "; - MySqlCommand cmd = new MySqlCommand(command, connection); - cmd.Parameters.AddWithValue("@ID", Profile.ID); - cmd.Parameters.AddWithValue("@UserName", Profile.UserName); - cmd.Parameters.AddWithValue("@Email", Profile.Email); - cmd.Parameters.AddWithValue("@EmailVerified", Profile.EmailVerified); - cmd.Parameters.AddWithValue("@PasswordHash", Profile.PasswordHash); - cmd.Parameters.AddWithValue("@FailedPasswordLock", Profile.FailedPasswordLock); - cmd.Parameters.AddWithValue("@PasswordAttempts", Profile.PasswordAttempts); - cmd.Parameters.AddWithValue("@CurrentPasswordAttempts", Profile.CurrentPasswordAttempts); - cmd.Parameters.AddWithValue("@Role", Profile.Role); - cmd.Parameters.AddWithValue("@EmailToken", Profile.EmailToken); - cmd.Parameters.AddWithValue("@DataServer", Profile.DataServer); + MySqlCommand cmd = new MySqlCommand(command, connection); + cmd.Parameters.AddWithValue("@ID", resume.ID); + cmd.Parameters.AddWithValue("@AccountID", resume.AccountID); + cmd.Parameters.AddWithValue("@Name", resume.Name); + cmd.Parameters.AddWithValue("@Field", resume.Field); + cmd.Parameters.AddWithValue("@Email", resume.Email); + cmd.Parameters.AddWithValue("@PhoneNumber", resume.PhoneNumber); + cmd.Parameters.AddWithValue("@PostalCode", resume.PostalCode); + cmd.Parameters.AddWithValue("@Country", resume.Country); + cmd.Parameters.AddWithValue("@StateOrRegion", resume.StateOrRegion); + cmd.Parameters.AddWithValue("@City", resume.City); + cmd.Parameters.AddWithValue("@IsActive", resume.IsActive); - await cmd.ExecuteNonQueryAsync(); + await cmd.ExecuteNonQueryAsync(); } public async Task SetResumeExperienceBullets(MySqlConnection connection, ResumeExperienceBullet[] bullets) { - + foreach (ResumeExperienceBullet cur in bullets) { + string command = @" + INSERT INTO Resume + (ID,ResumeID,ResumeExperienceID,JobFunction) + VALUES + (@ID,@ResumeID,@ResumeExperienceID,@JobFunction); + ON DUPLICATE KEY UPDATE + ID = @ID, + ResumeID = @ResumeID, + ResumeExperienceID = @ResumeExperienceID, + JobFunction = @JobFunction; + "; + + MySqlCommand cmd = new MySqlCommand(command, connection); + cmd.Parameters.AddWithValue("@ID", cur.ID); + cmd.Parameters.AddWithValue("@ResumeID", cur.ResumeID); + cmd.Parameters.AddWithValue("@ResumeExperienceID", cur.ResumeExperienceID); + cmd.Parameters.AddWithValue("@JobFunction", cur.JobFunction); + + await cmd.ExecuteNonQueryAsync(); + } } public async Task SetResumeExperience(MySqlConnection connection, ResumeExperience[] experiences) { - + foreach (ResumeExperience cur in experiences) { + string command = @" + INSERT INTO Resume + (ID,ResumeID,JobTitle,Company,PostalCode,Country,StateOrRegion,City,DateStarted,StillEmployed,DateEnded) + VALUES + (@ID,@ResumeID,@JobTitle,@Company,@PostalCode,@Country,@StateOrRegion,@City,@DateStarted,@StillEmployed,@DateEnded); + ON DUPLICATE KEY UPDATE + ID = @ID, + ResumeID = @ResumeID, + JobTitle = @JobTitle, + Company = @Company, + PostalCode = @PostalCode, + Country = @Country, + StateOrRegion = @StateOrRegion, + City = @City, + DateStarted = @DateStarted, + StillEmployed = @StillEmployed, + DateEnded = @DateEnded; + "; + + MySqlCommand cmd = new MySqlCommand(command, connection); + cmd.Parameters.AddWithValue("@ID", cur.ID); + cmd.Parameters.AddWithValue("@ResumeID", cur.ResumeID); + cmd.Parameters.AddWithValue("@JobTitle", cur.JobTitle); + cmd.Parameters.AddWithValue("@Company", cur.Company); + cmd.Parameters.AddWithValue("@PostalCode", cur.PostalCode); + cmd.Parameters.AddWithValue("@DateStarted", cur.DateStarted); + cmd.Parameters.AddWithValue("@StillEmployed", cur.StillEmployed); + cmd.Parameters.AddWithValue("@DateEnded", cur.DateEnded); + + await cmd.ExecuteNonQueryAsync(); + } } public async Task SetResumeMilitaryBullets(MySqlConnection connection, ResumeMilitaryBullet[] bullets) { - + foreach (ResumeMilitaryBullet cur in bullets) { + string command = @" + INSERT INTO Resume + (ID,ResumeID,ResumeMilitaryID,Achievement) + VALUES + (@ID,@ResumeID,@ResumeMilitaryID,@Achievement); + ON DUPLICATE KEY UPDATE + ID = @ID, + ResumeID = @ResumeID, + ResumeMilitaryID = @ResumeMilitaryID, + Achievement = @Achievement, + Description = @Description; + "; + + MySqlCommand cmd = new MySqlCommand(command, connection); + cmd.Parameters.AddWithValue("@ID", cur.ID); + cmd.Parameters.AddWithValue("@ResumeID", cur.ResumeID); + cmd.Parameters.AddWithValue("@ResumeMilitaryID", cur.ResumeMilitaryID); + cmd.Parameters.AddWithValue("@Achievement", cur.Achievement); + cmd.Parameters.AddWithValue("@Description", cur.Description); + + await cmd.ExecuteNonQueryAsync(); + } } - public async Task SetResumeMilitary(MySqlConnection connection, ResumeMilitary? military) { - + public async Task SetResumeMilitary(MySqlConnection connection, ResumeMilitary military) { + string command = @" + INSERT INTO Resume + (ID,ResumeID,Country,Rank,DateStarted,StillServing,DateEnded) + VALUES + (@ID,@ResumeID,@Country,@Rank,@DateStarted,@StillServing,@DateEnded); + ON DUPLICATE KEY UPDATE + ID = @ID, + ResumeID = @ResumeID, + Country = @Country, + Rank = @Rank, + DateStarted = @DateStarted, + StillServing = @StillServing, + DateEnded = @DateEnded, + "; + + MySqlCommand cmd = new MySqlCommand(command, connection); + cmd.Parameters.AddWithValue("@ID", military.ID); + cmd.Parameters.AddWithValue("@ResumeID", military.ResumeID); + cmd.Parameters.AddWithValue("@Country", military.Country); + cmd.Parameters.AddWithValue("@Rank", military.Rank); + cmd.Parameters.AddWithValue("@DateStarted", military.DateStarted); + cmd.Parameters.AddWithValue("@StillServing", military.StillServing); + cmd.Parameters.AddWithValue("@DateEnded", military.DateEnded); + + await cmd.ExecuteNonQueryAsync(); } public async Task SetResumeEducation(MySqlConnection connection, ResumeEducation[] educations) { - + foreach (ResumeEducation cur in educations) { + string command = @" + INSERT INTO Resume + (ID,ResumeID,DegreeType,DegreeField,School,PostalCode,Country,StateOrRegion,City,DateStarted,StillStudying,DateEnded) + VALUES + (@ID,@ResumeID,@DegreeType,@DegreeField,@School,@PostalCode,@Country,@StateOrRegion,@City,@DateStarted,@StillStudying,@DateEnded); + ON DUPLICATE KEY UPDATE + ID = @ID, + ResumeID = @ResumeID, + DegreeType = @DegreeType, + DegreeField = @DegreeField, + School = @School, + PostalCode = @PostalCode, + Country = @Country, + StateOrRegion = @StateOrRegion, + City = @City, + DateStarted = @DateStarted, + StillStudying = @StillStudying, + DateEnded = @DateEnded; + "; + + MySqlCommand cmd = new MySqlCommand(command, connection); + cmd.Parameters.AddWithValue("@ID", cur.ID); + cmd.Parameters.AddWithValue("@ResumeID", cur.ResumeID); + cmd.Parameters.AddWithValue("@DegreeType", cur.DegreeType); + cmd.Parameters.AddWithValue("@DegreeField", cur.DegreeField); + cmd.Parameters.AddWithValue("@School", cur.School); + cmd.Parameters.AddWithValue("@PostalCode", cur.PostalCode); + cmd.Parameters.AddWithValue("@Country", cur.Country); + cmd.Parameters.AddWithValue("@StateOrRegion", cur.StateOrRegion); + cmd.Parameters.AddWithValue("@City", cur.City); + cmd.Parameters.AddWithValue("@DateStarted", cur.DateStarted); + cmd.Parameters.AddWithValue("@StillStudying", cur.StillStudying); + cmd.Parameters.AddWithValue("@DateEnded", cur.DateEnded); + + await cmd.ExecuteNonQueryAsync(); + } } public async Task SetResumeSkills(MySqlConnection connection, ResumeSkill[] skills) { - + foreach (ResumeSkill cur in skills) { + string command = @" + INSERT INTO Resume + (ID,ResumeID,Name,Description) + VALUES + (@ID,@ResumeID,@Name,@Description); + ON DUPLICATE KEY UPDATE + ID = @ID, + ResumeID = @ResumeID, + Name = @Name, + Description = @Description; + "; + + MySqlCommand cmd = new MySqlCommand(command, connection); + cmd.Parameters.AddWithValue("@ID", cur.ID); + cmd.Parameters.AddWithValue("@ResumeID", cur.ResumeID); + cmd.Parameters.AddWithValue("@Name", cur.Name); + cmd.Parameters.AddWithValue("@Description", cur.Description); + + await cmd.ExecuteNonQueryAsync(); + } } public async Task SetResumeLanguages(MySqlConnection connection, ResumeLanguage[] languages) { - + foreach (ResumeLanguage cur in languages) { + string command = @" + INSERT INTO Resume + (ID,ResumeID,Language,Proficiency) + VALUES + (@ID,@ResumeID,@Language,@Proficiency); + ON DUPLICATE KEY UPDATE + ID = @ID, + ResumeID = @ResumeID, + Language = @Language, + Proficiency = @Proficiency; + "; + + MySqlCommand cmd = new MySqlCommand(command, connection); + cmd.Parameters.AddWithValue("@ID", cur.ID); + cmd.Parameters.AddWithValue("@ResumeID", cur.ResumeID); + cmd.Parameters.AddWithValue("@Language", cur.Language); + cmd.Parameters.AddWithValue("@Proficiency", cur.Proficiency); + + await cmd.ExecuteNonQueryAsync(); + } } public async Task SetResumeCertification(MySqlConnection connection, ResumeCertification[] certifications) { - + foreach (ResumeCertification cur in certifications) { + string command = @" + INSERT INTO Resume + (ID,ResumeID,Name,VerificationURL,Description) + VALUES + (@ID,@ResumeID,@Name,@VerificationURL,@Description); + ON DUPLICATE KEY UPDATE + ID = @ID, + ResumeID = @ResumeID, + Name = @DegreeNameType, + VerificationURL = @VerificationURL, + Description = @Description; + "; + + MySqlCommand cmd = new MySqlCommand(command, connection); + cmd.Parameters.AddWithValue("@ID", cur.ID); + cmd.Parameters.AddWithValue("@ResumeID", cur.ResumeID); + cmd.Parameters.AddWithValue("@Name", cur.Name); + cmd.Parameters.AddWithValue("@VerificationURL", cur.VerificationURL); + cmd.Parameters.AddWithValue("@Description", cur.Description); + + await cmd.ExecuteNonQueryAsync(); + } } public async Task SetResumeProjects(MySqlConnection connection, ResumeProject[] projects) { - + foreach (ResumeProject cur in projects) { + string command = @" + INSERT INTO Resume + (ID,ResumeID,Name,URL,Description) + VALUES + (@ID,@ResumeID,@Name,@URL,@Description); + ON DUPLICATE KEY UPDATE + ID = @ID, + ResumeID = @ResumeID, + Name = @Name, + URL = @URL, + Description = @Description; + "; + + MySqlCommand cmd = new MySqlCommand(command, connection); + cmd.Parameters.AddWithValue("@ID", cur.ID); + cmd.Parameters.AddWithValue("@ResumeID", cur.ResumeID); + cmd.Parameters.AddWithValue("@Name", cur.Name); + cmd.Parameters.AddWithValue("@URL", cur.URL); + cmd.Parameters.AddWithValue("@Description", cur.Description); + + await cmd.ExecuteNonQueryAsync(); + } } } From 34ce91d886d3e16bde6e3f2c502351ed7504adda Mon Sep 17 00:00:00 2001 From: Derek Holloway Date: Mon, 14 Jul 2025 20:55:54 -0700 Subject: [PATCH 20/37] Split out JobListings --- src/Server/Entities/Company.cs | 13 ++----------- src/Server/Entities/JobListing.cs | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 11 deletions(-) create mode 100644 src/Server/Entities/JobListing.cs diff --git a/src/Server/Entities/Company.cs b/src/Server/Entities/Company.cs index fe42507..40b57f4 100644 --- a/src/Server/Entities/Company.cs +++ b/src/Server/Entities/Company.cs @@ -15,19 +15,10 @@ namespace BoredCareers.Entities { public string Description { get; set; } = ""; } - public class JobListing { + public class CompanyUser { public int ID { get; set; } // PK + public int AccountID { get; set; } // FK public int CompanyID { get; set; } // FK - public string Title { get; set; } = ""; - public string PostalCode { get; set; } = ""; - public string Country { get; set; } = ""; // 2 Letter Country Code - public string StateOrRegion { get; set; } = ""; - public string City { get; set; } = ""; - public int SalaryMin { get; set; } = 0; - public int SalaryMax { get; set; } = 0; - public string JobType { get; set; } = ""; - public bool Remote { get; set; } = false; - public string Description { get; set; } = ""; } } \ No newline at end of file diff --git a/src/Server/Entities/JobListing.cs b/src/Server/Entities/JobListing.cs new file mode 100644 index 0000000..3c02654 --- /dev/null +++ b/src/Server/Entities/JobListing.cs @@ -0,0 +1,18 @@ +namespace BoredCareers.Entities { + + public class JobListing { + public int ID { get; set; } // PK + public int CompanyID { get; set; } // FK + public string Title { get; set; } = ""; + public string PostalCode { get; set; } = ""; + public string Country { get; set; } = ""; // 2 Letter Country Code + public string StateOrRegion { get; set; } = ""; + public string City { get; set; } = ""; + public int SalaryMin { get; set; } = 0; + public int SalaryMax { get; set; } = 0; + public string JobType { get; set; } = ""; + public bool Remote { get; set; } = false; + public string Description { get; set; } = ""; + } + +} \ No newline at end of file From 26d8366f2f9be4244d1de6618e8c8bc9741bce28 Mon Sep 17 00:00:00 2001 From: Derek Holloway Date: Mon, 14 Jul 2025 20:56:21 -0700 Subject: [PATCH 21/37] Cleanup --- src/Server/Services/DatabaseService/Account.cs | 13 +++++-------- src/Server/Services/DatabaseService/Resume.cs | 4 ++-- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/Server/Services/DatabaseService/Account.cs b/src/Server/Services/DatabaseService/Account.cs index ca84844..ae527da 100755 --- a/src/Server/Services/DatabaseService/Account.cs +++ b/src/Server/Services/DatabaseService/Account.cs @@ -21,10 +21,7 @@ namespace BoredCareers.Services.DatabaseService { using( DbDataReader reader = await cmd.ExecuteReaderAsync() ) { while( await reader.ReadAsync() ) { - if( reader == null ) { - break; - } - + if( reader == null ) { break; } int _id = reader.GetInt32("ID"); string _username = reader.GetString("UserName"); string _email = reader.GetString("Email"); @@ -56,7 +53,7 @@ namespace BoredCareers.Services.DatabaseService { return account; } - public async Task GetAccount( int ID ) { + public async Task GetAccount( int AccountID ) { Account? account = null; using( MySqlConnection connection = GetConnection() ) { connection.Open(); @@ -67,7 +64,7 @@ namespace BoredCareers.Services.DatabaseService { "; MySqlCommand cmd = new MySqlCommand(command, connection); - cmd.Parameters.AddWithValue("@ID", ID); + cmd.Parameters.AddWithValue("@ID", AccountID); using( DbDataReader reader = await cmd.ExecuteReaderAsync() ) { while( await reader.ReadAsync() ) { @@ -144,7 +141,7 @@ namespace BoredCareers.Services.DatabaseService { } } - public async Task DeleteAccount( int ID ) { + public async Task DeleteAccount( int AccountID ) { using( MySqlConnection connection = GetConnection() ) { MySqlCommand cmd; connection.Open(); @@ -153,7 +150,7 @@ namespace BoredCareers.Services.DatabaseService { DELETE FROM Account WHERE ID = @ID; "; cmd = new MySqlCommand( command, connection ); - cmd.Parameters.AddWithValue("@ID", ID); + cmd.Parameters.AddWithValue("@ID", AccountID); await cmd.ExecuteNonQueryAsync(); } diff --git a/src/Server/Services/DatabaseService/Resume.cs b/src/Server/Services/DatabaseService/Resume.cs index f7155f3..f5e2895 100644 --- a/src/Server/Services/DatabaseService/Resume.cs +++ b/src/Server/Services/DatabaseService/Resume.cs @@ -218,7 +218,7 @@ namespace BoredCareers.Services.DatabaseService { } - public async Task DeleteResume( int ID ) { + public async Task DeleteResume( int ResumeID ) { using( MySqlConnection connection = GetConnection() ) { MySqlCommand cmd; connection.Open(); @@ -227,7 +227,7 @@ namespace BoredCareers.Services.DatabaseService { DELETE FROM Resume WHERE ID = @ID; "; cmd = new MySqlCommand( command, connection ); - cmd.Parameters.AddWithValue("@ID", ID); + cmd.Parameters.AddWithValue("@ID", ResumeID); await cmd.ExecuteNonQueryAsync(); } From 5fe270013ed93bb849da4a7e997da56aa50aa105 Mon Sep 17 00:00:00 2001 From: Derek Holloway Date: Mon, 14 Jul 2025 20:56:35 -0700 Subject: [PATCH 22/37] Add company to DbDriver --- database/mistox.sql | 7 ++ .../Services/DatabaseService/Company.cs | 115 ++++++++++++++++++ 2 files changed, 122 insertions(+) create mode 100644 src/Server/Services/DatabaseService/Company.cs diff --git a/database/mistox.sql b/database/mistox.sql index 9e77991..bb33233 100755 --- a/database/mistox.sql +++ b/database/mistox.sql @@ -160,6 +160,13 @@ CREATE TABLE IF NOT EXISTS `Company` ( PRIMARY KEY (`ID`) ) AUTO_INCREMENT=1; +CREATE TABLE IF NOT EXISTS `CompanyUser` ( + `ID` int NOT NULL AUTO_INCREMENT, + `AccountID` int NOT NULL, + `CompanyID` int NOT NULL, + PRIMARY KEY (`ID`) +) AUTO_INCREMENT=1; + CREATE TABLE IF NOT EXISTS `JobListing` ( `ID` int NOT NULL AUTO_INCREMENT, `CompanyID` int NOT NULL, diff --git a/src/Server/Services/DatabaseService/Company.cs b/src/Server/Services/DatabaseService/Company.cs new file mode 100644 index 0000000..2eafd16 --- /dev/null +++ b/src/Server/Services/DatabaseService/Company.cs @@ -0,0 +1,115 @@ +using BoredCareers.Entities; +using MySql.Data.MySqlClient; +using System.Data; +using System.Data.Common; + +namespace BoredCareers.Services.DatabaseService { + public partial class DatabaseService { + + public async Task GetCompany( int CompanyID ) { + Company? company = null; + using( MySqlConnection connection = GetConnection() ) { + connection.Open(); + string command = @" + SELECT * + FROM Company + WHERE ID = @ID; + "; + + MySqlCommand cmd = new MySqlCommand(command, connection); + cmd.Parameters.AddWithValue("@ID", CompanyID); + + using( DbDataReader reader = await cmd.ExecuteReaderAsync() ) { + while( await reader.ReadAsync() ) { + if( reader == null ) { break; } + int _id = reader.GetInt32("ID"); + string _name = reader.GetString("Name"); + string _email = reader.GetString("Email"); + bool _emailVerified = reader.GetBoolean("EmailVerified"); + string _websiteurl = reader.GetString("WebsiteURL"); + string _logourl = reader.GetString( "LogoURL" ); + string _phone = reader.GetString( "Phone" ); + string _postalcode = reader.GetString( "PostalCode" ); + string _country = reader.GetString( "Country" ); + string _state = reader.GetString( "StateOrRegion" ); + string _city = reader.GetString( "City" ); + string _description = reader.GetString( "Description" ); + + company = new Company() { + ID = _id, + Name = _name, + Email = _email, + EmailVerified = _emailVerified, + WebsiteURL = _websiteurl, + LogoURL = _logourl, + Phone = _phone, + PostalCode = _postalcode, + Country = _country, + StateOrRegion = _state, + City = _city, + Description = _description + }; + } + } + } + return company; + } + + public async Task SetCompany( Company company ) { + using( MySqlConnection connection = GetConnection() ) { + connection.Open(); + + string command = @" + INSERT INTO Account + (ID,Name,Email,EmailVerified,WebsiteURL,LogoURL,Phone,PostalCode,Country,StateOrRegion,City,Description) + VALUES + (@ID,@Name,@Email,@EmailVerified,@WebsiteURL,@LogoURL,@Phone,@PostalCode,@Country,@StateOrRegion,@City,@Description); + ON DUPLICATE KEY UPDATE + Name = @Name, + Email = @Email, + EmailVerified = @EmailVerified, + WebsiteURL = @WebsiteURL, + LogoURL = @LogoURL, + Phone = @Phone, + PostalCode = @PostalCode, + Country = @Country, + StateOrRegion = @StateOrRegion; + City = @City, + Description = @Description; + "; + + MySqlCommand cmd = new MySqlCommand( command , connection); + cmd.Parameters.AddWithValue("@ID", company.ID); + cmd.Parameters.AddWithValue("@Name", company.Name); + cmd.Parameters.AddWithValue("@Email", company.Email); + cmd.Parameters.AddWithValue("@EmailVerified", company.EmailVerified); + cmd.Parameters.AddWithValue("@WebsiteURL", company.WebsiteURL); + cmd.Parameters.AddWithValue("@LogoURL", company.LogoURL); + cmd.Parameters.AddWithValue("@Phone", company.Phone); + cmd.Parameters.AddWithValue("@PostalCode", company.PostalCode); + cmd.Parameters.AddWithValue("@Country", company.Country); + cmd.Parameters.AddWithValue("@StateOrRegion", company.StateOrRegion); + cmd.Parameters.AddWithValue("@City", company.City); + cmd.Parameters.AddWithValue("@Description", company.Description); + + await cmd.ExecuteNonQueryAsync(); + } + } + + public async Task DeleteCompany( int CompanyID ) { + using( MySqlConnection connection = GetConnection() ) { + MySqlCommand cmd; + connection.Open(); + + string command = @" + DELETE FROM Company WHERE ID = @ID; + "; + cmd = new MySqlCommand( command, connection ); + cmd.Parameters.AddWithValue("@ID", CompanyID); + + await cmd.ExecuteNonQueryAsync(); + } + } + + } +} From b53c1493f78e6adee970f410a082c9c82d5f1e49 Mon Sep 17 00:00:00 2001 From: Derek Holloway Date: Mon, 14 Jul 2025 21:27:25 -0700 Subject: [PATCH 23/37] Use Yaml for syntax hilighting --- ToDo.txt => ToDo.yaml | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) rename ToDo.txt => ToDo.yaml (68%) diff --git a/ToDo.txt b/ToDo.yaml similarity index 68% rename from ToDo.txt rename to ToDo.yaml index 854f2d6..886da1c 100755 --- a/ToDo.txt +++ b/ToDo.yaml @@ -1,53 +1,53 @@ Server: - AccountInventory.cs - SetInventory isnt fully implimented - - ProjectMistData.cs - Data inside the sql doesnt match what is inside the database - Emails: Dont follow theme of website Admin Functions: - Dont inforce Admin on the API side + Need to inforce Admin on the API side - Authentication ProductController + Authentication ProductController: When the create account is called. right after the getaccount is called. Have all New for database return the object they create - Update API + Update API: Split apart the different routes and Functions No more new / update -> only get / set Make all apis return statuscodes make all input types form's make all getLoggedInUsers() -> make sure that i cant just call getLoggedInUserID - Need to timeout email reset tokens + Need to timeout email reset tokens: Client: - Program + Program: Probably need to turn on cors at some point - Account + Account: Need to add in settings / data pages After a new account is created notify a user that they need to verify their email before logging in - ProductController + ProductController: Need to figure out new way to download purchased items as there is currently no way - Store + Store: Edit product needs created Need to add cart back Need to add in payment page Need to add in payment success/failed Need to add in Receipt page - TopBar + TopBar: No way to minimize the UI topbar on mobile Not themed on mobile - API + API: Some of the API's Changed. Need to go back and update the client API calls -database - Need to create all the forign key policies \ No newline at end of file +database: + Add Created and Modified to the JobListing Table + So JobListing's will only run for a set number of time before auto closing + + Add Deleted bool to the JobListing Table + Dont Delete Job Postings -> Simply mark as deleted to check for duplicate postings. + + Add Applied Jobs Table \ No newline at end of file From 8e38778c6e3565dbca64ae14336c5e189d53d667 Mon Sep 17 00:00:00 2001 From: Derek Holloway Date: Mon, 14 Jul 2025 21:28:03 -0700 Subject: [PATCH 24/37] Add Timestamps to joblisting --- database/mistox.sql | 15 +- src/Server/Entities/JobListing.cs | 5 +- .../Services/DatabaseService/JobListing.cs | 186 ++++++++++++++++++ 3 files changed, 199 insertions(+), 7 deletions(-) create mode 100644 src/Server/Services/DatabaseService/JobListing.cs diff --git a/database/mistox.sql b/database/mistox.sql index bb33233..f428885 100755 --- a/database/mistox.sql +++ b/database/mistox.sql @@ -149,14 +149,14 @@ CREATE TABLE IF NOT EXISTS `Company` ( `Name` varchar(100) DEFAULT NULL, `Email` varchar(255) DEFAULT NULL, `EmailVerified` boolean DEFAULT 0, - `WebsiteURL` VARCHAR(255) DEFAULT NULL, - `LogoURL` VARCHAR(2048) DEFAULT NULL, - `Phone` VARCHAR(20) DEFAULT NULL, + `WebsiteURL` varchar(255) DEFAULT NULL, + `LogoURL` varchar(2048) DEFAULT NULL, + `Phone` varchar(20) DEFAULT NULL, `PostalCode` varchar(20) NOT NULL, - `Country` CHAR(2) NOT NULL, + `Country` char(2) NOT NULL, `StateOrRegion` varchar(100) NOT NULL, - `City` VARCHAR(100), - `Description` TEXT, + `City` varchar(100), + `Description` text, PRIMARY KEY (`ID`) ) AUTO_INCREMENT=1; @@ -180,6 +180,9 @@ CREATE TABLE IF NOT EXISTS `JobListing` ( `JobType` varchar(20) NOT NULL, `Remote` boolean DEFAULT 0, `Description` text NOT NULL, + `CreatedTime` datetime Default NULL, + `ModifiedTime` datetime DEFAULT NULL, + `IsDeleted` boolean Default 0, PRIMARY KEY (`ID`), FOREIGN KEY (`CompanyID`) REFERENCES `Company`(`ID`) ON DELETE CASCADE ) AUTO_INCREMENT=1; diff --git a/src/Server/Entities/JobListing.cs b/src/Server/Entities/JobListing.cs index 3c02654..f88744e 100644 --- a/src/Server/Entities/JobListing.cs +++ b/src/Server/Entities/JobListing.cs @@ -13,6 +13,9 @@ namespace BoredCareers.Entities { public string JobType { get; set; } = ""; public bool Remote { get; set; } = false; public string Description { get; set; } = ""; + public DateTime CreatedTime { get; set; } + public DateTime ModifiedTime { get; set; } + public bool IsDeleted { get; set; } = false; } - + } \ No newline at end of file diff --git a/src/Server/Services/DatabaseService/JobListing.cs b/src/Server/Services/DatabaseService/JobListing.cs new file mode 100644 index 0000000..d16a3c1 --- /dev/null +++ b/src/Server/Services/DatabaseService/JobListing.cs @@ -0,0 +1,186 @@ +using BoredCareers.Entities; +using MySql.Data.MySqlClient; +using System.Data; +using System.Data.Common; + +namespace BoredCareers.Services.DatabaseService { + public partial class DatabaseService { + + public async Task GetJobListingPage(int PageNumber, int CountPerPage) { + List joblistings = new List(); + using (MySqlConnection connection = GetConnection()) { + connection.Open(); + string command = @" + SELECT * + FROM JobListing + ORDER BY ModifiedTime <---- Need to update this + LIMIT @PageSize OFFSET @PageNumber; + "; + + MySqlCommand cmd = new MySqlCommand(command, connection); + cmd.Parameters.AddWithValue("@PageSize", CountPerPage); + cmd.Parameters.AddWithValue("PageNumber", (PageNumber - 1) * CountPerPage); + + using (DbDataReader reader = await cmd.ExecuteReaderAsync()) { + while (await reader.ReadAsync()) { + if (reader == null) { break; } + int _id = reader.GetInt32("ID"); + int _companyid = reader.GetInt32("CompanyID"); + string _title = reader.GetString("Title"); + string _postalcode = reader.GetString("PostalCode"); + string _country = reader.GetString("Country"); + string _state = reader.GetString("StateOrRegion"); + string _city = reader.GetString("City"); + int _salarymin = reader.GetInt32("SalaryMin"); + int _salarymax = reader.GetInt32("SalaryMax"); + string _jobtype = reader.GetString("JobType"); + bool _remote = reader.GetBoolean("Remote"); + string _description = reader.GetString("Description"); + DateTime _createtime = reader.GetDateTime("CreatedTime"); + DateTime _modifiedtime = reader.GetDateTime("ModifiedTime"); + bool _isdeleted = reader.GetBoolean("IsDeleted"); + + joblistings.Add(new JobListing() { + ID = _id, + CompanyID = _companyid, + Title = _title, + PostalCode = _postalcode, + Country = _country, + StateOrRegion = _state, + City = _city, + SalaryMin = _salarymin, + SalaryMax = _salarymax, + JobType = _jobtype, + Remote = _remote, + Description = _description, + CreatedTime = _createtime, + ModifiedTime = _modifiedtime, + IsDeleted = _isdeleted + }); + } + } + } + return joblistings.ToArray(); + } + + public async Task GetJobListing(int JobListingID) { + JobListing? joblisting = null; + using (MySqlConnection connection = GetConnection()) { + connection.Open(); + string command = @" + SELECT * + FROM JobListing + WHERE ID = @ID; + "; + + MySqlCommand cmd = new MySqlCommand(command, connection); + cmd.Parameters.AddWithValue("@ID", JobListingID); + + using (DbDataReader reader = await cmd.ExecuteReaderAsync()) { + while (await reader.ReadAsync()) { + if (reader == null) { break; } + int _id = reader.GetInt32("ID"); + int _companyid = reader.GetInt32("CompanyID"); + string _title = reader.GetString("Title"); + string _postalcode = reader.GetString("PostalCode"); + string _country = reader.GetString("Country"); + string _state = reader.GetString("StateOrRegion"); + string _city = reader.GetString("City"); + int _salarymin = reader.GetInt32("SalaryMin"); + int _salarymax = reader.GetInt32("SalaryMax"); + string _jobtype = reader.GetString("JobType"); + bool _remote = reader.GetBoolean("Remote"); + string _description = reader.GetString("Description"); + DateTime _createtime = reader.GetDateTime("CreatedTime"); + DateTime _modifiedtime = reader.GetDateTime("ModifiedTime"); + bool _isdeleted = reader.GetBoolean("IsDeleted"); + + joblisting = new JobListing() { + ID = _id, + CompanyID = _companyid, + Title = _title, + PostalCode = _postalcode, + Country = _country, + StateOrRegion = _state, + City = _city, + SalaryMin = _salarymin, + SalaryMax = _salarymax, + JobType = _jobtype, + Remote = _remote, + Description = _description, + CreatedTime = _createtime, + ModifiedTime = _modifiedtime, + IsDeleted = _isdeleted + }; + } + } + } + return joblisting; + } + + public async Task SetJobListing( JobListing jobListing ) { + using( MySqlConnection connection = GetConnection() ) { + connection.Open(); + + string command = @" + INSERT INTO JobListing + (ID,CompanyID,Title,PostalCode,Country,StateOrRegion,City,SalaryMin,SalaryMax,JobType,Remote,Description,CreatedTime,ModifiedTime,IsDeleted) + VALUES + (@ID,@CompanyID,@Title,@PostalCode,@Country,@StateOrRegion,@City,@SalaryMin,@SalaryMax,@JobType,@Remote,@Description,@CreatedTime,@ModifiedTime,@IsDeleted); + ON DUPLICATE KEY UPDATE + CompanyID = @CompanyID, + Title = @Title, + PostalCode = @PostalCode, + Country = @Country, + StateOrRegion = @StateOrRegion, + City = @City, + SalaryMin = @SalaryMin, + SalaryMax = @SalaryMax, + JobType = @JobType, + Remote = @Remote, + Description = @Description, + CreatedTime = @CreatedTime, + ModifiedTime = @ModifiedTime, + IsDeleted = @IsDeleted; + "; + + MySqlCommand cmd = new MySqlCommand( command , connection); + cmd.Parameters.AddWithValue("@ID", jobListing.ID); + cmd.Parameters.AddWithValue("@CompanyID", jobListing.CompanyID); + cmd.Parameters.AddWithValue("@Title", jobListing.Title); + cmd.Parameters.AddWithValue("@PostalCode", jobListing.PostalCode); + cmd.Parameters.AddWithValue("@Country", jobListing.Country); + cmd.Parameters.AddWithValue("@StateOrRegion", jobListing.StateOrRegion); + cmd.Parameters.AddWithValue("@City", jobListing.City); + cmd.Parameters.AddWithValue("@SalaryMin", jobListing.SalaryMin); + cmd.Parameters.AddWithValue("@SalaryMax", jobListing.SalaryMax); + cmd.Parameters.AddWithValue("@JobType", jobListing.JobType); + cmd.Parameters.AddWithValue("@Remote", jobListing.Remote); + cmd.Parameters.AddWithValue("@Description", jobListing.Description); + cmd.Parameters.AddWithValue("@CreatedTime", jobListing.CreatedTime); + cmd.Parameters.AddWithValue("@ModifiedTime", jobListing.ModifiedTime); + cmd.Parameters.AddWithValue("@IsDeleted", jobListing.IsDeleted); + + await cmd.ExecuteNonQueryAsync(); + } + } + + public async Task DeleteJobListing( int JobListingID ) { + using( MySqlConnection connection = GetConnection() ) { + MySqlCommand cmd; + connection.Open(); + + string command = @" + UPDATE JobListing + SET IsDeleted = TRUE + WHERE ID = @ID; + "; + cmd = new MySqlCommand( command, connection ); + cmd.Parameters.AddWithValue("@ID", JobListingID); + + await cmd.ExecuteNonQueryAsync(); + } + } + + } +} From 4153dc2038ee22dea1a116e74bfed11dde2bca20 Mon Sep 17 00:00:00 2001 From: Derek Holloway Date: Mon, 14 Jul 2025 21:38:46 -0700 Subject: [PATCH 25/37] Remove bad ID Updates --- .../DatabaseService/ResumeParts/SetResumeParts.cs | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/src/Server/Services/DatabaseService/ResumeParts/SetResumeParts.cs b/src/Server/Services/DatabaseService/ResumeParts/SetResumeParts.cs index e127c51..2968e4e 100644 --- a/src/Server/Services/DatabaseService/ResumeParts/SetResumeParts.cs +++ b/src/Server/Services/DatabaseService/ResumeParts/SetResumeParts.cs @@ -13,7 +13,6 @@ namespace BoredCareers.Services.DatabaseService { VALUES (@ID,@AccountID,@Name,@Field,@Email,@PhoneNumber,@PostalCode,@Country,@StateOrRegion,@City,@IsActive); ON DUPLICATE KEY UPDATE - ID = @ID, AccountID = @AccountID, Name = @Name, Field = @Field, @@ -50,7 +49,6 @@ namespace BoredCareers.Services.DatabaseService { VALUES (@ID,@ResumeID,@ResumeExperienceID,@JobFunction); ON DUPLICATE KEY UPDATE - ID = @ID, ResumeID = @ResumeID, ResumeExperienceID = @ResumeExperienceID, JobFunction = @JobFunction; @@ -74,7 +72,6 @@ namespace BoredCareers.Services.DatabaseService { VALUES (@ID,@ResumeID,@JobTitle,@Company,@PostalCode,@Country,@StateOrRegion,@City,@DateStarted,@StillEmployed,@DateEnded); ON DUPLICATE KEY UPDATE - ID = @ID, ResumeID = @ResumeID, JobTitle = @JobTitle, Company = @Company, @@ -109,7 +106,6 @@ namespace BoredCareers.Services.DatabaseService { VALUES (@ID,@ResumeID,@ResumeMilitaryID,@Achievement); ON DUPLICATE KEY UPDATE - ID = @ID, ResumeID = @ResumeID, ResumeMilitaryID = @ResumeMilitaryID, Achievement = @Achievement, @@ -134,13 +130,12 @@ namespace BoredCareers.Services.DatabaseService { VALUES (@ID,@ResumeID,@Country,@Rank,@DateStarted,@StillServing,@DateEnded); ON DUPLICATE KEY UPDATE - ID = @ID, ResumeID = @ResumeID, Country = @Country, Rank = @Rank, DateStarted = @DateStarted, StillServing = @StillServing, - DateEnded = @DateEnded, + DateEnded = @DateEnded; "; MySqlCommand cmd = new MySqlCommand(command, connection); @@ -163,7 +158,6 @@ namespace BoredCareers.Services.DatabaseService { VALUES (@ID,@ResumeID,@DegreeType,@DegreeField,@School,@PostalCode,@Country,@StateOrRegion,@City,@DateStarted,@StillStudying,@DateEnded); ON DUPLICATE KEY UPDATE - ID = @ID, ResumeID = @ResumeID, DegreeType = @DegreeType, DegreeField = @DegreeField, @@ -203,7 +197,6 @@ namespace BoredCareers.Services.DatabaseService { VALUES (@ID,@ResumeID,@Name,@Description); ON DUPLICATE KEY UPDATE - ID = @ID, ResumeID = @ResumeID, Name = @Name, Description = @Description; @@ -227,7 +220,6 @@ namespace BoredCareers.Services.DatabaseService { VALUES (@ID,@ResumeID,@Language,@Proficiency); ON DUPLICATE KEY UPDATE - ID = @ID, ResumeID = @ResumeID, Language = @Language, Proficiency = @Proficiency; @@ -251,7 +243,6 @@ namespace BoredCareers.Services.DatabaseService { VALUES (@ID,@ResumeID,@Name,@VerificationURL,@Description); ON DUPLICATE KEY UPDATE - ID = @ID, ResumeID = @ResumeID, Name = @DegreeNameType, VerificationURL = @VerificationURL, @@ -277,7 +268,6 @@ namespace BoredCareers.Services.DatabaseService { VALUES (@ID,@ResumeID,@Name,@URL,@Description); ON DUPLICATE KEY UPDATE - ID = @ID, ResumeID = @ResumeID, Name = @Name, URL = @URL, From 486ecdc108bbb01775eb07159b582923688fa8ac Mon Sep 17 00:00:00 2001 From: Derek Holloway Date: Mon, 14 Jul 2025 21:38:58 -0700 Subject: [PATCH 26/37] Correct SQL Query --- src/Server/Services/DatabaseService/JobListing.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Server/Services/DatabaseService/JobListing.cs b/src/Server/Services/DatabaseService/JobListing.cs index d16a3c1..fb1f00b 100644 --- a/src/Server/Services/DatabaseService/JobListing.cs +++ b/src/Server/Services/DatabaseService/JobListing.cs @@ -13,13 +13,14 @@ namespace BoredCareers.Services.DatabaseService { string command = @" SELECT * FROM JobListing - ORDER BY ModifiedTime <---- Need to update this + WHERE IsDeleted = FALSE + ORDER BY CreatedTime DESC LIMIT @PageSize OFFSET @PageNumber; "; MySqlCommand cmd = new MySqlCommand(command, connection); cmd.Parameters.AddWithValue("@PageSize", CountPerPage); - cmd.Parameters.AddWithValue("PageNumber", (PageNumber - 1) * CountPerPage); + cmd.Parameters.AddWithValue("@PageNumber", (PageNumber - 1) * CountPerPage); using (DbDataReader reader = await cmd.ExecuteReaderAsync()) { while (await reader.ReadAsync()) { From 8575ad225d166ce957e2d78327ef4ebc40294d79 Mon Sep 17 00:00:00 2001 From: Derek Holloway Date: Mon, 14 Jul 2025 21:39:05 -0700 Subject: [PATCH 27/37] Update ToDo --- ToDo.yaml | 38 ++------------------------------------ 1 file changed, 2 insertions(+), 36 deletions(-) diff --git a/ToDo.yaml b/ToDo.yaml index 886da1c..cdc199a 100755 --- a/ToDo.yaml +++ b/ToDo.yaml @@ -9,45 +9,11 @@ Server: When the create account is called. right after the getaccount is called. Have all New for database return the object they create - Update API: - Split apart the different routes and Functions - No more new / update -> only get / set - Make all apis return statuscodes - make all input types form's - make all getLoggedInUsers() -> make sure that i cant just call getLoggedInUserID - Need to timeout email reset tokens: Client: - Program: - Probably need to turn on cors at some point - Account: - Need to add in settings / data pages - After a new account is created notify a user that they need to verify their email before logging in - - ProductController: - Need to figure out new way to download purchased items as there is currently no way - - Store: - Edit product needs created - Need to add cart back - Need to add in payment page - Need to add in payment success/failed - Need to add in Receipt page - - TopBar: - No way to minimize the UI topbar on mobile - Not themed on mobile - - API: - Some of the API's Changed. Need to go back and update the client API calls database: - Add Created and Modified to the JobListing Table - So JobListing's will only run for a set number of time before auto closing - - Add Deleted bool to the JobListing Table - Dont Delete Job Postings -> Simply mark as deleted to check for duplicate postings. - - Add Applied Jobs Table \ No newline at end of file + Add Applied Jobs Table + Make sure that input datatypes are MySQL safe. Such as DateTime's \ No newline at end of file From dd7b7e6de67afe7827962f2925ad278b084555e0 Mon Sep 17 00:00:00 2001 From: "derek.holloway" Date: Tue, 15 Jul 2025 11:02:30 -0700 Subject: [PATCH 28/37] Cleanup --- .../Controllers/AuthenticationController.cs | 2 +- src/Server/Controllers/PaymentController.cs | 23 +------------------ .../Controllers/PaymentMethods/IPayment.cs | 2 +- .../PaymentMethods/StripeIntents.cs | 3 +-- src/Server/Services/DatabaseService/Resume.cs | 1 - 5 files changed, 4 insertions(+), 27 deletions(-) diff --git a/src/Server/Controllers/AuthenticationController.cs b/src/Server/Controllers/AuthenticationController.cs index e5dfdbb..5eaf19e 100755 --- a/src/Server/Controllers/AuthenticationController.cs +++ b/src/Server/Controllers/AuthenticationController.cs @@ -8,7 +8,7 @@ using BoredCareers.Entities; namespace BoredCareers.Controllers { [ApiController] - [Route("api/account/[controller]")] + [Route("api/account/")] public class AuthenticationController : MistoxControllerBase { EmailService _emailContext; diff --git a/src/Server/Controllers/PaymentController.cs b/src/Server/Controllers/PaymentController.cs index 17917c7..0d9fdbf 100755 --- a/src/Server/Controllers/PaymentController.cs +++ b/src/Server/Controllers/PaymentController.cs @@ -1,11 +1,10 @@ using Microsoft.AspNetCore.Mvc; using BoredCareers.Controllers.Payment; using BoredCareers.Services.DatabaseService; -using BoredCareers.Entities; namespace BoredCareers.Controllers { [ApiController] - [Route("api/payment/[controller]")] + [Route("api/payment/")] public class PaymentController : MistoxControllerBase { IPayment _paymentService; @@ -20,26 +19,6 @@ namespace BoredCareers.Controllers { // Add new payment plugins here } - [Route("getcheckouttoken")] - [HttpPost] - public async Task GetCheckoutToken() { - string OrderNumber = Guid.NewGuid().ToString().Substring(0, 10); - if (isLoggedIn()) { - Cart[] carts = await _databaseService.GetCart(getLoggedInUserID()); - (bool, string) PaymentResponse = await _paymentService.TryGetCheckoutToken(OrderNumber, getLoggedInUserID(), carts); - if (PaymentResponse.Item1) { - // Returns client secret - return PaymentResponse.Item2; - } else { - Console.WriteLine("An error has occured in the payment plugin\n\n"); - Console.WriteLine(PaymentResponse.Item2); - Console.WriteLine("\n"); - return "An error has occured in the payment plugin"; - } - } - return "You must be logged in"; - } - [Route("getpublickey")] [HttpPost] public IActionResult GetPublicKey() { diff --git a/src/Server/Controllers/PaymentMethods/IPayment.cs b/src/Server/Controllers/PaymentMethods/IPayment.cs index 3ade142..5be09a3 100644 --- a/src/Server/Controllers/PaymentMethods/IPayment.cs +++ b/src/Server/Controllers/PaymentMethods/IPayment.cs @@ -8,7 +8,7 @@ namespace BoredCareers.Controllers.Payment { public static string _EndpointSecret = ""; public static string _PublicKey = ""; - public Task<(bool, string)> TryGetCheckoutToken(string OrderNumber, int userID, Cart[] cart); + public Task<(bool, string)> TryGetCheckoutToken(string OrderNumber, int userID); public Task ValidatePurchase(string WebHookData, string Headers); } diff --git a/src/Server/Controllers/PaymentMethods/StripeIntents.cs b/src/Server/Controllers/PaymentMethods/StripeIntents.cs index 2e5f12f..f6110da 100644 --- a/src/Server/Controllers/PaymentMethods/StripeIntents.cs +++ b/src/Server/Controllers/PaymentMethods/StripeIntents.cs @@ -1,6 +1,5 @@ using BoredCareers.Controllers.Payment; using BoredCareers.Services.DatabaseService; -using BoredCareers.Entities; namespace BoredCareers.Controllers { @@ -12,7 +11,7 @@ namespace BoredCareers.Controllers { _databaseService = databaseService; } - public async Task<(bool, string)> TryGetCheckoutToken(string OrderNumber, int userID, Cart[] cart) { + public async Task<(bool, string)> TryGetCheckoutToken(string OrderNumber, int userID) { try { // build Recipt and calculate Tax var options = new Stripe.Tax.CalculationCreateOptions { diff --git a/src/Server/Services/DatabaseService/Resume.cs b/src/Server/Services/DatabaseService/Resume.cs index f5e2895..5050a10 100644 --- a/src/Server/Services/DatabaseService/Resume.cs +++ b/src/Server/Services/DatabaseService/Resume.cs @@ -1,6 +1,5 @@ using BoredCareers.Entities; using MySql.Data.MySqlClient; -using Stripe.Terminal; using System.Data; using System.Data.Common; From b93eb3ad21bc34ba2c2a149c73177d0a5897973d Mon Sep 17 00:00:00 2001 From: "derek.holloway" Date: Tue, 15 Jul 2025 11:02:39 -0700 Subject: [PATCH 29/37] Add ResumeController --- src/Server/Controllers/ResumeController.cs | 63 ++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 src/Server/Controllers/ResumeController.cs diff --git a/src/Server/Controllers/ResumeController.cs b/src/Server/Controllers/ResumeController.cs new file mode 100644 index 0000000..9e12fc3 --- /dev/null +++ b/src/Server/Controllers/ResumeController.cs @@ -0,0 +1,63 @@ +using Microsoft.AspNetCore.Mvc; +using BoredCareers.Services.DatabaseService; +using BoredCareers.Entities; +using System.Web.Http; + +namespace BoredCareers.Controllers { + [ApiController] + [Route("api/resume/")] + public class ResumeController : MistoxControllerBase { + + public ResumeController(DatabaseService db) : base(db) {} + + [Route("getall")] + [HttpPost] + public async Task GetResumes() { + if (isLoggedIn()) { + int accountID = getLoggedInUserID(); + Resume[] resumes = await _databaseService.GetResumes(accountID); + return Ok(resumes); + } + return NotFound(); + } + + [Route("getfull")] + [HttpPost] + public async Task GetResume([FromForm] int ResumeID) { + Resume? resume = await _databaseService.GetResume(ResumeID); + if (resume == null) { + return Ok(resume); + } + return NotFound(); + } + + [Route("set")] + [HttpPost] + public async Task SetResume([FromBody] Resume resume) { + if (isLoggedIn()) { + int AccountID = getLoggedInUserID(); + if (resume.AccountID == AccountID) { + await _databaseService.SetResume(resume); + return Ok(); + } + } + return NotFound(); + } + + [Route("delete")] + [HttpPost] + public async Task DeleteResume([FromForm] int ResumeID) { + if (isLoggedIn()){ + int accountID = getLoggedInUserID(); + Resume? resume = await _databaseService.GetResume(ResumeID); + if (resume != null && resume.AccountID == accountID) { + await _databaseService.DeleteResume(ResumeID); + return Ok(); + } + } + return NotFound(); + } + + } + +} From c04877464a7a49357497dc5f2b2cf5120dddef08 Mon Sep 17 00:00:00 2001 From: "derek.holloway" Date: Tue, 15 Jul 2025 14:38:02 -0700 Subject: [PATCH 30/37] Add RateLimiting For API --- .../Controllers/AuthenticationController.cs | 3 ++- src/Server/Program.cs | 27 ++++++++++++++++--- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/Server/Controllers/AuthenticationController.cs b/src/Server/Controllers/AuthenticationController.cs index 5eaf19e..9b37afd 100755 --- a/src/Server/Controllers/AuthenticationController.cs +++ b/src/Server/Controllers/AuthenticationController.cs @@ -34,7 +34,8 @@ namespace BoredCareers.Controllers { await _databaseService.SetAccount(test); List claims = new List() { - new Claim("ID", test.ID.ToString()) + new Claim("ID", test.ID.ToString()), + new Claim(ClaimTypes.NameIdentifier, test.ID.ToString()) }; await HttpContext.SignInAsync( diff --git a/src/Server/Program.cs b/src/Server/Program.cs index 15d312b..1df3be9 100755 --- a/src/Server/Program.cs +++ b/src/Server/Program.cs @@ -2,7 +2,10 @@ using Microsoft.AspNetCore.Authentication.Cookies; using BoredCareers.Controllers.Payment; using BoredCareers.Services; using BoredCareers.Services.DatabaseService; +using System.Threading.RateLimiting; +using Microsoft.AspNetCore.RateLimiting; using Stripe; +using System.Security.Claims; var builder = WebApplication.CreateBuilder(args); @@ -90,9 +93,27 @@ builder.Services.AddAuthentication( options => { options.SlidingExpiration = true; }); -builder.Services.AddCors( o => o.AddDefaultPolicy( builder => { +builder.Services.AddCors(o => o.AddDefaultPolicy(builder => { builder.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader(); // No CORS -} ) ); +})); + +builder.Services.AddRateLimiter(options => { + options.AddPolicy("PerUserPolicy", httpContext => { + // Identify the user (assumes authenticated user with NameIdentifier claim) + var userId = httpContext.User.FindFirst(ClaimTypes.NameIdentifier)?.Value + ?? httpContext.User.Identity?.Name + ?? httpContext.Connection.RemoteIpAddress?.ToString(); + + return RateLimitPartition.GetTokenBucketLimiter(userId, key => new TokenBucketRateLimiterOptions { + TokenLimit = 10, // max 10 requests + QueueProcessingOrder = QueueProcessingOrder.OldestFirst, + QueueLimit = 0, + ReplenishmentPeriod = TimeSpan.FromSeconds(15), + TokensPerPeriod = 2, + AutoReplenishment = true + }); + }); +}); // Pages Service builder.Services.AddControllers(); @@ -113,7 +134,7 @@ app.UseCors(); app.UseRouting(); app.UseAuthentication(); -app.MapControllers(); +app.MapControllers().RequireRateLimiting("perUserPolicy"); app.MapFallbackToFile("index.html"); From 055ca16b6c6a6ad2f7d7334ef88d66e5fe0699d2 Mon Sep 17 00:00:00 2001 From: "derek.holloway" Date: Tue, 15 Jul 2025 14:41:16 -0700 Subject: [PATCH 31/37] Start work on joblistingcontroller --- ToDo.yaml | 2 + .../Controllers/JobListingController.cs | 49 +++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 src/Server/Controllers/JobListingController.cs diff --git a/ToDo.yaml b/ToDo.yaml index cdc199a..be54de0 100755 --- a/ToDo.yaml +++ b/ToDo.yaml @@ -11,6 +11,8 @@ Server: Need to timeout email reset tokens: + Need to impliment Reset / Delte JobListingContorller + Client: diff --git a/src/Server/Controllers/JobListingController.cs b/src/Server/Controllers/JobListingController.cs new file mode 100644 index 0000000..f177fbe --- /dev/null +++ b/src/Server/Controllers/JobListingController.cs @@ -0,0 +1,49 @@ +using Microsoft.AspNetCore.Mvc; +using BoredCareers.Services.DatabaseService; +using BoredCareers.Entities; +using System.Web.Http; + +namespace BoredCareers.Controllers { + [ApiController] + [Route("api/joblisting/")] + public class JobListingController : MistoxControllerBase { + + public JobListingController(DatabaseService db) : base(db) {} + + [Route("getlistings")] + [HttpPost] + public async Task GetJobListings([FromForm] int page) { + if (isLoggedIn()) { + JobListing[] jobListings = await _databaseService.GetJobListingPage(page, 25); // 10 items per page + return Ok(jobListings); + } + return NotFound(); + } + + [Route("getlisting")] + [HttpPost] + public async Task GetJobListing([FromForm] int JobListingID) { + JobListing? jobListing = await _databaseService.GetJobListing(JobListingID); + if (jobListing == null) { + return Ok(jobListing); + } + return NotFound(); + } + + [Route("set")] + [HttpPost] + public async Task SetJobListing([FromBody] Resume resume) { + await Task.Delay(1); + return Ok(); + } + + [Route("delete")] + [HttpPost] + public async Task DeleteJobListing([FromForm] int ResumeID) { + await Task.Delay(1); + return Ok(); + } + + } + +} From 0df9818139ae2c4c5fa4c3b30b7e2c2ae8be6b8b Mon Sep 17 00:00:00 2001 From: Derek Holloway Date: Tue, 15 Jul 2025 16:53:45 -0700 Subject: [PATCH 32/37] Convert all DateTime's to UTC --- ToDo.yaml | 3 +-- src/Server/Services/DatabaseService/JobListing.cs | 4 ++-- .../DatabaseService/ResumeParts/SetResumeParts.cs | 12 ++++++------ 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/ToDo.yaml b/ToDo.yaml index be54de0..4d6307c 100755 --- a/ToDo.yaml +++ b/ToDo.yaml @@ -17,5 +17,4 @@ Client: database: - Add Applied Jobs Table - Make sure that input datatypes are MySQL safe. Such as DateTime's \ No newline at end of file + Add Applied Jobs Table \ No newline at end of file diff --git a/src/Server/Services/DatabaseService/JobListing.cs b/src/Server/Services/DatabaseService/JobListing.cs index fb1f00b..384fa84 100644 --- a/src/Server/Services/DatabaseService/JobListing.cs +++ b/src/Server/Services/DatabaseService/JobListing.cs @@ -158,8 +158,8 @@ namespace BoredCareers.Services.DatabaseService { cmd.Parameters.AddWithValue("@JobType", jobListing.JobType); cmd.Parameters.AddWithValue("@Remote", jobListing.Remote); cmd.Parameters.AddWithValue("@Description", jobListing.Description); - cmd.Parameters.AddWithValue("@CreatedTime", jobListing.CreatedTime); - cmd.Parameters.AddWithValue("@ModifiedTime", jobListing.ModifiedTime); + cmd.Parameters.AddWithValue("@CreatedTime", jobListing.CreatedTime.ToUniversalTime()); + cmd.Parameters.AddWithValue("@ModifiedTime", jobListing.ModifiedTime.ToUniversalTime()); cmd.Parameters.AddWithValue("@IsDeleted", jobListing.IsDeleted); await cmd.ExecuteNonQueryAsync(); diff --git a/src/Server/Services/DatabaseService/ResumeParts/SetResumeParts.cs b/src/Server/Services/DatabaseService/ResumeParts/SetResumeParts.cs index 2968e4e..d82992a 100644 --- a/src/Server/Services/DatabaseService/ResumeParts/SetResumeParts.cs +++ b/src/Server/Services/DatabaseService/ResumeParts/SetResumeParts.cs @@ -90,9 +90,9 @@ namespace BoredCareers.Services.DatabaseService { cmd.Parameters.AddWithValue("@JobTitle", cur.JobTitle); cmd.Parameters.AddWithValue("@Company", cur.Company); cmd.Parameters.AddWithValue("@PostalCode", cur.PostalCode); - cmd.Parameters.AddWithValue("@DateStarted", cur.DateStarted); + cmd.Parameters.AddWithValue("@DateStarted", cur.DateStarted.ToUniversalTime()); cmd.Parameters.AddWithValue("@StillEmployed", cur.StillEmployed); - cmd.Parameters.AddWithValue("@DateEnded", cur.DateEnded); + cmd.Parameters.AddWithValue("@DateEnded", cur.DateEnded.ToUniversalTime()); await cmd.ExecuteNonQueryAsync(); } @@ -143,9 +143,9 @@ namespace BoredCareers.Services.DatabaseService { cmd.Parameters.AddWithValue("@ResumeID", military.ResumeID); cmd.Parameters.AddWithValue("@Country", military.Country); cmd.Parameters.AddWithValue("@Rank", military.Rank); - cmd.Parameters.AddWithValue("@DateStarted", military.DateStarted); + cmd.Parameters.AddWithValue("@DateStarted", military.DateStarted.ToUniversalTime()); cmd.Parameters.AddWithValue("@StillServing", military.StillServing); - cmd.Parameters.AddWithValue("@DateEnded", military.DateEnded); + cmd.Parameters.AddWithValue("@DateEnded", military.DateEnded.ToUniversalTime()); await cmd.ExecuteNonQueryAsync(); } @@ -181,9 +181,9 @@ namespace BoredCareers.Services.DatabaseService { cmd.Parameters.AddWithValue("@Country", cur.Country); cmd.Parameters.AddWithValue("@StateOrRegion", cur.StateOrRegion); cmd.Parameters.AddWithValue("@City", cur.City); - cmd.Parameters.AddWithValue("@DateStarted", cur.DateStarted); + cmd.Parameters.AddWithValue("@DateStarted", cur.DateStarted.ToUniversalTime()); cmd.Parameters.AddWithValue("@StillStudying", cur.StillStudying); - cmd.Parameters.AddWithValue("@DateEnded", cur.DateEnded); + cmd.Parameters.AddWithValue("@DateEnded", cur.DateEnded.ToUniversalTime()); await cmd.ExecuteNonQueryAsync(); } From 15d41985e6cdff309e820e53593af8c6db628775 Mon Sep 17 00:00:00 2001 From: Derek Holloway Date: Tue, 15 Jul 2025 17:32:29 -0700 Subject: [PATCH 33/37] Rename CompanyUser to Employee --- database/mistox.sql | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/database/mistox.sql b/database/mistox.sql index f428885..1308b01 100755 --- a/database/mistox.sql +++ b/database/mistox.sql @@ -160,11 +160,13 @@ CREATE TABLE IF NOT EXISTS `Company` ( PRIMARY KEY (`ID`) ) AUTO_INCREMENT=1; -CREATE TABLE IF NOT EXISTS `CompanyUser` ( +CREATE TABLE IF NOT EXISTS `Employee` ( `ID` int NOT NULL AUTO_INCREMENT, `AccountID` int NOT NULL, `CompanyID` int NOT NULL, - PRIMARY KEY (`ID`) + PRIMARY KEY (`ID`), + FOREIGN KEY (`AccountID`) REFERENCES `Account`(`ID`) ON DELETE CASCADE, + FOREIGN KEY (`CompanyID`) REFERENCES `Company`(`ID`) ON DELETE CASCADE ) AUTO_INCREMENT=1; CREATE TABLE IF NOT EXISTS `JobListing` ( From d8f6d606ae0cc9b385af0b0dba29d5908a741215 Mon Sep 17 00:00:00 2001 From: Derek Holloway Date: Tue, 15 Jul 2025 17:33:04 -0700 Subject: [PATCH 34/37] Cleanup for CompanyUser rename --- src/Server/Entities/Company.cs | 2 +- .../Services/DatabaseService/Employee.cs | 79 +++++++++++++++++++ 2 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 src/Server/Services/DatabaseService/Employee.cs diff --git a/src/Server/Entities/Company.cs b/src/Server/Entities/Company.cs index 40b57f4..e0a5a97 100644 --- a/src/Server/Entities/Company.cs +++ b/src/Server/Entities/Company.cs @@ -15,7 +15,7 @@ namespace BoredCareers.Entities { public string Description { get; set; } = ""; } - public class CompanyUser { + public class Employee { public int ID { get; set; } // PK public int AccountID { get; set; } // FK public int CompanyID { get; set; } // FK diff --git a/src/Server/Services/DatabaseService/Employee.cs b/src/Server/Services/DatabaseService/Employee.cs new file mode 100644 index 0000000..e317e89 --- /dev/null +++ b/src/Server/Services/DatabaseService/Employee.cs @@ -0,0 +1,79 @@ +using BoredCareers.Entities; +using MySql.Data.MySqlClient; +using System.Data; +using System.Data.Common; + +namespace BoredCareers.Services.DatabaseService { + public partial class DatabaseService { + + public async Task GetEmployees( int CompanyID ) { + List employees = new List(); + using( MySqlConnection connection = GetConnection() ) { + connection.Open(); + string command = @" + SELECT * + FROM Employee + WHERE ID = @ID; + "; + + MySqlCommand cmd = new MySqlCommand(command, connection); + cmd.Parameters.AddWithValue("@ID", CompanyID); + + using( DbDataReader reader = await cmd.ExecuteReaderAsync() ) { + while( await reader.ReadAsync() ) { + if( reader == null ) { break; } + int _id = reader.GetInt32("ID"); + int _accountid = reader.GetInt32("AccountID"); + int _companyid = reader.GetInt32("CompanyID"); + + employees.Add(new Employee() { + ID = _id, + AccountID = _accountid, + CompanyID = _companyid + }); + } + } + } + return employees.ToArray(); + } + + public async Task SetEmployee( Employee employee ) { + using( MySqlConnection connection = GetConnection() ) { + connection.Open(); + + string command = @" + INSERT INTO Employee + (ID,AccountID,CompanyID) + VALUES + (@ID,@AccountID,@CompanyID); + ON DUPLICATE KEY UPDATE + AccountID = @AccountID, + CompanyID = @CompanyID; + "; + + MySqlCommand cmd = new MySqlCommand( command , connection); + cmd.Parameters.AddWithValue("@ID", employee.ID); + cmd.Parameters.AddWithValue("@AccountID", employee.AccountID); + cmd.Parameters.AddWithValue("@CompanyID", employee.CompanyID); + + await cmd.ExecuteNonQueryAsync(); + } + } + + public async Task DeleteEmployee( int EmployeeID ) { + using( MySqlConnection connection = GetConnection() ) { + MySqlCommand cmd; + connection.Open(); + + string command = @" + DELETE FROM Employee WHERE ID = @ID; + "; + cmd = new MySqlCommand( command, connection ); + cmd.Parameters.AddWithValue("@ID", EmployeeID); + + await cmd.ExecuteNonQueryAsync(); + } + } + + } +} From 59944e5a661701e7140192838345937b9076df63 Mon Sep 17 00:00:00 2001 From: Derek Holloway Date: Tue, 15 Jul 2025 17:33:15 -0700 Subject: [PATCH 35/37] Finalize Services --- ToDo.yaml | 9 ---- src/Server/Controllers/CompanyController.cs | 51 +++++++++++++++++++ .../Controllers/JobListingController.cs | 34 ++++++++----- .../Controllers/MistoxControllerBase.cs | 10 ++++ src/Server/Controllers/ResumeController.cs | 2 +- 5 files changed, 83 insertions(+), 23 deletions(-) create mode 100644 src/Server/Controllers/CompanyController.cs diff --git a/ToDo.yaml b/ToDo.yaml index 4d6307c..80938c7 100755 --- a/ToDo.yaml +++ b/ToDo.yaml @@ -2,17 +2,8 @@ Server: Emails: Dont follow theme of website - Admin Functions: - Need to inforce Admin on the API side - - Authentication ProductController: - When the create account is called. right after the getaccount is called. - Have all New for database return the object they create - Need to timeout email reset tokens: - Need to impliment Reset / Delte JobListingContorller - Client: diff --git a/src/Server/Controllers/CompanyController.cs b/src/Server/Controllers/CompanyController.cs new file mode 100644 index 0000000..799f4f0 --- /dev/null +++ b/src/Server/Controllers/CompanyController.cs @@ -0,0 +1,51 @@ +using Microsoft.AspNetCore.Mvc; +using BoredCareers.Services.DatabaseService; +using BoredCareers.Entities; +using System.Web.Http; + +namespace BoredCareers.Controllers { + [ApiController] + [Route("api/company/")] + public class CompanyController : MistoxControllerBase { + + public CompanyController(DatabaseService db) : base(db) {} + + [Route("get")] + [HttpPost] + public async Task GetCompany([FromForm] int companyID) { + if (isLoggedIn()) { + Company? company = await _databaseService.GetCompany(companyID); + if (company != null) { + return Ok(company); + } + } + return NotFound(); + } + + [Route("set")] + [HttpPost] + public async Task SetCompany([FromBody] Company company) { + if (isLoggedIn()) { + if (await isLoggedInUserEmployeeOf(company.ID)) { + await _databaseService.SetCompany(company); + return Ok(); + } + } + return NotFound(); + } + + [Route("delete")] + [HttpPost] + public async Task DeleteCompany([FromForm] int CompanyID) { + if (isLoggedIn()) { + if (await isLoggedInUserEmployeeOf(CompanyID)) { + await _databaseService.DeleteCompany(CompanyID); + return Ok(); + } + } + return NotFound(); + } + + } + +} diff --git a/src/Server/Controllers/JobListingController.cs b/src/Server/Controllers/JobListingController.cs index f177fbe..805d12a 100644 --- a/src/Server/Controllers/JobListingController.cs +++ b/src/Server/Controllers/JobListingController.cs @@ -10,17 +10,14 @@ namespace BoredCareers.Controllers { public JobListingController(DatabaseService db) : base(db) {} - [Route("getlistings")] + [Route("getpage")] [HttpPost] public async Task GetJobListings([FromForm] int page) { - if (isLoggedIn()) { - JobListing[] jobListings = await _databaseService.GetJobListingPage(page, 25); // 10 items per page - return Ok(jobListings); - } - return NotFound(); + JobListing[] jobListings = await _databaseService.GetJobListingPage(page, 25); // 10 items per page + return Ok(jobListings); } - [Route("getlisting")] + [Route("get")] [HttpPost] public async Task GetJobListing([FromForm] int JobListingID) { JobListing? jobListing = await _databaseService.GetJobListing(JobListingID); @@ -32,16 +29,27 @@ namespace BoredCareers.Controllers { [Route("set")] [HttpPost] - public async Task SetJobListing([FromBody] Resume resume) { - await Task.Delay(1); - return Ok(); + public async Task SetJobListing([FromBody] JobListing jobListing) { + if (isLoggedIn()) { + if (await isLoggedInUserEmployeeOf(jobListing.CompanyID)) { + await _databaseService.SetJobListing(jobListing); + } + } + return NotFound(); } [Route("delete")] [HttpPost] - public async Task DeleteJobListing([FromForm] int ResumeID) { - await Task.Delay(1); - return Ok(); + public async Task DeleteJobListing([FromForm] int JobListingID) { + if (isLoggedIn()) { + JobListing? jobListing = await _databaseService.GetJobListing(JobListingID); + if (jobListing != null) { + if (await isLoggedInUserEmployeeOf(JobListingID)) { + await _databaseService.DeleteJobListing(JobListingID); + } + } + } + return NotFound(); } } diff --git a/src/Server/Controllers/MistoxControllerBase.cs b/src/Server/Controllers/MistoxControllerBase.cs index daed8cc..0084227 100644 --- a/src/Server/Controllers/MistoxControllerBase.cs +++ b/src/Server/Controllers/MistoxControllerBase.cs @@ -35,6 +35,16 @@ namespace BoredCareers.Controllers { } } + public async Task isLoggedInUserEmployeeOf(int CompanyID) { + Employee[] employees = await _databaseService.GetEmployees(CompanyID); + foreach (Employee cur in employees) { + if (getLoggedInUserID() == cur.AccountID) { + return true; + } + } + return false; + } + public string Substitue(string message, string subString, string Replacement) { for (int i = 0; i < (message.Length - subString.Length); i++) { if (message.Substring(i, subString.Length) == subString) { diff --git a/src/Server/Controllers/ResumeController.cs b/src/Server/Controllers/ResumeController.cs index 9e12fc3..01d6c67 100644 --- a/src/Server/Controllers/ResumeController.cs +++ b/src/Server/Controllers/ResumeController.cs @@ -21,7 +21,7 @@ namespace BoredCareers.Controllers { return NotFound(); } - [Route("getfull")] + [Route("get")] [HttpPost] public async Task GetResume([FromForm] int ResumeID) { Resume? resume = await _databaseService.GetResume(ResumeID); From 9e13317ca393df6f3b82bf00c1c391904f23f782 Mon Sep 17 00:00:00 2001 From: Derek Holloway Date: Tue, 15 Jul 2025 21:06:16 -0700 Subject: [PATCH 36/37] Update API to follow REST --- .../Controllers/AuthenticationController.cs | 93 ++++++++++--------- src/Server/Controllers/CompanyController.cs | 15 ++- .../Controllers/JobListingController.cs | 36 ++++--- src/Server/Controllers/PaymentController.cs | 12 +-- src/Server/Controllers/ResumeController.cs | 41 ++++---- src/Server/Program.cs | 8 +- .../ResumeParts/SetResumeParts.cs | 2 - 7 files changed, 95 insertions(+), 112 deletions(-) diff --git a/src/Server/Controllers/AuthenticationController.cs b/src/Server/Controllers/AuthenticationController.cs index 9b37afd..4a532da 100755 --- a/src/Server/Controllers/AuthenticationController.cs +++ b/src/Server/Controllers/AuthenticationController.cs @@ -5,6 +5,8 @@ using System.Security.Claims; using BoredCareers.Services; using BoredCareers.Services.DatabaseService; using BoredCareers.Entities; +using Microsoft.AspNetCore.Http.HttpResults; +using System.Web.Http; namespace BoredCareers.Controllers { [ApiController] @@ -26,7 +28,7 @@ namespace BoredCareers.Controllers { if (test.EmailVerified == true) { if (test.FailedPasswordLock) { if (test.CurrentPasswordAttempts >= test.PasswordAttempts) { - return new Account() { ID = -1, UserName = "Too many failed password attempts. Please reset your password" }; + return NotFound("Too many failed password attempts. Please reset your password"); } } if (BCrypt.Net.BCrypt.Verify(PasswordHash, test.PasswordHash)) { @@ -47,21 +49,20 @@ namespace BoredCareers.Controllers { } ); return test; - } - else { + } else { test.CurrentPasswordAttempts += 1; await _databaseService.SetAccount(test); - return new Account() { ID = -1, UserName = "Wrong Password" }; + return Ok(new Account() { ID = -1, UserName = "Wrong Password" }); } - } - else { + } else { await SendVerify(test.UserName); - return new Account() { ID = -1, UserName = "A new verify email has been sent. \n Note only 1 email send every 5 mintes" }; + return NotFound("A new verify email has been sent. \n Note only 1 email send every 5 mintes"); } } - return new Account() { ID = -1, UserName = "User doesn't exist" }; + return NotFound("Account Not Found"); } catch (Exception ex) { - return new Account() { ID = -1, UserName = ex.Message }; + Console.WriteLine("Login Error: " + ex.Message); + return NotFound(); } } @@ -71,37 +72,35 @@ namespace BoredCareers.Controllers { try { if (await _databaseService.GetAccount(UserName.ToLower()) == null) { if (await _databaseService.GetAccount(Email.ToLower()) == null) { - Account? created = new Account() { + Account created = new Account() { UserName = UserName.ToLower(), Email = Email.ToLower(), EmailVerified = false, PasswordHash = BCrypt.Net.BCrypt.HashPassword(PasswordHash), }; await _databaseService.SetAccount(created); - created = await _databaseService.GetAccount(Email.ToLower()); - if (created != null) { - await SendVerify(created.UserName); - return created; + Account? loadedAccount = await _databaseService.GetAccount(Email.ToLower()); + if (loadedAccount != null) { + await SendVerify(loadedAccount.UserName); + return Ok(loadedAccount); } - return new Account() { ID = -1, UserName = "Unknown Error" }; + return NotFound("Unable to create the account"); + } else { + return NotFound("Email is already in use"); } - else { - return new Account() { ID = -1, UserName = "Email is already in use" }; - } - } - else { - return new Account() { ID = -1, UserName = "UserName is taken" }; + } else { + return NotFound("UserName is taken"); } } catch (Exception ex) { - Console.WriteLine("Error: " + ex.Message); - return new Account() { ID = -1, UserName = ex.Message }; + Console.WriteLine("Register Error: " + ex.Message); + return NotFound(); } } [Route("changepassword")] [HttpPost] - public async Task> ChangePassword([FromForm] string OldPassword, [FromForm] string NewPassword) { + public async Task ChangePassword([FromForm] string OldPassword, [FromForm] string NewPassword) { try { if (isLoggedIn()) { Account user = await getLoggedInUser(); @@ -109,12 +108,13 @@ namespace BoredCareers.Controllers { user.PasswordHash = BCrypt.Net.BCrypt.HashPassword(NewPassword); user.CurrentPasswordAttempts = 0; await _databaseService.SetAccount(user); - return true; + return Ok(); } } - return false; - } catch { - return false; + return NotFound(); + } catch (Exception ex) { + Console.WriteLine("ChangePassword Error: " + ex.Message); + return NotFound(); } } @@ -127,31 +127,37 @@ namespace BoredCareers.Controllers { user.FailedPasswordLock = AccountLock; user.CurrentPasswordAttempts = 0; await _databaseService.SetAccount(user); - return "Account Lock Status Updated"; + return Ok(); } - return "Unknown Error Occurred"; + return NotFound(); } catch (Exception ex) { - return ex.Message; + Console.WriteLine("ToggleAccountLock Error: " + ex.Message); + return NotFound(); } } [Route("get")] [HttpPost] - public async Task> Get() { + public async Task> Get() { try { if (isLoggedIn()) { - return await getLoggedInUser(); + return Ok(await getLoggedInUser()); } - return Ok(); - } catch { - return Ok(); + return NotFound(); + } catch (Exception ex) { + Console.WriteLine("Get Error: " + ex); + return NotFound(); } } [Route("logout")] [HttpPost] - public async Task Logout() { - await HttpContext.SignOutAsync(); + public async Task Logout() { + if (isLoggedIn()) { + await HttpContext.SignOutAsync(); + return Ok(); + } + return NotFound(); } [Route("sendverifyemail")] @@ -267,18 +273,19 @@ namespace BoredCareers.Controllers { [Route("delete")] [HttpPost] - public async Task> delete([FromForm] string Password) { + public async Task delete([FromForm] string Password) { try { if (isLoggedIn()) { Account user = await getLoggedInUser(); if (BCrypt.Net.BCrypt.Verify(Password, user.PasswordHash)) { await _databaseService.DeleteAccount(user.ID); - return true; + return Ok(); } } - return false; - } catch { - return false; + return NotFound(); + } catch (Exception ex) { + Console.WriteLine("Delete Error: " + ex.Message); + return NotFound(); } } diff --git a/src/Server/Controllers/CompanyController.cs b/src/Server/Controllers/CompanyController.cs index 799f4f0..cb644a2 100644 --- a/src/Server/Controllers/CompanyController.cs +++ b/src/Server/Controllers/CompanyController.cs @@ -5,16 +5,15 @@ using System.Web.Http; namespace BoredCareers.Controllers { [ApiController] - [Route("api/company/")] + [Route("api/company")] public class CompanyController : MistoxControllerBase { public CompanyController(DatabaseService db) : base(db) {} - [Route("get")] - [HttpPost] - public async Task GetCompany([FromForm] int companyID) { + [HttpGet] + public async Task GetCompany(int CompanyID) { if (isLoggedIn()) { - Company? company = await _databaseService.GetCompany(companyID); + Company? company = await _databaseService.GetCompany(CompanyID); if (company != null) { return Ok(company); } @@ -22,7 +21,6 @@ namespace BoredCareers.Controllers { return NotFound(); } - [Route("set")] [HttpPost] public async Task SetCompany([FromBody] Company company) { if (isLoggedIn()) { @@ -34,9 +32,8 @@ namespace BoredCareers.Controllers { return NotFound(); } - [Route("delete")] - [HttpPost] - public async Task DeleteCompany([FromForm] int CompanyID) { + [HttpDelete] + public async Task DeleteCompany(int CompanyID) { if (isLoggedIn()) { if (await isLoggedInUserEmployeeOf(CompanyID)) { await _databaseService.DeleteCompany(CompanyID); diff --git a/src/Server/Controllers/JobListingController.cs b/src/Server/Controllers/JobListingController.cs index 805d12a..495fb0b 100644 --- a/src/Server/Controllers/JobListingController.cs +++ b/src/Server/Controllers/JobListingController.cs @@ -5,42 +5,38 @@ using System.Web.Http; namespace BoredCareers.Controllers { [ApiController] - [Route("api/joblisting/")] + [Route("api/joblisting")] public class JobListingController : MistoxControllerBase { public JobListingController(DatabaseService db) : base(db) {} - [Route("getpage")] - [HttpPost] - public async Task GetJobListings([FromForm] int page) { - JobListing[] jobListings = await _databaseService.GetJobListingPage(page, 25); // 10 items per page - return Ok(jobListings); - } - - [Route("get")] - [HttpPost] - public async Task GetJobListing([FromForm] int JobListingID) { - JobListing? jobListing = await _databaseService.GetJobListing(JobListingID); - if (jobListing == null) { + [HttpGet("{JobListingID}")] + public async Task GetJobListing([FromRoute] int JobListingID) { + var jobListing = await _databaseService.GetJobListing(JobListingID); + if (jobListing != null) { return Ok(jobListing); } return NotFound(); } - [Route("set")] + [HttpGet] + public async Task GetJobListings(int Page = 1, int PageQuantity = 25) { + JobListing[] jobListings = await _databaseService.GetJobListingPage(Page, PageQuantity); + return Ok(jobListings); + } + [HttpPost] - public async Task SetJobListing([FromBody] JobListing jobListing) { + public async Task SetJobListing([FromBody] JobListing JobListing) { if (isLoggedIn()) { - if (await isLoggedInUserEmployeeOf(jobListing.CompanyID)) { - await _databaseService.SetJobListing(jobListing); + if (await isLoggedInUserEmployeeOf(JobListing.CompanyID)) { + await _databaseService.SetJobListing(JobListing); } } return NotFound(); } - [Route("delete")] - [HttpPost] - public async Task DeleteJobListing([FromForm] int JobListingID) { + [HttpDelete] + public async Task DeleteJobListing(int JobListingID) { if (isLoggedIn()) { JobListing? jobListing = await _databaseService.GetJobListing(JobListingID); if (jobListing != null) { diff --git a/src/Server/Controllers/PaymentController.cs b/src/Server/Controllers/PaymentController.cs index 0d9fdbf..31e0537 100755 --- a/src/Server/Controllers/PaymentController.cs +++ b/src/Server/Controllers/PaymentController.cs @@ -4,7 +4,7 @@ using BoredCareers.Services.DatabaseService; namespace BoredCareers.Controllers { [ApiController] - [Route("api/payment/")] + [Route("api/payment")] public class PaymentController : MistoxControllerBase { IPayment _paymentService; @@ -19,17 +19,11 @@ namespace BoredCareers.Controllers { // Add new payment plugins here } - [Route("getpublickey")] - [HttpPost] + [HttpGet] public IActionResult GetPublicKey() { - try { - return Ok(IPayment._PublicKey); - } catch (Exception ex) { - return NotFound(ex.ToString()); - } + return Ok(IPayment._PublicKey); } - [Route("response")] [HttpPost] public async Task paymentWebhook() { try { diff --git a/src/Server/Controllers/ResumeController.cs b/src/Server/Controllers/ResumeController.cs index 01d6c67..65b9099 100644 --- a/src/Server/Controllers/ResumeController.cs +++ b/src/Server/Controllers/ResumeController.cs @@ -5,33 +5,28 @@ using System.Web.Http; namespace BoredCareers.Controllers { [ApiController] - [Route("api/resume/")] + [Route("api/resume")] public class ResumeController : MistoxControllerBase { public ResumeController(DatabaseService db) : base(db) {} - [Route("getall")] - [HttpPost] - public async Task GetResumes() { - if (isLoggedIn()) { - int accountID = getLoggedInUserID(); - Resume[] resumes = await _databaseService.GetResumes(accountID); - return Ok(resumes); + [HttpGet] + public async Task GetResume(int? ResumeID) { + if (ResumeID != null) { + Resume? resume = await _databaseService.GetResume(ResumeID.Value); + if (resume != null) { + return Ok(resume); + } + }else{ + if (isLoggedIn()) { + int accountID = getLoggedInUserID(); + Resume[] resumes = await _databaseService.GetResumes(accountID); + return Ok(resumes); + } } return NotFound(); } - [Route("get")] - [HttpPost] - public async Task GetResume([FromForm] int ResumeID) { - Resume? resume = await _databaseService.GetResume(ResumeID); - if (resume == null) { - return Ok(resume); - } - return NotFound(); - } - - [Route("set")] [HttpPost] public async Task SetResume([FromBody] Resume resume) { if (isLoggedIn()) { @@ -44,9 +39,8 @@ namespace BoredCareers.Controllers { return NotFound(); } - [Route("delete")] - [HttpPost] - public async Task DeleteResume([FromForm] int ResumeID) { + [HttpDelete] + public async Task DeleteResume(int ResumeID) { if (isLoggedIn()){ int accountID = getLoggedInUserID(); Resume? resume = await _databaseService.GetResume(ResumeID); @@ -59,5 +53,4 @@ namespace BoredCareers.Controllers { } } - -} +} \ No newline at end of file diff --git a/src/Server/Program.cs b/src/Server/Program.cs index 1df3be9..bed8db4 100755 --- a/src/Server/Program.cs +++ b/src/Server/Program.cs @@ -3,7 +3,6 @@ using BoredCareers.Controllers.Payment; using BoredCareers.Services; using BoredCareers.Services.DatabaseService; using System.Threading.RateLimiting; -using Microsoft.AspNetCore.RateLimiting; using Stripe; using System.Security.Claims; @@ -99,10 +98,9 @@ builder.Services.AddCors(o => o.AddDefaultPolicy(builder => { builder.Services.AddRateLimiter(options => { options.AddPolicy("PerUserPolicy", httpContext => { - // Identify the user (assumes authenticated user with NameIdentifier claim) - var userId = httpContext.User.FindFirst(ClaimTypes.NameIdentifier)?.Value - ?? httpContext.User.Identity?.Name - ?? httpContext.Connection.RemoteIpAddress?.ToString(); + var userId = httpContext.User.FindFirst(ClaimTypes.NameIdentifier)?.Value + ?? httpContext.User.Identity?.Name + ?? httpContext.Connection.RemoteIpAddress?.ToString(); return RateLimitPartition.GetTokenBucketLimiter(userId, key => new TokenBucketRateLimiterOptions { TokenLimit = 10, // max 10 requests diff --git a/src/Server/Services/DatabaseService/ResumeParts/SetResumeParts.cs b/src/Server/Services/DatabaseService/ResumeParts/SetResumeParts.cs index d82992a..089f385 100644 --- a/src/Server/Services/DatabaseService/ResumeParts/SetResumeParts.cs +++ b/src/Server/Services/DatabaseService/ResumeParts/SetResumeParts.cs @@ -1,8 +1,6 @@ using BoredCareers.Entities; using MySql.Data.MySqlClient; -// TODO: Fix Data type mangling. Such As DateTime - namespace BoredCareers.Services.DatabaseService { public partial class DatabaseService { From d18dcbff5cd83abd1ec0344a80e1fc018a868abc Mon Sep 17 00:00:00 2001 From: Derek Holloway Date: Tue, 15 Jul 2025 21:09:20 -0700 Subject: [PATCH 37/37] Remove enough from payment service to compile --- .../Controllers/PaymentMethods/IPayment.cs | 1 - .../PaymentMethods/StripeIntents.cs | 81 ------------------- 2 files changed, 82 deletions(-) diff --git a/src/Server/Controllers/PaymentMethods/IPayment.cs b/src/Server/Controllers/PaymentMethods/IPayment.cs index 5be09a3..73196d3 100644 --- a/src/Server/Controllers/PaymentMethods/IPayment.cs +++ b/src/Server/Controllers/PaymentMethods/IPayment.cs @@ -8,7 +8,6 @@ namespace BoredCareers.Controllers.Payment { public static string _EndpointSecret = ""; public static string _PublicKey = ""; - public Task<(bool, string)> TryGetCheckoutToken(string OrderNumber, int userID); public Task ValidatePurchase(string WebHookData, string Headers); } diff --git a/src/Server/Controllers/PaymentMethods/StripeIntents.cs b/src/Server/Controllers/PaymentMethods/StripeIntents.cs index f6110da..fca5a5e 100644 --- a/src/Server/Controllers/PaymentMethods/StripeIntents.cs +++ b/src/Server/Controllers/PaymentMethods/StripeIntents.cs @@ -11,72 +11,6 @@ namespace BoredCareers.Controllers { _databaseService = databaseService; } - public async Task<(bool, string)> TryGetCheckoutToken(string OrderNumber, int userID) { - try { - // build Recipt and calculate Tax - var options = new Stripe.Tax.CalculationCreateOptions { - Currency = "usd", - CustomerDetails = new Stripe.Tax.CalculationCustomerDetailsOptions { - AddressSource = "billing", - }, - Expand = new List() { "line_items" }, - LineItems = new List() - }; - - List prods = new List(); - - // Add items to receipt - int subtotal = 0; - foreach (Cart items in cart) { - Product? product = await _databaseService.GetProduct(items.ProductID); - if (product != null) { - prods.Add(product.ID); - if (product != null) { - subtotal += product.Cost; - options.LineItems.Add(new Stripe.Tax.CalculationLineItemOptions { - Amount = product.Cost, - TaxCode = "txcd_10201000", // Tax code for downloadable digital games - Quantity = 1, - Reference = product.Name, - TaxBehavior = "exclusive" - }); - } - } - - } - - var service = new Stripe.Tax.CalculationService(); - Stripe.Tax.Calculation result = service.Create(options); - - string csv = ""; - foreach (int cur in prods) { - csv = csv + cur + ","; - } - - // Crate Payment Intent - Stripe.PaymentIntentCreateOptions paymentIntent = new Stripe.PaymentIntentCreateOptions() { - Amount = result.AmountTotal, - Currency = "usd", - Metadata = new Dictionary { - { "ordernumber", OrderNumber }, - { "user", userID.ToString() }, - { "products", csv }, - { "subtotal", subtotal.ToString() }, - { "total", result.AmountTotal.ToString() } - }, - StatementDescriptor = "Mistox.Net #" + OrderNumber - }; - - Stripe.PaymentIntentService intentService = new Stripe.PaymentIntentService(); - Stripe.PaymentIntent x = await intentService.CreateAsync(paymentIntent); - - return (true, x.ClientSecret); - } catch (Exception e) { - return (false, e.ToString()); - } - - } - public async Task ValidatePurchase(string WebHookData, string Headers) { Stripe.Event e = Stripe.EventUtility.ConstructEvent( WebHookData, Headers, IPayment._EndpointSecret ); if (e.Type == "payment_intent.succeeded") { @@ -113,22 +47,7 @@ namespace BoredCareers.Controllers { } } - // Clear the cart - await _databaseService.ClearCart(userID); - // Add data to misox receipt - for (int i = 0; i < productIDs.Count; i++) { - int product = productIDs[i]; - await _databaseService.NewReceipt(new Receipt { - AccountID = userID, - ProductID = product, - ReceiptID = orderNumber, - Time = DateTime.Now, - TaxAmount = total - subtotal, - TotalCost = total, - LineItem = i - }); - } } else { Console.WriteLine("Unhandled event type: {0}", e.Type); }