feat: 新增NapCat运行态Profile持久化
This commit is contained in:
parent
d8d722e92a
commit
38cc5fb5bf
@ -141,6 +141,118 @@ CREATE TABLE IF NOT EXISTS `napcat_runtime_cleanup` (
|
||||
KEY `idx_napcat_runtime_cleanup_session` (`session_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `napcat_runtime_profile` (
|
||||
`id` bigint NOT NULL,
|
||||
`account_id` bigint NOT NULL,
|
||||
`container_id` bigint DEFAULT NULL,
|
||||
`device_identity_id` bigint DEFAULT NULL,
|
||||
`profile_version` varchar(64) NOT NULL,
|
||||
`image_ref` varchar(255) NOT NULL,
|
||||
`image_digest` varchar(255) DEFAULT NULL,
|
||||
`base_image_digest` varchar(255) DEFAULT NULL,
|
||||
`desktop_profile_version` varchar(64) DEFAULT NULL,
|
||||
`locale_available` tinyint(1) NOT NULL DEFAULT 0,
|
||||
`fontconfig_evidence` json DEFAULT NULL,
|
||||
`timezone_evidence` json DEFAULT NULL,
|
||||
`runtime_uid` int DEFAULT NULL,
|
||||
`runtime_gid` int DEFAULT NULL,
|
||||
`shm_size` varchar(32) DEFAULT NULL,
|
||||
`locale` varchar(64) DEFAULT NULL,
|
||||
`xdg_config_home` varchar(255) DEFAULT NULL,
|
||||
`xdg_cache_home` varchar(255) DEFAULT NULL,
|
||||
`xdg_data_home` varchar(255) DEFAULT NULL,
|
||||
`persist_cache` tinyint(1) NOT NULL DEFAULT 1,
|
||||
`persist_local_share` tinyint(1) NOT NULL DEFAULT 1,
|
||||
`persist_logs` tinyint(1) NOT NULL DEFAULT 1,
|
||||
`hostname_strategy` varchar(64) NOT NULL,
|
||||
`mac_strategy` varchar(64) NOT NULL,
|
||||
`migrate_device_identity` tinyint(1) NOT NULL DEFAULT 0,
|
||||
`profile_status` varchar(32) NOT NULL,
|
||||
`last_check_evidence` json DEFAULT NULL,
|
||||
`last_checked_at` datetime DEFAULT NULL,
|
||||
`create_time` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
|
||||
`update_time` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_napcat_runtime_profile_account` (`account_id`),
|
||||
KEY `idx_napcat_runtime_profile_container` (`container_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `napcat_protocol_profile` (
|
||||
`id` bigint NOT NULL,
|
||||
`account_id` bigint NOT NULL,
|
||||
`container_id` bigint DEFAULT NULL,
|
||||
`profile_version` varchar(64) NOT NULL,
|
||||
`packet_backend` varchar(64) NOT NULL,
|
||||
`packet_server` varchar(255) NOT NULL DEFAULT '',
|
||||
`o3_hook_mode` int NOT NULL DEFAULT 1,
|
||||
`o3_hook_gray_enabled` tinyint(1) NOT NULL DEFAULT 0,
|
||||
`onebot_config_hash` varchar(128) DEFAULT NULL,
|
||||
`onebot_config_json` json DEFAULT NULL,
|
||||
`napcat_config_hash` varchar(128) DEFAULT NULL,
|
||||
`napcat_config_json` json DEFAULT NULL,
|
||||
`profile_status` varchar(32) NOT NULL,
|
||||
`last_check_evidence` json DEFAULT NULL,
|
||||
`last_checked_at` datetime DEFAULT NULL,
|
||||
`create_time` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
|
||||
`update_time` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_napcat_protocol_profile_account` (`account_id`),
|
||||
KEY `idx_napcat_protocol_profile_container` (`container_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `napcat_session_behavior_profile` (
|
||||
`id` bigint NOT NULL,
|
||||
`account_id` bigint NOT NULL,
|
||||
`profile_version` varchar(64) NOT NULL,
|
||||
`enabled` tinyint(1) NOT NULL DEFAULT 1,
|
||||
`cold_start_until` datetime DEFAULT NULL,
|
||||
`housekeeping_enabled` tinyint(1) NOT NULL DEFAULT 0,
|
||||
`housekeeping_interval_ms` int DEFAULT NULL,
|
||||
`next_housekeeping_at` datetime DEFAULT NULL,
|
||||
`last_housekeeping_at` datetime DEFAULT NULL,
|
||||
`last_housekeeping_result` json DEFAULT NULL,
|
||||
`presence_enabled` tinyint(1) NOT NULL DEFAULT 0,
|
||||
`presence_strategy` varchar(64) DEFAULT NULL,
|
||||
`last_presence_event_at` datetime DEFAULT NULL,
|
||||
`next_presence_event_at` datetime DEFAULT NULL,
|
||||
`auto_capability_stage` varchar(32) NOT NULL,
|
||||
`last_behavior_evidence` json DEFAULT NULL,
|
||||
`create_time` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
|
||||
`update_time` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_napcat_session_behavior_profile_account` (`account_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `napcat_login_event` (
|
||||
`id` bigint NOT NULL,
|
||||
`account_id` bigint NOT NULL,
|
||||
`container_id` bigint DEFAULT NULL,
|
||||
`event_kind` varchar(64) NOT NULL,
|
||||
`event_source` varchar(32) NOT NULL,
|
||||
`event_status` varchar(32) NOT NULL,
|
||||
`evidence` json DEFAULT NULL,
|
||||
`create_time` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
|
||||
`update_time` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_napcat_login_event_account` (`account_id`, `create_time`),
|
||||
KEY `idx_napcat_login_event_container` (`container_id`, `create_time`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `napcat_risk_mode` (
|
||||
`id` bigint NOT NULL,
|
||||
`account_id` bigint NOT NULL,
|
||||
`risk_mode` varchar(32) NOT NULL,
|
||||
`reason` varchar(255) DEFAULT NULL,
|
||||
`source_event` varchar(64) DEFAULT NULL,
|
||||
`expires_at` datetime DEFAULT NULL,
|
||||
`last_evidence` json DEFAULT NULL,
|
||||
`create_time` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
|
||||
`update_time` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_napcat_risk_mode_account` (`account_id`),
|
||||
KEY `idx_napcat_risk_mode_mode` (`risk_mode`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `qqbot_config` (
|
||||
`id` bigint NOT NULL,
|
||||
`config_key` varchar(120) NOT NULL,
|
||||
|
||||
@ -1016,3 +1016,110 @@ CREATE TABLE IF NOT EXISTS napcat_runtime_cleanup (
|
||||
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
KEY idx_napcat_runtime_cleanup_session (session_id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS napcat_runtime_profile (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
account_id BIGINT NOT NULL,
|
||||
container_id BIGINT NULL,
|
||||
device_identity_id BIGINT NULL,
|
||||
profile_version VARCHAR(64) NOT NULL,
|
||||
image_ref VARCHAR(255) NOT NULL,
|
||||
image_digest VARCHAR(255) NULL,
|
||||
base_image_digest VARCHAR(255) NULL,
|
||||
desktop_profile_version VARCHAR(64) NULL,
|
||||
locale_available TINYINT NOT NULL DEFAULT 0,
|
||||
fontconfig_evidence JSON NULL,
|
||||
timezone_evidence JSON NULL,
|
||||
runtime_uid INT NULL,
|
||||
runtime_gid INT NULL,
|
||||
shm_size VARCHAR(32) NULL,
|
||||
locale VARCHAR(64) NULL,
|
||||
xdg_config_home VARCHAR(255) NULL,
|
||||
xdg_cache_home VARCHAR(255) NULL,
|
||||
xdg_data_home VARCHAR(255) NULL,
|
||||
persist_cache TINYINT NOT NULL DEFAULT 1,
|
||||
persist_local_share TINYINT NOT NULL DEFAULT 1,
|
||||
persist_logs TINYINT NOT NULL DEFAULT 1,
|
||||
hostname_strategy VARCHAR(64) NOT NULL,
|
||||
mac_strategy VARCHAR(64) NOT NULL,
|
||||
migrate_device_identity TINYINT NOT NULL DEFAULT 0,
|
||||
profile_status VARCHAR(32) NOT NULL,
|
||||
last_check_evidence JSON NULL,
|
||||
last_checked_at DATETIME NULL,
|
||||
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
KEY idx_napcat_runtime_profile_account (account_id),
|
||||
KEY idx_napcat_runtime_profile_container (container_id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS napcat_protocol_profile (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
account_id BIGINT NOT NULL,
|
||||
container_id BIGINT NULL,
|
||||
profile_version VARCHAR(64) NOT NULL,
|
||||
packet_backend VARCHAR(64) NOT NULL,
|
||||
packet_server VARCHAR(255) NOT NULL DEFAULT '',
|
||||
o3_hook_mode INT NOT NULL DEFAULT 1,
|
||||
o3_hook_gray_enabled TINYINT NOT NULL DEFAULT 0,
|
||||
onebot_config_hash VARCHAR(128) NULL,
|
||||
onebot_config_json JSON NULL,
|
||||
napcat_config_hash VARCHAR(128) NULL,
|
||||
napcat_config_json JSON NULL,
|
||||
profile_status VARCHAR(32) NOT NULL,
|
||||
last_check_evidence JSON NULL,
|
||||
last_checked_at DATETIME NULL,
|
||||
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
KEY idx_napcat_protocol_profile_account (account_id),
|
||||
KEY idx_napcat_protocol_profile_container (container_id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS napcat_session_behavior_profile (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
account_id BIGINT NOT NULL,
|
||||
profile_version VARCHAR(64) NOT NULL,
|
||||
enabled TINYINT NOT NULL DEFAULT 1,
|
||||
cold_start_until DATETIME NULL,
|
||||
housekeeping_enabled TINYINT NOT NULL DEFAULT 0,
|
||||
housekeeping_interval_ms INT NULL,
|
||||
next_housekeeping_at DATETIME NULL,
|
||||
last_housekeeping_at DATETIME NULL,
|
||||
last_housekeeping_result JSON NULL,
|
||||
presence_enabled TINYINT NOT NULL DEFAULT 0,
|
||||
presence_strategy VARCHAR(64) NULL,
|
||||
last_presence_event_at DATETIME NULL,
|
||||
next_presence_event_at DATETIME NULL,
|
||||
auto_capability_stage VARCHAR(32) NOT NULL,
|
||||
last_behavior_evidence JSON NULL,
|
||||
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
KEY idx_napcat_session_behavior_profile_account (account_id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS napcat_login_event (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
account_id BIGINT NOT NULL,
|
||||
container_id BIGINT NULL,
|
||||
event_kind VARCHAR(64) NOT NULL,
|
||||
event_source VARCHAR(32) NOT NULL,
|
||||
event_status VARCHAR(32) NOT NULL,
|
||||
evidence JSON NULL,
|
||||
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
KEY idx_napcat_login_event_account (account_id, create_time),
|
||||
KEY idx_napcat_login_event_container (container_id, create_time)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS napcat_risk_mode (
|
||||
id BIGINT NOT NULL PRIMARY KEY,
|
||||
account_id BIGINT NOT NULL,
|
||||
risk_mode VARCHAR(32) NOT NULL,
|
||||
reason VARCHAR(255) NULL,
|
||||
source_event VARCHAR(64) NULL,
|
||||
expires_at DATETIME NULL,
|
||||
last_evidence JSON NULL,
|
||||
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
UNIQUE KEY uk_napcat_risk_mode_account (account_id),
|
||||
KEY idx_napcat_risk_mode_mode (risk_mode)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
@ -11,6 +11,11 @@ SELECT 'napcat_account_binding' AS table_name, COUNT(*) AS row_count FROM napcat
|
||||
SELECT 'napcat_login_session' AS table_name, COUNT(*) AS row_count FROM napcat_login_session;
|
||||
SELECT 'napcat_login_challenge' AS table_name, COUNT(*) AS row_count FROM napcat_login_challenge;
|
||||
SELECT 'napcat_runtime_cleanup' AS table_name, COUNT(*) AS row_count FROM napcat_runtime_cleanup;
|
||||
SELECT 'napcat_runtime_profile' AS table_name, COUNT(*) AS row_count FROM napcat_runtime_profile;
|
||||
SELECT 'napcat_protocol_profile' AS table_name, COUNT(*) AS row_count FROM napcat_protocol_profile;
|
||||
SELECT 'napcat_session_behavior_profile' AS table_name, COUNT(*) AS row_count FROM napcat_session_behavior_profile;
|
||||
SELECT 'napcat_login_event' AS table_name, COUNT(*) AS row_count FROM napcat_login_event;
|
||||
SELECT 'napcat_risk_mode' AS table_name, COUNT(*) AS row_count FROM napcat_risk_mode;
|
||||
SELECT 'qqbot_plugin_task' AS table_name, COUNT(*) AS row_count FROM qqbot_plugin_task;
|
||||
SELECT 'qqbot_plugin_task_run' AS table_name, COUNT(*) AS row_count FROM qqbot_plugin_task_run;
|
||||
|
||||
@ -111,6 +116,60 @@ WHERE table_schema = DATABASE()
|
||||
AND table_name = 'napcat_runtime_cleanup'
|
||||
AND index_name = 'idx_napcat_runtime_cleanup_session';
|
||||
|
||||
SELECT 'index_napcat_runtime_profile_account' AS check_name, COUNT(*) AS matched_rows
|
||||
FROM information_schema.statistics
|
||||
WHERE table_schema = DATABASE()
|
||||
AND table_name = 'napcat_runtime_profile'
|
||||
AND index_name = 'idx_napcat_runtime_profile_account';
|
||||
|
||||
SELECT 'index_napcat_runtime_profile_container' AS check_name, COUNT(*) AS matched_rows
|
||||
FROM information_schema.statistics
|
||||
WHERE table_schema = DATABASE()
|
||||
AND table_name = 'napcat_runtime_profile'
|
||||
AND index_name = 'idx_napcat_runtime_profile_container';
|
||||
|
||||
SELECT 'index_napcat_protocol_profile_account' AS check_name, COUNT(*) AS matched_rows
|
||||
FROM information_schema.statistics
|
||||
WHERE table_schema = DATABASE()
|
||||
AND table_name = 'napcat_protocol_profile'
|
||||
AND index_name = 'idx_napcat_protocol_profile_account';
|
||||
|
||||
SELECT 'index_napcat_protocol_profile_container' AS check_name, COUNT(*) AS matched_rows
|
||||
FROM information_schema.statistics
|
||||
WHERE table_schema = DATABASE()
|
||||
AND table_name = 'napcat_protocol_profile'
|
||||
AND index_name = 'idx_napcat_protocol_profile_container';
|
||||
|
||||
SELECT 'index_napcat_session_behavior_profile_account' AS check_name, COUNT(*) AS matched_rows
|
||||
FROM information_schema.statistics
|
||||
WHERE table_schema = DATABASE()
|
||||
AND table_name = 'napcat_session_behavior_profile'
|
||||
AND index_name = 'idx_napcat_session_behavior_profile_account';
|
||||
|
||||
SELECT 'index_napcat_login_event_account' AS check_name, COUNT(*) AS matched_rows
|
||||
FROM information_schema.statistics
|
||||
WHERE table_schema = DATABASE()
|
||||
AND table_name = 'napcat_login_event'
|
||||
AND index_name = 'idx_napcat_login_event_account';
|
||||
|
||||
SELECT 'index_napcat_login_event_container' AS check_name, COUNT(*) AS matched_rows
|
||||
FROM information_schema.statistics
|
||||
WHERE table_schema = DATABASE()
|
||||
AND table_name = 'napcat_login_event'
|
||||
AND index_name = 'idx_napcat_login_event_container';
|
||||
|
||||
SELECT 'index_napcat_risk_mode_account' AS check_name, COUNT(*) AS matched_rows
|
||||
FROM information_schema.statistics
|
||||
WHERE table_schema = DATABASE()
|
||||
AND table_name = 'napcat_risk_mode'
|
||||
AND index_name = 'uk_napcat_risk_mode_account';
|
||||
|
||||
SELECT 'index_napcat_risk_mode_mode' AS check_name, COUNT(*) AS matched_rows
|
||||
FROM information_schema.statistics
|
||||
WHERE table_schema = DATABASE()
|
||||
AND table_name = 'napcat_risk_mode'
|
||||
AND index_name = 'idx_napcat_risk_mode_mode';
|
||||
|
||||
SELECT 'column_napcat_login_challenge_session_id_varchar' AS check_name, COUNT(*) AS matched_rows
|
||||
FROM information_schema.columns
|
||||
WHERE table_schema = DATABASE()
|
||||
|
||||
@ -2,8 +2,13 @@ import { NapcatAccountBinding } from './napcat-account-binding.entity';
|
||||
import { NapcatContainer } from './napcat-container.entity';
|
||||
import { NapcatDeviceIdentity } from './napcat-device-identity.entity';
|
||||
import { NapcatLoginChallengeEntity } from './napcat-login-challenge.entity';
|
||||
import { NapcatLoginEvent } from './napcat-login-event.entity';
|
||||
import { NapcatLoginSession } from './napcat-login-session.entity';
|
||||
import { NapcatProtocolProfile } from './napcat-protocol-profile.entity';
|
||||
import { NapcatRiskMode } from './napcat-risk-mode.entity';
|
||||
import { NapcatRuntimeCleanup } from './napcat-runtime-cleanup.entity';
|
||||
import { NapcatRuntimeProfile } from './napcat-runtime-profile.entity';
|
||||
import { NapcatSessionBehaviorProfile } from './napcat-session-behavior-profile.entity';
|
||||
|
||||
export const NAPCAT_RUNTIME_DOMAIN_CONTRACT = {
|
||||
tables: [
|
||||
@ -13,6 +18,11 @@ export const NAPCAT_RUNTIME_DOMAIN_CONTRACT = {
|
||||
'napcat_login_session',
|
||||
'napcat_login_challenge',
|
||||
'napcat_runtime_cleanup',
|
||||
'napcat_runtime_profile',
|
||||
'napcat_protocol_profile',
|
||||
'napcat_session_behavior_profile',
|
||||
'napcat_login_event',
|
||||
'napcat_risk_mode',
|
||||
],
|
||||
} as const;
|
||||
|
||||
@ -23,4 +33,17 @@ export const NAPCAT_RUNTIME_ENTITIES = [
|
||||
NapcatLoginChallengeEntity,
|
||||
NapcatLoginSession,
|
||||
NapcatRuntimeCleanup,
|
||||
NapcatRuntimeProfile,
|
||||
NapcatProtocolProfile,
|
||||
NapcatSessionBehaviorProfile,
|
||||
NapcatLoginEvent,
|
||||
NapcatRiskMode,
|
||||
];
|
||||
|
||||
export {
|
||||
NapcatLoginEvent,
|
||||
NapcatProtocolProfile,
|
||||
NapcatRiskMode,
|
||||
NapcatRuntimeProfile,
|
||||
NapcatSessionBehaviorProfile,
|
||||
};
|
||||
|
||||
@ -0,0 +1,76 @@
|
||||
import { BeforeInsert, Column, Entity, Index, PrimaryColumn } from 'typeorm';
|
||||
import {
|
||||
ensureSnowflakeId,
|
||||
KtCreateDateColumn,
|
||||
KtDateTime,
|
||||
KtUpdateDateColumn,
|
||||
} from '@/common';
|
||||
|
||||
export type NapcatLoginEventKind =
|
||||
| 'captcha_required'
|
||||
| 'container_recreate'
|
||||
| 'container_restart'
|
||||
| 'manual_qr_created'
|
||||
| 'manual_qr_scanned'
|
||||
| 'new_device_required'
|
||||
| 'password_attempt'
|
||||
| 'quick_attempt'
|
||||
| 'recovery_suspended';
|
||||
|
||||
export type NapcatLoginEventSource =
|
||||
| 'admin'
|
||||
| 'runtime'
|
||||
| 'system'
|
||||
| 'watchdog';
|
||||
|
||||
export type NapcatLoginEventStatus =
|
||||
| 'blocked'
|
||||
| 'failed'
|
||||
| 'pending'
|
||||
| 'skipped'
|
||||
| 'success';
|
||||
|
||||
@Entity('napcat_login_event')
|
||||
@Index('idx_napcat_login_event_account', ['accountId', 'createTime'])
|
||||
@Index('idx_napcat_login_event_container', ['containerId', 'createTime'])
|
||||
export class NapcatLoginEvent {
|
||||
@PrimaryColumn({ type: 'bigint' })
|
||||
id: string;
|
||||
|
||||
@Column({ name: 'account_id', type: 'bigint' })
|
||||
accountId: string;
|
||||
|
||||
@Column({
|
||||
default: null,
|
||||
name: 'container_id',
|
||||
nullable: true,
|
||||
type: 'bigint',
|
||||
})
|
||||
containerId: null | string;
|
||||
|
||||
@Column({ length: 64, name: 'event_kind' })
|
||||
eventKind: NapcatLoginEventKind;
|
||||
|
||||
@Column({ length: 32, name: 'event_source' })
|
||||
eventSource: NapcatLoginEventSource;
|
||||
|
||||
@Column({ length: 32, name: 'event_status' })
|
||||
eventStatus: NapcatLoginEventStatus;
|
||||
|
||||
@Column({ default: null, nullable: true, type: 'json' })
|
||||
evidence: null | Record<string, unknown>;
|
||||
|
||||
@KtCreateDateColumn({ name: 'create_time' })
|
||||
createTime: KtDateTime;
|
||||
|
||||
@KtUpdateDateColumn({ name: 'update_time' })
|
||||
updateTime: KtDateTime;
|
||||
|
||||
/**
|
||||
* Assigns a stable Snowflake id before persisting a login-side risk event.
|
||||
*/
|
||||
@BeforeInsert()
|
||||
createId() {
|
||||
ensureSnowflakeId(this);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,112 @@
|
||||
import { BeforeInsert, Column, Entity, Index, PrimaryColumn } from 'typeorm';
|
||||
import {
|
||||
ensureSnowflakeId,
|
||||
KtCreateDateColumn,
|
||||
KtDateTime,
|
||||
KtDateTimeColumn,
|
||||
KtUpdateDateColumn,
|
||||
} from '@/common';
|
||||
|
||||
export type NapcatProtocolProfileStatus =
|
||||
| 'drifted'
|
||||
| 'failed'
|
||||
| 'pending'
|
||||
| 'synced';
|
||||
|
||||
@Entity('napcat_protocol_profile')
|
||||
@Index('idx_napcat_protocol_profile_account', ['accountId'])
|
||||
@Index('idx_napcat_protocol_profile_container', ['containerId'])
|
||||
export class NapcatProtocolProfile {
|
||||
@PrimaryColumn({ type: 'bigint' })
|
||||
id: string;
|
||||
|
||||
@Column({ name: 'account_id', type: 'bigint' })
|
||||
accountId: string;
|
||||
|
||||
@Column({
|
||||
default: null,
|
||||
name: 'container_id',
|
||||
nullable: true,
|
||||
type: 'bigint',
|
||||
})
|
||||
containerId: null | string;
|
||||
|
||||
@Column({ length: 64, name: 'profile_version' })
|
||||
profileVersion: string;
|
||||
|
||||
@Column({ length: 64, name: 'packet_backend' })
|
||||
packetBackend: string;
|
||||
|
||||
@Column({ default: '', length: 255, name: 'packet_server' })
|
||||
packetServer: string;
|
||||
|
||||
@Column({ default: 1, name: 'o3_hook_mode', type: 'int' })
|
||||
o3HookMode: number;
|
||||
|
||||
@Column({ default: false, name: 'o3_hook_gray_enabled' })
|
||||
o3HookGrayEnabled: boolean;
|
||||
|
||||
@Column({
|
||||
default: null,
|
||||
length: 128,
|
||||
name: 'onebot_config_hash',
|
||||
nullable: true,
|
||||
})
|
||||
onebotConfigHash: null | string;
|
||||
|
||||
@Column({
|
||||
default: null,
|
||||
name: 'onebot_config_json',
|
||||
nullable: true,
|
||||
type: 'json',
|
||||
})
|
||||
onebotConfigJson: null | Record<string, unknown>;
|
||||
|
||||
@Column({
|
||||
default: null,
|
||||
length: 128,
|
||||
name: 'napcat_config_hash',
|
||||
nullable: true,
|
||||
})
|
||||
napcatConfigHash: null | string;
|
||||
|
||||
@Column({
|
||||
default: null,
|
||||
name: 'napcat_config_json',
|
||||
nullable: true,
|
||||
type: 'json',
|
||||
})
|
||||
napcatConfigJson: null | Record<string, unknown>;
|
||||
|
||||
@Column({ length: 32, name: 'profile_status' })
|
||||
profileStatus: NapcatProtocolProfileStatus;
|
||||
|
||||
@Column({
|
||||
default: null,
|
||||
name: 'last_check_evidence',
|
||||
nullable: true,
|
||||
type: 'json',
|
||||
})
|
||||
lastCheckEvidence: null | Record<string, unknown>;
|
||||
|
||||
@KtDateTimeColumn({
|
||||
default: null,
|
||||
name: 'last_checked_at',
|
||||
nullable: true,
|
||||
})
|
||||
lastCheckedAt: KtDateTime | null;
|
||||
|
||||
@KtCreateDateColumn({ name: 'create_time' })
|
||||
createTime: KtDateTime;
|
||||
|
||||
@KtUpdateDateColumn({ name: 'update_time' })
|
||||
updateTime: KtDateTime;
|
||||
|
||||
/**
|
||||
* Assigns a stable Snowflake id before persisting protocol-profile evidence.
|
||||
*/
|
||||
@BeforeInsert()
|
||||
createId() {
|
||||
ensureSnowflakeId(this);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,60 @@
|
||||
import { BeforeInsert, Column, Entity, Index, PrimaryColumn } from 'typeorm';
|
||||
import {
|
||||
ensureSnowflakeId,
|
||||
KtCreateDateColumn,
|
||||
KtDateTime,
|
||||
KtDateTimeColumn,
|
||||
KtUpdateDateColumn,
|
||||
} from '@/common';
|
||||
|
||||
export type NapcatRiskModeValue = 'cooldown' | 'manual_only' | 'normal';
|
||||
|
||||
@Entity('napcat_risk_mode')
|
||||
@Index('uk_napcat_risk_mode_account', ['accountId'], { unique: true })
|
||||
@Index('idx_napcat_risk_mode_mode', ['riskMode'])
|
||||
export class NapcatRiskMode {
|
||||
@PrimaryColumn({ type: 'bigint' })
|
||||
id: string;
|
||||
|
||||
@Column({ name: 'account_id', type: 'bigint' })
|
||||
accountId: string;
|
||||
|
||||
@Column({ length: 32, name: 'risk_mode' })
|
||||
riskMode: NapcatRiskModeValue;
|
||||
|
||||
@Column({ default: null, length: 255, nullable: true })
|
||||
reason: null | string;
|
||||
|
||||
@Column({
|
||||
default: null,
|
||||
length: 64,
|
||||
name: 'source_event',
|
||||
nullable: true,
|
||||
})
|
||||
sourceEvent: null | string;
|
||||
|
||||
@KtDateTimeColumn({ default: null, name: 'expires_at', nullable: true })
|
||||
expiresAt: KtDateTime | null;
|
||||
|
||||
@Column({
|
||||
default: null,
|
||||
name: 'last_evidence',
|
||||
nullable: true,
|
||||
type: 'json',
|
||||
})
|
||||
lastEvidence: null | Record<string, unknown>;
|
||||
|
||||
@KtCreateDateColumn({ name: 'create_time' })
|
||||
createTime: KtDateTime;
|
||||
|
||||
@KtUpdateDateColumn({ name: 'update_time' })
|
||||
updateTime: KtDateTime;
|
||||
|
||||
/**
|
||||
* Assigns a stable Snowflake id before persisting account risk-mode state.
|
||||
*/
|
||||
@BeforeInsert()
|
||||
createId() {
|
||||
ensureSnowflakeId(this);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,171 @@
|
||||
import { BeforeInsert, Column, Entity, Index, PrimaryColumn } from 'typeorm';
|
||||
import {
|
||||
ensureSnowflakeId,
|
||||
KtCreateDateColumn,
|
||||
KtDateTime,
|
||||
KtDateTimeColumn,
|
||||
KtUpdateDateColumn,
|
||||
} from '@/common';
|
||||
|
||||
export type NapcatRuntimeProfileStatus =
|
||||
| 'drifted'
|
||||
| 'failed'
|
||||
| 'pending'
|
||||
| 'synced';
|
||||
|
||||
@Entity('napcat_runtime_profile')
|
||||
@Index('idx_napcat_runtime_profile_account', ['accountId'])
|
||||
@Index('idx_napcat_runtime_profile_container', ['containerId'])
|
||||
export class NapcatRuntimeProfile {
|
||||
@PrimaryColumn({ type: 'bigint' })
|
||||
id: string;
|
||||
|
||||
@Column({ name: 'account_id', type: 'bigint' })
|
||||
accountId: string;
|
||||
|
||||
@Column({
|
||||
default: null,
|
||||
name: 'container_id',
|
||||
nullable: true,
|
||||
type: 'bigint',
|
||||
})
|
||||
containerId: null | string;
|
||||
|
||||
@Column({
|
||||
default: null,
|
||||
name: 'device_identity_id',
|
||||
nullable: true,
|
||||
type: 'bigint',
|
||||
})
|
||||
deviceIdentityId: null | string;
|
||||
|
||||
@Column({ length: 64, name: 'profile_version' })
|
||||
profileVersion: string;
|
||||
|
||||
@Column({ length: 255, name: 'image_ref' })
|
||||
imageRef: string;
|
||||
|
||||
@Column({ default: null, length: 255, name: 'image_digest', nullable: true })
|
||||
imageDigest: null | string;
|
||||
|
||||
@Column({
|
||||
default: null,
|
||||
length: 255,
|
||||
name: 'base_image_digest',
|
||||
nullable: true,
|
||||
})
|
||||
baseImageDigest: null | string;
|
||||
|
||||
@Column({
|
||||
default: null,
|
||||
length: 64,
|
||||
name: 'desktop_profile_version',
|
||||
nullable: true,
|
||||
})
|
||||
desktopProfileVersion: null | string;
|
||||
|
||||
@Column({ default: false, name: 'locale_available' })
|
||||
localeAvailable: boolean;
|
||||
|
||||
@Column({
|
||||
default: null,
|
||||
name: 'fontconfig_evidence',
|
||||
nullable: true,
|
||||
type: 'json',
|
||||
})
|
||||
fontconfigEvidence: null | Record<string, unknown>;
|
||||
|
||||
@Column({
|
||||
default: null,
|
||||
name: 'timezone_evidence',
|
||||
nullable: true,
|
||||
type: 'json',
|
||||
})
|
||||
timezoneEvidence: null | Record<string, unknown>;
|
||||
|
||||
@Column({ default: null, name: 'runtime_uid', nullable: true, type: 'int' })
|
||||
runtimeUid: null | number;
|
||||
|
||||
@Column({ default: null, name: 'runtime_gid', nullable: true, type: 'int' })
|
||||
runtimeGid: null | number;
|
||||
|
||||
@Column({ default: null, length: 32, name: 'shm_size', nullable: true })
|
||||
shmSize: null | string;
|
||||
|
||||
@Column({ default: null, length: 64, nullable: true })
|
||||
locale: null | string;
|
||||
|
||||
@Column({
|
||||
default: null,
|
||||
length: 255,
|
||||
name: 'xdg_config_home',
|
||||
nullable: true,
|
||||
})
|
||||
xdgConfigHome: null | string;
|
||||
|
||||
@Column({
|
||||
default: null,
|
||||
length: 255,
|
||||
name: 'xdg_cache_home',
|
||||
nullable: true,
|
||||
})
|
||||
xdgCacheHome: null | string;
|
||||
|
||||
@Column({
|
||||
default: null,
|
||||
length: 255,
|
||||
name: 'xdg_data_home',
|
||||
nullable: true,
|
||||
})
|
||||
xdgDataHome: null | string;
|
||||
|
||||
@Column({ default: true, name: 'persist_cache' })
|
||||
persistCache: boolean;
|
||||
|
||||
@Column({ default: true, name: 'persist_local_share' })
|
||||
persistLocalShare: boolean;
|
||||
|
||||
@Column({ default: true, name: 'persist_logs' })
|
||||
persistLogs: boolean;
|
||||
|
||||
@Column({ length: 64, name: 'hostname_strategy' })
|
||||
hostnameStrategy: string;
|
||||
|
||||
@Column({ length: 64, name: 'mac_strategy' })
|
||||
macStrategy: string;
|
||||
|
||||
@Column({ default: false, name: 'migrate_device_identity' })
|
||||
migrateDeviceIdentity: boolean;
|
||||
|
||||
@Column({ length: 32, name: 'profile_status' })
|
||||
profileStatus: NapcatRuntimeProfileStatus;
|
||||
|
||||
@Column({
|
||||
default: null,
|
||||
name: 'last_check_evidence',
|
||||
nullable: true,
|
||||
type: 'json',
|
||||
})
|
||||
lastCheckEvidence: null | Record<string, unknown>;
|
||||
|
||||
@KtDateTimeColumn({
|
||||
default: null,
|
||||
name: 'last_checked_at',
|
||||
nullable: true,
|
||||
})
|
||||
lastCheckedAt: KtDateTime | null;
|
||||
|
||||
@KtCreateDateColumn({ name: 'create_time' })
|
||||
createTime: KtDateTime;
|
||||
|
||||
@KtUpdateDateColumn({ name: 'update_time' })
|
||||
updateTime: KtDateTime;
|
||||
|
||||
/**
|
||||
* Assigns a stable Snowflake id before persisting runtime-profile evidence.
|
||||
*/
|
||||
@BeforeInsert()
|
||||
createId() {
|
||||
ensureSnowflakeId(this);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,120 @@
|
||||
import { BeforeInsert, Column, Entity, Index, PrimaryColumn } from 'typeorm';
|
||||
import {
|
||||
ensureSnowflakeId,
|
||||
KtCreateDateColumn,
|
||||
KtDateTime,
|
||||
KtDateTimeColumn,
|
||||
KtUpdateDateColumn,
|
||||
} from '@/common';
|
||||
|
||||
export type NapcatAutoCapabilityStage =
|
||||
| 'automatic'
|
||||
| 'image_commands'
|
||||
| 'manual_only'
|
||||
| 'text_commands';
|
||||
|
||||
@Entity('napcat_session_behavior_profile')
|
||||
@Index('idx_napcat_session_behavior_profile_account', ['accountId'])
|
||||
export class NapcatSessionBehaviorProfile {
|
||||
@PrimaryColumn({ type: 'bigint' })
|
||||
id: string;
|
||||
|
||||
@Column({ name: 'account_id', type: 'bigint' })
|
||||
accountId: string;
|
||||
|
||||
@Column({ length: 64, name: 'profile_version' })
|
||||
profileVersion: string;
|
||||
|
||||
@Column({ default: true })
|
||||
enabled: boolean;
|
||||
|
||||
@KtDateTimeColumn({
|
||||
default: null,
|
||||
name: 'cold_start_until',
|
||||
nullable: true,
|
||||
})
|
||||
coldStartUntil: KtDateTime | null;
|
||||
|
||||
@Column({ default: false, name: 'housekeeping_enabled' })
|
||||
housekeepingEnabled: boolean;
|
||||
|
||||
@Column({
|
||||
default: null,
|
||||
name: 'housekeeping_interval_ms',
|
||||
nullable: true,
|
||||
type: 'int',
|
||||
})
|
||||
housekeepingIntervalMs: null | number;
|
||||
|
||||
@KtDateTimeColumn({
|
||||
default: null,
|
||||
name: 'next_housekeeping_at',
|
||||
nullable: true,
|
||||
})
|
||||
nextHousekeepingAt: KtDateTime | null;
|
||||
|
||||
@KtDateTimeColumn({
|
||||
default: null,
|
||||
name: 'last_housekeeping_at',
|
||||
nullable: true,
|
||||
})
|
||||
lastHousekeepingAt: KtDateTime | null;
|
||||
|
||||
@Column({
|
||||
default: null,
|
||||
name: 'last_housekeeping_result',
|
||||
nullable: true,
|
||||
type: 'json',
|
||||
})
|
||||
lastHousekeepingResult: null | Record<string, unknown>;
|
||||
|
||||
@Column({ default: false, name: 'presence_enabled' })
|
||||
presenceEnabled: boolean;
|
||||
|
||||
@Column({
|
||||
default: null,
|
||||
length: 64,
|
||||
name: 'presence_strategy',
|
||||
nullable: true,
|
||||
})
|
||||
presenceStrategy: null | string;
|
||||
|
||||
@KtDateTimeColumn({
|
||||
default: null,
|
||||
name: 'last_presence_event_at',
|
||||
nullable: true,
|
||||
})
|
||||
lastPresenceEventAt: KtDateTime | null;
|
||||
|
||||
@KtDateTimeColumn({
|
||||
default: null,
|
||||
name: 'next_presence_event_at',
|
||||
nullable: true,
|
||||
})
|
||||
nextPresenceEventAt: KtDateTime | null;
|
||||
|
||||
@Column({ length: 32, name: 'auto_capability_stage' })
|
||||
autoCapabilityStage: NapcatAutoCapabilityStage;
|
||||
|
||||
@Column({
|
||||
default: null,
|
||||
name: 'last_behavior_evidence',
|
||||
nullable: true,
|
||||
type: 'json',
|
||||
})
|
||||
lastBehaviorEvidence: null | Record<string, unknown>;
|
||||
|
||||
@KtCreateDateColumn({ name: 'create_time' })
|
||||
createTime: KtDateTime;
|
||||
|
||||
@KtUpdateDateColumn({ name: 'update_time' })
|
||||
updateTime: KtDateTime;
|
||||
|
||||
/**
|
||||
* Assigns a stable Snowflake id before persisting session-behavior profile state.
|
||||
*/
|
||||
@BeforeInsert()
|
||||
createId() {
|
||||
ensureSnowflakeId(this);
|
||||
}
|
||||
}
|
||||
@ -1,7 +1,8 @@
|
||||
# QQBot NapCat Schema
|
||||
|
||||
NapCat owns container runtime, account binding, device identity, login session,
|
||||
challenge, and cleanup persistence through the v3 `napcat_*` tables.
|
||||
challenge, cleanup, runtime/profile evidence, login-event, and risk-mode
|
||||
persistence through the v3 `napcat_*` tables.
|
||||
|
||||
- `napcat_container`
|
||||
- `napcat_device_identity`
|
||||
@ -9,6 +10,11 @@ challenge, and cleanup persistence through the v3 `napcat_*` tables.
|
||||
- `napcat_login_session`
|
||||
- `napcat_login_challenge`
|
||||
- `napcat_runtime_cleanup`
|
||||
- `napcat_runtime_profile`
|
||||
- `napcat_protocol_profile`
|
||||
- `napcat_session_behavior_profile`
|
||||
- `napcat_login_event`
|
||||
- `napcat_risk_mode`
|
||||
|
||||
Verification SQL:
|
||||
|
||||
@ -16,4 +22,9 @@ Verification SQL:
|
||||
SELECT COUNT(*) FROM napcat_container WHERE is_deleted = 0;
|
||||
SELECT COUNT(*) FROM napcat_device_identity;
|
||||
SELECT COUNT(*) FROM napcat_account_binding WHERE is_deleted = 0;
|
||||
SELECT COUNT(*) FROM napcat_runtime_profile;
|
||||
SELECT COUNT(*) FROM napcat_protocol_profile;
|
||||
SELECT COUNT(*) FROM napcat_session_behavior_profile;
|
||||
SELECT COUNT(*) FROM napcat_login_event;
|
||||
SELECT COUNT(*) FROM napcat_risk_mode;
|
||||
```
|
||||
|
||||
90
test/modules/qqbot/napcat/runtime-protocol-profile.spec.ts
Normal file
90
test/modules/qqbot/napcat/runtime-protocol-profile.spec.ts
Normal file
@ -0,0 +1,90 @@
|
||||
import { getMetadataArgsStorage } from 'typeorm';
|
||||
import {
|
||||
NapcatLoginEvent,
|
||||
NapcatProtocolProfile,
|
||||
NapcatRiskMode,
|
||||
NapcatRuntimeProfile,
|
||||
NapcatSessionBehaviorProfile,
|
||||
NAPCAT_RUNTIME_DOMAIN_CONTRACT,
|
||||
NAPCAT_RUNTIME_ENTITIES,
|
||||
} from '../../../../src/modules/qqbot/napcat';
|
||||
import { readRefactorV3SqlSchema } from '../../../helpers/sql-schema.helper';
|
||||
|
||||
type EntityClass = new (...args: never[]) => unknown;
|
||||
|
||||
/**
|
||||
* Reads a TypeORM entity table name from decorator metadata.
|
||||
* @param entity - Entity class selected by the test to compare against SQL schema ownership.
|
||||
*/
|
||||
const getEntityTableName = (entity: EntityClass) =>
|
||||
getMetadataArgsStorage().tables.find((table) => table.target === entity)
|
||||
?.name;
|
||||
|
||||
/**
|
||||
* Reads entity column names as they are persisted in MySQL.
|
||||
* @param entity - Entity class whose decorator column metadata must match refactor-v3 SQL.
|
||||
*/
|
||||
const getEntityColumnNames = (entity: EntityClass) =>
|
||||
getMetadataArgsStorage()
|
||||
.columns.filter((column) => column.target === entity)
|
||||
.map((column) => `${column.options.name || column.propertyName}`);
|
||||
|
||||
describe('NapCat runtime and protocol profile persistence', () => {
|
||||
const schema = readRefactorV3SqlSchema();
|
||||
|
||||
it('declares runtime profile tables as NapCat-owned domain tables', () => {
|
||||
expect(NAPCAT_RUNTIME_DOMAIN_CONTRACT.tables).toEqual(
|
||||
expect.arrayContaining([
|
||||
'napcat_runtime_profile',
|
||||
'napcat_protocol_profile',
|
||||
'napcat_session_behavior_profile',
|
||||
'napcat_login_event',
|
||||
'napcat_risk_mode',
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
it.each([
|
||||
[NapcatRuntimeProfile, 'napcat_runtime_profile'],
|
||||
[NapcatProtocolProfile, 'napcat_protocol_profile'],
|
||||
[NapcatSessionBehaviorProfile, 'napcat_session_behavior_profile'],
|
||||
[NapcatLoginEvent, 'napcat_login_event'],
|
||||
[NapcatRiskMode, 'napcat_risk_mode'],
|
||||
])('maps %p to %s in the v3 SQL schema', (entity, tableName) => {
|
||||
expect(NAPCAT_RUNTIME_ENTITIES).toContain(entity);
|
||||
expect(getEntityTableName(entity)).toBe(tableName);
|
||||
schema.expectTableColumns(tableName, getEntityColumnNames(entity));
|
||||
});
|
||||
|
||||
it('keeps login-event fields separate from send budget fields', () => {
|
||||
const loginEventColumns = getEntityColumnNames(NapcatLoginEvent);
|
||||
expect(loginEventColumns).toEqual(
|
||||
expect.arrayContaining([
|
||||
'account_id',
|
||||
'container_id',
|
||||
'event_kind',
|
||||
'event_source',
|
||||
'event_status',
|
||||
'evidence',
|
||||
]),
|
||||
);
|
||||
expect(loginEventColumns.join(' ')).not.toMatch(
|
||||
/hour|daily|quota|budget|limit_count/i,
|
||||
);
|
||||
});
|
||||
|
||||
it('keeps risk mode separate from account send budgets', () => {
|
||||
const riskColumns = getEntityColumnNames(NapcatRiskMode);
|
||||
expect(riskColumns).toEqual(
|
||||
expect.arrayContaining([
|
||||
'account_id',
|
||||
'risk_mode',
|
||||
'reason',
|
||||
'source_event',
|
||||
'expires_at',
|
||||
'last_evidence',
|
||||
]),
|
||||
);
|
||||
expect(riskColumns.join(' ')).not.toMatch(/daily|hour|budget|quota/i);
|
||||
});
|
||||
});
|
||||
Loading…
Reference in New Issue
Block a user