feat(web): D 消息编辑/删除前端入口 + 日志聚合前端(Observability后端tail) + 修api-client契约
- D前端: lib/api/messages.ts(patchMessage/deleteMessage,兼容裸DTO与{message}) + MessageActions.tsx(仅user消息; 单击/右键/450ms长按开Miuix玻璃菜单; 编辑弹窗预填+字数→乐观updateMessageById→patchMessage→失败回滚; 删除确认→deleteMessage→removeMessageById; 乐观气泡禁用) + chat.tsx挂载 + Icon加edit/trash/more
- 日志前端: types(AdminLogLine time:string|number) + adminSystem.adminObservabilityLogs + Observability.tsx 后端分级tail(level筛选chip注明'该级及以上'+等宽行HH:mm:ss.SSS·level着色·source·msg+30s刷新) 下半保留WS回显
- Docs/api-client §6a/§4.2 修正: PATCH /messages/{id} 实际返回 {data:<DTO>} 非 {data:{message}}(前端已双向兼容,避免误导Android)
- 前端 build 绿(167 modules); SSR冒烟11 checks
- 遗留: 未实机登录; 菜单absolute理论上边缘可裁切(可改portal)
This commit is contained in:
1 parent
025dd1796b
commit
1ac126fc9e
9 files changed
+611
-12
No files matched your search
@@ -1,4 +1,6 @@
|
||||
import type {
|
||||
AdminLogLine,
|
||||
AdminLogQuery,
|
||||
AdminNotification,
|
||||
AdminNotificationQuery,
|
||||
AdminObservabilityMetrics,
|
||||
@@ -24,6 +26,18 @@ export function adminObservabilityTrace(limit?: number): Promise<AdminTraceSpan[
|
||||
export function adminObservabilityAudit(limit?: number): Promise<AuditRecord[]> {
|
||||
return request<AuditRecord[]>(`/admin/observability/audit${limitQuery(limit)}`);
|
||||
}
|
||||
/** GET /api/v1/admin/observability/logs?level=&limit= — the gateway's structured
|
||||
* log tail, newest first (owner/admin only). An omitted `level` means “all”; the
|
||||
* backend defaults `limit` to 200 and caps it at 1000. */
|
||||
export function adminObservabilityLogs(
|
||||
params: AdminLogQuery = {}
|
||||
): Promise<AdminLogLine[]> {
|
||||
const qs = new URLSearchParams();
|
||||
if (params.level) qs.set("level", params.level);
|
||||
if (params.limit !== undefined) qs.set("limit", String(params.limit));
|
||||
const q = qs.toString();
|
||||
return request<AdminLogLine[]>(`/admin/observability/logs${q ? `?${q}` : ""}`);
|
||||
}
|
||||
|
||||
/** GET /api/v1/admin/backthink — persisted background-think records (newest first). */
|
||||
export function adminBackthink(limit?: number): Promise<BackthinkItem[]> {
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
import type { MessageDto } from "./sessions";
|
||||
import { request } from "./core";
|
||||
|
||||
/**
|
||||
* Single-message mutations (design/16 §4.2 D3, Docs/api-client.md §6a/6b).
|
||||
* Both succeed against the persisted row and broadcast the change over the WS
|
||||
* hub (`message_edited` / `message_deleted`), so other clients converge without
|
||||
* a re-fetch; the caller's REST response is only the acknowledgement.
|
||||
*/
|
||||
|
||||
/** The gateway answers PATCH with `{data: <updated message DTO>}` (see
|
||||
* gateway/history_write.go `writeJSON(w, 200, toHistoryMessage(msg))` and
|
||||
* history_write_broadcast_test.go, which decodes `data` straight into the DTO).
|
||||
* Docs/api-client.md §6a writes that envelope as `{data:{message}}`; the optional
|
||||
* `message` field keeps this call working either way, so a plain DTO is never
|
||||
* mistaken for an empty result (which would blank the bubble). */
|
||||
type PatchMessageEnvelope = MessageDto & { message?: MessageDto };
|
||||
|
||||
/** PATCH /api/v1/messages/{id} — replace one message's `content`. The backend
|
||||
* validates non-empty `≤4000` runes (400) and existence (404), and only ever
|
||||
* changes `content`; role / timestamps / platform are preserved. */
|
||||
export async function patchMessage(id: string, content: string): Promise<MessageDto> {
|
||||
const res = await request<PatchMessageEnvelope>(
|
||||
`/messages/${encodeURIComponent(id)}`,
|
||||
{ method: "PATCH", body: JSON.stringify({ content }) }
|
||||
);
|
||||
return res.message ?? res;
|
||||
}
|
||||
|
||||
/** DELETE /api/v1/messages/{id} — remove one message (404 when already gone).
|
||||
* The response echoes the deleted id, so a caller can assert what it removed. */
|
||||
export function deleteMessage(id: string): Promise<{ deleted: string }> {
|
||||
return request<{ deleted: string }>(`/messages/${encodeURIComponent(id)}`, {
|
||||
method: "DELETE",
|
||||
});
|
||||
}
|
||||
Reference in new issue
Block a user