ba-cafe

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit b96f687633fb314a9fcc2e9f56426808689e9832
parent b07af1eb2bb373268c70db85050cb1f57bea3519
Author: Sunny <122193933+Sunny-JP@users.noreply.github.com>
Date:   Sat, 24 Jan 2026 21:42:23 +0900

Merge pull request #18 from Sunny-JP/v4-sunny-dev

debug api
Diffstat:
Msrc/app/api/tap/route.ts | 76++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------
Msrc/components/Settings.tsx | 33+++++++++++++++++++++++----------
2 files changed, 85 insertions(+), 24 deletions(-)

diff --git a/src/app/api/tap/route.ts b/src/app/api/tap/route.ts @@ -12,17 +12,31 @@ export async function POST(request: Request) { try { const { tapTime, onesignalId, ticket1Time, ticket2Time } = await request.json(); const authHeader = request.headers.get('Authorization'); + if (!authHeader) return NextResponse.json({ error: 'No token' }, { status: 401 }); + const supabase = createClient( process.env.NEXT_PUBLIC_SUPABASE_URL!, process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!, { global: { headers: { Authorization: authHeader } } } ); + const { data: { user }, error: authError } = await supabase.auth.getUser(); if (authError || !user) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); - await cleanupDevices(user.id); + + console.log(`[Debug] Start process for user: ${user.id}`); + + // 通知ボタンまたはタップ時に ID があればクリーンアップを実行 + // 現在のデバイスID (onesignalId) を渡して保護する + if (onesignalId) { + console.log(`[Cleanup] Triggered by ID: ${onesignalId}`); + await cleanupDevices(user.id, onesignalId); + } + + // DB更新処理 const { data: profile } = await supabase.from('profiles').select('tap_history').eq('id', user.id).single(); const newHistory = tapTime ? [...(profile?.tap_history || []), tapTime] : (profile?.tap_history || []); + const { error: upsertError } = await supabase.from('profiles').upsert({ id: user.id, updated_at: new Date().toISOString(), @@ -31,42 +45,75 @@ export async function POST(request: Request) { ticket1_time: ticket1Time ? new Date(ticket1Time).toISOString() : (ticket1Time === null ? null : undefined), ticket2_time: ticket2Time ? new Date(ticket2Time).toISOString() : (ticket2Time === null ? null : undefined), }); + if (upsertError) throw new Error(upsertError.message); + + // 通知スケジュール if (tapTime && shouldScheduleNotification(new Date(tapTime))) { await scheduleNotification(user.id, tapTime); } + return NextResponse.json({ success: true, history: newHistory }); + } catch (error: any) { - console.error("Critical API Error:", error); + console.error("[Error] Critical API Error:", error); return NextResponse.json({ error: error.message }, { status: 500 }); } } -async function cleanupDevices(userId: string) { +async function cleanupDevices(userId: string, currentId: string) { + console.log(`[Cleanup] Fetching subscriptions for ${userId}...`); try { const res = await fetch(`https://onesignal.com/api/v1/apps/${APP_ID}/users/by/external_id/${userId}`, { - headers: { "Authorization": `Basic ${API_KEY}` } + headers: { + "Authorization": `Basic ${API_KEY}`, + "Content-Type": "application/json" + } }); - if (!res.ok) return; + + if (!res.ok) { + console.error(`[Cleanup] Fetch failed: ${res.status}`); + return; + } + const data = await res.json(); - const pushSubs = (data.subscriptions || []) - .filter((s: any) => s.type === "Push") - .sort((a: any, b: any) => { - const timeA = new Date(a.last_active || a.created_at || 0).getTime(); - const timeB = new Date(b.last_active || b.created_at || 0).getTime(); - return timeB - timeA; - }); + const subscriptions = data.subscriptions || (data.identity && data.identity.subscriptions) || []; + + // Push通知端末を全て抽出 + const pushSubs = subscriptions.filter((s: any) => { + const typeStr = String(s.type); + return typeStr.includes("Push") || typeStr === "1"; + }); + + // 今操作しているデバイスをリストから除外して保護 + const otherDevices = pushSubs.filter((s: any) => s.id !== currentId); + + console.log(`[Cleanup] Keeping Current: ${currentId}. Others count: ${otherDevices.length}`); + + // 他のデバイスをアクティブ順(または作成順)にソート + otherDevices.sort((a: any, b: any) => { + const timeA = Number(a.last_active || a.created_at || 0); + const timeB = Number(b.last_active || b.created_at || 0); + return timeB - timeA; + }); + + // 全体で3台以上(自分 + 他2台以上)ある場合、他の中で最新の1台以外を削除 if (pushSubs.length > 2) { - const targets = pushSubs.slice(2); + const targets = otherDevices.slice(1); + console.log(`[Cleanup] Target for deletion: ${targets.length} old devices.`); + for (const sub of targets) { + console.log(`[Cleanup] Deleting sub_id: ${sub.id}`); await fetch(`https://onesignal.com/api/v1/apps/${APP_ID}/subscriptions/${sub.id}`, { method: "DELETE", headers: { "Authorization": `Basic ${API_KEY}` } }); } + } else { + console.log(`[Cleanup] Within limit (Total: ${pushSubs.length})`); } } catch (e) { - console.error("Cleanup failed:", e); + console.error("[Cleanup] Error:", e); } } @@ -74,6 +121,7 @@ async function scheduleNotification(userId: string, tapTime: string) { const sendAfter = new Date(tapTime); sendAfter.setHours(sendAfter.getHours() + 3); const msg = messages[Math.floor(Math.random() * messages.length)] || { title: "Cafe Timer", body: "カフェ業務の時間です" }; + await fetch("https://onesignal.com/api/v1/notifications", { method: "POST", headers: { diff --git a/src/components/Settings.tsx b/src/components/Settings.tsx @@ -71,16 +71,29 @@ const handleNotificationClick = async () => { return; } - const isOptedIn = OneSignal.User.PushSubscription.optedIn; - - if (isOptedIn) { - await OneSignal.User.PushSubscription.optOut(); - alert("通知をOFFにしました。"); - } else { - await OneSignal.Notifications.requestPermission(); - await OneSignal.User.PushSubscription.optIn(); - alert("通知をONにしました!"); - } + const isOptedIn = OneSignal.User.PushSubscription.optedIn; + const currentSubscriptionId = OneSignal.User.PushSubscription.id; // 現在のデバイスID取得 + + if (isOptedIn) { + await OneSignal.User.PushSubscription.optOut(); + alert("通知をOFFにしました。"); + } else { + await OneSignal.Notifications.requestPermission(); + await OneSignal.User.PushSubscription.optIn(); + alert("通知をONにしました!"); + } + + // 通知ボタンを押したタイミングで API を叩き、整理を実行させる + await fetch('/api/tap', { // 実際のパスに合わせてください + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${(await supabase.auth.getSession()).data.session?.access_token}` + }, + body: JSON.stringify({ + onesignalId: currentSubscriptionId // これをトリガーにする + }) + }); } catch (e: any) { console.error("Notification Setup Error:", e);