Skip to content

Commit 32a212b

Browse files
committed
fix: require registered account to publish modules + leave reviews
- POST /api/modules: verifies user_profiles exists + email_verified - POST /api/modules/:slug/review: verifies user_profiles exists - Returns 401 with signup link if not registered - Returns 403 if email not verified - Updated mocks, 128 tests passing
1 parent 19e646a commit 32a212b

4 files changed

Lines changed: 60 additions & 7 deletions

File tree

src/app/api/modules/[slug]/review/__tests__/route.test.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,15 @@ vi.mock("@/lib/supabase", () => ({
5757
}),
5858
};
5959
}
60+
if (table === "user_profiles") {
61+
return {
62+
select: vi.fn().mockReturnValue({
63+
eq: vi.fn().mockReturnValue({
64+
single: vi.fn().mockResolvedValue({ data: { id: 'user-1' }, error: null }),
65+
}),
66+
}),
67+
};
68+
}
6069
return {};
6170
},
6271
}),

src/app/api/modules/[slug]/review/route.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,20 @@ export async function POST(
7272

7373
const sb = getSupabaseAdmin();
7474

75+
// Verify user is registered
76+
const { data: profile } = await sb
77+
.from("user_profiles")
78+
.select("id")
79+
.eq("email", body.user_email)
80+
.single();
81+
82+
if (!profile) {
83+
return NextResponse.json(
84+
{ error: "You must create an account to leave reviews." },
85+
{ status: 401 }
86+
);
87+
}
88+
7589
const { data: mod } = await sb
7690
.from("modules")
7791
.select("id, rating_avg, rating_count")

src/app/api/modules/__tests__/route.test.ts

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,21 @@ function resetChain(overrides: {
2929
mockSelect.mockReturnValue({ eq: mockEq });
3030
}
3131

32+
const mockProfileSingle = vi.fn().mockResolvedValue({ data: { id: 'user-1', email: 'test@example.com', email_verified: true }, error: null });
33+
3234
vi.mock("@/lib/supabase", () => ({
3335
getSupabaseAdmin: () => ({
34-
from: () => ({
35-
select: mockSelect,
36-
insert: mockInsert,
37-
}),
36+
from: (table: string) => {
37+
if (table === 'user_profiles') {
38+
return {
39+
select: () => ({ eq: () => ({ single: mockProfileSingle }) }),
40+
};
41+
}
42+
return {
43+
select: mockSelect,
44+
insert: mockInsert,
45+
};
46+
},
3847
}),
3948
slugify: (name: string) =>
4049
name

src/app/api/modules/route.ts

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,8 @@ export async function GET(request: NextRequest) {
6363

6464
/**
6565
* POST /api/modules
66-
* Publish a new module. Accepts name + git_url/homepage_url + optional overrides.
66+
* Publish a new module. Requires a registered account.
67+
* Accepts name + git_url/homepage_url + optional overrides.
6768
*/
6869
export async function POST(request: NextRequest) {
6970
let body: Record<string, unknown>;
@@ -83,9 +84,29 @@ export async function POST(request: NextRequest) {
8384
return NextResponse.json({ error: "author_email is required" }, { status: 400 });
8485
}
8586

86-
const slug = slugify(name);
87-
87+
// Verify user is registered
8888
const sb = getSupabaseAdmin();
89+
const { data: profile } = await sb
90+
.from("user_profiles")
91+
.select("id, email, email_verified")
92+
.eq("email", author_email)
93+
.single();
94+
95+
if (!profile) {
96+
return NextResponse.json(
97+
{ error: "You must create an account at threatcrush.com/auth/signup before publishing modules." },
98+
{ status: 401 }
99+
);
100+
}
101+
102+
if (!profile.email_verified) {
103+
return NextResponse.json(
104+
{ error: "Please verify your email before publishing modules." },
105+
{ status: 403 }
106+
);
107+
}
108+
109+
const slug = slugify(name);
89110

90111
// Check if slug already exists
91112
const { data: existing } = await sb

0 commit comments

Comments
 (0)