From 2a890d894dec0b4fa0909d1d64c1b3ff94c051b0 Mon Sep 17 00:00:00 2001 From: sunlei Date: Thu, 18 Jun 2026 13:12:08 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E6=94=B6=E6=95=9BNapCat=E8=BF=90?= =?UTF-8?q?=E8=A1=8C=E6=80=81=E6=A3=80=E6=9F=A5=E8=84=9A=E6=9C=AC=E8=BE=B9?= =?UTF-8?q?=E7=95=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../qqbot-napcat-account-runtime.service.ts | 2 +- ...apcat-runtime-profile-inspector.service.ts | 44 ++++++++----------- .../runtime/napcat-runtime-profile.service.ts | 6 +-- .../napcat-session-behavior.service.ts | 2 +- .../runtime/napcat-physical-oui-catalog.ts | 2 +- src/modules/qqbot/napcat/index.ts | 1 + ...ntime-profile-inspection-script.service.ts | 27 ++++++++++++ .../qqbot/napcat/qqbot-napcat.module.ts | 2 + .../napcat/runtime-protocol-profile.spec.ts | 8 +--- 9 files changed, 57 insertions(+), 37 deletions(-) create mode 100644 src/modules/qqbot/napcat/infrastructure/integration/container/napcat-runtime-profile-inspection-script.service.ts diff --git a/src/modules/qqbot/napcat/application/account-runtime/qqbot-napcat-account-runtime.service.ts b/src/modules/qqbot/napcat/application/account-runtime/qqbot-napcat-account-runtime.service.ts index dd1496c..18fcb7d 100644 --- a/src/modules/qqbot/napcat/application/account-runtime/qqbot-napcat-account-runtime.service.ts +++ b/src/modules/qqbot/napcat/application/account-runtime/qqbot-napcat-account-runtime.service.ts @@ -26,7 +26,7 @@ export class QqbotNapcatAccountRuntimeService implements QqbotAccountNapcatRunti /** * Creates the account-list runtime adapter that joins persisted bindings, container status, and optional profile summaries. * @param accountNapcatRepository - Binding repository used to pick the primary NapCat container for each QQBot account. - * @param napcatContainerRepository - Container repository used for cached Docker/WebUI status and non-secret metadata. + * @param napcatContainerRepository - Container repository used for cached runtime/WebUI status and non-secret metadata. * @param napcatContainerService - Runtime integration service for bounded NapCat/WebUI status probes and auto-login. * @param toolsService - Shared helpers for status text normalization and NapCat offline-message classification. * @param runtimeProfileInspector - Optional profile reader that enriches list rows without changing login state. diff --git a/src/modules/qqbot/napcat/application/runtime/napcat-runtime-profile-inspector.service.ts b/src/modules/qqbot/napcat/application/runtime/napcat-runtime-profile-inspector.service.ts index 9c4dc3b..a012a91 100644 --- a/src/modules/qqbot/napcat/application/runtime/napcat-runtime-profile-inspector.service.ts +++ b/src/modules/qqbot/napcat/application/runtime/napcat-runtime-profile-inspector.service.ts @@ -1,8 +1,9 @@ -import { Injectable } from '@nestjs/common'; +import { Injectable, Optional } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { InjectRepository } from '@nestjs/typeorm'; import { In, Repository } from 'typeorm'; import { ToolsService } from '@/common'; +import { NapcatRuntimeProfileInspectionScriptService } from '../../infrastructure/integration/container/napcat-runtime-profile-inspection-script.service'; import { NapcatProtocolProfile } from '../../infrastructure/persistence/napcat-protocol-profile.entity'; import { NapcatRuntimeProfile } from '../../infrastructure/persistence/napcat-runtime-profile.entity'; @@ -22,11 +23,12 @@ export type NapcatRuntimeProfileSummary = { @Injectable() export class NapcatRuntimeProfileInspectorService { /** - * Initializes runtime inspection over the existing SSH-managed container model. - * @param runtimeProfileRepository - Runtime profile repository updated with latest Docker and desktop evidence. + * Initializes runtime inspection over the existing remote-managed runtime model. + * @param runtimeProfileRepository - Runtime profile repository updated with latest container and desktop evidence. * @param protocolProfileRepository - Protocol profile repository updated with config hashes and drift state. - * @param configService - Runtime config provider used for SSH target and inspection timeout defaults. + * @param configService - Runtime config provider used for inspection timeout defaults. * @param toolsService - Shared helper used to normalize string evidence before redaction. + * @param inspectionScriptService - Infrastructure helper that owns remote script construction. */ constructor( @InjectRepository(NapcatRuntimeProfile) @@ -35,25 +37,26 @@ export class NapcatRuntimeProfileInspectorService { private readonly protocolProfileRepository: Repository, private readonly configService: ConfigService, private readonly toolsService: ToolsService, - ) {} + @Optional() + private readonly inspectionScriptService?: NapcatRuntimeProfileInspectionScriptService, + ) { + this.inspectionScriptService = + inspectionScriptService || + new NapcatRuntimeProfileInspectionScriptService(); + } /** - * Builds the remote inspection script for Docker and in-container profile evidence. - * @param containerName - Docker container name selected from the persisted NapCat container row. - * @returns Shell script that collects runtime evidence without reading secret env values. + * Delegates remote profile evidence script creation to the infrastructure helper. + * @param containerName - Runtime container name selected from the persisted NapCat container row. + * @returns Read-only profile evidence script without secret environment reads. */ buildInspectScript(containerName: string) { - return ` -set -eu -NAME=${this.sh(containerName)} -docker inspect "$NAME" -docker exec "$NAME" sh -lc 'locale -a; locale; date +%Z; fc-match "Noto Sans CJK SC"; test ! -e /.dockerenv; cat /proc/1/cgroup; id; ps -eo user,args | grep -E "qq|NapCat|Xvfb" | grep -v grep || true' -`; + return this.inspectionScriptService.buildInspectScript(containerName); } /** * Redacts secrets before evidence is stored, logged, or returned to Admin. - * @param value - Evidence object or primitive produced by Docker, NapCat, or config writers. + * @param value - Evidence object or primitive produced by runtime, NapCat, or config writers. * @returns Evidence with sensitive keys and token query values replaced by placeholders. */ sanitizeEvidence(value: unknown): unknown { @@ -140,7 +143,7 @@ docker exec "$NAME" sh -lc 'locale -a; locale; date +%Z; fc-match "Noto Sans CJK /** * Reads the bounded runtime profile inspection timeout. - * @returns Positive timeout in milliseconds for future SSH inspection calls. + * @returns Positive timeout in milliseconds for future remote inspection calls. */ private getInspectionTimeoutMs() { const value = Number( @@ -173,13 +176,4 @@ docker exec "$NAME" sh -lc 'locale -a; locale; date +%Z; fc-match "Noto Sans CJK private redactString(value: string) { return value.replace(/token=[^&\s]+/gi, 'token=[REDACTED]'); } - - /** - * Quotes shell literals used by read-only inspection scripts. - * @param value - Container name selected from trusted persistence. - * @returns POSIX-safe single-quoted shell literal. - */ - private sh(value: string) { - return `'${`${value}`.replace(/'/g, `'\\''`)}'`; - } } diff --git a/src/modules/qqbot/napcat/application/runtime/napcat-runtime-profile.service.ts b/src/modules/qqbot/napcat/application/runtime/napcat-runtime-profile.service.ts index 8c05e21..6e19990 100644 --- a/src/modules/qqbot/napcat/application/runtime/napcat-runtime-profile.service.ts +++ b/src/modules/qqbot/napcat/application/runtime/napcat-runtime-profile.service.ts @@ -5,7 +5,7 @@ import type { NapcatRuntimeProfileSnapshot } from '../../domain/runtime/napcat-p @Injectable() export class NapcatRuntimeProfileService { /** - * Initializes the profile resolver used before Docker script generation. + * Initializes the profile resolver used before managed runtime script generation. * @param configService - Nest config provider that supplies image ref, UID/GID, shm size, and profile version. */ constructor(private readonly configService: ConfigService) {} @@ -13,7 +13,7 @@ export class NapcatRuntimeProfileService { /** * Resolves the runtime profile for an account-owned NapCat container. * @param input - Account, container, data directory, and device identity ids that tie generated profile evidence to persistence. - * @returns Runtime profile snapshot used by Docker script generation and later persistence. + * @returns Runtime profile snapshot used by managed runtime script generation and later persistence. */ resolveRuntimeProfile(input: { accountId: string; @@ -49,7 +49,7 @@ export class NapcatRuntimeProfileService { * Reads a trimmed string config value for profile generation. * @param key - Environment key that controls NapCat runtime profile generation. * @param defaultValue - Value used when the key is absent from runtime config. - * @returns Trimmed string value consumed by Docker script generation. + * @returns Trimmed string value consumed by managed runtime script generation. */ private getString(key: string, defaultValue: string) { return `${this.configService.get(key) || defaultValue}`.trim(); diff --git a/src/modules/qqbot/napcat/application/runtime/napcat-session-behavior.service.ts b/src/modules/qqbot/napcat/application/runtime/napcat-session-behavior.service.ts index e47566b..216f5bc 100644 --- a/src/modules/qqbot/napcat/application/runtime/napcat-session-behavior.service.ts +++ b/src/modules/qqbot/napcat/application/runtime/napcat-session-behavior.service.ts @@ -41,7 +41,7 @@ export class NapcatSessionBehaviorService { /** * Converts housekeeping failure into an evidence-only action. * @param input - Account and failure summary from a low-side-effect housekeeping call. - * @returns Decision that disables behavior extensions without resetting login, retrying password, recreating Docker, or refreshing QR. + * @returns Decision that disables behavior extensions without resetting login, retrying password, recreating runtime, or refreshing QR. */ handleHousekeepingFailure(input: { accountId: string; diff --git a/src/modules/qqbot/napcat/domain/runtime/napcat-physical-oui-catalog.ts b/src/modules/qqbot/napcat/domain/runtime/napcat-physical-oui-catalog.ts index 1d1d64d..d472083 100644 --- a/src/modules/qqbot/napcat/domain/runtime/napcat-physical-oui-catalog.ts +++ b/src/modules/qqbot/napcat/domain/runtime/napcat-physical-oui-catalog.ts @@ -28,7 +28,7 @@ export const NAPCAT_REJECTED_VIRTUAL_OUI_PREFIXES = [ ] as const; /** - * Checks whether a generated MAC starts with a Docker or VM-style prefix. + * Checks whether a generated MAC starts with a container or VM-style prefix. * @param macAddress - Stable MAC candidate generated from account/device seed. * @returns True when the prefix belongs to a rejected container or virtualization range. */ diff --git a/src/modules/qqbot/napcat/index.ts b/src/modules/qqbot/napcat/index.ts index 190cd0d..1535a77 100644 --- a/src/modules/qqbot/napcat/index.ts +++ b/src/modules/qqbot/napcat/index.ts @@ -19,4 +19,5 @@ export * from './application/runtime/napcat-login-event.service'; export * from './application/runtime/napcat-runtime-profile-inspector.service'; export * from './application/runtime/napcat-session-behavior.service'; export * from './infrastructure/persistence'; +export * from './infrastructure/integration/container/napcat-runtime-profile-inspection-script.service'; export * from './infrastructure/integration/container/qqbot-napcat-container.service'; diff --git a/src/modules/qqbot/napcat/infrastructure/integration/container/napcat-runtime-profile-inspection-script.service.ts b/src/modules/qqbot/napcat/infrastructure/integration/container/napcat-runtime-profile-inspection-script.service.ts new file mode 100644 index 0000000..619072b --- /dev/null +++ b/src/modules/qqbot/napcat/infrastructure/integration/container/napcat-runtime-profile-inspection-script.service.ts @@ -0,0 +1,27 @@ +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class NapcatRuntimeProfileInspectionScriptService { + /** + * Builds the remote inspection script for Docker and in-container profile evidence. + * @param containerName - Docker container name selected from the persisted NapCat container row. + * @returns Shell script that collects runtime evidence without reading secret environment values. + */ + buildInspectScript(containerName: string) { + return ` +set -eu +NAME=${this.sh(containerName)} +docker inspect "$NAME" +docker exec "$NAME" sh -lc 'locale -a; locale; date +%Z; fc-match "Noto Sans CJK SC"; test ! -e /.dockerenv; cat /proc/1/cgroup; id; ps -eo user,args | grep -E "qq|NapCat|Xvfb" | grep -v grep || true' +`; + } + + /** + * Quotes shell literals used by read-only inspection scripts. + * @param value - Container name selected from trusted persistence. + * @returns POSIX-safe single-quoted shell literal. + */ + private sh(value: string) { + return `'${`${value}`.replace(/'/g, `'\\''`)}'`; + } +} diff --git a/src/modules/qqbot/napcat/qqbot-napcat.module.ts b/src/modules/qqbot/napcat/qqbot-napcat.module.ts index 57309fd..f9b9885 100644 --- a/src/modules/qqbot/napcat/qqbot-napcat.module.ts +++ b/src/modules/qqbot/napcat/qqbot-napcat.module.ts @@ -14,6 +14,7 @@ import { NapcatRuntimeProfileService } from './application/runtime/napcat-runtim import { NapcatSessionBehaviorService } from './application/runtime/napcat-session-behavior.service'; import { QqbotNapcatLoginController } from './contract/qqbot-napcat-login.controller'; import { QqbotNapcatRuntimeController } from './contract/qqbot-napcat-runtime.controller'; +import { NapcatRuntimeProfileInspectionScriptService } from './infrastructure/integration/container/napcat-runtime-profile-inspection-script.service'; import { QqbotNapcatContainerService } from './infrastructure/integration/container/qqbot-napcat-container.service'; import { NapcatDeviceIdentityService } from './infrastructure/integration/device/napcat-device-identity.service'; import { NAPCAT_RUNTIME_ENTITIES } from './infrastructure/persistence'; @@ -32,6 +33,7 @@ export const QQBOT_NAPCAT_PROVIDERS = [ NapcatLoginStateStoreService, NapcatLoginEventService, NapcatRuntimeProfileInspectorService, + NapcatRuntimeProfileInspectionScriptService, NapcatRuntimeProfileService, NapcatSessionBehaviorService, QqbotNapcatAccountRuntimeService, diff --git a/test/modules/qqbot/napcat/runtime-protocol-profile.spec.ts b/test/modules/qqbot/napcat/runtime-protocol-profile.spec.ts index eeaf2b7..6b09fd9 100644 --- a/test/modules/qqbot/napcat/runtime-protocol-profile.spec.ts +++ b/test/modules/qqbot/napcat/runtime-protocol-profile.spec.ts @@ -9,6 +9,7 @@ import { JwtAuthGuard } from '../../../../src/modules/admin/identity/auth/jwt-au import { NapcatConfigWriterService } from '../../../../src/modules/qqbot/napcat/application/runtime/napcat-config-writer.service'; import { NapcatRuntimeProfileService } from '../../../../src/modules/qqbot/napcat/application/runtime/napcat-runtime-profile.service'; import { NapcatRuntimeProfileInspectorService } from '../../../../src/modules/qqbot/napcat/application/runtime/napcat-runtime-profile-inspector.service'; +import { NapcatRuntimeProfileInspectionScriptService } from '../../../../src/modules/qqbot/napcat/infrastructure/integration/container/napcat-runtime-profile-inspection-script.service'; import { QqbotNapcatRuntimeController } from '../../../../src/modules/qqbot/napcat/contract/qqbot-napcat-runtime.controller'; import { NapcatLoginEvent, @@ -178,12 +179,7 @@ describe('NapCat runtime profile generation', () => { describe('NapCat runtime profile inspector', () => { it('builds a bounded SSH inspection script without exposing secrets', () => { - const service = new NapcatRuntimeProfileInspectorService( - {} as any, - {} as any, - {} as any, - new ToolsService(), - ) as any; + const service = new NapcatRuntimeProfileInspectionScriptService(); const script = service.buildInspectScript('kt-qqbot-napcat-10001');