174 lines
5.5 KiB
JavaScript
174 lines
5.5 KiB
JavaScript
// Code.gs — Google Apps Script Web App
|
|
// Deploy as: Web app, Execute as "Me", Who has access: "Anyone"
|
|
|
|
// ============================================
|
|
// CONFIGURATION — change these two, then create a NEW deployment version
|
|
// ============================================
|
|
var SECRET_KEY = '5f5bd4c1751f7582f870954da60cafe2'; // e.g. output of: openssl rand -hex 16
|
|
var ALLOWED_EMAIL = 'justiceawudi@outlook.com'; // must match "Login Email" in extension Settings
|
|
|
|
function doPost(e) {
|
|
try {
|
|
var data = JSON.parse(e.postData.contents);
|
|
|
|
if (!data.key || data.key !== SECRET_KEY) {
|
|
Logger.log('Unauthorized request - invalid key');
|
|
return ContentService.createTextOutput(
|
|
JSON.stringify({ error: 'Unauthorized' }),
|
|
).setMimeType(ContentService.MimeType.JSON);
|
|
}
|
|
|
|
if (data.email !== ALLOWED_EMAIL) {
|
|
Logger.log('Unauthorized request - invalid email: ' + data.email);
|
|
return ContentService.createTextOutput(
|
|
JSON.stringify({ error: 'Unauthorized email' }),
|
|
).setMimeType(ContentService.MimeType.JSON);
|
|
}
|
|
|
|
if (data.action === 'pollForLoginLink') {
|
|
var result = pollForMagicLink(data.requestId);
|
|
return ContentService.createTextOutput(
|
|
JSON.stringify(result),
|
|
).setMimeType(ContentService.MimeType.JSON);
|
|
}
|
|
|
|
return ContentService.createTextOutput(
|
|
JSON.stringify({ error: 'Unknown action' }),
|
|
).setMimeType(ContentService.MimeType.JSON);
|
|
} catch (err) {
|
|
Logger.log('Error in doPost: ' + err.message);
|
|
return ContentService.createTextOutput(
|
|
JSON.stringify({ error: err.message }),
|
|
).setMimeType(ContentService.MimeType.JSON);
|
|
}
|
|
}
|
|
|
|
// ============================================
|
|
// Poll for magic login link.
|
|
//
|
|
// No "to:" filter — the email is sent to your Outlook address and
|
|
// lands in Gmail via a forwarding rule, so its To/Delivered-To headers
|
|
// may still reference Outlook rather than Gmail. from + subject +
|
|
// newer_than + is:unread is specific enough since this only ever
|
|
// searches your own mailbox.
|
|
// ============================================
|
|
function pollForMagicLink(requestId) {
|
|
var maxAttempts = 4;
|
|
var pollInterval = 15000;
|
|
|
|
Logger.log('[' + requestId + '] Starting poll for login link...');
|
|
|
|
for (var attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
Logger.log('[' + requestId + '] Attempt ' + attempt + '/' + maxAttempts);
|
|
|
|
var searchQuery =
|
|
'from:talent.andela.com subject:"Andela Login Link" newer_than:5m is:unread';
|
|
var threads = GmailApp.search(searchQuery, 0, 5);
|
|
|
|
Logger.log(
|
|
'[' + requestId + '] Found ' + threads.length + ' matching threads',
|
|
);
|
|
|
|
if (threads.length > 0) {
|
|
var allMessages = [];
|
|
for (var i = 0; i < threads.length; i++) {
|
|
var messages = threads[i].getMessages();
|
|
for (var j = 0; j < messages.length; j++) allMessages.push(messages[j]);
|
|
}
|
|
|
|
allMessages.sort(function (a, b) {
|
|
return b.getDate().getTime() - a.getDate().getTime();
|
|
});
|
|
|
|
var latestMessage = allMessages[0];
|
|
var body = latestMessage.getBody();
|
|
|
|
Logger.log(
|
|
'[' + requestId + '] Processing email from: ' + latestMessage.getDate(),
|
|
);
|
|
|
|
var accessAccountPattern =
|
|
/<a\s+[^>]*href=["']([^"']+)["'][^>]*>\s*Access account\s*<\/a>/i;
|
|
var match = body.match(accessAccountPattern);
|
|
|
|
if (match && match[1]) {
|
|
var link = match[1]
|
|
.replace(/&/g, '&')
|
|
.replace(/=/g, '=')
|
|
.replace(/=/g, '=')
|
|
.replace(/&/g, '&');
|
|
|
|
Logger.log('[' + requestId + '] Found Access account link: ' + link);
|
|
latestMessage.markRead();
|
|
|
|
return {
|
|
success: true,
|
|
loginLink: link,
|
|
emailDate: latestMessage.getDate().toISOString(),
|
|
attempt: attempt,
|
|
};
|
|
}
|
|
|
|
Logger.log(
|
|
'[' + requestId + '] Email found but no Access account link detected',
|
|
);
|
|
}
|
|
|
|
if (attempt < maxAttempts) {
|
|
Logger.log('[' + requestId + '] Waiting ' + pollInterval / 1000 + 's...');
|
|
Utilities.sleep(pollInterval);
|
|
}
|
|
}
|
|
|
|
Logger.log(
|
|
'[' +
|
|
requestId +
|
|
'] No login link found after ' +
|
|
maxAttempts +
|
|
' attempts',
|
|
);
|
|
return {
|
|
success: false,
|
|
error: 'No login link found within timeout',
|
|
attempts: maxAttempts,
|
|
};
|
|
}
|
|
|
|
// ============================================
|
|
// Debug helpers — run manually from the Apps Script editor's function
|
|
// dropdown, then check the Executions log
|
|
// ============================================
|
|
function debugLoginEmails() {
|
|
var searchQuery =
|
|
'from:talent.andela.com subject:"Andela Login Link" newer_than:7d';
|
|
var threads = GmailApp.search(searchQuery, 0, 5);
|
|
Logger.log('Found ' + threads.length + ' emails');
|
|
|
|
for (var i = 0; i < threads.length; i++) {
|
|
var messages = threads[i].getMessages();
|
|
for (var j = 0; j < messages.length; j++) {
|
|
var msg = messages[j];
|
|
Logger.log('---');
|
|
Logger.log('Subject: ' + msg.getSubject());
|
|
Logger.log('From: ' + msg.getFrom());
|
|
Logger.log('To: ' + msg.getTo());
|
|
Logger.log('Date: ' + msg.getDate());
|
|
|
|
var body = msg.getBody();
|
|
var pattern =
|
|
/<a\s+[^>]*href=["']([^"']+)["'][^>]*>\s*Access account\s*<\/a>/i;
|
|
var match = body.match(pattern);
|
|
if (match) {
|
|
Logger.log('Link found: ' + match[1].substring(0, 100));
|
|
} else {
|
|
Logger.log('Access account link not found');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function testExtraction() {
|
|
var result = pollForMagicLink('test-' + Date.now());
|
|
Logger.log(JSON.stringify(result, null, 2));
|
|
}
|