Skip to content

Commit db80bda

Browse files
authored
Merge pull request #193 from jphacks:ffffff
Ffffff
2 parents 59e6d39 + 4593771 commit db80bda

10 files changed

Lines changed: 312 additions & 124 deletions

File tree

backend/src/routes/modules/agora.routes.ts

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,6 @@ const tokenRequestSchema = z.object({
2121
role: z.enum(["user", "partner"]).describe("User role in the session"),
2222
});
2323

24-
/**
25-
* Token generation response schema
26-
*/
27-
const tokenResponseSchema = z.object({
28-
token: z.string().describe("Agora RTC token"),
29-
appId: z.string().describe("Agora App ID"),
30-
channelName: z.string().describe("Channel name"),
31-
uid: z.number().describe("Numeric user ID for Agora"),
32-
expiresIn: z.number().describe("Token expiration time in seconds"),
33-
});
34-
3524
/**
3625
* Generate Agora RTC token
3726
* POST /api/agora/token

backend/src/routes/modules/partners.routes.ts

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -657,4 +657,99 @@ partners.openapi(getPartnerFeedbackRoute, async (c) => {
657657
}
658658
});
659659

660+
// List waiting sessions for partners
661+
const listWaitingSessionsRoute = createRoute({
662+
method: "get",
663+
path: "/sessions/waiting",
664+
tags: ["Partners"],
665+
request: {
666+
query: z.object({
667+
partnerId: z.string().optional(),
668+
}),
669+
},
670+
responses: {
671+
200: {
672+
description: "Waiting sessions list",
673+
content: {
674+
"application/json": {
675+
schema: z.object({
676+
sessions: z.array(
677+
z.object({
678+
id: z.string(),
679+
status: z.string(),
680+
createdAt: z.string(),
681+
user: z
682+
.object({
683+
id: z.string(),
684+
name: z.string().nullable(),
685+
image: z.string().nullable(),
686+
})
687+
.nullable(),
688+
partner: z.object({
689+
id: z.string(),
690+
name: z.string().nullable(),
691+
}),
692+
})
693+
),
694+
}),
695+
},
696+
},
697+
},
698+
500: {
699+
description: "Failed to fetch waiting sessions",
700+
content: {
701+
"application/json": {
702+
schema: z.object({
703+
error: z.string(),
704+
}),
705+
},
706+
},
707+
},
708+
},
709+
});
710+
711+
partners.openapi(listWaitingSessionsRoute, async (c) => {
712+
try {
713+
const partnerId = c.req.query("partnerId");
714+
715+
const sessions = await prisma.humanPartnerSession.findMany({
716+
where: {
717+
status: "waiting",
718+
...(partnerId ? { partnerId } : {}),
719+
},
720+
include: {
721+
user: {
722+
select: {
723+
id: true,
724+
name: true,
725+
image: true,
726+
},
727+
},
728+
partner: {
729+
select: {
730+
id: true,
731+
name: true,
732+
},
733+
},
734+
},
735+
orderBy: {
736+
createdAt: "asc",
737+
},
738+
});
739+
740+
return c.json({
741+
sessions: sessions.map((session) => ({
742+
id: session.id,
743+
status: session.status,
744+
createdAt: session.createdAt.toISOString(),
745+
user: session.user,
746+
partner: session.partner,
747+
})),
748+
});
749+
} catch (error) {
750+
console.error("Failed to fetch waiting sessions:", error);
751+
return c.json({ error: "Failed to fetch waiting sessions" }, 500);
752+
}
753+
});
754+
660755
export default partners;
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { NextResponse, type NextRequest } from "next/server";
2+
import { auth } from "@/lib/auth";
3+
import { prisma } from "@/lib/prisma";
4+
5+
export async function POST(req: NextRequest) {
6+
try {
7+
const session = await auth.api.getSession({
8+
headers: req.headers,
9+
});
10+
11+
if (!session?.user?.id) {
12+
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
13+
}
14+
15+
const role = (session.user as { role?: string }).role;
16+
if (role !== "partner") {
17+
return NextResponse.json(
18+
{ error: "Partner role required" },
19+
{ status: 403 },
20+
);
21+
}
22+
23+
const body = (await req.json().catch(() => null)) as {
24+
isAvailable?: boolean;
25+
} | null;
26+
27+
if (!body || typeof body.isAvailable !== "boolean") {
28+
return NextResponse.json(
29+
{ error: "Invalid payload" },
30+
{ status: 400 },
31+
);
32+
}
33+
34+
await prisma.user.update({
35+
where: { id: session.user.id },
36+
data: {
37+
isAvailable: body.isAvailable,
38+
},
39+
});
40+
41+
return NextResponse.json({ success: true });
42+
} catch (error) {
43+
console.error("Failed to update availability:", error);
44+
return NextResponse.json(
45+
{ error: "Failed to update availability" },
46+
{ status: 500 },
47+
);
48+
}
49+
}

frontend/src/app/api/partner/sessions/waiting/route.ts

Lines changed: 0 additions & 52 deletions
This file was deleted.
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { type NextRequest, NextResponse } from "next/server";
2+
import { auth } from "@/lib/auth";
3+
import { prisma } from "@/lib/prisma";
4+
5+
export async function GET(req: NextRequest) {
6+
try {
7+
const { searchParams } = new URL(req.url);
8+
const partnerIdQuery = searchParams.get("partnerId");
9+
10+
const session = await auth.api.getSession({
11+
headers: req.headers,
12+
});
13+
14+
const requesterId = session?.user?.id;
15+
const requesterRole = (session?.user as { role?: string } | undefined)?.role;
16+
17+
if (!partnerIdQuery && requesterRole === "partner" && requesterId) {
18+
searchParams.set("partnerId", requesterId);
19+
}
20+
21+
const partnerFilter = partnerIdQuery || undefined;
22+
23+
const waitingSessions = await prisma.humanPartnerSession.findMany({
24+
where: {
25+
status: "waiting",
26+
...(partnerFilter ? { partnerId: partnerFilter } : {}),
27+
},
28+
include: {
29+
user: {
30+
select: {
31+
id: true,
32+
name: true,
33+
image: true,
34+
},
35+
},
36+
partner: {
37+
select: {
38+
id: true,
39+
name: true,
40+
},
41+
},
42+
},
43+
orderBy: {
44+
createdAt: "asc",
45+
},
46+
});
47+
48+
return NextResponse.json({
49+
sessions: waitingSessions.map((session) => ({
50+
id: session.id,
51+
status: session.status,
52+
createdAt: session.createdAt.toISOString(),
53+
user: session.user,
54+
partner: session.partner,
55+
})),
56+
});
57+
} catch (error) {
58+
console.error("Failed to fetch waiting sessions:", error);
59+
return NextResponse.json(
60+
{ error: "Failed to fetch waiting sessions" },
61+
{ status: 500 },
62+
);
63+
}
64+
}

frontend/src/app/login/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"use client";
22

3-
import { Heart, Loader2, Lock, Mail } from "lucide-react";
3+
import { Loader2, Lock, Mail } from "lucide-react";
44
import Link from "next/link";
55
import { useRouter } from "next/navigation";
66
import { useId, useState } from "react";

frontend/src/app/partner/waiting/page.tsx

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ type WaitingSession = {
2929
name: string | null;
3030
image: string | null;
3131
} | null;
32+
partner?: {
33+
id: string;
34+
name: string | null;
35+
} | null;
3236
};
3337

3438
const POLL_INTERVAL_MS = 15_000;
@@ -37,7 +41,6 @@ export default function PartnerWaitingPage() {
3741
const router = useRouter();
3842
const sessionState = useSession();
3943
const { data: sessionData, isPending } = sessionState;
40-
const isPartner = (sessionData?.user as { role?: string } | undefined)?.role === "partner";
4144

4245
const [sessions, setSessions] = useState<WaitingSession[]>([]);
4346
const [isLoading, setIsLoading] = useState(true);
@@ -48,29 +51,34 @@ export default function PartnerWaitingPage() {
4851
const fetchWaitingSessions = useCallback(async () => {
4952
setError(null);
5053
try {
51-
const response = await fetch("/api/partner/sessions/waiting", {
52-
cache: "no-store",
53-
});
54+
const params = new URLSearchParams();
55+
if (sessionData?.user?.id) {
56+
params.set("partnerId", sessionData.user.id);
57+
}
58+
const response = await fetch(
59+
`/api/partners/sessions/waiting${params.size > 0 ? `?${params.toString()}` : ""}`,
60+
{
61+
cache: "no-store",
62+
},
63+
);
5464

5565
if (!response.ok) {
56-
if (response.status === 401) {
57-
setError("パートナーログインが必要です。");
58-
} else if (response.status === 403) {
59-
setError("パートナー権限が必要です。");
60-
} else {
61-
const payload = await response.json().catch(() => ({}));
62-
const message =
63-
typeof payload?.error === "string"
64-
? payload.error
65-
: "待機中セッションの取得に失敗しました。";
66-
setError(message);
67-
}
66+
const payload = await response.json().catch(() => ({}));
67+
const message =
68+
typeof payload?.error === "string"
69+
? payload.error
70+
: "待機中セッションの取得に失敗しました。";
71+
setError(message);
6872
setSessions([]);
6973
return;
7074
}
7175

7276
const data = (await response.json()) as { sessions: WaitingSession[] };
73-
setSessions(data.sessions ?? []);
77+
setSessions(
78+
data.sessions?.filter((session) =>
79+
sessionData?.user?.id ? session.partner?.id === sessionData.user.id : true,
80+
) ?? [],
81+
);
7482
setLastUpdated(new Date());
7583
} catch (err) {
7684
console.error("Failed to load waiting sessions:", err);
@@ -79,7 +87,7 @@ export default function PartnerWaitingPage() {
7987
} finally {
8088
setIsLoading(false);
8189
}
82-
}, []);
90+
}, [sessionData?.user?.id]);
8391

8492
useEffect(() => {
8593
void fetchWaitingSessions();

0 commit comments

Comments
 (0)