pug-site-core 3.0.28 → 3.0.30

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,179 @@
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
+ // 执行查询的通用函数
42
+ const executeQuery = async (sql, params = []) => {
43
+ try {
44
+ const response = await fetch(apiUrl, {
45
+ method: 'POST',
46
+ headers: {
47
+ 'Authorization': `Bearer ${apiToken}`,
48
+ 'Content-Type': 'application/json',
49
+ },
50
+ body: JSON.stringify({
51
+ sql: sql,
52
+ params: params.length > 0 ? params : undefined,
53
+ }),
54
+ });
55
+
56
+ if (!response.ok) {
57
+ const errorText = await response.text();
58
+ throw new Error(`D1 API 错误 (${response.status}): ${errorText}`);
59
+ }
60
+
61
+ const data = await response.json();
62
+
63
+ if (!data.success) {
64
+ throw new Error(`D1 查询失败: ${JSON.stringify(data.errors || data)}`);
65
+ }
66
+
67
+ return data;
68
+ } catch (error) {
69
+ console.error('D1 查询错误:', error);
70
+ throw error;
71
+ }
72
+ };
73
+
74
+ return {
75
+ prepare: (sql) => {
76
+ // 创建一个可以链式调用的对象
77
+ // 支持两种调用方式:
78
+ // 1. prepare(sql).bind(...args).all() / first() / run()
79
+ // 2. prepare(sql).all() / first() / run() (当没有参数时)
80
+ const prepared = {
81
+ bind: (...args) => {
82
+ return {
83
+ all: async () => {
84
+ const data = await executeQuery(sql, args);
85
+ const results = data.result && data.result[0] ? data.result[0].results : [];
86
+ return { results };
87
+ },
88
+ 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;
92
+ },
93
+ run: async () => {
94
+ const data = await executeQuery(sql, args);
95
+ const meta = data.result && data.result[0] ? data.result[0].meta : {};
96
+ return { success: true, meta };
97
+ }
98
+ };
99
+ },
100
+ // 支持直接调用 all()、first()、run()(当没有参数时)
101
+ all: async () => {
102
+ const data = await executeQuery(sql, []);
103
+ const results = data.result && data.result[0] ? data.result[0].results : [];
104
+ return { results };
105
+ },
106
+ 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;
110
+ },
111
+ run: async () => {
112
+ const data = await executeQuery(sql, []);
113
+ const meta = data.result && data.result[0] ? data.result[0].meta : {};
114
+ return { success: true, meta };
115
+ }
116
+ };
117
+
118
+ return prepared;
119
+ }
120
+ };
121
+ }
122
+
123
+ /**
124
+ * 从配置中获取数据库连接信息
125
+ * @param {string} configPath - 项目 config.js 的路径
126
+ */
127
+ async function getD1Config(configPath) {
128
+ // 优先从环境变量读取
129
+ const accountId = process.env.D1_ACCOUNT_ID || process.env.CF_ACCOUNT_ID;
130
+ const databaseId = process.env.D1_DATABASE_ID;
131
+ const apiToken = process.env.CLOUDFLARE_API_TOKEN || process.env.CF_API_TOKEN;
132
+
133
+ if (accountId && databaseId && apiToken) {
134
+ console.log('使用环境变量配置 D1 数据库连接');
135
+ return { accountId, databaseId, apiToken };
136
+ }
137
+
138
+ // 如果环境变量未配置,尝试从项目的 SITE_CONFIGS_JSON 读取
139
+ try {
140
+ const { config, SITE_CONFIGS_JSON } = await import(configPath);
141
+ const siteName = config.siteConfig?.siteName || 'default';
142
+
143
+ console.log(`尝试从 SITE_CONFIGS_JSON 读取配置,站点名称: ${siteName}`);
144
+
145
+ // 检查是否有 SITE_CONFIGS_JSON 配置
146
+ if (SITE_CONFIGS_JSON && SITE_CONFIGS_JSON[siteName]) {
147
+ const siteConfig = SITE_CONFIGS_JSON[siteName];
148
+ console.log(`找到 ${siteName} 的配置`);
149
+ return {
150
+ accountId: siteConfig.cf_account_id,
151
+ databaseId: siteConfig.d1_database_id,
152
+ apiToken: siteConfig.cf_api_token,
153
+ };
154
+ } else {
155
+ console.warn(`SITE_CONFIGS_JSON 中未找到 ${siteName} 的配置`);
156
+ }
157
+ } catch (error) {
158
+ // 忽略配置读取错误
159
+ console.warn('读取 SITE_CONFIGS_JSON 配置失败:', error.message);
160
+ }
161
+
162
+ return null;
163
+ }
164
+
165
+ /**
166
+ * 创建数据库连接
167
+ * @param {string} configPath - 项目 config.js 的路径
168
+ */
169
+ export async function getDatabaseConnection(configPath) {
170
+ const config = await getD1Config(configPath);
171
+
172
+ if (!config) {
173
+ throw new Error('未配置 D1 数据库连接信息。请设置环境变量或配置 SITE_CONFIGS_JSON');
174
+ }
175
+
176
+ console.log(`使用账户 ID: ${config.accountId}, 数据库 ID: ${config.databaseId}`);
177
+
178
+ return await createD1RemoteConnection(config.accountId, config.databaseId, config.apiToken);
179
+ }
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.30",
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"