Skip to content

Commit 9bca7fe

Browse files
committed
refactor(share): unify session share adapters in connectors-core
Use one SessionShareAdapter for Koko and Lion, drop the unused Koko share drawer, and copy Chen grid cells through the shared clipboard helper.
1 parent 8f0e92e commit 9bca7fe

13 files changed

Lines changed: 81 additions & 176 deletions

File tree

‎packages/connectors-core/package.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"./types/fileTransfer": "./src/types/fileTransfer.ts",
1010
"./types/message": "./src/types/message.ts",
1111
"./types/session": "./src/types/session.ts",
12+
"./types/sessionShare": "./src/types/sessionShare.ts",
1213
"./fileTransferRegistry": "./src/fileTransferRegistry.ts",
1314
"./useConnectorEndpoint": "./src/useConnectorEndpoint.ts",
1415
"./useHostBridge": "./src/useHostBridge.ts",

‎packages/connectors-core/src/index.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ export * from "./types/component";
44
export * from "./types/fileTransfer";
55
export * from "./types/message";
66
export * from "./types/session";
7+
export * from "./types/sessionShare";
78
export * from "./useConnectorEndpoint";
89
export * from "./useHostBridge";
910
export * from "./utils/formatMessage";
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import type { Ref } from "vue";
2+
3+
export interface SessionShareUser {
4+
id: string;
5+
name: string;
6+
username: string;
7+
}
8+
9+
export interface SessionShareOnlineUser {
10+
user_id: string;
11+
user: string;
12+
primary: boolean;
13+
writable: boolean;
14+
remote_addr?: string;
15+
}
16+
17+
export interface SessionShareInfo {
18+
shareId: string;
19+
shareCode: string;
20+
sessionId: string;
21+
enableShare: boolean;
22+
shareURL: string;
23+
}
24+
25+
export interface SessionShareLinkRequest {
26+
expiredTime: number;
27+
actionPerm: "writable" | "readonly";
28+
users: SessionShareUser[];
29+
}
30+
31+
export interface SessionShareAdapter {
32+
onlineUsers: Readonly<Ref<SessionShareOnlineUser[]>>;
33+
shareInfo: Readonly<Ref<SessionShareInfo>>;
34+
userOptions: Readonly<Ref<SessionShareUser[]>>;
35+
hasMoreUsers: Readonly<Ref<boolean>>;
36+
searchUsers: (query: string, loadMore?: boolean) => void | Promise<void>;
37+
createShareLink: (request: SessionShareLinkRequest) => void | Promise<void>;
38+
copyShareURL: () => void | Promise<void>;
39+
removeShareUser: (user: SessionShareOnlineUser) => void | Promise<void>;
40+
resetShareState: () => void;
41+
}

