mirror of
https://github.com/Sea-Haven-Industries/seahaven-site.git
synced 2026-09-30 04:13:15 +00:00
81 lines
2.4 KiB
JavaScript
81 lines
2.4 KiB
JavaScript
|
|
const FEED_URL = "https://jobs.appone.com/api/portal/v1/companyjobposts/seahaven";
|
||
|
|
const PORTAL_URL = "https://jobs.appone.com/seahaven";
|
||
|
|
const ALLOWED_HOSTS = new Set(["apply.appone.com", "jobs.appone.com"]);
|
||
|
|
|
||
|
|
function isAllowedJobUrl(value) {
|
||
|
|
if (typeof value !== "string") return false;
|
||
|
|
try {
|
||
|
|
const url = new URL(value);
|
||
|
|
return url.protocol === "https:" && ALLOWED_HOSTS.has(url.hostname);
|
||
|
|
} catch {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
function titleCase(value) {
|
||
|
|
return value
|
||
|
|
.replace(/[_-]+/g, " ")
|
||
|
|
.toLowerCase()
|
||
|
|
.replace(/\b\w/g, (char) => char.toUpperCase());
|
||
|
|
}
|
||
|
|
|
||
|
|
function formatSalary(salary) {
|
||
|
|
if (!salary || salary.salaryOption !== "RANGE" || salary.periodType !== "YEAR") {
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
const { minimum, maximum } = salary;
|
||
|
|
if (!Number.isFinite(minimum) || !Number.isFinite(maximum)) return null;
|
||
|
|
const money = new Intl.NumberFormat("en-US", {
|
||
|
|
style: "currency",
|
||
|
|
currency: "USD",
|
||
|
|
maximumFractionDigits: 0,
|
||
|
|
});
|
||
|
|
return `${money.format(minimum)}\u2013${money.format(maximum)} per year`;
|
||
|
|
}
|
||
|
|
|
||
|
|
function normalizePost(post) {
|
||
|
|
if (!post || typeof post !== "object") {
|
||
|
|
throw new Error("Paychex job feed returned a posting that is not an object");
|
||
|
|
}
|
||
|
|
const title = typeof post.jobTitle === "string" ? post.jobTitle.trim() : "";
|
||
|
|
if (!title) {
|
||
|
|
throw new Error("Paychex job feed returned a posting without a title");
|
||
|
|
}
|
||
|
|
if (!isAllowedJobUrl(post.jobPostUrl)) {
|
||
|
|
throw new Error(`Paychex job feed returned an unexpected apply URL for "${title}"`);
|
||
|
|
}
|
||
|
|
return {
|
||
|
|
title,
|
||
|
|
url: post.jobPostUrl,
|
||
|
|
location: typeof post.location === "string" ? post.location.trim() : "",
|
||
|
|
jobType: typeof post.jobType === "string" ? post.jobType.trim() : "",
|
||
|
|
workplaceType: typeof post.workplaceType === "string" && post.workplaceType.trim()
|
||
|
|
? titleCase(post.workplaceType.trim())
|
||
|
|
: "",
|
||
|
|
salary: formatSalary(post.salary),
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
module.exports = async function () {
|
||
|
|
const response = await fetch(FEED_URL, {
|
||
|
|
headers: {
|
||
|
|
Accept: "application/json",
|
||
|
|
"x-payx-bizpn": "Load Company Job Portal",
|
||
|
|
},
|
||
|
|
signal: AbortSignal.timeout(15000),
|
||
|
|
});
|
||
|
|
if (!response.ok) {
|
||
|
|
throw new Error(`Paychex job feed request failed (${response.status})`);
|
||
|
|
}
|
||
|
|
|
||
|
|
const data = await response.json();
|
||
|
|
if (!data || !Array.isArray(data.jobPosts)) {
|
||
|
|
throw new Error("Paychex job feed is missing jobPosts");
|
||
|
|
}
|
||
|
|
|
||
|
|
return {
|
||
|
|
portalUrl: PORTAL_URL,
|
||
|
|
posts: data.jobPosts.map(normalizePost),
|
||
|
|
};
|
||
|
|
};
|