an app to share curated trails sidetrail.app
1

Configure Feed

Select the types of activity you want to include in your feed.

1"use server"; 2 3import "server-only"; 4import { redirect } from "next/navigation"; 5import { getOAuthClient, getSession } from "./index"; 6 7function isSameOrigin(url: string): boolean { 8 try { 9 // Use a dummy base - we only care that the URL doesn't escape to a different origin 10 const base = "https://self"; 11 return new URL(url, base).origin === base; 12 } catch { 13 return false; 14 } 15} 16 17export async function login(formData: FormData) { 18 const rawHandle = formData.get("loginHint") as string; 19 let returnUrl = (formData.get("returnUrl") as string) || "/"; 20 if (!isSameOrigin(returnUrl)) { 21 returnUrl = "/"; 22 } 23 24 const handle = rawHandle?.trim().replace(/^@/, ""); 25 if (!handle) { 26 redirect("/login?error=" + encodeURIComponent("Please enter your handle")); 27 } 28 29 let authorizationUrl: string; 30 try { 31 const client = await getOAuthClient(); 32 authorizationUrl = ( 33 await client.authorize(handle, { 34 scope: "atproto transition:generic", 35 state: JSON.stringify({ returnUrl }), 36 }) 37 ).toString(); 38 } catch (error) { 39 console.error("OAuth authorize error:", error); 40 const message = error instanceof Error ? error.message : "Unknown error"; 41 redirect("/login?error=" + encodeURIComponent(`Login failed: ${message}`)); 42 } 43 redirect(authorizationUrl); 44} 45 46export async function logout(returnUrl: string = "/") { 47 const session = await getSession(); 48 49 if (session.did) { 50 try { 51 const client = await getOAuthClient(); 52 const oauthSession = await client.restore(session.did); 53 if (oauthSession) { 54 await oauthSession.signOut(); 55 } 56 } catch (err) { 57 console.error("Failed to sign out OAuth session:", err); 58 } 59 } 60 61 session.destroy(); 62 redirect(returnUrl); 63}