feat: restore wechat thread ui and group chat
This commit is contained in:
34
src/app/api/v1/projects/[projectId]/group-chat/route.ts
Normal file
34
src/app/api/v1/projects/[projectId]/group-chat/route.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { requireRequestSession } from "@/lib/boss-auth";
|
||||
import { createProjectGroupChat } from "@/lib/boss-data";
|
||||
|
||||
export async function POST(
|
||||
request: NextRequest,
|
||||
context: { params: Promise<{ projectId: string }> },
|
||||
) {
|
||||
const session = await requireRequestSession(request);
|
||||
if (!session) {
|
||||
return NextResponse.json({ ok: false, message: "UNAUTHORIZED" }, { status: 401 });
|
||||
}
|
||||
|
||||
const { projectId } = await context.params;
|
||||
const body = (await request.json()) as {
|
||||
memberProjectIds?: string[];
|
||||
};
|
||||
|
||||
try {
|
||||
const project = await createProjectGroupChat({
|
||||
sourceProjectId: projectId,
|
||||
memberProjectIds: Array.isArray(body.memberProjectIds)
|
||||
? body.memberProjectIds.filter((memberProjectId) => typeof memberProjectId === "string")
|
||||
: [],
|
||||
createdBy: session.account,
|
||||
});
|
||||
return NextResponse.json({ ok: true, project });
|
||||
} catch (error) {
|
||||
return NextResponse.json(
|
||||
{ ok: false, message: error instanceof Error ? error.message : "UNKNOWN_ERROR" },
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { requireRequestSession } from "@/lib/boss-auth";
|
||||
import { appendProjectMessage } from "@/lib/boss-data";
|
||||
import { appendProjectMessage, readState } from "@/lib/boss-data";
|
||||
import { replyToMasterAgentUserMessage } from "@/lib/boss-master-agent";
|
||||
|
||||
export async function POST(
|
||||
@@ -38,7 +38,24 @@ export async function POST(
|
||||
});
|
||||
}
|
||||
|
||||
return NextResponse.json({ ok: true, message, masterReply });
|
||||
const state = await readState();
|
||||
const project = state.projects.find((item) => item.id === projectId);
|
||||
const collaborationGate = project
|
||||
? {
|
||||
isGroup: project.isGroup,
|
||||
collaborationMode: project.collaborationMode,
|
||||
requiresMasterAgentApproval:
|
||||
project.isGroup && project.collaborationMode === "approval_required",
|
||||
approvalState: project.approvalState,
|
||||
}
|
||||
: {
|
||||
isGroup: false,
|
||||
collaborationMode: "development" as const,
|
||||
requiresMasterAgentApproval: false,
|
||||
approvalState: "not_required" as const,
|
||||
};
|
||||
|
||||
return NextResponse.json({ ok: true, message, masterReply, collaborationGate });
|
||||
} catch (error) {
|
||||
return NextResponse.json(
|
||||
{ ok: false, message: error instanceof Error ? error.message : "UNKNOWN_ERROR" },
|
||||
|
||||
102
src/app/api/v1/projects/[projectId]/participants/route.ts
Normal file
102
src/app/api/v1/projects/[projectId]/participants/route.ts
Normal file
@@ -0,0 +1,102 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { requireRequestSession } from "@/lib/boss-auth";
|
||||
import { readState } from "@/lib/boss-data";
|
||||
|
||||
type ConversationParticipant = {
|
||||
projectId: string;
|
||||
deviceId: string;
|
||||
threadId: string;
|
||||
threadDisplayName: string;
|
||||
folderName: string;
|
||||
avatar: string;
|
||||
isSourceProject: boolean;
|
||||
};
|
||||
|
||||
function getFallbackAvatar(label: string) {
|
||||
const trimmed = label.trim();
|
||||
if (!trimmed) return "A";
|
||||
return trimmed.slice(0, 1).toUpperCase();
|
||||
}
|
||||
|
||||
function buildParticipant(
|
||||
projectId: string,
|
||||
deviceId: string,
|
||||
threadId: string,
|
||||
threadDisplayName: string,
|
||||
folderName: string,
|
||||
avatar?: string,
|
||||
isSourceProject = false,
|
||||
): ConversationParticipant {
|
||||
return {
|
||||
projectId,
|
||||
deviceId,
|
||||
threadId,
|
||||
threadDisplayName,
|
||||
folderName,
|
||||
avatar: avatar?.trim() || getFallbackAvatar(threadDisplayName),
|
||||
isSourceProject,
|
||||
};
|
||||
}
|
||||
|
||||
export async function GET(
|
||||
request: NextRequest,
|
||||
context: { params: Promise<{ projectId: string }> },
|
||||
) {
|
||||
const session = await requireRequestSession(request);
|
||||
if (!session) {
|
||||
return NextResponse.json({ ok: false, message: "UNAUTHORIZED" }, { status: 401 });
|
||||
}
|
||||
|
||||
const { projectId } = await context.params;
|
||||
const state = await readState();
|
||||
const project = state.projects.find((item) => item.id === projectId);
|
||||
|
||||
if (!project) {
|
||||
return NextResponse.json({ ok: false, message: "PROJECT_NOT_FOUND" }, { status: 404 });
|
||||
}
|
||||
|
||||
const participants = project.isGroup
|
||||
? (project.groupMembers.length > 0
|
||||
? project.groupMembers.map((member) => {
|
||||
const device = state.devices.find((item) => item.id === member.deviceId);
|
||||
return buildParticipant(
|
||||
member.projectId,
|
||||
member.deviceId,
|
||||
member.threadId,
|
||||
member.threadDisplayName,
|
||||
member.folderName,
|
||||
device?.avatar,
|
||||
member.projectId === project.id,
|
||||
);
|
||||
})
|
||||
: [
|
||||
buildParticipant(
|
||||
project.id,
|
||||
project.deviceIds[0] ?? project.id,
|
||||
project.threadMeta.threadId,
|
||||
project.threadMeta.threadDisplayName,
|
||||
project.threadMeta.folderName,
|
||||
state.devices.find((item) => item.id === project.deviceIds[0])?.avatar,
|
||||
true,
|
||||
),
|
||||
])
|
||||
: [
|
||||
buildParticipant(
|
||||
project.id,
|
||||
project.deviceIds[0] ?? project.id,
|
||||
project.threadMeta.threadId,
|
||||
project.threadMeta.threadDisplayName,
|
||||
project.threadMeta.folderName,
|
||||
state.devices.find((item) => item.id === project.deviceIds[0])?.avatar,
|
||||
true,
|
||||
),
|
||||
];
|
||||
|
||||
return NextResponse.json({
|
||||
ok: true,
|
||||
projectId: project.id,
|
||||
isGroup: project.isGroup,
|
||||
threadMeta: project.threadMeta,
|
||||
participants,
|
||||
});
|
||||
}
|
||||
45
src/app/api/v1/projects/[projectId]/rename/route.ts
Normal file
45
src/app/api/v1/projects/[projectId]/rename/route.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { requireRequestSession } from "@/lib/boss-auth";
|
||||
import { renameGroupChat, renameProjectThread } from "@/lib/boss-data";
|
||||
|
||||
export async function POST(
|
||||
request: NextRequest,
|
||||
context: { params: Promise<{ projectId: string }> },
|
||||
) {
|
||||
const session = await requireRequestSession(request);
|
||||
if (!session) {
|
||||
return NextResponse.json({ ok: false, message: "UNAUTHORIZED" }, { status: 401 });
|
||||
}
|
||||
|
||||
const { projectId } = await context.params;
|
||||
const body = (await request.json()) as {
|
||||
mode?: "thread" | "group";
|
||||
name?: string;
|
||||
};
|
||||
|
||||
const name = body.name?.trim();
|
||||
if (!name) {
|
||||
return NextResponse.json({ ok: false, message: "EMPTY_NAME" }, { status: 400 });
|
||||
}
|
||||
|
||||
try {
|
||||
const project =
|
||||
body.mode === "group"
|
||||
? await renameGroupChat({
|
||||
projectId,
|
||||
name,
|
||||
requestedBy: session.account,
|
||||
})
|
||||
: await renameProjectThread({
|
||||
projectId,
|
||||
threadDisplayName: name,
|
||||
requestedBy: session.account,
|
||||
});
|
||||
return NextResponse.json({ ok: true, project });
|
||||
} catch (error) {
|
||||
return NextResponse.json(
|
||||
{ ok: false, message: error instanceof Error ? error.message : "UNKNOWN_ERROR" },
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user