Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/app/api/subscriptions/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export async function GET(request: NextRequest) {
.from("subscriptions")
.select("*")
.eq("user_id", auth.user.id)
.single();
.maybeSingle();

if (error && error.code !== "PGRST116") {
return NextResponse.json({ error: error.message }, { status: 400 });
Expand Down Expand Up @@ -54,7 +54,7 @@ export async function DELETE(request: NextRequest) {
.from("subscriptions")
.select("stripe_subscription_id, status")
.eq("user_id", auth.user.id)
.single();
.maybeSingle();

if (error || !subscription) {
return NextResponse.json(
Expand Down Expand Up @@ -106,7 +106,7 @@ export async function PUT(request: NextRequest) {
.from("subscriptions")
.select("stripe_subscription_id, cancel_at_period_end")
.eq("user_id", auth.user.id)
.single();
.maybeSingle();

if (error || !subscription) {
return NextResponse.json(
Expand Down
12 changes: 6 additions & 6 deletions src/app/api/testimonials/route.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { NextRequest, NextResponse } from "next/server";

Check failure on line 1 in src/app/api/testimonials/route.ts

View workflow job for this annotation

GitHub Actions / build

File appears to be binary.
import { getAuthContext } from "@/lib/auth/get-user";
import { createServiceClient } from "@/lib/supabase/service";
import { sendEmail } from "@/lib/email";
Expand Down Expand Up @@ -122,7 +122,7 @@
.from("gigs")
.select("poster_id, title")
.eq("id", gig_id)
.single();
.maybeSingle();

if (gigError || !gig) {
return NextResponse.json(
Expand All @@ -148,7 +148,7 @@
);
}
} else {
// Someone else leaving a testimonial on this gig â€notify the poster
// Someone else leaving a testimonial on this gig �notify the poster
notifyUserId = gig.poster_id;
targetLabel = `your gig "${gig.title}"`;
}
Expand Down Expand Up @@ -183,7 +183,7 @@
...(autoApprove ? { status: "approved" } : {}),
})
.select()
.single();
.maybeSingle();

if (error) {
if (error.code === "23505") {
Expand All @@ -202,10 +202,10 @@
.from("profiles")
.select("full_name, username")
.eq("id", user.id)
.single();
.maybeSingle();

const authorName = authorProfile?.full_name || authorProfile?.username || "Someone";
const stars = "★".repeat(rating) + "☆".repeat(5 - rating);
const stars = "�.repeat(rating) + "�.repeat(5 - rating);

Check failure on line 208 in src/app/api/testimonials/route.ts

View workflow job for this annotation

GitHub Actions / build

',' expected.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Corrupted emoji characters in stars variable

The and characters have been replaced with garbled/replacement bytes (?). As written, the stars variable will contain corrupted characters repeated rating times rather than actual star glyphs. This string appears in the HTML email body (line 230) and the email subject (line 227), so every testimonial notification email will show garbage characters instead of a star rating like ★★★☆☆.


// In-app notification
await serviceClient.from("notifications").insert({
Expand All @@ -224,7 +224,7 @@
const baseUrl = process.env.NEXT_PUBLIC_APP_URL || "https://ugig.net";
await sendEmail({
to: ownerEmail,
subject: `${authorName} left a ${rating}-star testimonial on ${targetLabel} â€ugig.net`,
subject: `${authorName} left a ${rating}-star testimonial on ${targetLabel} �ugig.net`,
html: `
<div style="font-family: sans-serif; max-width: 500px;">
<h2>New Testimonial ${stars}</h2>
Expand Down Expand Up @@ -257,3 +257,3 @@
);
}
}
8 changes: 4 additions & 4 deletions src/app/api/verification/request/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
} from "@/lib/rate-limit";
import { getUserDid, onVerificationRequested } from "@/lib/reputation-hooks";

// POST /api/verification/request â€submit a verification request with evidence
// POST /api/verification/request �submit a verification request with evidence
export async function POST(request: NextRequest) {
try {
const auth = await getAuthContext(request);
Expand Down Expand Up @@ -40,7 +40,7 @@ export async function POST(request: NextRequest) {
.from("profiles")
.select("verified")
.eq("id", user.id)
.single();
.maybeSingle();

if (profile?.verified) {
return NextResponse.json(
Expand All @@ -55,7 +55,7 @@ export async function POST(request: NextRequest) {
.select("id, status")
.eq("user_id", user.id)
.eq("status", "pending")
.single();
.maybeSingle();

if (existingRequest) {
return NextResponse.json(
Expand All @@ -73,7 +73,7 @@ export async function POST(request: NextRequest) {
status: "pending",
})
.select()
.single();
.maybeSingle();

if (error) {
return NextResponse.json({ error: error.message }, { status: 400 });
Expand Down
8 changes: 4 additions & 4 deletions src/app/api/video-calls/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ export async function POST(request: NextRequest) {
.from("profiles")
.select("id, username, full_name")
.eq("id", participant_id)
.single();
.maybeSingle();

if (!participant) {
return NextResponse.json(
Expand Down Expand Up @@ -147,7 +147,7 @@ export async function POST(request: NextRequest) {
scheduled_at,
})
.select()
.single();
.maybeSingle();

if (createError) {
return NextResponse.json({ error: createError.message }, { status: 400 });
Expand Down Expand Up @@ -180,7 +180,7 @@ export async function POST(request: NextRequest) {
.from("profiles")
.select("full_name, username")
.eq("id", user.id)
.single();
.maybeSingle();

const initiatorName = initiatorProfile?.full_name || initiatorProfile?.username || "Someone";
const participantName = participant.full_name || participant.username || "there";
Expand All @@ -192,7 +192,7 @@ export async function POST(request: NextRequest) {
.from("gigs")
.select("title")
.eq("id", gig_id)
.single();
.maybeSingle();
gigTitle = gig?.title || null;
}

Expand Down
18 changes: 9 additions & 9 deletions src/app/api/wallet/zap/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
return NextResponse.json({ error: "No Lightning wallet found" }, { status: 400 });
}

// Pre-check sender's balance (informational only â€authoritative check happens at transfer time)
// Pre-check sender's balance (informational only �authoritative check happens at transfer time)
const senderBalance = await getLnBalance(senderWallet.invoice_key);
if (senderBalance < amount_sats) {
return NextResponse.json({ error: "Insufficient balance", balance_sats: senderBalance }, { status: 400 });
Expand All @@ -70,7 +70,7 @@
const fee_sats = Math.floor(amount_sats * PLATFORM_FEE_RATE);
const recipient_amount = amount_sats - fee_sats;

// Transfer recipient's share: sender â†recipient (instant internal transfer)
// Transfer recipient's share: sender �recipient (instant internal transfer)
// Handle insufficient-funds errors gracefully (TOCTOU race condition #78)
try {
await internalTransfer(
Expand Down Expand Up @@ -117,13 +117,13 @@
}, { status: 502 });
}

// Generic LNbits error â€expose the actual message
// Generic LNbits error �expose the actual message
return NextResponse.json({
error: err?.message || "Zap transfer failed",
}, { status: 502 });
}

// Transfer platform fee: sender â†platform wallet (retry up to 3 times)
// Transfer platform fee: sender �platform wallet (retry up to 3 times)
if (fee_sats > 0) {
let feeTransferred = false;
for (let attempt = 1; attempt <= 3; attempt++) {
Expand Down Expand Up @@ -190,7 +190,7 @@
note: note || null,
})
.select()
.single();
.maybeSingle();

const zapId = (zap as any)?.id;

Expand Down Expand Up @@ -229,30 +229,30 @@
.from("profiles")
.select("*")
.eq("id", recipient_id)
.single();
.maybeSingle();

const { data: senderProfile } = await admin
.from("profiles")
.select("username")
.eq("id", senderId)
.single();
.maybeSingle();

const senderName = senderProfile?.username || "Someone";

if ((recipientProfile as any)?.ln_address) {
await (admin.from("notifications") as any).insert({
user_id: recipient_id,
type: "zap_received",
title: "You received a zap! âš¡",
title: "You received a zap! âš?,

Check failure on line 246 in src/app/api/wallet/zap/route.ts

View workflow job for this annotation

GitHub Actions / build

Unterminated string literal.
body: `${senderName} zapped you ${recipient_amount.toLocaleString()} sats`,

Check failure on line 247 in src/app/api/wallet/zap/route.ts

View workflow job for this annotation

GitHub Actions / build

',' expected.
data: { zap_id: zapId, amount_sats: recipient_amount, target_type, target_id },
});
} else {
await (admin.from("notifications") as any).insert({
user_id: recipient_id,
type: "zap_received",
title: "You received a zap! âš¡",
title: "You received a zap! âš?,

Check failure on line 254 in src/app/api/wallet/zap/route.ts

View workflow job for this annotation

GitHub Actions / build

Unterminated string literal.
body: `${senderName} zapped you ${recipient_amount.toLocaleString()} sats. Add a Lightning Address to your profile to withdraw.`,

Check failure on line 255 in src/app/api/wallet/zap/route.ts

View workflow job for this annotation

GitHub Actions / build

',' expected.
Comment on lines +246 to 255

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Corrupted ⚡ emoji in notification titles

The lightning bolt emoji in both "You received a zap! ⚡" notification title strings has been replaced with garbled replacement bytes. Every zap notification sent to recipients (both the ln_address and non-ln_address branches) will display a corrupted character instead of the emoji. The title field is user-visible in the in-app notification UI.

data: { zap_id: zapId, amount_sats: recipient_amount, target_type, target_id, action_url: "/profile" },
});
}
Expand Down
Loading