Merge pull request 'working' (#31) from working into main
Docker Build and Release Upload / build (push) Successful in 1m24s

Reviewed-on: #31
This commit was merged in pull request #31.
This commit is contained in:
2025-08-20 04:18:48 +00:00
15 changed files with 53 additions and 163 deletions
+1 -20
View File
@@ -1,13 +1,4 @@
Server: Server:
Emails:
Dont follow theme of website
When a company is created:
Send email -> verify ownership of the email
Resume:
Block API Access as much as possible [ Disallow AI keyword filters ]
Auth: Auth:
Make sure autorenew works Make sure autorenew works
@@ -21,13 +12,9 @@ Server:
Need to update notification email Need to update notification email
Create page to notify cx that their work email has been verified Create page to notify cx that their work email has been verified
Server.csproj:
Find a way to keep all the libraries up to date
Client: Client:
jobs/editor: jobs/editor:
Job Listing Skills exists but isn't implimented in the UI 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 Want to add completed job listing preview at end of carosel
Resume: Resume:
@@ -41,23 +28,17 @@ Client:
Mark ghost listings to allow users to be informed and put companies on blast Mark ghost listings to allow users to be informed and put companies on blast
company/editor: company/editor:
Need to lookup company before making a new one Keyboard Tab key does nothing
Tab key does nothing
Format phone number for database Format phone number for database
Check DataType's for email and phone. Check DataType's for email and phone.
Setup QueryParam's for Edit and New
Edit employees not implimented yet Edit employees not implimented yet
resume/editor: resume/editor:
Not fully tested yet
When adding new fields the fields above it glitch out and disappear
There is no data validation There is no data validation
Company: Company:
Need to impliment Add employee Need to impliment Add employee
Need to impliment Remove employee Need to impliment Remove employee
Edit Company -> Dont allow edit of company email due to it being verified
database: database:
Add Applied Jobs Table Add Applied Jobs Table
@@ -41,7 +41,7 @@
<img [src]="newListing.logo" /> <img [src]="newListing.logo" />
<!-- Need to fix for image file upload --> <!-- Need to fix for image file upload -->
<div id="FileUploadPlaceholder" ></div> <div id="FileUploadPlaceholder" ></div>
<input type="file" (change)="onFileSelected($event)" accept="image/*" /> <input name="file" type="file" (change)="onFileSelected($event)" accept="image/*" />
<button type="button" (click)="prevStep()">Back</button> <button type="button" (click)="prevStep()">Back</button>
<button type="button" (click)="nextStep()">Next</button> <button type="button" (click)="nextStep()">Next</button>
@@ -64,7 +64,7 @@
</div> </div>
<div class="half-frame"> <div class="half-frame">
<label>Company Phone Number</label> <label>Company Phone Number</label>
<input class="input-field" name="email" [(ngModel)]="newListing.phone" type="text" placeholder="+1 800-000-0000" /> <input class="input-field" name="phone" [(ngModel)]="newListing.phone" type="text" placeholder="+1 800-000-0000" />
</div> </div>
</div> </div>
<button type="button" (click)="prevStep()">Back</button> <button type="button" (click)="prevStep()">Back</button>
@@ -156,7 +156,11 @@
</div> </div>
<div class="content-frame"> <div class="content-frame">
<button type="button" (click)="prevStep()">Back</button> <button type="button" (click)="prevStep()">Back</button>
<button type="submit">CREATE COMPANY</button> @if(isNewCompany){
<button type="submit">CREATE COMPANY</button>
}@else{
<button type="submit">UPDATE COMPANY</button>
}
</div> </div>
<div class="footer-frame"> <div class="footer-frame">
<span>Does everything look good</span><br /> <span>Does everything look good</span><br />
@@ -19,6 +19,7 @@ export class CompanyEditorComponent {
currentStep: number = 0; currentStep: number = 0;
public newListing: Company = new Company(); public newListing: Company = new Company();
public isNewCompany: boolean = true;
public ErrorMsg: string = ""; public ErrorMsg: string = "";
MaxFileMB: number = 3; MaxFileMB: number = 3;
@@ -30,8 +31,20 @@ export class CompanyEditorComponent {
}; };
ngOnInit(){ ngOnInit(){
// Query param CompanyID -> Edit this.route.queryParams.subscribe(params => {
// Query param null -> New const CompanyID = params['CompanyID'] ? +params['CompanyID'] : null;
if (CompanyID !== null){
this.http.get<Company>("api/company?CompanyID=" + CompanyID).subscribe({
next: data => {
this.newListing = data;
this.isNewCompany = false;
},
error: err => {
this.ErrorMsg = err.error;
}
});
}
});
} }
ngAfterViewInit(){ ngAfterViewInit(){
@@ -158,9 +171,9 @@ export class CompanyEditorComponent {
return; return;
} }
this.http.post("api/company?newCompany=true", company).subscribe({ this.http.post("api/company", company).subscribe({
next: data => { next: data => {
this.router.navigate([""]); this.router.navigate(["/company"]);
}, },
error: err => { error: err => {
this.ErrorMsg = err.error; this.ErrorMsg = err.error;
@@ -108,7 +108,11 @@
<div class="center"> <div class="center">
<div class="content-frame"> <div class="content-frame">
<button type="button" (click)="prevStep()">Back</button> <button type="button" (click)="prevStep()">Back</button>
<button type="submit">CREATE JOB LISTING</button> @if (isNewListing){
<button type="submit">CREATE LISTING</button>
}@else{
<button type="submit">UPDATE LISTING</button>
}
</div> </div>
</div> </div>
</div> </div>
@@ -20,9 +20,9 @@ export class JobEditorComponent {
currentStep: number = 0; currentStep: number = 0;
public Listing: JobListing = new JobListing(); public Listing: JobListing = new JobListing();
public isNewListing: boolean = true;
public mode: string = ""; public mode: string = "";
public modeID: number = 0;
constructor( private http: HttpClient, private router: Router, private route: ActivatedRoute, private title: Title, public auth: Authentication ) { constructor( private http: HttpClient, private router: Router, private route: ActivatedRoute, private title: Title, public auth: Authentication ) {
this.title.setTitle("Jobs - Editor | BoredCareers"); this.title.setTitle("Jobs - Editor | BoredCareers");
@@ -39,10 +39,11 @@ export class JobEditorComponent {
this.router.navigate([""]); this.router.navigate([""]);
}else if (CompanyID !== null ){ }else if (CompanyID !== null ){
this.mode = "new"; this.mode = "new";
this.modeID = CompanyID; this.Listing.companyID = CompanyID;
}else if(JobID !== null){ }else if(JobID !== null){
this.mode = "edit"; this.mode = "edit";
this.modeID = JobID; this.Listing.id = JobID;
this.isNewListing = false;
}else if (CompanyID === null && JobID === null){ }else if (CompanyID === null && JobID === null){
this.router.navigate([""]); this.router.navigate([""]);
} }
@@ -90,14 +91,9 @@ export class JobEditorComponent {
} }
SubmitForm(jobListing: JobListing){ SubmitForm(jobListing: JobListing){
if (this.mode === "new"){
jobListing.companyID = this.modeID;
} else if (this.mode === "edit"){
jobListing.id = this.modeID;
}
this.http.post("api/joblisting", jobListing).subscribe({ this.http.post("api/joblisting", jobListing).subscribe({
next: data => { next: data => {
this.router.navigate([""]); this.router.navigate(["/company"]);
}, },
error: err => { error: err => {
this.ErrorMsg = err.error; this.ErrorMsg = err.error;
@@ -84,7 +84,7 @@ export class ResumesEditorComponent {
resume.accountID = this.auth.loggedInUser.id; resume.accountID = this.auth.loggedInUser.id;
this.http.post("api/resume", resume).subscribe({ this.http.post("api/resume", resume).subscribe({
next: data => { next: data => {
this.router.navigate(["/"]); this.router.navigate(["/resumes"]);
}, },
error: err => { error: err => {
this.ErrorMsg = err.error; this.ErrorMsg = err.error;
@@ -1,7 +1,6 @@
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using BoredCareers.Services.DatabaseService; using BoredCareers.Services.DatabaseService;
using BoredCareers.Entities; using BoredCareers.Entities;
using System.Web.Http;
namespace BoredCareers.Controllers { namespace BoredCareers.Controllers {
[ApiController] [ApiController]
@@ -1,7 +1,6 @@
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using BoredCareers.Services.DatabaseService; using BoredCareers.Services.DatabaseService;
using BoredCareers.Entities; using BoredCareers.Entities;
using System.Web.Http;
using System.Text.Json; using System.Text.Json;
using System.Text; using System.Text;
+15 -16
View File
@@ -1,7 +1,6 @@
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using BoredCareers.Services.DatabaseService; using BoredCareers.Services.DatabaseService;
using BoredCareers.Entities; using BoredCareers.Entities;
using System.Web.Http;
using BoredCareers.Services; using BoredCareers.Services;
namespace BoredCareers.Controllers { namespace BoredCareers.Controllers {
@@ -29,25 +28,25 @@ namespace BoredCareers.Controllers {
} }
[HttpPost] [HttpPost]
public async Task<IActionResult> SetCompany([FromBody] Company company, [FromQuery] bool newCompany = false) { public async Task<IActionResult> SetCompany([FromBody] Company company) {
if (isLoggedIn()) { if (isLoggedIn()) {
if (newCompany) { Company? test = await _databaseService.GetCompany(Convert.ToInt32(company.ID));
Company? test = await _databaseService.GetCompany(Convert.ToInt32(company.ID)); if (test == null) {
if (test == null) { company.ID = await _databaseService.SetCompany(company);
company.ID = await _databaseService.SetCompany(company);
await _databaseService.SetEmployee(new Employee() {
await _databaseService.SetEmployee(new Employee() { AccountID = getLoggedInUserID(),
AccountID = getLoggedInUserID(), AccountName = getLoggedInUser().UserName,
AccountName = getLoggedInUser().UserName, AccountEmail = getLoggedInUser().Email,
AccountEmail = getLoggedInUser().Email, Company = company
Company = company });
}); await SendVerify(Convert.ToInt32(company.ID));
return Ok(); return Ok();
}
return NotFound("The company already exists");
} else { } else {
if (await isLoggedInUserEmployeeOf(Convert.ToInt32(company.ID))) { if (await isLoggedInUserEmployeeOf(Convert.ToInt32(company.ID))) {
if (company.Email != test.Email) {
company.EmailVerified = false;
}
await _databaseService.SetCompany(company); await _databaseService.SetCompany(company);
return Ok(); return Ok();
} }
@@ -1,7 +1,6 @@
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using BoredCareers.Services.DatabaseService; using BoredCareers.Services.DatabaseService;
using BoredCareers.Entities; using BoredCareers.Entities;
using System.Web.Http;
namespace BoredCareers.Controllers { namespace BoredCareers.Controllers {
[ApiController] [ApiController]
@@ -1,7 +1,6 @@
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using BoredCareers.Services.DatabaseService; using BoredCareers.Services.DatabaseService;
using BoredCareers.Entities; using BoredCareers.Entities;
using System.Web.Http;
namespace BoredCareers.Controllers { namespace BoredCareers.Controllers {
[ApiController] [ApiController]
@@ -1,7 +1,6 @@
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using BoredCareers.Services.DatabaseService; using BoredCareers.Services.DatabaseService;
using BoredCareers.Entities; using BoredCareers.Entities;
using System.Web.Http;
namespace BoredCareers.Controllers { namespace BoredCareers.Controllers {
[ApiController] [ApiController]
-1
View File
@@ -175,7 +175,6 @@ builder.Services.AddRateLimiter(options => {
//////////////////////////////// ////////////////////////////////
builder.Services.AddHostedService<JobCleanupService>(); builder.Services.AddHostedService<JobCleanupService>();
ResumeService.init();
//////////////////////////////// ////////////////////////////////
///// ASPNET Core Function ///// ///// ASPNET Core Function /////
+2 -10
View File
@@ -8,18 +8,10 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="9.0.3" /> <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.8" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="9.0.3" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.3" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.WebApiCompatShim" Version="2.3.0" />
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3" /> <PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
<PackageReference Include="MySql.Data" Version="9.4.0" />
<PackageReference Include="Stripe.net" Version="48.2.0" /> <PackageReference Include="Stripe.net" Version="48.2.0" />
<PackageReference Include="MySql.Data" Version="9.2.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="System.Net.Http" Version="4.3.4" />
<PackageReference Include="HtmlSanitizer" Version="9.0.886" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
-93
View File
@@ -1,93 +0,0 @@
using Ganss.Xss;
namespace BoredCareers.Services {
public class ResumeService {
static HtmlSanitizer _self = new HtmlSanitizer();
public static void init() {
// Clear default allowed tags and attributes
_self.AllowedAttributes.Clear();
_self.AllowedSchemes.Clear();
_self.AllowedAtRules.Clear();
_self.AllowedClasses.Clear();
// Allowed HTML Tags
_self.AllowedTags.Clear();
string[] safeTags = [
"b", "strong", "i", "em", "u", "small", "mark", "del", "ins", "sub", "sup",
"p", "br", "hr", "div", "span",
"section", "article", "header", "footer", "aside", "main", "nav",
"ul", "ol", "li", "dl", "dt", "dd",
"h1", "h2", "h3", "h4", "h5", "h6",
"blockquote", "q", "cite",
"code", "pre", "samp", "kbd", "var",
"table", "thead", "tbody", "tfoot", "tr", "td", "th",
];
foreach (string cur in safeTags) {
_self.AllowedTags.Add(cur);
}
// Allow inline styles only
_self.AllowedAttributes.Add("style");
string[] safeCssProperties = [
"align-content", "align-items", "align-self", "all",
"animation", "animation-delay", "animation-direction", "animation-duration",
"animation-fill-mode", "animation-iteration-count", "animation-name", "animation-play-state",
"animation-timing-function", "backface-visibility", "background-color", "background-clip",
"background-origin", "background-position", "background-repeat", "background-size",
"border", "border-bottom", "border-bottom-color", "border-bottom-left-radius",
"border-bottom-right-radius", "border-bottom-style", "border-bottom-width", "border-color",
"border-image-outset", "border-image-repeat", "border-image-slice", "border-image-source",
"border-image-width", "border-left", "border-left-color", "border-left-style",
"border-left-width", "border-radius", "border-right", "border-right-color",
"border-right-style", "border-right-width", "border-spacing", "border-style",
"border-top", "border-top-color", "border-top-left-radius", "border-top-right-radius",
"border-top-style", "border-top-width", "border-width", "bottom",
"box-decoration-break", "box-shadow", "box-sizing", "caption-side",
"clear", "color", "column-count", "column-fill",
"column-gap", "column-rule-color", "column-rule-style", "column-rule-width",
"column-span", "column-width", "columns", "counter-increment",
"counter-reset", "direction", "display", "empty-cells",
"flex", "flex-basis", "flex-direction", "flex-flow",
"flex-grow", "flex-shrink", "flex-wrap", "float",
"font-family", "font-feature-settings", "font-kerning", "font-language-override",
"font-size", "font-size-adjust", "font-stretch", "font-style",
"font-synthesis", "font-variant", "font-variant-alternates", "font-variant-caps",
"font-variant-east-asian", "font-variant-ligatures", "font-variant-numeric", "font-variant-position",
"font-weight", "grid", "grid-area", "grid-auto-columns",
"grid-auto-flow", "grid-auto-rows", "grid-column", "grid-column-end",
"grid-column-gap", "grid-column-start", "grid-gap", "grid-row",
"grid-row-end", "grid-row-gap", "grid-row-start", "grid-template",
"grid-template-areas", "grid-template-columns", "grid-template-rows", "height",
"hyphens", "image-rendering", "isolation", "justify-content",
"left", "letter-spacing", "line-height", "list-style-position",
"list-style-type", "margin", "margin-bottom", "margin-left",
"margin-right", "margin-top", "max-height", "max-width",
"min-height", "min-width", "object-fit", "object-position",
"opacity", "order", "orphans", "outline-color",
"outline-offset", "outline-style", "outline-width", "overflow",
"overflow-wrap", "overflow-x", "overflow-y", "padding",
"padding-bottom", "padding-left", "padding-right", "padding-top",
"page-break-after", "page-break-before", "page-break-inside", "perspective",
"perspective-origin", "pointer-events", "position", "quotes",
"resize", "right", "scroll-behavior", "table-layout",
"tab-size", "text-align", "text-align-last", "text-combine-upright",
"text-indent", "text-justify", "text-orientation", "text-overflow",
"text-shadow", "text-transform", "text-underline-position", "top",
"transform", "transform-origin", "transform-style", "transition",
"transition-delay", "transition-duration", "transition-property", "transition-timing-function",
"unicode-bidi", "user-select", "vertical-align", "visibility",
"white-space", "widows", "width", "word-break",
"word-spacing", "word-wrap", "writing-mode", "z-index"
];
foreach (string cur in safeCssProperties) {
_self.AllowedCssProperties.Add(cur);
}
}
public static string RemoveJavascript(string InputHTML) {
return _self.Sanitize(InputHTML);
}
}
}