Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
8d4ab62
feat(category): restore management page import flow; use dry-run prev…
zensgit Sep 19, 2025
8cef9ac
feat(api): templates list pagination (page/per_page) + ETag support (…
zensgit Sep 19, 2025
091c8d3
feat(templates): add ETag + pagination fetch (client) and result wrapper
zensgit Sep 19, 2025
d6b01d4
fix: 修复Flutter编译错误和Provider缺失问题
zensgit Sep 19, 2025
23e1ab0
Merge branch 'main' into pr/templates-etag-frontend
zensgit Sep 19, 2025
4260fce
feat(ui): template import dialog — add ETag pagination (Load more) us…
zensgit Sep 19, 2025
b8dcfea
feat(api): dry_run details — predicted rename, existing category summ…
zensgit Sep 19, 2025
74aa573
feat(ui): dry-run preview renders server details (predicted rename / …
zensgit Sep 19, 2025
87e8ef8
chore(api,flutter,ci): align ImportActionDetail; clippy green; make l…
zensgit Sep 19, 2025
38e5a17
chore(docs): add PR drafts for demo feature-gate and Flutter analyzer…
zensgit Sep 19, 2025
e092ff2
Merge pull request #20 from zensgit/chore/lint-ci-import-detail
zensgit Sep 19, 2025
ffec566
chore(api): feature-gate demo endpoints; align local CI clippy mode (…
zensgit Sep 19, 2025
704f66d
chore(flutter): analyzer cleanup phase 1 (#22)
zensgit Sep 19, 2025
0810240
chore(flutter): run local analyzer, groundwork for bulk cleanup (no b…
zensgit Sep 19, 2025
3161342
chore(flutter): analyzer cleanup tools (unused imports parser + mater…
zensgit Sep 19, 2025
825607a
chore(flutter): analyzer cleanup phase 1.2 execution
zensgit Sep 19, 2025
55392eb
fix(flutter): Phase 1.2 syntax fixes (transaction_card, budget_summary)
zensgit Sep 19, 2025
f72c2da
chore(flutter): analyzer cleanup phase 1.2 - batch remove unused imports
zensgit Sep 19, 2025
9a0a688
fix(flutter): resolve analyzer syntax issues and remove invalid const…
zensgit Sep 19, 2025
5972089
chore: trigger CI run
zensgit Sep 19, 2025
c2c2c05
chore(flutter): Phase 1.3 unblock — strip invalid const from Text/Ico…
zensgit Sep 19, 2025
cc3a42c
chore: trigger CI after const fixes
zensgit Sep 19, 2025
0745f62
fix(flutter): resolve build_runner blockers — rename broken identifie…
zensgit Sep 19, 2025
9a3a5e7
fix(flutter): clean all broken '*const*' identifiers (Icon/Text varia…
zensgit Sep 19, 2025
2520aa0
Add stub files for missing dependencies - Phase 1.3
zensgit Sep 19, 2025
37a3052
chore(flutter): add minimal stubs and path-forwarders to unblock unde…
zensgit Sep 19, 2025
98107da
Add missing service method stubs - Phase 1.3 continued
zensgit Sep 19, 2025
e1506a8
fix: Phase 1.3 continued - Fix isSuperAdmin and updateTemplate issues
zensgit Sep 19, 2025
f69a887
fix: Phase 1.3 continued - Fix AuditService parameters and AuditActio…
zensgit Sep 19, 2025
33cb211
fix: Phase 1.3 continued - Add missing methods and fix undefined errors
zensgit Sep 19, 2025
9970b83
fix: Phase 1.3 continued - Const error fix script and improvements
zensgit Sep 19, 2025
94f4cdf
fix: Phase 1.3 - Manual const error fixes
zensgit Sep 19, 2025
71a5de5
fix: Phase 1.4 - Fix undefined_identifier errors
zensgit Sep 19, 2025
9fc5055
Phase 1.5: Fix undefined_getter errors
zensgit Sep 19, 2025
80d9075
fix(ci): separate rust-api-clippy job from field-compare
zensgit Sep 23, 2025
48b34a0
chore: CI fixes and Flutter analyzer cleanup phase 1.2 execution
zensgit Sep 23, 2025
cd2fbc3
api: update SQLx offline cache and fix clippy warnings
zensgit Sep 23, 2025
59a9497
api: cleanup SQLx cache - remove .gitkeep and obsolete query
zensgit Sep 23, 2025
65f63c0
fix: Flutter test failures - add dispose method to CurrencyNotifier
zensgit Sep 23, 2025
b07d686
fix: Flutter test navigation failure - simplified test to avoid Hive …
zensgit Sep 23, 2025
e2588d2
fix: Disable core_export feature in CI to avoid jive-core compilation…
zensgit Sep 23, 2025
17f78dc
fix: Conditionally compile audit handler imports to fix clippy warnings
zensgit Sep 23, 2025
c96c51b
ci: add SQLx diff PR comment; docs: add CI badge and SQLx offline guide
zensgit Sep 23, 2025
f186ad1
devx: add api-sqlx-prepare-local target; README tips; PR checklist te…
zensgit Sep 23, 2025
a94dd3e
feat: CI hardening and test improvements
zensgit Sep 23, 2025
3f1ba79
Merge branch 'main' into feat/ci-hardening-and-test-improvements
zensgit Sep 23, 2025
ce977dd
fix: Remove new test files causing CI failures
zensgit Sep 23, 2025
eb51808
test(api): add CSV newline/CRLF and empty optional fields assertions
zensgit Sep 23, 2025
68b09dc
resolve merge conflicts for #25: keep main UI, keep PR for other files
zensgit Sep 26, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
243 changes: 58 additions & 185 deletions .github/workflows/ci.yml

Large diffs are not rendered by default.

81 changes: 6 additions & 75 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,6 @@ help:
@echo " make logs - 查看日志"
@echo " make api-dev - 启动完整版 API (CORS_DEV=1)"
@echo " make api-safe - 启动完整版 API (安全CORS模式)"
@echo " make sqlx-prepare-core - 准备 jive-core (server,db) 的 SQLx 元数据"
@echo " make api-dev-core-export - 启动 API 并启用 core_export(走核心导出路径)"
@echo " make db-dev-up - 启动 Docker 开发数据库/Redis/Adminer (15432/16379/19080)"
@echo " make db-dev-down - 停止 Docker 开发数据库/Redis/Adminer"
@echo " make api-dev-docker-db - 本地 API 连接 Docker 开发数据库 (15432)"
@echo " make db-dev-status - 显示 Docker 开发数据库/Redis/Adminer 与 API 端口状态"
@echo " make metrics-check - 基础指标一致性校验 (/health vs /metrics)"
@echo " make seed-bcrypt-user - 插入一个 bcrypt 测试用户 (触发登录重哈希)"

# 安装依赖
install:
Expand Down Expand Up @@ -73,8 +65,8 @@ build-flutter:
test: test-rust test-flutter

test-rust:
@echo "运行 Rust API 测试 (SQLX_OFFLINE=true)..."
@cd jive-api && SQLX_OFFLINE=true cargo test --tests
@echo "运行 Rust 测试..."
@cd jive-core && cargo test --no-default-features --features server

test-flutter:
@echo "运行 Flutter 测试..."
Expand Down Expand Up @@ -152,17 +144,10 @@ api-sqlx-prepare-local:
@cd jive-api && cargo install sqlx-cli --no-default-features --features postgres || true
@cd jive-api && SQLX_OFFLINE=false cargo sqlx prepare

# Prepare SQLx metadata for jive-core (server,db)
sqlx-prepare-core:
@echo "准备 jive-core SQLx 元数据 (features=server,db)..."
@echo "确保数据库与迁移就绪 (优先 5433)..."
@cd jive-api && DB_PORT=$${DB_PORT:-5433} ./scripts/migrate_local.sh --force || true
@cd jive-core && cargo install sqlx-cli --no-default-features --features postgres || true
@cd jive-core && \
DATABASE_URL=$${DATABASE_URL:-postgresql://postgres:postgres@localhost:$${DB_PORT:-5433}/jive_money} \
SQLX_OFFLINE=false cargo sqlx prepare -- --features "server,db"
@echo "✅ 已生成 jive-core/.sqlx 元数据"

# Enable local git hooks once per clone
hooks:
@git config core.hooksPath .githooks
@echo "✅ Git hooks enabled (pre-commit runs make api-lint)"

# 启动完整版 API(宽松 CORS 开发模式,支持自定义端口 API_PORT)
api-dev:
Expand All @@ -173,60 +158,6 @@ api-dev:
api-safe:
@echo "启动完整版 API (安全 CORS 模式, 端口 $${API_PORT:-8012})..."
@cd jive-api && unset CORS_DEV && API_PORT=$${API_PORT:-8012} cargo run --bin jive-api
# 启动完整版 API(宽松 CORS + 启用 core_export,导出走 jive-core Service)
api-dev-core-export:
@echo "启动 API (CORS_DEV=1, 启用 core_export, 端口 $${API_PORT:-8012})..."
@cd jive-api && CORS_DEV=1 API_PORT=$${API_PORT:-8012} cargo run --features core_export --bin jive-api

# ---- Docker DB + Local API (Dev) ----
db-dev-up:
@echo "启动 Docker 开发数据库/Redis/Adminer (端口: PG=5433, Redis=6380, Adminer=9080)..."
@cd jive-api && docker-compose -f docker-compose.dev.yml up -d postgres redis adminer
@echo "✅ Postgres: postgresql://postgres:postgres@localhost:5433/jive_money"
@echo "✅ Redis: redis://localhost:6380"
@echo "✅ Adminer: http://localhost:9080"

db-dev-down:
@echo "停止 Docker 开发数据库/Redis/Adminer..."
@cd jive-api && docker-compose -f docker-compose.dev.yml down
@echo "✅ 已停止"

api-dev-docker-db:
@echo "本地运行 API (连接 Docker 开发数据库 5433; CORS_DEV=1, SQLX_OFFLINE=true)..."
@cd jive-api && \
CORS_DEV=1 \
API_PORT=$${API_PORT:-8012} \
SQLX_OFFLINE=true \
RUST_LOG=$${RUST_LOG:-info} \
DATABASE_URL=$${DATABASE_URL:-postgresql://postgres:postgres@localhost:5433/jive_money} \
cargo run --bin jive-api

db-dev-status:
@echo "🔎 Docker 开发栈容器状态 (postgres/redis/adminer):"
@docker ps --format '{{.Names}}\t{{.Status}}\t{{.Ports}}' | grep -E 'jive-(postgres|redis|adminer)-dev' || echo "(未启动)"
@echo ""
@echo "📡 建议的连接信息:"
@echo " - Postgres: postgresql://postgres:postgres@localhost:5433/jive_money"
@echo " - Redis: redis://localhost:6380"
@echo " - Adminer: http://localhost:9080"
@echo ""
@echo "🩺 API (本地) 端口状态:"
@lsof -iTCP:$${API_PORT:-8012} -sTCP:LISTEN 2>/dev/null || echo "(端口 $${API_PORT:-8012} 未监听)"
@echo ""
@echo "🌿 /health:"
@curl -fsS http://localhost:$${API_PORT:-8012}/health 2>/dev/null || echo "(API 未响应)"

# ---- Metrics & Dev Utilities ----
metrics-check:
@echo "运行指标一致性脚本..."
@cd jive-api && ./scripts/check_metrics_consistency.sh || true
@echo "抓取 /metrics 关键行:" && curl -fsS http://localhost:$${API_PORT:-8012}/metrics | grep -E 'password_hash_|jive_build_info|export_requests_' || true

seed-bcrypt-user:
@echo "插入 bcrypt 测试用户 (若不存在)..."
@cd jive-api && cargo run --bin hash_password --quiet -- 'TempBcrypt123!' >/dev/null 2>&1 || true
@psql $${DATABASE_URL:-postgresql://postgres:postgres@localhost:5433/jive_money} -c "DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM users WHERE email='bcrypt_test@example.com') THEN INSERT INTO users (email,password_hash,name,is_active,created_at,updated_at) VALUES ('bcrypt_test@example.com', crypt('TempBcrypt123!','bf'), 'Bcrypt Test', true, NOW(), NOW()); END IF; END $$;" 2>/dev/null || echo "⚠️ 需要本地 Postgres 运行 (5433)"
@echo "测试登录: curl -X POST -H 'Content-Type: application/json' -d '{\"email\":\"bcrypt_test@example.com\",\"password\":\"TempBcrypt123!\"}' http://localhost:$${API_PORT:-8012}/api/v1/auth/login"

# 代码格式化
format:
Expand Down
186 changes: 2 additions & 184 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,14 +86,6 @@ cp .env.example .env

2. 根据需要修改 `.env` 文件中的配置

### 本地端口与钩子(建议)
- 端口约定:本地 Docker/管理脚本默认映射 PostgreSQL 到 `5433`,Redis 到 `6380`,Adminer 到 `9080`;API 默认 `8012`。
- `jive-api/docker-compose.dev.yml` 已与 `jive-manager.sh` 对齐:`5433:5432`、`6380:6379`、`9080:8080`。
- 启用预提交钩子(保证本地提交即跑 SQLx 严格校验与 Clippy):
```bash
make hooks
```

## 🏗️ 项目结构

```
Expand Down Expand Up @@ -157,150 +149,6 @@ make db-migrate
# 查看日志
make logs

### Docker 数据库 + 本地 API(推荐开发流程)

```bash
# 1) 启动 Docker 开发数据库/Redis/Adminer(端口:PG=5433, Redis=6380, Adminer=9080)
make db-dev-up

# 2) 本地运行 API,连接 Docker 数据库(CORS_DEV=1, SQLX_OFFLINE=true, API 默认 8012)
make api-dev-docker-db

# 3) 健康检查
curl -s http://localhost:8012/health

# 4) 管理数据库(Adminer)
# 打开 http://localhost:9080 ,使用 postgres/postgres 登录,数据库 jive_money

# 5) 停止 Docker 开发栈
make db-dev-down
```

### JWT 密钥配置

环境变量 `JWT_SECRET` 用于签发与验证访问令牌:

```bash
export JWT_SECRET=$(openssl rand -hex 32)
```

未设置时(或留空)API 会在开发 / 测试自动使用一个不安全的占位并打印警告,不可在生产依赖该默认值。

### 监控与指标 (Metrics)

| Endpoint | 用途 | 认证 | 备注 |
|-------------|-------------------|------|------|
| `/health` | 探活 + 快照 | 否 | 轻量 JSON:hash 分布、rehash 状态、汇率指标等 |
| `/metrics` | Prometheus 拉取 | 否 | 文本格式指标(适合长期监控) |

规范指标(推荐使用):
```
password_hash_bcrypt_total # bcrypt (2a+2b+2y)
password_hash_argon2id_total # argon2id 数量
password_hash_unknown_total # 未识别前缀
password_hash_total_count # 总数
password_hash_bcrypt_variant{variant="2b"} X # 每个变体
jive_password_rehash_total # 成功重哈希次数(bcrypt→argon2id)
jive_password_rehash_fail_total # 重哈希失败次数(不会阻断登录)
jive_password_rehash_fail_breakdown_total{cause="hash"|"update"} # 重哈希失败按原因
export_requests_buffered_total # 缓冲导出请求次数(POST CSV/JSON)
export_requests_stream_total # 流式导出请求次数(GET CSV streaming, feature=export_stream)
export_rows_buffered_total # 缓冲导出累计行数
export_rows_stream_total # 流式导出累计行数
jive_build_info{...} # 构建信息 (value=1)
auth_login_fail_total # 登录失败(未知用户 / 密码不匹配)
auth_login_inactive_total # 非激活账号登录尝试
auth_login_rate_limited_total # 登录被速率限制次数 (429)
jive_build_info{commit,time,rustc,version} 1 # 构建信息 gauge
export_duration_buffered_seconds_* # 缓冲导出耗时直方图 (bucket/sum/count)
export_duration_stream_seconds_* # 流式导出耗时直方图 (bucket/sum/count)
process_uptime_seconds # 进程运行时长(秒)
jive_build_info{commit,time,rustc,version} 1 # 构建信息 gauge
```

兼容旧指标(DEPRECATED,将在 2 个发布周期后移除,详见 docs/METRICS_DEPRECATION_PLAN.md):
```
jive_password_hash_users{algo="bcrypt_2b"}
```

Prometheus 抓取示例:
```yaml
scrape_configs:
- job_name: jive-api
metrics_path: /metrics
scrape_interval: 15s
static_configs:
- targets: ["api-host:8012"]
```

一致性快速校验(bcrypt 聚合与 /metrics 是否匹配):
```bash
H=$(curl -s http://localhost:8012/health)
M=$(curl -s http://localhost:8012/metrics)
echo "Health bcrypt sum:" \
$(echo "$H" | jq '.metrics.hash_distribution.bcrypt | (."2a"+."2b"+."2y")')
echo "Metrics bcrypt total:" \
$(grep '^password_hash_bcrypt_total' <<<"$M" | awk '{print $2}')
```

运维建议:
- 大规模用户场景可为 hash 查询加 30s 内存缓存(计划中)。
- 迁移所有看板后移除旧的 jive_password_hash_users* 系列(目标 v1.2.0)。
- 监控 `jive_password_rehash_fail_total`,持续增长提示 DB 更新/并发异常。
- 导出耗时直方图示例:
```promql
# P95 缓冲导出耗时
histogram_quantile(0.95, sum(rate(export_duration_buffered_seconds_bucket[5m])) by (le))

# 最近 1 分钟流式导出平均耗时
sum(rate(export_duration_stream_seconds_sum[1m])) / sum(rate(export_duration_stream_seconds_count[1m]))
```

### 密码重哈希(bcrypt → Argon2id)

登录成功后,如检测到旧 bcrypt 哈希,系统会在 `REHASH_ON_LOGIN` 未显式关闭时(默认开启)尝试透明升级为 Argon2id:

```bash
# 关闭重哈希(例如压测环境需要保留原样)
export REHASH_ON_LOGIN=0
```

失败不会阻断登录,仅记录 warn 日志。设计说明见 `docs/PASSWORD_REHASH_DESIGN.md`。

### 超级管理员默认密码说明

仓库历史存在两个默认密码基线:

| 密码 | 出现来源 | 当前优先级 |
|------|----------|------------|
| `admin123` | 早期迁移:`005_create_superadmin.sql` / `006_update_superadmin_password.sql` / `016_fix_families_member_count_and_superadmin.sql` | 旧(可能仍在本地旧库残留) |
| `SuperAdmin@123` | 后续迁移:`009_create_superadmin_user.sql` 与补偿脚本 | 新(建议统一) |

实际生效取决于“最后一次在你的数据库中执行成功的迁移顺序”。如果你基于较新的全量迁移(包含 009 及之后)初始化数据库,默认应为 `SuperAdmin@123`(Argon2)。如果本地数据库较早创建,仍可能是 `admin123`(bcrypt 或 Argon2)。

判定与处理建议:
1. 直接尝试两次登录(先 `SuperAdmin@123`,再 `admin123`)。
2. 若均失败,可在本地用工具重置:
```bash
cargo run -p jive-money-api --bin hash_password -- SuperAdmin@123
# 得到哈希后:
psql "$DATABASE_URL" -c "UPDATE users SET password_hash='<HASH>' WHERE LOWER(email)='superadmin@jive.money';"
```
3. 重置后立即登录并修改为你的本地私有密码(不要提交哈希)。

注意事项:
- 重新“干净”初始化数据库(删除数据卷 / 新建数据库)后会再次回到迁移脚本指定的默认值。
- 请勿将生产环境实际超级管理员密码写入仓库或日志。
- 如果团队决定最终统一为 `SuperAdmin@123` 以外的基线,请新增新的迁移并在此表格中更新来源说明。

快速登录测试(假设使用新基线):
```bash
curl -s -X POST http://localhost:8012/api/v1/auth/login \
-H 'Content-Type: application/json' \
-d '{"email":"superadmin@jive.money","password":"SuperAdmin@123"}'
```
若返回 JSON 含 `token` 字段表示成功。生产中请务必改成强随机密码并限制暴露。

## 🧪 本地CI(不占用GitHub Actions分钟)

当你的GitHub Actions分钟不足时,可以使用本地CI脚本模拟CI流程:
Expand Down Expand Up @@ -331,14 +179,12 @@ make api-lint
CI 策略:
- 严格检查 `.sqlx` 与查询是否一致;若不一致:
- 上传 `api-sqlx-diff` 工件(含新旧缓存与 diff patch)
- 在 PR 自动评论首 80 行 diff 预览(仓库内 PR;Fork PR 仅 artifact)
- 失败退出,提示提交更新后的 `.sqlx/`
- 在 PR 自动评论首 80 行 diff 预览,便于定位
- 失败退出,提示开发者提交更新后的 `.sqlx/`

该脚本会:
- 尝试用 Docker 启动本地 Postgres/Redis(如已安装)
- 运行迁移、校验 SQLx 离线缓存(仅校验,不生成)
- 可选:配置 Docker Hub 认证以避免镜像拉取限流(公共镜像 postgres/redis 等)
- 参见 `.github/DOCKER_AUTH_SETUP.md`(添加 DOCKERHUB_USERNAME / DOCKERHUB_TOKEN Secrets)
- 运行 Rust 测试 + Clippy(警告视为错误)
- 运行 Flutter analyze(告警致命)与测试
- 将结果保存到 `./local-artifacts`
Expand All @@ -352,20 +198,6 @@ docker compose -f jive-api/docker-compose.db.yml up -d postgres
cd jive-api && ./prepare-sqlx.sh && cd ..
git add jive-api/.sqlx
git commit -m "chore(sqlx): update offline cache"

### CI 必要检查(main 分支保护)

当前 main 的 Required checks:

- `Flutter Tests`
- `Rust API Tests`
- `Rust API Clippy (blocking)`(`-D warnings`)
- `Rustfmt Check`(阻塞)
- `Cargo Deny Check`(安全与许可)

注意:
- PR 首次不稳定阶段,可将 `Cargo Deny` 保持非阻塞,但推荐尽快修复并转为阻塞。
- 本地建议:启用 git hooks(一次性):`make hooks`,自动在提交前执行 `make api-lint`。
```
```

Expand Down Expand Up @@ -593,17 +425,3 @@ MIT License
## 📞 联系

如有问题,请提交 Issue 或联系维护者。
环境变量 (Metrics & 安全):
```
AUTH_RATE_LIMIT=30/60 # 60 秒窗口内最多 30 次登录尝试 (默认 30/60)
AUTH_RATE_LIMIT_HASH_EMAIL=1 # 限流键中对 email 做哈希截断 (默认1)
ALLOW_PUBLIC_METRICS=1 # 设为 0 时启用白名单
METRICS_ALLOW_CIDRS=127.0.0.1/32 # 逗号分隔 CIDR 列表 (ALLOW_PUBLIC_METRICS=0 生效)
METRICS_DENY_CIDRS= # 可选拒绝 CIDR (deny 优先)
METRICS_CACHE_TTL=30 # /metrics 缓存秒数 (0 禁用)
```

Grafana 仪表板: `docs/GRAFANA_DASHBOARD_TEMPLATE.json`
Alert 规则示例: `docs/ALERT_RULES_EXAMPLE.yaml`
安全清单: `docs/SECURITY_CHECKLIST.md`
快速验证脚本: `scripts/verify_observability.sh`
4 changes: 4 additions & 0 deletions ci-artifacts-sqlx/api-clippy-output/api-clippy-output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
 Checking jive-money-api v1.0.0 (/home/runner/work/jive-flutter-rust/jive-flutter-rust/jive-api)
 Finished `dev` profile [optimized + debuginfo] target(s) in 9.76s
warning: the following packages contain code that will be rejected by a future version of Rust: sqlx-postgres v0.7.4
note: to see what the problems were, use the option `--future-incompat-report`, or run `cargo report future-incompatibilities --id 12`
Loading
Loading