feat: Implement Qwen adapter and enhance AI platform detection
- Added a new Qwen adapter to facilitate monitoring through Playwright, leveraging internal text/chat managers. - Updated account detection logic to recognize Qwen sessions based on persisted cookies, improving binding reliability. - Relaxed authentication requirements for monitor tasks, allowing anonymous execution for certain AI platforms. - Enhanced the generic AI platform detection to include Qwen-specific logic, ensuring accurate session identification. - Modified the monitoring dashboard to include runtime state indicating if the current user's desktop client is online. - Updated various files including `account-binder.ts`, `runtime-controller.ts`, and `monitoring_service.go` to support new features and improvements.
This commit is contained in:
@@ -13,7 +13,7 @@ import type {
|
||||
MonitoringTimeBucket,
|
||||
} from "@geo/shared-types";
|
||||
import { aiPlatformCatalog } from "@geo/shared-types";
|
||||
import { computed, ref, watch } from "vue";
|
||||
import { computed, watch } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
|
||||
@@ -21,8 +21,6 @@ import PageHero from "@/components/PageHero.vue";
|
||||
import { brandsApi, monitoringApi, tenantAccountsApi } from "@/lib/api";
|
||||
import { formatDateTime } from "@/lib/display";
|
||||
import { formatError } from "@/lib/errors";
|
||||
import { kickoffMonitoringCycle } from "@/lib/monitoring-plugin";
|
||||
import { loadPublisherRuntimeState } from "@/lib/publisher-runtime";
|
||||
|
||||
const { t } = useI18n();
|
||||
const route = useRoute();
|
||||
@@ -102,7 +100,6 @@ const selectedBrandId = useStorage<number | null>("tracking_selected_brand", nul
|
||||
const selectedKeywordId = useStorage<number | null>("tracking_selected_keyword", null);
|
||||
const selectedPlatformId = useStorage<string>("tracking_selected_ai_platform_id", "all");
|
||||
const selectedBusinessDate = useStorage<string>("tracking_selected_business_date", trackingToday);
|
||||
const pluginPreparing = ref(false);
|
||||
|
||||
selectedPlatformId.value = normalizeTrackingPlatformId(selectedPlatformId.value);
|
||||
selectedBusinessDate.value = normalizeTrackingBusinessDate(selectedBusinessDate.value) ?? trackingToday;
|
||||
@@ -228,25 +225,6 @@ const trackingWindow = computed(() => {
|
||||
};
|
||||
});
|
||||
|
||||
async function ensureMonitoringPluginReady(options?: { silent?: boolean }): Promise<void> {
|
||||
pluginPreparing.value = true;
|
||||
try {
|
||||
const runtime = await loadPublisherRuntimeState();
|
||||
if (!runtime.ping.installed) {
|
||||
throw new Error("monitoring_plugin_required");
|
||||
}
|
||||
if (!runtime.pluginInstallationId) {
|
||||
throw new Error("monitoring_plugin_register_required");
|
||||
}
|
||||
} catch (error) {
|
||||
if (!options?.silent) {
|
||||
throw error;
|
||||
}
|
||||
} finally {
|
||||
pluginPreparing.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
const dashboardQuery = useQuery({
|
||||
queryKey: computed(() => [
|
||||
"tracking",
|
||||
@@ -274,52 +252,28 @@ const aiAccountsQuery = useQuery({
|
||||
|
||||
const collectNowMutation = useMutation({
|
||||
mutationFn: async () => {
|
||||
if (!selectedBrandId.value) {
|
||||
throw new Error("tracking_brand_required");
|
||||
}
|
||||
if (!selectedKeywordId.value) {
|
||||
throw new Error("tracking_keyword_required");
|
||||
}
|
||||
await ensureMonitoringPluginReady();
|
||||
const collectNowResult = await monitoringApi.collectNow(selectedBrandId.value as number, {
|
||||
|
||||
return monitoringApi.collectNow(selectedBrandId.value, {
|
||||
keyword_id: selectedKeywordId.value,
|
||||
});
|
||||
|
||||
try {
|
||||
const kickoffResult = await kickoffMonitoringCycle();
|
||||
return {
|
||||
collectNowResult,
|
||||
kickoffResult,
|
||||
kickoffError: null,
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
collectNowResult,
|
||||
kickoffResult: null,
|
||||
kickoffError: formatError(error),
|
||||
};
|
||||
}
|
||||
},
|
||||
onSuccess: ({ collectNowResult, kickoffResult, kickoffError }) => {
|
||||
if (kickoffError) {
|
||||
message.warning(`${collectNowResult.message},但后台采集启动失败:${kickoffError}`);
|
||||
void dashboardQuery.refetch();
|
||||
return;
|
||||
}
|
||||
|
||||
if (kickoffResult?.status === "already_running") {
|
||||
message.success(`${collectNowResult.message},插件后台已继续执行`);
|
||||
void dashboardQuery.refetch();
|
||||
return;
|
||||
}
|
||||
|
||||
onSuccess: (collectNowResult) => {
|
||||
message.success(collectNowResult.message);
|
||||
void dashboardQuery.refetch();
|
||||
},
|
||||
onError: (error) => {
|
||||
if (error instanceof Error && error.message === "tracking_keyword_required") {
|
||||
message.warning(t("tracking.collectKeywordRequired"));
|
||||
if (error instanceof Error && error.message === "tracking_brand_required") {
|
||||
message.warning(t("tracking.collectBrandRequired"));
|
||||
return;
|
||||
}
|
||||
if (error instanceof Error && (error.message === "monitoring_plugin_required" || error.message === "monitoring_plugin_register_required")) {
|
||||
message.warning(formatError(error));
|
||||
if (error instanceof Error && error.message === "tracking_keyword_required") {
|
||||
message.warning(t("tracking.collectKeywordRequired"));
|
||||
return;
|
||||
}
|
||||
message.error(formatError(error));
|
||||
@@ -330,6 +284,26 @@ const selectedBrand = computed<Brand | null>(() => {
|
||||
return brandsQuery.data.value?.find((item) => item.id === selectedBrandId.value) ?? null;
|
||||
});
|
||||
|
||||
const currentUserClientOnline = computed(() => {
|
||||
return dashboardQuery.data.value?.runtime.current_user_client_online === true;
|
||||
});
|
||||
|
||||
const collectNowDisabledReason = computed(() => {
|
||||
if (!selectedBrandId.value) {
|
||||
return t("tracking.collectBrandRequired");
|
||||
}
|
||||
if (!selectedKeywordId.value) {
|
||||
return t("tracking.collectKeywordRequired");
|
||||
}
|
||||
if (dashboardQuery.isLoading.value && !dashboardQuery.data.value) {
|
||||
return t("tracking.collectClientChecking");
|
||||
}
|
||||
if (!currentUserClientOnline.value) {
|
||||
return t("tracking.collectClientOnlineRequired");
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
const selectedDateHasData = computed(() => {
|
||||
return (dashboardQuery.data.value?.overview.actual_sample_count ?? 0) > 0;
|
||||
});
|
||||
@@ -514,8 +488,6 @@ const citationChartStyle = computed(() => {
|
||||
};
|
||||
});
|
||||
|
||||
void ensureMonitoringPluginReady({ silent: true });
|
||||
|
||||
const heroDescription = computed(() => {
|
||||
const overview = dashboardQuery.data.value?.overview;
|
||||
if (!overview?.last_sampled_at) {
|
||||
@@ -767,15 +739,19 @@ function disableTrackingDate(current: dayjs.Dayjs): boolean {
|
||||
:disabled-date="disableTrackingDate"
|
||||
/>
|
||||
</div>
|
||||
<a-button
|
||||
type="primary"
|
||||
:disabled="!selectedBrandId || !selectedKeywordId"
|
||||
:loading="collectNowMutation.isPending.value || pluginPreparing"
|
||||
@click="collectNowMutation.mutate()"
|
||||
>
|
||||
<template #icon><ReloadOutlined /></template>
|
||||
{{ t("tracking.collectNow") }}
|
||||
</a-button>
|
||||
<a-tooltip :title="collectNowDisabledReason ?? undefined">
|
||||
<span>
|
||||
<a-button
|
||||
type="primary"
|
||||
:disabled="Boolean(collectNowDisabledReason)"
|
||||
:loading="collectNowMutation.isPending.value"
|
||||
@click="collectNowMutation.mutate()"
|
||||
>
|
||||
<template #icon><ReloadOutlined /></template>
|
||||
{{ t("tracking.collectNow") }}
|
||||
</a-button>
|
||||
</span>
|
||||
</a-tooltip>
|
||||
</a-space>
|
||||
</template>
|
||||
</PageHero>
|
||||
|
||||
Reference in New Issue
Block a user