diff --git a/ToDo.yaml b/ToDo.yaml index 8b9c668..3f213f4 100755 --- a/ToDo.yaml +++ b/ToDo.yaml @@ -5,42 +5,42 @@ Server: When a company is created: Send email -> verify ownership of the email - Need to timeout email reset tokens: + Resume: + Block API Access as much as possible [ Disallow AI keyword filters ] + + Auth: + Make sure autorenew works + + When Job Posting Closes Successful: + Update the company rating + + JobCleanupService: + Need to update notification email Client: jobs/new: - Want to add Required skills to help with filtering - When enter is pressed it tries to submit the form - Should run the whole carosel on enter before the submit is sent - Need to validate input before allowing next step + Job Listing Skills exists but isn't implimented in the UI + Tab doesnt do anything Want to add completed job listing preview at end of carosel + Edit employees not implimented yet + + Jobs/editor: + Jobs/editor w/ Querystring JobID=# is not implimented yet + Edit employees not implimented yet + + Resume: + Resume builder minimal user input [ Dont allow AI input ] + Allow company to look up users if their resume is public [ Maybe auto with notify ] + Allow multiple resume's for job specific work + Create advanced filtering tools for company lookup and resume lookup + + Job Board: + Allow users to look up jobs and apply [ Boost visibility | Completely manual ] + Mark ghost listings to allow users to be informed and put companies on blast + + CompanyConnect: + need to lookup company before making a new one + database: - Add Applied Jobs Table - - - -Task: - Block API Access as much as possible [ Rate limit | Auth Req | CORS | Disallow AI keyword filters ] - Resume builder minimal user input [ Dont allow AI input ] - Auto unlist jobs after a month of no activity [ Multiple offenders marked ] - Dont allow external applications for users on company sites from the start - Allow company to look up users if their resume is public [ Maybe auto with notify ] - Allow users to look up jobs and apply [ Boost visibility | Completely manual ] - Allow multiple resume's for job specific work - Mark ghost listings to allow users to be informed and put companies on blast - Create advanced filtering tools for company lookup and resume lookup - - Create and Auth Database based on the docker compose - Create a server table inside the auth database - Point all requests after auth to the correct regional server. -> Currently only Mistox-West exists - - CompanyConnect | need to lookup company before making a new one - - Finish Auth setup - Make sure autorenew works - - Jobs/editor w/ Querystring JobID=# is not implimented yet - Company -> Edit employees not implimented yet - - Resume fields in angular models need to be public \ No newline at end of file + Add Applied Jobs Table \ No newline at end of file diff --git a/database/mistox.sql b/database/mistox.sql index da44b17..72c979b 100755 --- a/database/mistox.sql +++ b/database/mistox.sql @@ -131,6 +131,8 @@ CREATE TABLE IF NOT EXISTS `Company` ( `EmailVerified` boolean DEFAULT 0, `WebsiteURL` varchar(255) DEFAULT NULL, `Logo` mediumblob DEFAULT NULL, + `JobsClosedSuccessful` int DEFAULT 0, + `JobsAutoClosed` int DEFAULT 0, `Phone` varchar(20) DEFAULT NULL, `PostalCode` varchar(20) NOT NULL, `Country` char(2) NOT NULL, @@ -161,9 +163,36 @@ CREATE TABLE IF NOT EXISTS `JobListing` ( `JobType` varchar(20) NOT NULL, `Remote` boolean DEFAULT 0, `Description` text NOT NULL, - `CreatedTime` datetime Default NULL, + `CreatedTime` datetime DEFAULT NULL, `ModifiedTime` datetime DEFAULT NULL, - `IsDeleted` boolean Default 0, + `IsDeleted` boolean DEFAULT 0, PRIMARY KEY (`ID`), FOREIGN KEY (`CompanyID`) REFERENCES `Company`(`ID`) ON DELETE CASCADE +) AUTO_INCREMENT=1; + +CREATE TABLE IF NOT EXISTS `JobListingSkill` ( + `ID` int NOT NULL AUTO_INCREMENT, + `JobListingID` int NOT NULL, + `Name` varchar(150) NOT NULL, + `Description` text DEFAULT NULL, + PRIMARY KEY (`ID`), + FOREIGN KEY (`JobListingID`) REFERENCES `JobListing`(`ID`) ON DELETE CASCADE +) AUTO_INCREMENT=1; + +-- Application Section + +CREATE TABLE IF NOT EXISTS `JobApplication` ( + `ID` int NOT NULL AUTO_INCREMENT, + `AccountID` int NOT NULL, + `ResumeID` int NOT NULL, + `JobListingID` int NOT NULL, + `ResponseEmail` varchar(255) DEFAULT NULL, + `DateApplied` datetime DEFAULT NULL, + `ResponseStatus` varchar(50) NOT NULL DEFAULT 'Pending', + `HasBeenViewed` boolean DEFAULT 0, + `Rating` int DEFAULT NULL, + `Notes` text DEFAULT NULL, + PRIMARY KEY (`ID`), + FOREIGN KEY (`ResumeID`) REFERENCES `Resume`(`ID`) ON DELETE CASCADE, + FOREIGN KEY (`JobListingID`) REFERENCES `JobListing`(`ID`) ON DELETE CASCADE ) AUTO_INCREMENT=1; \ No newline at end of file diff --git a/src/Client/src/app/models/Application.ts b/src/Client/src/app/models/Application.ts new file mode 100644 index 0000000..576fefd --- /dev/null +++ b/src/Client/src/app/models/Application.ts @@ -0,0 +1,12 @@ +export class Application { + public id: number | null = null; + public accountID: number = 0; + public resumeID: number = 0; + public jobListingID: number = 0; + public responseEmail: string = ""; + public dateApplied: Date = new Date(); + public responseStatus: string = ""; + public hasBeenViewed: boolean = false; + public rating: number = 0; + public notes: string = ""; +} \ No newline at end of file diff --git a/src/Client/src/app/models/Company.ts b/src/Client/src/app/models/Company.ts index 80a1e65..ae79789 100644 --- a/src/Client/src/app/models/Company.ts +++ b/src/Client/src/app/models/Company.ts @@ -5,6 +5,8 @@ export class Company { public emailVerified: boolean = false; public websiteURL: string = ""; public logo: string = ""; + public jobsClosedSuccessful: number = 0; + public jobsAutoClosed: number = 0; public phone: string = ""; public postalCode: string = ""; public country: string = ""; // 2 Letter Country Code diff --git a/src/Client/src/app/models/JobListing.ts b/src/Client/src/app/models/JobListing.ts index 52fb396..ffd34e3 100644 --- a/src/Client/src/app/models/JobListing.ts +++ b/src/Client/src/app/models/JobListing.ts @@ -11,7 +11,15 @@ export class JobListing { public jobType: string = ""; public remote: boolean = false; public description: string = ""; + public skills: JobListingSkills[] = []; public createdTime: Date = new Date(); public modifiedTime: Date = new Date(); public isDeleted: boolean = false; +} + +export class JobListingSkills { + public id: number | null = null; + public jobListingID: number = 0; + public name: string = ""; + public Description: string = ""; } \ No newline at end of file diff --git a/src/Client/src/app/models/Resume.ts b/src/Client/src/app/models/Resume.ts index 74e8ed9..c917a5a 100644 --- a/src/Client/src/app/models/Resume.ts +++ b/src/Client/src/app/models/Resume.ts @@ -21,86 +21,86 @@ export class Resume { export class ResumeExperience { public id: number | null = null; - resumeID: number = 0; - jobTitle: string = ""; - company: string = ""; - postalCode: string = ""; - country: string = ""; - stateOrRegion: string = ""; - city: string = ""; - dateStarted: Date = new Date(); - stillEmployed: boolean = false; - dateEnded: Date = new Date(); - experienceBullets: ResumeExperienceBullet[] = []; + public resumeID: number = 0; + public jobTitle: string = ""; + public company: string = ""; + public postalCode: string = ""; + public country: string = ""; + public stateOrRegion: string = ""; + public city: string = ""; + public dateStarted: Date = new Date(); + public stillEmployed: boolean = false; + public dateEnded: Date = new Date(); + public experienceBullets: ResumeExperienceBullet[] = []; } export class ResumeExperienceBullet { public id: number | null = null; - resumeID: number = 0; - resumeExperienceID: number = 0; - jobFunction: string = ""; + public resumeID: number = 0; + public resumeExperienceID: number = 0; + public jobFunction: string = ""; } export class ResumeMilitary { public id: number | null = null; - resumeID: number = 0; - country: string = ""; - rank: string = ""; - dateStarted: Date = new Date(); - stillServing: boolean = false; - dateEnded: Date = new Date(); - millitaryBullets: ResumeMilitaryBullet[] = []; + public resumeID: number = 0; + public country: string = ""; + public rank: string = ""; + public dateStarted: Date = new Date(); + public stillServing: boolean = false; + public dateEnded: Date = new Date(); + public millitaryBullets: ResumeMilitaryBullet[] = []; } export class ResumeMilitaryBullet { public id: number | null = null; - resumeID: number = 0; - resumeMilitaryID: number = 0; - achievement: string = ""; - description: string = ""; + public resumeID: number = 0; + public resumeMilitaryID: number = 0; + public achievement: string = ""; + public description: string = ""; } export class ResumeEducation { public id: number | null = null; - resumeID: number = 0; - degreeType: string = ""; - degreeField: string = ""; - school: string = ""; - postalCode: string = ""; - country: string = ""; - stateOrRegion: string = ""; - city: string = ""; - dateStarted: Date = new Date(); - stillStudying: boolean = false; - dateEnded: Date = new Date(); + public resumeID: number = 0; + public degreeType: string = ""; + public degreeField: string = ""; + public school: string = ""; + public postalCode: string = ""; + public country: string = ""; + public stateOrRegion: string = ""; + public city: string = ""; + public dateStarted: Date = new Date(); + public stillStudying: boolean = false; + public dateEnded: Date = new Date(); } export class ResumeSkill { public id: number | null = null; - resumeID: number = 0; - name: string = ""; - description: string = ""; + public resumeID: number = 0; + public name: string = ""; + public description: string = ""; } export class ResumeLanguage { public id: number | null = null; - resumeID: number = 0; - language: string = ""; - proficiency: string = ""; + public resumeID: number = 0; + public language: string = ""; + public proficiency: string = ""; } export class ResumeCertification { public id: number | null = null; - resumeID: number = 0; - name: string = ""; - verificationURL: string = ""; - description: string = ""; + public resumeID: number = 0; + public name: string = ""; + public verificationURL: string = ""; + public description: string = ""; } export class ResumeProject { public id: number | null = null; - resumeID: number = 0; - name: string = ""; - url: string = ""; - description: string = ""; + public resumeID: number = 0; + public name: string = ""; + public url: string = ""; + public description: string = ""; } \ No newline at end of file diff --git a/src/Client/src/app/pages/main/company/company.component.css b/src/Client/src/app/pages/main/company/company.component.css index 4263503..290016c 100644 --- a/src/Client/src/app/pages/main/company/company.component.css +++ b/src/Client/src/app/pages/main/company/company.component.css @@ -30,4 +30,15 @@ button { margin: 10px; overflow: scroll; padding: 10px; + color: var(--Mistox-White); +} + +.center-item { + display: flex; + width: 100%; + justify-content: center; +} + +.center-item img { + width: 300px; } \ No newline at end of file diff --git a/src/Client/src/app/pages/main/company/company.component.html b/src/Client/src/app/pages/main/company/company.component.html index 3f3a63d..204c13b 100644 --- a/src/Client/src/app/pages/main/company/company.component.html +++ b/src/Client/src/app/pages/main/company/company.component.html @@ -4,11 +4,16 @@
-

