diff --git a/01_卡资(金)/金仓_存储备份/服务器管理/SKILL.md b/01_卡资(金)/金仓_存储备份/服务器管理/SKILL.md index c41e2628..44d4013e 100644 --- a/01_卡资(金)/金仓_存储备份/服务器管理/SKILL.md +++ b/01_卡资(金)/金仓_存储备份/服务器管理/SKILL.md @@ -308,6 +308,7 @@ ss -tlnp | grep :端口号 | `快速检查服务器.py` | 一键检查所有服务器状态 | `./scripts/` | | `一键部署.py` | 根据配置文件部署项目 | `./scripts/` | | `ssl证书检查.py` | 检查/修复SSL证书 | `./scripts/` | +| `腾讯云镜像快照备份到CKB_NAS/tencent_image_snapshot_backup_to_nas.py` | 腾讯云镜像/快照元数据备份到 CKB NAS,1000G 限制与超限邮件告警 | `./scripts/腾讯云镜像快照备份到CKB_NAS/` | --- diff --git a/01_卡资(金)/金仓_存储备份/服务器管理/scripts/腾讯云镜像快照备份到CKB_NAS/.gitignore b/01_卡资(金)/金仓_存储备份/服务器管理/scripts/腾讯云镜像快照备份到CKB_NAS/.gitignore new file mode 100644 index 00000000..2549b3dd --- /dev/null +++ b/01_卡资(金)/金仓_存储备份/服务器管理/scripts/腾讯云镜像快照备份到CKB_NAS/.gitignore @@ -0,0 +1 @@ +config.env diff --git a/01_卡资(金)/金仓_存储备份/服务器管理/scripts/腾讯云镜像快照备份到CKB_NAS/README.md b/01_卡资(金)/金仓_存储备份/服务器管理/scripts/腾讯云镜像快照备份到CKB_NAS/README.md new file mode 100644 index 00000000..8accc197 --- /dev/null +++ b/01_卡资(金)/金仓_存储备份/服务器管理/scripts/腾讯云镜像快照备份到CKB_NAS/README.md @@ -0,0 +1,91 @@ +# 腾讯云镜像/快照备份到 CKB NAS + +将腾讯云 CVM 自定义镜像、云硬盘快照的元数据与(可选)镜像文件备份到 **CKB NAS**(192.168.1.201),并限制备份目录总容量为 **1000GB**,超过时发邮件告警。 + +## 功能 + +- **镜像**:拉取账号下全部地域的**自定义镜像**列表,写入 NAS 的 `meta/images_*.json`、`meta/images_latest.json` +- **快照**:拉取全部地域的**云硬盘快照**列表,写入 NAS 的 `meta/snapshots_*.json`、`meta/snapshots_latest.json` +- **容量限制**:备份根目录总大小超过 **1000GB**(可配置)时: + - 发送告警邮件到配置的邮箱 + - 不再拉取新的镜像文件(仅更新元数据) +- **镜像文件落盘**:腾讯云不提供镜像/快照“直接下载”。需在控制台将**自定义镜像导出到 COS**,再通过 COS 工具(如 coscmd)或本脚本扩展逻辑,从 COS 下载到 NAS。 + +## 前置条件 + +1. **CKB NAS 已挂载到本机** + - CKB NAS 内网 IP:`192.168.1.201`(见群晖 NAS 管理 SKILL) + - 在 Mac:访达 → 前往 → 连接服务器 → `smb://192.168.1.201/共享名`,挂载后得到如 `/Volumes/ckb_backup` + - 本脚本使用的目录为挂载点下的子目录,如 `/Volumes/ckb_backup/tencent_cloud_backup` + +2. **腾讯云凭证** + - 环境变量 `TENCENTCLOUD_SECRET_ID`、`TENCENTCLOUD_SECRET_KEY`,或 + - 在 `运营中枢/工作台/00_账号与API索引.md` 的「腾讯云」段落填写 SecretId/SecretKey + +3. **告警邮件(可选)** + - 使用 QQ 邮箱时需在 QQ 邮箱 → 设置 → 账户 → 开启 SMTP 并生成**授权码**,在 `config.env` 中填 `SMTP_PASSWORD=授权码` + +## 配置 + +1. 复制配置示例并编辑: + ```bash + cd "01_卡资(金)/金仓_存储备份/服务器管理/scripts/腾讯云镜像快照备份到CKB_NAS" + cp config.example.env config.env + ``` +2. 编辑 `config.env`: + - **NAS_BACKUP_ROOT**:NAS 上备份根目录(必须已挂载),如 `/Volumes/ckb_backup/tencent_cloud_backup` + - **SIZE_LIMIT_GB**:容量上限(GB),默认 `1000` + - **ALERT_EMAIL_TO**:超限告警收件人 + - **SMTP_*****:发信用(QQ 邮箱用授权码) + +## 运行 + +```bash +# 建议使用项目根下已有 venv(含 tencentcloud-sdk) +cd /Users/karuo/Documents/个人/卡若AI +. .venv_tencent/bin/activate # 或 服务器管理/scripts 下的 venv +pip install tencentcloud-sdk-python-common tencentcloud-sdk-python-cvm tencentcloud-sdk-python-cbs + +python3 "01_卡资(金)/金仓_存储备份/服务器管理/scripts/腾讯云镜像快照备份到CKB_NAS/tencent_image_snapshot_backup_to_nas.py" +``` + +## 定时拉取(有新镜像/快照即同步元数据) + +- 建议用 cron 或 launchd 定期执行(如每天 2:00),这样**新产生的镜像/快照**会在下次执行时被拉取并写入 NAS 的 `meta/`。 +- 示例 crontab(每天 2:00): + ```bash + 0 2 * * * /Users/karuo/Documents/个人/卡若AI/.venv_tencent/bin/python3 /Users/karuo/Documents/个人/卡若AI/01_卡资(金)/金仓_存储备份/服务器管理/scripts/腾讯云镜像快照备份到CKB_NAS/tencent_image_snapshot_backup_to_nas.py >> /tmp/tencent_backup_nas.log 2>&1 + ``` +- 若备份目录超过 1000GB,脚本会发邮件提醒,且不会从 COS 拉取新镜像文件(仅更新元数据)。 + +## 镜像文件如何落到 NAS(需先导出到 COS) + +1. 腾讯云控制台 → 云服务器 → 镜像 → 自定义镜像 → 选择镜像 → **导出镜像** → 选择同地域 COS 桶与前缀。 +2. 导出完成后,在 COS 桶中会得到镜像文件(如 `.qcow2` / `.vhd`)。 +3. 将 COS 中的文件下载到 NAS: + - 方式 A:安装 [coscmd](https://cloud.tencent.com/document/product/436/10976),配置后执行 `coscmd download -r bucket://prefix /path/on/nas` + - 方式 B:在本脚本中扩展「从 COS 列举并下载」逻辑(需配置 `COS_BUCKET`、`COS_REGION`、`COS_PREFIX`)。 + +## 快照说明 + +- 腾讯云**云硬盘快照**不提供“下载到本地”的接口,仅支持云内恢复、从快照创建云盘/镜像。 +- 本脚本将快照**元数据**(ID、名称、大小、创建时间等)同步到 NAS,便于审计与清单管理。 +- 若需将某快照“备份成文件”,需:用该快照创建自定义镜像 → 再按上文将镜像导出到 COS → 从 COS 下载到 NAS。 + +## 文件结构(NAS 备份根目录下) + +``` +tencent_cloud_backup/ +├── meta/ +│ ├── images_20260219_020000.json +│ ├── images_latest.json +│ ├── snapshots_20260219_020000.json +│ └── snapshots_latest.json +└── (可选)images/ # 若从 COS 下载镜像文件,可放于此 +``` + +## 相关 + +- 服务器管理 SKILL:`01_卡资(金)/金仓_存储备份/服务器管理/SKILL.md` +- 群晖/CKB NAS:`01_卡资(金)/金仓_存储备份/群晖NAS管理/SKILL.md` +- 账号与 API 索引:`运营中枢/工作台/00_账号与API索引.md` diff --git a/01_卡资(金)/金仓_存储备份/服务器管理/scripts/腾讯云镜像快照备份到CKB_NAS/config.example.env b/01_卡资(金)/金仓_存储备份/服务器管理/scripts/腾讯云镜像快照备份到CKB_NAS/config.example.env new file mode 100644 index 00000000..ad001f2e --- /dev/null +++ b/01_卡资(金)/金仓_存储备份/服务器管理/scripts/腾讯云镜像快照备份到CKB_NAS/config.example.env @@ -0,0 +1,26 @@ +# 腾讯云镜像/快照备份到 CKB NAS - 配置示例 +# 复制为 config.env 并填写真实值,config.env 勿提交 Git + +# ---------- NAS 备份目录(必须先挂载 CKB NAS 的 SMB 共享) +# 示例:Mac 挂载后 /Volumes/ckb_backup ;Linux /mnt/ckb/tencent_cloud_backup +NAS_BACKUP_ROOT=/Volumes/ckb_backup/tencent_cloud_backup + +# 容量上限(GB),超过则发邮件告警,且本次不再拉取新镜像文件 +SIZE_LIMIT_GB=1000 + +# ---------- 告警邮件(超过 SIZE_LIMIT_GB 时发送) +ALERT_EMAIL_TO=zhiqun@qq.com +# QQ 邮箱发信需用授权码,非登录密码。见:QQ邮箱 -> 设置 -> 账户 -> POP3/IMAP -> 生成授权码 +SMTP_HOST=smtp.qq.com +SMTP_PORT=465 +SMTP_USER=zhiqun@qq.com +SMTP_PASSWORD= + +# ---------- 腾讯云(可选,不填则从 00_账号与API索引.md 读取) +# TENCENTCLOUD_SECRET_ID= +# TENCENTCLOUD_SECRET_KEY= + +# ---------- COS 同步(可选。若在控制台已将镜像导出到 COS,填此项可从 COS 拉取到 NAS) +# COS_BUCKET=your-bucket-1251077262 +# COS_REGION=ap-guangzhou +# COS_PREFIX=export_images diff --git a/01_卡资(金)/金仓_存储备份/服务器管理/scripts/腾讯云镜像快照备份到CKB_NAS/cron_example.txt b/01_卡资(金)/金仓_存储备份/服务器管理/scripts/腾讯云镜像快照备份到CKB_NAS/cron_example.txt new file mode 100644 index 00000000..3b82da36 --- /dev/null +++ b/01_卡资(金)/金仓_存储备份/服务器管理/scripts/腾讯云镜像快照备份到CKB_NAS/cron_example.txt @@ -0,0 +1,14 @@ +# 腾讯云镜像/快照备份到 CKB NAS - 定时执行示例 +# 每天 2:00 执行,有新镜像/快照会自动拉取元数据到 NAS;超过 1000G 会发邮件告警 + +# 1) 先挂载 CKB NAS(Mac 可开机自动挂载或手动) +# 访达 → 连接服务器 → smb://192.168.1.201/共享名 +# 挂载后例如:/Volumes/ckb_backup + +# 2) 在脚本目录配置 config.env(NAS_BACKUP_ROOT、告警邮箱、SMTP) + +# 3) crontab -e 添加(路径按本机实际修改): +0 2 * * * NAS_BACKUP_ROOT=/Volumes/ckb_backup/tencent_cloud_backup /Users/karuo/Documents/个人/卡若AI/.venv_tencent/bin/python3 /Users/karuo/Documents/个人/卡若AI/01_卡资(金)/金仓_存储备份/服务器管理/scripts/腾讯云镜像快照备份到CKB_NAS/tencent_image_snapshot_backup_to_nas.py >> /tmp/tencent_backup_nas.log 2>&1 + +# 或使用 config.env 中的 NAS_BACKUP_ROOT(不设环境变量): +# 0 2 * * * cd /Users/karuo/Documents/个人/卡若AI/01_卡资(金)/金仓_存储备份/服务器管理/scripts/腾讯云镜像快照备份到CKB_NAS && /Users/karuo/Documents/个人/卡若AI/.venv_tencent/bin/python3 tencent_image_snapshot_backup_to_nas.py >> /tmp/tencent_backup_nas.log 2>&1 diff --git a/01_卡资(金)/金仓_存储备份/服务器管理/scripts/腾讯云镜像快照备份到CKB_NAS/tencent_image_snapshot_backup_to_nas.py b/01_卡资(金)/金仓_存储备份/服务器管理/scripts/腾讯云镜像快照备份到CKB_NAS/tencent_image_snapshot_backup_to_nas.py new file mode 100644 index 00000000..3ff4443c --- /dev/null +++ b/01_卡资(金)/金仓_存储备份/服务器管理/scripts/腾讯云镜像快照备份到CKB_NAS/tencent_image_snapshot_backup_to_nas.py @@ -0,0 +1,310 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +腾讯云 CVM 镜像与云硬盘快照 → 备份到 CKB NAS + +- 拉取账号下全部自定义镜像、快照的元数据并写入 NAS +- 可选:从 COS 同步已导出的镜像文件到 NAS(需先在控制台将镜像导出到 COS) +- 备份目录容量上限(默认 1000GB),超过则发邮件告警且不再拉取新文件 + +凭证:环境变量 或 00_账号与API索引.md § 腾讯云 +配置:同目录 config.env(参考 config.example.env) +依赖:tencentcloud-sdk-python-common, tencentcloud-sdk-python-cvm, tencentcloud-sdk-python-cbs +""" +from __future__ import annotations + +import json +import os +import re +import smtplib +import sys +from datetime import datetime +from email.mime.text import MIMEText +from pathlib import Path + +# 多地域 +CVM_REGIONS = [ + "ap-guangzhou", "ap-shanghai", "ap-beijing", "ap-chengdu", + "ap-nanjing", "ap-shenzhen-fsi", "ap-hongkong", +] +CBS_REGIONS = CVM_REGIONS # 云硬盘与 CVM 地域一致 + +def _find_karuo_ai_root(): + d = Path(__file__).resolve().parent + for _ in range(6): + if d.name == "卡若AI" or ( + (d / "运营中枢").is_dir() and (d / "01_卡资(金)").is_dir() + ): + return d + d = d.parent + return None + +def _read_tencent_creds(): + sid = os.environ.get("TENCENTCLOUD_SECRET_ID") + skey = os.environ.get("TENCENTCLOUD_SECRET_KEY") + if sid and skey: + return sid, skey + root = _find_karuo_ai_root() + if not root: + return None, None + idx = root / "运营中枢" / "工作台" / "00_账号与API索引.md" + if not idx.is_file(): + return None, None + text = idx.read_text(encoding="utf-8") + in_tencent = False + sid = skey = None + for line in text.splitlines(): + if "### 腾讯云" in line: + in_tencent = True + continue + if in_tencent and line.strip().startswith("###"): + break + if not in_tencent: + continue + m = re.search(r"\|\s*[^|]*(?:SecretId|密钥)[^|]*\|\s*`([^`]+)`", line, re.I) + if m and m.group(1).strip().startswith("AKID"): + sid = m.group(1).strip() + m = re.search(r"\|\s*SecretKey\s*\|\s*`([^`]+)`", line, re.I) + if m: + skey = m.group(1).strip() + return sid, skey + +def _load_config(): + script_dir = Path(__file__).resolve().parent + env_file = script_dir / "config.env" + if not env_file.is_file(): + env_file = script_dir / "config.example.env" + config = { + "NAS_BACKUP_ROOT": os.environ.get("NAS_BACKUP_ROOT", ""), + "SIZE_LIMIT_GB": int(os.environ.get("SIZE_LIMIT_GB", "1000")), + "ALERT_EMAIL_TO": os.environ.get("ALERT_EMAIL_TO", ""), + "SMTP_HOST": os.environ.get("SMTP_HOST", "smtp.qq.com"), + "SMTP_PORT": int(os.environ.get("SMTP_PORT", "465")), + "SMTP_USER": os.environ.get("SMTP_USER", ""), + "SMTP_PASSWORD": os.environ.get("SMTP_PASSWORD", ""), + } + if env_file.is_file(): + for line in env_file.read_text(encoding="utf-8").splitlines(): + line = line.strip() + if not line or line.startswith("#") or "=" not in line: + continue + k, _, v = line.partition("=") + k, v = k.strip(), v.strip().strip('"').strip("'") + if k in config: + if k == "SIZE_LIMIT_GB": + try: + config[k] = int(v) + except ValueError: + pass + elif k == "SMTP_PORT": + try: + config[k] = int(v) + except ValueError: + pass + else: + config[k] = v + for k, v in os.environ.items(): + if k in config and v: + if k == "SIZE_LIMIT_GB": + try: + config[k] = int(v) + except ValueError: + pass + elif k == "SMTP_PORT": + try: + config[k] = int(v) + except ValueError: + pass + else: + config[k] = v + return config + +def _dir_size_gb(path: Path) -> float: + """目录占用大小(GB)""" + if not path.exists() or not path.is_dir(): + return 0.0 + total = 0 + try: + for entry in path.rglob("*"): + if entry.is_file(): + try: + total += entry.stat().st_size + except OSError: + pass + except OSError: + pass + return total / (1024 ** 3) + +def _send_alert_email(config: dict, used_gb: float, limit_gb: int): + to_addr = config.get("ALERT_EMAIL_TO") or "" + if not to_addr: + print("[告警] 未配置 ALERT_EMAIL_TO,跳过发邮件。") + return + user = config.get("SMTP_USER") or "" + password = config.get("SMTP_PASSWORD") or "" + if not user or not password: + print("[告警] 未配置 SMTP_USER/SMTP_PASSWORD,跳过发邮件。") + return + subject = "[腾讯云备份] CKB NAS 备份目录已超过 %d GB 限制" % limit_gb + body = ( + "腾讯云镜像/快照备份目录容量超限告警\n\n" + "备份根目录:%s\n" + "当前占用:%.2f GB\n" + "设定上限:%d GB\n\n" + "请清理旧备份或扩大限额,否则将不再拉取新镜像/快照文件。\n" + "时间:%s\n" + ) % ( + config.get("NAS_BACKUP_ROOT", ""), + used_gb, + limit_gb, + datetime.now().strftime("%Y-%m-%d %H:%M:%S"), + ) + msg = MIMEText(body, "plain", "utf-8") + msg["Subject"] = subject + msg["From"] = user + msg["To"] = to_addr + try: + with smtplib.SMTP_SSL(config.get("SMTP_HOST", "smtp.qq.com"), config.get("SMTP_PORT", 465)) as s: + s.login(user, password) + s.sendmail(user, [to_addr], msg.as_string()) + print("[告警] 已发送邮件至 %s" % to_addr) + except Exception as e: + print("[告警] 发邮件失败: %s" % e) + +def main(): + config = _load_config() + nas_root = config.get("NAS_BACKUP_ROOT") or "" + if not nas_root: + print("请配置 NAS_BACKUP_ROOT(CKB NAS 挂载后的备份根目录)") + print("参考 config.example.env,复制为 config.env 并填写。") + return 1 + nas_root = Path(nas_root) + limit_gb = config.get("SIZE_LIMIT_GB", 1000) + + # 检查当前占用 + used_gb = _dir_size_gb(nas_root) + if used_gb >= limit_gb: + print("备份目录已超过 %d GB(当前 %.2f GB),发送告警邮件并跳过拉取新文件。" % (limit_gb, used_gb)) + _send_alert_email(config, used_gb, limit_gb) + # 仍可更新元数据(不占大空间) + else: + print("备份目录当前 %.2f GB / %d GB" % (used_gb, limit_gb)) + + sid, skey = _read_tencent_creds() + if not sid or not skey: + print("未配置腾讯云 SecretId/SecretKey(环境变量或 00_账号与API索引.md)") + return 1 + + try: + from tencentcloud.common import credential + from tencentcloud.cvm.v20170312 import cvm_client, models as cvm_models + from tencentcloud.cbs.v20170312 import cbs_client, models as cbs_models + except ImportError: + print("请安装: pip install tencentcloud-sdk-python-common tencentcloud-sdk-python-cvm tencentcloud-sdk-python-cbs") + return 1 + + cred = credential.Credential(sid, skey) + meta_dir = nas_root / "meta" + meta_dir.mkdir(parents=True, exist_ok=True) + + # 1) 拉取自定义镜像(多地域) + all_images = [] + for region in CVM_REGIONS: + try: + client = cvm_client.CvmClient(cred, region) + req = cvm_models.DescribeImagesRequest() + req.Limit = 100 + req.Offset = 0 + # 只拉自定义镜像 + while True: + resp = client.DescribeImages(req) + items = getattr(resp, "ImageSet", None) or [] + for img in items: + if getattr(img, "ImageType", "") == "PRIVATE_IMAGE": + all_images.append({ + "Region": region, + "ImageId": getattr(img, "ImageId", ""), + "ImageName": getattr(img, "ImageName", ""), + "ImageSize": getattr(img, "ImageSize", 0), + "CreatedTime": getattr(img, "CreatedTime", ""), + "ImageState": getattr(img, "ImageState", ""), + }) + if len(items) < req.Limit: + break + req.Offset += req.Limit + except Exception as e: + print("[%s] 镜像列表: %s" % (region, e)) + + images_file = meta_dir / ("images_%s.json" % datetime.now().strftime("%Y%m%d_%H%M%S")) + images_file.write_text(json.dumps(all_images, ensure_ascii=False, indent=2), encoding="utf-8") + # 保留最新一份为 images_latest.json + (meta_dir / "images_latest.json").write_text( + json.dumps(all_images, ensure_ascii=False, indent=2), encoding="utf-8" + ) + print("镜像元数据已写入 %s(共 %d 个自定义镜像)" % (images_file, len(all_images))) + + # 2) 拉取快照(多地域) + all_snapshots = [] + for region in CBS_REGIONS: + try: + client = cbs_client.CbsClient(cred, region) + req = cbs_models.DescribeSnapshotsRequest() + req.Limit = 100 + req.Offset = 0 + while True: + resp = client.DescribeSnapshots(req) + items = getattr(resp, "SnapshotSet", None) or [] + for sn in items: + all_snapshots.append({ + "Region": region, + "SnapshotId": getattr(sn, "SnapshotId", ""), + "SnapshotName": getattr(sn, "SnapshotName", ""), + "DiskSize": getattr(sn, "DiskSize", 0), + "CreatedTime": getattr(sn, "CreatedTime", ""), + "SnapshotState": getattr(sn, "SnapshotState", ""), + }) + if len(items) < req.Limit: + break + req.Offset += req.Limit + except Exception as e: + print("[%s] 快照列表: %s" % (region, e)) + + snapshots_file = meta_dir / ("snapshots_%s.json" % datetime.now().strftime("%Y%m%d_%H%M%S")) + snapshots_file.write_text(json.dumps(all_snapshots, ensure_ascii=False, indent=2), encoding="utf-8") + (meta_dir / "snapshots_latest.json").write_text( + json.dumps(all_snapshots, ensure_ascii=False, indent=2), encoding="utf-8" + ) + print("快照元数据已写入 %s(共 %d 个快照)" % (snapshots_file, len(all_snapshots))) + + # 3) 若配置了 COS,可从 COS 同步镜像文件到 NAS(需 tencentcloud-sdk-python-cos) + cos_bucket = os.environ.get("COS_BUCKET") or "" + if not cos_bucket and os.path.isfile(Path(__file__).parent / "config.env"): + for line in (Path(__file__).parent / "config.env").read_text(encoding="utf-8").splitlines(): + if line.strip().startswith("COS_BUCKET=") and "=" in line: + cos_bucket = line.split("=", 1)[1].strip().strip('"').strip("'") + break + if cos_bucket and used_gb < limit_gb: + try: + from tencentcloud.cos.cos_client import CosS3Client + from tencentcloud.cos.cos_config import CosConfig + region = os.environ.get("COS_REGION", "ap-guangzhou") + cos_prefix = os.environ.get("COS_PREFIX", "export_images") + config_cos = CosConfig(Region=region, SecretId=sid, SecretKey=skey) + client_cos = CosS3Client(config_cos) + # 列举并下载(示例:只列举,实际下载需根据 COS 返回的 Key 逐条 download) + # 此处简化:仅提示用户可在此扩展 COS 下载逻辑 + print("[COS] 已配置 COS_BUCKET,如需自动从 COS 拉取镜像文件,请在脚本中扩展 COS 下载逻辑。") + except ImportError: + print("[COS] 未安装 cos-python-sdk-v5,跳过从 COS 同步。") + else: + print("镜像文件需在腾讯云控制台「导出镜像」到 COS 后,再通过 COS 同步或 coscmd 下载到 NAS。") + + # 若开始时未超限但结束时超限(例如本轮有下载),再发一次告警 + used_gb_after = _dir_size_gb(nas_root) + if used_gb_after >= limit_gb and used_gb < limit_gb: + _send_alert_email(config, used_gb_after, limit_gb) + + return 0 + +if __name__ == "__main__": + sys.exit(main()) diff --git a/运营中枢/工作台/gitea_push_log.md b/运营中枢/工作台/gitea_push_log.md index 45dc6a68..5e0ab268 100644 --- a/运营中枢/工作台/gitea_push_log.md +++ b/运营中枢/工作台/gitea_push_log.md @@ -37,3 +37,4 @@ | 2026-02-19 18:07:18 | 🔄 卡若AI 同步 2026-02-19 18:07 | 更新:运营中枢工作台 | 排除 >20MB: 5 个 | | 2026-02-19 19:40:16 | 🔄 卡若AI 同步 2026-02-19 19:40 | 更新:金仓、水桥平台对接、运营中枢工作台 | 排除 >20MB: 5 个 | | 2026-02-19 20:11:22 | 🔄 卡若AI 同步 2026-02-19 20:11 | 更新:金仓、运营中枢工作台 | 排除 >20MB: 5 个 | +| 2026-02-20 07:09:16 | 🔄 卡若AI 同步 2026-02-20 07:09 | 更新:金仓、水桥平台对接、运营中枢参考资料、运营中枢工作台 | 排除 >20MB: 5 个 | diff --git a/运营中枢/工作台/代码管理.md b/运营中枢/工作台/代码管理.md index c0b99cc0..936e0f6a 100644 --- a/运营中枢/工作台/代码管理.md +++ b/运营中枢/工作台/代码管理.md @@ -40,3 +40,4 @@ | 2026-02-19 18:07:18 | 成功 | 成功 | 🔄 卡若AI 同步 2026-02-19 18:07 | 更新:运营中枢工作台 | 排除 >20MB: 5 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) | | 2026-02-19 19:40:16 | 成功 | 成功 | 🔄 卡若AI 同步 2026-02-19 19:40 | 更新:金仓、水桥平台对接、运营中枢工作台 | 排除 >20MB: 5 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) | | 2026-02-19 20:11:22 | 成功 | 成功 | 🔄 卡若AI 同步 2026-02-19 20:11 | 更新:金仓、运营中枢工作台 | 排除 >20MB: 5 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) | +| 2026-02-20 07:09:16 | 成功 | 成功 | 🔄 卡若AI 同步 2026-02-20 07:09 | 更新:金仓、水桥平台对接、运营中枢参考资料、运营中枢工作台 | 排除 >20MB: 5 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) |