Skip to content

Commit

Permalink
Added Auth
Browse files Browse the repository at this point in the history
  • Loading branch information
nischal-shetty2 committed Sep 25, 2024
1 parent b8587e7 commit 31110fc
Show file tree
Hide file tree
Showing 12 changed files with 154 additions and 113 deletions.
2 changes: 1 addition & 1 deletion .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npm run format:fix
npm run lint:fix
npm run format:fix

git add .

Expand Down
124 changes: 74 additions & 50 deletions src/actions/bounty/adminActions.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
'use server';
import prisma from '@/db';
import { adminApprovalSchema } from './schema';
import { AdminApprovalData } from './types';
import { getServerSession } from 'next-auth';
import { authOptions } from '@/lib/auth';
import { ROLES } from '../types';

export type Bounty = {
id: string;
Expand All @@ -19,67 +20,90 @@ export type Bounty = {
};

type BountyResponse = {
bounties: Bounty[];
totalINRBounties: number;
totalSOLBounties: number;
bounties?: Bounty[];
totalINRBounties?: number;
totalSOLBounties?: number;
error?: string;
};

export async function approveBounty(data: AdminApprovalData) {
const validatedData = adminApprovalSchema.parse(data);

const updatedBounty = await prisma.bountySubmission.update({
where: { id: validatedData.bountyId },
data: { status: validatedData.status },
});
export async function getBounties(): Promise<BountyResponse> {
const session = await getServerSession(authOptions);

return updatedBounty;
}
if (!session || !session.user || session.user.role !== ROLES.ADMIN) {
return { error: 'Unauthorized or insufficient permissions' };
}

export async function getBounties(): Promise<BountyResponse> {
const bounties = await prisma.bountySubmission.findMany({
select: {
id: true,
prLink: true,
paymentMethod: true,
status: true,
createdAt: true,
updatedAt: true,
amount: true,
userId: true,
user: {
select: {
id: true,
name: true,
try {
const bounties = await prisma.bountySubmission.findMany({
select: {
id: true,
prLink: true,
paymentMethod: true,
status: true,
createdAt: true,
updatedAt: true,
amount: true,
userId: true,
user: {
select: {
id: true,
name: true,
},
},
},
},
});
let totalINRBounties = 0;
let totalSOLBounties = 0;
});

bounties.forEach((bounty) => {
if (bounty.paymentMethod.includes('@')) {
totalINRBounties += bounty.amount || 0;
} else {
totalSOLBounties += bounty.amount || 0;
}
});
let totalINRBounties = 0;
let totalSOLBounties = 0;

return { bounties, totalINRBounties, totalSOLBounties };
bounties.forEach((bounty) => {
if (bounty.paymentMethod.includes('@')) {
totalINRBounties += bounty.amount || 0;
} else {
totalSOLBounties += bounty.amount || 0;
}
});

return { bounties, totalINRBounties, totalSOLBounties };
} catch (e) {
return { error: 'An error occurred while approving the bounty.' };
}
}

export async function deleteBounty(bountyId: string) {
const deleteBounty = await prisma.bountySubmission.delete({
where: { id: bountyId },
});
return { success: !!deleteBounty };
const session = await getServerSession(authOptions);

if (!session || !session.user || session.user.role !== ROLES.ADMIN) {
return { error: 'Unauthorized or insufficient permissions' };
}
try {
const deleteBounty = await prisma.bountySubmission.delete({
where: { id: bountyId },
});
return { success: !!deleteBounty };
} catch (e) {
return { error: 'An error occurred while approving the bounty.' };
}
}

export async function confirmBounty(bountyId: string, amount: number) {
const updatedBounty = await prisma.bountySubmission.update({
where: { id: bountyId },
data: { status: 'confirmed', amount },
});
const session = await getServerSession(authOptions);

if (!session || !session.user || session.user.role !== ROLES.ADMIN) {
return { error: 'Unauthorized or insufficient permissions' };
}

return { success: !!updatedBounty };
try {
const updatedBounty = await prisma.bountySubmission.update({
where: { id: bountyId },
data: { status: 'confirmed', amount },
});

if (updatedBounty) {
return { success: true };
}
return { error: 'Failed to update bounty.' };
} catch (e) {
return { error: 'An error occurred while approving the bounty.' };
}
}
2 changes: 1 addition & 1 deletion src/actions/bounty/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ export const bountySubmissionSchema = z.object({
});

export const adminApprovalSchema = z.object({
bountyId: z.string().uuid({ message: 'Invalid Bounty ID' }),
bountyId: z.string(),
status: z.enum(['approved', 'rejected']),
});
1 change: 0 additions & 1 deletion src/actions/bounty/types.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
export interface BountySubmissionData {
prLink: string;
paymentMethod: string;
userId: string;
}

export interface AdminApprovalData {
Expand Down
35 changes: 23 additions & 12 deletions src/actions/bounty/userActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,36 +4,42 @@ import { bountySubmissionSchema } from './schema';
import { BountySubmissionData } from './types';
import { getServerSession } from 'next-auth';
import { authOptions } from '@/lib/auth';
import { createSafeAction } from '@/lib/create-safe-action';
import { Prisma } from '@prisma/client';

export async function submitBounty(data: BountySubmissionData) {
async function submitBountyHandler(data: BountySubmissionData) {
try {
const validatedData = bountySubmissionSchema.parse(data);
const session = await getServerSession(authOptions);

if (!session?.user.id) {
throw new Error('User not authenticated');
if (!session || !session.user) {
return { error: 'User not authenticated' };
}

const bountySubmission = await prisma.bountySubmission.create({
data: {
prLink: validatedData.prLink,
paymentMethod: validatedData.paymentMethod,
prLink: data.prLink,
paymentMethod: data.paymentMethod,
userId: session.user.id,
},
});

return bountySubmission;
} catch (error) {
console.error('Error submitting bounty:', error);
throw error;
return { data: bountySubmission };
} catch (error: any) {
if (error instanceof Prisma.PrismaClientKnownRequestError) {
if (error.code === 'P2002') {
return {
error: 'PR already submitted. Try a different one.',
};
}
}
return { error: 'Failed to submit bounty!' };
}
}

export async function getUserBounties() {
try {
const session = await getServerSession(authOptions);

if (!session?.user.id) {
if (!session || !session.user) {
throw new Error('User not authenticated');
}

Expand All @@ -48,3 +54,8 @@ export async function getUserBounties() {
throw error;
}
}

export const submitBounty = createSafeAction(
bountySubmissionSchema,
submitBountyHandler,
);
8 changes: 4 additions & 4 deletions src/actions/payoutMethods/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ import { z } from 'zod';
export const payoutMethodSchema = z.object({
upiId: z
.string()
.refine((value) => (/^[0-9A-Za-z._-]{2,256}@[A-Za-z]{2,64}$/).test(value), {
.refine((value) => /^[0-9A-Za-z._-]{2,256}@[A-Za-z]{2,64}$/.test(value), {
message: 'Enter a valid UPI address',
})
.optional(),
solanaAddress: z
.string()
.refine((value) => (/^[A-Za-z0-9]{44}$/).test(value), {
.refine((value) => /^[A-Za-z0-9]{44}$/.test(value), {
message: 'Enter a valid Solana address',
})
.optional(),
Expand All @@ -18,13 +18,13 @@ export const payoutMethodSchema = z.object({
export const upiIdInsertSchema = z.object({
upiId: z
.string()
.refine((value) => (/^[0-9A_Za-z._-]{2,256}@[A_Za-z]{2,64}$/).test(value), {
.refine((value) => /^[0-9A_Za-z._-]{2,256}@[A_Za-z]{2,64}$/.test(value), {
message: 'Invalid UPI address',
}),
});

export const solanaAddressInsertSchema = z.object({
solanaAddress: z.string().refine((value) => (/^[A-Za-z0-9]{44}$/).test(value), {
solanaAddress: z.string().refine((value) => /^[A-Za-z0-9]{44}$/.test(value), {
message: 'Invalid Solana address',
}),
});
Expand Down
21 changes: 12 additions & 9 deletions src/app/Bounty/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export default function Page() {
};

const fetchUserBounties = async () => {
setIsLoading(true);
try {
const userBounties = await getUserBounties();

Expand All @@ -40,6 +41,8 @@ export default function Page() {
setBounties(sortedBounties);
} catch (error) {
toast.error('Failed to fetch bounties');
} finally {
setIsLoading(false);
}
};

Expand All @@ -48,11 +51,6 @@ export default function Page() {
fetchUserBounties();
}, []);

useEffect(() => {
fetchPayoutMethods();
fetchUserBounties();
}, [isBountyDialogOpen]);

const handleBountyDialogOpen = () => {
if (upiAddresses?.length || solanaAddresses?.length) {
setIsBountyDialogOpen(true);
Expand All @@ -61,14 +59,19 @@ export default function Page() {
}
};

const handleBountyDialogClose = () => setIsBountyDialogOpen(false);
const handleBountyDialogClose = () => {
setIsBountyDialogOpen(false);
fetchUserBounties();
};

return (
<>
<div className="h-max pb-4 transition-colors duration-500 md:p-8">
<div className="mb-6 flex flex-col items-start justify-center px-4 pt-3 sm:px-8">
<div className="text-3xl text-black transition-colors duration-500 dark:text-white">
<h1 className="text-black dark:text-white">Your Bounties</h1>
<div className="text-2xl text-black transition-colors duration-500 dark:text-white sm:text-3xl">
<h1 className="mt-20 text-black dark:text-white sm:mt-16">
Your Bounties
</h1>
</div>

<PaymentMethodsDropdown
Expand Down Expand Up @@ -112,7 +115,7 @@ export default function Page() {
href={bounty.prLink}
target="_blank"
rel="noopener noreferrer"
className="text-blue-500 underline"
className="break-all text-blue-500 underline"
>
{bounty.prLink}
</a>
Expand Down
Loading

0 comments on commit 31110fc

Please sign in to comment.