Skip to content

Instantly share code, notes, and snippets.

@proton0210
Created April 11, 2024 10:35
Show Gist options
  • Save proton0210/1cc7bdbd8c29610de79c25d396989ee4 to your computer and use it in GitHub Desktop.
Save proton0210/1cc7bdbd8c29610de79c25d396989ee4 to your computer and use it in GitHub Desktop.
Webhooks and Actions
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
"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;
};
/* 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 });
}
}
//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