mirror of
https://github.com/jjbrunton/Sauci.git
synced 2026-10-31 18:19:43 +00:00
Send push notifications when partner disconnects, resets progress, or deletes relationship. Update Matches screen with new styling and remove legacy premium elements. Remove message content preview from admin User Activity page.
174 lines
6.1 KiB
TypeScript
174 lines
6.1 KiB
TypeScript
import "jsr:@supabase/functions-js/edge-runtime.d.ts";
|
|
import { createClient } from "https://esm.sh/@supabase/supabase-js@2";
|
|
|
|
const corsHeaders = {
|
|
"Access-Control-Allow-Origin": "*",
|
|
"Access-Control-Allow-Headers": "authorization, x-client-info, apikey, content-type",
|
|
};
|
|
|
|
interface ExpoPushMessage {
|
|
to: string;
|
|
title: string;
|
|
body: string;
|
|
sound?: string;
|
|
data?: Record<string, unknown>;
|
|
}
|
|
|
|
async function sendExpoPushNotifications(messages: ExpoPushMessage[]) {
|
|
if (messages.length === 0) return;
|
|
|
|
const response = await fetch("https://exp.host/--/api/v2/push/send", {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
Accept: "application/json",
|
|
},
|
|
body: JSON.stringify(messages),
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const error = await response.text();
|
|
console.error("Expo push error:", error);
|
|
}
|
|
|
|
return response.json();
|
|
}
|
|
|
|
Deno.serve(async (req) => {
|
|
// Handle CORS preflight
|
|
if (req.method === "OPTIONS") {
|
|
return new Response("ok", { headers: corsHeaders });
|
|
}
|
|
|
|
if (req.method !== "DELETE") {
|
|
return new Response(
|
|
JSON.stringify({ error: "Method not allowed" }),
|
|
{ status: 405, headers: { ...corsHeaders, "Content-Type": "application/json" } }
|
|
);
|
|
}
|
|
|
|
try {
|
|
const supabase = createClient(
|
|
Deno.env.get("SUPABASE_URL")!,
|
|
Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!
|
|
);
|
|
|
|
// Get user from JWT
|
|
const authHeader = req.headers.get("Authorization");
|
|
if (!authHeader) {
|
|
return new Response(
|
|
JSON.stringify({ error: "Missing authorization header" }),
|
|
{ status: 401, headers: { ...corsHeaders, "Content-Type": "application/json" } }
|
|
);
|
|
}
|
|
|
|
const { data: { user }, error: authError } = await supabase.auth.getUser(
|
|
authHeader.replace("Bearer ", "")
|
|
);
|
|
|
|
if (authError || !user) {
|
|
console.error("Auth Error:", authError);
|
|
return new Response(
|
|
JSON.stringify({ error: `Invalid token: ${authError?.message || 'No user found'}` }),
|
|
{ status: 401, headers: { ...corsHeaders, "Content-Type": "application/json" } }
|
|
);
|
|
}
|
|
|
|
// Get user's profile and couple_id
|
|
const { data: profile, error: profileError } = await supabase
|
|
.from("profiles")
|
|
.select("couple_id")
|
|
.eq("id", user.id)
|
|
.single();
|
|
|
|
if (profileError || !profile?.couple_id) {
|
|
return new Response(
|
|
JSON.stringify({ error: "You are not in a relationship" }),
|
|
{ status: 400, headers: { ...corsHeaders, "Content-Type": "application/json" } }
|
|
);
|
|
}
|
|
|
|
const coupleId = profile.couple_id;
|
|
|
|
// Get partner's push token BEFORE clearing data
|
|
const { data: partnerProfile } = await supabase
|
|
.from("profiles")
|
|
.select("push_token")
|
|
.eq("couple_id", coupleId)
|
|
.neq("id", user.id)
|
|
.maybeSingle();
|
|
|
|
// Get all match IDs for this couple (needed for storage cleanup)
|
|
const { data: matches } = await supabase
|
|
.from("matches")
|
|
.select("id")
|
|
.eq("couple_id", coupleId);
|
|
|
|
const matchIds = matches?.map(m => m.id) || [];
|
|
|
|
// Delete all chat media from storage for each match
|
|
for (const matchId of matchIds) {
|
|
const { data: files } = await supabase.storage
|
|
.from("chat-media")
|
|
.list(matchId);
|
|
|
|
if (files && files.length > 0) {
|
|
const filePaths = files.map(f => `${matchId}/${f.name}`);
|
|
await supabase.storage
|
|
.from("chat-media")
|
|
.remove(filePaths);
|
|
}
|
|
}
|
|
|
|
// Clear couple_id from both partners' profiles first
|
|
const { error: updateError } = await supabase
|
|
.from("profiles")
|
|
.update({ couple_id: null })
|
|
.eq("couple_id", coupleId);
|
|
|
|
if (updateError) {
|
|
console.error("Failed to clear couple_id from profiles:", updateError);
|
|
return new Response(
|
|
JSON.stringify({ error: "Failed to clear relationship from profiles" }),
|
|
{ status: 500, headers: { ...corsHeaders, "Content-Type": "application/json" } }
|
|
);
|
|
}
|
|
|
|
// Delete the couple record (cascades to responses, matches, messages, couple_packs)
|
|
const { error: deleteError } = await supabase
|
|
.from("couples")
|
|
.delete()
|
|
.eq("id", coupleId);
|
|
|
|
if (deleteError) {
|
|
console.error("Failed to delete couple:", deleteError);
|
|
return new Response(
|
|
JSON.stringify({ error: "Failed to delete relationship data" }),
|
|
{ status: 500, headers: { ...corsHeaders, "Content-Type": "application/json" } }
|
|
);
|
|
}
|
|
|
|
// Notify partner about the deletion
|
|
if (partnerProfile?.push_token) {
|
|
await sendExpoPushNotifications([{
|
|
to: partnerProfile.push_token,
|
|
title: "Relationship Deleted",
|
|
body: "Your partner has deleted all shared data. You can start fresh with a new partner anytime.",
|
|
sound: "default",
|
|
data: { type: "relationship_deleted" },
|
|
}]);
|
|
}
|
|
|
|
return new Response(
|
|
JSON.stringify({ success: true, message: "Relationship data deleted successfully" }),
|
|
{ headers: { ...corsHeaders, "Content-Type": "application/json" } }
|
|
);
|
|
} catch (error) {
|
|
console.error("Error:", error);
|
|
return new Response(
|
|
JSON.stringify({ error: "Internal server error" }),
|
|
{ status: 500, headers: { ...corsHeaders, "Content-Type": "application/json" } }
|
|
);
|
|
}
|
|
});
|