hatchkit 0.2.3 → 0.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config.d.ts +3 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +136 -40
- package/dist/config.js.map +1 -1
- package/dist/index.js +103 -29
- package/dist/index.js.map +1 -1
- package/dist/provision/index.d.ts +8 -0
- package/dist/provision/index.d.ts.map +1 -1
- package/dist/provision/index.js +303 -51
- package/dist/provision/index.js.map +1 -1
- package/dist/provision/search-console.d.ts +12 -9
- package/dist/provision/search-console.d.ts.map +1 -1
- package/dist/provision/search-console.js +36 -20
- package/dist/provision/search-console.js.map +1 -1
- package/dist/utils/cloudflare-api.d.ts.map +1 -1
- package/dist/utils/cloudflare-api.js +32 -17
- package/dist/utils/cloudflare-api.js.map +1 -1
- package/package.json +3 -3
package/dist/config.d.ts
CHANGED
|
@@ -112,8 +112,8 @@ export interface GoogleSearchConsoleMeta extends ProviderStatus {
|
|
|
112
112
|
/** Scopes granted to the stored refresh token. Non-sensitive; useful
|
|
113
113
|
* in status/doctor output when a user authorized only one API. */
|
|
114
114
|
scopes?: string[];
|
|
115
|
-
/** `hatchkit-pkce` uses Hatchkit
|
|
116
|
-
* `byo-client` is the
|
|
115
|
+
/** `hatchkit-pkce` uses a Hatchkit OAuth client supplied through env vars.
|
|
116
|
+
* `byo-client` is the user-owned Google Cloud OAuth client path. */
|
|
117
117
|
oauthMode?: "hatchkit-pkce" | "byo-client";
|
|
118
118
|
}
|
|
119
119
|
export interface StripeMeta extends ProviderStatus {
|
|
@@ -291,6 +291,7 @@ export declare function ensurePlausible(): Promise<PlausibleConfig>;
|
|
|
291
291
|
export declare function getPlausibleConfig(): Promise<PlausibleConfig | null>;
|
|
292
292
|
export declare function ensureResend(): Promise<ResendConfig>;
|
|
293
293
|
export declare function getResendConfig(): Promise<ResendConfig | null>;
|
|
294
|
+
export declare function extractGoogleOAuthCodeFromCallbackUrl(rawUrl: string, expectedState: string): string;
|
|
294
295
|
export declare function refreshGoogleSearchConsoleAccessToken(cfg: GoogleSearchConsoleConfig): Promise<string>;
|
|
295
296
|
export declare function ensureGoogleSearchConsole(): Promise<GoogleSearchConsoleConfig>;
|
|
296
297
|
export declare function getGoogleSearchConsoleConfig(): Promise<GoogleSearchConsoleConfig | null>;
|
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAIA,OAAO,IAAI,MAAM,MAAM,CAAC;AAcxB;;;;;;;;;;;;8DAY8D;AAC9D,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,GAChB,MAAM,GAAG,IAAI,CAaf;AAeD;;;;;;;;;;;gBAWgB;AAChB,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CA0EvE;AAmBD;;sDAEsD;AACtD,wBAAsB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAkBxE;
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
package/dist/config.js
CHANGED
|
@@ -160,6 +160,23 @@ export async function confirmPastedSecret(label) {
|
|
|
160
160
|
return value;
|
|
161
161
|
}
|
|
162
162
|
}
|
|
163
|
+
async function confirmOptionalPastedSecret(label) {
|
|
164
|
+
for (;;) {
|
|
165
|
+
const raw = await password({ message: `${label} (optional, leave blank to skip):` });
|
|
166
|
+
const value = sanitizePastedSecret(raw);
|
|
167
|
+
if (!value)
|
|
168
|
+
return undefined;
|
|
169
|
+
const preview = value.length <= 8
|
|
170
|
+
? `${"*".repeat(value.length)} (${value.length} chars — looks short?)`
|
|
171
|
+
: `${value.slice(0, 4)}…${value.slice(-4)} (${value.length} chars)`;
|
|
172
|
+
const ok = await confirm({
|
|
173
|
+
message: `Looks like: ${chalk.cyan(preview)} — use this?`,
|
|
174
|
+
default: true,
|
|
175
|
+
});
|
|
176
|
+
if (ok)
|
|
177
|
+
return value;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
163
180
|
// ---------------------------------------------------------------------------
|
|
164
181
|
// Config store
|
|
165
182
|
// ---------------------------------------------------------------------------
|
|
@@ -1026,24 +1043,17 @@ const GOOGLE_SEARCH_CONSOLE_SCOPES = [
|
|
|
1026
1043
|
"https://www.googleapis.com/auth/webmasters",
|
|
1027
1044
|
"https://www.googleapis.com/auth/siteverification.verify_only",
|
|
1028
1045
|
];
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
// HATCHKIT_GOOGLE_SEARCH_CONSOLE_CLIENT_ID / _CLIENT_SECRET to exercise the
|
|
1035
|
-
// packaged PKCE path.
|
|
1036
|
-
const PACKAGED_GOOGLE_SEARCH_CONSOLE_CLIENT_ID = "932614455438-s0ih891al5pkeo4aeafekf01t6pbqd21.apps.googleusercontent.com";
|
|
1037
|
-
const PACKAGED_GOOGLE_SEARCH_CONSOLE_CLIENT_SECRET = "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";
|
|
1046
|
+
const GOOGLE_OAUTH_CALLBACK_PATH = "/oauth/google/callback";
|
|
1047
|
+
const GOOGLE_OAUTH_CALLBACK_WAIT_MS = 120_000;
|
|
1048
|
+
const GOOGLE_OAUTH_TOKEN_TIMEOUT_MS = 30_000;
|
|
1049
|
+
const HATCHKIT_GOOGLE_SEARCH_CONSOLE_CLIENT_ID_ENV = "HATCHKIT_GOOGLE_SEARCH_CONSOLE_CLIENT_ID";
|
|
1050
|
+
const HATCHKIT_GOOGLE_SEARCH_CONSOLE_CLIENT_SECRET_ENV = "HATCHKIT_GOOGLE_SEARCH_CONSOLE_CLIENT_SECRET";
|
|
1038
1051
|
function hatchkitGoogleSearchConsoleClientId() {
|
|
1039
|
-
const configured = process.env
|
|
1040
|
-
PACKAGED_GOOGLE_SEARCH_CONSOLE_CLIENT_ID.trim();
|
|
1052
|
+
const configured = process.env[HATCHKIT_GOOGLE_SEARCH_CONSOLE_CLIENT_ID_ENV]?.trim();
|
|
1041
1053
|
return configured || null;
|
|
1042
1054
|
}
|
|
1043
1055
|
function hatchkitGoogleSearchConsoleClientSecret() {
|
|
1044
|
-
return
|
|
1045
|
-
PACKAGED_GOOGLE_SEARCH_CONSOLE_CLIENT_SECRET.trim() ||
|
|
1046
|
-
undefined);
|
|
1056
|
+
return process.env[HATCHKIT_GOOGLE_SEARCH_CONSOLE_CLIENT_SECRET_ENV]?.trim() || undefined;
|
|
1047
1057
|
}
|
|
1048
1058
|
const GOOGLE_SEARCH_CONSOLE_SCOPE_REQUIREMENTS = [
|
|
1049
1059
|
{
|
|
@@ -1075,6 +1085,65 @@ function createPkcePair() {
|
|
|
1075
1085
|
const challenge = base64Url(createHash("sha256").update(verifier).digest());
|
|
1076
1086
|
return { verifier, challenge };
|
|
1077
1087
|
}
|
|
1088
|
+
function sleep(ms) {
|
|
1089
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1090
|
+
}
|
|
1091
|
+
export function extractGoogleOAuthCodeFromCallbackUrl(rawUrl, expectedState) {
|
|
1092
|
+
const trimmed = rawUrl.trim();
|
|
1093
|
+
if (!trimmed)
|
|
1094
|
+
throw new Error("Redirect URL is empty.");
|
|
1095
|
+
let url;
|
|
1096
|
+
try {
|
|
1097
|
+
url = new URL(trimmed);
|
|
1098
|
+
}
|
|
1099
|
+
catch {
|
|
1100
|
+
throw new Error("Could not parse the pasted Google redirect URL.");
|
|
1101
|
+
}
|
|
1102
|
+
if (url.pathname !== GOOGLE_OAUTH_CALLBACK_PATH) {
|
|
1103
|
+
throw new Error(`Expected a Google redirect URL ending in ${GOOGLE_OAUTH_CALLBACK_PATH}, got ${url.pathname}.`);
|
|
1104
|
+
}
|
|
1105
|
+
const gotState = url.searchParams.get("state");
|
|
1106
|
+
const code = url.searchParams.get("code");
|
|
1107
|
+
const error = url.searchParams.get("error");
|
|
1108
|
+
if (gotState !== expectedState)
|
|
1109
|
+
throw new Error("Google OAuth state mismatch.");
|
|
1110
|
+
if (error || !code)
|
|
1111
|
+
throw new Error(`Google OAuth failed: ${error ?? "missing code"}.`);
|
|
1112
|
+
return code;
|
|
1113
|
+
}
|
|
1114
|
+
async function waitForGoogleOAuthCode(args) {
|
|
1115
|
+
const callbackResult = args.codePromise.then((code) => ({ kind: "code", code }), (error) => ({ kind: "error", error: error }));
|
|
1116
|
+
while (true) {
|
|
1117
|
+
const result = await Promise.race([
|
|
1118
|
+
callbackResult,
|
|
1119
|
+
sleep(GOOGLE_OAUTH_CALLBACK_WAIT_MS).then(() => ({ kind: "timeout" })),
|
|
1120
|
+
]);
|
|
1121
|
+
if (result.kind === "code")
|
|
1122
|
+
return result.code;
|
|
1123
|
+
if (result.kind === "error")
|
|
1124
|
+
throw result.error;
|
|
1125
|
+
console.log(chalk.yellow("\n Still waiting for Google to redirect your browser back to the local callback server."));
|
|
1126
|
+
console.log(chalk.dim(` Expected callback: ${chalk.cyan(args.redirectUri)}`));
|
|
1127
|
+
console.log(chalk.dim(' If your browser ended on a localhost URL, a blank page, or a "site can\'t be reached" page, paste the full address below.'));
|
|
1128
|
+
if (!process.stdin.isTTY) {
|
|
1129
|
+
throw new Error("Timed out waiting for the Google OAuth callback. Re-run in an interactive terminal, or copy the final localhost redirect URL from your browser when prompted.");
|
|
1130
|
+
}
|
|
1131
|
+
const pasted = (await input({
|
|
1132
|
+
message: "Google redirect URL (leave blank to keep waiting):",
|
|
1133
|
+
})).trim();
|
|
1134
|
+
if (!pasted) {
|
|
1135
|
+
console.log(chalk.dim(" Keeping the callback server open. Finish the browser flow."));
|
|
1136
|
+
continue;
|
|
1137
|
+
}
|
|
1138
|
+
try {
|
|
1139
|
+
return extractGoogleOAuthCodeFromCallbackUrl(pasted, args.state);
|
|
1140
|
+
}
|
|
1141
|
+
catch (err) {
|
|
1142
|
+
console.log(chalk.yellow(` ${err.message}`));
|
|
1143
|
+
console.log(chalk.dim(" Try pasting the final localhost callback URL, including the code and state."));
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1078
1147
|
async function exchangeGoogleCode(args) {
|
|
1079
1148
|
const body = new URLSearchParams({
|
|
1080
1149
|
client_id: args.clientId,
|
|
@@ -1086,18 +1155,25 @@ async function exchangeGoogleCode(args) {
|
|
|
1086
1155
|
body.set("client_secret", args.clientSecret);
|
|
1087
1156
|
if (args.codeVerifier)
|
|
1088
1157
|
body.set("code_verifier", args.codeVerifier);
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1158
|
+
let res;
|
|
1159
|
+
try {
|
|
1160
|
+
res = await fetch("https://oauth2.googleapis.com/token", {
|
|
1161
|
+
method: "POST",
|
|
1162
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
1163
|
+
body,
|
|
1164
|
+
signal: AbortSignal.timeout(GOOGLE_OAUTH_TOKEN_TIMEOUT_MS),
|
|
1165
|
+
});
|
|
1166
|
+
}
|
|
1167
|
+
catch (err) {
|
|
1168
|
+
throw new Error(`Google OAuth token exchange failed: ${err.message}`);
|
|
1169
|
+
}
|
|
1094
1170
|
const json = (await res.json().catch(() => null));
|
|
1095
1171
|
if (!res.ok || !json?.access_token) {
|
|
1096
1172
|
const msg = json?.error_description ?? json?.error ?? `HTTP ${res.status}`;
|
|
1097
1173
|
if (/client_secret is missing/i.test(msg) && !args.clientSecret) {
|
|
1098
1174
|
throw new Error("Google OAuth token exchange failed: client_secret is missing. " +
|
|
1099
|
-
"
|
|
1100
|
-
|
|
1175
|
+
"Desktop OAuth clients using PKCE normally do not require one. " +
|
|
1176
|
+
`Verify the OAuth client type is Desktop app, or set ${HATCHKIT_GOOGLE_SEARCH_CONSOLE_CLIENT_SECRET_ENV} / re-run setup with the optional secret as a fallback.`);
|
|
1101
1177
|
}
|
|
1102
1178
|
throw new Error(`Google OAuth token exchange failed: ${msg}`);
|
|
1103
1179
|
}
|
|
@@ -1121,7 +1197,7 @@ async function runGoogleOAuthLoopback(args) {
|
|
|
1121
1197
|
});
|
|
1122
1198
|
const server = createServer((req, res) => {
|
|
1123
1199
|
const url = new URL(req.url ?? "/", redirectUri);
|
|
1124
|
-
if (url.pathname !==
|
|
1200
|
+
if (url.pathname !== GOOGLE_OAUTH_CALLBACK_PATH) {
|
|
1125
1201
|
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
1126
1202
|
res.end("Not found");
|
|
1127
1203
|
return;
|
|
@@ -1174,8 +1250,12 @@ async function runGoogleOAuthLoopback(args) {
|
|
|
1174
1250
|
}
|
|
1175
1251
|
console.log(chalk.dim("\n Open this URL in your browser, approve access, then return here:"));
|
|
1176
1252
|
console.log(chalk.cyan(` ${authUrl.toString()}\n`));
|
|
1253
|
+
console.log(chalk.dim(` Waiting for Google to redirect your browser to ${chalk.cyan(redirectUri)}. ` +
|
|
1254
|
+
"If the browser cannot reach localhost, Hatchkit will ask you to paste the final redirect URL."));
|
|
1177
1255
|
try {
|
|
1178
|
-
const code = await codePromise;
|
|
1256
|
+
const code = await waitForGoogleOAuthCode({ codePromise, redirectUri, state });
|
|
1257
|
+
console.log(chalk.green(" ✓ Google OAuth callback received"));
|
|
1258
|
+
console.log(chalk.dim(" Exchanging authorization code for a refresh token..."));
|
|
1179
1259
|
const token = await exchangeGoogleCode({
|
|
1180
1260
|
clientId: args.clientId,
|
|
1181
1261
|
clientSecret: args.clientSecret,
|
|
@@ -1188,6 +1268,7 @@ async function runGoogleOAuthLoopback(args) {
|
|
|
1188
1268
|
}
|
|
1189
1269
|
const scopes = token.scope?.split(/\s+/).filter(Boolean) ?? GOOGLE_SEARCH_CONSOLE_SCOPES;
|
|
1190
1270
|
assertGoogleSearchConsoleScopes(scopes);
|
|
1271
|
+
console.log(chalk.green(" ✓ Google granted Search Console + Site Verification access"));
|
|
1191
1272
|
return {
|
|
1192
1273
|
refreshToken: token.refresh_token,
|
|
1193
1274
|
scopes,
|
|
@@ -1205,18 +1286,25 @@ export async function refreshGoogleSearchConsoleAccessToken(cfg) {
|
|
|
1205
1286
|
});
|
|
1206
1287
|
if (cfg.clientSecret)
|
|
1207
1288
|
body.set("client_secret", cfg.clientSecret);
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1289
|
+
let res;
|
|
1290
|
+
try {
|
|
1291
|
+
res = await fetch("https://oauth2.googleapis.com/token", {
|
|
1292
|
+
method: "POST",
|
|
1293
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
1294
|
+
body,
|
|
1295
|
+
signal: AbortSignal.timeout(GOOGLE_OAUTH_TOKEN_TIMEOUT_MS),
|
|
1296
|
+
});
|
|
1297
|
+
}
|
|
1298
|
+
catch (err) {
|
|
1299
|
+
throw new Error(`Google refresh token failed: ${err.message}`);
|
|
1300
|
+
}
|
|
1213
1301
|
const json = (await res.json().catch(() => null));
|
|
1214
1302
|
if (!res.ok || !json?.access_token) {
|
|
1215
1303
|
const msg = json?.error_description ?? json?.error ?? `HTTP ${res.status}`;
|
|
1216
1304
|
if (/client_secret is missing/i.test(msg) && !cfg.clientSecret) {
|
|
1217
1305
|
throw new Error("Google refresh token failed: client_secret is missing. " +
|
|
1218
|
-
"
|
|
1219
|
-
|
|
1306
|
+
"Desktop OAuth clients using PKCE normally do not require one. " +
|
|
1307
|
+
`Verify the OAuth client type is Desktop app, or set ${HATCHKIT_GOOGLE_SEARCH_CONSOLE_CLIENT_SECRET_ENV} / re-run setup with the optional secret as a fallback.`);
|
|
1220
1308
|
}
|
|
1221
1309
|
throw new Error(`Google refresh token failed: ${msg}`);
|
|
1222
1310
|
}
|
|
@@ -1228,7 +1316,7 @@ export async function ensureGoogleSearchConsole() {
|
|
|
1228
1316
|
const existingClientSecret = await getSecret(SECRET_KEYS.googleSearchConsoleClientSecret);
|
|
1229
1317
|
const existingRefreshToken = await getSecret(SECRET_KEYS.googleSearchConsoleRefreshToken);
|
|
1230
1318
|
const hatchkitClientId = hatchkitGoogleSearchConsoleClientId();
|
|
1231
|
-
const existingMode = existing?.oauthMode ?? (
|
|
1319
|
+
const existingMode = existing?.oauthMode ?? (existingClientId ? "byo-client" : "hatchkit-pkce");
|
|
1232
1320
|
if (existing?.status === "configured" && existingRefreshToken) {
|
|
1233
1321
|
if (existingMode === "hatchkit-pkce" && hatchkitClientId) {
|
|
1234
1322
|
return {
|
|
@@ -1239,12 +1327,12 @@ export async function ensureGoogleSearchConsole() {
|
|
|
1239
1327
|
refreshToken: existingRefreshToken,
|
|
1240
1328
|
};
|
|
1241
1329
|
}
|
|
1242
|
-
if (existingClientId
|
|
1330
|
+
if (existingClientId) {
|
|
1243
1331
|
return {
|
|
1244
1332
|
...existing,
|
|
1245
1333
|
oauthMode: "byo-client",
|
|
1246
1334
|
clientId: existingClientId,
|
|
1247
|
-
clientSecret: existingClientSecret,
|
|
1335
|
+
clientSecret: existingClientSecret ?? undefined,
|
|
1248
1336
|
refreshToken: existingRefreshToken,
|
|
1249
1337
|
};
|
|
1250
1338
|
}
|
|
@@ -1260,22 +1348,22 @@ export async function ensureGoogleSearchConsole() {
|
|
|
1260
1348
|
clientId = hatchkitClientId;
|
|
1261
1349
|
clientSecret = hatchkitGoogleSearchConsoleClientSecret();
|
|
1262
1350
|
oauthMode = "hatchkit-pkce";
|
|
1263
|
-
console.log(chalk.dim(
|
|
1351
|
+
console.log(chalk.dim(` Using the Google OAuth desktop client from ${HATCHKIT_GOOGLE_SEARCH_CONSOLE_CLIENT_ID_ENV} with PKCE.`));
|
|
1264
1352
|
if (!clientSecret) {
|
|
1265
|
-
console.log(chalk.yellow(
|
|
1353
|
+
console.log(chalk.yellow(` If Google rejects the token exchange with \`client_secret is missing\`, set ${HATCHKIT_GOOGLE_SEARCH_CONSOLE_CLIENT_SECRET_ENV} and retry.`));
|
|
1266
1354
|
}
|
|
1267
1355
|
}
|
|
1268
1356
|
else {
|
|
1269
1357
|
oauthMode = "byo-client";
|
|
1270
|
-
console.log(chalk.dim(" No
|
|
1358
|
+
console.log(chalk.dim(" No Hatchkit Google OAuth client id is configured in this environment.\n" +
|
|
1271
1359
|
" Falling back to the legacy BYO Google Cloud OAuth client setup.\n"));
|
|
1272
|
-
tokenHint("https://console.cloud.google.com/apis/credentials", "OAuth client (Desktop app) with Search Console API + Site Verification API enabled", `Scopes: ${GOOGLE_SEARCH_CONSOLE_SCOPES.join(", ")}`);
|
|
1360
|
+
tokenHint("https://console.cloud.google.com/apis/credentials", "OAuth client (Desktop app) with Search Console API + Site Verification API enabled", `Scopes: ${GOOGLE_SEARCH_CONSOLE_SCOPES.join(", ")}; client secret is optional for Desktop app + PKCE`);
|
|
1273
1361
|
clientId = (await input({
|
|
1274
1362
|
message: "Google OAuth client ID:",
|
|
1275
1363
|
default: existingClientId ?? undefined,
|
|
1276
1364
|
validate: validateRequired,
|
|
1277
1365
|
})).trim();
|
|
1278
|
-
clientSecret = await
|
|
1366
|
+
clientSecret = await confirmOptionalPastedSecret("Google OAuth client secret");
|
|
1279
1367
|
}
|
|
1280
1368
|
const oauth = await runGoogleOAuthLoopback({ clientId, clientSecret });
|
|
1281
1369
|
const meta = {
|
|
@@ -1289,6 +1377,8 @@ export async function ensureGoogleSearchConsole() {
|
|
|
1289
1377
|
await setSecret(SECRET_KEYS.googleSearchConsoleClientId, clientId);
|
|
1290
1378
|
if (clientSecret)
|
|
1291
1379
|
await setSecret(SECRET_KEYS.googleSearchConsoleClientSecret, clientSecret);
|
|
1380
|
+
else
|
|
1381
|
+
await deleteSecret(SECRET_KEYS.googleSearchConsoleClientSecret);
|
|
1292
1382
|
}
|
|
1293
1383
|
else {
|
|
1294
1384
|
await deleteSecret(SECRET_KEYS.googleSearchConsoleClientId);
|
|
@@ -1320,9 +1410,15 @@ export async function getGoogleSearchConsoleConfig() {
|
|
|
1320
1410
|
refreshToken,
|
|
1321
1411
|
};
|
|
1322
1412
|
}
|
|
1323
|
-
if (!clientId
|
|
1413
|
+
if (!clientId)
|
|
1324
1414
|
return null;
|
|
1325
|
-
return {
|
|
1415
|
+
return {
|
|
1416
|
+
...meta,
|
|
1417
|
+
oauthMode: "byo-client",
|
|
1418
|
+
clientId,
|
|
1419
|
+
clientSecret: clientSecret ?? undefined,
|
|
1420
|
+
refreshToken,
|
|
1421
|
+
};
|
|
1326
1422
|
}
|
|
1327
1423
|
// ---------------------------------------------------------------------------
|
|
1328
1424
|
// Provider: Stripe (payments)
|