Fix Generate Matrix 404: drive.file can't see the pre-existing template

drive.file scope only grants access to files the app itself creates —
it deliberately can't see a file it didn't create, even one the
signed-in user can otherwise view. Drive's API returns 404 (not 403)
for a file outside the token's grant, which is indistinguishable from
a bad file ID — that's what surfaced as "File not found:
1tSG1BxqLwmMtwFnaHozaLqVsKlmk_QsCXRorIxQBcMo" on the very first API
call (confirmed nothing was created in Drive yet, matching a rejected
initial copy rather than a later step).

Added drive.readonly alongside drive.file: readonly covers reading
the pre-existing template to copy it, drive.file continues to cover
the generated copies and everything written to them afterward.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SBzcNkW7JcYipnAX6HfgmK
This commit is contained in:
2026-09-04 18:18:26 -05:00
co-authored by Claude Sonnet 5
parent 06b0cf4418
commit 5bb6268080
+9 -3
View File
@@ -4,15 +4,21 @@
Auth: Google Identity Services (GIS) token client — the viewer signs in Auth: Google Identity Services (GIS) token client — the viewer signs in
with their own Google account (a real OAuth popup), no backend, no with their own Google account (a real OAuth popup), no backend, no
client secret. Scopes are drive.file (only files this app creates, not client secret. Scopes: drive.file (files this app creates — covers the
full Drive access) and spreadsheets. */ generated matrix copies and everything written to them afterward) plus
drive.readonly (needed for exactly one thing: reading the pre-existing
template to copy it — drive.file deliberately can't see a file the app
didn't create, which is why the first version of this only requesting
drive.file 404'd on the template with no way to even see it existed —
Drive's API returns 404, not 403, for a file outside the token's grant,
so it looks identical to a bad file ID) and spreadsheets. */
import { detectFormat, parseGwAppV11, parseWarOrganV11, parseV11List, generateDiscordText, buildAbbreviationIndex } from "./vendor/40k-compactor/index.js"; import { detectFormat, parseGwAppV11, parseWarOrganV11, parseV11List, generateDiscordText, buildAbbreviationIndex } from "./vendor/40k-compactor/index.js";
import skippableWargear from "./vendor/40k-compactor/skippable_wargear.json"; import skippableWargear from "./vendor/40k-compactor/skippable_wargear.json";
const GOOGLE_CLIENT_ID = "698705216189-rqbg3f7vlosi3077c3n410a7lpmsn16r.apps.googleusercontent.com"; const GOOGLE_CLIENT_ID = "698705216189-rqbg3f7vlosi3077c3n410a7lpmsn16r.apps.googleusercontent.com";
const TEMPLATE_FILE_ID = "1tSG1BxqLwmMtwFnaHozaLqVsKlmk_QsCXRorIxQBcMo"; const TEMPLATE_FILE_ID = "1tSG1BxqLwmMtwFnaHozaLqVsKlmk_QsCXRorIxQBcMo";
const SCOPES = "https://www.googleapis.com/auth/drive.file https://www.googleapis.com/auth/spreadsheets"; const SCOPES = "https://www.googleapis.com/auth/drive.file https://www.googleapis.com/auth/drive.readonly https://www.googleapis.com/auth/spreadsheets";
const DRIVE_API = "https://www.googleapis.com/drive/v3"; const DRIVE_API = "https://www.googleapis.com/drive/v3";
const SHEETS_API = "https://sheets.googleapis.com/v4/spreadsheets"; const SHEETS_API = "https://sheets.googleapis.com/v4/spreadsheets";