@lark-apaas/client-toolkit 1.1.37-alpha.34 → 1.1.38-alpha.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/README.md +0 -68
- package/lib/components/AppContainer/index.d.ts +1 -4
- package/lib/components/AppContainer/index.js +24 -19
- package/lib/server-log/index.d.ts +9 -3
- package/lib/server-log/index.js +2 -1
- package/lib/server-log/sse-client.d.ts +108 -0
- package/lib/server-log/sse-client.js +209 -0
- package/lib/server-log/types.d.ts +9 -0
- package/package.json +2 -8
- package/lib/auth.d.ts +0 -1
- package/lib/auth.js +0 -2
package/README.md
CHANGED
|
@@ -6,71 +6,3 @@
|
|
|
6
6
|
|
|
7
7
|
* 导出所有对外暴露的 API 和组件,并通过 npm exports 来控制
|
|
8
8
|
* 其他目录作为 internal 实现,不对外暴露
|
|
9
|
-
|
|
10
|
-
### auth 目录
|
|
11
|
-
|
|
12
|
-
* 包含与权限相关的 API 和组件
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
### 开发组件 - 使用 CanRole 组件 (推荐)
|
|
16
|
-
|
|
17
|
-
```tsx
|
|
18
|
-
import { CanRole } from '@lark-apaas/client-toolkit/auth';
|
|
19
|
-
|
|
20
|
-
function Home() {
|
|
21
|
-
return (
|
|
22
|
-
<div>
|
|
23
|
-
<CanRole roles={['role_admin']}>
|
|
24
|
-
<div>管理员按钮</div>
|
|
25
|
-
</CanRole>
|
|
26
|
-
<CanRole roles={['role_admin', 'role_editor']}>
|
|
27
|
-
<div>编辑按钮</div>
|
|
28
|
-
</CanRole>
|
|
29
|
-
</div>
|
|
30
|
-
);
|
|
31
|
-
}
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
### 开发组件 - 使用 AbilityContext 处理复杂场景
|
|
35
|
-
|
|
36
|
-
```tsx
|
|
37
|
-
import { useContext } from 'react';
|
|
38
|
-
import { AbilityContext, ROLE_SUBJECT } from '@lark-apaas/client-toolkit/auth';
|
|
39
|
-
|
|
40
|
-
function Home() {
|
|
41
|
-
const ability = useContext(AbilityContext);
|
|
42
|
-
return (
|
|
43
|
-
<div>
|
|
44
|
-
{ability.can('role_admin', ROLE_SUBJECT) || ability.can('role_editor', ROLE_SUBJECT) ? (
|
|
45
|
-
<div>可见的仪表盘</div>
|
|
46
|
-
) : null}
|
|
47
|
-
</div>
|
|
48
|
-
);
|
|
49
|
-
}
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
### 开发组件 - 进阶示例
|
|
53
|
-
|
|
54
|
-
### 菜单按权限过滤
|
|
55
|
-
|
|
56
|
-
```tsx
|
|
57
|
-
import { useContext } from 'react';
|
|
58
|
-
import { AbilityContext, ROLE_SUBJECT } from '@lark-apaas/client-toolkit/auth';
|
|
59
|
-
|
|
60
|
-
const menus = [
|
|
61
|
-
{ name: 'Dashboard', path: '/dashboard', p: { action: 'role_admin', subject: ROLE_SUBJECT } },
|
|
62
|
-
{ name: 'Users', path: '/users', p: { action: 'role_editor', subject: ROLE_SUBJECT } },
|
|
63
|
-
{ name: 'Settings', path: '/settings', p: { action: 'role_admin', subject: ROLE_SUBJECT } },
|
|
64
|
-
];
|
|
65
|
-
|
|
66
|
-
function Nav() {
|
|
67
|
-
const ability = useContext(AbilityContext);
|
|
68
|
-
return (
|
|
69
|
-
<nav>
|
|
70
|
-
{menus.map(m => ability.can(m.p.action, m.p.subject) && (
|
|
71
|
-
<a key={m.path} href={m.path}>{m.name}</a>
|
|
72
|
-
))}
|
|
73
|
-
</nav>
|
|
74
|
-
);
|
|
75
|
-
}
|
|
76
|
-
```
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { IBaseThemeProviderProps } from '../theme';
|
|
3
3
|
import '../../index.css';
|
|
4
|
-
interface IBaseAuthProviderProps {
|
|
5
|
-
enableAuth?: boolean;
|
|
6
|
-
}
|
|
7
4
|
declare const AppContainer: React.FC<{
|
|
8
5
|
children: React.ReactNode;
|
|
9
|
-
} & IBaseThemeProviderProps
|
|
6
|
+
} & IBaseThemeProviderProps>;
|
|
10
7
|
export default AppContainer;
|
|
@@ -15,10 +15,9 @@ import { useAppInfo } from "../../hooks/index.js";
|
|
|
15
15
|
import { TrackKey } from "../../types/tea.js";
|
|
16
16
|
import safety from "./safety.js";
|
|
17
17
|
import { getAppId } from "../../utils/getAppId.js";
|
|
18
|
-
import {
|
|
18
|
+
import { ServerLogSSEClient } from "../../server-log/index.js";
|
|
19
19
|
import QueryProvider from "../QueryProvider/index.js";
|
|
20
20
|
import { initObservable } from "./utils/observable.js";
|
|
21
|
-
import { AuthProvider } from "@lark-apaas/auth-sdk";
|
|
22
21
|
registerDayjsPlugins();
|
|
23
22
|
initAxiosConfig();
|
|
24
23
|
initObservable();
|
|
@@ -33,9 +32,9 @@ const readCssVarColor = (varName, fallback)=>{
|
|
|
33
32
|
}
|
|
34
33
|
};
|
|
35
34
|
const App = (props)=>{
|
|
36
|
-
const { themeMeta = {}
|
|
35
|
+
const { themeMeta = {} } = props;
|
|
37
36
|
useAppInfo();
|
|
38
|
-
const
|
|
37
|
+
const serverLogClientRef = useRef(null);
|
|
39
38
|
const { rem } = findValueByPixel(themeMetaOptions.themeRadius, themeMeta.borderRadius) || {
|
|
40
39
|
rem: '0.625'
|
|
41
40
|
};
|
|
@@ -51,22 +50,32 @@ const App = (props)=>{
|
|
|
51
50
|
if ('production' !== process.env.NODE_ENV && window.parent !== window) {
|
|
52
51
|
try {
|
|
53
52
|
const backendUrl = window.location.origin;
|
|
54
|
-
|
|
53
|
+
serverLogClientRef.current = new ServerLogSSEClient({
|
|
55
54
|
serverUrl: backendUrl,
|
|
56
|
-
|
|
57
|
-
pollInterval: 2000,
|
|
58
|
-
limit: 100,
|
|
55
|
+
sseEndpoint: '/dev/logs/server-logs/stream',
|
|
59
56
|
debug: true
|
|
60
57
|
});
|
|
61
|
-
|
|
62
|
-
console.log('[AppContainer] Server log
|
|
58
|
+
serverLogClientRef.current.start();
|
|
59
|
+
console.log('[AppContainer] Server log SSE client started');
|
|
63
60
|
} catch (error) {
|
|
64
|
-
console.error('[AppContainer] Failed to start server log
|
|
61
|
+
console.error('[AppContainer] Failed to start server log SSE client:', error);
|
|
65
62
|
}
|
|
63
|
+
const handleVisibilityChange = ()=>{
|
|
64
|
+
if (!serverLogClientRef.current) return;
|
|
65
|
+
if (document.hidden) {
|
|
66
|
+
serverLogClientRef.current.pause();
|
|
67
|
+
console.log('[AppContainer] Tab hidden, SSE paused');
|
|
68
|
+
} else {
|
|
69
|
+
serverLogClientRef.current.resume();
|
|
70
|
+
console.log('[AppContainer] Tab visible, SSE resumed');
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
document.addEventListener('visibilitychange', handleVisibilityChange);
|
|
66
74
|
return ()=>{
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
75
|
+
document.removeEventListener('visibilitychange', handleVisibilityChange);
|
|
76
|
+
if (serverLogClientRef.current) {
|
|
77
|
+
serverLogClientRef.current.stop();
|
|
78
|
+
console.log('[AppContainer] Server log SSE client stopped');
|
|
70
79
|
}
|
|
71
80
|
};
|
|
72
81
|
}
|
|
@@ -89,10 +98,7 @@ const App = (props)=>{
|
|
|
89
98
|
}
|
|
90
99
|
});
|
|
91
100
|
}, []);
|
|
92
|
-
return /*#__PURE__*/ jsxs(
|
|
93
|
-
config: {
|
|
94
|
-
enable: enableAuth
|
|
95
|
-
},
|
|
101
|
+
return /*#__PURE__*/ jsxs(Fragment, {
|
|
96
102
|
children: [
|
|
97
103
|
/*#__PURE__*/ jsx(Toaster, {}),
|
|
98
104
|
'production' !== process.env.NODE_ENV && /*#__PURE__*/ jsx(MiaodaInspector, {
|
|
@@ -239,7 +245,6 @@ const AppContainer_AppContainer = (props)=>{
|
|
|
239
245
|
},
|
|
240
246
|
children: /*#__PURE__*/ jsx(App, {
|
|
241
247
|
themeMeta: props.themeMeta,
|
|
242
|
-
enableAuth: props.enableAuth,
|
|
243
248
|
children: children
|
|
244
249
|
})
|
|
245
250
|
})
|
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Server Log 模块
|
|
3
3
|
*
|
|
4
|
-
* 通过
|
|
4
|
+
* 通过 SSE (Server-Sent Events) 方式获取服务端日志并转发给父窗口
|
|
5
|
+
*
|
|
6
|
+
* 变更说明 (2026-01-04):
|
|
7
|
+
* - 从 HTTP 轮询改为 SSE,降低延迟和网络压力
|
|
8
|
+
* - ServerLogPoller 已弃用,使用 ServerLogSSEClient 替代
|
|
5
9
|
*/
|
|
10
|
+
export { ServerLogSSEClient } from './sse-client';
|
|
11
|
+
export type { ServerLogSSEClientOptions } from './sse-client';
|
|
6
12
|
export { ServerLogPoller } from './poller';
|
|
7
13
|
export type { ServerLogPollerOptions } from './poller';
|
|
8
|
-
export type { ServerLog, ServerLogLevel, ServerLogSource, ServerLogMeta, ServerLogPostMessage, ClientToServerMessage, ServerToClientMessage, } from './types';
|
|
9
|
-
export {
|
|
14
|
+
export type { ServerLog, ServerLogLevel, ServerLogSource, ServerLogMeta, ServerLogPostMessage, ServerLogControlMessage, ClientToServerMessage, ServerToClientMessage, } from './types';
|
|
15
|
+
export { ServerLogSSEClient as default } from './sse-client';
|
package/lib/server-log/index.js
CHANGED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server Log SSE Client (SSE 版本)
|
|
3
|
+
*
|
|
4
|
+
* 职责:
|
|
5
|
+
* 1. 通过 EventSource 连接后端 SSE 端点 (/dev/logs/server-logs/stream)
|
|
6
|
+
* 2. 实时接收服务端日志
|
|
7
|
+
* 3. 将接收到的日志通过 postMessage 转发给父窗口 (miaoda)
|
|
8
|
+
* 4. 管理连接状态并通知父窗口
|
|
9
|
+
* 5. 支持通过 postMessage 从父窗口控制 (pause/resume/stop)
|
|
10
|
+
*
|
|
11
|
+
* 优势:
|
|
12
|
+
* - 相比 HTTP 轮询,延迟更低(~10ms vs 0-2000ms)
|
|
13
|
+
* - 无需持续请求,减少网络压力
|
|
14
|
+
* - EventSource 自带自动重连机制
|
|
15
|
+
* - 支持暂停/恢复,可按需关闭
|
|
16
|
+
*/
|
|
17
|
+
export interface ServerLogSSEClientOptions {
|
|
18
|
+
/**
|
|
19
|
+
* 后端服务器 URL
|
|
20
|
+
* @example 'http://localhost:3000'
|
|
21
|
+
*/
|
|
22
|
+
serverUrl: string;
|
|
23
|
+
/**
|
|
24
|
+
* SSE 端点路径
|
|
25
|
+
* @default '/dev/logs/server-logs/stream'
|
|
26
|
+
*/
|
|
27
|
+
sseEndpoint?: string;
|
|
28
|
+
/**
|
|
29
|
+
* 是否启用调试日志
|
|
30
|
+
* @default false
|
|
31
|
+
*/
|
|
32
|
+
debug?: boolean;
|
|
33
|
+
}
|
|
34
|
+
type ConnectionStatus = 'disconnected' | 'connecting' | 'connected' | 'error';
|
|
35
|
+
export declare class ServerLogSSEClient {
|
|
36
|
+
private eventSource;
|
|
37
|
+
private status;
|
|
38
|
+
private isPaused;
|
|
39
|
+
private options;
|
|
40
|
+
private messageListener;
|
|
41
|
+
private reconnectCount;
|
|
42
|
+
constructor(options: ServerLogSSEClientOptions);
|
|
43
|
+
/**
|
|
44
|
+
* 启动 SSE 连接
|
|
45
|
+
*/
|
|
46
|
+
start(): void;
|
|
47
|
+
/**
|
|
48
|
+
* 停止 SSE 连接
|
|
49
|
+
*/
|
|
50
|
+
stop(): void;
|
|
51
|
+
/**
|
|
52
|
+
* 暂停接收日志(断开 SSE 连接)
|
|
53
|
+
*/
|
|
54
|
+
pause(): void;
|
|
55
|
+
/**
|
|
56
|
+
* 恢复接收日志(重新建立 SSE 连接)
|
|
57
|
+
*/
|
|
58
|
+
resume(): void;
|
|
59
|
+
/**
|
|
60
|
+
* 重新连接(用于断开后手动重连)
|
|
61
|
+
*/
|
|
62
|
+
reconnect(): void;
|
|
63
|
+
/**
|
|
64
|
+
* 获取当前连接状态
|
|
65
|
+
*/
|
|
66
|
+
getStatus(): ConnectionStatus;
|
|
67
|
+
/**
|
|
68
|
+
* 是否暂停中
|
|
69
|
+
*/
|
|
70
|
+
getIsPaused(): boolean;
|
|
71
|
+
/**
|
|
72
|
+
* 设置 EventSource 连接
|
|
73
|
+
*/
|
|
74
|
+
private setupEventSource;
|
|
75
|
+
/**
|
|
76
|
+
* 清理 EventSource
|
|
77
|
+
*/
|
|
78
|
+
private cleanupEventSource;
|
|
79
|
+
/**
|
|
80
|
+
* 设置父窗口控制消息监听器
|
|
81
|
+
*/
|
|
82
|
+
private setupParentMessageListener;
|
|
83
|
+
/**
|
|
84
|
+
* 清理消息监听器
|
|
85
|
+
*/
|
|
86
|
+
private cleanupMessageListener;
|
|
87
|
+
/**
|
|
88
|
+
* 转发日志到父窗口
|
|
89
|
+
*/
|
|
90
|
+
private forwardLog;
|
|
91
|
+
/**
|
|
92
|
+
* 更新连接状态并通知父窗口
|
|
93
|
+
*/
|
|
94
|
+
private updateStatus;
|
|
95
|
+
/**
|
|
96
|
+
* 发送消息到父窗口
|
|
97
|
+
*/
|
|
98
|
+
private postToParent;
|
|
99
|
+
/**
|
|
100
|
+
* 调试日志
|
|
101
|
+
*/
|
|
102
|
+
private log;
|
|
103
|
+
/**
|
|
104
|
+
* 错误日志
|
|
105
|
+
*/
|
|
106
|
+
private error;
|
|
107
|
+
}
|
|
108
|
+
export {};
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
class ServerLogSSEClient {
|
|
2
|
+
eventSource = null;
|
|
3
|
+
status = 'disconnected';
|
|
4
|
+
isPaused = false;
|
|
5
|
+
options;
|
|
6
|
+
messageListener = null;
|
|
7
|
+
reconnectCount = 0;
|
|
8
|
+
constructor(options){
|
|
9
|
+
this.options = {
|
|
10
|
+
serverUrl: options.serverUrl,
|
|
11
|
+
sseEndpoint: (process.env.CLIENT_BASE_PATH || '') + (options.sseEndpoint || '/dev/logs/server-logs/stream'),
|
|
12
|
+
debug: options.debug ?? false
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
start() {
|
|
16
|
+
if (this.eventSource) return void this.log('SSE client already started');
|
|
17
|
+
this.log('Starting SSE client...', {
|
|
18
|
+
serverUrl: this.options.serverUrl,
|
|
19
|
+
sseEndpoint: this.options.sseEndpoint
|
|
20
|
+
});
|
|
21
|
+
this.updateStatus('connecting');
|
|
22
|
+
this.setupEventSource();
|
|
23
|
+
this.setupParentMessageListener();
|
|
24
|
+
}
|
|
25
|
+
stop() {
|
|
26
|
+
this.log('Stopping SSE client...');
|
|
27
|
+
this.cleanupEventSource();
|
|
28
|
+
this.cleanupMessageListener();
|
|
29
|
+
this.updateStatus('disconnected');
|
|
30
|
+
}
|
|
31
|
+
pause() {
|
|
32
|
+
if (this.isPaused) return;
|
|
33
|
+
this.isPaused = true;
|
|
34
|
+
this.cleanupEventSource();
|
|
35
|
+
this.log('Paused - SSE connection closed');
|
|
36
|
+
}
|
|
37
|
+
resume() {
|
|
38
|
+
if (!this.isPaused) return;
|
|
39
|
+
this.isPaused = false;
|
|
40
|
+
this.setupEventSource();
|
|
41
|
+
this.log('Resumed - SSE connection reopened');
|
|
42
|
+
}
|
|
43
|
+
reconnect() {
|
|
44
|
+
this.log('Reconnecting...');
|
|
45
|
+
this.cleanupEventSource();
|
|
46
|
+
this.isPaused = false;
|
|
47
|
+
this.setupEventSource();
|
|
48
|
+
}
|
|
49
|
+
getStatus() {
|
|
50
|
+
return this.status;
|
|
51
|
+
}
|
|
52
|
+
getIsPaused() {
|
|
53
|
+
return this.isPaused;
|
|
54
|
+
}
|
|
55
|
+
setupEventSource() {
|
|
56
|
+
this.reconnectCount = 0;
|
|
57
|
+
const url = `${this.options.serverUrl}${this.options.sseEndpoint}`;
|
|
58
|
+
this.log('Connecting to SSE endpoint:', url);
|
|
59
|
+
try {
|
|
60
|
+
this.eventSource = new EventSource(url);
|
|
61
|
+
this.eventSource.addEventListener('connected', (event)=>{
|
|
62
|
+
try {
|
|
63
|
+
const data = JSON.parse(event.data);
|
|
64
|
+
this.log('Connected to SSE server', data);
|
|
65
|
+
this.updateStatus('connected');
|
|
66
|
+
} catch (e) {
|
|
67
|
+
this.error('Failed to parse connected event', e);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
this.eventSource.addEventListener('log', (event)=>{
|
|
71
|
+
if (this.isPaused) return;
|
|
72
|
+
try {
|
|
73
|
+
const log = JSON.parse(event.data);
|
|
74
|
+
this.forwardLog(log);
|
|
75
|
+
} catch (e) {
|
|
76
|
+
this.error('Failed to parse log event', e);
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
this.eventSource.addEventListener('heartbeat', (event)=>{
|
|
80
|
+
try {
|
|
81
|
+
const data = JSON.parse(event.data);
|
|
82
|
+
this.log('Heartbeat received', data);
|
|
83
|
+
} catch (e) {}
|
|
84
|
+
});
|
|
85
|
+
this.eventSource.onmessage = (event)=>{
|
|
86
|
+
this.log('Received generic message:', event.data);
|
|
87
|
+
};
|
|
88
|
+
this.eventSource.onerror = (event)=>{
|
|
89
|
+
this.error('SSE connection error', event);
|
|
90
|
+
this.reconnectCount++;
|
|
91
|
+
if (this.reconnectCount > 1) {
|
|
92
|
+
this.log('Max reconnect attempts reached, closing connection');
|
|
93
|
+
this.cleanupEventSource();
|
|
94
|
+
this.updateStatus('error');
|
|
95
|
+
this.postToParent({
|
|
96
|
+
type: 'SERVER_LOG_CONNECTION',
|
|
97
|
+
status: 'error',
|
|
98
|
+
error: 'SSE connection failed after retry'
|
|
99
|
+
});
|
|
100
|
+
} else if (this.eventSource?.readyState === EventSource.CONNECTING) {
|
|
101
|
+
this.log(`Reconnecting... attempt ${this.reconnectCount}`);
|
|
102
|
+
this.updateStatus('connecting');
|
|
103
|
+
} else if (this.eventSource?.readyState === EventSource.CLOSED) {
|
|
104
|
+
this.updateStatus('error');
|
|
105
|
+
this.postToParent({
|
|
106
|
+
type: 'SERVER_LOG_CONNECTION',
|
|
107
|
+
status: 'error',
|
|
108
|
+
error: 'SSE connection closed'
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
this.eventSource.onopen = ()=>{
|
|
113
|
+
this.log('SSE connection opened');
|
|
114
|
+
this.reconnectCount = 0;
|
|
115
|
+
};
|
|
116
|
+
} catch (e) {
|
|
117
|
+
this.error('Failed to create EventSource', e);
|
|
118
|
+
this.updateStatus('error');
|
|
119
|
+
this.postToParent({
|
|
120
|
+
type: 'SERVER_LOG_CONNECTION',
|
|
121
|
+
status: 'error',
|
|
122
|
+
error: e instanceof Error ? e.message : String(e)
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
cleanupEventSource() {
|
|
127
|
+
if (this.eventSource) {
|
|
128
|
+
this.eventSource.close();
|
|
129
|
+
this.eventSource = null;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
setupParentMessageListener() {
|
|
133
|
+
this.messageListener = (event)=>{
|
|
134
|
+
const data = event.data;
|
|
135
|
+
if (data?.type !== 'SERVER_LOG_CONTROL') return;
|
|
136
|
+
this.log('Received control message:', data);
|
|
137
|
+
switch(data.action){
|
|
138
|
+
case 'pause':
|
|
139
|
+
this.pause();
|
|
140
|
+
break;
|
|
141
|
+
case 'resume':
|
|
142
|
+
this.resume();
|
|
143
|
+
break;
|
|
144
|
+
case 'stop':
|
|
145
|
+
this.stop();
|
|
146
|
+
break;
|
|
147
|
+
case 'reconnect':
|
|
148
|
+
this.reconnect();
|
|
149
|
+
break;
|
|
150
|
+
default:
|
|
151
|
+
this.log('Unknown control action:', data.action);
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
window.addEventListener('message', this.messageListener);
|
|
155
|
+
}
|
|
156
|
+
cleanupMessageListener() {
|
|
157
|
+
if (this.messageListener) {
|
|
158
|
+
window.removeEventListener('message', this.messageListener);
|
|
159
|
+
this.messageListener = null;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
forwardLog(log) {
|
|
163
|
+
try {
|
|
164
|
+
this.log('Forwarding log to parent window', {
|
|
165
|
+
type: 'SERVER_LOG',
|
|
166
|
+
logId: log.id,
|
|
167
|
+
level: log.level,
|
|
168
|
+
tags: log.tags
|
|
169
|
+
});
|
|
170
|
+
this.postToParent({
|
|
171
|
+
type: 'SERVER_LOG',
|
|
172
|
+
payload: JSON.stringify(log)
|
|
173
|
+
});
|
|
174
|
+
this.log('Log forwarded successfully');
|
|
175
|
+
} catch (e) {
|
|
176
|
+
this.error('Failed to forward log', e);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
updateStatus(status) {
|
|
180
|
+
const previousStatus = this.status;
|
|
181
|
+
this.status = status;
|
|
182
|
+
if (previousStatus !== status) {
|
|
183
|
+
this.log(`Status changed: ${previousStatus} → ${status}`);
|
|
184
|
+
this.postToParent({
|
|
185
|
+
type: 'SERVER_LOG_CONNECTION',
|
|
186
|
+
status
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
postToParent(message) {
|
|
191
|
+
if (window.parent === window) return;
|
|
192
|
+
try {
|
|
193
|
+
window.parent.postMessage(message, '*');
|
|
194
|
+
} catch (e) {
|
|
195
|
+
this.error('postMessage error', e);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
log(message, data) {
|
|
199
|
+
const enableLog = 'true' === localStorage.getItem('debug_server_log_sse');
|
|
200
|
+
if (this.options.debug && enableLog) if (data) console.log(`[ServerLogSSEClient] ${message}`, data);
|
|
201
|
+
else console.log(`[ServerLogSSEClient] ${message}`);
|
|
202
|
+
}
|
|
203
|
+
error(message, error) {
|
|
204
|
+
const enableLog = 'true' === localStorage.getItem('debug_server_log_sse');
|
|
205
|
+
if (enableLog) if (error) console.error(`[ServerLogSSEClient] ${message}`, error);
|
|
206
|
+
else console.error(`[ServerLogSSEClient] ${message}`);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
export { ServerLogSSEClient };
|
|
@@ -164,3 +164,12 @@ export type ServerLogPostMessage = {
|
|
|
164
164
|
} | {
|
|
165
165
|
type: 'SERVER_LOG_CLEARED';
|
|
166
166
|
};
|
|
167
|
+
/**
|
|
168
|
+
* PostMessage 控制类型(parent → iframe)
|
|
169
|
+
*
|
|
170
|
+
* 父窗口可以通过 postMessage 控制 iframe 内的 SSE 客户端
|
|
171
|
+
*/
|
|
172
|
+
export type ServerLogControlMessage = {
|
|
173
|
+
type: 'SERVER_LOG_CONTROL';
|
|
174
|
+
action: 'pause' | 'resume' | 'stop' | 'reconnect';
|
|
175
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lark-apaas/client-toolkit",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.38-alpha.0",
|
|
4
4
|
"types": "./lib/index.d.ts",
|
|
5
5
|
"main": "./lib/index.js",
|
|
6
6
|
"files": [
|
|
@@ -60,11 +60,6 @@
|
|
|
60
60
|
"import": "./lib/apis/utils/*.js",
|
|
61
61
|
"require": "./lib/apis/utils/*.js",
|
|
62
62
|
"types": "./lib/apis/utils/*.d.ts"
|
|
63
|
-
},
|
|
64
|
-
"./auth": {
|
|
65
|
-
"import": "./lib/auth.js",
|
|
66
|
-
"require": "./lib/auth.js",
|
|
67
|
-
"types": "./lib/auth.d.ts"
|
|
68
63
|
}
|
|
69
64
|
},
|
|
70
65
|
"scripts": {
|
|
@@ -86,8 +81,7 @@
|
|
|
86
81
|
"@ant-design/colors": "^7.2.1",
|
|
87
82
|
"@ant-design/cssinjs": "^1.24.0",
|
|
88
83
|
"@data-loom/js": "^0.4.3",
|
|
89
|
-
"@lark-apaas/
|
|
90
|
-
"@lark-apaas/miaoda-inspector": "^1.0.9",
|
|
84
|
+
"@lark-apaas/miaoda-inspector": "^1.0.8",
|
|
91
85
|
"@lark-apaas/observable-web": "^1.0.0",
|
|
92
86
|
"@radix-ui/react-avatar": "^1.1.10",
|
|
93
87
|
"@radix-ui/react-popover": "^1.1.15",
|
package/lib/auth.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { CanRole, AbilityContext, ROLE_SUBJECT } from '@lark-apaas/auth-sdk';
|
package/lib/auth.js
DELETED