Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion src/features/jobs-moderation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,13 @@ const jobModeration = async (bot: Client) => {
const errors = validate(posts, message).filter(
(e) => e.type !== POST_FAILURE_REASONS.tooFrequent,
);
console.log(
`[DEBUG] validating edited job post from @${
message.author.username
}, errors: ${JSON.stringify(errors)}`,
);

if (errors) {
if (errors.length > 0) {
const isRecentEdit =
differenceInMinutes(new Date(), message.createdAt) < REPOST_THRESHOLD;
errors.unshift({
Expand Down
2 changes: 1 addition & 1 deletion src/features/jobs-moderation/job-mod-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ export const loadJobs = async (bot: Client, channel: TextChannel) => {
}
oldestMessage = newMessages
.sort((a, b) => compareAsc(b.createdAt, a.createdAt))
.at(-0);
.at(-1);
if (!oldestMessage) break;

const [hiring, forHire] = partition(
Expand Down
76 changes: 54 additions & 22 deletions src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
StoredMessage,
getJobPosts,
} from "./features/jobs-moderation/job-mod-helpers.js";
import { compressLineBreaks } from "./helpers/string.js";
import { compressLineBreaks, simplifyString } from "./helpers/string.js";
import { constructDiscordLink } from "./helpers/discord.js";
import { reactibotApiKey } from "./helpers/env.js";

Expand Down Expand Up @@ -81,6 +81,7 @@ const openApiConfig = {
},
},
JobBoardCache: {
count: { type: "number" },
pages: { type: "number" },
page: { type: "number" },
limit: { type: "number" },
Expand Down Expand Up @@ -129,8 +130,7 @@ const openApiConfig = {

fastify.addHook("onRequest", async (request, reply) => {
const apiKey = request.headers["api-key"];
console.log("onreq");
if (apiKey !== reactibotApiKey) {
if (request.hostname !== "localhost" && apiKey !== reactibotApiKey) {
reply.code(401).send({ error: "Unauthorized" });
return;
}
Expand Down Expand Up @@ -160,10 +160,14 @@ fastify.get(
},
},
async (req) => {
const { page, limit } = getPaginationFromRequest(req);
const { page, limit, requiredTags } = parseQuery(req);
const { hiring } = getJobPosts();

return paginateResponse(page, limit, hiring.map(renderPost));
return paginateResponse(
page,
limit,
filterTags(requiredTags, hiring).map(renderPost),
);
},
);
fastify.get(
Expand All @@ -181,31 +185,58 @@ fastify.get(
},
},
async (req) => {
const { page, limit } = getPaginationFromRequest(req);
const { page, limit, requiredTags } = parseQuery(req);
const { forHire } = getJobPosts();

return paginateResponse(page, limit, forHire.map(renderPost));
return paginateResponse(
page,
limit,
filterTags(requiredTags, forHire).map(renderPost),
);
},
);

const filterTags = (requiredTags: string[], posts: StoredMessage[]) => {
return posts.filter((post) => {
const simplifiedTags = post.tags.map((t) =>
simplifyString(t).replaceAll(" ", ""),
);
return requiredTags.every((rt) => simplifiedTags.includes(rt));
});
};

const DEFAULT_LIMIT = 10;
const getPaginationFromRequest = (req: FastifyRequest) => {
const { page, limit } = req.query as { page?: string; limit?: string };
let outPage: number, outLimit: number;
if (!page) {
outPage = 1;
} else {
outPage = parseInt(page);
if (isNaN(outPage)) outPage = 1;
}
if (!limit) {
outLimit = DEFAULT_LIMIT;
const parseQuery = (req: FastifyRequest) => {
const {
page: rawPage,
limit: rawLimit,
...tags
} = req.query as {
page?: string;
limit?: string;
};

return {
requiredTags: Object.entries(tags)
.filter(([, value]) => value)
.map(([tag]) => normalizeTags(tag)),
page: parseNumber(rawPage, 1),
limit: parseNumber(rawLimit, DEFAULT_LIMIT, MAX_LIMIT),
};
};

const normalizeTags = (tag: string) => simplifyString(tag);

const parseNumber = (inVal: any, defaultVal: number, max = Infinity) => {
let out;
if (!inVal) {
out = defaultVal;
} else {
outLimit = parseInt(limit);
if (isNaN(outLimit)) outLimit = DEFAULT_LIMIT;
if (outLimit > MAX_LIMIT) outLimit = MAX_LIMIT;
out = parseInt(inVal);
if (isNaN(out)) out = defaultVal;
}
return { page: outPage, limit: outLimit };
if (out > max) out = max;
return out;
};

const paginateResponse = <T extends Array<any>>(
Expand All @@ -215,6 +246,7 @@ const paginateResponse = <T extends Array<any>>(
) => {
const offset = (page - 1) * limit;
return {
count: data.length,
data: data.slice(offset, offset + limit),
page,
limit,
Expand Down
Loading