diff --git a/apps/mobile/src/app/account/index.tsx b/apps/mobile/src/app/account/index.tsx index a673c73..a804cc9 100644 --- a/apps/mobile/src/app/account/index.tsx +++ b/apps/mobile/src/app/account/index.tsx @@ -30,6 +30,24 @@ interface RestaurantDetail { address: string | null; } +export interface TxtRecord { + name: string; + value: string; +} + +export interface DomainInstructions { + cname?: { type: string; host: string; target: string }; + ownershipTxt?: TxtRecord | null; + sslTxt?: TxtRecord[]; +} + +export interface CloudflareStatus { + hostnameStatus?: string; + sslStatus?: string; + ownershipTxt?: TxtRecord | null; + sslTxt?: TxtRecord[]; +} + interface DomainRow { id: string; hostname: string; @@ -37,6 +55,8 @@ interface DomainRow { status: "pending" | "verified" | "failed"; verified_at: string | null; created_at: string; + instructions?: DomainInstructions; + cloudflareStatus?: CloudflareStatus | null; } const THEME_OPTIONS = [ @@ -192,15 +212,20 @@ export default function AccountScreen() { setAddingDomain(true); Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium).catch(() => {}); try { - const res = await api.post<{ domain: DomainRow }>(`/restaurants/${active.restaurantId}/domains`, { - hostname: newDomainInput.trim(), - }); - setDomains((prev) => [res.domain, ...prev]); + const res = await api.post<{ domain: DomainRow; instructions?: DomainInstructions; cnameTarget?: string }>( + `/restaurants/${active.restaurantId}/domains`, + { hostname: newDomainInput.trim() }, + ); + const updatedDomain: DomainRow = { + ...res.domain, + instructions: res.instructions, + }; + setDomains((prev) => [updatedDomain, ...prev]); setNewDomainInput(""); Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success).catch(() => {}); Alert.alert( "Alan Adı Eklendi 🎉", - `Lütfen DNS sağlayıcınızda (GoDaddy, Cloudflare vb.) ${res.domain.hostname} için CNAME kaydını ${cnameTarget} adresine yönlendirin.`, + `Lütfen DNS sağlayıcınızda (GoDaddy, Cloudflare vb.) yönlendirme (CNAME) ve varsa SSL TXT doğrulama kayıtlarını ekleyin.`, ); } catch (err) { Alert.alert("Hata", err instanceof Error ? err.message : "Alan adı eklenemedi."); @@ -214,15 +239,19 @@ export default function AccountScreen() { setVerifyingDomainId(domainId); Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light).catch(() => {}); try { - const res = await api.post<{ verified: boolean; domain: DomainRow; message: string }>( + const res = await api.post<{ verified: boolean; domain: DomainRow; cloudflareStatus?: CloudflareStatus; message: string }>( `/restaurants/${active.restaurantId}/domains/${domainId}/verify`, ); + const updatedDomain: DomainRow = { + ...res.domain, + cloudflareStatus: res.cloudflareStatus, + }; if (res.verified) { - setDomains((prev) => prev.map((d) => (d.id === domainId ? res.domain : d))); + setDomains((prev) => prev.map((d) => (d.id === domainId ? updatedDomain : d))); Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success).catch(() => {}); Alert.alert("Tebrikler! 🎉", res.message); } else { - setDomains((prev) => prev.map((d) => (d.id === domainId ? res.domain : d))); + setDomains((prev) => prev.map((d) => (d.id === domainId ? updatedDomain : d))); Haptics.notificationAsync(Haptics.NotificationFeedbackType.Warning).catch(() => {}); Alert.alert("Doğrulanamadı", res.message); } @@ -673,25 +702,135 @@ export default function AccountScreen() { - {/* CNAME instructions if pending */} + {/* DNS & SSL Instructions if Pending */} {!isVerified ? ( - - DNS Kaydı Talimatı: + + 📌 DNS & SSL Doğrulama Talimatları - - Tür: CNAME | Hedef:{" "} - {cnameTarget} + + Domain sağlayıcınızın (GoDaddy, Natro, Cloudflare vb.) DNS paneline aşağıdaki kayıtları ekleyin: + + {/* 1. CNAME Record */} + + + + 1. CNAME Yönlendirme Kaydı (Zorunlu) + + { + Clipboard.setStringAsync(cnameTarget); + Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success).catch(() => {}); + Alert.alert("Kopyalandı! 📋", `CNAME Hedefi (${cnameTarget}) panoya kopyalandı.`); + }} + style={{ backgroundColor: "#F59E0B20", paddingHorizontal: 8, paddingVertical: 2, borderRadius: 6 }} + > + Kopyala + + + + Tür: CNAME + + + Host: {dom.hostname} + + + Hedef: {cnameTarget} + + + + {/* 2. TXT Verification Records (If returned by Cloudflare for SSL) */} + {(() => { + const txts: TxtRecord[] = []; + if (dom.instructions?.ownershipTxt) txts.push(dom.instructions.ownershipTxt); + if (dom.instructions?.sslTxt) txts.push(...dom.instructions.sslTxt); + if (dom.cloudflareStatus?.ownershipTxt) txts.push(dom.cloudflareStatus.ownershipTxt); + if (dom.cloudflareStatus?.sslTxt) txts.push(...dom.cloudflareStatus.sslTxt); + + const uniqueTxts = txts.filter( + (txt, index, self) => + index === self.findIndex((t) => t.name === txt.name && t.value === txt.value), + ); + + if (uniqueTxts.length === 0) return null; + + return ( + + + 2. SSL Sertifikası TXT Doğrulama Kaydı (Gerekliyse): + + {uniqueTxts.map((txt, idx) => ( + + + TXT Kaydı #{idx + 1} (SSL Doğrulama) + + + + + İsim (Name): {txt.name} + + { + Clipboard.setStringAsync(txt.name); + Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success).catch(() => {}); + Alert.alert("Kopyalandı! 📋", `TXT İsmi (${txt.name}) panoya kopyalandı.`); + }} + style={{ backgroundColor: "#0284C720", paddingHorizontal: 6, paddingVertical: 3, borderRadius: 4 }} + > + İsim Kopyala + + + + + + Değer (Value): {txt.value} + + { + Clipboard.setStringAsync(txt.value); + Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success).catch(() => {}); + Alert.alert("Kopyalandı! 📋", `TXT Değeri panoya kopyalandı.`); + }} + style={{ backgroundColor: "#0284C720", paddingHorizontal: 6, paddingVertical: 3, borderRadius: 4 }} + > + Değer Kopyala + + + + ))} + + ); + })()} ) : null}