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.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