3297 Commits

Author SHA1 Message Date
IanShaw027
b309822199 fix: tighten legacy payment provider resolution 2026-04-20 21:46:24 +08:00
IanShaw027
422f60a145 fix: normalize legacy wechat auth identity keys 2026-04-20 21:42:35 +08:00
mini
6328881801 feat(portal): mount PuroLocaleSwitcher in Landing/Docs/AuthLayout 2026-04-20 21:36:41 +08:00
IanShaw027
f65429145e fix: route legacy linuxdo users to account binding 2026-04-20 21:31:05 +08:00
mini
e711a20373 feat(i18n): add PuroLocaleSwitcher for portal pages 2026-04-20 21:24:51 +08:00
IanShaw027
5adefb466b fix: finalize oauth identity bindings 2026-04-20 21:24:33 +08:00
IanShaw027
bdcd3d87e5 fix: resolve unique legacy payment providers 2026-04-20 21:09:38 +08:00
IanShaw027
32059ae9d5 fix: backfill email identities on successful login 2026-04-20 20:58:19 +08:00
IanShaw027
9bebf1c1a6 feat: resolve payment results by resume token 2026-04-20 20:53:46 +08:00
IanShaw027
c0b24aefba feat: snapshot payment provider keys on orders 2026-04-20 20:47:14 +08:00
IanShaw027
e3f69e0246 fix: tighten webhook provider resolution 2026-04-20 20:42:01 +08:00
IanShaw027
7c7924e9fa fix: guard payment fulfillment provider mismatch 2026-04-20 20:31:19 +08:00
IanShaw027
97c9b992cb fix: require wechat unionid for oauth identity 2026-04-20 20:27:15 +08:00
erio
40d4e167cd feat(payment): i18n payment error codes and label localization
Pairs with the backend structured payment errors (reason + metadata). The
frontend now maps reason codes to localized messages with metadata as
interpolation variables, and automatically localizes raw config-field names
(e.g. "certSerial" → "证书序列号") using the existing UI-label i18n
namespace.

- frontend/src/utils/apiError.ts
  - extractApiErrorCode now prefers the string `reason` over the numeric HTTP
    `code`; reason is granular enough to drive i18n lookup, HTTP code is not.
  - New extractApiErrorMetadata to pull interpolation params off the error.
  - New extractI18nErrorMessage(err, t, namespace, fallback): looks up
    `<namespace>.<REASON>` in i18n and substitutes metadata. Before
    substitution, `metadata.key` and `metadata.keys` (slash-joined) are
    re-translated through `admin.settings.payment.field_<key>` so users see
    "缺少必填项:证书序列号" instead of "缺少必填项:certSerial".

- frontend/src/i18n/locales/{zh,en}.ts
  - Add payment.errors entries for every structured reason code returned by
    the backend (PAYMENT_DISABLED, INVALID_AMOUNT, TOO_MANY_PENDING,
    DAILY_LIMIT_EXCEEDED, NO_AVAILABLE_INSTANCE, PAYMENT_PROVIDER_MISCONFIGURED,
    WXPAY_CONFIG_MISSING_KEY / INVALID_KEY_LENGTH / INVALID_KEY, NOT_FOUND,
    FORBIDDEN, CONFLICT, INVALID_ORDER_TYPE, INVALID_STATUS,
    BALANCE_NOT_ENOUGH, REFUND_AMOUNT_EXCEEDED, REFUND_FAILED, and more),
    with placeholders for template variables.

- 13 payment-related Vue files
  - Migrate catch-block error reporting from extractApiErrorMessage to
    extractI18nErrorMessage(err, t, 'payment.errors', fallback).
  - Remove the ad-hoc paymentErrorMap computed in SettingsView.vue, which the
    new helper supersedes (it reads i18n directly via t).

- frontend/src/components/payment/providerConfig.ts
  - wxpay: publicKey and publicKeyId are now required (was optional), matching
    the pubkey-only verifier direction; certSerial is already required.

