121 lines
3.3 KiB
JavaScript
121 lines
3.3 KiB
JavaScript
// Google Sheets sync helper
|
|
// Non-blocking secondary write for team access and redundancy
|
|
// Reads credentials from file (GOOGLE_SERVICE_ACCOUNT_FILE) or env var (GOOGLE_SERVICE_ACCOUNT)
|
|
|
|
const fs = require('fs');
|
|
|
|
let sheetsClient = null;
|
|
let authClient = null;
|
|
|
|
function getCredentials() {
|
|
if (!process.env.GOOGLE_SHEET_ID) return null;
|
|
|
|
// Prefer file-based credentials (cleaner for Docker)
|
|
const filePath = process.env.GOOGLE_SERVICE_ACCOUNT_FILE;
|
|
if (filePath) {
|
|
try {
|
|
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
} catch (err) {
|
|
console.error('[Google Sheets] Failed to read credentials file:', err.message);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
// Fall back to JSON string in env var
|
|
const raw = process.env.GOOGLE_SERVICE_ACCOUNT;
|
|
if (!raw) return null;
|
|
try {
|
|
return JSON.parse(raw.trim());
|
|
} catch {
|
|
console.error('[Google Sheets] Failed to parse service account JSON');
|
|
return null;
|
|
}
|
|
}
|
|
|
|
async function getSheetsClient() {
|
|
if (sheetsClient) return sheetsClient;
|
|
|
|
const creds = getCredentials();
|
|
if (!creds) return null;
|
|
|
|
const { google } = require('googleapis');
|
|
authClient = new google.auth.GoogleAuth({
|
|
credentials: creds,
|
|
scopes: ['https://www.googleapis.com/auth/spreadsheets'],
|
|
});
|
|
sheetsClient = google.sheets({ version: 'v4', auth: authClient });
|
|
return sheetsClient;
|
|
}
|
|
|
|
/**
|
|
* Append a row to a specific sheet tab.
|
|
* Non-blocking — logs errors but never throws.
|
|
*/
|
|
async function appendRow(sheetName, values) {
|
|
try {
|
|
const sheets = await getSheetsClient();
|
|
if (!sheets) {
|
|
console.log('[Google Sheets] Skipping sync — no credentials configured');
|
|
return;
|
|
}
|
|
|
|
await sheets.spreadsheets.values.append({
|
|
spreadsheetId: process.env.GOOGLE_SHEET_ID,
|
|
range: `${sheetName}!A:Z`,
|
|
valueInputOption: 'USER_ENTERED',
|
|
insertDataOption: 'INSERT_ROWS',
|
|
resource: { values: [values] },
|
|
});
|
|
|
|
console.log(`[Google Sheets] Synced row to "${sheetName}"`);
|
|
} catch (error) {
|
|
console.error(`[Google Sheets] Failed to sync to "${sheetName}":`, error.message);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sync a waitlist signup to the "Waitlist" sheet tab.
|
|
* Columns: Timestamp | Email | Name | Involvement
|
|
*/
|
|
function syncWaitlistSignup({ email, name, involvement }) {
|
|
// Fire and forget — don't await in the request handler
|
|
appendRow('Waitlist', [
|
|
new Date().toISOString(),
|
|
email,
|
|
name,
|
|
involvement || '',
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Sync an application to the "Applications" sheet tab.
|
|
* Columns: Timestamp | App ID | Status | First Name | Last Name | Email | Phone |
|
|
* Country | City | Attendance | Motivation | Contribution | How Heard |
|
|
* Referral | Scholarship | Scholarship Reason | Weeks/Dates
|
|
*/
|
|
function syncApplication(app) {
|
|
appendRow('Applications', [
|
|
new Date().toISOString(),
|
|
app.id || '',
|
|
'pending',
|
|
app.first_name || '',
|
|
app.last_name || '',
|
|
app.email || '',
|
|
app.phone || '',
|
|
app.country || '',
|
|
app.city || '',
|
|
app.attendance_type || '',
|
|
app.motivation || '',
|
|
app.contribution || '',
|
|
app.how_heard || '',
|
|
app.referral_name || '',
|
|
app.scholarship_needed ? 'Yes' : 'No',
|
|
app.scholarship_reason || '',
|
|
app.arrival_date && app.departure_date
|
|
? `${app.arrival_date} to ${app.departure_date}`
|
|
: '',
|
|
]);
|
|
}
|
|
|
|
module.exports = { syncWaitlistSignup, syncApplication };
|