← back to Prestige Car Wash
scripts/places-read.js
62 lines
#!/usr/bin/env node
'use strict';
/**
* places-read.js — read-only pull of the Google Business listing via the Places API.
* Writes data/places.json (hours, rating, reviews, photos). No writes to Google.
*
* Needs GOOGLE_PLACES_API_KEY + PCW_PLACE_ID (Steve-gated: adding the key + billing).
* Without them it leaves the mock in place and explains what to do.
*/
const fs = require('fs');
const path = require('path');
const OUT = path.join(__dirname, '..', 'data', 'places.json');
function keyFrom(name) {
if (process.env[name]) return process.env[name];
try {
const m = fs.readFileSync(path.join(process.env.HOME, 'Projects/secrets-manager/.env'), 'utf8')
.match(new RegExp('^' + name + '=(.+)$', 'm'));
return m ? m[1].trim() : '';
} catch { return ''; }
}
const KEY = keyFrom('GOOGLE_PLACES_API_KEY');
const PLACE = process.env.PCW_PLACE_ID || keyFrom('PCW_PLACE_ID');
async function main() {
if (!KEY || !PLACE) {
console.log('⚠ GOOGLE_PLACES_API_KEY / PCW_PLACE_ID not set — keeping mock data/places.json.');
console.log(' Gated setup: enable Places API + billing on Google Cloud, then `/secrets` the key.');
return;
}
// Places API (New) Place Details
const url = `https://places.googleapis.com/v1/places/${PLACE}`;
const r = await fetch(url, {
headers: {
'X-Goog-Api-Key': KEY,
'X-Goog-FieldMask': 'displayName,formattedAddress,nationalPhoneNumber,rating,userRatingCount,regularOpeningHours,websiteUri,googleMapsUri,reviews,photos'
}
});
if (!r.ok) { console.error(`Places API ${r.status}: ${(await r.text()).slice(0, 200)}`); process.exit(1); }
const p = await r.json();
const out = {
_mock: false,
name: p.displayName?.text || 'Prestige Car Wash',
address: p.formattedAddress || '',
phone: p.nationalPhoneNumber || '',
rating: p.rating || null,
reviews_count: p.userRatingCount || 0,
hours: p.regularOpeningHours?.weekdayDescriptions || [],
website: p.websiteUri || '',
maps_url: p.googleMapsUri || '',
place_id: PLACE,
reviews: (p.reviews || []).slice(0, 5).map(rv => ({
author: rv.authorAttribution?.displayName || '', rating: rv.rating, text: rv.text?.text || ''
})),
photos: (p.photos || []).slice(0, 6).map(ph => ph.name),
updated_at: new Date().toISOString()
};
fs.writeFileSync(OUT, JSON.stringify(out, null, 2));
console.log(`✔ wrote data/places.json — ${out.name}, ${out.rating}★ (${out.reviews_count})`);
}
main().catch(e => { console.error(e); process.exit(1); });