Update API to follow REST

This commit is contained in:
2025-07-15 21:06:16 -07:00
parent 59944e5a66
commit 9e13317ca3
7 changed files with 95 additions and 112 deletions
@@ -5,6 +5,8 @@ using System.Security.Claims;
using BoredCareers.Services; using BoredCareers.Services;
using BoredCareers.Services.DatabaseService; using BoredCareers.Services.DatabaseService;
using BoredCareers.Entities; using BoredCareers.Entities;
using Microsoft.AspNetCore.Http.HttpResults;
using System.Web.Http;
namespace BoredCareers.Controllers { namespace BoredCareers.Controllers {
[ApiController] [ApiController]
@@ -26,7 +28,7 @@ namespace BoredCareers.Controllers {
if (test.EmailVerified == true) { if (test.EmailVerified == true) {
if (test.FailedPasswordLock) { if (test.FailedPasswordLock) {
if (test.CurrentPasswordAttempts >= test.PasswordAttempts) { 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)) { if (BCrypt.Net.BCrypt.Verify(PasswordHash, test.PasswordHash)) {
@@ -47,21 +49,20 @@ namespace BoredCareers.Controllers {
} }
); );
return test; return test;
} } else {
else {
test.CurrentPasswordAttempts += 1; test.CurrentPasswordAttempts += 1;
await _databaseService.SetAccount(test); 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); 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) { } 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 { try {
if (await _databaseService.GetAccount(UserName.ToLower()) == null) { if (await _databaseService.GetAccount(UserName.ToLower()) == null) {
if (await _databaseService.GetAccount(Email.ToLower()) == null) { if (await _databaseService.GetAccount(Email.ToLower()) == null) {
Account? created = new Account() { Account created = new Account() {
UserName = UserName.ToLower(), UserName = UserName.ToLower(),
Email = Email.ToLower(), Email = Email.ToLower(),
EmailVerified = false, EmailVerified = false,
PasswordHash = BCrypt.Net.BCrypt.HashPassword(PasswordHash), PasswordHash = BCrypt.Net.BCrypt.HashPassword(PasswordHash),
}; };
await _databaseService.SetAccount(created); await _databaseService.SetAccount(created);
created = await _databaseService.GetAccount(Email.ToLower()); Account? loadedAccount = await _databaseService.GetAccount(Email.ToLower());
if (created != null) { if (loadedAccount != null) {
await SendVerify(created.UserName); await SendVerify(loadedAccount.UserName);
return created; 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 { } else {
return new Account() { ID = -1, UserName = "Email is already in use" }; return NotFound("UserName is taken");
}
}
else {
return new Account() { ID = -1, UserName = "UserName is taken" };
} }
} catch (Exception ex) { } catch (Exception ex) {
Console.WriteLine("Error: " + ex.Message); Console.WriteLine("Register Error: " + ex.Message);
return new Account() { ID = -1, UserName = ex.Message }; return NotFound();
} }
} }
[Route("changepassword")] [Route("changepassword")]
[HttpPost] [HttpPost]
public async Task<ActionResult<bool>> ChangePassword([FromForm] string OldPassword, [FromForm] string NewPassword) { public async Task<ActionResult> ChangePassword([FromForm] string OldPassword, [FromForm] string NewPassword) {
try { try {
if (isLoggedIn()) { if (isLoggedIn()) {
Account user = await getLoggedInUser(); Account user = await getLoggedInUser();
@@ -109,12 +108,13 @@ namespace BoredCareers.Controllers {
user.PasswordHash = BCrypt.Net.BCrypt.HashPassword(NewPassword); user.PasswordHash = BCrypt.Net.BCrypt.HashPassword(NewPassword);
user.CurrentPasswordAttempts = 0; user.CurrentPasswordAttempts = 0;
await _databaseService.SetAccount(user); await _databaseService.SetAccount(user);
return true; return Ok();
} }
} }
return false; return NotFound();
} catch { } catch (Exception ex) {
return false; Console.WriteLine("ChangePassword Error: " + ex.Message);
return NotFound();
} }
} }
@@ -127,31 +127,37 @@ namespace BoredCareers.Controllers {
user.FailedPasswordLock = AccountLock; user.FailedPasswordLock = AccountLock;
user.CurrentPasswordAttempts = 0; user.CurrentPasswordAttempts = 0;
await _databaseService.SetAccount(user); await _databaseService.SetAccount(user);
return "Account Lock Status Updated"; return Ok();
} }
return "Unknown Error Occurred"; return NotFound();
} catch (Exception ex) { } catch (Exception ex) {
return ex.Message; Console.WriteLine("ToggleAccountLock Error: " + ex.Message);
return NotFound();
} }
} }
[Route("get")] [Route("get")]
[HttpPost] [HttpPost]
public async Task<ActionResult<Account?>> Get() { public async Task<ActionResult<Account>> Get() {
try { try {
if (isLoggedIn()) { if (isLoggedIn()) {
return await getLoggedInUser(); return Ok(await getLoggedInUser());
} }
return Ok(); return NotFound();
} catch { } catch (Exception ex) {
return Ok(); Console.WriteLine("Get Error: " + ex);
return NotFound();
} }
} }
[Route("logout")] [Route("logout")]
[HttpPost] [HttpPost]
public async Task Logout() { public async Task<ActionResult> Logout() {
await HttpContext.SignOutAsync(); if (isLoggedIn()) {
await HttpContext.SignOutAsync();
return Ok();
}
return NotFound();
} }
[Route("sendverifyemail")] [Route("sendverifyemail")]
@@ -267,18 +273,19 @@ namespace BoredCareers.Controllers {
[Route("delete")] [Route("delete")]
[HttpPost] [HttpPost]
public async Task<ActionResult<bool>> delete([FromForm] string Password) { public async Task<ActionResult> delete([FromForm] string Password) {
try { try {
if (isLoggedIn()) { if (isLoggedIn()) {
Account user = await getLoggedInUser(); Account user = await getLoggedInUser();
if (BCrypt.Net.BCrypt.Verify(Password, user.PasswordHash)) { if (BCrypt.Net.BCrypt.Verify(Password, user.PasswordHash)) {
await _databaseService.DeleteAccount(user.ID); await _databaseService.DeleteAccount(user.ID);
return true; return Ok();
} }
} }
return false; return NotFound();
} catch { } catch (Exception ex) {
return false; Console.WriteLine("Delete Error: " + ex.Message);
return NotFound();
} }
} }
+6 -9
View File
@@ -5,16 +5,15 @@ using System.Web.Http;
namespace BoredCareers.Controllers { namespace BoredCareers.Controllers {
[ApiController] [ApiController]
[Route("api/company/")] [Route("api/company")]
public class CompanyController : MistoxControllerBase { public class CompanyController : MistoxControllerBase {
public CompanyController(DatabaseService db) : base(db) {} public CompanyController(DatabaseService db) : base(db) {}
[Route("get")] [HttpGet]
[HttpPost] public async Task<IActionResult> GetCompany(int CompanyID) {
public async Task<IActionResult> GetCompany([FromForm] int companyID) {
if (isLoggedIn()) { if (isLoggedIn()) {
Company? company = await _databaseService.GetCompany(companyID); Company? company = await _databaseService.GetCompany(CompanyID);
if (company != null) { if (company != null) {
return Ok(company); return Ok(company);
} }
@@ -22,7 +21,6 @@ namespace BoredCareers.Controllers {
return NotFound(); return NotFound();
} }
[Route("set")]
[HttpPost] [HttpPost]
public async Task<IActionResult> SetCompany([FromBody] Company company) { public async Task<IActionResult> SetCompany([FromBody] Company company) {
if (isLoggedIn()) { if (isLoggedIn()) {
@@ -34,9 +32,8 @@ namespace BoredCareers.Controllers {
return NotFound(); return NotFound();
} }
[Route("delete")] [HttpDelete]
[HttpPost] public async Task<IActionResult> DeleteCompany(int CompanyID) {
public async Task<IActionResult> DeleteCompany([FromForm] int CompanyID) {
if (isLoggedIn()) { if (isLoggedIn()) {
if (await isLoggedInUserEmployeeOf(CompanyID)) { if (await isLoggedInUserEmployeeOf(CompanyID)) {
await _databaseService.DeleteCompany(CompanyID); await _databaseService.DeleteCompany(CompanyID);
+16 -20
View File
@@ -5,42 +5,38 @@ using System.Web.Http;
namespace BoredCareers.Controllers { namespace BoredCareers.Controllers {
[ApiController] [ApiController]
[Route("api/joblisting/")] [Route("api/joblisting")]
public class JobListingController : MistoxControllerBase { public class JobListingController : MistoxControllerBase {
public JobListingController(DatabaseService db) : base(db) {} public JobListingController(DatabaseService db) : base(db) {}
[Route("getpage")] [HttpGet("{JobListingID}")]
[HttpPost] public async Task<IActionResult> GetJobListing([FromRoute] int JobListingID) {
public async Task<IActionResult> GetJobListings([FromForm] int page) { var jobListing = await _databaseService.GetJobListing(JobListingID);
JobListing[] jobListings = await _databaseService.GetJobListingPage(page, 25); // 10 items per page if (jobListing != null) {
return Ok(jobListings);
}
[Route("get")]
[HttpPost]
public async Task<IActionResult> GetJobListing([FromForm] int JobListingID) {
JobListing? jobListing = await _databaseService.GetJobListing(JobListingID);
if (jobListing == null) {
return Ok(jobListing); return Ok(jobListing);
} }
return NotFound(); return NotFound();
} }
[Route("set")] [HttpGet]
public async Task<IActionResult> GetJobListings(int Page = 1, int PageQuantity = 25) {
JobListing[] jobListings = await _databaseService.GetJobListingPage(Page, PageQuantity);
return Ok(jobListings);
}
[HttpPost] [HttpPost]
public async Task<IActionResult> SetJobListing([FromBody] JobListing jobListing) { public async Task<IActionResult> SetJobListing([FromBody] JobListing JobListing) {
if (isLoggedIn()) { if (isLoggedIn()) {
if (await isLoggedInUserEmployeeOf(jobListing.CompanyID)) { if (await isLoggedInUserEmployeeOf(JobListing.CompanyID)) {
await _databaseService.SetJobListing(jobListing); await _databaseService.SetJobListing(JobListing);
} }
} }
return NotFound(); return NotFound();
} }
[Route("delete")] [HttpDelete]
[HttpPost] public async Task<IActionResult> DeleteJobListing(int JobListingID) {
public async Task<IActionResult> DeleteJobListing([FromForm] int JobListingID) {
if (isLoggedIn()) { if (isLoggedIn()) {
JobListing? jobListing = await _databaseService.GetJobListing(JobListingID); JobListing? jobListing = await _databaseService.GetJobListing(JobListingID);
if (jobListing != null) { if (jobListing != null) {
+3 -9
View File
@@ -4,7 +4,7 @@ using BoredCareers.Services.DatabaseService;
namespace BoredCareers.Controllers { namespace BoredCareers.Controllers {
[ApiController] [ApiController]
[Route("api/payment/")] [Route("api/payment")]
public class PaymentController : MistoxControllerBase { public class PaymentController : MistoxControllerBase {
IPayment _paymentService; IPayment _paymentService;
@@ -19,17 +19,11 @@ namespace BoredCareers.Controllers {
// Add new payment plugins here // Add new payment plugins here
} }
[Route("getpublickey")] [HttpGet]
[HttpPost]
public IActionResult GetPublicKey() { public IActionResult GetPublicKey() {
try { return Ok(IPayment._PublicKey);
return Ok(IPayment._PublicKey);
} catch (Exception ex) {
return NotFound(ex.ToString());
}
} }
[Route("response")]
[HttpPost] [HttpPost]
public async Task<IActionResult> paymentWebhook() { public async Task<IActionResult> paymentWebhook() {
try { try {
+17 -24
View File
@@ -5,33 +5,28 @@ using System.Web.Http;
namespace BoredCareers.Controllers { namespace BoredCareers.Controllers {
[ApiController] [ApiController]
[Route("api/resume/")] [Route("api/resume")]
public class ResumeController : MistoxControllerBase { public class ResumeController : MistoxControllerBase {
public ResumeController(DatabaseService db) : base(db) {} public ResumeController(DatabaseService db) : base(db) {}
[Route("getall")] [HttpGet]
[HttpPost] public async Task<IActionResult> GetResume(int? ResumeID) {
public async Task<IActionResult> GetResumes() { if (ResumeID != null) {
if (isLoggedIn()) { Resume? resume = await _databaseService.GetResume(ResumeID.Value);
int accountID = getLoggedInUserID(); if (resume != null) {
Resume[] resumes = await _databaseService.GetResumes(accountID); return Ok(resume);
return Ok(resumes); }
}else{
if (isLoggedIn()) {
int accountID = getLoggedInUserID();
Resume[] resumes = await _databaseService.GetResumes(accountID);
return Ok(resumes);
}
} }
return NotFound(); return NotFound();
} }
[Route("get")]
[HttpPost]
public async Task<IActionResult> GetResume([FromForm] int ResumeID) {
Resume? resume = await _databaseService.GetResume(ResumeID);
if (resume == null) {
return Ok(resume);
}
return NotFound();
}
[Route("set")]
[HttpPost] [HttpPost]
public async Task<IActionResult> SetResume([FromBody] Resume resume) { public async Task<IActionResult> SetResume([FromBody] Resume resume) {
if (isLoggedIn()) { if (isLoggedIn()) {
@@ -44,9 +39,8 @@ namespace BoredCareers.Controllers {
return NotFound(); return NotFound();
} }
[Route("delete")] [HttpDelete]
[HttpPost] public async Task<IActionResult> DeleteResume(int ResumeID) {
public async Task<IActionResult> DeleteResume([FromForm] int ResumeID) {
if (isLoggedIn()){ if (isLoggedIn()){
int accountID = getLoggedInUserID(); int accountID = getLoggedInUserID();
Resume? resume = await _databaseService.GetResume(ResumeID); Resume? resume = await _databaseService.GetResume(ResumeID);
@@ -59,5 +53,4 @@ namespace BoredCareers.Controllers {
} }
} }
}
}
+3 -5
View File
@@ -3,7 +3,6 @@ using BoredCareers.Controllers.Payment;
using BoredCareers.Services; using BoredCareers.Services;
using BoredCareers.Services.DatabaseService; using BoredCareers.Services.DatabaseService;
using System.Threading.RateLimiting; using System.Threading.RateLimiting;
using Microsoft.AspNetCore.RateLimiting;
using Stripe; using Stripe;
using System.Security.Claims; using System.Security.Claims;
@@ -99,10 +98,9 @@ builder.Services.AddCors(o => o.AddDefaultPolicy(builder => {
builder.Services.AddRateLimiter(options => { builder.Services.AddRateLimiter(options => {
options.AddPolicy("PerUserPolicy", httpContext => { options.AddPolicy("PerUserPolicy", httpContext => {
// Identify the user (assumes authenticated user with NameIdentifier claim) var userId = httpContext.User.FindFirst(ClaimTypes.NameIdentifier)?.Value
var userId = httpContext.User.FindFirst(ClaimTypes.NameIdentifier)?.Value ?? httpContext.User.Identity?.Name
?? httpContext.User.Identity?.Name ?? httpContext.Connection.RemoteIpAddress?.ToString();
?? httpContext.Connection.RemoteIpAddress?.ToString();
return RateLimitPartition.GetTokenBucketLimiter(userId, key => new TokenBucketRateLimiterOptions { return RateLimitPartition.GetTokenBucketLimiter(userId, key => new TokenBucketRateLimiterOptions {
TokenLimit = 10, // max 10 requests TokenLimit = 10, // max 10 requests
@@ -1,8 +1,6 @@
using BoredCareers.Entities; using BoredCareers.Entities;
using MySql.Data.MySqlClient; using MySql.Data.MySqlClient;
// TODO: Fix Data type mangling. Such As DateTime
namespace BoredCareers.Services.DatabaseService { namespace BoredCareers.Services.DatabaseService {
public partial class DatabaseService { public partial class DatabaseService {