xinyu-pro 0.22.6 → 0.23.0

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 (105) hide show
  1. package/app/api/chat/route.ts +2 -2
  2. package/app/api/generate-svg/route.ts +1 -1
  3. package/app/api/generate-theme/route.ts +112 -135
  4. package/app/api/plugins/bindings/route.ts +4 -4
  5. package/app/api/plugins/export/route.ts +9 -3
  6. package/app/api/plugins/export-xye/route.ts +12 -7
  7. package/app/api/plugins/files/route.ts +9 -9
  8. package/app/api/plugins/files-list/route.ts +7 -3
  9. package/app/api/plugins/files-upload/route.ts +3 -3
  10. package/app/api/plugins/files-write/all/route.ts +250 -0
  11. package/app/api/plugins/files-write/route.ts +13 -15
  12. package/app/api/plugins/import/route.ts +5 -5
  13. package/app/api/plugins/import-package/route.ts +9 -9
  14. package/app/api/plugins/resources/route.ts +6 -2
  15. package/app/api/plugins/route.ts +215 -119
  16. package/app/api/plugins/scan/route.ts +1 -1
  17. package/app/api/plugins/storage/route.ts +3 -3
  18. package/app/api/plugins/toggle/route.ts +21 -0
  19. package/app/api/sessions/route.ts +8 -3
  20. package/app/api/settings/route.ts +2 -2
  21. package/app/api/suggest-fields/route.ts +1 -1
  22. package/app/api/templates/route.ts +2 -2
  23. package/app/api/themes/route.ts +116 -0
  24. package/app/editor/page.tsx +21 -4
  25. package/app/extensions/edit/[id]/page.tsx +2148 -984
  26. package/app/extensions/page.tsx +477 -86
  27. package/app/extensions/tutorial/code-examples.ts +833 -0
  28. package/app/extensions/tutorial/nav-link.tsx +27 -0
  29. package/app/extensions/tutorial/page.tsx +47 -3616
  30. package/app/extensions/tutorial/sections/AiInterceptionSection.tsx +87 -0
  31. package/app/extensions/tutorial/sections/ArchitectureSection.tsx +39 -0
  32. package/app/extensions/tutorial/sections/BestPracticesSection.tsx +78 -0
  33. package/app/extensions/tutorial/sections/ChatApiSection.tsx +212 -0
  34. package/app/extensions/tutorial/sections/CrossPluginSection.tsx +90 -0
  35. package/app/extensions/tutorial/sections/CssVariablesSection.tsx +189 -0
  36. package/app/extensions/tutorial/sections/DebugSection.tsx +48 -0
  37. package/app/extensions/tutorial/sections/DependencySection.tsx +134 -0
  38. package/app/extensions/tutorial/sections/DomSandboxSection.tsx +229 -0
  39. package/app/extensions/tutorial/sections/ExamplesSection.tsx +29 -0
  40. package/app/extensions/tutorial/sections/FileApiSection.tsx +44 -0
  41. package/app/extensions/tutorial/sections/GlobalApiSection.tsx +50 -0
  42. package/app/extensions/tutorial/sections/InputApiSection.tsx +213 -0
  43. package/app/extensions/tutorial/sections/LifecycleSection.tsx +105 -0
  44. package/app/extensions/tutorial/sections/OverviewSection.tsx +54 -0
  45. package/app/extensions/tutorial/sections/PermissionsSection.tsx +239 -0
  46. package/app/extensions/tutorial/sections/PersistentStorageSection.tsx +49 -0
  47. package/app/extensions/tutorial/sections/PluginConfigSection.tsx +81 -0
  48. package/app/extensions/tutorial/sections/PluginImportExportSection.tsx +68 -0
  49. package/app/extensions/tutorial/sections/PluginStorageSection.tsx +94 -0
  50. package/app/extensions/tutorial/sections/ResourceApiSection.tsx +88 -0
  51. package/app/extensions/tutorial/sections/SecuritySection.tsx +106 -0
  52. package/app/extensions/tutorial/sections/SlotsSection.tsx +130 -0
  53. package/app/extensions/tutorial/sections/UiApiSection.tsx +147 -0
  54. package/app/extensions/tutorial/sections/UtilsApiSection.tsx +96 -0
  55. package/app/extensions/tutorial/sections-data.tsx +325 -0
  56. package/app/extensions/tutorial/styles.ts +59 -0
  57. package/app/extensions/tutorial/types.ts +14 -0
  58. package/app/game/[id]/page.tsx +336 -148
  59. package/app/globals.css +41 -0
  60. package/app/page.tsx +17 -10
  61. package/app/settings/page.tsx +218 -236
  62. package/bin/cli.js +3 -10
  63. package/components/ChatInput.tsx +20 -6
  64. package/components/ChatWindow.tsx +1 -1
  65. package/components/MessageBubble.tsx +5 -6
  66. package/components/ui/ConfirmDialog.tsx +28 -6
  67. package/components/ui/DeprecatedBadge.tsx +163 -0
  68. package/components/ui/PageHeader.tsx +1 -1
  69. package/components/ui/PermissionConflictDialog.tsx +1 -1
  70. package/components/ui/PluginConfigForm.tsx +2 -3
  71. package/components/ui/PluginFloatingLayer.tsx +2 -2
  72. package/components/ui/PluginModalRenderer.tsx +84 -38
  73. package/components/ui/PluginProvider.tsx +32 -13
  74. package/components/ui/PluginSlotRenderer.tsx +2 -2
  75. package/components/ui/ThemeCustomizer.tsx +4 -7
  76. package/components/ui/ThemeProvider.tsx +214 -125
  77. package/components/ui/ThemeSwitcher.tsx +119 -157
  78. package/components/ui/ToastProvider.tsx +47 -17
  79. package/lib/{parseWorldCard.ts → client/parseWorldCard.ts} +1 -1
  80. package/lib/{plugin-dom-sandbox.ts → client/plugin-dom-sandbox.ts} +2 -2
  81. package/lib/{plugin-runtime.ts → client/plugin-runtime.ts} +166 -67
  82. package/lib/client/plugin-utils.ts +105 -0
  83. package/lib/{router-history.ts → client/router-history.ts} +1 -1
  84. package/lib/{storage.ts → client/storage.ts} +66 -3
  85. package/lib/{themes.ts → client/themes.ts} +21 -1
  86. package/lib/{builtin-plugins.ts → server/builtin-plugins.ts} +1 -1
  87. package/lib/{db-init.ts → server/db-init.ts} +2 -2
  88. package/lib/{db.ts → server/db.ts} +48 -8
  89. package/lib/{get-ai-config.ts → server/get-ai-config.ts} +2 -2
  90. package/lib/{plugin-files.ts → server/plugin-files.ts} +7 -2
  91. package/lib/server/plugin-utils.ts +88 -0
  92. package/lib/{manifest-parser.ts → shared/manifest-parser.ts} +4 -4
  93. package/lib/{plugin-types.ts → shared/plugin-types.ts} +11 -6
  94. package/lib/{version.ts → shared/version.ts} +1 -1
  95. package/next.config.mjs +3 -2
  96. package/package.json +4 -3
  97. package/styles/themes.css +7 -2
  98. package/version.json +2 -2
  99. package/app/extensions/create/page.tsx +0 -1422
  100. /package/lib/{plugin-events.ts → client/plugin-events.ts} +0 -0
  101. /package/lib/{plugin-resource-tracker.ts → client/plugin-resource-tracker.ts} +0 -0
  102. /package/lib/{plugin-html-sanitizer.ts → shared/plugin-html-sanitizer.ts} +0 -0
  103. /package/lib/{plugin-security.ts → shared/plugin-security.ts} +0 -0
  104. /package/lib/{prompt-builder.ts → shared/prompt-builder.ts} +0 -0
  105. /package/lib/{types.ts → shared/types.ts} +0 -0
