-function base64UrlEncode(s) {
- return btoa(s)
- .replaceAll('=', '')
- .replaceAll('+', '-')
- .replaceAll('/', '_');
-}
-
-async function sign(message, privateKey) {
- const cleanedKey = privateKey
- .replace('-----BEGIN PRIVATE KEY-----', '')
- .replace('-----END PRIVATE KEY-----', '')
- .replaceAll(/\r\n|\n|\r/g, '');
- const keyBuffer = Uint8Array.from(atob(cleanedKey), c => c.charCodeAt(0));
-
- const signingKey = await crypto.subtle.importKey(
- 'pkcs8',
- keyBuffer,
- {
- name: 'RSASSA-PKCS1-V1_5',
- hash: 'SHA-256'
- },
- false,
- ['sign']
- );
-
- const signatureBuffer = await crypto.subtle.sign('RSASSA-PKCS1-V1_5', signingKey, new TextEncoder().encode(message));
- return base64UrlEncode(String.fromCharCode(...new Uint8Array(signatureBuffer)));
-}
-
-async function getGoogleAuthToken(clientEmail, privateKey, scopes) {
- const header = base64UrlEncode(JSON.stringify({
- alg: 'RS256',
- typ: 'JWT'
- }));
-
- const now = Math.round(Date.now() / 1000);
- const expires = now + 3600;
- const payload = base64UrlEncode(JSON.stringify({
- iss: clientEmail,
- aud: 'https://oauth2.googleapis.com/token',
- exp: expires,
- iat: now,
- scope: scopes
- }));
-
- const unsignedJwt = `${header}.${payload}`;
- const signature = await sign(unsignedJwt, privateKey);
- const jwt = `${unsignedJwt}.${signature}`;
-
- const response = await fetch('https://oauth2.googleapis.com/token', {
- method: 'POST',
- body: JSON.stringify({
- assertion: jwt,
- grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer'
- })
- });
- const oauth = await response.json();
-
- return oauth.access_token;
-}
+import { getGoogleAuthToken } from './auth.js';
export async function onRequestGet({ request, env }) {
const url = new URL(request.url);
--- /dev/null
+import { getGoogleAuthToken } from './auth.js';
+
+export async function onRequestGet({ env }) {
+ const entries = [];
+ const updatedDates = [];
+ const token = await getGoogleAuthToken(env.EMAIL, env.PRIVATE_KEY, env.SCOPES);
+ const sheetUrl = 'https://sheets.googleapis.com/v4/spreadsheets/1pfGF8yBu3D0GPTezygLuzu3Cif8SkjhtG98nL-czlhc/values/B3:I';
+ await fetch(sheetUrl, { headers: { Authorization: `Bearer ${token}` } })
+ .then(r => r.json())
+ .then(googleSheet => {
+ for (const article of googleSheet.values) {
+ entries.push(`<entry>
+ <id>urn:google.com:${article[2]}</id>
+ <title>${article[0]}</title>
+ <summary>${article[1]}</summary>
+ <link href="https://ardislu.dev${article[4]}"/>
+ <published>${new Date(article[5]).toISOString()}</published>
+ <updated>${new Date(article[6]).toISOString()}</updated>
+ </entry>`);
+ updatedDates.push(new Date(article[6]));
+ }
+ });
+
+ const feed = `<?xml version="1.0" encoding="utf-8"?>
+ <feed xmlns="http://www.w3.org/2005/Atom">
+
+ <id>https://ardislu.dev/</id>
+ <title>ardislu.dev</title>
+ <subtitle>Notes on web development, crypto, self-hosting, and tech in general.</subtitle>
+ <author>
+ <name>Ardis Lu</name>
+ </author>
+ <link href="https://ardislu.dev/"/>
+ <updated>${new Date(Math.max(...updatedDates)).toISOString()}</updated>
+
+ ${entries.join('')}
+
+ </feed>`
+
+ return new Response(feed, { headers: { 'Content-Type': 'application/atom+xml' } });
+}
\ No newline at end of file
--- /dev/null
+function base64UrlEncode(s) {
+ return btoa(s)
+ .replaceAll('=', '')
+ .replaceAll('+', '-')
+ .replaceAll('/', '_');
+}
+
+async function sign(message, privateKey) {
+ const cleanedKey = privateKey
+ .replace('-----BEGIN PRIVATE KEY-----', '')
+ .replace('-----END PRIVATE KEY-----', '')
+ .replaceAll(/\r\n|\n|\r/g, '');
+ const keyBuffer = Uint8Array.from(atob(cleanedKey), c => c.charCodeAt(0));
+
+ const signingKey = await crypto.subtle.importKey(
+ 'pkcs8',
+ keyBuffer,
+ {
+ name: 'RSASSA-PKCS1-V1_5',
+ hash: 'SHA-256'
+ },
+ false,
+ ['sign']
+ );
+
+ const signatureBuffer = await crypto.subtle.sign('RSASSA-PKCS1-V1_5', signingKey, new TextEncoder().encode(message));
+ return base64UrlEncode(String.fromCharCode(...new Uint8Array(signatureBuffer)));
+}
+
+export async function getGoogleAuthToken(clientEmail, privateKey, scopes) {
+ const header = base64UrlEncode(JSON.stringify({
+ alg: 'RS256',
+ typ: 'JWT'
+ }));
+
+ const now = Math.round(Date.now() / 1000);
+ const expires = now + 3600;
+ const payload = base64UrlEncode(JSON.stringify({
+ iss: clientEmail,
+ aud: 'https://oauth2.googleapis.com/token',
+ exp: expires,
+ iat: now,
+ scope: scopes
+ }));
+
+ const unsignedJwt = `${header}.${payload}`;
+ const signature = await sign(unsignedJwt, privateKey);
+ const jwt = `${unsignedJwt}.${signature}`;
+
+ const response = await fetch('https://oauth2.googleapis.com/token', {
+ method: 'POST',
+ body: JSON.stringify({
+ assertion: jwt,
+ grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer'
+ })
+ });
+ const oauth = await response.json();
+
+ return oauth.access_token;
+}
\ No newline at end of file