feat: 完成鸽达达后台安全与审计改造
This commit is contained in:
@@ -0,0 +1,350 @@
|
||||
-- Admin Server schema changes for the GedaDa management-console adaptation.
|
||||
-- Target: MySQL 8.0+, control database `hotgo`.
|
||||
-- Scope: control-plane configuration only. Do not create tenant business data here.
|
||||
-- Review before execution. DDL statements auto-commit in MySQL.
|
||||
|
||||
SET NAMES utf8mb4;
|
||||
USE `hotgo`;
|
||||
|
||||
-- ============================================================
|
||||
-- 1. User policy groups.
|
||||
-- status: 1=enabled, 2=disabled.
|
||||
-- policy_json action values: allow, deny, challenge, audit_only.
|
||||
-- Friend entry values: search, profile_card, group_member, qrcode,
|
||||
-- invite_auto_add, recommendation.
|
||||
-- ============================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `im_user_policy_groups` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '0 means global default',
|
||||
`group_code` varchar(64) NOT NULL COMMENT 'Stable policy group code',
|
||||
`group_name` varchar(128) NOT NULL COMMENT 'Policy group name',
|
||||
`priority` int NOT NULL DEFAULT 0 COMMENT 'Higher value has higher priority',
|
||||
`status` tinyint NOT NULL DEFAULT 1 COMMENT '1 enabled, 2 disabled',
|
||||
`policy_json` json NOT NULL COMMENT 'Login, friend, message, group and file policies',
|
||||
`ip_whitelist` json DEFAULT NULL COMMENT 'IPv4/IPv6 or CIDR list',
|
||||
`ip_blacklist` json DEFAULT NULL COMMENT 'IPv4/IPv6 or CIDR list',
|
||||
`version` int unsigned NOT NULL DEFAULT 1 COMMENT 'Optimistic-lock/config version',
|
||||
`remark` varchar(500) NOT NULL DEFAULT '',
|
||||
`created_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`created_by` bigint NOT NULL DEFAULT 0,
|
||||
`updated_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||||
`updated_by` bigint NOT NULL DEFAULT 0,
|
||||
`deleted_at` datetime(3) DEFAULT NULL,
|
||||
`deleted_by` bigint DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_im_user_policy_group_code` (`tenant_id`, `group_code`),
|
||||
KEY `idx_im_user_policy_group_list` (`tenant_id`, `status`, `priority`, `id`),
|
||||
KEY `idx_im_user_policy_group_deleted` (`deleted_at`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='IM user policy groups';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `im_user_policy_group_members` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`tenant_id` bigint NOT NULL DEFAULT 0,
|
||||
`group_id` bigint NOT NULL,
|
||||
`user_id` bigint NOT NULL COMMENT 'User ID in the tenant database',
|
||||
`status` tinyint NOT NULL DEFAULT 1 COMMENT '1 enabled, 2 disabled',
|
||||
`valid_from` datetime(3) DEFAULT NULL,
|
||||
`valid_until` datetime(3) DEFAULT NULL,
|
||||
`remark` varchar(500) NOT NULL DEFAULT '',
|
||||
`created_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`created_by` bigint NOT NULL DEFAULT 0,
|
||||
`updated_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||||
`updated_by` bigint NOT NULL DEFAULT 0,
|
||||
`deleted_at` datetime(3) DEFAULT NULL,
|
||||
`deleted_by` bigint DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_im_user_policy_group_member` (`group_id`, `user_id`),
|
||||
KEY `idx_im_user_policy_member_user` (`tenant_id`, `user_id`, `status`),
|
||||
KEY `idx_im_user_policy_member_validity` (`status`, `valid_from`, `valid_until`),
|
||||
KEY `idx_im_user_policy_member_deleted` (`deleted_at`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='IM user-to-policy-group assignments';
|
||||
|
||||
-- ============================================================
|
||||
-- 2. Sensitive word dictionary.
|
||||
-- match_type: exact, contains, regex, pinyin.
|
||||
-- action: replace, block, audit_only, manual_review.
|
||||
-- risk_level: low, medium, high.
|
||||
-- scene_json values include private_message, group_message, user_profile,
|
||||
-- group_profile, feedback, report.
|
||||
-- ============================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `im_sensitive_word_groups` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '0 means global dictionary',
|
||||
`group_code` varchar(64) NOT NULL,
|
||||
`group_name` varchar(128) NOT NULL,
|
||||
`priority` int NOT NULL DEFAULT 0,
|
||||
`status` tinyint NOT NULL DEFAULT 1 COMMENT '1 enabled, 2 disabled',
|
||||
`remark` varchar(500) NOT NULL DEFAULT '',
|
||||
`created_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`created_by` bigint NOT NULL DEFAULT 0,
|
||||
`updated_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||||
`updated_by` bigint NOT NULL DEFAULT 0,
|
||||
`deleted_at` datetime(3) DEFAULT NULL,
|
||||
`deleted_by` bigint DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_im_sensitive_word_group_code` (`tenant_id`, `group_code`),
|
||||
KEY `idx_im_sensitive_word_group_list` (`tenant_id`, `status`, `priority`, `id`),
|
||||
KEY `idx_im_sensitive_word_group_deleted` (`deleted_at`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='IM sensitive word groups';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `im_sensitive_words` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '0 means global dictionary',
|
||||
`group_id` bigint NOT NULL,
|
||||
`word` varchar(255) NOT NULL,
|
||||
`word_hash` char(64) NOT NULL COMMENT 'SHA-256 of normalized word',
|
||||
`match_type` varchar(24) NOT NULL DEFAULT 'contains',
|
||||
`scene_json` json NOT NULL COMMENT 'Enabled scenes',
|
||||
`action` varchar(24) NOT NULL DEFAULT 'block',
|
||||
`replacement` varchar(255) NOT NULL DEFAULT '',
|
||||
`risk_level` varchar(16) NOT NULL DEFAULT 'medium',
|
||||
`priority` int NOT NULL DEFAULT 0,
|
||||
`status` tinyint NOT NULL DEFAULT 1 COMMENT '1 enabled, 2 disabled',
|
||||
`remark` varchar(500) NOT NULL DEFAULT '',
|
||||
`created_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`created_by` bigint NOT NULL DEFAULT 0,
|
||||
`updated_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||||
`updated_by` bigint NOT NULL DEFAULT 0,
|
||||
`deleted_at` datetime(3) DEFAULT NULL,
|
||||
`deleted_by` bigint DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_im_sensitive_word_normalized` (`tenant_id`, `group_id`, `word_hash`),
|
||||
KEY `idx_im_sensitive_word_list` (`tenant_id`, `status`, `risk_level`, `id`),
|
||||
KEY `idx_im_sensitive_word_group` (`group_id`, `status`, `priority`),
|
||||
KEY `idx_im_sensitive_word_deleted` (`deleted_at`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='IM sensitive words';
|
||||
|
||||
-- ============================================================
|
||||
-- 3. Versioned security policy.
|
||||
-- publish_status: draft, published, archived, failed.
|
||||
-- The latest published row is the source of truth. The summary switches in
|
||||
-- im_tenant_config are updated in the same application transaction on publish.
|
||||
-- ============================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `im_security_policy` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '0 means global fallback policy',
|
||||
`policy_version` int unsigned NOT NULL,
|
||||
`policy_name` varchar(128) NOT NULL DEFAULT '',
|
||||
`publish_status` varchar(16) NOT NULL DEFAULT 'draft',
|
||||
`password_policy` json DEFAULT NULL,
|
||||
`login_rate_limit` json DEFAULT NULL,
|
||||
`sensitive_word_policy` json DEFAULT NULL,
|
||||
`file_review_policy` json DEFAULT NULL,
|
||||
`realname_policy` json DEFAULT NULL,
|
||||
`risk_challenge_policy` json DEFAULT NULL,
|
||||
`global_friend_enabled` tinyint(1) NOT NULL DEFAULT 1 COMMENT 'Backend master switch for every add-friend entry',
|
||||
`friend_disabled_policy` json DEFAULT NULL COMMENT 'Profile masking, QR and group-member restrictions',
|
||||
`message_curfew_enabled` tinyint(1) NOT NULL DEFAULT 0 COMMENT 'Backend master switch for message curfew',
|
||||
`remark` varchar(500) NOT NULL DEFAULT '',
|
||||
`published_at` datetime(3) DEFAULT NULL,
|
||||
`published_by` bigint DEFAULT NULL,
|
||||
`created_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`created_by` bigint NOT NULL DEFAULT 0,
|
||||
`updated_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||||
`updated_by` bigint NOT NULL DEFAULT 0,
|
||||
`deleted_at` datetime(3) DEFAULT NULL,
|
||||
`deleted_by` bigint DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_im_security_policy_version` (`tenant_id`, `policy_version`),
|
||||
KEY `idx_im_security_policy_publish` (`tenant_id`, `publish_status`, `published_at`),
|
||||
KEY `idx_im_security_policy_deleted` (`deleted_at`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='Versioned IM security policies';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `im_message_curfew_rules` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`tenant_id` bigint NOT NULL DEFAULT 0,
|
||||
`policy_id` bigint NOT NULL,
|
||||
`rule_code` varchar(64) NOT NULL,
|
||||
`rule_name` varchar(128) NOT NULL,
|
||||
`date_type` varchar(16) NOT NULL DEFAULT 'daily' COMMENT 'daily, weekday, weekend, custom',
|
||||
`custom_dates` json DEFAULT NULL COMMENT 'YYYY-MM-DD values for custom date type',
|
||||
`weekdays` json DEFAULT NULL COMMENT 'ISO weekdays 1 through 7',
|
||||
`timezone` varchar(64) NOT NULL DEFAULT 'Asia/Shanghai',
|
||||
`start_time` time NOT NULL,
|
||||
`end_time` time NOT NULL COMMENT 'Less than start_time means crossing midnight',
|
||||
`scope` varchar(24) NOT NULL DEFAULT 'private_message' COMMENT 'private_message or group_message',
|
||||
`whitelist_user_ids` json DEFAULT NULL,
|
||||
`whitelist_group_ids` json DEFAULT NULL,
|
||||
`message` varchar(255) NOT NULL DEFAULT '当前时段暂不能发送消息',
|
||||
`status` tinyint NOT NULL DEFAULT 1 COMMENT '1 enabled, 2 disabled',
|
||||
`sort` int NOT NULL DEFAULT 0,
|
||||
`created_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`created_by` bigint NOT NULL DEFAULT 0,
|
||||
`updated_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||||
`updated_by` bigint NOT NULL DEFAULT 0,
|
||||
`deleted_at` datetime(3) DEFAULT NULL,
|
||||
`deleted_by` bigint DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_im_message_curfew_rule` (`policy_id`, `rule_code`),
|
||||
KEY `idx_im_message_curfew_policy` (`tenant_id`, `policy_id`, `status`, `sort`),
|
||||
KEY `idx_im_message_curfew_deleted` (`deleted_at`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='Message curfew rules';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `im_security_ip_rules` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`tenant_id` bigint NOT NULL DEFAULT 0,
|
||||
`rule_type` varchar(16) NOT NULL COMMENT 'whitelist or blacklist',
|
||||
`ip_cidr` varchar(64) NOT NULL COMMENT 'IPv4, IPv6 or CIDR',
|
||||
`action` varchar(24) NOT NULL DEFAULT 'block' COMMENT 'allow, challenge, block, audit_only',
|
||||
`scene_json` json NOT NULL COMMENT 'login, register, message, upload and other scenes',
|
||||
`risk_level` varchar(16) NOT NULL DEFAULT 'high',
|
||||
`status` tinyint NOT NULL DEFAULT 1 COMMENT '1 enabled, 2 disabled',
|
||||
`expires_at` datetime(3) DEFAULT NULL,
|
||||
`remark` varchar(500) NOT NULL DEFAULT '',
|
||||
`created_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`created_by` bigint NOT NULL DEFAULT 0,
|
||||
`updated_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||||
`updated_by` bigint NOT NULL DEFAULT 0,
|
||||
`deleted_at` datetime(3) DEFAULT NULL,
|
||||
`deleted_by` bigint DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_im_security_ip_rule` (`tenant_id`, `rule_type`, `ip_cidr`),
|
||||
KEY `idx_im_security_ip_rule_list` (`tenant_id`, `status`, `rule_type`, `id`),
|
||||
KEY `idx_im_security_ip_rule_expiry` (`status`, `expires_at`),
|
||||
KEY `idx_im_security_ip_rule_deleted` (`deleted_at`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='IM IP whitelist and blacklist rules';
|
||||
|
||||
-- ============================================================
|
||||
-- 4. Lightweight published-policy summary in im_tenant_config.
|
||||
-- These columns are read optimizations, not the policy source of truth.
|
||||
-- The conditional ALTER blocks are safe to rerun on MySQL 8.0.
|
||||
-- ============================================================
|
||||
|
||||
SET @im_sql = IF(
|
||||
(SELECT COUNT(*) FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_tenant_config'
|
||||
AND COLUMN_NAME = 'default_policy_group_id') = 0,
|
||||
'ALTER TABLE `im_tenant_config` ADD COLUMN `default_policy_group_id` BIGINT DEFAULT NULL COMMENT ''Default user policy group'' AFTER `max_group_members`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE im_stmt FROM @im_sql;
|
||||
EXECUTE im_stmt;
|
||||
DEALLOCATE PREPARE im_stmt;
|
||||
|
||||
SET @im_sql = IF(
|
||||
(SELECT COUNT(*) FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_tenant_config'
|
||||
AND COLUMN_NAME = 'security_policy_version') = 0,
|
||||
'ALTER TABLE `im_tenant_config` ADD COLUMN `security_policy_version` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT ''Published security policy version'' AFTER `default_policy_group_id`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE im_stmt FROM @im_sql;
|
||||
EXECUTE im_stmt;
|
||||
DEALLOCATE PREPARE im_stmt;
|
||||
|
||||
SET @im_sql = IF(
|
||||
(SELECT COUNT(*) FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_tenant_config'
|
||||
AND COLUMN_NAME = 'global_friend_enabled') = 0,
|
||||
'ALTER TABLE `im_tenant_config` ADD COLUMN `global_friend_enabled` TINYINT(1) NOT NULL DEFAULT 1 COMMENT ''Published global friend switch'' AFTER `security_policy_version`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE im_stmt FROM @im_sql;
|
||||
EXECUTE im_stmt;
|
||||
DEALLOCATE PREPARE im_stmt;
|
||||
|
||||
SET @im_sql = IF(
|
||||
(SELECT COUNT(*) FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_tenant_config'
|
||||
AND COLUMN_NAME = 'message_curfew_enabled') = 0,
|
||||
'ALTER TABLE `im_tenant_config` ADD COLUMN `message_curfew_enabled` TINYINT(1) NOT NULL DEFAULT 0 COMMENT ''Published message curfew switch'' AFTER `global_friend_enabled`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE im_stmt FROM @im_sql;
|
||||
EXECUTE im_stmt;
|
||||
DEALLOCATE PREPARE im_stmt;
|
||||
|
||||
-- ============================================================
|
||||
-- 5. Invitation-code compatibility for the 6-digit target format.
|
||||
-- Existing 5-letter codes remain stored and valid during the application
|
||||
-- rollout. New code generation must switch to exactly six ASCII digits, while
|
||||
-- Admin and IM Server temporarily accept both legacy 5-letter and new 6-digit
|
||||
-- values. max_uses=0 preserves the current unlimited-use behavior.
|
||||
-- ============================================================
|
||||
|
||||
SET @im_sql = IF(
|
||||
(SELECT CHARACTER_MAXIMUM_LENGTH FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_registration_invite_codes'
|
||||
AND COLUMN_NAME = 'code') = 5,
|
||||
'ALTER TABLE `im_registration_invite_codes` MODIFY COLUMN `code` CHAR(6) CHARACTER SET ascii COLLATE ascii_bin NOT NULL',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE im_stmt FROM @im_sql;
|
||||
EXECUTE im_stmt;
|
||||
DEALLOCATE PREPARE im_stmt;
|
||||
|
||||
SET @im_sql = IF(
|
||||
(SELECT COUNT(*) FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_registration_invite_codes'
|
||||
AND COLUMN_NAME = 'max_uses') = 0,
|
||||
'ALTER TABLE `im_registration_invite_codes` ADD COLUMN `max_uses` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT ''0 means unlimited'' AFTER `expires_at`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE im_stmt FROM @im_sql;
|
||||
EXECUTE im_stmt;
|
||||
DEALLOCATE PREPARE im_stmt;
|
||||
|
||||
SET @im_sql = IF(
|
||||
(SELECT COUNT(*) FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_registration_invite_batches'
|
||||
AND COLUMN_NAME = 'max_uses_per_code') = 0,
|
||||
'ALTER TABLE `im_registration_invite_batches` ADD COLUMN `max_uses_per_code` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT ''0 means unlimited'' AFTER `quantity`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE im_stmt FROM @im_sql;
|
||||
EXECUTE im_stmt;
|
||||
DEALLOCATE PREPARE im_stmt;
|
||||
|
||||
-- ============================================================
|
||||
-- 6. Safe global fallback policy.
|
||||
-- Tenant-specific published policies override tenant_id=0.
|
||||
-- This keeps existing behavior permissive until an administrator publishes
|
||||
-- the first tenant policy.
|
||||
-- ============================================================
|
||||
|
||||
INSERT INTO `im_security_policy` (
|
||||
`tenant_id`, `policy_version`, `policy_name`, `publish_status`,
|
||||
`global_friend_enabled`, `friend_disabled_policy`,
|
||||
`message_curfew_enabled`, `remark`, `published_at`, `published_by`,
|
||||
`created_by`, `updated_by`
|
||||
)
|
||||
SELECT
|
||||
0, 1, '全局默认安全策略', 'published',
|
||||
1,
|
||||
JSON_OBJECT(
|
||||
'hide_profile', TRUE,
|
||||
'block_qrcode', TRUE,
|
||||
'block_group_member_entry', TRUE,
|
||||
'blocked_entries', JSON_ARRAY(
|
||||
'search', 'profile_card', 'group_member',
|
||||
'qrcode', 'invite_auto_add', 'recommendation'
|
||||
),
|
||||
'message', '当前时段暂不允许添加好友'
|
||||
),
|
||||
0, '系统初始化:保持原有好友和消息行为', CURRENT_TIMESTAMP(3), 0, 0, 0
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1 FROM `im_security_policy`
|
||||
WHERE `tenant_id` = 0 AND `publish_status` = 'published' AND `deleted_at` IS NULL
|
||||
);
|
||||
|
||||
UPDATE `im_security_policy`
|
||||
SET `friend_disabled_policy` = JSON_SET(
|
||||
`friend_disabled_policy`,
|
||||
'$.blocked_entries',
|
||||
JSON_ARRAY('search', 'profile_card', 'group_member', 'qrcode', 'invite_auto_add', 'recommendation')
|
||||
)
|
||||
WHERE `tenant_id` = 0
|
||||
AND `publish_status` = 'published'
|
||||
AND `deleted_at` IS NULL
|
||||
AND JSON_CONTAINS_PATH(`friend_disabled_policy`, 'one', '$.blocked_entries') = 0;
|
||||
|
||||
-- Post-deployment checks (read-only):
|
||||
-- SELECT tenant_id, group_code, status, priority FROM im_user_policy_groups;
|
||||
-- SELECT tenant_id, policy_version, publish_status,
|
||||
-- global_friend_enabled, message_curfew_enabled
|
||||
-- FROM im_security_policy ORDER BY tenant_id, policy_version DESC;
|
||||
-- SELECT tenant_id, policy_id, rule_code, date_type, start_time, end_time, scope
|
||||
-- FROM im_message_curfew_rules WHERE deleted_at IS NULL;
|
||||
@@ -0,0 +1,417 @@
|
||||
-- IM Server schema changes for the GedaDa management-console adaptation.
|
||||
-- Target: MySQL 8.0+, one independent tenant business database.
|
||||
-- Default local tenant database: `tinode`.
|
||||
-- IMPORTANT: execute this migration once for every tenant database. Tenant
|
||||
-- business tables intentionally do not contain tenant_id.
|
||||
-- Review before execution. DDL statements auto-commit in MySQL.
|
||||
|
||||
SET NAMES utf8mb4;
|
||||
USE `tinode`;
|
||||
|
||||
-- ============================================================
|
||||
-- 1. Applied runtime policy snapshots.
|
||||
-- policy_type: security, user_policy_groups, sensitive_words.
|
||||
-- The application uses the latest row with apply_status=applied and also keeps
|
||||
-- an in-memory cache. This table makes restart and Kafka replay deterministic.
|
||||
-- ============================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `im_runtime_policy_snapshots` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`policy_type` varchar(32) NOT NULL,
|
||||
`policy_version` bigint unsigned NOT NULL,
|
||||
`payload` json NOT NULL,
|
||||
`payload_sha256` char(64) NOT NULL,
|
||||
`source_event_id` varchar(96) DEFAULT NULL COMMENT 'NULL for a manual bootstrap snapshot',
|
||||
`apply_status` varchar(16) NOT NULL DEFAULT 'applied' COMMENT 'applied or failed',
|
||||
`apply_error` varchar(1000) NOT NULL DEFAULT '',
|
||||
`applied_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`created_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_im_runtime_policy_version` (`policy_type`, `policy_version`),
|
||||
UNIQUE KEY `uk_im_runtime_policy_event` (`source_event_id`),
|
||||
KEY `idx_im_runtime_policy_latest` (`policy_type`, `apply_status`, `policy_version` DESC)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='Last applied Admin policy payloads';
|
||||
|
||||
-- ============================================================
|
||||
-- 2. User login history and login failure history.
|
||||
-- Never store plaintext passwords, verification codes, access tokens or refresh
|
||||
-- tokens. account_hash is SHA-256 of the normalized login account; account_mask
|
||||
-- is the value shown in Admin.
|
||||
-- login_type: password, sms_code, sso, qr_code, token_refresh.
|
||||
-- event_type: login, refresh, logout, kicked, session_expired.
|
||||
-- failure_type: password_error, code_error, account_not_found,
|
||||
-- account_disabled, rate_limited, risk_blocked.
|
||||
-- ============================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `im_user_login_logs` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`user_id` bigint NOT NULL,
|
||||
`username` varchar(64) NOT NULL DEFAULT '',
|
||||
`account_type` varchar(24) NOT NULL DEFAULT '',
|
||||
`account_hash` char(64) NOT NULL DEFAULT '',
|
||||
`account_mask` varchar(128) NOT NULL DEFAULT '',
|
||||
`login_type` varchar(24) NOT NULL,
|
||||
`event_type` varchar(24) NOT NULL DEFAULT 'login',
|
||||
`session_id` varchar(96) NOT NULL DEFAULT '' COMMENT 'Opaque session identifier, never an auth token',
|
||||
`device_id` varchar(255) NOT NULL DEFAULT '',
|
||||
`platform` varchar(32) NOT NULL DEFAULT 'unknown',
|
||||
`app_version` varchar(32) NOT NULL DEFAULT '',
|
||||
`ip` varchar(64) NOT NULL DEFAULT '',
|
||||
`region` varchar(128) NOT NULL DEFAULT '',
|
||||
`user_agent` varchar(512) NOT NULL DEFAULT '',
|
||||
`risk_level` varchar(16) NOT NULL DEFAULT 'low',
|
||||
`request_id` varchar(64) NOT NULL DEFAULT '',
|
||||
`login_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`logout_at` datetime(3) DEFAULT NULL,
|
||||
`metadata` json DEFAULT NULL,
|
||||
`created_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_im_user_login_user_time` (`user_id`, `login_at` DESC, `id` DESC),
|
||||
KEY `idx_im_user_login_ip_time` (`ip`, `login_at` DESC, `id` DESC),
|
||||
KEY `idx_im_user_login_device_time` (`device_id`(64), `login_at` DESC),
|
||||
KEY `idx_im_user_login_request` (`request_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='User login and session event history';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `im_login_failure_logs` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`user_id` bigint DEFAULT NULL COMMENT 'NULL when the account cannot be resolved',
|
||||
`account_type` varchar(24) NOT NULL DEFAULT '',
|
||||
`account_hash` char(64) NOT NULL DEFAULT '',
|
||||
`account_mask` varchar(128) NOT NULL DEFAULT '',
|
||||
`failure_type` varchar(32) NOT NULL,
|
||||
`reason` varchar(96) NOT NULL DEFAULT '' COMMENT 'Stable backend reason code',
|
||||
`detail` varchar(500) NOT NULL DEFAULT '' COMMENT 'Sanitized detail without credentials',
|
||||
`ip` varchar(64) NOT NULL DEFAULT '',
|
||||
`region` varchar(128) NOT NULL DEFAULT '',
|
||||
`device_id` varchar(255) NOT NULL DEFAULT '',
|
||||
`platform` varchar(32) NOT NULL DEFAULT 'unknown',
|
||||
`app_version` varchar(32) NOT NULL DEFAULT '',
|
||||
`user_agent` varchar(512) NOT NULL DEFAULT '',
|
||||
`risk_level` varchar(16) NOT NULL DEFAULT 'medium',
|
||||
`risk_action` varchar(24) NOT NULL DEFAULT 'audit_only',
|
||||
`request_id` varchar(64) NOT NULL DEFAULT '',
|
||||
`attempt_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`metadata` json DEFAULT NULL,
|
||||
`created_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_im_login_failure_account_time` (`account_hash`, `attempt_at` DESC, `id` DESC),
|
||||
KEY `idx_im_login_failure_user_time` (`user_id`, `attempt_at` DESC, `id` DESC),
|
||||
KEY `idx_im_login_failure_ip_time` (`ip`, `attempt_at` DESC, `id` DESC),
|
||||
KEY `idx_im_login_failure_type_time` (`failure_type`, `attempt_at` DESC, `id` DESC),
|
||||
KEY `idx_im_login_failure_request` (`request_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='Authentication failure and risk-block history';
|
||||
|
||||
-- ============================================================
|
||||
-- 3. Message administration actions.
|
||||
-- Messages remain in im_messages/im_group_messages. This table is an immutable
|
||||
-- action overlay so hide/delete can be audited and restored.
|
||||
-- conversation_type: private, group.
|
||||
-- action: hide, unhide, delete, restore.
|
||||
-- action_status: pending, success, failed, dead.
|
||||
-- ============================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `im_message_admin_actions` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`action_id` varchar(96) NOT NULL,
|
||||
`command_id` varchar(96) NOT NULL,
|
||||
`conversation_type` varchar(16) NOT NULL,
|
||||
`topic` varchar(64) NOT NULL,
|
||||
`seqid` bigint NOT NULL,
|
||||
`sender_user_id` bigint DEFAULT NULL,
|
||||
`action` varchar(16) NOT NULL,
|
||||
`action_status` varchar(16) NOT NULL DEFAULT 'pending',
|
||||
`reason` varchar(500) NOT NULL DEFAULT '',
|
||||
`actor_admin_id` bigint NOT NULL DEFAULT 0,
|
||||
`request_id` varchar(64) NOT NULL DEFAULT '',
|
||||
`before_snapshot` json DEFAULT NULL,
|
||||
`after_snapshot` json DEFAULT NULL,
|
||||
`failure_reason` varchar(96) NOT NULL DEFAULT '',
|
||||
`failure_message` varchar(1000) NOT NULL DEFAULT '',
|
||||
`processed_at` datetime(3) DEFAULT NULL,
|
||||
`created_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`updated_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_im_message_admin_action_id` (`action_id`),
|
||||
UNIQUE KEY `uk_im_message_admin_command` (`command_id`),
|
||||
KEY `idx_im_message_admin_target` (`conversation_type`, `topic`, `seqid`, `id` DESC),
|
||||
KEY `idx_im_message_admin_status` (`action_status`, `created_at`, `id`),
|
||||
KEY `idx_im_message_admin_actor` (`actor_admin_id`, `created_at` DESC)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='Auditable Admin message actions';
|
||||
|
||||
-- ============================================================
|
||||
-- 4. Sensitive-word hits and policy decisions.
|
||||
-- Store only a digest and a short masked excerpt. Do not duplicate complete
|
||||
-- private messages in log tables.
|
||||
-- policy decision reasons include FRIEND_DISABLED and MESSAGE_CURFEW_ACTIVE.
|
||||
-- ============================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `im_sensitive_word_hits` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`word_id` bigint NOT NULL DEFAULT 0 COMMENT 'Admin control-database word ID',
|
||||
`word_group_id` bigint NOT NULL DEFAULT 0,
|
||||
`word_snapshot` varchar(255) NOT NULL DEFAULT '' COMMENT 'Masked word snapshot',
|
||||
`scene` varchar(32) NOT NULL,
|
||||
`user_id` bigint NOT NULL DEFAULT 0,
|
||||
`target_user_id` bigint DEFAULT NULL,
|
||||
`topic` varchar(64) NOT NULL DEFAULT '',
|
||||
`seqid` bigint DEFAULT NULL,
|
||||
`content_sha256` char(64) NOT NULL DEFAULT '',
|
||||
`matched_excerpt` varchar(500) NOT NULL DEFAULT '',
|
||||
`match_type` varchar(24) NOT NULL DEFAULT 'contains',
|
||||
`action` varchar(24) NOT NULL,
|
||||
`risk_level` varchar(16) NOT NULL DEFAULT 'medium',
|
||||
`request_id` varchar(64) NOT NULL DEFAULT '',
|
||||
`metadata` json DEFAULT NULL,
|
||||
`created_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_im_sensitive_hit_scene_time` (`scene`, `created_at` DESC, `id` DESC),
|
||||
KEY `idx_im_sensitive_hit_user_time` (`user_id`, `created_at` DESC, `id` DESC),
|
||||
KEY `idx_im_sensitive_hit_target` (`topic`, `seqid`, `id` DESC),
|
||||
KEY `idx_im_sensitive_hit_word` (`word_id`, `created_at` DESC),
|
||||
KEY `idx_im_sensitive_hit_request` (`request_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='Sensitive-word match events';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `im_policy_decision_logs` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`policy_type` varchar(32) NOT NULL COMMENT 'security or user_policy_group',
|
||||
`policy_version` bigint unsigned NOT NULL DEFAULT 0,
|
||||
`policy_group_id` bigint NOT NULL DEFAULT 0,
|
||||
`scene` varchar(64) NOT NULL COMMENT 'friend_request, qrcode_resolve, private_message and so on',
|
||||
`action` varchar(24) NOT NULL COMMENT 'deny, challenge or audit_only',
|
||||
`reason_code` varchar(96) NOT NULL COMMENT 'For example FRIEND_DISABLED or MESSAGE_CURFEW_ACTIVE',
|
||||
`user_id` bigint NOT NULL DEFAULT 0,
|
||||
`target_user_id` bigint DEFAULT NULL,
|
||||
`topic` varchar(64) NOT NULL DEFAULT '',
|
||||
`request_id` varchar(64) NOT NULL DEFAULT '',
|
||||
`ip` varchar(64) NOT NULL DEFAULT '',
|
||||
`metadata` json DEFAULT NULL,
|
||||
`created_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_im_policy_decision_user_time` (`user_id`, `created_at` DESC, `id` DESC),
|
||||
KEY `idx_im_policy_decision_reason_time` (`reason_code`, `created_at` DESC, `id` DESC),
|
||||
KEY `idx_im_policy_decision_scene_time` (`scene`, `created_at` DESC, `id` DESC),
|
||||
KEY `idx_im_policy_decision_request` (`request_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='Runtime policy deny/challenge decisions';
|
||||
|
||||
-- ============================================================
|
||||
-- 5. Group event history and daily IP/account statistics.
|
||||
-- im_group_events_outbox remains the delivery table; this log is the queryable
|
||||
-- business history after the operation result is known.
|
||||
-- ============================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `im_group_event_logs` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`event_id` varchar(96) NOT NULL,
|
||||
`topic` varchar(64) NOT NULL,
|
||||
`group_id` bigint DEFAULT NULL,
|
||||
`event_type` varchar(64) NOT NULL,
|
||||
`actor_user_id` bigint NOT NULL DEFAULT 0,
|
||||
`target_user_id` bigint DEFAULT NULL,
|
||||
`result` varchar(16) NOT NULL DEFAULT 'success',
|
||||
`reason` varchar(500) NOT NULL DEFAULT '',
|
||||
`request_id` varchar(64) NOT NULL DEFAULT '',
|
||||
`before_snapshot` json DEFAULT NULL,
|
||||
`after_snapshot` json DEFAULT NULL,
|
||||
`metadata` json DEFAULT NULL,
|
||||
`created_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_im_group_event_log_event` (`event_id`),
|
||||
KEY `idx_im_group_event_topic_time` (`topic`, `created_at` DESC, `id` DESC),
|
||||
KEY `idx_im_group_event_type_time` (`event_type`, `created_at` DESC, `id` DESC),
|
||||
KEY `idx_im_group_event_actor_time` (`actor_user_id`, `created_at` DESC, `id` DESC),
|
||||
KEY `idx_im_group_event_request` (`request_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='Queryable group event audit history';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `im_ip_account_stats_daily` (
|
||||
`id` bigint NOT NULL AUTO_INCREMENT,
|
||||
`stat_date` date NOT NULL,
|
||||
`ip` varchar(64) NOT NULL,
|
||||
`region` varchar(128) NOT NULL DEFAULT '',
|
||||
`account_count` int unsigned NOT NULL DEFAULT 0,
|
||||
`user_count` int unsigned NOT NULL DEFAULT 0,
|
||||
`login_success_count` int unsigned NOT NULL DEFAULT 0,
|
||||
`login_failure_count` int unsigned NOT NULL DEFAULT 0,
|
||||
`first_seen_at` datetime(3) DEFAULT NULL,
|
||||
`last_seen_at` datetime(3) DEFAULT NULL,
|
||||
`risk_level` varchar(16) NOT NULL DEFAULT 'low',
|
||||
`risk_action` varchar(24) NOT NULL DEFAULT 'audit_only',
|
||||
`sample_user_ids` json DEFAULT NULL COMMENT 'Bounded sample only, not an unlimited array',
|
||||
`created_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||
`updated_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_im_ip_account_stats_day_ip` (`stat_date`, `ip`),
|
||||
KEY `idx_im_ip_account_stats_ip_day` (`ip`, `stat_date` DESC),
|
||||
KEY `idx_im_ip_account_stats_risk_day` (`risk_level`, `stat_date` DESC, `id` DESC)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='Daily IP and account aggregation';
|
||||
|
||||
-- ============================================================
|
||||
-- 6. Compatible enhancements to existing tables.
|
||||
-- Every ALTER is guarded by information_schema and can be rerun.
|
||||
-- ============================================================
|
||||
|
||||
-- Message management queries need topic/time and sender/time indexes.
|
||||
SET @im_sql = IF(
|
||||
(SELECT COUNT(*) FROM information_schema.STATISTICS
|
||||
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_messages'
|
||||
AND INDEX_NAME = 'idx_im_messages_topic_created') = 0,
|
||||
'ALTER TABLE `im_messages` ADD INDEX `idx_im_messages_topic_created` (`topic`, `createdat`, `id`)',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE im_stmt FROM @im_sql;
|
||||
EXECUTE im_stmt;
|
||||
DEALLOCATE PREPARE im_stmt;
|
||||
|
||||
SET @im_sql = IF(
|
||||
(SELECT COUNT(*) FROM information_schema.STATISTICS
|
||||
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_messages'
|
||||
AND INDEX_NAME = 'idx_im_messages_from_created') = 0,
|
||||
'ALTER TABLE `im_messages` ADD INDEX `idx_im_messages_from_created` (`from`, `createdat`, `id`)',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE im_stmt FROM @im_sql;
|
||||
EXECUTE im_stmt;
|
||||
DEALLOCATE PREPARE im_stmt;
|
||||
|
||||
-- Extend the existing Tinode device record for device and login-history views.
|
||||
SET @im_sql = IF(
|
||||
(SELECT COUNT(*) FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_devices'
|
||||
AND COLUMN_NAME = 'firstseen') = 0,
|
||||
'ALTER TABLE `im_devices` ADD COLUMN `firstseen` DATETIME(3) DEFAULT NULL AFTER `platform`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE im_stmt FROM @im_sql;
|
||||
EXECUTE im_stmt;
|
||||
DEALLOCATE PREPARE im_stmt;
|
||||
|
||||
SET @im_sql = IF(
|
||||
(SELECT COUNT(*) FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_devices'
|
||||
AND COLUMN_NAME = 'lastip') = 0,
|
||||
'ALTER TABLE `im_devices` ADD COLUMN `lastip` VARCHAR(64) NOT NULL DEFAULT '''' AFTER `lastseen`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE im_stmt FROM @im_sql;
|
||||
EXECUTE im_stmt;
|
||||
DEALLOCATE PREPARE im_stmt;
|
||||
|
||||
SET @im_sql = IF(
|
||||
(SELECT COUNT(*) FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_devices'
|
||||
AND COLUMN_NAME = 'useragent') = 0,
|
||||
'ALTER TABLE `im_devices` ADD COLUMN `useragent` VARCHAR(512) NOT NULL DEFAULT '''' AFTER `lastip`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE im_stmt FROM @im_sql;
|
||||
EXECUTE im_stmt;
|
||||
DEALLOCATE PREPARE im_stmt;
|
||||
|
||||
SET @im_sql = IF(
|
||||
(SELECT COUNT(*) FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_devices'
|
||||
AND COLUMN_NAME = 'appversion') = 0,
|
||||
'ALTER TABLE `im_devices` ADD COLUMN `appversion` VARCHAR(32) NOT NULL DEFAULT '''' AFTER `useragent`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE im_stmt FROM @im_sql;
|
||||
EXECUTE im_stmt;
|
||||
DEALLOCATE PREPARE im_stmt;
|
||||
|
||||
SET @im_sql = IF(
|
||||
(SELECT COUNT(*) FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_devices'
|
||||
AND COLUMN_NAME = 'status') = 0,
|
||||
'ALTER TABLE `im_devices` ADD COLUMN `status` VARCHAR(16) NOT NULL DEFAULT ''active'' AFTER `appversion`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE im_stmt FROM @im_sql;
|
||||
EXECUTE im_stmt;
|
||||
DEALLOCATE PREPARE im_stmt;
|
||||
|
||||
-- Feedback handling fields required by the Admin processing workflow.
|
||||
SET @im_sql = IF(
|
||||
(SELECT COUNT(*) FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_feedback_submissions'
|
||||
AND COLUMN_NAME = 'assigned_to') = 0,
|
||||
'ALTER TABLE `im_feedback_submissions` ADD COLUMN `assigned_to` BIGINT DEFAULT NULL AFTER `status`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE im_stmt FROM @im_sql;
|
||||
EXECUTE im_stmt;
|
||||
DEALLOCATE PREPARE im_stmt;
|
||||
|
||||
SET @im_sql = IF(
|
||||
(SELECT COUNT(*) FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_feedback_submissions'
|
||||
AND COLUMN_NAME = 'handled_by') = 0,
|
||||
'ALTER TABLE `im_feedback_submissions` ADD COLUMN `handled_by` BIGINT DEFAULT NULL AFTER `assigned_to`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE im_stmt FROM @im_sql;
|
||||
EXECUTE im_stmt;
|
||||
DEALLOCATE PREPARE im_stmt;
|
||||
|
||||
SET @im_sql = IF(
|
||||
(SELECT COUNT(*) FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_feedback_submissions'
|
||||
AND COLUMN_NAME = 'handled_at') = 0,
|
||||
'ALTER TABLE `im_feedback_submissions` ADD COLUMN `handled_at` DATETIME(3) DEFAULT NULL AFTER `handled_by`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE im_stmt FROM @im_sql;
|
||||
EXECUTE im_stmt;
|
||||
DEALLOCATE PREPARE im_stmt;
|
||||
|
||||
SET @im_sql = IF(
|
||||
(SELECT COUNT(*) FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_feedback_submissions'
|
||||
AND COLUMN_NAME = 'resolution') = 0,
|
||||
'ALTER TABLE `im_feedback_submissions` ADD COLUMN `resolution` VARCHAR(1000) NOT NULL DEFAULT '''' AFTER `handled_at`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE im_stmt FROM @im_sql;
|
||||
EXECUTE im_stmt;
|
||||
DEALLOCATE PREPARE im_stmt;
|
||||
|
||||
SET @im_sql = IF(
|
||||
(SELECT COUNT(*) FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_feedback_submissions'
|
||||
AND COLUMN_NAME = 'admin_remark') = 0,
|
||||
'ALTER TABLE `im_feedback_submissions` ADD COLUMN `admin_remark` VARCHAR(1000) NOT NULL DEFAULT '''' AFTER `resolution`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE im_stmt FROM @im_sql;
|
||||
EXECUTE im_stmt;
|
||||
DEALLOCATE PREPARE im_stmt;
|
||||
|
||||
SET @im_sql = IF(
|
||||
(SELECT COUNT(*) FROM information_schema.STATISTICS
|
||||
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_feedback_submissions'
|
||||
AND INDEX_NAME = 'idx_im_feedback_status_time') = 0,
|
||||
'ALTER TABLE `im_feedback_submissions` ADD INDEX `idx_im_feedback_status_time` (`status`, `created_at`, `id`)',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE im_stmt FROM @im_sql;
|
||||
EXECUTE im_stmt;
|
||||
DEALLOCATE PREPARE im_stmt;
|
||||
|
||||
SET @im_sql = IF(
|
||||
(SELECT COUNT(*) FROM information_schema.STATISTICS
|
||||
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_audit_logs'
|
||||
AND INDEX_NAME = 'idx_im_audit_logs_ip_time') = 0,
|
||||
'ALTER TABLE `im_audit_logs` ADD INDEX `idx_im_audit_logs_ip_time` (`ip`, `created_at`, `id`)',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE im_stmt FROM @im_sql;
|
||||
EXECUTE im_stmt;
|
||||
DEALLOCATE PREPARE im_stmt;
|
||||
|
||||
-- Post-deployment checks (read-only):
|
||||
-- SELECT policy_type, policy_version, apply_status, applied_at
|
||||
-- FROM im_runtime_policy_snapshots ORDER BY policy_type, policy_version DESC;
|
||||
-- SELECT reason_code, scene, COUNT(*) AS deny_count
|
||||
-- FROM im_policy_decision_logs
|
||||
-- WHERE created_at >= CURRENT_DATE
|
||||
-- GROUP BY reason_code, scene;
|
||||
-- SELECT stat_date, ip, account_count, login_failure_count, risk_level
|
||||
-- FROM im_ip_account_stats_daily ORDER BY stat_date DESC, login_failure_count DESC;
|
||||
Reference in New Issue
Block a user