This PR is drop-in safe: reason-preferring extractApiErrorCode is backward
compatible with callers that pass their own i18nMap, and error codes missing
from i18n fall back to the existing message-based path.
2026-04-20 20:23:16 +08:00
IanShaw027
58b2cc380f test: harden payment result resume flow 2026-04-20 20:22:00 +08:00
erio
20a4e41872 feat(monitor): admin channel monitor MVP with SSRF protection and batch aggregation
新增 admin「渠道监控」模块(参考 BingZi-233/check-cx),独立于现有 Channel 体系。
admin 配置 + 后台定时调用上游 LLM chat completions 健康检查 + 所有登录用户只读可见。

后端:
- ent: channel_monitor + channel_monitor_history(AES-256-GCM 加密 api_key)
- service 按职责拆分:service/aggregator/validate/checker/runner/ssrf
- provider strategy map 替代 switch(openai/anthropic/gemini)
- repository batch 聚合(ListLatestForMonitorIDs + ComputeAvailabilityForMonitors)消除 N+1
- runner: ticker(5s) + pond worker pool(5) + inFlight 防并发 + TrySubmit 防雪崩
  + 凌晨 3 点 cron 清理 30 天历史
- SSRF 防护:强制 https + 私网/loopback/云元数据 IP 拒绝(127/8、10/8、172.16/12、
  192.168/16、169.254/16、100.64/10、::1、fc00::/7、fe80::/10)+ DialContext
  在 socket 层防 DNS rebinding
- API key sanitize:擦除 url.Error 与上游响应 body 中的 sk-/sk-ant-/AIza/JWT 模式
- APIKeyDecryptFailed 标志位 + 单 monitor 路径检测,避免空 key 调用上游

handler:
- admin: CRUD + 手动触发 + 历史接口(api_key 脱敏)
- user: 只读列表 + 状态详情(去除 api_key/endpoint)
- ParseChannelMonitorID 共用 + dto.ChannelMonitorExtraModelStatus 共用

前端:
- 路由 /admin/channels/{pricing,monitor} + /monitor(用户只读)
- AppSidebar 父项 expandOnly 支持
- ChannelMonitorView 拆为 8 个子组件 + ChannelStatusView 拆出 detail dialog
- composables/useChannelMonitorFormat + constants/channelMonitor 共享
- i18n monitorCommon namespace 消除 admin/user 两 view 重复

合规:所有文件符合 CLAUDE.md(Go ≤ 500 行 / Vue ≤ 300 行 / 函数 ≤ 30 行)
CI: go build / gofmt / golangci-lint(0 issues) / make test-unit / pnpm build 全绿
2026-04-20 20:21:02 +08:00
IanShaw027
b51bc7ee24 feat: wire payment return url payloads 2026-04-20 20:19:23 +08:00
IanShaw027
7826e9880c feat: support linuxdo pending bind 2fa callback 2026-04-20 19:53:22 +08:00
IanShaw027
fb6204ea8b feat: apply oauth first-bind defaults and pending bind 2fa 2026-04-20 19:53:22 +08:00
erio
79192cf65b feat(payment): harden wxpay config validation with structured errors
Motivation: platform-certificate mode is being phased out by WeChat (2024-10+,
newly-provisioned merchants already cannot download platform certificates at
all), and wxpay config errors currently surface only when an order is being
created — admins have no feedback at save time. Also, errors were returned as
natural-language strings, leaving the frontend no way to localize them.

Changes:

- backend/internal/payment/provider/wxpay.go
  - Replace fmt.Errorf with structured infraerrors.BadRequest errors:
    - WXPAY_CONFIG_MISSING_KEY    (metadata: key)
    - WXPAY_CONFIG_INVALID_KEY_LENGTH  (metadata: key, expected, actual)
    - WXPAY_CONFIG_INVALID_KEY    (metadata: key) for malformed PEMs
  - Parse privateKey and publicKey PEMs in NewWxpay so malformed keys fail
    at save time instead of at order creation.
  - Keep the pubkey verifier (NewSHA256WithRSAPubkeyVerifier) as the single
    supported verifier; no more loadKeyPair helper.