‎ui/chen/components/DataGrid.client.vue‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ import type {
2424
import { AllCommunityModule, isColumn, ModuleRegistry } from "ag-grid-community";
2525
import { AgGridVue } from "ag-grid-vue3";
2626
import { formatChenGridValue, useChenGridPreferences } from "~/chen/composables/useChenGridPreferences";
27-
import { writeChenClipboardText } from "~/chen/runtime/clipboard";
2827
import {
2928
canUseChenCopy,
3029
createChenInsertSql,
@@ -50,6 +49,7 @@ import {
5049
isChenInsertRow,
5150
normalizeChenDataViewValue
5251
} from "~/chen/utils/dataViewEditing";
52+
import { writeClipboardText } from "~/utils/clipboard";
5353
5454
import "ag-grid-community/styles/ag-grid.css";
5555
import "ag-grid-community/styles/ag-theme-balham.css";
@@ -285,7 +285,7 @@ function selectedData() {
285285
async function copyText(text: string, successTitle: string) {
286286
if (!canUseChenCopy(props.canCopy)) return;
287287
try {
288-
await writeChenClipboardText(text);
288+
await writeClipboardText(text);
289289
toast.add({ title: successTitle, color: "success" });
290290
} catch {
291291
addErrorToast({ title: t("Common.CopyFailed"), description: t("Chen.CopyOperationFailedDescription") });

‎ui/chen/runtime/clipboard.ts‎

Lines changed: 0 additions & 24 deletions
This file was deleted.

‎ui/components/RightPanel/sessionPanel.vue‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ function formatUptime(seconds: number) {
111111
112112
const showShareSection = computed(() => {
113113
if (!isConnectedSession.value) return false;
114-
if (lionSession.value) return Boolean(lionSession.value.share.sessionId.value);
114+
if (lionSession.value) return Boolean(lionSession.value.share.shareInfo.value.sessionId);
115115
return Boolean(sessionDetails.value?.shareAllowed);
116116
});
117117
@@ -133,7 +133,7 @@ const sessionRows = computed(() => {
133133
label: t("RightPanel.SessionId"),
134134
value:
135135
details?.sessionId ||
136-
lionSession.value?.share.sessionId.value ||
136+
lionSession.value?.share.shareInfo.value.sessionId ||
137137
(!lionSession.value ? kokoPane.value.sessionId : "") ||
138138
tokenId ||
139139
"-"
@@ -153,7 +153,8 @@ const statusLabel = computed(() => {
153153
const canShare = computed(() => {
154154
if (!showShareSection.value) return false;
155155
if (lionSession.value) {
156-
return Boolean(lionSession.value.share.enableShare.value && lionSession.value.share.sessionId.value);
156+
const shareInfo = lionSession.value.share.shareInfo.value;
157+
return Boolean(shareInfo.enableShare && shareInfo.sessionId);
157158
}
158159
return Boolean(kokoPane.value.enableShare && kokoPane.value.sessionId);
159160
});

‎ui/components/RightPanel/sessionShareSection.vue‎

Lines changed: 16 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<script setup lang="ts">
2-
import type { SuggestionUser } from "@/lion/api";
2+
import type { SessionShareAdapter, SessionShareUser } from "@jumpserver/connectors-core";
33
import { useKokoSessionAdapter } from "#koko";
44
import { getLionWorkspaceSession } from "@/lion/workspaces/useLionWorkspaceSessionRegistry";
55
@@ -14,22 +14,21 @@ const activeSessionId = computed(() => {
1414
return tab?.panes.find((pane) => pane.id === activePaneId.value)?.id || tab?.id || "";
1515
});
1616
const kokoAdapter = useKokoSessionAdapter(activeSessionId);
17-
const lionAdapter = computed(() => getLionWorkspaceSession(activeSessionId.value)?.share || null);
18-
const onlineUsers = computed(() => lionAdapter.value?.onlineUsers.value || kokoAdapter.onlineUsers.value);
19-
const shareInfo = computed(() => lionAdapter.value?.shareInfo.value || kokoAdapter.shareInfo.value);
20-
const userOptions = computed<SuggestionUser[]>(
21-
() => (lionAdapter.value?.userOptions.value || kokoAdapter.userOptions.value) as SuggestionUser[]
17+
const adapter = computed<SessionShareAdapter>(
18+
() => getLionWorkspaceSession(activeSessionId.value)?.share || kokoAdapter
2219
);
23-
const hasMoreUsers = computed(() => Boolean(lionAdapter.value?.hasMoreUsers.value));
24-
const adapterKey = computed(() => `${lionAdapter.value ? "lion" : "koko"}:${activeSessionId.value}`);
20+
const onlineUsers = computed(() => adapter.value.onlineUsers.value);
21+
const shareInfo = computed(() => adapter.value.shareInfo.value);
22+
const userOptions = computed(() => adapter.value.userOptions.value);
23+
const hasMoreUsers = computed(() => adapter.value.hasMoreUsers.value);
2524
2625
const shareModalOpen = ref(false);
2726
const shareUserMenuOpen = ref(false);
2827
const searchLoading = ref(false);
2928
const showLinkResult = ref(false);
3029
const searchQuery = ref("");
3130
const selectedUserIds = ref<string[]>([]);
32-
const selectedUsers = ref<Record<string, SuggestionUser>>({});
31+
const selectedUsers = ref<Record<string, SessionShareUser>>({});
3332
3433
const shareLinkRequest = reactive({
3534
expiredTime: 10,
@@ -100,7 +99,7 @@ watch(
10099
}
101100
);
102101
103-
watch(adapterKey, () => {
102+
watch([activeSessionId, adapter], () => {
104103
shareModalOpen.value = false;
105104
shareUserMenuOpen.value = false;
106105
showLinkResult.value = Boolean(shareInfo.value.shareCode);
@@ -129,12 +128,7 @@ watch(selectedUserIds, (ids) => cacheSelectedUsers(ids));
129128
130129
async function runSearch(query: string, loadMore = false) {
131130
searchLoading.value = true;
132-
const lion = lionAdapter.value;
133-
if (!lion) {
134-
kokoAdapter.searchUsers(query);
135-
return;
136-
}
137-
await lion.searchUsers(query, loadMore);
131+
await adapter.value.searchUsers(query, loadMore);
138132
searchLoading.value = false;
139133
}
140134
@@ -145,10 +139,10 @@ watch(searchQuery, (query) => {
145139
debouncedSearch(query);
146140
});
147141
148-
const selectedShareUsers = computed<SuggestionUser[]>(() =>
142+
const selectedShareUsers = computed<SessionShareUser[]>(() =>
149143
selectedUserIds.value
150144
.map((id) => selectedUsers.value[id] || userOptions.value.find((item) => item.id === id))
151-
.filter((item): item is SuggestionUser => Boolean(item))
145+
.filter((item): item is SessionShareUser => Boolean(item))
152146
);
153147
154148
function openShareModal() {
@@ -167,29 +161,25 @@ function handleCreateLink() {
167161
actionPerm: shareLinkRequest.actionPerm,
168162
users: selectedShareUsers.value
169163
};
170-
if (lionAdapter.value) void lionAdapter.value.createShareLink(request);
171-
else kokoAdapter.createShareLink(request);
164+
void adapter.value.createShareLink(request);
172165
}
173166
174167
function handleRemoveShareUser(userId: string) {
175168
const user = onlineUsers.value.find((item) => item.user_id === userId && !item.primary);
176169
if (!user) return;
177-
if (lionAdapter.value) void lionAdapter.value.removeShareUser(user);
178-
else kokoAdapter.removeShareUser(user as any);
170+
void adapter.value.removeShareUser(user);
179171
}
180172
181173
function handleBack() {
182-
if (lionAdapter.value) lionAdapter.value.resetShareState();
183-
else kokoAdapter.resetShareState();
174+
adapter.value.resetShareState();
184175
showLinkResult.value = false;
185176
selectedUserIds.value = [];
186177
selectedUsers.value = {};
187178
searchQuery.value = "";
188179
}
189180
190181
function handleCopyShareURL() {
191-
if (lionAdapter.value) void lionAdapter.value.copyShareURL();
192-
else kokoAdapter.copyShareURL();
182+
void adapter.value.copyShareURL();
193183
}
194184
</script>
195185

‎ui/koko/components/Drawer/SessionShare/index.vue‎

Lines changed: 0 additions & 53 deletions
This file was deleted.

‎ui/koko/composables/useSessionAdapter.ts‎

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import type { OnlineUser, ShareUserOptions } from "#koko/types/session";
1+
import type { SessionShareAdapter, SessionShareLinkRequest, SessionShareOnlineUser } from "@jumpserver/connectors-core";
22
import type { MaybeRefOrGetter } from "vue";
3+
import type { ShareUserOptions } from "#koko/types/session";
34
import { FORMATTER_MESSAGE_TYPE } from "@jumpserver/connectors-core";
45

56
import { writeText } from "clipboard-polyfill";
@@ -12,7 +13,7 @@ import { useShareLink } from "~/composables/useShareLink";
1213
* right panel and the in-terminal drawer always act on the terminal they show,
1314
* never on the most recently connected one.
1415
*/
15-
export function useKokoSessionAdapter(paneId: MaybeRefOrGetter<string>) {
16+
export function useKokoSessionAdapter(paneId: MaybeRefOrGetter<string>): SessionShareAdapter {
1617
const { t } = useI18n();
1718
const toast = useToast();
1819
const { addErrorToast } = useErrorToast();
@@ -22,6 +23,7 @@ export function useKokoSessionAdapter(paneId: MaybeRefOrGetter<string>) {
2223
const pane = computed(() => connectionStore.pane(toValue(paneId)));
2324

2425
const onlineUsers = computed(() => pane.value.onlineUsers);
26+
const hasMoreUsers = computed(() => false);
2527

2628
const shareInfo = computed(() => {
2729
const { shareId, shareCode, sessionId, enableShare } = pane.value;
@@ -44,11 +46,7 @@ export function useKokoSessionAdapter(paneId: MaybeRefOrGetter<string>) {
4446
return true;
4547
};
4648

47-
const createShareLink = (shareLinkRequest: {
48-
expiredTime: number;
49-
actionPerm: string;
50-
users: ShareUserOptions[];
51-
}) => {
49+
const createShareLink = (shareLinkRequest: SessionShareLinkRequest) => {
5250
const sessionId = pane.value.sessionId;
5351
const sent =
5452
Boolean(sessionId) &&
@@ -67,7 +65,7 @@ export function useKokoSessionAdapter(paneId: MaybeRefOrGetter<string>) {
6765
sendToPane(FORMATTER_MESSAGE_TYPE.TERMINAL_GET_SHARE_USER, { query });
6866
};
6967

70-
const removeShareUser = (user: OnlineUser) => {
68+
const removeShareUser = (user: SessionShareOnlineUser) => {
7169
const sessionId = pane.value.sessionId;
7270
if (!sessionId) return;
7371

@@ -91,6 +89,7 @@ export function useKokoSessionAdapter(paneId: MaybeRefOrGetter<string>) {
9189
shareInfo,
9290
onlineUsers,
9391
userOptions,
92+
hasMoreUsers,
9493
searchUsers,
9594
copyShareURL,
9695
createShareLink,

‎ui/koko/tests/terminal/terminalUi.test.ts‎

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { describe, expect, it } from "vitest";
22
import localShellSurface from "../../../workspaces/LocalShellSessionSurface.vue?raw";
33
import drawerGeneral from "../../components/Drawer/General/index.vue?raw";
44
import drawerComponent from "../../components/Drawer/index.vue?raw";
5-
import sessionShare from "../../components/Drawer/SessionShare/index.vue?raw";
65
import searchInput from "../../components/SearchInput/index.vue?raw";
76
import terminalComponent from "../../components/Terminal/index.vue?raw";
87
import terminalProvider from "../../components/TerminalProvider/index.vue?raw";
@@ -75,12 +74,6 @@ describe("terminal UI composition", () => {
7574
expect(drawerGeneral).toContain("writeDataToTerminal");
7675
});
7776

78-
it("copies a share URL only when sharing is enabled", () => {
79-
expect(sessionShare).toContain("copyShareURL");
80-
expect(sessionShare).toContain(':disabled="!shareInfo.enableShare || !shareInfo.shareId"');
81-
expect(sessionShare).toContain("onlineUsers");
82-
});
83-
8477
it("keeps SearchInput clicks void-typed and bound to the xterm search addon", () => {
8578
expect(searchInput).toContain("void props.searchAddon.findPrevious");
8679
expect(searchInput).toContain("void props.searchAddon.findNext");
@@ -91,6 +84,5 @@ describe("terminal UI composition", () => {
9184
it("voids the workspace shell retry emit for UButton", () => {
9285
expect(baseWorkspaceShell).toContain("@click=\"void $emit('retry')\"");
9386
expect(baseWorkspaceShell).toContain("i-lucide-circle-alert");
94-
expect(baseWorkspaceShell).toContain("i-lucide-loader-circle");
9587
});
9688
});

0 commit comments

Comments
 (0)