pug-site-core 3.0.28 → 3.0.29

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.
@@ -0,0 +1,200 @@
1
+ /**
2
+ * 数据库连接辅助文件
3
+ * 用于在本地 dev 模式下通过 Cloudflare API 连接远程 D1 数据库
4
+ *
5
+ * 配置方式:
6
+ * 1. 通过环境变量配置(推荐)
7
+ * - D1_ACCOUNT_ID 或 CF_ACCOUNT_ID: Cloudflare 账户 ID
8
+ * - D1_DATABASE_ID: D1 数据库 ID
9
+ * - CLOUDFLARE_API_TOKEN 或 CF_API_TOKEN: Cloudflare API Token
10
+ *
11
+ * 2. 通过 SITE_CONFIGS_JSON 配置(在项目的 config.js 中)
12
+ */
13
+
14
+ // Cloudflare D1 API 基础 URL
15
+ const CLOUDFLARE_API_BASE = 'https://api.cloudflare.com/client/v4';
16
+
17
+ /**
18
+ * 创建 D1 远程连接
19
+ * @param {string} accountId - Cloudflare 账户 ID
20
+ * @param {string} databaseId - D1 数据库 ID
21
+ * @param {string} apiToken - Cloudflare API Token
22
+ */
23
+ async function createD1RemoteConnection(accountId, databaseId, apiToken) {
24
+ if (!accountId || !databaseId || !apiToken) {
25
+ throw new Error('缺少必要的 D1 连接配置:accountId、databaseId 和 apiToken');
26
+ }
27
+
28
+ console.log(`正在连接到 Cloudflare D1 数据库: ${databaseId}`);
29
+
30
+ // 创建 D1 远程适配器
31
+ return createD1RemoteAdapter(accountId, databaseId, apiToken);
32
+ }
33
+
34
+ /**
35
+ * 创建 D1 远程适配器
36
+ * 通过 Cloudflare REST API 调用 D1 数据库
37
+ */
38
+ function createD1RemoteAdapter(accountId, databaseId, apiToken) {
39
+ const apiUrl = `${CLOUDFLARE_API_BASE}/accounts/${accountId}/d1/database/${databaseId}/query`;
40
+
41
+ return {
42
+ prepare: (sql) => {
43
+ return {
44
+ bind: (...args) => {
45
+ return {
46
+ all: async () => {
47
+ try {
48
+ const response = await fetch(apiUrl, {
49
+ method: 'POST',
50
+ headers: {
51
+ 'Authorization': `Bearer ${apiToken}`,
52
+ 'Content-Type': 'application/json',
53
+ },
54
+ body: JSON.stringify({
55
+ sql: sql,
56
+ params: args.length > 0 ? args : undefined,
57
+ }),
58
+ });
59
+
60
+ if (!response.ok) {
61
+ const errorText = await response.text();
62
+ throw new Error(`D1 API 错误 (${response.status}): ${errorText}`);
63
+ }
64
+
65
+ const data = await response.json();
66
+
67
+ if (!data.success) {
68
+ throw new Error(`D1 查询失败: ${JSON.stringify(data.errors || data)}`);
69
+ }
70
+
71
+ // D1 API 返回格式: { success: true, result: [{ results: [...] }] }
72
+ const results = data.result && data.result[0] ? data.result[0].results : [];
73
+ return { results };
74
+ } catch (error) {
75
+ console.error('D1 查询错误:', error);
76
+ throw error;
77
+ }
78
+ },
79
+ first: async () => {
80
+ try {
81
+ const response = await fetch(apiUrl, {
82
+ method: 'POST',
83
+ headers: {
84
+ 'Authorization': `Bearer ${apiToken}`,
85
+ 'Content-Type': 'application/json',
86
+ },
87
+ body: JSON.stringify({
88
+ sql: sql,
89
+ params: args.length > 0 ? args : undefined,
90
+ }),
91
+ });
92
+
93
+ if (!response.ok) {
94
+ const errorText = await response.text();
95
+ throw new Error(`D1 API 错误 (${response.status}): ${errorText}`);
96
+ }
97
+
98
+ const data = await response.json();
99
+
100
+ if (!data.success) {
101
+ throw new Error(`D1 查询失败: ${JSON.stringify(data.errors || data)}`);
102
+ }
103
+
104
+ // D1 API 返回格式: { success: true, result: [{ results: [...] }] }
105
+ const results = data.result && data.result[0] ? data.result[0].results : [];
106
+ return results.length > 0 ? results[0] : null;
107
+ } catch (error) {
108
+ console.error('D1 查询错误:', error);
109
+ throw error;
110
+ }
111
+ },
112
+ run: async () => {
113
+ try {
114
+ const response = await fetch(apiUrl, {
115
+ method: 'POST',
116
+ headers: {
117
+ 'Authorization': `Bearer ${apiToken}`,
118
+ 'Content-Type': 'application/json',
119
+ },
120
+ body: JSON.stringify({
121
+ sql: sql,
122
+ params: args.length > 0 ? args : undefined,
123
+ }),
124
+ });
125
+
126
+ if (!response.ok) {
127
+ const errorText = await response.text();
128
+ throw new Error(`D1 API 错误 (${response.status}): ${errorText}`);
129
+ }
130
+
131
+ const data = await response.json();
132
+
133
+ if (!data.success) {
134
+ throw new Error(`D1 执行失败: ${JSON.stringify(data.errors || data)}`);
135
+ }
136
+
137
+ // D1 API 返回格式: { success: true, result: [{ meta: {...} }] }
138
+ const meta = data.result && data.result[0] ? data.result[0].meta : {};
139
+ return { success: true, meta };
140
+ } catch (error) {
141
+ console.error('D1 执行错误:', error);
142
+ throw error;
143
+ }
144
+ }
145
+ };
146
+ }
147
+ };
148
+ }
149
+ };
150
+ }
151
+
152
+ /**
153
+ * 从配置中获取数据库连接信息
154
+ * @param {string} configPath - 项目 config.js 的路径
155
+ */
156
+ async function getD1Config(configPath) {
157
+ // 优先从环境变量读取
158
+ const accountId = process.env.D1_ACCOUNT_ID || process.env.CF_ACCOUNT_ID;
159
+ const databaseId = process.env.D1_DATABASE_ID;
160
+ const apiToken = process.env.CLOUDFLARE_API_TOKEN || process.env.CF_API_TOKEN;
161
+
162
+ if (accountId && databaseId && apiToken) {
163
+ return { accountId, databaseId, apiToken };
164
+ }
165
+
166
+ // 如果环境变量未配置,尝试从项目的 SITE_CONFIGS_JSON 读取
167
+ try {
168
+ const { config, SITE_CONFIGS_JSON } = await import(configPath);
169
+ const siteName = config.siteConfig?.siteName || 'default';
170
+
171
+ // 检查是否有 SITE_CONFIGS_JSON 配置
172
+ if (SITE_CONFIGS_JSON && SITE_CONFIGS_JSON[siteName]) {
173
+ const siteConfig = SITE_CONFIGS_JSON[siteName];
174
+ return {
175
+ accountId: siteConfig.cf_account_id,
176
+ databaseId: siteConfig.d1_database_id,
177
+ apiToken: siteConfig.cf_api_token,
178
+ };
179
+ }
180
+ } catch (error) {
181
+ // 忽略配置读取错误
182
+ console.warn('读取 SITE_CONFIGS_JSON 配置失败:', error.message);
183
+ }
184
+
185
+ return null;
186
+ }
187
+
188
+ /**
189
+ * 创建数据库连接
190
+ * @param {string} configPath - 项目 config.js 的路径
191
+ */
192
+ export async function getDatabaseConnection(configPath) {
193
+ const config = await getD1Config(configPath);
194
+
195
+ if (!config) {
196
+ throw new Error('未配置 D1 数据库连接信息。请设置环境变量或配置 SITE_CONFIGS_JSON');
197
+ }
198
+
199
+ return await createD1RemoteConnection(config.accountId, config.databaseId, config.apiToken);
200
+ }
package/lib/devServer.js CHANGED
@@ -19,6 +19,7 @@ import { Worker } from "worker_threads";
19
19
  import { paths } from "./paths.js";
