mioku-service-webui 2.0.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.
- package/LICENSE +21 -0
- package/README.md +2 -0
- package/auth.ts +48 -0
- package/config-page-loader.ts +109 -0
- package/database.ts +322 -0
- package/datasources.ts +187 -0
- package/index.ts +231 -0
- package/package.json +20 -0
- package/routes/ai.ts +191 -0
- package/routes/config.ts +52 -0
- package/routes/db.ts +218 -0
- package/routes/index.ts +6 -0
- package/routes/manage.ts +92 -0
- package/routes/store.ts +475 -0
- package/routes/webui.ts +35 -0
- package/system.ts +1894 -0
- package/types.ts +45 -0
- package/utils.ts +114 -0
package/routes/db.ts
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import { Hono } from "hono";
|
|
2
|
+
import * as fs from "node:fs";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
import {
|
|
5
|
+
deleteMessagesByRange,
|
|
6
|
+
exportData,
|
|
7
|
+
getPluginConfigs,
|
|
8
|
+
getStats,
|
|
9
|
+
importDataFromJson,
|
|
10
|
+
listMemeTree,
|
|
11
|
+
listTables,
|
|
12
|
+
queryMessages,
|
|
13
|
+
savePluginConfig,
|
|
14
|
+
updateMessage,
|
|
15
|
+
} from "../database";
|
|
16
|
+
import { CHAT_DATA_DIR } from "../utils";
|
|
17
|
+
import { loadPluginConfigPage } from "../config-page-loader";
|
|
18
|
+
import { getDatasource } from "../datasources";
|
|
19
|
+
|
|
20
|
+
export function createDBRoutes() {
|
|
21
|
+
const app = new Hono();
|
|
22
|
+
|
|
23
|
+
app.get("/tables", (c) =>
|
|
24
|
+
c.json({ ok: true, data: listTables() }),
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
app.get("/messages", (c) => {
|
|
28
|
+
const q = c.req.query();
|
|
29
|
+
const data = queryMessages({
|
|
30
|
+
table: q.table,
|
|
31
|
+
keyword: q.keyword,
|
|
32
|
+
userId: q.userId,
|
|
33
|
+
sessionId: q.sessionId,
|
|
34
|
+
startTime: q.startTime ? Number(q.startTime) : undefined,
|
|
35
|
+
endTime: q.endTime ? Number(q.endTime) : undefined,
|
|
36
|
+
page: q.page ? Number(q.page) : 1,
|
|
37
|
+
pageSize: q.pageSize ? Number(q.pageSize) : 20,
|
|
38
|
+
});
|
|
39
|
+
return c.json({ ok: true, data });
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
app.get("/stats", (c) =>
|
|
43
|
+
c.json({ ok: true, data: getStats() }),
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
app.put("/message", async (c) => {
|
|
47
|
+
const body = await c.req.json();
|
|
48
|
+
return c.json({ ok: true, data: updateMessage(body) });
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
app.post("/cleanup", async (c) => {
|
|
52
|
+
const body = await c.req.json();
|
|
53
|
+
return c.json({ ok: true, data: deleteMessagesByRange(body) });
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
app.get("/export", (c) => {
|
|
57
|
+
const format = (c.req.query("format") === "csv" ? "csv" : "json") as
|
|
58
|
+
| "json"
|
|
59
|
+
| "csv";
|
|
60
|
+
const result = exportData(format);
|
|
61
|
+
return c.json({ ok: true, data: result });
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
app.post("/import", async (c) => {
|
|
65
|
+
const form = await c.req.formData();
|
|
66
|
+
const file = form.get("file") as File | null;
|
|
67
|
+
if (!file) {
|
|
68
|
+
return c.json({ ok: false, error: "FILE_REQUIRED" }, 400);
|
|
69
|
+
}
|
|
70
|
+
const result = importDataFromJson(file as any);
|
|
71
|
+
return c.json({ ok: true, data: result });
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
return app;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function createPluginConfigRoutes() {
|
|
78
|
+
const app = new Hono();
|
|
79
|
+
|
|
80
|
+
app.get("/overview", (c) => {
|
|
81
|
+
const pluginDir = path.join(process.cwd(), "plugins");
|
|
82
|
+
if (!fs.existsSync(pluginDir)) {
|
|
83
|
+
return c.json({ ok: true, data: [] });
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const plugins = fs.readdirSync(pluginDir).filter((name) => {
|
|
87
|
+
const pluginPath = path.join(pluginDir, name);
|
|
88
|
+
return fs.statSync(pluginPath).isDirectory();
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
const overview = plugins
|
|
92
|
+
.map((name) => {
|
|
93
|
+
const configDir = path.join(process.cwd(), "config", name);
|
|
94
|
+
const manifest = loadPluginConfigPage(name);
|
|
95
|
+
const existingConfigFiles =
|
|
96
|
+
fs.existsSync(configDir)
|
|
97
|
+
? fs
|
|
98
|
+
.readdirSync(configDir)
|
|
99
|
+
.filter((f) => f.endsWith(".json"))
|
|
100
|
+
.map((f) => f.replace(".json", ""))
|
|
101
|
+
: [];
|
|
102
|
+
const declaredConfigFiles = manifest
|
|
103
|
+
? Array.from(
|
|
104
|
+
new Set(
|
|
105
|
+
(manifest.fields || [])
|
|
106
|
+
.map((field) => String(field.key || ""))
|
|
107
|
+
.filter((key) => key.includes("."))
|
|
108
|
+
.map((key) => key.split(".")[0]),
|
|
109
|
+
),
|
|
110
|
+
)
|
|
111
|
+
: [];
|
|
112
|
+
const configFiles = Array.from(
|
|
113
|
+
new Set([...existingConfigFiles, ...declaredConfigFiles]),
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
return {
|
|
117
|
+
name,
|
|
118
|
+
title: manifest?.title || name,
|
|
119
|
+
description: manifest?.description,
|
|
120
|
+
hasPage: manifest?.hasCustomPage || false,
|
|
121
|
+
configFiles,
|
|
122
|
+
};
|
|
123
|
+
})
|
|
124
|
+
.filter((p) => p.hasPage);
|
|
125
|
+
|
|
126
|
+
return c.json({ ok: true, data: overview });
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
app.get("/:name/page", (c) => {
|
|
130
|
+
const name = c.req.param("name");
|
|
131
|
+
const manifest = loadPluginConfigPage(name);
|
|
132
|
+
const configs = getPluginConfigs(name);
|
|
133
|
+
|
|
134
|
+
if (!manifest) {
|
|
135
|
+
return c.json({ ok: true, data: null });
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return c.json({
|
|
139
|
+
ok: true,
|
|
140
|
+
data: {
|
|
141
|
+
plugin: manifest.plugin,
|
|
142
|
+
title: manifest.title,
|
|
143
|
+
description: manifest.description,
|
|
144
|
+
markdown: manifest.markdown,
|
|
145
|
+
fields: manifest.fields,
|
|
146
|
+
hasCustomPage: manifest.hasCustomPage,
|
|
147
|
+
configs,
|
|
148
|
+
},
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
app.get("/datasources/:source", async (c) => {
|
|
153
|
+
const source = c.req.param("source");
|
|
154
|
+
const q = c.req.query("q") || undefined;
|
|
155
|
+
const limitText = c.req.query("limit");
|
|
156
|
+
const limit = limitText ? Number(limitText) : undefined;
|
|
157
|
+
const options = await getDatasource(source, {
|
|
158
|
+
q,
|
|
159
|
+
limit: Number.isFinite(limit) ? limit : undefined,
|
|
160
|
+
});
|
|
161
|
+
return c.json({ ok: true, data: options });
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
app.get("/:name", (c) => {
|
|
165
|
+
const name = c.req.param("name");
|
|
166
|
+
return c.json({ ok: true, data: getPluginConfigs(name) });
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
app.put("/:name/:config", async (c) => {
|
|
170
|
+
const pluginName = c.req.param("name");
|
|
171
|
+
const configName = c.req.param("config");
|
|
172
|
+
const body = await c.req.json();
|
|
173
|
+
savePluginConfig(pluginName, configName, body);
|
|
174
|
+
return c.json({ ok: true });
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
return app;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export function createMemeRoutes() {
|
|
181
|
+
const app = new Hono();
|
|
182
|
+
|
|
183
|
+
app.get("/tree", (c) =>
|
|
184
|
+
c.json({ ok: true, data: listMemeTree() }),
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
app.post("/upload", async (c) => {
|
|
188
|
+
const form = await c.req.formData();
|
|
189
|
+
const character = String(form.get("character") || "unknown");
|
|
190
|
+
const emotion = String(form.get("emotion") || "default");
|
|
191
|
+
const file = form.get("file") as File | null;
|
|
192
|
+
if (!file) {
|
|
193
|
+
return c.json({ ok: false, error: "FILE_REQUIRED" }, 400);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const dir = path.join(CHAT_DATA_DIR, "meme", character, emotion);
|
|
197
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
198
|
+
|
|
199
|
+
const fileName = file.name || `upload-${Date.now()}.png`;
|
|
200
|
+
const filePath = path.join(dir, fileName);
|
|
201
|
+
const buffer = Buffer.from(await file.arrayBuffer());
|
|
202
|
+
fs.writeFileSync(filePath, buffer);
|
|
203
|
+
|
|
204
|
+
return c.json({ ok: true, path: path.relative(process.cwd(), filePath) });
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
app.delete("/", async (c) => {
|
|
208
|
+
const body = await c.req.json();
|
|
209
|
+
const filePath = path.join(process.cwd(), String(body.path || ""));
|
|
210
|
+
if (!fs.existsSync(filePath)) {
|
|
211
|
+
return c.json({ ok: false, error: "NOT_FOUND" }, 404);
|
|
212
|
+
}
|
|
213
|
+
fs.unlinkSync(filePath);
|
|
214
|
+
return c.json({ ok: true });
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
return app;
|
|
218
|
+
}
|
package/routes/index.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { createConfigRoutes } from "./config";
|
|
2
|
+
export { createWebUISettingsRoutes } from "./webui";
|
|
3
|
+
export { createAIRoutes } from "./ai";
|
|
4
|
+
export { createDBRoutes, createPluginConfigRoutes, createMemeRoutes } from "./db";
|
|
5
|
+
export { createManageRoutes } from "./manage";
|
|
6
|
+
export { createStoreRoutes } from "./store";
|
package/routes/manage.ts
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { Hono } from "hono";
|
|
2
|
+
import type {
|
|
3
|
+
ChangeRepoRequest,
|
|
4
|
+
InstallRequest,
|
|
5
|
+
RemoveRequest,
|
|
6
|
+
UpdateAllRequest,
|
|
7
|
+
UpdateRequest,
|
|
8
|
+
} from "../types";
|
|
9
|
+
import {
|
|
10
|
+
changeManagedPackageRepo,
|
|
11
|
+
getManagedPackageDetail,
|
|
12
|
+
installManagedPackage,
|
|
13
|
+
listManagedPackages,
|
|
14
|
+
listManagedPackagesWithUpdates,
|
|
15
|
+
updateManagedPackage,
|
|
16
|
+
updateAllManagedPackages,
|
|
17
|
+
checkUpdate,
|
|
18
|
+
removeManagedPackage,
|
|
19
|
+
} from "../system";
|
|
20
|
+
|
|
21
|
+
export function createManageRoutes() {
|
|
22
|
+
const app = new Hono();
|
|
23
|
+
|
|
24
|
+
app.get("/plugins", (c) =>
|
|
25
|
+
c.json({ ok: true, data: listManagedPackages("plugin") }),
|
|
26
|
+
);
|
|
27
|
+
app.get("/services", (c) =>
|
|
28
|
+
c.json({ ok: true, data: listManagedPackages("service") }),
|
|
29
|
+
);
|
|
30
|
+
app.get("/plugins/overview", async (c) =>
|
|
31
|
+
c.json({ ok: true, data: await listManagedPackagesWithUpdates("plugin") }),
|
|
32
|
+
);
|
|
33
|
+
app.get("/plugins/:name", async (c) => {
|
|
34
|
+
const name = c.req.param("name");
|
|
35
|
+
const result = await getManagedPackageDetail(name, "plugin");
|
|
36
|
+
return c.json(result);
|
|
37
|
+
});
|
|
38
|
+
app.get("/services/overview", async (c) =>
|
|
39
|
+
c.json({ ok: true, data: await listManagedPackagesWithUpdates("service") }),
|
|
40
|
+
);
|
|
41
|
+
app.get("/services/:name", async (c) => {
|
|
42
|
+
const name = c.req.param("name");
|
|
43
|
+
const result = await getManagedPackageDetail(name, "service");
|
|
44
|
+
return c.json(result);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
app.post("/install", async (c) => {
|
|
48
|
+
const body = (await c.req.json()) as InstallRequest;
|
|
49
|
+
const result = await installManagedPackage(body);
|
|
50
|
+
return c.json(result);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
app.post("/check-update", async (c) => {
|
|
54
|
+
const body = (await c.req.json()) as {
|
|
55
|
+
name: string;
|
|
56
|
+
target: "plugin" | "service";
|
|
57
|
+
};
|
|
58
|
+
const result = await checkUpdate(body.name, body.target);
|
|
59
|
+
return c.json(result);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
app.post("/update", async (c) => {
|
|
63
|
+
const body = (await c.req.json()) as UpdateRequest;
|
|
64
|
+
const result = await updateManagedPackage(body);
|
|
65
|
+
return c.json(result);
|
|
66
|
+
});
|
|
67
|
+
app.post("/update-all", async (c) => {
|
|
68
|
+
const body = (await c.req.json().catch(() => ({}))) as Partial<UpdateAllRequest>;
|
|
69
|
+
const result = await updateAllManagedPackages({
|
|
70
|
+
target: body.target || "plugin",
|
|
71
|
+
packageManager: body.packageManager,
|
|
72
|
+
});
|
|
73
|
+
return c.json(result);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
app.post("/remove", async (c) => {
|
|
77
|
+
const body = (await c.req.json()) as RemoveRequest;
|
|
78
|
+
const result = await removeManagedPackage(body);
|
|
79
|
+
return c.json(result);
|
|
80
|
+
});
|
|
81
|
+
app.post("/change-repo", async (c) => {
|
|
82
|
+
const body = (await c.req.json()) as Partial<ChangeRepoRequest>;
|
|
83
|
+
const result = await changeManagedPackageRepo(
|
|
84
|
+
String(body.name || ""),
|
|
85
|
+
(body.target || "plugin") as "plugin" | "service",
|
|
86
|
+
String(body.repoUrl || ""),
|
|
87
|
+
);
|
|
88
|
+
return c.json(result);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
return app;
|
|
92
|
+
}
|