104 lines
3.3 KiB
TypeScript
104 lines
3.3 KiB
TypeScript
import { google } from 'googleapis';
|
|
import { env } from '$env/dynamic/private';
|
|
import quotedPrintable from 'quoted-printable'; // tiny, zero-dep package
|
|
|
|
export const scopes = ['https://www.googleapis.com/auth/gmail.send'];
|
|
|
|
export function getOAuthClient() {
|
|
return new google.auth.OAuth2(
|
|
env.GOOGLE_CLIENT_ID,
|
|
env.GOOGLE_CLIENT_SECRET,
|
|
env.GOOGLE_REDIRECT_URI
|
|
);
|
|
}
|
|
|
|
export function createAuthUrl() {
|
|
return getOAuthClient().generateAuthUrl({
|
|
access_type: 'offline',
|
|
prompt: 'consent',
|
|
scope: scopes
|
|
});
|
|
}
|
|
|
|
export async function exchangeCodeForTokens(code: string) {
|
|
const { tokens } = await getOAuthClient().getToken(code);
|
|
if (!tokens.refresh_token) throw new Error('No refresh_token returned');
|
|
return tokens.refresh_token;
|
|
}
|
|
|
|
export async function sendGmail(
|
|
refreshToken: string,
|
|
{ to, subject, text, qr_code }: { to: string; subject: string; text: string; qr_code: string }
|
|
) {
|
|
const oauth = getOAuthClient();
|
|
oauth.setCredentials({ refresh_token: refreshToken });
|
|
|
|
const gmail = google.gmail({ version: 'v1', auth: oauth });
|
|
|
|
const message_html =
|
|
`<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<style>
|
|
@import url('https://fonts.googleapis.com/css2?family=Lato&display=swap');
|
|
</style>
|
|
</head>
|
|
<body style="font-family: 'Lato', sans-serif; background-color: #f9f9f9; padding: 20px; margin: 0;">
|
|
<div style="max-width: 600px; margin: auto; background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.05);">
|
|
<p style="white-space: pre-line;font-size: 16px; line-height: 1.5; color: #333;">${text}</p>
|
|
<img src="cid:qrCode1" alt="QR Code" style="display: block; margin: 20px auto; max-width: 50%; min-width: 200px; height: auto;" />
|
|
<div style="width: 100%; display: flex; flex-direction: row; justify-content: space-between">
|
|
<div style="height: 4px; width: 20%; background: #00aeef;"></div>
|
|
<div style="height: 4px; width: 20%; background: #ec008c;"></div>
|
|
<div style="height: 4px; width: 20%; background: #7ac143;"></div>
|
|
<div style="height: 4px; width: 20%; background: #f47b20;"></div>
|
|
<div style="height: 4px; width: 20%; background: #2e3192;"></div>
|
|
</div>
|
|
<div style="font-size: 12px; color: #999; padding-top: 0px; margin-top: 10px; line-height: 1.5; ">
|
|
<p>This email has been generated with the help of *insert software name*</p>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
</html>`;
|
|
|
|
|
|
const boundary = 'BOUNDARY';
|
|
const nl = '\r\n'; // RFC-5322 line ending
|
|
|
|
const htmlQP = quotedPrintable.encode(message_html);
|
|
const qrLines = qr_code.replace(/.{1,76}/g, '$&' + nl);
|
|
|
|
const rawParts = [
|
|
'MIME-Version: 1.0',
|
|
`To: ${to}`,
|
|
`Subject: ${subject}`,
|
|
`Content-Type: multipart/related; boundary="${boundary}"`,
|
|
'',
|
|
`--${boundary}`,
|
|
'Content-Type: text/html; charset="UTF-8"',
|
|
'Content-Transfer-Encoding: quoted-printable',
|
|
'',
|
|
htmlQP,
|
|
'',
|
|
`--${boundary}`,
|
|
'Content-Type: image/png',
|
|
'Content-Transfer-Encoding: base64',
|
|
'Content-ID: <qrCode1>',
|
|
'Content-Disposition: inline; filename="qr.png"',
|
|
'',
|
|
qrLines,
|
|
'',
|
|
`--${boundary}--`,
|
|
''
|
|
];
|
|
|
|
const rawMessage = rawParts.join(nl);
|
|
|
|
const raw = Buffer.from(rawMessage).toString('base64url');
|
|
|
|
await gmail.users.messages.send({
|
|
userId: 'me',
|
|
requestBody: { raw }
|
|
});
|
|
}
|