34 lines
2.2 KiB
TypeScript
34 lines
2.2 KiB
TypeScript
// A Google 401 / 403 that says what is missing (SERVICE_DISABLED, PERMISSION_DENIED, a refused key) becomes one
|
|
// GoogleAccessError with the exact API or grant and the link to fix it. It is never retried: the same call fails the
|
|
// same way until the owner (or the credential broker) changes something. With the broker as the token source the text
|
|
// also names the Play permissions the broker lists for the package and the broker-side fix.
|
|
import { brokerConfig, brokerPlayInfo, grantsOf, redact } from './broker';
|
|
import { findingText, type GoogleFinding, parseGoogleError } from './google-errors';
|
|
|
|
export class GoogleAccessError extends Error {
|
|
/** Never sent again: retry loops and the upsert layer check this. */
|
|
readonly noRetry = true;
|
|
constructor(msg: string, readonly finding: GoogleFinding) { super(msg); }
|
|
}
|
|
|
|
/** The GoogleAccessError for a failed call's text, or null when the text is another error. */
|
|
export async function accessError(text: string, o: { app?: string | null; root?: string; source?: 'broker' | 'file' } = {}): Promise<GoogleAccessError | null> {
|
|
const f = parseGoogleError(text, { app: o.app ?? null });
|
|
if (!f || f.kind === 'quota') return null;
|
|
const lines = [redact(text), findingText(f, { withUrl: true })];
|
|
if (o.source === 'broker') {
|
|
if (f.kind === 'auth-revoked') lines.push('The token came from the credential broker: its key for this service account was revoked or deleted at Google; rotate the key on the broker.');
|
|
if (f.kind === 'api-disabled') lines.push(`Or enable it on the credential broker (google_api_enable ${f.projectId ?? '<project>'} ${f.service}).`);
|
|
if (f.kind === 'no-app-access' && o.root && o.app) {
|
|
try {
|
|
const cfg = brokerConfig(o.root);
|
|
const r = cfg ? await brokerPlayInfo(cfg, o.app) : null;
|
|
if (r?.kind === 'ok') {
|
|
const g = grantsOf(r.info);
|
|
lines.push(`The credential broker lists ${g ? (g.length ? g.join(', ') : 'no Play permissions') : 'no permission data'} for ${r.info.account ?? 'the service account'} on ${o.app}: grant the missing one there (google_play_grant).`);
|
|
} else if (r) lines.push(r.message);
|
|
} catch { /* the Google error stands on its own */ }
|
|
}
|
|
}
|
|
return new GoogleAccessError(lines.join('\n'), f);
|
|
}
|