Watch-IP

تحديد الموقع الجغرافي لعنوان IP في Next.js: أنماط المتصفح والخادم

يمكن لإطار Next.js عرض الصفحة نفسها من جانب الخادم أو في المتصفح، وهذا الاختيار يُغيّر أي نقطة نهاية في Watch-IP تُحدِّد فعليًا موقع الزائر — أو تُحدِّد موقع خادمك بدلًا منه.

قبل أن تبدأ

  • مفتاح زوار قابل للنشر لنمط العميل، أو مفتاح بحث سري لنمط الخادم — راجع صفحة المصادقة.
  • مشروع Next.js يستخدم App Router (تفترض الأمثلة التقسيم بين مكوّنات الخادم والعميل في Next.js 16).
  • بالنسبة إلى نمط الخادم، ترويسة تُعيّنها منصة الاستضافة الخاصة بك على الطلبات الواردة تحمل عنوان IP الحقيقي للزائر (مثل CF-Connecting-IP في Cloudflare) — لا ترويسة يمكن للعميل تعيينها بنفسه.

1. فهم ما يستدعيه كل نمط فعليًا

تُحدِّد GET /v1/geo هوية الاتصال الذي يصل فعليًا إلى الواجهة البرمجية — سواء كان متصفح الزائر يستدعيها مباشرة، أو خادم Next.js الخاص بك إذا استدعاها الكود الخاص بك أثناء العرض من جانب الخادم. استدعاء GET /v1/geo من مكوّن خادم أو معالج مسار (Route Handler) يُحدِّد الموقع الشبكي لخادمك، لا موقع الزائر، وهذا عادةً ليس ما تريده. هناك نمطان يُحدِّدان موقع الزائر فعليًا: تشغيل الطلب في المتصفح (مكوّن العميل)، أو تشغيله على خادمك مقابل عنوان IP مُقدَّم باستخدام GET /v1/lookup/{ip}.

2. مكوّن العميل: استدعاء GET /v1/geo من المتصفح

هذا هو الطلب نفسه الذي يستخدمه دليلا JavaScript وReact — مكوّن عميل يُنفِّذ الطلب بمفتاح قابل للنشر. تُلبّي ترويسة Origin الخاصة بالمتصفح نفسه فحص النطاقات المسموح بها للمفتاح، لذا يجب أن يكون عنوان URL العام لتطبيقك مُدرَجًا في تلك القائمة.

TypeScript (مكوّن العميل)

"use client";

import { useEffect, useState } from "react";

export function VisitorLocation() {
  const [city, setCity] = useState<string | null>(null);

  useEffect(() => {
    const controller = new AbortController();

    fetch("https://api.watch-ip.com/v1/geo", {
      headers: { "X-Api-Key": "YOUR_PUBLISHABLE_KEY" },
      signal: controller.signal,
    })
      .then((res) => (res.ok ? res.json() : null))
      .then((geo) => setCity(geo?.city ?? null))
      .catch(() => {});

    return () => controller.abort();
  }, []);

  return <span>{city ?? "منطقتك"}</span>;
}

3. مكوّن الخادم أو معالج المسار: البحث عن عنوان IP الخاص بالزائر صراحةً

لتحديد موقع الزائر أثناء العرض من جانب الخادم، لا تستدعِ GET /v1/geo — بل استدعِ GET /v1/lookup/{ip} بمفتاح سري وعنوان IP الحقيقي للزائر، مقروءًا من أي ترويسة تُعيّنها منصة الاستضافة على الطلب الوارد. لا تثق أبدًا بقيمة X-Forwarded-For العادية التي يمكن للعميل تعيينها بنفسه، ما لم تكن منصتك تُزيلها وتُعيد تعيينها عند الحافة (edge).

TypeScript (مكوّن الخادم)

import { headers } from "next/headers";

export async function VisitorLocationServer() {
  const ip = (await headers()).get("cf-connecting-ip");
  if (!ip) return null;

  const response = await fetch(`https://api.watch-ip.com/v1/lookup/${ip}`, {
    headers: { Authorization: "Bearer YOUR_SECRET_KEY" },
    cache: "no-store",
  });
  if (!response.ok) return null;

  const geo = await response.json();
  return <span>{geo.city ?? "منطقتك"}</span>;
}

4. لا تُخزِّن نتيجة زائر مؤقتًا لعرضها على زائر آخر

الإعداد cache: 'no-store' أعلاه مقصود: فنتيجة البحث خاصة بالزائر الذي طلبها. إذا كان هذا المكوّن يُعرَض ضمن مسار يمكن لـ Next.js أو لشبكة CDN تخزينه مؤقتًا — صفحة ثابتة، أو صفحة ISR، أو ذاكرة تخزين مؤقت مشتركة للطلبات — فإن الاستجابة المخزَّنة ستعرض مدينة زائر واحد على الجميع بعد ذلك. حافظ على تشغيل الفرع المخصَّص (personalized) بشكل ديناميكي، ولا تُدرِج أبدًا استجابة بحث ضمن ذاكرة تخزين مؤقت على مستوى الصفحة أو على مستوى CDN.

الأخطاء والحالات الحدّية

غياب ترويسة IP أو كونها فارغة (بعض إعدادات الخوادم المحلية أو الوكيلة لا تُعيّن واحدة) يعني عدم وجود عنوان IP للزائر للبحث عنه — اعرض الحالة الاحتياطية بدلًا من استدعاء نقطة النهاية بسلسلة فارغة. الرمز 403 من GET /v1/lookup/{ip} يعني عادةً أنه تم استخدام مفتاح قابل للنشر في مسار يتطلب مفتاحًا سريًا؛ أما الرمز 400 فيعني أن الترويسة كانت تحتوي على قيمة ليست عنوان IPv4/IPv6 صالحًا. راجع صفحة الأخطاء وحدود المعدل.

الخطوة التالية

للحصول على مكوّن يمكنك إعادة استخدامه في تطبيقات React التي تعمل من جانب العميل فقط، بالإضافة إلى Next.js، راجع دليل React — فنمط مكوّن العميل أعلاه هو غلاف خاص بـ Next.js حول الطلب نفسه.

صفحات ذات صلة