summaryrefslogtreecommitdiff
path: root/apps/web/app/api/share
diff options
context:
space:
mode:
authorFuwn <[email protected]>2026-02-08 07:07:59 -0800
committerFuwn <[email protected]>2026-02-08 07:07:59 -0800
commita33dbfa6a1cb1d34ce9a5286efdb25818ff7b6c1 (patch)
tree7d44bdcb94cc1b69fbc201a4757f27f3751c5adb /apps/web/app/api/share
parentchore: gate Vercel analytics and speed insights to production only (diff)
downloadasa.news-a33dbfa6a1cb1d34ce9a5286efdb25818ff7b6c1.tar.xz
asa.news-a33dbfa6a1cb1d34ce9a5286efdb25818ff7b6c1.zip
feat: share with highlighted excerpt and fix auth redirect URLs
Add "share" button to text selection toolbar so users can share an entry with a highlighted passage visible to visitors. The public share page renders the highlight and scrolls to it on load. Also fix magic link and password reset redirects to use NEXT_PUBLIC_APP_URL instead of window.location.origin so emails link to the production domain.
Diffstat (limited to 'apps/web/app/api/share')
-rw-r--r--apps/web/app/api/share/route.ts39
1 files changed, 38 insertions, 1 deletions
diff --git a/apps/web/app/api/share/route.ts b/apps/web/app/api/share/route.ts
index b6a9c88..58d0c2a 100644
--- a/apps/web/app/api/share/route.ts
+++ b/apps/web/app/api/share/route.ts
@@ -64,6 +64,26 @@ export async function POST(request: Request) {
note = rawNote.trim() || null
}
+ let highlightedText: string | null = null
+ let highlightTextOffset: number | null = null
+ let highlightTextLength: number | null = null
+ let highlightTextPrefix: string = ""
+ let highlightTextSuffix: string = ""
+
+ if (body.highlightedText && typeof body.highlightedText === "string") {
+ if (typeof body.highlightTextOffset !== "number" || typeof body.highlightTextLength !== "number") {
+ return NextResponse.json(
+ { error: "highlightTextOffset and highlightTextLength are required with highlightedText" },
+ { status: 400 }
+ )
+ }
+ highlightedText = body.highlightedText
+ highlightTextOffset = body.highlightTextOffset
+ highlightTextLength = body.highlightTextLength
+ highlightTextPrefix = typeof body.highlightTextPrefix === "string" ? body.highlightTextPrefix : ""
+ highlightTextSuffix = typeof body.highlightTextSuffix === "string" ? body.highlightTextSuffix : ""
+ }
+
const { data: entryAccess } = await supabaseClient
.from("entries")
.select("id, feed_id")
@@ -95,12 +115,24 @@ export async function POST(request: Request) {
const { data: existingShare } = await supabaseClient
.from("shared_entries")
- .select("share_token")
+ .select("share_token, id")
.eq("entry_id", entryIdentifier)
.eq("user_id", user.id)
.maybeSingle()
if (existingShare) {
+ if (highlightedText !== null) {
+ await supabaseClient
+ .from("shared_entries")
+ .update({
+ highlighted_text: highlightedText,
+ highlight_text_offset: highlightTextOffset,
+ highlight_text_length: highlightTextLength,
+ highlight_text_prefix: highlightTextPrefix,
+ highlight_text_suffix: highlightTextSuffix,
+ })
+ .eq("id", existingShare.id)
+ }
const shareUrl = `${origin}/shared/${existingShare.share_token}`
return NextResponse.json({
shareToken: existingShare.share_token,
@@ -116,6 +148,11 @@ export async function POST(request: Request) {
share_token: shareToken,
expires_at: expiresAt,
note,
+ highlighted_text: highlightedText,
+ highlight_text_offset: highlightTextOffset,
+ highlight_text_length: highlightTextLength,
+ highlight_text_prefix: highlightTextPrefix,
+ highlight_text_suffix: highlightTextSuffix,
})
if (error) {