Skip to content

Commit d86f048

Browse files
feat: 适配interface协议中的加密字段 (#328)
1 parent 115fcb3 commit d86f048

16 files changed

Lines changed: 409 additions & 63 deletions

src-tauri/src/commands/maa_agent.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -737,8 +737,9 @@ pub async fn start_tasks_impl(
737737
debug!("[start_tasks] Preparing task {}: entry={}", idx, task.entry);
738738

739739
info!(
740-
"[start_tasks] Calling post_task: entry={}, override={}",
741-
task.entry, task.pipeline_override
740+
"[start_tasks] Calling post_task: entry={}, override_len={}",
741+
task.entry,
742+
task.pipeline_override.len()
742743
);
743744
match tasker.post_task(&task.entry, &task.pipeline_override) {
744745
Ok(job) => {

src/components/DashboardView.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import clsx from 'clsx';
2626
import { useAppStore } from '@/stores/appStore';
2727
import { maaService } from '@/services/maaService';
2828
import { buildTaskOptionSummary } from '@/services/telemetryService';
29+
import { collectPasswordPlaintextsFromRunnableTasks } from '@/utils/passwordOptionValues';
2930
import { ContextMenu, useContextMenu, type MenuItem } from './ContextMenu';
3031
import { FrameRateSelector, getFrameInterval } from './FrameRateSelector';
3132
import { resolveI18nText } from '@/services/contentResolver';
@@ -375,6 +376,11 @@ function InstanceCard({ instanceId, instanceName, isActive, onSelect }: Instance
375376
type: projectInterface?.controller.find((c) => c.name === currentControllerName)
376377
?.type,
377378
},
379+
collectPasswordPlaintextsFromRunnableTasks(
380+
batchTasks,
381+
useAppStore.getState().globalOptionValues,
382+
projectInterface?.option ?? {},
383+
),
378384
);
379385

380386
batchTaskIds.forEach((maaTaskId, index) => {

src/components/FormControls.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ interface TextInputProps {
7474
disabled?: boolean;
7575
hasError?: boolean;
7676
className?: string;
77-
type?: 'text' | 'number';
77+
type?: 'text' | 'number' | 'password';
7878
inputMode?: React.HTMLAttributes<HTMLInputElement>['inputMode'];
7979
step?: number;
8080
integerOnly?: boolean;
@@ -120,6 +120,7 @@ export function TextInput({
120120
return (
121121
<input
122122
type={type}
123+
autoComplete={type === 'password' ? 'off' : undefined}
123124
value={value}
124125
onChange={(e) => {
125126
if (!integerOnly) {

src/components/OptionEditor.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,13 @@ function InputField({
348348
disabled={disabled}
349349
hasError={!!validationError}
350350
className="min-w-[min(12rem,100%)] flex-1 basis-[30%]"
351-
type={input.pipeline_type === 'int' ? 'number' : 'text'}
351+
type={
352+
input.password
353+
? 'password'
354+
: input.pipeline_type === 'int'
355+
? 'number'
356+
: 'text'
357+
}
352358
inputMode={input.pipeline_type === 'int' ? 'numeric' : undefined}
353359
step={input.pipeline_type === 'int' ? 1 : undefined}
354360
integerOnly={input.pipeline_type === 'int'}

src/components/TaskItem.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { isPretaskName, getPretaskItem, buildPretaskDef } from '@/types/pretasks
1818
import { getInterfaceLangKey } from '@/i18n';
1919
import clsx from 'clsx';
2020
import { loggers } from '@/utils/logger';
21+
import { isPasswordInput } from '@/utils/passwordOptionValues';
2122

2223
/** 选项预览标签组件 */
2324
function OptionPreviewTag({
@@ -581,7 +582,7 @@ export function TaskItem({ instanceId, task }: TaskItemProps) {
581582
previews.push({
582583
key: optionKey,
583584
label: optionLabel,
584-
value: inputValue,
585+
value: isPasswordInput(firstInput) ? '••••' : inputValue,
585586
type: 'input',
586587
});
587588
}

src/components/TaskList.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ import {
4545
getImportErrorType,
4646
} from '@/utils/tabExportImport';
4747
import { generateId, initializeAllOptionValues, sanitizeOptionValues } from '@/stores/helpers';
48+
import { decryptPasswordOptionValues } from '@/utils/passwordOptionValues';
4849
import { isPretaskName } from '@/types/pretasks';
4950
import { loggers } from '@/utils/logger';
5051
import { toast } from 'sonner';
@@ -142,7 +143,11 @@ function useImportConfigActions(instanceId: string) {
142143
? initializeAllOptionValues(taskDef.option, mergedOptions)
143144
: {};
144145
const cleanedValues = mergedOptions
145-
? sanitizeOptionValues(task.optionValues, mergedOptions)
146+
? decryptPasswordOptionValues(
147+
sanitizeOptionValues(task.optionValues, mergedOptions),
148+
mergedOptions,
149+
projectName,
150+
)
146151
: {};
147152

148153
return {
@@ -361,6 +366,7 @@ export function TaskList() {
361366
? t('preset.exportShareHint', { projectName, tabName: instance.name })
362367
: '';
363368
const exportFooter = projectName ? t('preset.exportShareFooter', { projectName }) : '';
369+
const exportOptions = projectInterface?.option;
364370

365371
const menuItems: MenuItem[] = [
366372
{
@@ -424,7 +430,7 @@ export function TaskList() {
424430
exportWithToast(instance, projectName, exportHint, exportFooter, {
425431
success: t('preset.exportSuccess'),
426432
failed: t('preset.exportFailed'),
427-
});
433+
}, exportOptions);
428434
}
429435
},
430436
},
@@ -437,7 +443,7 @@ export function TaskList() {
437443
exportFileWithToast(instance, projectName, exportHint, exportFooter, {
438444
success: t('preset.exportFileSuccess'),
439445
failed: t('preset.exportFileFailed'),
440-
});
446+
}, exportOptions);
441447
}
442448
},
443449
},

src/components/Toolbar.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { useAppStore } from '@/stores/appStore';
1414
import { isTaskCompatible } from '@/stores/helpers';
1515
import { maaService } from '@/services/maaService';
1616
import { buildTaskOptionSummary } from '@/services/telemetryService';
17+
import { collectPasswordPlaintextsFromRunnableTasks } from '@/utils/passwordOptionValues';
1718
import clsx from 'clsx';
1819
import {
1920
loggers,
@@ -1283,6 +1284,11 @@ export function Toolbar({ showAddPanel, onToggleAddPanel, className }: ToolbarPr
12831284
type: projectInterface?.controller.find((c) => c.name === currentControllerName)
12841285
?.type,
12851286
},
1287+
collectPasswordPlaintextsFromRunnableTasks(
1288+
batchTasks,
1289+
useAppStore.getState().globalOptionValues,
1290+
projectInterface?.option ?? {},
1291+
),
12861292
);
12871293

12881294
log.info(`实例 ${targetInstance.name}: ${batchName}任务已提交, task_ids:`, batchTaskIds);

src/services/maaService.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import type {
1616
InstanceRuntimeInfo,
1717
} from '@/types/maa';
1818
import { loggers } from '@/utils/logger';
19+
import { redactSecretsInText } from '@/utils/passwordOptionValues';
1920
import { isTauri } from '@/utils/paths';
2021
import i18n from '@/i18n';
2122
import { apiDelete, apiGet, apiPost, apiPut, getApiBase } from '@/utils/backendApi';
@@ -646,10 +647,15 @@ export const maaService = {
646647
piEnvs?: Record<string, string>,
647648
resetState: boolean = true,
648649
controllerInfo?: ControllerTelemetryInfo,
650+
logRedactSecrets?: string[],
649651
): Promise<number[]> {
650652
log.info('启动任务, 实例:', instanceId, ', 任务数:', tasks.length, ', cwd:', cwd || '.');
651653
tasks.forEach((task, i) => {
652-
log.debug(` 任务[${i}]: entry=${task.entry}, pipelineOverride=${task.pipeline_override}`);
654+
const override =
655+
logRedactSecrets && logRedactSecrets.length > 0
656+
? redactSecretsInText(task.pipeline_override, logRedactSecrets)
657+
: task.pipeline_override;
658+
log.debug(` 任务[${i}]: entry=${task.entry}, pipelineOverride=${override}`);
653659
});
654660
if (agentConfigs && agentConfigs.length > 0) {
655661
log.info(

src/services/telemetryService.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import type {
1616
} from '@/types/interface';
1717
import { loggers } from '@/utils/logger';
1818
import { findSwitchCase } from '@/utils/optionHelpers';
19+
import { isPasswordInput } from '@/utils/passwordOptionValues';
1920
import { isTauri } from '@/utils/paths';
2021

2122
const log = loggers.telemetry;
@@ -39,7 +40,9 @@ const summarizeInputValue = (
3940
value: string,
4041
pipelineType?: 'string' | 'int' | 'bool',
4142
inputType?: 'text' | 'file' | 'time',
43+
password?: boolean,
4244
): string => {
45+
if (password) return value ? 'filled' : 'empty';
4346
const isSafeToReport =
4447
pipelineType === 'int' || pipelineType === 'bool' || (inputType === 'time' && !!value);
4548
if (isSafeToReport) return value;
@@ -100,7 +103,12 @@ const collectOptionSummary = (
100103
const raw = optionValue.values[inputDef.name] ?? inputDef.default ?? '';
101104
put(
102105
`${optionKey}.${inputDef.name}`,
103-
summarizeInputValue(raw, inputDef.pipeline_type, inputDef.input_type),
106+
summarizeInputValue(
107+
raw,
108+
inputDef.pipeline_type,
109+
inputDef.input_type,
110+
isPasswordInput(inputDef),
111+
),
104112
);
105113
}
106114
}

src/stores/appStore.ts

Lines changed: 79 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {
1111
resolveThemeMode,
1212
unregisterCustomAccent,
1313
} from '@/themes';
14-
import type { LegacyActionConfig, MxuConfig, RecentlyClosedInstance } from '@/types/config';
14+
import type { LegacyActionConfig, MxuConfig, RecentlyClosedInstance, SavedTask } from '@/types/config';
1515
import {
1616
clampAddTaskPanelHeight,
1717
DEFAULT_MAX_LOGS_PER_INSTANCE,
@@ -40,6 +40,10 @@ import {
4040
resolveCompatTaskDef,
4141
} from '@/types/pretasks';
4242
import { decryptCdk, encryptCdk } from '@/utils/cdkCrypto';
43+
import {
44+
decryptPasswordOptionValues,
45+
encryptPasswordOptionValues,
46+
} from '@/utils/passwordOptionValues';
4347
import { loggers } from '@/utils/logger';
4448
import { findSwitchCase } from '@/utils/optionHelpers';
4549
import { create } from 'zustand';
@@ -109,6 +113,25 @@ function cleanOptionValues(
109113
return sanitizeOptionValues(optionValues, pi.option, (message) => loggers.config.warn(message));
110114
}
111115

116+
function restoreOptionValuesFromConfig(
117+
optionValues: Record<string, OptionValue>,
118+
pi: ProjectInterface | null,
119+
projectName?: string,
120+
): Record<string, OptionValue> {
121+
const cleaned = cleanOptionValues(optionValues, pi);
122+
if (!pi?.option) return cleaned;
123+
return decryptPasswordOptionValues(cleaned, pi.option, projectName);
124+
}
125+
126+
function persistOptionValues(
127+
optionValues: Record<string, OptionValue>,
128+
pi: ProjectInterface | null,
129+
projectName?: string,
130+
): Record<string, OptionValue> {
131+
if (!pi?.option) return optionValues;
132+
return encryptPasswordOptionValues(optionValues, pi.option, projectName);
133+
}
134+
112135
function updateSelectedName(
113136
selectedNames: Record<string, string>,
114137
instanceId: string,
@@ -1235,7 +1258,11 @@ export const useAppStore = create<AppState>()(
12351258
// pretask 伪任务的 option 引用顶层 pi.option
12361259
if (isPretaskName(t.taskName)) {
12371260
const pretaskItem = getPretaskItem(pi, t.taskName);
1238-
const cleanedValues = cleanOptionValues(t.optionValues, pi);
1261+
const cleanedValues = restoreOptionValuesFromConfig(
1262+
t.optionValues,
1263+
pi,
1264+
get().projectInterface?.name,
1265+
);
12391266
const defaultValues =
12401267
pretaskItem?.option && pi?.option
12411268
? initializeAllOptionValues(pretaskItem.option, pi.option)
@@ -1256,7 +1283,11 @@ export const useAppStore = create<AppState>()(
12561283
}
12571284

12581285
const taskDef = pi?.task.find((td) => td.name === t.taskName);
1259-
const cleanedValues = cleanOptionValues(t.optionValues, pi);
1286+
const cleanedValues = restoreOptionValuesFromConfig(
1287+
t.optionValues,
1288+
pi,
1289+
get().projectInterface?.name,
1290+
);
12601291
// 为缺失的 option 添加默认值(根据 default_case)
12611292
const defaultValues =
12621293
taskDef?.option && pi?.option
@@ -1437,7 +1468,21 @@ export const useAppStore = create<AppState>()(
14371468
stopTasks: 'F11',
14381469
globalEnabled: false,
14391470
},
1440-
recentlyClosed: config.recentlyClosed || [],
1471+
recentlyClosed: (config.recentlyClosed || []).map((rc) => ({
1472+
...rc,
1473+
tasks: rc.tasks.map((t) =>
1474+
isMxuSpecialTask(t.taskName)
1475+
? t
1476+
: {
1477+
...t,
1478+
optionValues: restoreOptionValuesFromConfig(
1479+
t.optionValues,
1480+
pi,
1481+
pi?.name,
1482+
),
1483+
},
1484+
),
1485+
})),
14411486
// 记录新增任务,并在有新增时自动展开添加任务面板
14421487
newTaskNames: detectedNewTaskNames,
14431488
showAddTaskPanel: detectedNewTaskNames.length > 0,
@@ -1446,13 +1491,14 @@ export const useAppStore = create<AppState>()(
14461491
// 全局任务设置值:以 global_option 的默认值为基底,合并已保存值(保存值优先)
14471492
globalOptionValues: (() => {
14481493
const globalKeys = pi?.global_option;
1494+
const projectName = pi?.name;
14491495
if (!globalKeys || globalKeys.length === 0 || !pi?.option) {
1450-
return cleanOptionValues(config.globalOptionValues || {}, pi);
1496+
return restoreOptionValuesFromConfig(config.globalOptionValues || {}, pi, projectName);
14511497
}
14521498
const defaults = initializeAllOptionValues(globalKeys, pi.option);
14531499
return {
14541500
...defaults,
1455-
...cleanOptionValues(config.globalOptionValues || {}, pi),
1501+
...restoreOptionValuesFromConfig(config.globalOptionValues || {}, pi, projectName),
14561502
};
14571503
})(),
14581504
});
@@ -2047,7 +2093,7 @@ export const useAppStore = create<AppState>()(
20472093
customName: t.customName,
20482094
enabled: t.enabled,
20492095
enabledByController: t.enabledByController ? { ...t.enabledByController } : undefined,
2050-
optionValues: cleanOptionValues(t.optionValues, pi),
2096+
optionValues: restoreOptionValuesFromConfig(t.optionValues, pi, pi?.name),
20512097
expanded: false,
20522098
})),
20532099
isRunning: false,
@@ -2295,6 +2341,13 @@ const _isWebUI = !isTauri();
22952341
// 生成配置用于保存
22962342
function generateConfig(): MxuConfig {
22972343
const state = useAppStore.getState();
2344+
const pi = state.projectInterface;
2345+
const projectName = pi?.name;
2346+
const persistTasks = (tasks: SavedTask[]): SavedTask[] =>
2347+
tasks.map((t) => ({
2348+
...t,
2349+
optionValues: persistOptionValues(t.optionValues, pi, projectName),
2350+
}));
22982351
return {
22992352
version: '1.0',
23002353
instances: state.instances.map((inst) => ({
@@ -2305,18 +2358,20 @@ function generateConfig(): MxuConfig {
23052358
controllerName: inst.controllerName,
23062359
resourceName: inst.resourceName,
23072360
savedDevice: inst.savedDevice,
2308-
tasks: inst.selectedTasks.map((t) => ({
2309-
id: t.id,
2310-
taskName: t.taskName,
2311-
customName: t.customName,
2312-
enabled: t.enabled,
2313-
enabledByController: cacheTaskEnabledForController(
2314-
t.enabledByController,
2315-
inst.controllerName,
2316-
t.enabled,
2317-
),
2318-
optionValues: t.optionValues,
2319-
})),
2361+
tasks: persistTasks(
2362+
inst.selectedTasks.map((t) => ({
2363+
id: t.id,
2364+
taskName: t.taskName,
2365+
customName: t.customName,
2366+
enabled: t.enabled,
2367+
enabledByController: cacheTaskEnabledForController(
2368+
t.enabledByController,
2369+
inst.controllerName,
2370+
t.enabled,
2371+
),
2372+
optionValues: t.optionValues,
2373+
})),
2374+
),
23202375
schedulePolicies: inst.schedulePolicies,
23212376
preActions: inst.preActions,
23222377
})),
@@ -2368,8 +2423,11 @@ function generateConfig(): MxuConfig {
23682423
customAccents: ba?.customAccents ?? state.customAccents,
23692424
};
23702425
})(),
2371-
globalOptionValues: state.globalOptionValues,
2372-
recentlyClosed: state.recentlyClosed,
2426+
globalOptionValues: persistOptionValues(state.globalOptionValues, pi, projectName),
2427+
recentlyClosed: state.recentlyClosed.map((rc) => ({
2428+
...rc,
2429+
tasks: persistTasks(rc.tasks),
2430+
})),
23732431
interfaceTaskSnapshot: state.projectInterface?.task.map((t) => t.name) || [],
23742432
newTaskNames: state.newTaskNames,
23752433
lastActiveInstanceId: state.activeInstanceId || undefined,

0 commit comments

Comments
 (0)