Warning: IT Stuff ahead...
You can use Google’s geocoding API to get lat/long of a given city/state/country. I do this now for a client, except down to the address level. I throttle it at 2,500 records per day, which keeps you in Google’s free tier. It is all written in Python so it’s very transferable to our application.
Each night you look for city/state/country records where lat/long hasn’t been captured. In the SQL world this is called an “unmatched query”. You then iterate through those records and fire the google API call for each, writing the JSON results to a table (preferably cleaned up). Lastly, you join your user list to the lat/long list based on the user’s city/state/country.
After that you can pretty much plug that dataset into any available/free mapping application. That is all web stuff which not a core competency of mine...