xinyu-pro 0.22.5 → 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 +27 -7
  25. package/app/extensions/edit/[id]/page.tsx +3576 -2342
  26. package/app/extensions/page.tsx +1967 -1572
  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 +18 -11
  61. package/app/settings/page.tsx +218 -236
  62. package/bin/cli.js +219 -211
  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} +49 -9
  89. package/lib/{get-ai-config.ts → server/get-ai-config.ts} +2 -2
  90. package/lib/{plugin-files.ts → server/plugin-files.ts} +191 -186
  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 +5 -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
@@ -1,8 +1,8 @@
1
1
  // app/api/chat/route.ts - AI 对话 API 接口
2
2
 
3
3
  import { NextRequest, NextResponse } from 'next/server';
4
- import { buildSystemPrompt } from '@/lib/prompt-builder';
5
- import { getAiConfig } from '@/lib/get-ai-config';
4
+ import { buildSystemPrompt } from '@/lib/shared/prompt-builder';
5
+ import { getAiConfig } from '@/lib/server/get-ai-config';
6
6
 
7
7
  export async function POST(request: NextRequest) {
8
8
  try {
@@ -1,7 +1,7 @@
1
1
  // app/api/generate-svg/route.ts - AI 生成 SVG 世界卡片
2
2
 
3
3
  import { NextRequest, NextResponse } from 'next/server';
4
- import { getAiConfig } from '@/lib/get-ai-config';
4
+ import { getAiConfig } from '@/lib/server/get-ai-config';
5
5
 
6
6
  interface GenerateSvgRequest {
7
7
  worldData: Record<string, unknown>;
@@ -1,135 +1,112 @@
1
- // app/api/generate-theme/route.ts - AI 生成配色方案
2
-
3
- import { NextRequest, NextResponse } from 'next/server';
4
- import { getAiConfig } from '@/lib/get-ai-config';
5
-
6
- interface GenerateThemeRequest {
7
- prompt: string;
8
- format: 'svg' | 'json';
9
- apiConfig?: {
10
- apiKey: string;
11
- apiBase: string;
12
- model: string;
13
- };
14
- }
15
-
16
- const THEME_SYSTEM_PROMPT = `你是一位专业的 UI 配色方案设计师。你的任务是生成视觉精美的配色方案。
17
-
18
- ## 输出格式
19
-
20
- ### SVG 格式
21
- 生成一个 400x560 的 SVG 色卡展示图,要求:
22
- 1. 包含渐变背景、色块展示、文字标注
23
- 2. 展示主背景色、次背景色、文字色、强调色、边框色等
24
- 3. 每个颜色用色块+十六进制值标注
25
- 4. 风格要美观,体现配色方案的氛围
26
- 5. 只输出 SVG 代码,不要包含 <script> 标签
27
- 6. 必须以 <svg 开头,以 </svg> 结尾
28
-
29
- ### JSON 格式
30
- 输出一个 JSON 对象,包含以下结构:
31
- {
32
- "name": "主题名称",
33
- "description": "主题描述",
34
- "isDark": true/false,
35
- "variables": {
36
- "--color-bg-primary": "#背景色",
37
- "--color-bg-secondary": "#次背景色",
38
- "--color-bg-tertiary": "#三级背景色",
39
- "--color-text-primary": "#主文字色",
40
- "--color-text-secondary": "#次文字色",
41
- "--color-text-muted": "#弱化文字色",
42
- "--color-accent": "#强调色",
43
- "--color-accent-hover": "#强调色悬停",
44
- "--color-border": "#边框色",
45
- "--color-shadow": "rgba阴影色",
46
- "--color-user-bubble": "#用户气泡色",
47
- "--color-ai-bubble": "#AI气泡色",
48
- "--font-body": "字体",
49
- "--font-heading": "标题字体",
50
- "--border-radius": "圆角大小"
51
- }
52
- }
53
-
54
- ## 配色原则
55
- - 确保文字在背景上有足够对比度(WCAG AA 标准)
56
- - 强调色要醒目但不刺眼
57
- - 背景色层次分明
58
- - 整体配色和谐统一`;
59
-
60
- export async function POST(request: NextRequest) {
61
- try {
62
- const body: GenerateThemeRequest = await request.json();
63
- const { prompt, format, apiConfig } = body;
64
-
65
- if (!prompt) {
66
- return NextResponse.json({ error: '请描述你想要的配色风格' }, { status: 400 });
67
- }
68
-
69
- const aiConfig = await getAiConfig();
70
- const apiKey = apiConfig?.apiKey || aiConfig.apiKey;
71
- const apiBase = apiConfig?.apiBase || aiConfig.apiBase;
72
- const model = apiConfig?.model || aiConfig.model;
73
-
74
- if (!apiKey) {
75
- return NextResponse.json({ error: 'AI API Key 未配置' }, { status: 500 });
76
- }
77
-
78
- const formatInstruction = format === 'svg'
79
- ? '请以 SVG 格式输出一个精美的色卡展示图。只输出 SVG 代码。'
80
- : '请以 JSON 格式输出配色方案。只输出 JSON 代码。';
81
-
82
- const response = await fetch(`${apiBase}/chat/completions`, {
83
- method: 'POST',
84
- headers: {
85
- 'Content-Type': 'application/json',
86
- Authorization: `Bearer ${apiKey}`,
87
- },
88
- body: JSON.stringify({
89
- model,
90
- messages: [
91
- { role: 'system', content: THEME_SYSTEM_PROMPT },
92
- { role: 'user', content: `请设计一个配色方案:${prompt}\n\n${formatInstruction}` },
93
- ],
94
- temperature: 0.9,
95
- max_tokens: 8192,
96
- }),
97
- });
98
-
99
- if (!response.ok) {
100
- return NextResponse.json({ error: `AI 调用失败 (${response.status})` }, { status: 502 });
101
- }
102
-
103
- const data = await response.json();
104
- let content: string = data.choices?.[0]?.message?.content || '';
105
-
106
- content = content.trim();
107
- // 清理 markdown 代码块
108
- if (content.startsWith('```svg')) content = content.slice(6);
109
- else if (content.startsWith('```json')) content = content.slice(7);
110
- else if (content.startsWith('```')) content = content.slice(3);
111
- if (content.endsWith('```')) content = content.slice(0, -3);
112
- content = content.trim();
113
-
114
- if (format === 'svg') {
115
- // 提取 SVG
116
- const start = content.indexOf('<svg');
117
- const end = content.lastIndexOf('</svg>');
118
- if (start !== -1 && end !== -1) content = content.slice(start, end + 6);
119
- if (!content.includes('xmlns')) content = content.replace('<svg', '<svg xmlns="http://www.w3.org/2000/svg"');
120
- // 移除 script 标签
121
- content = content.replace(/<script[^>]*>[\s\S]*?<\/script>/gi, '').trim();
122
- return NextResponse.json({ type: 'svg', content });
123
- } else {
124
- // 提取 JSON
125
- const start = content.indexOf('{');
126
- const end = content.lastIndexOf('}');
127
- if (start !== -1 && end !== -1) content = content.slice(start, end + 1);
128
- const parsed = JSON.parse(content);
129
- return NextResponse.json({ type: 'json', content: parsed });
130
- }
131
- } catch (error) {
132
- console.error('Generate theme error:', error);
133
- return NextResponse.json({ error: '生成失败' }, { status: 500 });
134
- }
135
- }
1
+ // app/api/generate-theme/route.ts - AI 生成配色方案(仅 JSON 格式)
2
+
3
+ import { NextRequest, NextResponse } from 'next/server';
4
+ import { getAiConfig } from '@/lib/server/get-ai-config';
5
+
6
+ interface GenerateThemeRequest {
7
+ prompt: string;
8
+ apiConfig?: {
9
+ apiKey: string;
10
+ apiBase: string;
11
+ model: string;
12
+ };
13
+ }
14
+
15
+ const THEME_SYSTEM_PROMPT = `你是一位专业的 UI 配色方案设计师。你的任务是生成视觉精美的配色方案。
16
+
17
+ ## 输出格式
18
+ 输出一个 JSON 对象,包含以下结构:
19
+ {
20
+ "name": "主题名称",
21
+ "description": "主题描述",
22
+ "isDark": true/false,
23
+ "variables": {
24
+ "--color-bg-primary": "主背景色",
25
+ "--color-bg-secondary": "次背景色",
26
+ "--color-bg-tertiary": "三级背景色",
27
+ "--color-bg-hover": "悬浮背景色",
28
+ "--color-text-primary": "主文字色",
29
+ "--color-text-secondary": "次文字色",
30
+ "--color-text-muted": "弱化文字色",
31
+ "--color-text-hover": "悬浮文字色",
32
+ "--color-accent": "强调色",
33
+ "--color-accent-hover": "强调色悬停",
34
+ "--color-border": "边框色",
35
+ "--color-border-hover": "悬浮边框色",
36
+ "--color-shadow": "rgba阴影色",
37
+ "--color-user-bubble": "用户气泡背景色",
38
+ "--color-ai-bubble": "AI气泡背景色",
39
+ "--color-user-text": "用户气泡文本色",
40
+ "--color-ai-text": "AI气泡文本色",
41
+ "--font-body": "字体",
42
+ "--font-heading": "标题字体",
43
+ "--border-radius": "圆角大小"
44
+ }
45
+ }
46
+
47
+ ## 配色原则
48
+ - 确保文字在背景上有足够对比度(WCAG AA 标准)
49
+ - 强调色要醒目但不刺眼
50
+ - 背景色层次分明
51
+ - 整体配色和谐统一`;
52
+
53
+ export async function POST(request: NextRequest) {
54
+ try {
55
+ const body: GenerateThemeRequest = await request.json();
56
+ const { prompt, apiConfig } = body;
57
+
58
+ if (!prompt) {
59
+ return NextResponse.json({ error: '请描述你想要的配色风格' }, { status: 400 });
60
+ }
61
+
62
+ const aiConfig = await getAiConfig();
63
+ const apiKey = apiConfig?.apiKey || aiConfig.apiKey;
64
+ const apiBase = apiConfig?.apiBase || aiConfig.apiBase;
65
+ const model = apiConfig?.model || aiConfig.model;
66
+
67
+ if (!apiKey) {
68
+ return NextResponse.json({ error: 'AI API Key 未配置' }, { status: 500 });
69
+ }
70
+
71
+ const response = await fetch(`${apiBase}/chat/completions`, {
72
+ method: 'POST',
73
+ headers: {
74
+ 'Content-Type': 'application/json',
75
+ Authorization: `Bearer ${apiKey}`,
76
+ },
77
+ body: JSON.stringify({
78
+ model,
79
+ messages: [
80
+ { role: 'system', content: THEME_SYSTEM_PROMPT },
81
+ { role: 'user', content: `请设计一个配色方案:${prompt}\n\n请以 JSON 格式输出配色方案。只输出 JSON 代码。` },
82
+ ],
83
+ temperature: 0.9,
84
+ max_tokens: 8192,
85
+ }),
86
+ });
87
+
88
+ if (!response.ok) {
89
+ return NextResponse.json({ error: `AI 调用失败 (${response.status})` }, { status: 502 });
90
+ }
91
+
92
+ const data = await response.json();
93
+ let content: string = data.choices?.[0]?.message?.content || '';
94
+
95
+ content = content.trim();
96
+ // 清理 markdown 代码块
97
+ if (content.startsWith('```json')) content = content.slice(7);
98
+ else if (content.startsWith('```')) content = content.slice(3);
99
+ if (content.endsWith('```')) content = content.slice(0, -3);
100
+ content = content.trim();
101
+
102
+ // 提取 JSON
103
+ const start = content.indexOf('{');
104
+ const end = content.lastIndexOf('}');
105
+ if (start !== -1 && end !== -1) content = content.slice(start, end + 1);
106
+ const parsed = JSON.parse(content);
107
+ return NextResponse.json({ type: 'json', content: parsed });
108
+ } catch (error) {
109
+ console.error('Generate theme error:', error);
110
+ return NextResponse.json({ error: '生成失败' }, { status: 500 });
111
+ }
112
+ }
@@ -1,8 +1,8 @@
1
1
  // app/api/plugins/bindings/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';
4
+ import { execute } from '@/lib/server/db';
5
+ import { ensureDb } from '@/lib/server/db-init';
6
6
 
7
7
  /** 解析 JSON 字段 */
8
8
  function parseJsonField<T>(value: unknown): T {
@@ -47,14 +47,14 @@ export async function GET(request: NextRequest) {
47
47
  return NextResponse.json(rowToBinding((rows as Record<string, unknown>[])[0]));
48
48
  }
49
49
 
50
- let query = 'SELECT * FROM extension_bindings WHERE 1=1';
50
+ let query = 'SELECT * FROM extension_bindings WHERE TRUE';
51
51
  const params: (string | null)[] = [];
52
52
 
53
53
  if (scope) {
54
54
  query += ' AND scope = ?';
55
55
  params.push(scope);
56
56
  }
57
- if (worldId) {
57
+ if (scope === 'world' && worldId) {
58
58
  query += ' AND world_id = ?';
59
59
  params.push(worldId);
60
60
  }
@@ -1,9 +1,12 @@
1
1
  // app/api/plugins/export/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 { readPluginCode } from '@/lib/plugin-files';
4
+
5
+ export const dynamic = 'force-dynamic';
6
+
7
+ import { execute } from '@/lib/server/db';
8
+ import { ensureDb } from '@/lib/server/db-init';
9
+ import { readPluginCode } from '@/lib/server/plugin-files';
7
10
 
8
11
  /** 解析 JSON 字段 */
9
12
  function parseJsonField<T>(value: unknown): T {
@@ -81,10 +84,12 @@ export async function GET(request: NextRequest) {
81
84
  [pluginId]
82
85
  );
83
86
 
87
+ // @ts-ignore
84
88
  if (rows.length === 0) {
85
89
  return NextResponse.json({ error: '插件不存在' }, { status: 404 });
86
90
  }
87
91
 
92
+ // @ts-ignore
88
93
  const plugin = await fillPluginCode(rowToPlugin(rows[0]));
89
94
 
90
95
  return new NextResponse(JSON.stringify(plugin, null, 2), {
@@ -100,6 +105,7 @@ export async function GET(request: NextRequest) {
100
105
  'SELECT * FROM extensions ORDER BY updated_at DESC'
101
106
  );
102
107
 
108
+ // @ts-ignore
103
109
  const plugins = await Promise.all(rows.map(r => fillPluginCode(rowToPlugin(r))));
104
110
 
105
111
  const exportData = {
@@ -1,10 +1,13 @@
1
1
  // app/api/plugins/export-xye/route.ts - 导出插件为 .xye 包(ZIP 格式)
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 { getPluginDir, readPluginCode } from '@/lib/plugin-files';
7
- import { listPluginFiles } from '@/lib/plugin-files';
4
+
5
+ export const dynamic = 'force-dynamic';
6
+
7
+ import { execute } from '@/lib/server/db';
8
+ import { ensureDb } from '@/lib/server/db-init';
9
+ import { getPluginDir, readPluginCode } from '@/lib/server/plugin-files';
10
+ import { listPluginFiles } from '@/lib/server/plugin-files';
8
11
  import AdmZip from 'adm-zip';
9
12
  import path from 'path';
10
13
  import { promises as fs } from 'fs';
@@ -54,10 +57,12 @@ export async function GET(request: NextRequest) {
54
57
  [pluginId]
55
58
  );
56
59
 
60
+ // @ts-ignore
57
61
  if (rows.length === 0) {
58
62
  return NextResponse.json({ error: '插件不存在' }, { status: 404 });
59
63
  }
60
64
 
65
+ // @ts-ignore
61
66
  const row = rows[0];
62
67
  const perms = parsePermissions(row.ui_permissions);
63
68
  const pluginDir = getPluginDir(pluginId);
@@ -92,9 +97,9 @@ export async function GET(request: NextRequest) {
92
97
  code = await readPluginCode(pluginId);
93
98
  } catch { /* empty */ }
94
99
 
95
- // 从 code_path 提取入口文件名
96
- const codePath = row.code_path as string || '';
97
- const entryName = codePath ? codePath.split('/').pop() || 'plugin.js' : 'plugin.js';
100
+ // 从 file_entry 提取入口文件名
101
+ const fileEntry = row.file_entry as string || '';
102
+ const entryName = fileEntry ? fileEntry.split('/').pop() || 'plugin.js' : 'plugin.js';
98
103
 
99
104
  // 生成 manifest.json
100
105
  const manifest: Record<string, unknown> = {
@@ -13,19 +13,19 @@ function isValidPluginId(id: string): boolean {
13
13
  }
14
14
 
15
15
  /** 验证文件名格式(防止路径遍历) */
16
- function isValidFileName(name: string): boolean {
16
+ function isValidFileName(name: string | any): boolean {
17
17
  // 禁止路径分隔符、.. 和空名称
18
- return typeof name === 'string' &&
19
- name.length > 0 &&
20
- name.length <= 255 &&
21
- !name.includes('..') &&
22
- !name.includes('/') &&
23
- !name.includes('\\') &&
24
- !name.includes('\0');
18
+ return typeof name === 'string' && name.length > 0 && name.length <= 255 && !name.includes('..') &&
19
+ !name.includes('/') && !name.includes('\\') && !name.includes('\0');
25
20
  }
26
21
 
27
- /** 获取插件的私有数据目录路径(resources 子目录) */
22
+ /** 获取插件的根目录路径 */
28
23
  function getPluginDir(pluginId: string): string {
24
+ return path.join(PLUGIN_FILES_ROOT, pluginId);
25
+ }
26
+
27
+ /** 获取插件资源文件的根目录路径 */
28
+ function getPluginResourceDir(pluginId: string): string {
29
29
  return path.join(PLUGIN_FILES_ROOT, pluginId, 'resources');
30
30
  }
31
31
 
@@ -1,9 +1,12 @@
1
1
  // app/api/plugins/files-list/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 { listPluginFiles, readPluginResourceText } from '@/lib/plugin-files';
4
+
5
+ export const dynamic = 'force-dynamic';
6
+
7
+ import { ensureDb } from '@/lib/server/db-init';
8
+ import { execute } from '@/lib/server/db';
9
+ import { listPluginFiles, readPluginResourceText } from '@/lib/server/plugin-files';
7
10
  import path from 'path';
8
11
 
9
12
  function isValidPluginId(id: string): boolean {
@@ -61,6 +64,7 @@ export async function GET(request: NextRequest) {
61
64
  [pluginId]
62
65
  );
63
66
 
67
+ // @ts-ignore
64
68
  if (rows.length === 0) {
65
69
  return NextResponse.json({ error: '插件不存在' }, { status: 404 });
66
70
  }
@@ -1,9 +1,9 @@
1
1
  // app/api/plugins/files-upload/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 { getPluginDir } from '@/lib/plugin-files';
4
+ import { ensureDb } from '@/lib/server/db-init';
5
+ import { execute } from '@/lib/server/db';
6
+ import { getPluginDir } from '@/lib/server/plugin-files';
7
7
  import path from 'path';
8
8
  import { promises as fs } from 'fs';
9
9