pug-site-core 3.0.30 → 3.0.32

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.
Files changed (2) hide show
  1. package/lib/dbConnection.js +105 -23
  2. package/package.json +1 -1
@@ -41,30 +41,57 @@ function createD1RemoteAdapter(accountId, databaseId, apiToken) {
41
41
  // 执行查询的通用函数
42
42
  const executeQuery = async (sql, params = []) => {
43
43
  try {
44
+ // 参考 Python 代码:params 始终为数组,即使为空也传空数组
45
+ const requestBody = {
46
+ sql: sql,
47
+ params: params || [],
48
+ };
49
+
44
50
  const response = await fetch(apiUrl, {
45
51
  method: 'POST',
46
52
  headers: {
47
53
  'Authorization': `Bearer ${apiToken}`,
48
54
  'Content-Type': 'application/json',
49
55
  },
50
- body: JSON.stringify({
51
- sql: sql,
52
- params: params.length > 0 ? params : undefined,
53
- }),
56
+ body: JSON.stringify(requestBody),
54
57
  });
55
58
 
56
59
  if (!response.ok) {
57
60
  const errorText = await response.text();
61
+ // 如果是 401 错误,提供更详细的提示
62
+ if (response.status === 401) {
63
+ console.error('❌ D1 API 认证失败 (401)');
64
+ console.error('可能的原因:');
65
+ console.error('1. API Token 无效或已过期');
66
+ console.error('2. API Token 没有 D1 数据库的访问权限');
67
+ console.error('3. API Token 格式不正确');
68
+ console.error('请检查 Cloudflare Dashboard 中的 API Token 配置');
69
+ }
58
70
  throw new Error(`D1 API 错误 (${response.status}): ${errorText}`);
59
71
  }
60
72
 
61
73
  const data = await response.json();
62
74
 
75
+ // 检查顶层 success
63
76
  if (!data.success) {
64
- throw new Error(`D1 查询失败: ${JSON.stringify(data.errors || data)}`);
77
+ const errors = data.errors || [];
78
+ throw new Error(`D1 请求被拒绝: ${JSON.stringify(errors)}`);
79
+ }
80
+
81
+ // 检查 result 数组
82
+ const resultList = data.result || [];
83
+ if (resultList.length === 0) {
84
+ return { results: [], meta: {} };
85
+ }
86
+
87
+ const result = resultList[0];
88
+
89
+ // 检查 result 的 success(参考 Python 代码)
90
+ if (result.success === false) {
91
+ throw new Error(`D1 SQL 执行失败: ${result.error || JSON.stringify(result)}`);
65
92
  }
66
93
 
67
- return data;
94
+ return result;
68
95
  } catch (error) {
69
96
  console.error('D1 查询错误:', error);
70
97
  throw error;
@@ -81,36 +108,37 @@ function createD1RemoteAdapter(accountId, databaseId, apiToken) {
81
108
  bind: (...args) => {
82
109
  return {
83
110
  all: async () => {
84
- const data = await executeQuery(sql, args);
85
- const results = data.result && data.result[0] ? data.result[0].results : [];
86
- return { results };
111
+ const result = await executeQuery(sql, args);
112
+ // executeQuery 现在直接返回 result 对象,包含 results meta
113
+ const rows = result.results || [];
114
+ return { results: Array.isArray(rows) ? rows : [] };
87
115
  },
88
116
  first: async () => {
89
- const data = await executeQuery(sql, args);
90
- const results = data.result && data.result[0] ? data.result[0].results : [];
91
- return results.length > 0 ? results[0] : null;
117
+ const result = await executeQuery(sql, args);
118
+ const rows = result.results || [];
119
+ return Array.isArray(rows) && rows.length > 0 ? rows[0] : null;
92
120
  },
93
121
  run: async () => {
94
- const data = await executeQuery(sql, args);
95
- const meta = data.result && data.result[0] ? data.result[0].meta : {};
122
+ const result = await executeQuery(sql, args);
123
+ const meta = result.meta || {};
96
124
  return { success: true, meta };
97
125
  }
98
126
  };
99
127
  },
100
128
  // 支持直接调用 all()、first()、run()(当没有参数时)
101
129
  all: async () => {
102
- const data = await executeQuery(sql, []);
103
- const results = data.result && data.result[0] ? data.result[0].results : [];
104
- return { results };
130
+ const result = await executeQuery(sql, []);
131
+ const rows = result.results || [];
132
+ return { results: Array.isArray(rows) ? rows : [] };
105
133
  },
106
134
  first: async () => {
107
- const data = await executeQuery(sql, []);
108
- const results = data.result && data.result[0] ? data.result[0].results : [];
109
- return results.length > 0 ? results[0] : null;
135
+ const result = await executeQuery(sql, []);
136
+ const rows = result.results || [];
137
+ return Array.isArray(rows) && rows.length > 0 ? rows[0] : null;
110
138
  },
111
139
  run: async () => {
112
- const data = await executeQuery(sql, []);
113
- const meta = data.result && data.result[0] ? data.result[0].meta : {};
140
+ const result = await executeQuery(sql, []);
141
+ const meta = result.meta || {};
114
142
  return { success: true, meta };
115
143
  }
116
144
  };
@@ -137,7 +165,19 @@ async function getD1Config(configPath) {
137
165
 
138
166
  // 如果环境变量未配置,尝试从项目的 SITE_CONFIGS_JSON 读取
139
167
  try {
140
- const { config, SITE_CONFIGS_JSON } = await import(configPath);
168
+ // 确保路径是有效的 file:// URL 格式(Windows 需要)
169
+ let importPath = configPath;
170
+ if (!configPath.startsWith('file://') && !configPath.startsWith('http://') && !configPath.startsWith('https://')) {
171
+ // 如果是绝对路径,转换为 file:// URL
172
+ if (configPath.match(/^[A-Za-z]:/)) {
173
+ importPath = `file:///${configPath.replace(/\\/g, '/')}`;
174
+ } else {
175
+ // 相对路径,使用 pathToFileURL
176
+ const { pathToFileURL } = await import('url');
177
+ importPath = pathToFileURL(configPath).href;
178
+ }
179
+ }
180
+ const { config, SITE_CONFIGS_JSON } = await import(importPath);
141
181
  const siteName = config.siteConfig?.siteName || 'default';
142
182
 
143
183
  console.log(`尝试从 SITE_CONFIGS_JSON 读取配置,站点名称: ${siteName}`);
@@ -162,6 +202,32 @@ async function getD1Config(configPath) {
162
202
  return null;
163
203
  }
164
204
 
205
+ /**
206
+ * 验证 API Token 是否有效
207
+ * @param {string} apiToken - Cloudflare API Token
208
+ * @returns {Promise<boolean>} - Token 是否有效
209
+ */
210
+ async function verifyApiToken(apiToken) {
211
+ try {
212
+ const response = await fetch('https://api.cloudflare.com/client/v4/user/tokens/verify', {
213
+ method: 'GET',
214
+ headers: {
215
+ 'Authorization': `Bearer ${apiToken}`,
216
+ 'Content-Type': 'application/json',
217
+ },
218
+ });
219
+
220
+ if (response.ok) {
221
+ const data = await response.json();
222
+ return data.success === true;
223
+ }
224
+ return false;
225
+ } catch (error) {
226
+ console.warn('验证 API Token 时出错:', error.message);
227
+ return false;
228
+ }
229
+ }
230
+
165
231
  /**
166
232
  * 创建数据库连接
167
233
  * @param {string} configPath - 项目 config.js 的路径
@@ -174,6 +240,22 @@ export async function getDatabaseConnection(configPath) {
174
240
  }
175
241
 
176
242
  console.log(`使用账户 ID: ${config.accountId}, 数据库 ID: ${config.databaseId}`);
243
+ // 只显示 Token 的前 10 个字符用于调试(不显示完整 Token)
244
+ const tokenPreview = config.apiToken ? `${config.apiToken.substring(0, 10)}...` : '未设置';
245
+ console.log(`API Token: ${tokenPreview}`);
246
+
247
+ // 验证 API Token 是否有效(可选,仅用于诊断)
248
+ console.log('正在验证 API Token...');
249
+ const isValid = await verifyApiToken(config.apiToken);
250
+ if (!isValid) {
251
+ console.warn('⚠️ API Token 验证失败,但将继续尝试连接数据库...');
252
+ console.warn(' 如果后续数据库查询失败,请检查:');
253
+ console.warn(' 1. Token 是否正确(检查 config.js 中的 cf_api_token)');
254
+ console.warn(' 2. Token 是否在 Cloudflare Dashboard 中仍然有效');
255
+ console.warn(' 3. Token 是否有 D1 数据库的访问权限');
256
+ } else {
257
+ console.log('✅ API Token 验证成功');
258
+ }
177
259
 
178
260
  return await createD1RemoteConnection(config.accountId, config.databaseId, config.apiToken);
179
261
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pug-site-core",
3
- "version": "3.0.30",
3
+ "version": "3.0.32",
4
4
  "main": "index.js",
5
5
  "type": "module",
6
6
  "scripts": {