-
-
Save admwrd/0f6084857791bf46a778e7b69cd25c93 to your computer and use it in GitHub Desktop.
Serverless Geolocation Service
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Serverless Geolocation via Cloudflare Workers. | |
* | |
* Idea originally from blog https://maxkostinevich.com/blog/serverless-geolocation | |
* | |
* Tested and working on deployed Cloudflare worker. | |
* | |
* Code Authors, Contact and Ownership/License | |
* 1. Marius. (https://gist.github.com/maephisto/9228207) © 2014. License Unknown. | |
* 2. Max Kostinevich. (https://maxkostinevich.com) © 2020. License Unknown. | |
* 3. AdmWrd. (https://github.com/admwrd) No copyright / No attribution required. BSD 3 Clause / MIT License. | |
*/ | |
/* | |
Code primarily by Author 1. | |
Changes by Author 3: | |
* adding 'KP' (North Korea) | |
* updating 'KR' to refer to 'South Korea', rather than just 'Korea' | |
*/ | |
const isoCountries = { | |
'AD': 'Andorra', | |
'AE': 'United Arab Emirates', | |
'AF': 'Afghanistan', | |
'AG': 'Antigua And Barbuda', | |
'AI': 'Anguilla', | |
'AL': 'Albania', | |
'AM': 'Armenia', | |
'AN': 'Netherlands Antilles', | |
'AO': 'Angola', | |
'AQ': 'Antarctica', | |
'AR': 'Argentina', | |
'AS': 'American Samoa', | |
'AT': 'Austria', | |
'AU': 'Australia', | |
'AW': 'Aruba', | |
'AX': 'Aland Islands', | |
'AZ': 'Azerbaijan', | |
'BA': 'Bosnia And Herzegovina', | |
'BB': 'Barbados', | |
'BD': 'Bangladesh', | |
'BE': 'Belgium', | |
'BF': 'Burkina Faso', | |
'BG': 'Bulgaria', | |
'BH': 'Bahrain', | |
'BI': 'Burundi', | |
'BJ': 'Benin', | |
'BL': 'Saint Barthelemy', | |
'BM': 'Bermuda', | |
'BN': 'Brunei Darussalam', | |
'BO': 'Bolivia', | |
'BR': 'Brazil', | |
'BS': 'Bahamas', | |
'BT': 'Bhutan', | |
'BV': 'Bouvet Island', | |
'BW': 'Botswana', | |
'BY': 'Belarus', | |
'BZ': 'Belize', | |
'CA': 'Canada', | |
'CC': 'Cocos (Keeling) Islands', | |
'CD': 'Congo, Democratic Republic', | |
'CF': 'Central African Republic', | |
'CG': 'Congo', | |
'CH': 'Switzerland', | |
'CI': 'Cote D\'Ivoire', | |
'CK': 'Cook Islands', | |
'CL': 'Chile', | |
'CM': 'Cameroon', | |
'CN': 'China', | |
'CO': 'Colombia', | |
'CR': 'Costa Rica', | |
'CU': 'Cuba', | |
'CV': 'Cape Verde', | |
'CX': 'Christmas Island', | |
'CY': 'Cyprus', | |
'CZ': 'Czech Republic', | |
'DE': 'Germany', | |
'DJ': 'Djibouti', | |
'DK': 'Denmark', | |
'DM': 'Dominica', | |
'DO': 'Dominican Republic', | |
'DZ': 'Algeria', | |
'EC': 'Ecuador', | |
'EE': 'Estonia', | |
'EG': 'Egypt', | |
'EH': 'Western Sahara', | |
'ER': 'Eritrea', | |
'ES': 'Spain', | |
'ET': 'Ethiopia', | |
'FI': 'Finland', | |
'FJ': 'Fiji', | |
'FK': 'Falkland Islands (Malvinas)', | |
'FM': 'Micronesia, Federated States Of', | |
'FO': 'Faroe Islands', | |
'FR': 'France', | |
'GA': 'Gabon', | |
'GB': 'United Kingdom', | |
'GD': 'Grenada', | |
'GE': 'Georgia', | |
'GF': 'French Guiana', | |
'GG': 'Guernsey', | |
'GH': 'Ghana', | |
'GI': 'Gibraltar', | |
'GL': 'Greenland', | |
'GM': 'Gambia', | |
'GN': 'Guinea', | |
'GP': 'Guadeloupe', | |
'GQ': 'Equatorial Guinea', | |
'GR': 'Greece', | |
'GS': 'South Georgia And Sandwich Isl.', | |
'GT': 'Guatemala', | |
'GU': 'Guam', | |
'GW': 'Guinea-Bissau', | |
'GY': 'Guyana', | |
'HK': 'Hong Kong', | |
'HM': 'Heard Island & Mcdonald Islands', | |
'HN': 'Honduras', | |
'HR': 'Croatia', | |
'HT': 'Haiti', | |
'HU': 'Hungary', | |
'ID': 'Indonesia', | |
'IE': 'Ireland', | |
'IL': 'Israel', | |
'IM': 'Isle Of Man', | |
'IN': 'India', | |
'IO': 'British Indian Ocean Territory', | |
'IQ': 'Iraq', | |
'IR': 'Iran, Islamic Republic Of', | |
'IS': 'Iceland', | |
'IT': 'Italy', | |
'JE': 'Jersey', | |
'JM': 'Jamaica', | |
'JO': 'Jordan', | |
'JP': 'Japan', | |
'KE': 'Kenya', | |
'KG': 'Kyrgyzstan', | |
'KH': 'Cambodia', | |
'KI': 'Kiribati', | |
'KM': 'Comoros', | |
'KN': 'Saint Kitts And Nevis', | |
'KP': 'North Korea', | |
'KR': 'South Korea', | |
'KW': 'Kuwait', | |
'KY': 'Cayman Islands', | |
'KZ': 'Kazakhstan', | |
'LA': 'Lao People\'s Democratic Republic', | |
'LB': 'Lebanon', | |
'LC': 'Saint Lucia', | |
'LI': 'Liechtenstein', | |
'LK': 'Sri Lanka', | |
'LR': 'Liberia', | |
'LS': 'Lesotho', | |
'LT': 'Lithuania', | |
'LU': 'Luxembourg', | |
'LV': 'Latvia', | |
'LY': 'Libyan Arab Jamahiriya', | |
'MA': 'Morocco', | |
'MC': 'Monaco', | |
'MD': 'Moldova', | |
'ME': 'Montenegro', | |
'MF': 'Saint Martin', | |
'MG': 'Madagascar', | |
'MH': 'Marshall Islands', | |
'MK': 'Macedonia', | |
'ML': 'Mali', | |
'MM': 'Myanmar', | |
'MN': 'Mongolia', | |
'MO': 'Macao', | |
'MP': 'Northern Mariana Islands', | |
'MQ': 'Martinique', | |
'MR': 'Mauritania', | |
'MS': 'Montserrat', | |
'MT': 'Malta', | |
'MU': 'Mauritius', | |
'MV': 'Maldives', | |
'MW': 'Malawi', | |
'MX': 'Mexico', | |
'MY': 'Malaysia', | |
'MZ': 'Mozambique', | |
'NA': 'Namibia', | |
'NC': 'New Caledonia', | |
'NE': 'Niger', | |
'NF': 'Norfolk Island', | |
'NG': 'Nigeria', | |
'NI': 'Nicaragua', | |
'NL': 'Netherlands', | |
'NO': 'Norway', | |
'NP': 'Nepal', | |
'NR': 'Nauru', | |
'NU': 'Niue', | |
'NZ': 'New Zealand', | |
'OM': 'Oman', | |
'PA': 'Panama', | |
'PE': 'Peru', | |
'PF': 'French Polynesia', | |
'PG': 'Papua New Guinea', | |
'PH': 'Philippines', | |
'PK': 'Pakistan', | |
'PL': 'Poland', | |
'PM': 'Saint Pierre And Miquelon', | |
'PN': 'Pitcairn', | |
'PR': 'Puerto Rico', | |
'PS': 'Palestinian Territory, Occupied', | |
'PT': 'Portugal', | |
'PW': 'Palau', | |
'PY': 'Paraguay', | |
'QA': 'Qatar', | |
'RE': 'Reunion', | |
'RO': 'Romania', | |
'RS': 'Serbia', | |
'RU': 'Russian Federation', | |
'RW': 'Rwanda', | |
'SA': 'Saudi Arabia', | |
'SB': 'Solomon Islands', | |
'SC': 'Seychelles', | |
'SD': 'Sudan', | |
'SE': 'Sweden', | |
'SG': 'Singapore', | |
'SH': 'Saint Helena', | |
'SI': 'Slovenia', | |
'SJ': 'Svalbard And Jan Mayen', | |
'SK': 'Slovakia', | |
'SL': 'Sierra Leone', | |
'SM': 'San Marino', | |
'SN': 'Senegal', | |
'SO': 'Somalia', | |
'SR': 'Suriname', | |
'ST': 'Sao Tome And Principe', | |
'SV': 'El Salvador', | |
'SY': 'Syrian Arab Republic', | |
'SZ': 'Swaziland', | |
'TC': 'Turks And Caicos Islands', | |
'TD': 'Chad', | |
'TF': 'French Southern Territories', | |
'TG': 'Togo', | |
'TH': 'Thailand', | |
'TJ': 'Tajikistan', | |
'TK': 'Tokelau', | |
'TL': 'Timor-Leste', | |
'TM': 'Turkmenistan', | |
'TN': 'Tunisia', | |
'TO': 'Tonga', | |
'TR': 'Turkey', | |
'TT': 'Trinidad And Tobago', | |
'TV': 'Tuvalu', | |
'TW': 'Taiwan', | |
'TZ': 'Tanzania', | |
'UA': 'Ukraine', | |
'UG': 'Uganda', | |
'UM': 'United States Outlying Islands', | |
'US': 'United States', | |
'UY': 'Uruguay', | |
'UZ': 'Uzbekistan', | |
'VA': 'Holy See (Vatican City State)', | |
'VC': 'Saint Vincent And Grenadines', | |
'VE': 'Venezuela', | |
'VG': 'Virgin Islands, British', | |
'VI': 'Virgin Islands, U.S.', | |
'VN': 'Viet Nam', | |
'VU': 'Vanuatu', | |
'WF': 'Wallis And Futuna', | |
'WS': 'Samoa', | |
'YE': 'Yemen', | |
'YT': 'Mayotte', | |
'ZA': 'South Africa', | |
'ZM': 'Zambia', | |
'ZW': 'Zimbabwe' | |
}; | |
// Code originally by Author 2. | |
// License for changes by Author 3. | |
// @license magnet:?xt=urn:btih:c80d50af7d3db9be66a4d0a86db0286e4fd33292&dn=bsd-3-clause.txt BSD 3 Clause | |
// @license magnet:?xt=urn:btih:d3d9a9a6595521f9666a5e94cc830dab83b65699&dn=expat.txt Expat (MIT) License | |
/** | |
* Return the full name of a country when given the ISO 3166-2 code. | |
* If the code is not found, returns 'Unknown' with an error about the code. | |
* | |
*/ | |
const getCountryName = (countryCode) => { | |
countryCode = countryCode.toUpperCase(); | |
if (isoCountries.hasOwnProperty(countryCode)) { | |
return isoCountries[countryCode]; | |
} else { | |
return "Unknown. Could not find country for code " + countryCode; | |
} | |
} | |
// @license-end | |
// Code originally by Author 2. | |
// License for changes by Author 3. | |
// @license magnet:?xt=urn:btih:c80d50af7d3db9be66a4d0a86db0286e4fd33292&dn=bsd-3-clause.txt BSD 3 Clause | |
// @license magnet:?xt=urn:btih:d3d9a9a6595521f9666a5e94cc830dab83b65699&dn=expat.txt Expat (MIT) License | |
/** | |
* Creates a Response that contains either a JSON object or a primitive type. | |
* This defaults to returning with a 200 OK Status, but can be overridden. | |
* | |
* @param message | |
* @param status | |
* @return {Response} | |
* @constructor | |
*/ | |
const JSONResponse = (message, status = 200) => { | |
status = status > 99 && status < 600 ? status : 200; | |
let headers = { | |
headers: { | |
"content-type": "application/json;charset=UTF-8", | |
"Access-Control-Allow-Origin": "*", | |
"Access-Control-Allow-Methods": "GET, HEAD, POST, OPTIONS", | |
"Access-Control-Allow-Headers": "Content-Type" | |
}, | |
status: status | |
}; | |
if (typeof message === 'string') { | |
return new Response(JSON.stringify({message: message}), headers); | |
} else { | |
return new Response(JSON.stringify(message), headers); | |
} | |
}; | |
// @license-end | |
// Code by Author 2. | |
const corsHeaders = { | |
"Access-Control-Allow-Origin": "*", | |
"Access-Control-Allow-Methods": "GET, HEAD, POST, OPTIONS", | |
"Access-Control-Allow-Headers": "Content-Type" | |
}; | |
// Code by Author 2. | |
/** | |
* Returns information on endpoint when met with an OPTION method. | |
* | |
* @param request | |
* @return {Response} | |
*/ | |
function handleOptions(request) { | |
if ( | |
request.headers.get("Origin") !== null && | |
request.headers.get("Access-Control-Request-Method") !== null && | |
request.headers.get("Access-Control-Request-Headers") !== null | |
) { | |
// Handle CORS pre-flight request. | |
return new Response(null, { | |
headers: corsHeaders | |
}); | |
} else { | |
// Handle standard OPTIONS request. | |
return new Response(null, { | |
headers: { | |
Allow: "GET, HEAD, POST, OPTIONS" | |
} | |
}); | |
} | |
} | |
// Code originally by Author 2. | |
// License for changes by Author 3. | |
// @license magnet:?xt=urn:btih:c80d50af7d3db9be66a4d0a86db0286e4fd33292&dn=bsd-3-clause.txt BSD 3 Clause | |
// @license magnet:?xt=urn:btih:d3d9a9a6595521f9666a5e94cc830dab83b65699&dn=expat.txt Expat (MIT) License | |
/** | |
* Respond to the Request | |
* | |
* @param request | |
* @return {Promise<Response>} | |
*/ | |
async function handleRequest(request) { | |
console.log("We have " + JSON.stringify(request)) | |
try { | |
const ip = request.headers.get('cf-connecting-ip'); | |
if (typeof request.cf === 'undefined' || typeof request.cf.country === 'undefined' || request.cf.country.trim() === '') { | |
return JSONResponse("Bad Request. You must supply a value for country code.", 400); | |
} | |
const countryCode = request.cf.country.trim(); | |
const country = getCountryName(countryCode); | |
return JSONResponse({message: 'success', ip, country: {code: countryCode, name: country}}); | |
} catch (err) { | |
console.log("Error! " + err); | |
return JSONResponse("Oops! Something went wrong.", 500); | |
} | |
} | |
// @license-end | |
// Code originally by Author 2. | |
// License for changes by Author 3. | |
// @license magnet:?xt=urn:btih:c80d50af7d3db9be66a4d0a86db0286e4fd33292&dn=bsd-3-clause.txt BSD 3 Clause | |
// @license magnet:?xt=urn:btih:d3d9a9a6595521f9666a5e94cc830dab83b65699&dn=expat.txt Expat (MIT) License | |
/** | |
* Sets up an Event Listener depending on what HTTP Method is being requested. | |
*/ | |
addEventListener('fetch', event => { | |
switch (event.request.method.toUpperCase()) { | |
case "OPTIONS": | |
event.respondWith(handleOptions(event.request)); | |
break; | |
default: | |
event.respondWith(handleRequest(event.request)); | |
} | |
}); | |
// @license-end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment