Add intensity-based preferences and harden E2EE payload verification

This commit is contained in:
jjbrunton committed 2026-01-07 19:53:09 +00:00
1 parent e23f4557bb
commit 0a919f06fc
20 files changed
+2033 -251

No files matched your search

@@ -67,14 +67,14 @@ Deno.serve(async (req) => {
return json({ error: "Missing encryption metadata" }, 400);
}
const adminPrivateKeyJwkStr = Deno.env.get("ADMIN_PRIVATE_KEY_JWK");
if (!adminPrivateKeyJwkStr) {
const adminPrivateKeyJwk = getAdminPrivateKey(keysMetadata.admin_key_id);
if (!adminPrivateKeyJwk) {
return json({ error: "Admin decryption key not configured" }, 500);
}
const rawAesKey = await unwrapWithAdminKey(
keysMetadata.admin_wrapped_key,
JSON.parse(adminPrivateKeyJwkStr)
adminPrivateKeyJwk
);
const aesKey = await crypto.subtle.importKey(
@@ -166,3 +166,28 @@ async function assertSuperAdmin(
throw new Error("Access denied");
}
}
function getAdminPrivateKey(keyId?: string | null): JsonWebKey | null {
const keysJson = Deno.env.get("ADMIN_KEYS_JSON");
if (keysJson && keyId) {
try {
const keys = JSON.parse(keysJson);
if (keys[keyId]) {
return keys[keyId];
}
} catch {
console.error("Failed to parse ADMIN_KEYS_JSON");
}
}
const singleKeyJson = Deno.env.get("ADMIN_PRIVATE_KEY_JWK");
if (singleKeyJson) {
try {
return JSON.parse(singleKeyJson);
} catch {
console.error("Failed to parse ADMIN_PRIVATE_KEY_JWK");
}
}
return null;
}
+36 -14
View File
@@ -177,20 +177,42 @@ Deno.serve(async (req) => {
);
}
// Update user's profile with couple_id
await supabase
.from("profiles")
.update({ couple_id: couple.id })
.eq("id", user.id);
return new Response(
JSON.stringify({
success: true,
couple_id: couple.id,
invite_code: couple.invite_code
}),
{ headers: { ...corsHeaders, "Content-Type": "application/json" } }
);
// Update user's profile with couple_id
await supabase
.from("profiles")
.update({ couple_id: couple.id })
.eq("id", user.id);
const { data: safePacks, error: safePacksError } = await supabase
.from("question_packs")
.select("id")
.lte("avg_intensity", 2)
.eq("is_public", true);
if (safePacksError) {
console.error("Failed to fetch safe packs:", safePacksError);
} else if (safePacks?.length) {
const { error: insertError } = await supabase
.from("couple_packs")
.insert(safePacks.map((pack: { id: string }) => ({
couple_id: couple.id,
pack_id: pack.id,
enabled: true,
})));
if (insertError) {
console.error("Failed to enable safe packs:", insertError);
}
}
return new Response(
JSON.stringify({
success: true,
couple_id: couple.id,
invite_code: couple.invite_code
}),
{ headers: { ...corsHeaders, "Content-Type": "application/json" } }
);
}
}
@@ -0,0 +1,88 @@
-- Add intensity levels for content balance
ALTER TABLE profiles
ADD COLUMN IF NOT EXISTS max_intensity INTEGER DEFAULT 2
CHECK (max_intensity >= 1 AND max_intensity <= 5);
UPDATE profiles
SET max_intensity = CASE
WHEN show_explicit_content IS TRUE THEN 5
ELSE 2
END
WHERE max_intensity IS NULL;
ALTER TABLE question_packs
ADD COLUMN IF NOT EXISTS min_intensity INTEGER,
ADD COLUMN IF NOT EXISTS max_intensity INTEGER,
ADD COLUMN IF NOT EXISTS avg_intensity NUMERIC(3,2);
CREATE OR REPLACE FUNCTION update_question_pack_intensity_stats(target_pack_id UUID)
RETURNS void
LANGUAGE plpgsql
SET search_path = 'public'
AS $$
BEGIN
UPDATE question_packs qp
SET min_intensity = stats.min_intensity,
max_intensity = stats.max_intensity,
avg_intensity = stats.avg_intensity
FROM (
SELECT pack_id,
MIN(intensity) AS min_intensity,
MAX(intensity) AS max_intensity,
ROUND(AVG(intensity)::numeric, 2) AS avg_intensity
FROM questions
WHERE pack_id = target_pack_id
GROUP BY pack_id
) stats
WHERE qp.id = stats.pack_id;
IF NOT FOUND THEN
UPDATE question_packs
SET min_intensity = NULL,
max_intensity = NULL,
avg_intensity = NULL
WHERE id = target_pack_id;
END IF;
END;
$$;
CREATE OR REPLACE FUNCTION handle_question_intensity_change()
RETURNS TRIGGER
LANGUAGE plpgsql
SET search_path = 'public'
AS $$
BEGIN
IF TG_OP = 'DELETE' THEN
PERFORM update_question_pack_intensity_stats(OLD.pack_id);
RETURN OLD;
END IF;
IF TG_OP = 'UPDATE' AND NEW.pack_id IS DISTINCT FROM OLD.pack_id THEN
PERFORM update_question_pack_intensity_stats(OLD.pack_id);
END IF;
PERFORM update_question_pack_intensity_stats(NEW.pack_id);
RETURN NEW;
END;
$$;
DROP TRIGGER IF EXISTS question_pack_intensity_stats ON questions;
CREATE TRIGGER question_pack_intensity_stats
AFTER INSERT OR UPDATE OR DELETE ON questions
FOR EACH ROW EXECUTE FUNCTION handle_question_intensity_change();
UPDATE question_packs qp
SET min_intensity = stats.min_intensity,
max_intensity = stats.max_intensity,
avg_intensity = stats.avg_intensity
FROM (
SELECT pack_id,
MIN(intensity) AS min_intensity,
MAX(intensity) AS max_intensity,
ROUND(AVG(intensity)::numeric, 2) AS avg_intensity
FROM questions
GROUP BY pack_id
) stats
WHERE qp.id = stats.pack_id;