// 国家坐标数据
const countryCoordinates = {
"US": [{ lat: 37.7749, lng: -122.4194 }, { lat: 34.0522, lng: -118.2437 }],
"UK": [{ lat: 51.5074, lng: -0.1278 }, { lat: 53.4808, lng: -2.2426 }],
"FR": [{ lat: 48.8566, lng: 2.3522 }, { lat: 45.7640, lng: 4.8357 }],
"DE": [{ lat: 52.5200, lng: 13.4050 }, { lat: 48.1351, lng: 11.5820 }],
"CN": [{ lat: 39.9042, lng: 116.4074 }, { lat: 31.2304, lng: 121.4737 }, {"lat": 23.1291, "lng": 113.2644}, {"lat": 30.6799, "lng": 104.0679}, {"lat": 30.2582, "lng": 120.1646}],
"TW": [{ lat: 25.0330, lng: 121.5654 }, { lat: 22.6273, lng: 120.3014 }],
"HK": [{ lat: 22.3193, lng: 114.1694 },{ lat: 22.3964, lng: 114.1095 }],
"JP": [{ lat: 35.6895, lng: 139.6917 }, { lat: 34.6937, lng: 135.5023 }],
"IN": [{ lat: 28.6139, lng: 77.2090 }, { lat: 19.0760, lng: 72.8777 }],
"AU": [{ lat: -33.8688, lng: 151.2093 }, { lat: -37.8136, lng: 144.9631 }],
"BR": [{ lat: -23.5505, lng: -46.6333 }, { lat: -22.9068, lng: -43.1729 }],
"CA": [{ lat: 43.651070, lng: -79.347015 }, { lat: 45.501690, lng: -73.567253 }],
"RU": [{ lat: 55.7558, lng: 37.6173 }, { lat: 59.9343, lng: 30.3351 }],
"ZA": [{ lat: -33.9249, lng: 18.4241 }, { lat: -26.2041, lng: 28.0473 }],
"MX": [{ lat: 19.4326, lng: -99.1332 }, { lat: 20.6597, lng: -103.3496 }],
"KR": [{ lat: 37.5665, lng: 126.9780 }, { lat: 35.1796, lng: 129.0756 }],
"IT": [{ lat: 41.9028, lng: 12.4964 }, { lat: 45.4642, lng: 9.1900 }],
"ES": [{ lat: 40.4168, lng: -3.7038 }, { lat: 41.3851, lng: 2.1734 }],
"TR": [{ lat: 41.0082, lng: 28.9784 }, { lat: 39.9334, lng: 32.8597 }],
"SA": [{ lat: 24.7136, lng: 46.6753 }, { lat: 21.3891, lng: 39.8579 }],
"AR": [{ lat: -34.6037, lng: -58.3816 }, { lat: -31.4201, lng: -64.1888 }],
"EG": [{ lat: 30.0444, lng: 31.2357 }, { lat: 31.2156, lng: 29.9553 }],
"NG": [{ lat: 6.5244, lng: 3.3792 }, { lat: 9.0579, lng: 7.4951 }],
"ID": [{ lat: -6.2088, lng: 106.8456 }, { lat: -7.7956, lng: 110.3695 }]
};
// 姓名数据
const namesByCountry = {
"CN": {
first: ["Li", "Wang", "Zhang", "Liu", "Chen", "Yang", "Huang", "Zhao", "Wu", "Zhou", "Xu", "Sun", "Ma", "Zhu", "Hu", "Guo", "He", "Gao", "Lin", "Zheng"],
last: ["Wei", "Fang", "Na", "Xiuying", "Min", "Jing", "Li", "Qiang", "Lei", "Jun", "Yang", "Yong", "Yan", "Jie", "Tao", "Ming", "Chao", "Xiulan", "Xia", "Ping"]
},
"JP": {
first: ["Sato", "Suzuki", "Takahashi", "Tanaka", "Watanabe", "Ito", "Yamamoto", "Nakamura", "Kobayashi", "Kato"],
last: ["Shota", "Ren", "Hina", "Yui", "Hiroto", "Sota", "Yota", "Misaki", "Nanami", "Yuto"]
},
"KR": {
first: ["Kim", "Lee", "Park", "Choi", "Jung", "Kang", "Jo", "Yoon", "Jang", "Lim"],
last: ["Minjun", "Seojun", "Doyun", "Jiho", "Jihun", "Seoyeon", "Seoyun", "Jiwoo", "Seohyun", "Minseo"]
},
"TW": {
first: ["Chen", "Lin", "Huang", "Chang", "Lee", "Wang", "Wu", "Liu", "Tsai", "Yang"],
last: ["Zhiming", "Jianhong", "Junjie", "Yijun", "Shufen", "Meiling", "Yating", "Jiahao", "Zhihao", "Shuhui"]
},
"HK": {
first: ["Chan", "Lee", "Wong", "Cheung", "Lau", "Wang", "Ng", "Cheng", "Leung", "Ho"],
last: ["Chiming", "Kayan", "Junjie", "Wingsze", "Kaming", "Meiling", "Kahao", "Winger", "Chihao", "Shukfan"]
},
"US": {
first: ["Smith", "Johnson", "Williams", "Brown", "Jones", "Garcia", "Miller", "Davis", "Rodriguez", "Martinez"],
last: ["James", "John", "Robert", "Michael", "William", "David", "Richard", "Joseph", "Thomas", "Christopher"]
},
"UK": {
first: ["Smith", "Jones", "Williams", "Taylor", "Brown", "Davies", "Evans", "Wilson", "Thomas", "Roberts"],
last: ["Oliver", "Jack", "Harry", "George", "Noah", "Charlie", "Jacob", "Oscar", "William", "Leo"]
},
"FR": {
first: ["Martin", "Bernard", "Dubois", "Thomas", "Robert", "Richard", "Petit", "Durand", "Leroy", "Moreau"],
last: ["Lucas", "Louis", "Gabriel", "Arthur", "Jules", "Hugo", "Leo", "Adam", "Raphael", "Paul"]
},
"DE": {
first: ["Mueller", "Schmidt", "Schneider", "Fischer", "Weber", "Meyer", "Wagner", "Becker", "Schulz", "Hoffmann"],
last: ["Ben", "Paul", "Leon", "Noah", "Luis", "Finn", "Felix", "Jonas", "Maximilian", "Henry"]
},
"IT": {
first: ["Rossi", "Ferrari", "Russo", "Bianchi", "Romano", "Gallo", "Costa", "Fontana", "Conti", "Esposito"],
last: ["Leonardo", "Francesco", "Alessandro", "Lorenzo", "Matteo", "Andrea", "Gabriele", "Marco", "Antonio", "Giuseppe"]
},
"ES": {
first: ["Garcia", "Rodriguez", "Gonzalez", "Fernandez", "Lopez", "Martinez", "Sanchez", "Perez", "Martin", "Gomez"],
last: ["Antonio", "Jose", "Manuel", "Francisco", "David", "Juan", "Miguel", "Javier", "Rafael", "Carlos"]
},
"BR": {
first: ["Silva", "Santos", "Oliveira", "Souza", "Rodrigues", "Ferreira", "Alves", "Pereira", "Lima", "Gomes"],
last: ["Miguel", "Arthur", "Heitor", "Pedro", "Davi", "Gabriel", "Bernardo", "Lucas", "Matheus", "Rafael"]
},
"RU": {
first: ["Ivanov", "Smirnov", "Kuznetsov", "Popov", "Vasiliev", "Petrov", "Sokolov", "Mikhailov", "Fedorov", "Morozov"],
last: ["Alexander", "Dmitry", "Maxim", "Ivan", "Andrey", "Mikhail", "Artem", "Daniel", "Roman", "Sergey"]
},
"IN": {
first: ["Kumar", "Singh", "Sharma", "Patel", "Gupta", "Shah", "Verma", "Rao", "Reddy", "Joshi"],
last: ["Aarav", "Vihaan", "Vivaan", "Aditya", "Arjun", "Reyansh", "Ayaan", "Sai", "Krishna", "Ishaan"]
},
"AU": {
first: ["Smith", "Jones", "Williams", "Brown", "Wilson", "Taylor", "Johnson", "White", "Anderson", "Thompson"],
last: ["Oliver", "William", "Jack", "Noah", "Thomas", "James", "Lucas", "Henry", "Ethan", "Alexander"]
},
"CA": {
first: ["Smith", "Brown", "Tremblay", "Martin", "Roy", "Wilson", "MacDonald", "Taylor", "Campbell", "Anderson"],
last: ["Liam", "Noah", "Oliver", "William", "James", "Benjamin", "Lucas", "Henry", "Theodore", "Jack"]
},
"MX": {
first: ["Garcia", "Rodriguez", "Martinez", "Lopez", "Gonzalez", "Perez", "Sanchez", "Ramirez", "Torres", "Flores"],
last: ["Santiago", "Mateo", "Sebastian", "Leonardo", "Diego", "Daniel", "Gabriel", "Adrian", "David", "Alexander"]
},
"TR": {
first: ["Yilmaz", "Kaya", "Demir", "Sahin", "Celik", "Yildiz", "Erdogan", "Ozturk", "Aydin", "Ozdemir"],
last: ["Yusuf", "Eymen", "Ömer", "Mustafa", "Ali", "Mehmet", "Ahmet", "Emir", "Hamza", "Ibrahim"]
},
"SA": {
first: ["Al-Saud", "Al-Sheikh", "Al-Rashid", "Al-Qahtani", "Al-Ghamdi", "Al-Zahrani", "Al-Dossari", "Al-Shammari", "Al-Otaibi", "Al-Harbi"],
last: ["Mohammed", "Abdullah", "Ahmed", "Ali", "Omar", "Ibrahim", "Khalid", "Hassan", "Fahad", "Abdul"]
},
"AR": {
first: ["Gonzalez", "Rodriguez", "Garcia", "Fernandez", "Lopez", "Martinez", "Perez", "Romero", "Sanchez", "Diaz"],
last: ["Mateo", "Thiago", "Benjamin", "Valentino", "Santiago", "Juan", "Lucas", "Martin", "Nicolas", "Joaquin"]
},
"EG": {
first: ["Mohamed", "Ahmed", "Mahmoud", "Ibrahim", "Ali", "Hassan", "Hussein", "Mostafa", "Kamal", "Samir"],
last: ["Omar", "Youssef", "Adam", "Malik", "Zain", "Hamza", "Kareem", "Hassan", "Ali", "Ibrahim"]
},
"NG": {
first: ["Okafor", "Adebayo", "Okonkwo", "Eze", "Oluwaseun", "Adegoke", "Afolabi", "Ogunleye", "Adeniyi", "Adesina"],
last: ["Oluwadamilare", "Oluwatobiloba", "Ayomide", "Temitope", "Oluwaseun", "Adebayo", "Chibuike", "Chisom", "Chidi", "Obinna"]
},
"ID": {
first: ["Wijaya", "Kusuma", "Suryanto", "Halim", "Santoso", "Tanaka", "Wibowo", "Susanto", "Hidayat", "Putra"],
last: ["Muhammad", "Ahmad", "Abdul", "Aditya", "Budi", "Dimas", "Eko", "Fajar", "Gading", "Hadi"]
},
"ZA": {
first: ["Nkosi", "Van der Merwe", "Botha", "Mkhize", "Khumalo", "Pretorius", "Venter", "Ndlovu", "Fourie", "Nel"],
last: ["Bandile", "Themba", "Sipho", "Thabo", "Jabu", "Mandla", "Blessing", "Gift", "Lucky", "Precious"]
}
};
// 电话号码格式配置
const phoneFormats = {
"US": {
format: "+1 (XXX) XXX-XXXX",
areaCodeRanges: [[201, 989]]
},
"CN": {
format: "+86 1XX-XXXX-XXXX",
mobilePrefix: ["30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "50", "51", "52", "53", "55", "56", "57", "58", "59", "66", "70", "71", "72", "73", "75", "76", "77", "78", "79", "80", "81", "82", "83", "84", "85", "86", "87", "88", "89"]
},
"JP": {
format: "+81 XX-XXXX-XXXX",
mobilePrefix: ["70", "80", "90"]
},
"KR": {
format: "+82 10-XXXX-XXXX"
},
"UK": {
format: "+44 7XXX XXXXXX",
mobilePrefix: ["7"]
},
"FR": {
format: "+33 6 XX XX XX XX",
mobilePrefix: ["6", "7"]
},
"DE": {
format: "+49 15X XXXXXXXX",
mobilePrefix: ["15", "16", "17"]
},
"TW": {
format: "+886 9XX-XXX-XXX"
},
"HK": {
format: "+852 XXXX XXXX",
mobilePrefix: ["5", "6", "9"]
},
"AU": {
format: "+61 4XX XXX XXX",
mobilePrefix: ["4"]
},
"CA": {
format: "+1 (XXX) XXX-XXXX",
areaCodeRanges: [[204, 989]]
},
"MX": {
format: "+52 1XX XXX XXXX"
},
"TR": {
format: "+90 5XX XXX XXXX",
mobilePrefix: ["5"]
},
"SA": {
format: "+966 5XX XXX XXXX",
mobilePrefix: ["5"]
},
"AR": {
format: "+54 9XX XXXX-XXXX"
},
"EG": {
format: "+20 1XX XXX XXXX",
mobilePrefix: ["1"]
},
"NG": {
format: "+234 8XX XXX XXXX",
mobilePrefix: ["7", "8", "9"]
},
"ID": {
format: "+62 8XX-XXXX-XXXX",
mobilePrefix: ["8"]
},
"ZA": {
format: "+27 8X XXX XXXX",
mobilePrefix: ["6", "7", "8"]
}
};
// 工具函数
function getRandomLocation(country) {
const coordsArray = countryCoordinates[country];
const randomCity = coordsArray[Math.floor(Math.random() * coordsArray.length)];
const lat = randomCity.lat + (Math.random() - 0.5) * 0.1;
const lng = randomCity.lng + (Math.random() - 0.5) * 0.1;
return { lat, lng };
}
function getRandomName(country) {
if (!namesByCountry[country]) {
return null;
}
const names = namesByCountry[country];
const firstName = names.first[Math.floor(Math.random() * names.first.length)];
const lastName = names.last[Math.floor(Math.random() * names.last.length)];
return `${firstName} ${lastName}`;
}
function generateAreaCode(ranges) {
const range = ranges[Math.floor(Math.random() * ranges.length)];
const [min, max] = range;
return Math.floor(min + Math.random() * (max - min + 1));
}
function getRandomPhoneNumber(country) {
const format = phoneFormats[country] || phoneFormats["US"];
let phone = format.format;
if (format.areaCodeRanges) {
const areaCode = generateAreaCode(format.areaCodeRanges);
phone = phone.replace("XXX", areaCode);
phone = phone.replace(/X/g, () => Math.floor(Math.random() * 10));
} else if (format.mobilePrefix) {
const prefix = format.mobilePrefix[Math.floor(Math.random() * format.mobilePrefix.length)];
// 先替换前缀
if (prefix.length === 2) {
phone = phone.replace(/XX/, prefix);
} else {
phone = phone.replace(/X/, prefix);
}
// 然后替换剩余的X
phone = phone.replace(/X/g, () => Math.floor(Math.random() * 10));
} else {
phone = phone.replace(/X/g, () => Math.floor(Math.random() * 10));
}
return phone;
}
function isValidAddress(data) {
return data &&
data.address &&
data.address.house_number &&
data.address.road &&
(data.address.city || data.address.town);
}
// 处理CORS请求的headers
const corsHeaders = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, HEAD, POST, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type',
};
// HTML 模板
const htmlContent = `
随机地址生成器
已复制到剪贴板!
`;
async function handleRequest(request) {
const url = new URL(request.url);
const path = url.pathname;
// 处理 API 请求
if (path === '/api') {
// 原有的 API 处理逻辑
return handleApiRequest(request);
}
// 处理根路径请求,返回 HTML 页面
return new Response(htmlContent, {
headers: {
'Content-Type': 'text/html;charset=UTF-8',
...corsHeaders
}
});
}
// 将原有的处理逻辑移到单独的函数中
async function handleApiRequest(request) {
if (request.method === 'OPTIONS') {
return new Response(null, {
headers: corsHeaders
});
}
if (request.method !== 'GET') {
return new Response('Method not allowed', { status: 405 });
}
const url = new URL(request.url);
const country = url.searchParams.get('country') || 'US';
if (!countryCoordinates[country]) {
return new Response(JSON.stringify({
error: 'Invalid country code'
}), {
status: 400,
headers: {
'Content-Type': 'application/json',
...corsHeaders
}
});
}
let attempts = 0;
const maxAttempts = 20;
while (attempts < maxAttempts) {
try {
const location = getRandomLocation(country);
const apiUrl = `https://nominatim.openstreetmap.org/reverse?format=json&lat=${location.lat}&lon=${location.lng}&zoom=18&addressdetails=1`;
const response = await fetch(apiUrl, {
headers: {
'User-Agent': 'Cloudflare Worker Random Address Generator'
}
});
const data = await response.json();
if (isValidAddress(data)) {
let city = data.address.city || data.address.town || '';
city = city.split(';')[0].trim();
const address = `${data.address.house_number} ${data.address.road}, ${city}, ${data.address.postcode || ''}, ${country}`.replace(/\s+/g, ' ').trim();
const name = getRandomName(country);
const gender = Math.random() > 0.5 ? 'Male' : 'Female';
const phone = getRandomPhoneNumber(country);
const result = {
name,
gender,
phone,
address,
coordinates: {
lat: location.lat,
lng: location.lng
}
};
return new Response(JSON.stringify(result), {
headers: {
'Content-Type': 'application/json',
...corsHeaders
}
});
}
attempts++;
if (attempts < maxAttempts) {
await new Promise(resolve => setTimeout(resolve, 100));
}
} catch (error) {
attempts++;
if (attempts < maxAttempts) {
await new Promise(resolve => setTimeout(resolve, 100));
}
}
}
return new Response(JSON.stringify({
error: 'Failed to generate valid address after multiple attempts'
}), {
status: 500,
headers: {
'Content-Type': 'application/json',
...corsHeaders
}
});
}
// 注册事件监听器
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request));
});