20
20
  import { injectDebugScript } from "./debug/pugDebug.js";
21
21
  import { setupDebugRoutes } from "./debug/debugRouter.js";
22
+ import { getDatabaseConnection } from "./dbConnection.js";
22
23
 
23
24
  const { config } = await import(paths.config);
24
25
  const pagsTemplatePath = config.devServer.isDebug ? paths.template.debugPages : paths.template.pages;
@@ -27,6 +28,32 @@ const port = await getIdleProt(config.devServer.port);
27
28
  process.env._port = port;
28
29
  process.env._localIp = localIp;
29
30
 
31
+ // 初始化数据库连接(如果配置了)- 在模块加载时初始化一次
32
+ let dbConnection = null;
33
+ if (config.devServer?.db) {
34
+ try {
35
+ if (typeof config.devServer.db === 'function') {
36
+ dbConnection = await config.devServer.db();
37
+ console.log('数据库连接已初始化(dev 模式)');
38
+ } else if (config.devServer.db === true) {
39
+ // 如果设置为 true,使用内置的数据库连接功能
40
+ try {
41
+ dbConnection = await getDatabaseConnection(paths.config);
42
+ console.log('✅ D1 数据库连接成功(使用内置连接)');
43
+ } catch (error) {
44
+ console.error('❌ 数据库连接失败:', error.message);
45
+ console.warn('将使用 null 作为数据库连接');
46
+ }
47
+ } else {
48
+ dbConnection = config.devServer.db;
49
+ console.log('使用已配置的数据库连接(dev 模式)');
50
+ }
51
+ } catch (error) {
52
+ console.error('初始化数据库连接失败:', error);
53
+ console.warn('将使用 null 作为数据库连接');
54
+ }
55
+ }
56
+
30
57
  function createServer() {
31
58
  const app = express();
32
59
  const server = http.createServer(app);
@@ -251,7 +278,18 @@ async function matchRouter(url, language, device) {
251
278
  let data = await getJsonData(jsonPath);
252
279
  return data;
253
280
  };
254
- let params = { url, language, device, getR2Data };
281
+
282
+ // 创建 params 对象,包含 env 属性以模拟线上环境(Cloudflare Workers)
283
+ let params = {
284
+ url,
285
+ language,
286
+ device,
287
+ getR2Data,
288
+ env: {
289
+ db: dbConnection // 使用预初始化的数据库连接,模拟线上环境的 this.env.db
290
+ }
291
+ };
292
+
255
293
  if(config.abtest && config.abtest.enabled && abtestRouter && abtestRouter[config.abtest.curVariant]){
256
294
  router.unshift(...abtestRouter[config.abtest.curVariant]);
257
295
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pug-site-core",
3
- "version": "3.0.28",
3
+ "version": "3.0.29",
4
4
  "main": "index.js",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -52,7 +52,7 @@
52
52
  "ws": "^8.18.0"
53
53
  },
54
54
  "license": "ISC",
55
- "description": "feat: 添加匈牙利语和菲律宾语支持",
55
+ "description": "feat: 添加 dev 模式下的 D1 数据库连接支持",
56
56
  "files": [
57
57
  "lib/",
58
58
  "index.js"