@@ -0,0 +1,250 @@
1
+ // app/api/plugins/files-write/all/route.ts
2
+
3
+ import {NextRequest, NextResponse} from 'next/server';
4
+ import {ensureDb} from '@/lib/server/db-init';
5
+ import {execute} from '@/lib/server/db';
6
+ import {getPluginDir, writePluginResource} from '@/lib/server/plugin-files';
7
+ import path from 'path';
8
+ import {promises as fs} from 'fs';
9
+
10
+ function isValidPluginId(id: string): boolean {
11
+ return /^[a-zA-Z0-9._-]+$/.test(id) && id.length <= 200 && !id.includes('..');
12
+ }
13
+
14
+ function isSafePath(filePath: string): boolean {
15
+ const normalized = path.normalize(filePath);
16
+ return !normalized.includes('..') && !path.isAbsolute(normalized) && normalized !== '';
17
+ }
18
+
19
+ /** 验证插件存在 */
20
+ async function verifyPlugin(pluginId: string) {
21
+ await ensureDb();
22
+ const [rows] = await execute(
23
+ 'SELECT id, file_entry FROM extensions WHERE id = ?',
24
+ [pluginId]
25
+ );
26
+ const list = rows as Record<string, unknown>[];
27
+ return list.length > 0 ? list[0] : null;
28
+ }
29
+
30
+ /** 验证插件存在 */
31
+ async function deleteOrphanedFiles(pluginId: string, existingFiles: Set<string>, currentFiles: Set<string>) {
32
+ const pluginDir = getPluginDir(pluginId);
33
+
34
+ // 使用 Array.from 或 for...of 配合 downlevelIteration 的替代方案
35
+ const existingFilesArray = Array.from(existingFiles);
36
+
37
+ for (const filePath of existingFilesArray) {
38
+ if (!currentFiles.has(filePath)) {
39
+ const fullPath = path.join(pluginDir, filePath);
40
+ try {
41
+ const stat = await fs.stat(fullPath);
42
+ if (stat.isFile()) {
43
+ await fs.unlink(fullPath);
44
+ console.log(`删除孤儿文件: ${filePath}`);
45
+ }
46
+ } catch (e) {
47
+ console.error(`删除文件失败 ${filePath}:`, e);
48
+ }
49
+ }
50
+ }
51
+ }
52
+
53
+ /** 获取插件当前所有文件列表 */
54
+ async function getExistingFiles(pluginId: string): Promise<Set<string>> {
55
+ const pluginDir = getPluginDir(pluginId);
56
+ const files = new Set<string>();
57
+
58
+ try {
59
+ await fs.access(pluginDir);
60
+ } catch {
61
+ return files; // 目录不存在,返回空集合
62
+ }
63
+
64
+ const walk = async (dir: string, basePath: string) => {
65
+ const entries = await fs.readdir(dir, { withFileTypes: true });
66
+ for (const entry of entries) {
67
+ const fullPath = path.join(dir, entry.name);
68
+ const relativePath = path.join(basePath, entry.name).replace(/\\/g, '/');
69
+
70
+ if (entry.isDirectory()) {
71
+ await walk(fullPath, relativePath);
72
+ } else {
73
+ files.add(relativePath);
74
+ }
75
+ }
76
+ };
77
+
78
+ await walk(pluginDir, '');
79
+ return files;
80
+ }
81
+
82
+ // ==================== PUT: 批量保存所有文件 ====================
83
+
84
+ export async function PUT(request: NextRequest) {
85
+ try {
86
+ const { searchParams } = new URL(request.url);
87
+ const pluginId = searchParams.get('pluginId');
88
+ const deleteOrphans = searchParams.get('deleteOrphans') !== 'false'; // 默认删除孤儿文件
89
+
90
+ if (!pluginId) {
91
+ return NextResponse.json({ error: '缺少 pluginId 参数' }, { status: 400 });
92
+ }
93
+ if (!isValidPluginId(pluginId)) {
94
+ return NextResponse.json({ error: '非法插件 ID' }, { status: 400 });
95
+ }
96
+
97
+ const plugin = await verifyPlugin(pluginId);
98
+ if (!plugin) {
99
+ return NextResponse.json({ error: '插件不存在' }, { status: 404 });
100
+ }
101
+
102
+ const body = await request.json();
103
+ const { files, manifest, currentFile } = body;
104
+
105
+ if (!files || typeof files !== 'object') {
106
+ return NextResponse.json({ error: '缺少 files 参数或格式错误' }, { status: 400 });
107
+ }
108
+
109
+ const errors: Array<{ path: string; error: string }> = [];
110
+ const savedFiles: string[] = [];
111
+
112
+ // 获取现有文件列表(用于删除孤儿文件)
113
+ let existingFiles: Set<string> = new Set();
114
+ if (deleteOrphans) {
115
+ existingFiles = await getExistingFiles(pluginId);
116
+ }
117
+
118
+ // 保存所有文件
119
+ const fileEntries = Object.entries(files);
120
+ for (const [filePath, content] of fileEntries) {
121
+ if (typeof content !== 'string') {
122
+ errors.push({ path: filePath, error: '内容必须是字符串' });
123
+ continue;
124
+ }
125
+
126
+ if (!isSafePath(filePath)) {
127
+ errors.push({ path: filePath, error: '非法文件路径' });
128
+ continue;
129
+ }
130
+
131
+ // 禁止写入关键目录检查
132
+ if (filePath.includes('..') || filePath.startsWith('.')) {
133
+ errors.push({ path: filePath, error: '非法文件路径' });
134
+ continue;
135
+ }
136
+
137
+ try {
138
+ await writePluginResource(pluginId, filePath, content);
139
+ savedFiles.push(filePath);
140
+ } catch (e) {
141
+ console.error(`保存文件失败 ${filePath}:`, e);
142
+ errors.push({ path: filePath, error: '写入失败' });
143
+ }
144
+ }
145
+
146
+ // 处理 manifest 单独传入的情况
147
+ if (manifest && !files['manifest.json']) {
148
+ try {
149
+ const manifestContent = JSON.stringify(manifest, null, 2);
150
+ await writePluginResource(pluginId, 'manifest.json', manifestContent);
151
+ savedFiles.push('manifest.json');
152
+ } catch (e) {
153
+ errors.push({ path: 'manifest.json', error: '保存失败' });
154
+ }
155
+ }
156
+
157
+ // 删除孤儿文件
158
+ let deletedCount = 0;
159
+ if (deleteOrphans && existingFiles.size > 0) {
160
+ const currentFilesSet = new Set(Object.keys(files));
161
+ await deleteOrphanedFiles(pluginId, existingFiles, currentFilesSet);
162
+ // 计算删除数量
163
+ const existingArray = Array.from(existingFiles);
164
+ const currentArray = Array.from(currentFilesSet);
165
+ deletedCount = existingArray.filter(f => !currentArray.includes(f)).length;
166
+ }
167
+
168
+ // 返回结果
169
+ if (errors.length > 0 && savedFiles.length === 0) {
170
+ return NextResponse.json({
171
+ error: '所有文件保存失败',
172
+ errors,
173
+ savedFiles: [],
174
+ deletedCount
175
+ }, { status: 500 });
176
+ }
177
+
178
+ return NextResponse.json({
179
+ success: true,
180
+ savedFiles,
181
+ savedCount: savedFiles.length,
182
+ deletedCount,
183
+ errors: errors.length > 0 ? errors : undefined,
184
+ currentFile: currentFile || null
185
+ });
186
+
187
+ } catch (e) {
188
+ console.error('批量保存插件文件失败:', e);
189
+ return NextResponse.json({
190
+ error: '批量保存失败',
191
+ message: e instanceof Error ? e.message : String(e)
192
+ }, { status: 500 });
193
+ }
194
+ }
195
+
196
+ // ==================== POST: 获取所有文件内容 ====================
197
+
198
+ export async function POST(request: NextRequest) {
199
+ try {
200
+ const { searchParams } = new URL(request.url);
201
+ const pluginId = searchParams.get('pluginId');
202
+
203
+ if (!pluginId) {
204
+ return NextResponse.json({ error: '缺少 pluginId 参数' }, { status: 400 });
205
+ }
206
+ if (!isValidPluginId(pluginId)) {
207
+ return NextResponse.json({ error: '非法插件 ID' }, { status: 400 });
208
+ }
209
+
210
+ const plugin = await verifyPlugin(pluginId);
211
+ if (!plugin) {
212
+ return NextResponse.json({ error: '插件不存在' }, { status: 404 });
213
+ }
214
+
215
+ const pluginDir = getPluginDir(pluginId);
216
+ const files: Record<string, string> = {};
217
+
218
+ try {
219
+ await fs.access(pluginDir);
220
+ } catch {
221
+ return NextResponse.json({ files: {}, message: '插件目录不存在' });
222
+ }
223
+
224
+ const walk = async (dir: string, basePath: string) => {
225
+ const entries = await fs.readdir(dir, { withFileTypes: true });
226
+ for (const entry of entries) {
227
+ const fullPath = path.join(dir, entry.name);
228
+ const relativePath = path.join(basePath, entry.name).replace(/\\/g, '/');
229
+
230
+ if (entry.isDirectory()) {
231
+ await walk(fullPath, relativePath);
232
+ } else {
233
+ files[relativePath] = await fs.readFile(fullPath, 'utf-8');
234
+ }
235
+ }
236
+ };
237
+
238
+ await walk(pluginDir, '');
239
+
240
+ return NextResponse.json({
241
+ success: true,
242
+ files,
243
+ fileCount: Object.keys(files).length
244
+ });
245
+
246
+ } catch (e) {
247
+ console.error('读取插件所有文件失败:', e);
248
+ return NextResponse.json({ error: '读取文件失败' }, { status: 500 });
249
+ }
250
+ }
@@ -1,9 +1,9 @@
1
1
  // app/api/plugins/files-write/route.ts - 插件文件写入/删除/新建/重命名