{{ Comp.name }}

-

{{ Comp.email }}

+
+
{{ Comp.email }}
+

{{ Comp.name }}

+
{{ Comp.websiteURL }}
+
+
+ +

{{ Comp.emailVerified }}

-

{{ Comp.websiteURL }}

-

{{ Comp.logo }}

+

{{ Comp.phone }}

{{ Comp.postalCode }}

{{ Comp.country }}

diff --git a/src/Client/src/app/pages/main/home/home.component.css b/src/Client/src/app/pages/main/home/home.component.css index 1b670c7..013fb16 100644 --- a/src/Client/src/app/pages/main/home/home.component.css +++ b/src/Client/src/app/pages/main/home/home.component.css @@ -3,6 +3,19 @@ width: calc(100% - 400px); } +.center-text { + text-align: center; +} + +.center-text h1 { + font-size: 100px; + margin: 20px 0; +} + +.center-text h2 { + margin-bottom: 200px; +} + .content-frame { display: flex; flex-direction: row; @@ -16,6 +29,7 @@ .floating-frame { width: 450px; + height: 200px; background-color: var(--Mistox-Frame); padding: 20px; margin: 20px 0; @@ -41,8 +55,11 @@ hr { } .solution-frame { - height: 200px; - background-color: blueviolet; width: calc(100% - 515px); margin: 20px 0; + text-align: center; +} + +.border { + border-bottom: solid 1px #000; } \ No newline at end of file diff --git a/src/Client/src/app/pages/main/home/home.component.html b/src/Client/src/app/pages/main/home/home.component.html index d36ac97..fa7c930 100644 --- a/src/Client/src/app/pages/main/home/home.component.html +++ b/src/Client/src/app/pages/main/home/home.component.html @@ -1,5 +1,11 @@
-
+ +
+

Bored Careers

+

The Anti-AI Job Board

+
+ +

death by a thousand applicants

@@ -11,11 +17,14 @@
- +
+

Rate Limiting - We use strong rate limiting to prevent bot's from flooding applications

+

Strong Authentication - All API's require authentication and will ban on suspision of bots

+
-
+

keyword frenzie

@@ -27,11 +36,12 @@
- +

No External Access - All companies and clients are required to interface with the application. Minimizing the surface for AI resume filters.

+

Skill Based Resumes - Resume's are skill based not work history based. This allows companies to know what your good at without infering it.

-
+

response black-hole

@@ -43,11 +53,12 @@
- +

Automated Email Notifications - No longer will you be left in the dark. No matter how the job listing is closed you will be notified

+

More Analytics - Visibility into if the company has looked at your resume, view your resume score

-
+

zombie postings

@@ -59,7 +70,8 @@
- +

Job Postings Auto Close - Job postings auto close 30 days after originally posted. If the company creates a repost habit they will be flagged.

+

Collective Flagging - Companies hold a reputation on the patform and its visible on all their job postings

@@ -75,7 +87,8 @@
- +

Resume Builder - No staring at a blank sheet trying to build a resume. Use our curated resume builder

+

Companies Search Tools - Companies can find you with ease with the strong search tools

\ No newline at end of file diff --git a/src/Client/src/styles.css b/src/Client/src/styles.css index 99bb268..15e206c 100644 --- a/src/Client/src/styles.css +++ b/src/Client/src/styles.css @@ -5,7 +5,7 @@ --Mistox-Medium: #890620; --Mistox-Light: #B6465F; --Mistox-Bright: #FC440F; - --Mistox-Frame: #FF5A00CC; + --Mistox-Frame: #FFE0A5; --Mistox-Button: #ff9999; --Mistox-Button-Hover: #ff999977; --Mistox-White: #FFF; diff --git a/src/Server/Controllers/ApplicationController.cs b/src/Server/Controllers/ApplicationController.cs new file mode 100644 index 0000000..0b9f1d6 --- /dev/null +++ b/src/Server/Controllers/ApplicationController.cs @@ -0,0 +1,55 @@ +using Microsoft.AspNetCore.Mvc; +using BoredCareers.Services.DatabaseService; +using BoredCareers.Entities; +using System.Web.Http; + +namespace BoredCareers.Controllers { + [ApiController] + [Route("api/application")] + public class ApplicationController : MistoxControllerBase { + + public ApplicationController(DatabaseService db) : base(db) {} + + [HttpGet] + public async Task GetApplication(int ApplicationID) { + if (isLoggedIn()) { + Application? application = await _databaseService.GetApplication(ApplicationID); + if (application != null) { + return Ok(application); + } + return NotFound("Application doesn't exist"); + } + return NotFound("Not logged in"); + } + + [HttpPost] + public async Task SetApplication([FromBody] Application application) { + if (isLoggedIn()) { + if (application.AccountID == getLoggedInUserID()) { + await _databaseService.SetApplication(application); + return Ok(); + } + return NotFound("Cannot apply for someone else"); + } + return NotFound("Not logged in"); + } + + [HttpDelete] + public async Task DeleteApplication(int ApplicationID) { + if (isLoggedIn()) { + Application? app = await _databaseService.GetApplication(ApplicationID); + if (app != null) { + if (app.AccountID == getLoggedInUserID()) { + await _databaseService.DeleteApplication(ApplicationID); + return Ok(); + } + return NotFound("You cannot delete an app that isnt yours"); + } + return NotFound("Application doesn't exist"); + } + return NotFound("Not logged in"); + } + + } + +} diff --git a/src/Server/Entities/Application.cs b/src/Server/Entities/Application.cs new file mode 100644 index 0000000..0683cb7 --- /dev/null +++ b/src/Server/Entities/Application.cs @@ -0,0 +1,14 @@ +namespace BoredCareers.Entities { + public class Application { + public int? ID { get; set; } // PK + public int AccountID { get; set; } // FK + public int ResumeID { get; set; } // FK + public int JobListingID { get; set; } // FK + public string ResponseEmail { get; set; } = ""; + public DateTime DateApplied { get; set; } + public string ResponseStatus { get; set; } = ""; + public bool HasBeenViewed { get; set; } = false; + public int Rating { get; set; } + public string Notes { get; set; } = ""; + } +} \ No newline at end of file diff --git a/src/Server/Entities/Company.cs b/src/Server/Entities/Company.cs index 1d78a4b..c6fbcb8 100644 --- a/src/Server/Entities/Company.cs +++ b/src/Server/Entities/Company.cs @@ -7,6 +7,8 @@ namespace BoredCareers.Entities { public bool EmailVerified { get; set; } = false; public string WebsiteURL { get; set; } = ""; public string Logo { get; set; } = ""; + public int JobsClosedSuccessful { get; set; } + public int JobsAutoClosed { get; set; } public string Phone { get; set; } = ""; public string PostalCode { get; set; } = ""; public string Country { get; set; } = ""; // 2 Letter Country Code diff --git a/src/Server/Entities/JobListing.cs b/src/Server/Entities/JobListing.cs index f4fdc5f..92089e0 100644 --- a/src/Server/Entities/JobListing.cs +++ b/src/Server/Entities/JobListing.cs @@ -13,9 +13,17 @@ namespace BoredCareers.Entities { public string JobType { get; set; } = ""; public bool Remote { get; set; } = false; public string Description { get; set; } = ""; + public JobListingSkill[] Skills { get; set; } = []; public DateTime CreatedTime { get; set; } public DateTime ModifiedTime { get; set; } public bool IsDeleted { get; set; } = false; } + public class JobListingSkill { + public int? ID { get; set; } // PK + public int JobListingID { get; set; } // FK + public string Name { get; set; } = ""; + public string Description { get; set; } = ""; + } + } \ No newline at end of file diff --git a/src/Server/Program.cs b/src/Server/Program.cs index 89b7ca9..c6dd26f 100755 --- a/src/Server/Program.cs +++ b/src/Server/Program.cs @@ -8,11 +8,11 @@ using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.IdentityModel.Tokens; using System.IdentityModel.Tokens.Jwt; using System.Security.Cryptography; +using BoredCareers.Services.TimerService; var builder = WebApplication.CreateBuilder(args); // Disable null warnings becuse string.IsNullOrEmpty checks for NULL or Empty -#pragma warning disable CS8600 #pragma warning disable CS8604 //////////////////////////////// @@ -36,8 +36,9 @@ string? _dbpass = Environment.GetEnvironmentVariable("MySQLPass"); string dbPass = !string.IsNullOrEmpty(_dbpass) ? _dbpass : "oasv34$8gpv023dd"; // Create the database serivice -DatabaseService databaseService = new DatabaseService(connectionString: "server=" + dbserver + ";user=" + dbUser + ";database=" + dbdatabase + ";password=" + dbPass + ";port=3306;"); -builder.Services.Add( new ServiceDescriptor( typeof( DatabaseService ), databaseService ) ); +builder.Services.AddSingleton(sp => + new DatabaseService("server=" + dbserver + ";user=" + dbUser + ";database=" + dbdatabase + ";password=" + dbPass + ";port=3306;") +); //////////////////////////////// ///////// Email Service //////// @@ -135,15 +136,28 @@ builder.Services.AddAuthentication(options => { }; }); -builder.Services.AddCors(o => o.AddDefaultPolicy(builder => { - builder.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader(); // No CORS -})); +//////////////////////////////// +/// Rate Limiting Service //// +//////////////////////////////// + +List allowedOrigins = new List{ "https://boredcareers.com", "https://www.boredcareers.com" }; +if (builder.Environment.IsDevelopment()) { + allowedOrigins.Add("http://localhost:5000"); +} + +builder.Services.AddCors(options => { + options.AddDefaultPolicy(policy => { + policy.WithOrigins(allowedOrigins.ToArray()) + .AllowAnyHeader() + .AllowAnyMethod() + .AllowCredentials(); + }); +}); builder.Services.AddRateLimiter(options => { options.AddPolicy("PerUserPolicy", httpContext => { var userId = httpContext.User.FindFirst(ClaimTypes.NameIdentifier)?.Value - ?? httpContext.User.Identity?.Name - ?? httpContext.Connection.RemoteIpAddress?.ToString(); + ?? $"ip:{httpContext.Connection.RemoteIpAddress}"; return RateLimitPartition.GetTokenBucketLimiter(userId, key => new TokenBucketRateLimiterOptions { TokenLimit = 10, // max 10 requests @@ -156,9 +170,17 @@ builder.Services.AddRateLimiter(options => { }); }); -// Pages Service +//////////////////////////////// +///// Background Services ///// +//////////////////////////////// + +builder.Services.AddHostedService(); + +//////////////////////////////// +///// ASPNET Core Function ///// +//////////////////////////////// + builder.Services.AddControllers(); -builder.Services.AddRazorPages(); var app = builder.Build(); @@ -170,12 +192,14 @@ if( !app.Environment.IsDevelopment() ) { app.UseDefaultFiles(); app.UseStaticFiles(); +app.UseRateLimiter(); + app.UseCors(); app.UseRouting(); app.UseAuthentication(); -app.MapControllers().RequireRateLimiting("perUserPolicy"); +app.MapControllers(); app.MapFallbackToFile("index.html"); diff --git a/src/Server/Services/BackgroundServices/JobCleanupService.cs b/src/Server/Services/BackgroundServices/JobCleanupService.cs new file mode 100644 index 0000000..fbd30fb --- /dev/null +++ b/src/Server/Services/BackgroundServices/JobCleanupService.cs @@ -0,0 +1,58 @@ +using BoredCareers.Entities; + +namespace BoredCareers.Services.TimerService { + public class JobCleanupService : BackgroundService { + + private readonly DatabaseService.DatabaseService _db; + private readonly EmailService _em; + + public JobCleanupService(DatabaseService.DatabaseService db, EmailService em) { + _db = db; + _em = em; + } + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) { + while (!stoppingToken.IsCancellationRequested) { + try { + // Get listing's past expire + JobListingDTO[] deletedJobListings = await _db.GetJobListingsPastExipre(); + + // Group them by CompanyID, ListingCount + Dictionary listingsByCompany = deletedJobListings + .GroupBy(l => l.CompanyID) + .ToDictionary(g => g.Key, g => g.Count()); + + // Update each company's rating + foreach (KeyValuePair kvp in listingsByCompany) { + Company? comp = await _db.GetCompany(kvp.Key); + if (comp != null) { + comp.JobsAutoClosed += kvp.Value; + await _db.SetCompany(comp); + } + } + + // Get each listing + foreach (JobListingDTO listing in deletedJobListings) { + // Get each Person + string[] emails = await _db.GetApplicationResponseEmailFromJobListing(listing.JobListingID); + foreach (string email in emails) { + // Send Notify Email + _em.Send(email, EmailService.JobAutoClosedSubject, EmailService.JobAutoClosedEmail); + } + } + + // Delete Listing's past expire + await _db.DeleteJobListingsPastExipre(); + } catch (Exception e) { + Console.WriteLine($"Error: {e.Message}"); + } + await Task.Delay(TimeSpan.FromHours(2), stoppingToken); + } + } + } + + public class JobListingDTO { + public int JobListingID { get; set; } + public int CompanyID { get; set; } + } +} diff --git a/src/Server/Services/DatabaseService/Application.cs b/src/Server/Services/DatabaseService/Application.cs new file mode 100644 index 0000000..22b4758 --- /dev/null +++ b/src/Server/Services/DatabaseService/Application.cs @@ -0,0 +1,216 @@ +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 GetApplcationsFromAccount(int AccountID) { + List applications = new List(); + using (MySqlConnection connection = GetConnection()) { + await connection.OpenAsync(); + string command = @" + SELECT * + FROM JobApplication + WHERE AccountID = @AccountID + "; + + MySqlCommand cmd = new MySqlCommand(command, connection); + cmd.Parameters.AddWithValue("@AccountID", AccountID); + + using (DbDataReader reader = await cmd.ExecuteReaderAsync()) { + while (await reader.ReadAsync()) { + int _id = reader.GetInt32("ID"); + int _accountid = reader.GetInt32("AccountID"); + int _resumeid = reader.GetInt32("ResumeID"); + int _joblistingid = reader.GetInt32("JobListingID"); + string _responseemail = reader.GetString("ResponseEmail"); + DateTime _dateapplied = reader.GetDateTime("DateApplied"); + string _responsestatus = reader.GetString("ResponseStatus"); + bool _hasbeenviewed = reader.GetBoolean("HasBeenViewed"); + int _rating = reader.GetInt32("Rating"); + string _notes = reader.GetString("Notes"); + + applications.Add(new Application() { + ID = _id, + AccountID = _accountid, + ResumeID = _resumeid, + JobListingID = _joblistingid, + ResponseEmail = _responseemail, + DateApplied = _dateapplied, + ResponseStatus = _responsestatus, + HasBeenViewed = _hasbeenviewed, + Rating = _rating, + Notes = _notes + }); + } + } + } + return applications.ToArray(); + } + + public async Task GetApplicationsFromJobListing(int JobListingID) { + List applications = new List(); + using (MySqlConnection connection = GetConnection()) { + await connection.OpenAsync(); + string command = @" + SELECT * + FROM JobApplication + WHERE JobListingID = @JobListingID + "; + + MySqlCommand cmd = new MySqlCommand(command, connection); + cmd.Parameters.AddWithValue("@JobListingID", JobListingID); + + using (DbDataReader reader = await cmd.ExecuteReaderAsync()) { + while (await reader.ReadAsync()) { + int _id = reader.GetInt32("ID"); + int _accountid = reader.GetInt32("AccountID"); + int _resumeid = reader.GetInt32("ResumeID"); + int _joblistingid = reader.GetInt32("JobListingID"); + string _responseemail = reader.GetString("ResponseEmail"); + DateTime _dateapplied = reader.GetDateTime("DateApplied"); + string _responsestatus = reader.GetString("ResponseStatus"); + bool _hasbeenviewed = reader.GetBoolean("HasBeenViewed"); + int _rating = reader.GetInt32("Rating"); + string _notes = reader.GetString("Notes"); + + applications.Add(new Application() { + ID = _id, + AccountID = _accountid, + ResumeID = _resumeid, + JobListingID = _joblistingid, + ResponseEmail = _responseemail, + DateApplied = _dateapplied, + ResponseStatus = _responsestatus, + HasBeenViewed = _hasbeenviewed, + Rating = _rating, + Notes = _notes + }); + } + } + } + return applications.ToArray(); + } + + public async Task GetApplicationResponseEmailFromJobListing(int JobListingID) { + List emailadds = new List(); + using (MySqlConnection connection = GetConnection()) { + await connection.OpenAsync(); + string command = @" + SELECT ResponseEmail + FROM JobApplication + WHERE JobListingID = @JobListingID + "; + + MySqlCommand cmd = new MySqlCommand(command, connection); + cmd.Parameters.AddWithValue("@JobListingID", JobListingID); + + using (DbDataReader reader = await cmd.ExecuteReaderAsync()) { + while (await reader.ReadAsync()) { + string _responseemail = reader.GetString("ResponseEmail"); + emailadds.Add(_responseemail); + } + } + } + return emailadds.ToArray(); + } + + public async Task GetApplication(int ApplicationID) { + Application? application = null; + using (MySqlConnection connection = GetConnection()) { + await connection.OpenAsync(); + string command = @" + SELECT * + FROM JobApplication + WHERE ID = @ApplicationID + "; + + MySqlCommand cmd = new MySqlCommand(command, connection); + cmd.Parameters.AddWithValue("@ApplicationID", ApplicationID); + + using (DbDataReader reader = await cmd.ExecuteReaderAsync()) { + while (await reader.ReadAsync()) { + int _id = reader.GetInt32("ID"); + int _accountid = reader.GetInt32("AccountID"); + int _resumeid = reader.GetInt32("ResumeID"); + int _joblistingid = reader.GetInt32("JobListingID"); + string _responseemail = reader.GetString("ResponseEmail"); + DateTime _dateapplied = reader.GetDateTime("DateApplied"); + string _responsestatus = reader.GetString("ResponseStatus"); + bool _hasbeenviewed = reader.GetBoolean("HasBeenViewed"); + int _rating = reader.GetInt32("Rating"); + string _notes = reader.GetString("Notes"); + + application = new Application() { + ID = _id, + AccountID = _accountid, + ResumeID = _resumeid, + JobListingID = _joblistingid, + ResponseEmail = _responseemail, + DateApplied = _dateapplied, + ResponseStatus = _responsestatus, + HasBeenViewed = _hasbeenviewed, + Rating = _rating, + Notes = _notes + }; + } + } + } + + + + return application; + } + + public async Task SetApplication(Application application) { + using (MySqlConnection connection = GetConnection()) { + await connection.OpenAsync(); + string command = @" + INSERT INTO JobApplication + (ID,AccountID,ResumeID,JobListingID,ResponseEmail,DateApplied,ResponseStatus,HasBeenViewed,Rating,Notes) + VALUES + (@ID,@AccountID,@ResumeID,@JobListingID,@ResponseEmail,@DateApplied,@ResponseStatus,@HasBeenViewed,@Rating,@Notes) + ON DUPLICATE KEY UPDATE + AccountID = @AccountID, + ResumeID = @ResumeID, + JobListingID = @JobListingID, + ResponseEmail = @ResponseEmail, + ResponseStatus = @ResponseStatus, + HasBeenViewed = @HasBeenViewed, + Rating = @Rating, + Notes = @Notes; + "; + + MySqlCommand cmd = new MySqlCommand(command, connection); + cmd.Parameters.AddWithValue("@ID", application.ID); + cmd.Parameters.AddWithValue("@AccountID", application.AccountID); + cmd.Parameters.AddWithValue("@ResumeID", application.ResumeID); + cmd.Parameters.AddWithValue("@JobListingID", application.JobListingID); + cmd.Parameters.AddWithValue("@ResponseEmail", application.ResponseEmail); + cmd.Parameters.AddWithValue("@DateApplied", DateTime.UtcNow); + cmd.Parameters.AddWithValue("@ResponseStatus", application.ResponseStatus); + cmd.Parameters.AddWithValue("@HasBeenViewed", application.HasBeenViewed); + cmd.Parameters.AddWithValue("@Rating", application.Rating); + cmd.Parameters.AddWithValue("@Notes", application.Notes); + + await cmd.ExecuteNonQueryAsync(); + } + } + + public async Task DeleteApplication(int ApplicationID) { + using (MySqlConnection connection = GetConnection()) { + MySqlCommand cmd; + await connection.OpenAsync(); + string command = @" + DELETE FROM JobApplication WHERE ID = @ID; + "; + cmd = new MySqlCommand(command, connection); + cmd.Parameters.AddWithValue("@ID", ApplicationID); + await cmd.ExecuteNonQueryAsync(); + } + } + + } +} diff --git a/src/Server/Services/DatabaseService/Company.cs b/src/Server/Services/DatabaseService/Company.cs index 504e481..5aaad81 100644 --- a/src/Server/Services/DatabaseService/Company.cs +++ b/src/Server/Services/DatabaseService/Company.cs @@ -10,7 +10,7 @@ namespace BoredCareers.Services.DatabaseService { public async Task GetCompany( int CompanyID ) { Company? company = null; using( MySqlConnection connection = GetConnection() ) { - connection.Open(); + await connection.OpenAsync(); string command = @" SELECT * FROM Company @@ -22,13 +22,14 @@ namespace BoredCareers.Services.DatabaseService { 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 _logo = Encoding.UTF8.GetString((byte[])reader["Logo"]); + int _jobsclosedsuccessful = reader.GetInt32("JobsClosedSuccessful"); + int _jobsautoclosed = reader.GetInt32("JobsAutoClosed"); string _phone = reader.GetString( "Phone" ); string _postalcode = reader.GetString( "PostalCode" ); string _country = reader.GetString( "Country" ); @@ -43,6 +44,8 @@ namespace BoredCareers.Services.DatabaseService { EmailVerified = _emailVerified, WebsiteURL = _websiteurl, Logo = _logo, + JobsAutoClosed = _jobsautoclosed, + JobsClosedSuccessful = _jobsclosedsuccessful, Phone = _phone, PostalCode = _postalcode, Country = _country, @@ -58,19 +61,20 @@ namespace BoredCareers.Services.DatabaseService { public async Task SetCompany( Company company ) { using (MySqlConnection connection = GetConnection()) { - connection.Open(); - + await connection.OpenAsync(); string command = @" INSERT INTO Company - (ID,Name,Email,EmailVerified,WebsiteURL,Logo,Phone,PostalCode,Country,StateOrRegion,City,Description) + (ID,Name,Email,EmailVerified,WebsiteURL,Logo,JobsClosedSuccessful,JobsAutoClosed,Phone,PostalCode,Country,StateOrRegion,City,Description) VALUES - (@ID,@Name,@Email,@EmailVerified,@WebsiteURL,@Logo,@Phone,@PostalCode,@Country,@StateOrRegion,@City,@Description) + (@ID,@Name,@Email,@EmailVerified,@WebsiteURL,@Logo,@JobsClosedSuccessful,@JobsAutoClosed,@Phone,@PostalCode,@Country,@StateOrRegion,@City,@Description) ON DUPLICATE KEY UPDATE Name = @Name, Email = @Email, EmailVerified = @EmailVerified, WebsiteURL = @WebsiteURL, Logo = @Logo, + JobsClosedSuccessful = @JobsClosedSuccessful, + JobsAutoClosed = @JobsAutoClosed, Phone = @Phone, PostalCode = @PostalCode, Country = @Country, @@ -88,6 +92,8 @@ namespace BoredCareers.Services.DatabaseService { cmd.Parameters.AddWithValue("@EmailVerified", company.EmailVerified); cmd.Parameters.AddWithValue("@WebsiteURL", company.WebsiteURL); cmd.Parameters.AddWithValue("@Logo", Encoding.UTF8.GetBytes(company.Logo)); + cmd.Parameters.AddWithValue("@JobsClosedSuccessful", company.JobsClosedSuccessful); + cmd.Parameters.AddWithValue("@JobsAutoClosed", company.JobsAutoClosed); cmd.Parameters.AddWithValue("@Phone", company.Phone); cmd.Parameters.AddWithValue("@PostalCode", company.PostalCode); cmd.Parameters.AddWithValue("@Country", company.Country); @@ -104,7 +110,7 @@ namespace BoredCareers.Services.DatabaseService { public async Task DeleteCompany( int CompanyID ) { using( MySqlConnection connection = GetConnection() ) { MySqlCommand cmd; - connection.Open(); + await connection.OpenAsync(); string command = @" DELETE FROM Company WHERE ID = @ID; diff --git a/src/Server/Services/DatabaseService/Employee.cs b/src/Server/Services/DatabaseService/Employee.cs index 143a5b0..b3e926b 100644 --- a/src/Server/Services/DatabaseService/Employee.cs +++ b/src/Server/Services/DatabaseService/Employee.cs @@ -10,7 +10,7 @@ namespace BoredCareers.Services.DatabaseService { public async Task GetEmployee( int EmployeeID ) { Employee? employee = null; using( MySqlConnection connection = GetConnection() ) { - connection.Open(); + await connection.OpenAsync(); string command = @" SELECT * FROM Employee @@ -23,7 +23,6 @@ namespace BoredCareers.Services.DatabaseService { 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"); @@ -66,7 +65,7 @@ namespace BoredCareers.Services.DatabaseService { public async Task GetEmployeesFromCompany(int CompanyID) { List employees = new List(); using (MySqlConnection connection = GetConnection()) { - connection.Open(); + await connection.OpenAsync(); string command = @" SELECT * FROM Employee @@ -79,7 +78,6 @@ namespace BoredCareers.Services.DatabaseService { 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"); @@ -122,7 +120,7 @@ namespace BoredCareers.Services.DatabaseService { public async Task GetEmployeesFromAccount(int AccountID) { List employees = new List(); using (MySqlConnection connection = GetConnection()) { - connection.Open(); + await connection.OpenAsync(); string command = @" SELECT * FROM Employee @@ -135,7 +133,6 @@ namespace BoredCareers.Services.DatabaseService { 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"); @@ -177,7 +174,7 @@ namespace BoredCareers.Services.DatabaseService { public async Task SetEmployee(Employee employee) { using (MySqlConnection connection = GetConnection()) { - connection.Open(); + await connection.OpenAsync(); string command = @" INSERT INTO Employee @@ -201,7 +198,7 @@ namespace BoredCareers.Services.DatabaseService { public async Task DeleteEmployee( int EmployeeID ) { using( MySqlConnection connection = GetConnection() ) { MySqlCommand cmd; - connection.Open(); + await connection.OpenAsync(); string command = @" DELETE FROM Employee WHERE ID = @ID; diff --git a/src/Server/Services/DatabaseService/JobListing.cs b/src/Server/Services/DatabaseService/JobListing.cs index 434fde9..18fa04a 100644 --- a/src/Server/Services/DatabaseService/JobListing.cs +++ b/src/Server/Services/DatabaseService/JobListing.cs @@ -1,4 +1,5 @@ using BoredCareers.Entities; +using BoredCareers.Services.TimerService; using MySql.Data.MySqlClient; using System.Data; using System.Data.Common; @@ -9,7 +10,7 @@ namespace BoredCareers.Services.DatabaseService { public async Task GetJobListingPage(int PageNumber, int CountPerPage) { List joblistings = new List(); using (MySqlConnection connection = GetConnection()) { - connection.Open(); + await connection.OpenAsync(); string command = @" SELECT * FROM JobListing @@ -24,7 +25,6 @@ namespace BoredCareers.Services.DatabaseService { 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"); @@ -37,6 +37,7 @@ namespace BoredCareers.Services.DatabaseService { string _jobtype = reader.GetString("JobType"); bool _remote = reader.GetBoolean("Remote"); string _description = reader.GetString("Description"); + JobListingSkill[] _skills = await GetJobListingSkills(_id); DateTime _createtime = reader.GetDateTime("CreatedTime"); DateTime _modifiedtime = reader.GetDateTime("ModifiedTime"); bool _isdeleted = reader.GetBoolean("IsDeleted"); @@ -54,6 +55,7 @@ namespace BoredCareers.Services.DatabaseService { JobType = _jobtype, Remote = _remote, Description = _description, + Skills = _skills, CreatedTime = _createtime, ModifiedTime = _modifiedtime, IsDeleted = _isdeleted @@ -67,7 +69,7 @@ namespace BoredCareers.Services.DatabaseService { public async Task GetJobListingFromCompany(int CompanyID) { List joblistings = new List(); ; using (MySqlConnection connection = GetConnection()) { - connection.Open(); + await connection.OpenAsync(); string command = @" SELECT * FROM JobListing @@ -79,7 +81,6 @@ namespace BoredCareers.Services.DatabaseService { 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"); @@ -92,6 +93,7 @@ namespace BoredCareers.Services.DatabaseService { string _jobtype = reader.GetString("JobType"); bool _remote = reader.GetBoolean("Remote"); string _description = reader.GetString("Description"); + JobListingSkill[] _skills = await GetJobListingSkills(_id); DateTime _createtime = reader.GetDateTime("CreatedTime"); DateTime _modifiedtime = reader.GetDateTime("ModifiedTime"); bool _isdeleted = reader.GetBoolean("IsDeleted"); @@ -109,6 +111,7 @@ namespace BoredCareers.Services.DatabaseService { JobType = _jobtype, Remote = _remote, Description = _description, + Skills = _skills, CreatedTime = _createtime, ModifiedTime = _modifiedtime, IsDeleted = _isdeleted @@ -122,7 +125,7 @@ namespace BoredCareers.Services.DatabaseService { public async Task GetJobListing(int JobListingID) { JobListing? joblisting = null; using (MySqlConnection connection = GetConnection()) { - connection.Open(); + await connection.OpenAsync(); string command = @" SELECT * FROM JobListing @@ -134,7 +137,6 @@ namespace BoredCareers.Services.DatabaseService { 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"); @@ -147,6 +149,7 @@ namespace BoredCareers.Services.DatabaseService { string _jobtype = reader.GetString("JobType"); bool _remote = reader.GetBoolean("Remote"); string _description = reader.GetString("Description"); + JobListingSkill[] _skills = await GetJobListingSkills(_id); DateTime _createtime = reader.GetDateTime("CreatedTime"); DateTime _modifiedtime = reader.GetDateTime("ModifiedTime"); bool _isdeleted = reader.GetBoolean("IsDeleted"); @@ -164,6 +167,7 @@ namespace BoredCareers.Services.DatabaseService { JobType = _jobtype, Remote = _remote, Description = _description, + Skills = _skills, CreatedTime = _createtime, ModifiedTime = _modifiedtime, IsDeleted = _isdeleted @@ -174,9 +178,35 @@ namespace BoredCareers.Services.DatabaseService { return joblisting; } - public async Task SetJobListing( JobListing jobListing ) { - using( MySqlConnection connection = GetConnection() ) { - connection.Open(); + public async Task GetJobListingsPastExipre() { + List joblistings = new List(); + using (MySqlConnection connection = GetConnection()) { + await connection.OpenAsync(); + string command = @" + SELECT ID, CompanyID + FROM JobListing + WHERE IsDeleted = FALSE + AND CreatedTime < NOW() - INTERVAL 1 MONTH; + "; + MySqlCommand cmd = new MySqlCommand(command, connection); + + using (DbDataReader reader = await cmd.ExecuteReaderAsync()) { + while (await reader.ReadAsync()) { + int _id = reader.GetInt32("ID"); + int _companyid = reader.GetInt32("CompanyID"); + joblistings.Add(new JobListingDTO() { + JobListingID = _id, + CompanyID = _companyid + }); + } + } + } + return joblistings.ToArray(); + } + + public async Task SetJobListing(JobListing jobListing) { + using (MySqlConnection connection = GetConnection()) { + await connection.OpenAsync(); string command = @" INSERT INTO JobListing @@ -195,12 +225,11 @@ namespace BoredCareers.Services.DatabaseService { JobType = @JobType, Remote = @Remote, Description = @Description, - CreatedTime = @CreatedTime, ModifiedTime = @ModifiedTime, IsDeleted = @IsDeleted; "; - MySqlCommand cmd = new MySqlCommand( command , connection); + MySqlCommand cmd = new MySqlCommand(command, connection); cmd.Parameters.AddWithValue("@ID", jobListing.ID); cmd.Parameters.AddWithValue("@CompanyID", jobListing.CompanyID); cmd.Parameters.AddWithValue("@Title", jobListing.Title); @@ -213,25 +242,43 @@ 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.ToUniversalTime()); - cmd.Parameters.AddWithValue("@ModifiedTime", jobListing.ModifiedTime.ToUniversalTime()); + cmd.Parameters.AddWithValue("@CreatedTime", DateTime.UtcNow); + cmd.Parameters.AddWithValue("@ModifiedTime", DateTime.UtcNow); cmd.Parameters.AddWithValue("@IsDeleted", jobListing.IsDeleted); await cmd.ExecuteNonQueryAsync(); + + foreach (JobListingSkill cur in jobListing.Skills) { + await SetJobListingSkills(cur); + } + } + } + + public async Task DeleteJobListingsPastExipre() { + using (MySqlConnection connection = GetConnection()) { + await connection.OpenAsync(); + string command = @" + UPDATE JobListing + SET IsDeleted = TRUE + WHERE IsDeleted = FALSE + AND CreatedTime < NOW() - INTERVAL 1 MONTH; + "; + MySqlCommand cmd = new MySqlCommand(command, connection); + await cmd.ExecuteNonQueryAsync(); } } - public async Task DeleteJobListing( int JobListingID ) { - using( MySqlConnection connection = GetConnection() ) { + public async Task DeleteJobListing(int JobListingID) { + using (MySqlConnection connection = GetConnection()) { MySqlCommand cmd; - connection.Open(); + await connection.OpenAsync(); string command = @" UPDATE JobListing SET IsDeleted = TRUE WHERE ID = @ID; "; - cmd = new MySqlCommand( command, connection ); + cmd = new MySqlCommand(command, connection); cmd.Parameters.AddWithValue("@ID", JobListingID); await cmd.ExecuteNonQueryAsync(); diff --git a/src/Server/Services/DatabaseService/JobListingSkill.cs b/src/Server/Services/DatabaseService/JobListingSkill.cs new file mode 100644 index 0000000..953bc0b --- /dev/null +++ b/src/Server/Services/DatabaseService/JobListingSkill.cs @@ -0,0 +1,67 @@ +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 GetJobListingSkills(int JobListingID) { + List joblistingskills = new List(); + using (MySqlConnection connection = GetConnection()) { + await connection.OpenAsync(); + string command = @" + SELECT * + FROM JobListingSkill + WHERE JobListingID = @JobListingID; + "; + + MySqlCommand cmd = new MySqlCommand(command, connection); + cmd.Parameters.AddWithValue("@JobListingID", JobListingID); + + using (DbDataReader reader = await cmd.ExecuteReaderAsync()) { + while (await reader.ReadAsync()) { + if (reader == null) { break; } + int _id = reader.GetInt32("ID"); + int _joblistingid = reader.GetInt32("JobListingID"); + string _name = reader.GetString("Name"); + string _description = reader.GetString("Description"); + + joblistingskills.Add(new JobListingSkill() { + ID = _id, + JobListingID = _joblistingid, + Name = _name, + Description = _description + }); + } + } + } + return joblistingskills.ToArray(); + } + + public async Task SetJobListingSkills( JobListingSkill jobListingSkill ) { + using( MySqlConnection connection = GetConnection() ) { + await connection.OpenAsync(); + string command = @" + INSERT INTO JobListing + (ID,JobListingID,Name,Description) + VALUES + (@ID,@JobListingID,@Name,@Description) + ON DUPLICATE KEY UPDATE + JobListingID = @JobListingID, + Name = @Name, + Description = @Description; + "; + + MySqlCommand cmd = new MySqlCommand( command , connection); + cmd.Parameters.AddWithValue("@ID", jobListingSkill.ID); + cmd.Parameters.AddWithValue("@JobListingID", jobListingSkill.JobListingID); + cmd.Parameters.AddWithValue("@Name", jobListingSkill.Name); + cmd.Parameters.AddWithValue("@Description", jobListingSkill.Description); + + await cmd.ExecuteNonQueryAsync(); + } + } + + } +} diff --git a/src/Server/Services/DatabaseService/Resume.cs b/src/Server/Services/DatabaseService/Resume.cs index 88ff4d2..86eda81 100644 --- a/src/Server/Services/DatabaseService/Resume.cs +++ b/src/Server/Services/DatabaseService/Resume.cs @@ -9,7 +9,7 @@ namespace BoredCareers.Services.DatabaseService { public async Task GetResumes(int AccountID) { List resumes = new List(); using (MySqlConnection connection = GetConnection()) { - connection.Open(); + await connection.OpenAsync(); string command = @" SELECT * FROM Resume diff --git a/src/Server/Services/EmailService/VerifyEmail.cs b/src/Server/Services/EmailService/JobAutoCloseEmail.cs similarity index 94% rename from src/Server/Services/EmailService/VerifyEmail.cs rename to src/Server/Services/EmailService/JobAutoCloseEmail.cs index 9207038..a1055f3 100755 --- a/src/Server/Services/EmailService/VerifyEmail.cs +++ b/src/Server/Services/EmailService/JobAutoCloseEmail.cs @@ -5,8 +5,8 @@ namespace BoredCareers.Services { // @VerifyPassword // https://mistox.com/api/account/verifyemail?UserName=@UserName&Guid=@VerifyPassword - public static string VerifyEmailSubject = "Verify Your Email Address"; - public static string VerifyEmailEmail = @" + public static string JobAutoClosedSubject = "Verify Your Email Address"; + public static string JobAutoClosedEmail = @" diff --git a/src/Server/Services/EmailService/ResetPasswordEmail.cs b/src/Server/Services/EmailService/ResetPasswordEmail.cs deleted file mode 100755 index 7eff79c..0000000 --- a/src/Server/Services/EmailService/ResetPasswordEmail.cs +++ /dev/null @@ -1,51 +0,0 @@ -namespace BoredCareers.Services { - public partial class EmailService { - -// @UserName -// @ResetPassWord -// https://mistox.com/account/resetpassword?UserName=@UserName&ResetPwd=@ResetPassWord - - public static string ResetPasswordSubject = "Password Reset Request"; - public static string ResetPasswordEmail = @" - - - - - - Password Reset - - - - - - -
- - - - - - - - - - -
-

Password Reset Request

-
-

Hi @UserName,

-

We received a request to reset your password. You can reset your password by clicking the button below:

-

- Reset Password -

-

If you didn't request a password reset, you can safely ignore this email.

-

Best regards

-
-

If you have any questions, feel free to contact support.

-
-
- -"; - - } -} \ No newline at end of file