Metadata-Version: 2.4
Name: pcs-sdk
Version: 0.1.0
Summary: pcs API client built on requests
Author: staugur
Requires-Python: >=3.8
Description-Content-Type: text/markdown
Requires-Dist: requests>=2.20

# pcs SDK

pcs 的 Python 客户端，基于 `requests` 封装「费用模块」与「防火墙模块」接口，
统一处理 HMAC-SHA256 请求签名、JSON 序列化与错误判定。接口字段与错误码见仓库根目录 `API.md`。

## 安装

```bash
# 方式一：从 PyPI 安装（推荐）
pip install pcs-sdk

# 方式二：本地开发（可编辑安装）
cd sdk
pip install -e .

# 方式三：直接安装 GitHub 仓库中的 sdk 子目录
pip install "git+https://github.com/staugur/pcs.git#subdirectory=sdk"
```

## 版本号与发布

版本号由 `setuptools_scm` 从 git 自动派生，**无需手改** `pcs/__init__.py` 里的
`__version__`：

- 打了 `v*` tag（如 `v0.1.0`）并推送 → 正式版 `0.1.0`；
- 推送改动落到 `sdk/` 目录（仅 `master` / `dev` 分支）→ 自动发布开发版，版本号形如
  `0.1.0.dev12+gabcdef`（距最近 tag 的提交数 + commit sha，天然唯一、不撞车）；
- 在 Actions 页手动 Run workflow → 开发版 `0.1.0.dev<时间戳>`（精确到秒，天然唯一、
  可直发 PyPI）。

> 仅 `sdk/` 目录的改动会触发自动发布；改 `src/`、`AGENTS.md`、`API.md` 等其他目录
> 不会触发，避免无关改动误发版本。同一 push 若同时命中多个触发条件（如打了 `v*` tag
> 且其提交刚好改了 `sdk/`），工作流只运行一次。

发布由仓库的 GitHub Action（`.github/workflows/publish.yml`）完成：

- 推送 `v*` tag → 自动发布正式版到 PyPI；
- 推送改动落到 `sdk/**`（仅 `master` / `dev`）→ 自动发布开发版；
- 在 Actions 页手动 Run workflow → 快速发布开发版（无需打 tag）。

> 采用 OIDC trusted publishing（免密钥）：首次发布前需在 PyPI 的 `pcs-sdk` 项目配置
> Trusted Publisher（Owner=`staugur`、Repository name=`pcs`、Workflow name=`publish.yml`、
> Environment name 留空），无需配置 `PYPI_API_TOKEN`。

## 快速上手

```python
from pcs import PcsClient

client = PcsClient("https://pcs.example.com", api_key="<API_KEY>")

# 费用：查询余额（记录不存在返回 None）
balance = client.fee_balance(uid="u1", module="blog")

# 费用：查询全部模块状态 / 流水 / 付费模块
client.fee_balances(uid="u1")
client.fee_logs(uid="u1", module="blog", limit=50)
client.fee_modules()

# 费用：充值（biz_no 务必幂等——重试复用原值，新动作换新值）
client.fee_recharge(
    uid="u1", module="blog", amount=120, pay_type="prepaid",
    pay_source="alipay", start_at=1790304000, end_at=1821840000,
    level="pro", trade_no="202609010001",
    biz_no="blog-2026-09-recharge-001",
)

# 费用：消费扣减（仅按量付费模块）
client.fee_consume(uid="u1", module="blog", amount=30,
                   biz_no="blog-2026-09-usage-001")

# 主机管理：列出已登记主机
client.host_hosts()

# 防火墙：列出放行记录（按主机过滤 / 分页：limit 上限 200）
client.host_allows(host_id=1)
client.host_allows(limit=20, offset=0)

# 防火墙：临时放行（到期时间 expire_at 为 10 位时间戳）
client.host_allow(host_id=1, ip="1.2.3.4", port="3306",
                  expire_at=1790304000 + 3600, remark="临时开库")

# 防火墙：撤销放行
client.host_revoke(allow_id=42)

# nginx 白名单：列出 / 新增 / 撤销（pcs 全权托管 conf，到期自动 reload）
client.host_nginx_allows(host_id=1)
client.host_nginx_allow(host_id=1, ip="1.2.3.4",
                        expire_at=1790304000 + 3600, remark="临时开站")
client.host_nginx_revoke(allow_id=42)
```

## 错误处理

接口返回 `success=false` 或 HTTP 错误时抛出 `pcs.PcsApiError`，可读取
`code`（错误码，如 `PARAM_ERROR` / `INSUFFICIENT_BALANCE` / `IDEMPOTENCY_CONFLICT`）、
`message` 与 `status_code`：

```python
from pcs import PcsClient, PcsApiError

try:
    client.fee_consume(uid="u1", module="blog", amount=999, biz_no="x-1")
except PcsApiError as e:
    print(e.code, e.status_code, e.message)
```

## 复用连接

`PcsClient` 默认内部持有 `requests.Session`（连接池复用）。需要自定义会话
（代理、证书、重试策略）时传入 `session=` 即可。
