Skip to content

Commit 864d3a1

Browse files
authored
Merge branch 'main' into replace-clearbit-logos
2 parents 4817cca + 120c281 commit 864d3a1

File tree

13 files changed

+12751
-6870
lines changed

13 files changed

+12751
-6870
lines changed

.github/workflows/migration.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@ jobs:
2828
POSTGRES_URL: ${{ secrets.POSTGRES_URL_STAGING }}
2929
steps:
3030
- uses: actions/checkout@v4
31-
31+
3232
- name: Setup
3333
uses: ./tooling/github/setup
34-
34+
3535
- name: Migrate Staging
3636
run: pnpm db:migrate

.vscode/launch.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
{
22
"version": "0.2.0",
33
"configurations": [
4-
54
{
65
"name": "Next.js",
76
"type": "node-terminal",

apps/web/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
"@trpc/client": "11.0.0-rc.441",
2525
"@trpc/react-query": "11.0.0-rc.441",
2626
"@trpc/server": "11.0.0-rc.441",
27+
"bad-words": "^4.0.0",
2728
"dayjs": "^1.11.13",
2829
"fuse.js": "^7.0.0",
2930
"geist": "^1.3.0",

apps/web/src/app/(pages)/(dashboard)/companies/page.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
"use client";
22

3-
import { useRouter } from "next/navigation";
3+
import { useRouter, useSearchParams } from "next/navigation";
44

5-
import { useSearchParams } from "next/navigation";
65
import { CompanyCardPreview } from "~/app/_components/companies/company-card-preview";
76
import LoadingResults from "~/app/_components/loading-results";
87
import NoResults from "~/app/_components/no-results";

apps/web/src/app/_components/form/review-form.tsx

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import { useState } from "react";
44
import Image from "next/image";
55
import { zodResolver } from "@hookform/resolvers/zod";
6+
import { Filter } from "bad-words";
67
import dayjs from "dayjs";
78
import { useForm } from "react-hook-form";
89
import { animateScroll as scroll } from "react-scroll";
@@ -26,6 +27,7 @@ import { SubmissionConfirmation } from "~/app/_components/form/submission-confir
2627
import { api } from "~/trpc/react";
2728
import { SubmissionFailure } from "./submission-failure";
2829

30+
const filter = new Filter();
2931
const formSchema = z.object({
3032
workTerm: z.nativeEnum(WorkTerm, {
3133
required_error: "You need to select a co-op cycle.",
@@ -78,20 +80,31 @@ const formSchema = z.object({
7880
})
7981
.min(1)
8082
.max(5),
81-
interviewReview: z.string().optional(),
83+
interviewReview: z
84+
.string()
85+
.optional()
86+
.refine((val) => !filter.isProfane(val ?? ""), {
87+
message: "The interview review cannot contain profane words.",
88+
}),
8289
reviewHeadline: z
8390
.string({
8491
required_error: "You need to enter a Review Headline.",
8592
})
8693
.min(8, {
8794
message: "The review headline must be at least 8 characters.",
95+
})
96+
.refine((val) => !filter.isProfane(val), {
97+
message: "The review headline cannot contain profane words.",
8898
}),
8999
textReview: z
90100
.string({
91101
required_error: "You need to enter a review for your co-op.",
92102
})
93103
.min(8, {
94104
message: "The review must be at least 8 characters.",
105+
})
106+
.refine((val) => !filter.isProfane(val), {
107+
message: "The review cannot contain profane words.",
95108
}),
96109
locationId: z.string().optional(),
97110
hourlyPay: z.coerce

apps/web/src/app/_components/reviews/new-role-dialogue.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { useState } from "react";
22
import { zodResolver } from "@hookform/resolvers/zod";
3+
import { Filter } from "bad-words";
34
import { useForm } from "react-hook-form";
45
import { z } from "zod";
56

@@ -27,11 +28,15 @@ import { Textarea } from "@cooper/ui/textarea";
2728

2829
import { api } from "~/trpc/react";
2930

31+
const filter = new Filter();
3032
const roleSchema = z.object({
3133
title: z
3234
.string({ required_error: "You need to enter a role title." })
3335
.min(5, {
3436
message: "The role title must be at least 5 characters.",
37+
})
38+
.refine((val) => !filter.isProfane(val), {
39+
message: "The title cannot contain profane words.",
3540
}),
3641
description: z
3742
.string()
@@ -40,6 +45,9 @@ const roleSchema = z.object({
4045
})
4146
.max(500, {
4247
message: "The description must be at most 500 characters.",
48+
})
49+
.refine((val) => !filter.isProfane(val), {
50+
message: "The description cannot contain profane words.",
4351
}),
4452
companyId: z.string(),
4553
createdBy: z.string(),

packages/api/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
"@cooper/db": "workspace:*",
2525
"@cooper/validators": "workspace:*",
2626
"@trpc/server": "11.0.0-rc.441",
27+
"bad-words": "^4.0.0",
2728
"fuse.js": "^7.0.0",
2829
"superjson": "2.2.1",
2930
"zod": "catalog:"

packages/api/src/router/review.ts

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { TRPCRouterRecord } from "@trpc/server";
22
import { TRPCError } from "@trpc/server";
3+
import { Filter } from "bad-words";
34
import Fuse from "fuse.js";
45
import { z } from "zod";
56

@@ -90,8 +91,40 @@ export const reviewRouter = {
9091
message: "You must be logged in to leave a review",
9192
});
9293
}
94+
95+
// Initialize bad words filter
96+
const filter = new Filter();
97+
98+
if (filter.isProfane(input.reviewHeadline)) {
99+
throw new TRPCError({
100+
code: "PRECONDITION_FAILED",
101+
message: "Review headline cannot contain profane words",
102+
});
103+
} else if (filter.isProfane(input.textReview)) {
104+
throw new TRPCError({
105+
code: "PRECONDITION_FAILED",
106+
message: "Review text cannot contain profane words",
107+
});
108+
} else if (filter.isProfane(input.interviewReview ?? "")) {
109+
throw new TRPCError({
110+
code: "PRECONDITION_FAILED",
111+
message: "Interview review cannot contain profane words",
112+
});
113+
}
114+
115+
// Create a clean version of the input with filtered strings
116+
const cleanInput = {
117+
...input,
118+
reviewHeadline: filter.clean(input.reviewHeadline),
119+
textReview: filter.clean(input.textReview),
120+
// Keep non-string fields as they are
121+
profileId: input.profileId,
122+
interviewReview: filter.clean(input.interviewReview ?? ""),
123+
};
124+
125+
// Rest of the validation logic
93126
const reviews = await ctx.db.query.Review.findMany({
94-
where: eq(Review.profileId, input.profileId),
127+
where: eq(Review.profileId, cleanInput.profileId),
95128
});
96129
if (reviews.length >= 5) {
97130
throw new TRPCError({
@@ -101,8 +134,8 @@ export const reviewRouter = {
101134
}
102135
const reviewsInSameCycle = reviews.filter(
103136
(review) =>
104-
review.workTerm === input.workTerm &&
105-
review.workYear === input.workYear,
137+
review.workTerm === cleanInput.workTerm &&
138+
review.workYear === cleanInput.workYear,
106139
);
107140
if (reviewsInSameCycle.length >= 2) {
108141
throw new TRPCError({
@@ -129,7 +162,7 @@ export const reviewRouter = {
129162
}
130163
}
131164

132-
return ctx.db.insert(Review).values(input);
165+
return ctx.db.insert(Review).values(cleanInput);
133166
}),
134167

135168
delete: protectedProcedure.input(z.string()).mutation(({ ctx, input }) => {

packages/api/src/router/role.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import type { TRPCRouterRecord } from "@trpc/server";
2+
import { TRPCError } from "@trpc/server";
3+
import { Filter } from "bad-words";
24
import { z } from "zod";
35

46
import type { ReviewType, RoleType } from "@cooper/db/schema";
@@ -134,7 +136,26 @@ export const roleRouter = {
134136
create: protectedProcedure
135137
.input(CreateRoleSchema)
136138
.mutation(({ ctx, input }) => {
137-
return ctx.db.insert(Role).values(input);
139+
const filter = new Filter();
140+
141+
if (filter.isProfane(input.title)) {
142+
throw new TRPCError({
143+
code: "PRECONDITION_FAILED",
144+
message: "Title cannot contain profane words",
145+
});
146+
} else if (filter.isProfane(input.description ?? "")) {
147+
throw new TRPCError({
148+
code: "PRECONDITION_FAILED",
149+
message: "Description cannot contain profane words",
150+
});
151+
}
152+
153+
const cleanInput = {
154+
...input,
155+
title: filter.clean(input.title),
156+
description: filter.clean(input.description ?? ""),
157+
};
158+
return ctx.db.insert(Role).values(cleanInput);
138159
}),
139160

140161
delete: protectedProcedure.input(z.string()).mutation(({ ctx, input }) => {

packages/scraper/scraped/companyDetails_2025-03-06.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24475,4 +24475,4 @@
2447524475
"description": "Zyoin Jobs offers comprehensive staffing and recruitment solutions in India, catering to both technical and non-technical talent. They serve over 300 partner organizations, addressing diverse hiring needs.",
2447624476
"website": "zyoin.com"
2447724477
}
24478-
}
24478+
}

packages/scraper/scraped/companyNames_2025-03-06.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6143,4 +6143,4 @@
61436143
"Zyston",
61446144
"Zymeworks",
61456145
"Zyoin Jobs"
6146-
]
6146+
]

packages/scraper/scraped/seed data/companies.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30594,4 +30594,4 @@
3059430594
"website": "zyoin.com",
3059530595
"industry": "TECHNOLOGY"
3059630596
}
30597-
}
30597+
}

0 commit comments

Comments
 (0)