2
2
 
3
3
  import { NextRequest, NextResponse } from 'next/server';
4
- import { ensureDb } from '@/lib/db-init';
5
- import { execute } from '@/lib/db';
6
- import { writePluginResource, getPluginDir } from '@/lib/plugin-files';
4
+ import { ensureDb } from '@/lib/server/db-init';
5
+ import { execute } from '@/lib/server/db';
6
+ import {writePluginResource, getPluginDir, getPluginCodeRelativePath} from '@/lib/server/plugin-files';
7
7
  import path from 'path';
8
8
  import { promises as fs } from 'fs';
9
9
 
@@ -168,35 +168,33 @@ export async function DELETE(request: NextRequest) {
168
168
  return NextResponse.json({ error: '非法文件路径' }, { status: 400 });
169
169
  }
170
170
 
171
- // 禁止删除关键文件:manifest.json、入口文件、resources 目录本身
171
+ // 禁止删除关键文件:manifest.json、入口文件
172
172
  if (filePath === 'manifest.json') {
173
173
  return NextResponse.json({ error: '不能删除 manifest.json' }, { status: 400 });
174
174
  }
175
- if (filePath === 'resources') {
176
- return NextResponse.json({ error: '不能删除 resources 目录(资源文件目录,仅支持删除其中的文件)' }, { status: 400 });
177
- }
178
175
 
