第 6 篇 Scrum 冲刺博客
站立会议照片
![image-20231123130846178]()
昨日已完成的工作
- 展示用户搜索结果,包括搜索功能、用户卡片列表和分页功能
- 处理 Clerk Webhooks 的 Next.js API 路由函数。
- 用于接收来自 Clerk 的实时事件通知,并根据事件类型执行相应的业务逻辑。
今日计划完成的工作
- 在用户已登录且已完成初始化的情况下,展示一个包含标题和发表新主题表单的页面。
- 展示社区列表,包括搜索功能、社区卡片列表和分页功能
项目燃尽图
![image-20231124084726279]()
代码签入记录
![image-20231125103641477]()
适当的项目程序/模块的最新(运行)截图
/* eslint-disable camelcase */
// Resource: https://clerk.com/docs/users/sync-data-to-your-backend
// Above article shows why we need webhooks i.e., to sync data to our backend
// Resource: https://docs.svix.com/receiving/verifying-payloads/why
// It's a good practice to verify webhooks. Above article shows why we should do it
import { Webhook, WebhookRequiredHeaders } from "svix";
import { headers } from "next/headers";
import { IncomingHttpHeaders } from "http";
import { NextResponse } from "next/server";
import {
addMemberToCommunity,
createCommunity,
deleteCommunity,
removeUserFromCommunity,
updateCommunityInfo,
} from "@/lib/actions/community.actions";
// Resource: https://clerk.com/docs/integration/webhooks#supported-events
// Above document lists the supported events
type EventType =
| "organization.created"
| "organizationInvitation.created"
| "organizationMembership.created"
| "organizationMembership.deleted"
| "organization.updated"
| "organization.deleted";
type Event = {
data: Record<string, string | number | Record<string, string>[]>;
object: "event";
type: EventType;
};
export const POST = async (request: Request) => {
const payload = await request.json();
const header = headers();
const heads = {
"svix-id": header.get("svix-id"),
"svix-timestamp": header.get("svix-timestamp"),
"svix-signature": header.get("svix-signature"),
};
// Activitate Webhook in the Clerk Dashboard.
// After adding the endpoint, you'll see the secret on the right side.
const wh = new Webhook(process.env.NEXT_CLERK_WEBHOOK_SECRET || "");
let evnt: Event | null = null;
try {
evnt = wh.verify(
JSON.stringify(payload),
heads as IncomingHttpHeaders & WebhookRequiredHeaders
) as Event;
} catch (err) {
return NextResponse.json({ message: err }, { status: 400 });
}
const eventType: EventType = evnt?.type!;
// Listen organization creation event
if (eventType === "organization.created") {
// Resource: https://clerk.com/docs/reference/backend-api/tag/Organizations#operation/CreateOrganization
// Show what evnt?.data sends from above resource
const { id, name, slug, logo_url, image_url, created_by } =
evnt?.data ?? {};
try {
// @ts-ignore
await createCommunity(
// @ts-ignore
id,
name,
slug,
logo_url || image_url,
"org bio",
created_by
);
return NextResponse.json({ message: "User created" }, { status: 201 });
} catch (err) {
console.log(err);
return NextResponse.json(
{ message: "Internal Server Error" },
{ status: 500 }
);
}
}
// Listen organization invitation creation event.
// Just to show. You can avoid this or tell people that we can create a new mongoose action and
// add pending invites in the database.
if (eventType === "organizationInvitation.created") {
try {
// Resource: https://clerk.com/docs/reference/backend-api/tag/Organization-Invitations#operation/CreateOrganizationInvitation
console.log("Invitation created", evnt?.data);
return NextResponse.json(
{ message: "Invitation created" },
{ status: 201 }
);
} catch (err) {
console.log(err);
return NextResponse.json(
{ message: "Internal Server Error" },
{ status: 500 }
);
}
}
// Listen organization membership (member invite & accepted) creation
if (eventType === "organizationMembership.created") {
try {
// Resource: https://clerk.com/docs/reference/backend-api/tag/Organization-Memberships#operation/CreateOrganizationMembership
// Show what evnt?.data sends from above resource
const { organization, public_user_data } = evnt?.data;
console.log("created", evnt?.data);
// @ts-ignore
await addMemberToCommunity(organization.id, public_user_data.user_id);
return NextResponse.json(
{ message: "Invitation accepted" },
{ status: 201 }
);
} catch (err) {
console.log(err);
return NextResponse.json(
{ message: "Internal Server Error" },
{ status: 500 }
);
}
}
// Listen member deletion event
if (eventType === "organizationMembership.deleted") {
try {
// Resource: https://clerk.com/docs/reference/backend-api/tag/Organization-Memberships#operation/DeleteOrganizationMembership
// Show what evnt?.data sends from above resource
const { organization, public_user_data } = evnt?.data;
console.log("removed", evnt?.data);
// @ts-ignore
await removeUserFromCommunity(public_user_data.user_id, organization.id);
return NextResponse.json({ message: "Member removed" }, { status: 201 });
} catch (err) {
console.log(err);
return NextResponse.json(
{ message: "Internal Server Error" },
{ status: 500 }
);
}
}
// Listen organization updation event
if (eventType === "organization.updated") {
try {
// Resource: https://clerk.com/docs/reference/backend-api/tag/Organizations#operation/UpdateOrganization
// Show what evnt?.data sends from above resource
const { id, logo_url, name, slug } = evnt?.data;
console.log("updated", evnt?.data);
// @ts-ignore
await updateCommunityInfo(id, name, slug, logo_url);
return NextResponse.json({ message: "Member removed" }, { status: 201 });
} catch (err) {
console.log(err);
return NextResponse.json(
{ message: "Internal Server Error" },
{ status: 500 }
);
}
}
// Listen organization deletion event
if (eventType === "organization.deleted") {
try {
// Resource: https://clerk.com/docs/reference/backend-api/tag/Organizations#operation/DeleteOrganization
// Show what evnt?.data sends from above resource
const { id } = evnt?.data;
console.log("deleted", evnt?.data);
// @ts-ignore
await deleteCommunity(id);
return NextResponse.json(
{ message: "Organization deleted" },
{ status: 201 }
);
} catch (err) {
console.log(err);
return NextResponse.json(
{ message: "Internal Server Error" },
{ status: 500 }
);
}
}
};
每人每日总结
| 成员 |
总结 |
| 林程星 |
项目接近完成,继续加油 |
| 邓梓荣 |
协助实现社区功能 |
| 曾中港 |
协助实现社区功能 |
| 刘鸿杰 |
收获到程序优化的方法 |
| 冯威炀 |
每天PDCA,每天进步1% |
| 陈昊宇 |
编程水平还有待提升 |
| 刘苑佳 |
制定基本的软件测试计划 |