- backend/internal/service/payment_order.go invokeProvider
  - If CreateProvider or CreatePayment returns a structured ApplicationError,
    pass it through (optionally enriching metadata with provider/instance_id)
    instead of wrapping it as generic PAYMENT_GATEWAY_ERROR — so clients see
    the actual reason code (e.g. WXPAY_CONFIG_MISSING_KEY) and can localize.
  - Simplify a few messages (TOO_MANY_PENDING, DAILY_LIMIT_EXCEEDED,
    PAYMENT_GATEWAY_ERROR, NO_AVAILABLE_INSTANCE) to keyword form with
    metadata for template variables.

- backend/internal/service/payment_config_providers.go
  - New helper validateProviderConfig calls provider.CreateProvider at save
    time. Enabled instances are validated on both Create and Update so admins
    see config errors immediately in the dialog, not later at order creation.
  - Disabled instances are not validated (half-filled drafts are allowed).

- backend/internal/payment/provider/wxpay_test.go
  - Add generateTestKeyPair helper that produces valid RSA-2048 PKCS8/PKIX
    PEMs per test, used by the valid-config baseline (prior fake strings no
    longer pass the eager PEM check).
  - Cover each structured-error branch (missing/invalid-length/malformed PEM).
2026-04-20 19:49:45 +08:00
IanShaw027
6ea3f42e2f feat: add oauth callback email binding ui 2026-04-20 19:30:19 +08:00
IanShaw027
6a75bd77e3 feat: add pending oauth email onboarding flow 2026-04-20 19:30:09 +08:00
IanShaw027
d47580a144 test: pin email signup defaults in register tests 2026-04-20 18:42:28 +08:00
IanShaw027
0353c3870f test: update user service stubs for identity summaries 2026-04-20 18:40:34 +08:00
IanShaw027
4e0e691546 feat: apply auth source signup defaults 2026-04-20 18:39:53 +08:00
IanShaw027
c6d8592484 feat: add profile auth identity binding flow 2026-04-20 18:28:44 +08:00
IanShaw027
13d9780df4 feat: expose user activity timestamps in admin list 2026-04-20 17:48:30 +08:00
IanShaw027
e9de839d87 feat: rebuild auth identity foundation flow 2026-04-20 17:39:57 +08:00
IanShaw027
fbd0a2e3c4 feat: carry suggested third-party profile through pending oauth 2026-04-20 16:27:23 +08:00
IanShaw027
d3d4267731 fix: harden oidc callback security 2026-04-20 16:23:42 +08:00
IanShaw027
584ded2182 docs: harden auth identity payment design 2026-04-20 14:41:12 +08:00
IanShaw027
b6751f7ebc docs: add auth identity implementation plan 2026-04-20 13:47:00 +08:00
IanShaw027
721d7ab3ab docs: add audit synthesis to auth identity spec 2026-04-20 13:40:31 +08:00
IanShaw027
e01c1eaceb docs: add auth identity payment foundation design spec 2026-04-20 13:18:30 +08:00
mini
49ee2cba8a fix(docs): DocsView fidelity port (plan A)
Some checks failed
continuous-integration/drone/push Build is passing
CI / test (push) Has been cancelled
CI / golangci-lint (push) Has been cancelled
Security Scan / backend-security (push) Has been cancelled
Security Scan / frontend-security (push) Has been cancelled
Visual alignment with zip Docs.html (excluding 3-col layout):
- SVG hexagon logo (replaces ⬢ emoji)
- h2 cyan accent bar (::before 3px left strip)
- Models section: replace <ul> with structured table
  (provider badges with brand-color dots, OK/BETA status chips)
- Wrap all code blocks in .code-panel with:
  - traffic-light header + filename tab
  - 复制 button with clipboard API + 已复制 feedback

