@modesto_ma/dsh-channels 0.5.0-beta.4 → 0.5.0-beta.6
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 +49 -28
- package/docs/attachment-pipeline-v2.md +1 -1
- package/docs/feishu-onboarding.md +6 -5
- package/lib/client.js +1874 -1510
- package/lib/client.js.map +1 -1
- package/lib/index.js +736 -353
- package/lib/index.js.map +1 -1
- package/package.json +2 -2
- package/src/client/api.ts +45 -2
- package/src/client/avatar.tsx +36 -0
- package/src/client/icon.tsx +25 -0
- package/src/client/index.ts +78 -23
- package/src/client/number-field.ts +97 -0
- package/src/client/origin-cache.ts +45 -0
- package/src/client/origin.tsx +201 -0
- package/src/client/panel/access-tab.tsx +90 -21
- package/src/client/panel/advanced-tab.tsx +59 -68
- package/src/client/panel/connection-tab.tsx +147 -164
- package/src/client/panel/credentials-card.tsx +124 -87
- package/src/client/panel/fields-tab.tsx +27 -0
- package/src/client/panel/index.tsx +77 -54
- package/src/client/panel/profiles.ts +73 -0
- package/src/client/panel/shared.tsx +358 -253
- package/src/client/panel/whitelist.tsx +203 -0
- package/src/client/panel/wiki-tab.tsx +33 -39
- package/src/client/styles.ts +132 -92
- package/src/domain.ts +42 -0
- package/src/feishu/scopes.ts +2 -1
- package/src/feishu-channel.ts +87 -0
- package/src/index.ts +1 -1
- package/src/routing/inbound.ts +13 -5
- package/src/service.ts +146 -3
- package/src/session/routes.ts +45 -4
- package/src/settings/contract.ts +265 -152
- package/src/settings/routes.ts +47 -0
- package/src/settings/surface.ts +201 -197
- package/src/client/panel/access-card.tsx +0 -329
- package/src/client/panel/analyze-token-card.tsx +0 -70
- package/src/client/panel/model-tab.tsx +0 -23
- package/src/client/panel/sessions-tab.tsx +0 -24
- package/src/client/panel/workspace-tab.tsx +0 -24
|
@@ -1,18 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* 「访问授权」tab
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* 「访问授权」tab:待处理申请(有才显示,置顶)、准入字段(调试模式 /
|
|
3
|
+
* 管理员 / 申请有效期)与群、用户白名单。字段保存走全局 draft,白名单增删
|
|
4
|
+
* 与申请处理是独立即时 mutation。
|
|
5
|
+
*
|
|
6
|
+
* `access.loaded === false`(飞书未连接、插件 storage 域未打开)时白名单与
|
|
7
|
+
* 申请列表是「未知」而不是「空」,因此只渲染连接提示并禁用全部写操作。
|
|
5
8
|
*
|
|
6
9
|
* @module dsh-channels/client/panel/access-tab
|
|
7
10
|
*/
|
|
8
11
|
|
|
9
|
-
import { PANEL_COPY, TAB_FIELD_GROUPS } from '../../settings/contract.ts'
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
+
import { fillCopy, PANEL_COPY, TAB_FIELD_GROUPS } from '../../settings/contract.ts'
|
|
13
|
+
import type { AccessWire } from '../../settings/contract.ts'
|
|
14
|
+
import { Avatar } from '../avatar.tsx'
|
|
15
|
+
import type { ProfileView } from './profiles.ts'
|
|
16
|
+
import { FieldSections, NoticeBar, Row } from './shared.tsx'
|
|
12
17
|
import type { FormView } from './shared.tsx'
|
|
18
|
+
import { RequestList, Whitelist } from './whitelist.tsx'
|
|
13
19
|
|
|
14
20
|
export interface AccessTabProps {
|
|
15
21
|
form: FormView
|
|
22
|
+
profiles: ProfileView
|
|
16
23
|
accessNotice: string | null
|
|
17
24
|
accessError: string | null
|
|
18
25
|
onAddGroup: (chatId: string, name: string) => Promise<boolean>
|
|
@@ -23,24 +30,86 @@ export interface AccessTabProps {
|
|
|
23
30
|
}
|
|
24
31
|
|
|
25
32
|
export function AccessTab(props: AccessTabProps): JSX.Element {
|
|
26
|
-
const { form, accessNotice, accessError, onAddGroup, onRemoveGroup, onAddUser, onRemoveUser, onResolveRequest } = props
|
|
33
|
+
const { form, profiles, accessNotice, accessError, onAddGroup, onRemoveGroup, onAddUser, onRemoveUser, onResolveRequest } = props
|
|
34
|
+
const { access } = form.panel
|
|
35
|
+
const working = form.busy === 'access'
|
|
36
|
+
const disabled = form.busy !== null || form.locked || !access.loaded
|
|
27
37
|
return (
|
|
28
38
|
<div className="dsh-channels-cfg__tabBody">
|
|
29
|
-
<
|
|
30
|
-
<
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
{accessNotice === null ? null : <NoticeBar kind="ok" text={accessNotice} />}
|
|
40
|
+
{accessError === null ? null : <NoticeBar kind="error" text={accessError} />}
|
|
41
|
+
{access.loaded && access.mode === 'guarded' && !access.admin.configured
|
|
42
|
+
? (
|
|
43
|
+
<NoticeBar
|
|
44
|
+
kind="error"
|
|
45
|
+
text={access.admin.ownerLookupError === undefined
|
|
46
|
+
? PANEL_COPY.accessAdminMissingWarning
|
|
47
|
+
: `${PANEL_COPY.accessAdminMissingWarning}(${access.admin.ownerLookupError})`}
|
|
48
|
+
/>
|
|
49
|
+
)
|
|
50
|
+
: null}
|
|
51
|
+
{profiles.warning === undefined ? null : <p className="dsh-channels-cfg__secFoot">{fillCopy(PANEL_COPY.profilesWarning, { reason: profiles.warning })}</p>}
|
|
52
|
+
{access.loaded && access.requests.length > 0
|
|
53
|
+
? <RequestList requests={access.requests} profiles={profiles} disabled={disabled} working={working} onResolve={onResolveRequest} />
|
|
54
|
+
: null}
|
|
55
|
+
<FieldSections
|
|
56
|
+
form={form}
|
|
57
|
+
groups={TAB_FIELD_GROUPS.access}
|
|
58
|
+
extra={{ access: <AdminRow admin={access.admin} profiles={profiles} /> }}
|
|
59
|
+
/>
|
|
60
|
+
<Whitelist
|
|
61
|
+
title={PANEL_COPY.accessGroupsTitle}
|
|
62
|
+
entries={access.groups.map(group => ({
|
|
63
|
+
id: group.chatId,
|
|
64
|
+
name: profiles.chat(group.chatId)?.name ?? group.name,
|
|
65
|
+
avatarUrl: profiles.chat(group.chatId)?.avatarUrl,
|
|
66
|
+
source: group.source,
|
|
67
|
+
}))}
|
|
68
|
+
avatar="群"
|
|
69
|
+
empty={PANEL_COPY.accessGroupsEmpty}
|
|
70
|
+
loaded={access.loaded}
|
|
71
|
+
addLabel={PANEL_COPY.accessAddGroup}
|
|
72
|
+
idPlaceholder={PANEL_COPY.accessGroupChatIdPlaceholder}
|
|
73
|
+
namePlaceholder={PANEL_COPY.accessGroupNamePlaceholder}
|
|
74
|
+
disabled={disabled}
|
|
75
|
+
working={working}
|
|
76
|
+
onAdd={onAddGroup}
|
|
77
|
+
onRemove={onRemoveGroup}
|
|
78
|
+
/>
|
|
79
|
+
<Whitelist
|
|
80
|
+
title={PANEL_COPY.accessUsersTitle}
|
|
81
|
+
entries={access.users.map(user => ({
|
|
82
|
+
id: user.userId,
|
|
83
|
+
name: profiles.user(user.userId)?.name ?? user.name,
|
|
84
|
+
avatarUrl: profiles.user(user.userId)?.avatarUrl,
|
|
85
|
+
source: user.source,
|
|
86
|
+
}))}
|
|
87
|
+
empty={PANEL_COPY.accessUsersEmpty}
|
|
88
|
+
loaded={access.loaded}
|
|
89
|
+
addLabel={PANEL_COPY.accessAddUser}
|
|
90
|
+
idPlaceholder={PANEL_COPY.accessUserIdPlaceholder}
|
|
91
|
+
namePlaceholder={PANEL_COPY.accessUserNamePlaceholder}
|
|
92
|
+
disabled={disabled}
|
|
93
|
+
working={working}
|
|
94
|
+
onAdd={onAddUser}
|
|
95
|
+
onRemove={onRemoveUser}
|
|
43
96
|
/>
|
|
44
97
|
</div>
|
|
45
98
|
)
|
|
46
99
|
}
|
|
100
|
+
|
|
101
|
+
/** The effective administrator with their Feishu name and avatar; nothing while there is none. */
|
|
102
|
+
function AdminRow(props: { admin: AccessWire['admin']; profiles: ProfileView }): JSX.Element | null {
|
|
103
|
+
const { admin, profiles } = props
|
|
104
|
+
if (admin.userId === undefined) return null
|
|
105
|
+
const profile = profiles.user(admin.userId)
|
|
106
|
+
const name = profile?.name ?? admin.userId
|
|
107
|
+
const source = admin.source === 'owner' ? PANEL_COPY.accessAdminSourceOwner : PANEL_COPY.accessAdminSourceConfig
|
|
108
|
+
return (
|
|
109
|
+
<Row
|
|
110
|
+
leading={<Avatar text={name} url={profile?.avatarUrl} />}
|
|
111
|
+
label={name}
|
|
112
|
+
summary={<>{PANEL_COPY.accessAdminCurrent} · <span className="dsh-channels-cfg__mono">{admin.userId}</span> · {source}</>}
|
|
113
|
+
/>
|
|
114
|
+
)
|
|
115
|
+
}
|
|
@@ -1,80 +1,71 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* 「高级」tab
|
|
3
|
-
*
|
|
2
|
+
* 「高级」tab:日志分析接口(监听地址字段 + 访问令牌凭据行)、SDK 日志级别
|
|
3
|
+
* 与 Web 覆盖文件(「恢复 profile 配置」删除整个覆盖层,不走 draft)。
|
|
4
4
|
*
|
|
5
5
|
* @module dsh-channels/client/panel/advanced-tab
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { PANEL_COPY, TAB_FIELD_GROUPS } from '../../settings/contract.ts'
|
|
9
|
-
import
|
|
10
|
-
import {
|
|
11
|
-
import type {
|
|
8
|
+
import { fillCopy, PANEL_COPY, TAB_FIELD_GROUPS } from '../../settings/contract.ts'
|
|
9
|
+
import { CredentialRow } from './credentials-card.tsx'
|
|
10
|
+
import { FieldSections, Row, Section } from './shared.tsx'
|
|
11
|
+
import type { AnalyzeTokenView, FormView } from './shared.tsx'
|
|
12
12
|
|
|
13
|
-
export function AdvancedTab(props: {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
<div className="dsh-channels-cfg__tabBody">
|
|
17
|
-
<TabHelp text={PANEL_COPY.advancedTabHelp} />
|
|
18
|
-
<FieldsCard form={form} groups={TAB_FIELD_GROUPS.advanced} />
|
|
19
|
-
<SaveBar form={form} tab="advanced" />
|
|
20
|
-
<AdvancedCard panel={form.panel} busy={form.busy} locked={form.locked} onClear={onClear} />
|
|
21
|
-
</div>
|
|
22
|
-
)
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/** Read-only deployment facts; the only write action here drops the overlay file. */
|
|
26
|
-
function AdvancedCard(props: {
|
|
27
|
-
panel: PanelWire
|
|
28
|
-
busy: BusyAction | null
|
|
29
|
-
locked: boolean
|
|
13
|
+
export function AdvancedTab(props: {
|
|
14
|
+
form: FormView
|
|
15
|
+
analyzeToken: AnalyzeTokenView
|
|
30
16
|
onClear: () => void
|
|
31
17
|
}): JSX.Element {
|
|
32
|
-
const {
|
|
33
|
-
const {
|
|
18
|
+
const { form, analyzeToken, onClear } = props
|
|
19
|
+
const { panel, busy, locked } = form
|
|
20
|
+
const tokenState = panel.config.credentials.analyzeToken
|
|
34
21
|
const overridden = panel.config.overriddenKeys.length
|
|
35
22
|
return (
|
|
36
|
-
<
|
|
37
|
-
<
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
<
|
|
61
|
-
|
|
62
|
-
<span className="dsh-channels-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
23
|
+
<div className="dsh-channels-cfg__tabBody">
|
|
24
|
+
<FieldSections
|
|
25
|
+
form={form}
|
|
26
|
+
groups={TAB_FIELD_GROUPS.advanced}
|
|
27
|
+
extra={{
|
|
28
|
+
analyze: (
|
|
29
|
+
<CredentialRow
|
|
30
|
+
label={PANEL_COPY.analyzeTokenLabel}
|
|
31
|
+
summary={PANEL_COPY.analyzeTokenSummary}
|
|
32
|
+
state={tokenState}
|
|
33
|
+
value={analyzeToken.value}
|
|
34
|
+
secret
|
|
35
|
+
placeholder={PANEL_COPY.analyzeTokenPlaceholder}
|
|
36
|
+
issue={analyzeToken.issues.find(item => item.key === 'analyzeToken')}
|
|
37
|
+
busy={analyzeToken.busy}
|
|
38
|
+
disabled={analyzeToken.busy !== null || analyzeToken.locked || !tokenState.writable}
|
|
39
|
+
onChange={analyzeToken.onChange}
|
|
40
|
+
onSave={analyzeToken.onSave}
|
|
41
|
+
onClear={analyzeToken.onClear}
|
|
42
|
+
/>
|
|
43
|
+
),
|
|
44
|
+
}}
|
|
45
|
+
/>
|
|
46
|
+
<Section title={PANEL_COPY.overlayTitle}>
|
|
47
|
+
<Row
|
|
48
|
+
label={PANEL_COPY.overlayPath}
|
|
49
|
+
summary={<span className="dsh-channels-cfg__mono">{panel.status.overlayPath}</span>}
|
|
50
|
+
details={(
|
|
51
|
+
<>
|
|
52
|
+
{PANEL_COPY.overlayPathHelp}
|
|
53
|
+
{' '}
|
|
54
|
+
{overridden === 0 ? PANEL_COPY.noOverrides : fillCopy(PANEL_COPY.overlayCount, { n: overridden })}
|
|
55
|
+
</>
|
|
56
|
+
)}
|
|
57
|
+
control={(
|
|
58
|
+
<button
|
|
59
|
+
type="button"
|
|
60
|
+
className="dsh-channels-cfg__btn dsh-channels-cfg__btn--danger"
|
|
61
|
+
onClick={onClear}
|
|
62
|
+
disabled={overridden === 0 || busy !== null || locked}
|
|
63
|
+
>
|
|
64
|
+
{busy === 'clear' ? PANEL_COPY.clearing : PANEL_COPY.clear}
|
|
65
|
+
</button>
|
|
66
|
+
)}
|
|
67
|
+
/>
|
|
68
|
+
</Section>
|
|
69
|
+
</div>
|
|
79
70
|
)
|
|
80
71
|
}
|