parse-target-diagnostic.mjs 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. #!/usr/bin/env node
  2. const configuredBase = String(process.env.JD_TARGET_PARSE_URL || '').replace(/\/+$/, '');
  3. const appId = process.env.JD_TARGET_PARSE_APP_ID;
  4. const masterKey = process.env.JD_TARGET_PARSE_MASTER_KEY;
  5. if (!configuredBase || !appId || !masterKey) {
  6. throw new Error('JD target Parse configuration is incomplete');
  7. }
  8. const origin = new URL(configuredBase).origin;
  9. const appPathMatch = configuredBase.match(/\/backend\/([^/]+)/);
  10. const appPath = appPathMatch?.[1] || '';
  11. const candidates = [
  12. configuredBase,
  13. `${configuredBase}/classes/ProductDetail?limit=1`,
  14. appPath ? `${origin}/backend/${appPath}/classes/ProductDetail?limit=1` : '',
  15. appPath ? `${origin}/backend/${appPath}/data/parse/classes/ProductDetail?limit=1` : '',
  16. `${origin}/parse/classes/ProductDetail?limit=1`,
  17. ].filter(Boolean);
  18. async function fetchWithAuthorizationRetry(url) {
  19. for (let attempt = 1; attempt <= 10; attempt += 1) {
  20. const response = await fetch(url, {
  21. headers: {
  22. 'X-Parse-Application-Id': appId,
  23. 'X-Parse-Master-Key': masterKey,
  24. },
  25. });
  26. const body = await response.json().catch(() => null);
  27. if (!(response.status === 403 && body?.error === 'unauthorized') || attempt === 10) {
  28. return { response, body, attempt };
  29. }
  30. }
  31. throw new Error('unreachable');
  32. }
  33. for (const url of [...new Set(candidates)]) {
  34. const response = await fetch(url, {
  35. headers: {
  36. 'X-Parse-Application-Id': appId,
  37. 'X-Parse-Master-Key': masterKey,
  38. },
  39. });
  40. const text = await response.text();
  41. let payload = null;
  42. try { payload = text ? JSON.parse(text) : null; } catch {}
  43. console.log(JSON.stringify({
  44. url,
  45. status: response.status,
  46. contentType: response.headers.get('content-type'),
  47. parseCode: payload?.code ?? null,
  48. error: payload?.error ?? payload?.message ?? null,
  49. resultCount: Array.isArray(payload?.results) ? payload.results.length : null,
  50. bodyKind: payload ? 'json' : text ? 'non-json' : 'empty',
  51. }, null, 2));
  52. }
  53. for (const className of ['ProductDetail', 'Product', 'AsinSkuMapping', 'JdStockSnapshot']) {
  54. const schemaResponse = await fetch(`${configuredBase}/schemas/${className}`, {
  55. headers: {
  56. 'X-Parse-Application-Id': appId,
  57. 'X-Parse-Master-Key': masterKey,
  58. },
  59. });
  60. const schema = await schemaResponse.json().catch(() => null);
  61. const countResponse = await fetch(`${configuredBase}/classes/${className}?limit=0&count=1`, {
  62. headers: {
  63. 'X-Parse-Application-Id': appId,
  64. 'X-Parse-Master-Key': masterKey,
  65. },
  66. });
  67. const countPayload = await countResponse.json().catch(() => null);
  68. console.log(JSON.stringify({
  69. schemaStatus: schemaResponse.status,
  70. className: schema?.className ?? className,
  71. countStatus: countResponse.status,
  72. count: countPayload?.count ?? null,
  73. fields: Object.fromEntries(
  74. Object.entries(schema?.fields || {}).map(([name, definition]) => [name, definition?.type || null]),
  75. ),
  76. }, null, 2));
  77. }
  78. for (const endpoint of [
  79. `${configuredBase}/classes/ProductDetail?limit=1`,
  80. `${origin}/parse/classes/ProductDetail?limit=1`,
  81. ]) {
  82. const samples = [];
  83. for (let attempt = 0; attempt < 10; attempt += 1) {
  84. const response = await fetch(endpoint, {
  85. headers: {
  86. 'X-Parse-Application-Id': appId,
  87. 'X-Parse-Master-Key': masterKey,
  88. },
  89. });
  90. const body = await response.json().catch(() => null);
  91. samples.push({ status: response.status, resultCount: Array.isArray(body?.results) ? body.results.length : null });
  92. }
  93. console.log(JSON.stringify({ endpoint, stabilitySamples: samples }, null, 2));
  94. }
  95. for (const className of ['ProductDetail', 'Product', 'AsinSkuMapping', 'JdStockSnapshot']) {
  96. const { response, body, attempt } = await fetchWithAuthorizationRetry(
  97. `${configuredBase}/classes/${className}?limit=100&count=1`,
  98. );
  99. console.log(JSON.stringify({
  100. verifiedClass: className,
  101. status: response.status,
  102. attempts: attempt,
  103. count: body?.count ?? null,
  104. returnedItems: Array.isArray(body?.results) ? body.results.length : null,
  105. }, null, 2));
  106. }