Kept intentionally different per Stage 1 decisions:
- Section 1 uses 'contact admin@puro.im' (not OAuth self-serve)
- Nav omits pricing / design-system links
- Codex CLI section preserved (Vue-only)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 23:19:46 +08:00
mini
e843a7aef8 fix: fidelity port of Landing/Login/Register from design zip
Some checks failed
continuous-integration/drone/push Build is passing
CI / test (push) Has been cancelled
CI / golangci-lint (push) Has been cancelled
Security Scan / backend-security (push) Has been cancelled
Security Scan / frontend-security (push) Has been cancelled
Merges 3 port commits bringing Vue views closer to Claude Design source:
- 04676563 LoginView: N/kicker/route-demo/n-bottom/SVG hex
- 9f78b70a RegisterView: N/kicker/steps panel/pw-strength/confirm-pw/terms
- 4cf68404 LandingView: badge/pills/9 bullets/SVG logos/traffic+tabs/donut/sidebar
2026-04-19 22:58:29 +08:00
mini
4cf6840479 fix(landing): LandingView fidelity port from design zip
A-group deltas restored (excluding Stage 1 decisions — no Pricing/FAQ/
CTA banner, kept existing Hero CTA copy):

- Nav + footer brand: SVG hexagon replaces ⬢ emoji
- Hero: add NEW badge in eyebrow; inline pills around OpenAI/Anthropic
- Section kickers: monospace // providers / // capabilities / etc
- Features: restored title "付一次订阅,用起一整个模型池" + subtitle
  + 9 bullet items (3 per card, dashed-border lists)
- Model wall: SVG letter-logos + green status chips (was plain dots)
- Code demo: traffic-light + tab header in each code-block
- Dashboard mockup: added sidebar nav + donut chart (chart-grid 2fr:1fr)
- Footer: Chinese product tagline; all-systems-operational indicator

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 22:58:07 +08:00
mini
9f78b70a87 fix(auth): RegisterView fidelity port from design zip
Port A-group deltas from design zip (excluding bonus/pricing which
are explicitly out of scope):
- Narrative: N (not 5) 个订阅; add '// 5 分钟开始用' n-kicker;
  SVG hexagon logo (was emoji); n-bottom live status bar
- Add 3-step onboarding panel (创建账户 → 绑定订阅 → 生成 key)
  in narrative, active-step highlighted
- Add password strength meter (4 bars + text label 弱/中/强/极强)
- Add confirm-password field with live // matched/mismatch hint
- Add Terms & Privacy consent checkbox (submit gated)
- New i18n keys: confirmPasswordLabel/Placeholder, passwordsDoNotMatch

All existing Vue logic preserved (OAuth/Turnstile/verify code/
invitation+promo codes).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 22:53:40 +08:00
mini
046765632d fix(auth): LoginView fidelity port from design zip
- Narrative: N (not 5) 个订阅, matching design intent
- Add '// 你的订阅,已经付过钱了' n-kicker above headline
- Port route-demo panel (POST /v1/chat/completions → pool → 200 OK)
- Port n-bottom live status bar (green dot + ai.puro.im operational)
- Replace ⬢ emoji with inline SVG hexagon (crisp at all sizes)

All Vue auth logic preserved: OAuth sections, Turnstile, 2FA modal,
forgot-password, form validation, v-model bindings.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 22:45:54 +08:00
mini
93481b8c45 fix(auth): restore dark split-layout visuals on /login /register
Some checks failed
continuous-integration/drone/push Build is passing
CI / test (push) Has been cancelled
CI / golangci-lint (push) Has been cancelled
Security Scan / backend-security (push) Has been cancelled
Security Scan / frontend-security (push) Has been cancelled
Three regressions from Task 7-9 caused /login /register to render broken:
- bg-glow not rendering: puro.css scopes .bg-glow to .puro-page,
  AuthLayout isn't inside one. Fix: duplicate bg-glow rules into
  AuthLayout scoped CSS keyed on .auth-shell-split.
- .auth-main had no background: right side showed naked body bg.
  Fix: .auth-shell-split now sets var(--bg-0) for whole shell.
- Heading/label colors used text-gray-900 light-mode classes,
  invisible on dark bg. Fix: switch to explicit text-slate-50/400,
  and :deep() override for form inputs via AuthLayout split scope.

Legacy (non-split) mode unaffected.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 22:23:11 +08:00
mini
6291dc40d0 fix(auth): restore dark split-layout visuals on /login /register
Three regressions from Task 7-9 caused /login /register to render broken:
- bg-glow not rendering: puro.css scopes .bg-glow to .puro-page,
  AuthLayout isn't inside one. Fix: duplicate bg-glow rules into
  AuthLayout scoped CSS keyed on .auth-shell-split.
- .auth-main had no background: right side showed naked body bg.
  Fix: .auth-shell-split now sets var(--bg-0) for whole shell.
- Heading/label colors used text-gray-900 light-mode classes,
  invisible on dark bg. Fix: switch to explicit text-slate-50/400,
  and :deep() override for form inputs via AuthLayout split scope.

Legacy (non-split) mode unaffected.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 22:22:25 +08:00
shaw
23def40bc5 chore: change license from MIT to LGPL v3.0 2026-04-19 22:06:04 +08:00
355370ad2a Merge pull request 'feat: PURO AI landing + auth + docs redesign' (#1) from feat/design-landing-auth into main
Some checks failed
continuous-integration/drone/push Build is passing
CI / test (push) Has been cancelled
CI / golangci-lint (push) Has been cancelled
Security Scan / backend-security (push) Has been cancelled
Security Scan / frontend-security (push) Has been cancelled
Release / update-version (push) Has been cancelled
Release / build-frontend (push) Has been cancelled
Release / release (push) Has been cancelled
Release / sync-version-file (push) Has been cancelled
feat: PURO AI landing + auth + docs redesign

Merges feat/design-landing-auth → main. Tasks 1-12 complete.
Triggers CI auto-deploy to ai.puro.im.
v0.2.0-puro-redesign
2026-04-19 13:58:25 +00:00
puro ci
829f101100 docs: tick local acceptance items [CI SKIP]
Some checks failed
CI / test (pull_request) Has been cancelled
CI / golangci-lint (pull_request) Has been cancelled
Security Scan / backend-security (pull_request) Has been cancelled
Security Scan / frontend-security (pull_request) Has been cancelled
CI / test (push) Has been cancelled
CI / golangci-lint (push) Has been cancelled
Security Scan / backend-security (push) Has been cancelled
Security Scan / frontend-security (push) Has been cancelled
11/12 checklist items verified via pnpm build + preview + curl.
Remaining: CI deploy + prod verification (Task 13).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 21:56:50 +08:00
mini
0fceb100e0 chore(i18n): consolidate PURO auth heading keys into zh.ts
Login and Register heading strings moved from hardcoded Chinese to
auth.puroLoginTitle / puroLoginSub / puroRegisterTitle / puroRegisterSub.

Landing (LandingView) and Docs (DocsView) intentionally keep hardcoded
Chinese this cycle (see spec §6 note 5 — English version deferred).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 21:54:49 +08:00
mini
7dc8062988 feat(docs): public DocsView with Codex/Claude Code/curl quickstart
Route /docs (no auth). Six sections: get key, codex CLI, claude code,
curl, supported models, feedback. Uses puro.css design system.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 21:42:08 +08:00
mini
f17a88c171 feat(auth): RegisterView with PURO narrative split layout
Same split layout as LoginView: left narrative, right form.
Heading: '创建账户' + '5 分钟开始用 PURO AI'.
All form logic preserved (OAuth, Turnstile, email verify code, password fields).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 21:29:08 +08:00
mini
91b9ae7e21 feat(auth): LoginView with PURO narrative split layout
- Left: ⬢ PURO AI brand + '5→1' headline + three-line value props
- Right: existing form (OAuth, Turnstile, 2FA all preserved unchanged)
- Heading changed from t('auth.welcomeBack') to '登录' — i18n key consolidation in Task 11

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 21:27:23 +08:00
mini
9ee99d17fd refactor(auth): AuthLayout supports optional narrative slot
New slot 'narrative' enables split-screen layout (50/50 desktop, collapses
to single column on mobile <900px).

Backward compatibility:
- Pages that don't pass a narrative slot still render the original
  centered-card layout with siteName + logo + copyright
- ForgotPassword, ResetPassword, EmailVerify unaffected

To be used in Tasks 8 and 9 (LoginView, RegisterView).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 21:25:01 +08:00
Wesley Liddick
f5ee93796d Merge pull request #1753 from touwaeriol/feat/fix-orphaned-scheduled-tests
fix: delete scheduled test plans when account is deleted
2026-04-19 21:14:23 +08:00