179
176
  const exists = await verifyPlugin(pluginId);
180
177
  if (!exists) {
181
178
  return NextResponse.json({ error: '插件不存在' }, { status: 404 });
182
179
  }
183
180
 
184
- // 从数据库读取 code_path,提取入口文件名,禁止删除入口文件
181
+ const pluginDir = getPluginDir(pluginId);
182
+ const fullPath = path.join(pluginDir, filePath);
183
+ const relativePath = getPluginCodeRelativePath(pluginId, filePath);
184
+
185
+ // 从数据库读取 file_entry,提取入口文件名,禁止删除入口文件
185
186
  const [codeRows] = await execute(
186
- 'SELECT code_path FROM extensions WHERE id = ?',
187
+ 'SELECT file_entry FROM extensions WHERE id = ?',
187
188
  [pluginId]
188
189
  );
189
190
  const codeRowList = codeRows as Record<string, unknown>[];
190
- if (codeRowList.length > 0 && codeRowList[0].code_path) {
191
- const entryFile = (codeRowList[0].code_path as string).split('/').pop();
192
- if (entryFile && filePath === entryFile) {
191
+ if (codeRowList.length > 0 && codeRowList[0].file_entry) {
192
+ const entryFile = codeRowList[0].file_entry as string;
193
+ if (entryFile && relativePath === entryFile) {
193
194
  return NextResponse.json({ error: `不能删除入口文件 ${entryFile}` }, { status: 400 });
194
195
  }
195
196
  }
196
197
 
197
- const pluginDir = getPluginDir(pluginId);
198
- const fullPath = path.join(pluginDir, filePath);
199
-
200
198
  try {
201
199
  const stat = await fs.stat(fullPath);
202
200
  if (stat.isDirectory()) {
@@ -1,10 +1,10 @@
1
1
  // app/api/plugins/import/route.ts - 插件导入 API
2
2
 
3
3
  import { NextRequest, NextResponse } from 'next/server';
4
- import { execute } from '@/lib/db';
5
- import { ensureDb } from '@/lib/db-init';
6
- import { parseManifestFromCode } from '@/lib/manifest-parser';
7
- import { writePluginCode } from '@/lib/plugin-files';
4
+ import { execute } from '@/lib/server/db';
5
+ import { ensureDb } from '@/lib/server/db-init';
6
+ import { parseManifestFromCode } from '@/lib/shared/manifest-parser';
7
+ import { writePluginCode } from '@/lib/server/plugin-files';
8
8
 
9
9
  /** 校验插件 ID 合法性(只允许字母、数字、连字符、下划线、点) */
10
10
  function isValidPluginId(id: string): boolean {
@@ -88,7 +88,7 @@ export async function POST(request: NextRequest) {
88
88
  // 将代码写入文件
89
89
  const codePath = await writePluginCode(pId, pCode);
90
90
 
91
- const sql = `INSERT INTO extensions (id, name, version, description, author, type, icon, min_app_version, code_path, config_schema, ui_slots, ui_permissions, public_exports, dependencies) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(id) DO UPDATE SET name = excluded.name, version = excluded.version, description = excluded.description, author = excluded.author, type = excluded.type, icon = excluded.icon, min_app_version = excluded.min_app_version, code_path = excluded.code_path, config_schema = excluded.config_schema, ui_slots = excluded.ui_slots, ui_permissions = excluded.ui_permissions, public_exports = excluded.public_exports, dependencies = excluded.dependencies`;
91
+ const sql = `INSERT INTO extensions (id, name, version, description, author, type, icon, min_app_version, file_entry, config_schema, ui_slots, ui_permissions, public_exports, dependencies) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(id) DO UPDATE SET name = excluded.name, version = excluded.version, description = excluded.description, author = excluded.author, type = excluded.type, icon = excluded.icon, min_app_version = excluded.min_app_version, file_entry = excluded.file_entry, config_schema = excluded.config_schema, ui_slots = excluded.ui_slots, ui_permissions = excluded.ui_permissions, public_exports = excluded.public_exports, dependencies = excluded.dependencies`;
92
92
  await execute(sql, [pId, pName, pVersion, pDescription, pAuthor, pType, pIcon, pMinAppVersion, codePath, pConfigSchema, pUiSlots, pUiPermissions, pPublicExports, pDependencies]);
93
93
 
94
94
  // 自动创建全局绑定,config 从 configSchema 的 defaultValue 填充
@@ -1,10 +1,10 @@
1
1
  // app/api/plugins/import-package/route.ts - .xye 插件包导入 API
2
2
 
3
3
  import { NextRequest, NextResponse } from 'next/server';
4
- import { execute } from '@/lib/db';
5
- import { ensureDb } from '@/lib/db-init';
6
- import { parseManifestFromCode } from '@/lib/manifest-parser';
7
- import { getPluginDir } from '@/lib/plugin-files';
4
+ import { execute } from '@/lib/server/db';
5
+ import { ensureDb } from '@/lib/server/db-init';
6
+ import { parseManifestFromCode } from '@/lib/shared/manifest-parser';
7
+ import {getPluginCodeRelativePath, getPluginDir} from '@/lib/server/plugin-files';
8
8
  import AdmZip from 'adm-zip';
9
9
  import path from 'path';
10
10
  import { promises as fs } from 'fs';
@@ -84,7 +84,7 @@ export async function POST(request: NextRequest) {
84
84
  }
85
85
 
86
86
  // 查找入口文件
87
- const entryPath = pluginEntry.startsWith('/') ? pluginEntry.slice(1) : pluginEntry;
87
+ const entryPath = path.basename(pluginEntry) || 'plugin.js';
88
88
  const codeEntry = entries.find(e => e.entryName === entryPath || e.entryName === `/${entryPath}`);
89
89
 
90
90
  if (!codeEntry) {
@@ -133,16 +133,16 @@ export async function POST(request: NextRequest) {
133
133
  await fs.mkdir(pluginDir, { recursive: true });
134
134
  const codeFilePath = path.join(pluginDir, entryPath);
135
135
  await fs.writeFile(codeFilePath, pluginCode, 'utf-8');
136
- const codePath = `data/plugins/${pluginId}/${entryPath}`;
136
+ const fileEntry = getPluginCodeRelativePath(pluginId, entryPath);
137
137
 
138
138
  // 写入数据库
139
139
  await execute(
140
- `INSERT INTO extensions (id, name, version, description, author, type, icon, min_app_version, code_path, config_schema, ui_slots, ui_permissions, public_exports, dependencies)
140
+ `INSERT INTO extensions (id, name, version, description, author, type, icon, min_app_version, file_entry, config_schema, ui_slots, ui_permissions, public_exports, dependencies)
141
141
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
142
142
  ON CONFLICT(id) DO UPDATE SET
143
143
  name = excluded.name, version = excluded.version, description = excluded.description,
144
144
  author = excluded.author, type = excluded.type, icon = excluded.icon,
145
- min_app_version = excluded.min_app_version, code_path = excluded.code_path,
145
+ min_app_version = excluded.min_app_version, file_entry = excluded.file_entry,
146
146
  config_schema = excluded.config_schema, ui_slots = excluded.ui_slots,
147
147
  ui_permissions = excluded.ui_permissions,
148
148
  public_exports = excluded.public_exports, dependencies = excluded.dependencies`,
@@ -150,7 +150,7 @@ export async function POST(request: NextRequest) {
150
150
  pluginId, pluginName, pluginVersion,
151
151
  String(manifest.description || ''), String(manifest.author || ''),
152
152
  pluginType, String(manifest.icon || ''), String(manifest.minAppVersion || ''),
153
- codePath, pConfigSchema, pUiSlots, pUiPermissions, pPublicExports, pDependencies,
153
+ fileEntry, pConfigSchema, pUiSlots, pUiPermissions, pPublicExports, pDependencies,
154
154
  ]
155
155
  );
156
156
 
@@ -1,8 +1,11 @@
1
1
  // app/api/plugins/resources/route.ts - 插件资源文件 HTTP 访问
2
2
 
3
3
  import { NextRequest, NextResponse } from 'next/server';
4
- import { ensureDb } from '@/lib/db-init';
5
- import { execute } from '@/lib/db';
4
+
5
+ export const dynamic = 'force-dynamic';
6
+
7
+ import { ensureDb } from '@/lib/server/db-init';
8
+ import { execute } from '@/lib/server/db';
6
9
  import path from 'path';
7
10
  import { promises as fs } from 'fs';
8
11
 
@@ -72,6 +75,7 @@ export async function GET(request: NextRequest) {
72
75
  [pluginId]
73
76
  );
74
77
 
78
+ // @ts-ignore
75
79
  if (rows.length === 0) {
76
80
  return NextResponse.json({ error: '插件不存在' }, { status: 404 });
77
81
  }