Created
April 11, 2024 10:35
-
-
Save proton0210/1cc7bdbd8c29610de79c25d396989ee4 to your computer and use it in GitHub Desktop.
Webhooks and Actions
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
NEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URL=/product | |
NEXT_PUBLIC_CLERK_AFTER_SIGN_UP_URL=/product | |
NEXT_CLERK_WEBHOOK_SECRET= | |
AWS_REGION= | |
AWS_ACCESS_KEY_ID= | |
AWS_SECRET_ACCESS_KEY= | |
AWS_S3_BUCKET_NAME= | |
STRIPE_SECRET_KEY=x | |
STRIPE_WEBHOOK_SECRET=x |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"use server"; | |
import { revalidatePath } from "next/cache"; | |
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3"; | |
import sharp from "sharp"; | |
const s3Client = new S3Client({ | |
region: process.env.AWS_REGION as string, | |
credentials: { | |
accessKeyId: process.env.AWS_ACCESS_KEY_ID as string, | |
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY as string, | |
}, | |
}); | |
async function uploadFileToS3(file: any, fileName: string) { | |
const fileBuffer = await sharp(file).jpeg().toBuffer(); | |
const params = { | |
Bucket: process.env.AWS_S3_BUCKET_NAME, | |
Key: `${fileName}`, | |
Body: fileBuffer, | |
ContentType: "image/*", | |
}; | |
const command = new PutObjectCommand(params); | |
console.log("Uploading file to S3:", fileName); | |
try { | |
const response = await s3Client.send(command); | |
console.log("File uploaded successfully:", response); | |
return fileName; | |
} catch (error: any) { | |
// Log the full error object for debugging purposes | |
console.error("Error uploading file to S3:", error); | |
// Construct a more descriptive error message | |
let errorMessage = `Error uploading file '${fileName}' to S3: ${error.message}`; | |
// Optionally, you can include additional context or suggestions | |
if (error.code === "AccessDeniedError") { | |
errorMessage += ". Please check your AWS credentials and permissions."; | |
} else if (error.code === "NoSuchBucket") { | |
errorMessage += ". Please ensure that the specified bucket exists."; | |
} | |
throw new Error(errorMessage); | |
} | |
} | |
async function uploadFile(prevState: any, formData: any, ClerkID: string) { | |
try { | |
const file = formData.get("file"); | |
if (file.size === 0) { | |
return { status: "error", message: "Please select a file." }; | |
} | |
const buffer = Buffer.from(await file.arrayBuffer()); | |
const fileName = `${ClerkID}_${file.name}`; // Prepend ClerkID to file.name | |
await uploadFileToS3(buffer, fileName); | |
revalidatePath("/product"); | |
return { | |
status: "success", | |
message: "File has been uploaded.Please check your Email", | |
}; | |
} catch (error) { | |
return { status: "error", message: "Failed to upload file." }; | |
} | |
} | |
export const uploadFileWrapper = async ( | |
state: { status: string; message: string }, | |
payload: any | |
) => { | |
const { formData, ClerkID } = payload; | |
const result = await uploadFile(state, formData, ClerkID); | |
return result; | |
}; | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* eslint-disable camelcase */ | |
// npm i svix | |
import { Webhook } from "svix"; | |
import { headers } from "next/headers"; | |
import { WebhookEvent } from "@clerk/nextjs/server"; | |
import { createUser } from "@/lib/actions/user.action"; | |
import { NextResponse } from "next/server"; | |
export async function POST(req: Request) { | |
// You can find this in the Clerk Dashboard -> Webhooks -> choose the webhook | |
// | |
const WEBHOOK_SECRET = process.env.NEXT_CLERK_WEBHOOK_SECRET; | |
if (!WEBHOOK_SECRET) { | |
throw new Error( | |
"Please add WEBHOOK_SECRET from Clerk Dashboard to .env or .env.local" | |
); | |
} | |
// Get the headers | |
const headerPayload = headers(); | |
const svix_id = headerPayload.get("svix-id"); | |
const svix_timestamp = headerPayload.get("svix-timestamp"); | |
const svix_signature = headerPayload.get("svix-signature"); | |
// If there are no headers, error out | |
if (!svix_id || !svix_timestamp || !svix_signature) { | |
return new Response("Error occured -- no svix headers", { | |
status: 400, | |
}); | |
} | |
// Get the body | |
const payload = await req.json(); | |
const body = JSON.stringify(payload); | |
// Create a new SVIX instance with your secret. | |
const wh = new Webhook(WEBHOOK_SECRET); | |
let evt: WebhookEvent; | |
// Verify the payload with the headers | |
try { | |
evt = wh.verify(body, { | |
"svix-id": svix_id, | |
"svix-timestamp": svix_timestamp, | |
"svix-signature": svix_signature, | |
}) as WebhookEvent; | |
} catch (err) { | |
console.error("Error verifying webhook:", err); | |
return new Response("Error occured", { | |
status: 400, | |
}); | |
} | |
const eventType = evt.type; | |
console.log({ eventType }); | |
// write this block by yourself | |
try { | |
// ... (existing code) | |
if (eventType === "user.created") { | |
const { | |
id, | |
email_addresses, | |
image_url, | |
username, | |
first_name, | |
last_name, | |
} = evt.data; | |
try { | |
console.log("Creating user..."); | |
const user = await createUser({ | |
clerkId: id, | |
name: `${first_name}${last_name ? ` ${last_name}` : ""}`, | |
username: username!, | |
email: email_addresses[0].email_address, | |
picture: image_url, | |
}); | |
return NextResponse.json({ message: "OK", user: user }); | |
} catch (err) { | |
console.error("Error creating user:", err); | |
return new Response("Error creating user", { status: 500 }); | |
} | |
} | |
return new Response("", { status: 201 }); | |
} catch (err) { | |
console.error("Error in API route:", err); | |
return new Response("Internal Server Error", { status: 500 }); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//lib/actions - user.action.ts | |
"use server"; | |
import { | |
DynamoDBClient, | |
GetItemCommand, | |
PutItemCommand, | |
} from "@aws-sdk/client-dynamodb"; | |
import { marshall, unmarshall } from "@aws-sdk/util-dynamodb"; | |
export const createUser = async ({ | |
clerkId, | |
name, | |
username, | |
email, | |
picture, | |
}: { | |
clerkId: string; | |
name: string; | |
username: string; | |
email: string; | |
picture: string; | |
}) => { | |
const ddbClient = new DynamoDBClient({ | |
region: process.env.AWS_REGION as string, | |
credentials: { | |
accessKeyId: process.env.AWS_ACCESS_KEY_ID as string, | |
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY as string, | |
}, | |
}); | |
const params = { | |
TableName: "Users", | |
Item: marshall({ | |
ClerkID: clerkId, | |
Name: name, | |
Username: username, | |
Email: email, | |
Picture: picture, | |
Credits: 2, | |
}), | |
}; | |
try { | |
await ddbClient.send(new PutItemCommand(params)); | |
} catch (err) { | |
throw err; | |
} | |
}; | |
export const getUserCredits = async (clerkId: string) => { | |
const ddbClient = new DynamoDBClient({ | |
region: process.env.AWS_REGION as string, | |
credentials: { | |
accessKeyId: process.env.AWS_ACCESS_KEY_ID as string, | |
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY as string, | |
}, | |
}); | |
const params = { | |
TableName: "Users", | |
Key: marshall({ ClerkID: clerkId }), | |
}; | |
try { | |
const { Item } = await ddbClient.send(new GetItemCommand(params)); | |
if (!Item) { | |
return null; | |
} | |
const result = unmarshall(Item); | |
console.log(result); | |
return result.Credits as number; | |
} catch (err) { | |
throw err; | |
} | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment