feat(enterprise-site): add WordPress support and scheduled auto-publish to sites
Frontend CI / Frontend (push) Successful in 4m17s
Backend CI / Backend (push) Failing after 6m42s

Add WordPress as an enterprise-site CMS type alongside pbootcms, and let
schedule tasks auto-publish generated articles to enterprise sites.

- WordPress connection/publisher service and tests; register wordpress
  media platform and relax cms_type CHECK constraint
- New publish_enterprise_site_targets JSONB column on schedule_tasks with
  array type constraint; thread targets through scheduler dispatch,
  prompt/KOL generation, and worker
- Admin UI: enterprise-site targets in generate/schedule/publish flows,
  KOL package form, MediaView, and i18n strings

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-09 19:44:24 +08:00
parent 41f5623791
commit c7bad83496
30 changed files with 2946 additions and 696 deletions
@@ -67,6 +67,30 @@ func TestEnterpriseSiteUpdateRealDatabaseRepro(t *testing.T) {
}
}
func TestValidateEnterpriseSiteCMSURLRequiresHTTPSForRemoteWordPress(t *testing.T) {
if err := validateEnterpriseSiteCMSURL("http://example.com", wordpressPlatformID); err == nil {
t.Fatalf("remote wordpress http url should be rejected")
}
if err := validateEnterpriseSiteCMSURL("http://127.0.0.1:6060", wordpressPlatformID); err != nil {
t.Fatalf("local wordpress http url should be allowed for development: %v", err)
}
}
func TestNormalizeEnterpriseSiteURLRejectsReservedTargets(t *testing.T) {
for _, raw := range []string{
"http://169.254.169.254",
"http://10.0.0.8",
"http://[fe80::1]",
} {
if _, err := normalizeEnterpriseSiteURL(raw); err == nil {
t.Fatalf("reserved site url %q should be rejected", raw)
}
}
if got, err := normalizeEnterpriseSiteURL("http://127.0.0.1:6060"); err != nil || got != "http://127.0.0.1:6060" {
t.Fatalf("local loopback url = %q, %v; want allowed", got, err)
}
}
func TestEnterpriseSiteDescriptionUsesRenderedHTMLText(t *testing.T) {
got := enterpriseSiteDescription(
"<h2>2026年合肥全屋定制行业发展现状</h2><p>消费者需求升级。</p>",
@@ -452,6 +476,18 @@ func TestEnterpriseSiteNormalizeLegacyPublishErrorStateKeepsPingError(t *testing
}
}
func TestSanitizeEnterpriseSiteErrorTranslatesWordPressAuthFailure(t *testing.T) {
got := sanitizeEnterpriseSiteError(&wordpressRequestError{
message: "wordpress wp/v2/users/me returned http 401: rest_not_logged_in: 您目前没有登录。",
status: 401,
code: "rest_not_logged_in",
})
want := "WordPress 认证失败,请使用 WordPress 用户名和 Application Password"
if got != want {
t.Fatalf("sanitize error = %q, want %q", got, want)
}
}
type enterpriseSiteImageObjectStorage struct {
content []byte
gotKey string