Googstr/public/drive.js
Jason Tudisco 21093eb800 Initial commit: Goostr static site
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 08:37:10 -06:00

78 lines
2.6 KiB
JavaScript

// Google Drive AppData helpers. Files written to the appDataFolder space are
// invisible to the user in the regular Drive UI and only readable by the
// OAuth client (i.e. *this* app's client_id) that created them.
//
// Docs: https://developers.google.com/drive/api/guides/appdata
const DRIVE = "https://www.googleapis.com/drive/v3";
const UPLOAD = "https://www.googleapis.com/upload/drive/v3";
async function driveFetch(token, url, init = {}) {
const res = await fetch(url, {
...init,
headers: {
Authorization: `Bearer ${token}`,
...(init.headers || {}),
},
});
if (!res.ok) {
const text = await res.text();
throw new Error(`Drive API ${res.status}: ${text.slice(0, 300)}`);
}
return res;
}
// Find a file by exact name inside the appDataFolder. Returns the file id, or
// null if not found.
export async function findAppDataFile(token, name) {
const url =
`${DRIVE}/files?spaces=appDataFolder` +
`&q=${encodeURIComponent(`name = '${name.replace(/'/g, "\\'")}'`)}` +
`&fields=files(id,name,modifiedTime)`;
const res = await driveFetch(token, url);
const { files } = await res.json();
if (!files || files.length === 0) return null;
// If somehow multiple, prefer the most recent.
files.sort((a, b) => (b.modifiedTime || "").localeCompare(a.modifiedTime || ""));
return files[0].id;
}
export async function downloadFile(token, fileId) {
const res = await driveFetch(token, `${DRIVE}/files/${fileId}?alt=media`);
return res.text();
}
// Multipart upload of a small JSON blob to appDataFolder. If fileId is given,
// updates that file (preserving parents); otherwise creates a new file.
export async function uploadAppDataJson(token, name, content, fileId = null) {
const boundary = "goostr-" + Math.random().toString(36).slice(2);
const metadata = fileId
? { name }
: { name, parents: ["appDataFolder"] };
const body =
`--${boundary}\r\n` +
`Content-Type: application/json; charset=UTF-8\r\n\r\n` +
`${JSON.stringify(metadata)}\r\n` +
`--${boundary}\r\n` +
`Content-Type: application/json\r\n\r\n` +
`${content}\r\n` +
`--${boundary}--`;
const url = fileId
? `${UPLOAD}/files/${fileId}?uploadType=multipart`
: `${UPLOAD}/files?uploadType=multipart`;
const res = await driveFetch(token, url, {
method: fileId ? "PATCH" : "POST",
headers: { "Content-Type": `multipart/related; boundary=${boundary}` },
body,
});
return res.json();
}
export async function fetchUserInfo(token) {
const res = await driveFetch(token, "https://www.googleapis.com/oauth2/v3/userinfo");
return res.json(); // { sub, name, picture, email, ... }
}