Skip to content

feat(appconfig): admin 可调的 disable_user_create_space 开关#4

Open
an9xyz wants to merge 6 commits into
mainfrom
appconfig-disable-user-create-space
Open

feat(appconfig): admin 可调的 disable_user_create_space 开关#4
an9xyz wants to merge 6 commits into
mainfrom
appconfig-disable-user-create-space

Conversation

@an9xyz
Copy link
Copy Markdown
Collaborator

@an9xyz an9xyz commented May 28, 2026

Summary

把「禁用普通用户创建空间」从 env-only (DM_SPACE_DISABLE_USER_CREATE) 升级为
system_setting KV 表驱动,SuperAdmin 可在管理台实时切换、无需重启;
v1/common/appconfig 同步返回新字段 disable_user_create_space 给客户端
按需隐藏「创建空间」入口。

改动

  • common
    • SpaceDisableUserCreate() getter:DB → env → false 三级回落,DB 路径委托
      getBool 与其它 bool 设置共用解析规则
    • systemSettingSchema 新增一行 (space, disable_user_create, bool)
      不 ALTER TABLE,admin UI 自动渲染
    • appConfigResp.disable_user_create_space 在 handler 两个分支(含 version
      短路)都下发,避免老客户端被缓存住
  • space
    • createSpace 改走 (*Space).isUserCreateDisabled() → SystemSettings
    • 保留 IsUserCreateDisabled() + env 常量作 fallback / 老调用方兼容

设计要点

  • 单一真源:DB 写入立即 Reload() 本实例生效;其它实例 60s 内自动 reload 收敛
  • 多写少读:高频 createSpace 路径走内存快照,零额外 DB 查询
  • 向后兼容:原 env 仍有效;DB 缺行时 appconfig 字段默认 0

前端对接

  • 客户端:GET /v1/common/appconfig → 读 disable_user_create_space(0/1)
  • 管理端:POST /v1/manager/common/system_setting,items 含
    {category: "space", key: "disable_user_create", value: "0|1"}

Test plan

  • go test -race ./modules/common/ — 通过
  • go test -race ./modules/space/ — 通过
  • 单测覆盖 getter 全部 fallback 分支(默认 / DB 1 / DB 0 压制 env / DB ""
    回落 env / env 正负样例)
  • appconfig HTTP 单测覆盖默认 / DB 覆盖 / version 短路三分支
  • space HTTP 单测覆盖 DB 开关 ON 时 createSpace 返回 403、不入库
  • e2e:manager 写 → appconfig 读 → createSpace 拒绝/通过 全链路用例
  • go-reviewer 复审通过(H1/H2 已修)

lml2468 and others added 4 commits May 28, 2026 10:08
…ininglamp-OSS#177)

This PR adds the `edited` event to the `pull_request_target.types`
trigger in both `check-sprint.yml` and `auto-add-to-project.yml`,
enabling the Issue-first Sprint check flow to work correctly when a
developer adds a `Closes #<issue>` reference to a PR description after
opening.

Co-authored-by: Octo Bot <bot@mininglamp.com>
…ininglamp-OSS#184)

Two changes for local ASR config reset flow:

1. **Add POST /local-config/reset proxy endpoint** - proxies reset
request to octo-speech service, preserving enabled state while resetting
other fields to defaults.

2. **Fix 404 handling** - when octo-speech returns 404 (config not
found), voice adapter now returns 200 with empty/default response
instead of propagating the error, since 'not found' means 'using
defaults'.


Closes Mininglamp-OSS#185
## Summary

Adds the source-of-truth marker generator that Phase 2 module rollouts
depend on. Without 100% recall here, every Phase 2 PR has to either
hand-author TOML keys (drift risk) or accept silent runtime
`DefaultMessage` fallback (translation gap).

This PR is pure tooling — no runtime code changes.

## What ships

- **`pkg/i18n/cmd/octo-i18n-extract`** — Go AST extractor that walks
  `pkg/i18n/codes` and `pkg/errcode`, recognizing both
  `Register(Code{...})` and `register(codes.Code{...})` call shapes.
  Strict by design:
  - `ID` and `DefaultMessage` MUST be basic string literals; computed
    values are rejected at extraction time, not papered over.
  - Duplicate IDs are a hard error (across files and across roots).
  - Test files and dot/underscore-prefixed dirs are skipped.

- **100% recall guarantee** — `main.go` side-effect imports `codes` +
  `errcode` and asserts the AST-discovered ID set equals `codes.All()`
  exactly. Missing-from-AST or extra-only-in-AST surfaces a sorted diff
  and a non-zero exit code, so a `Register` call that `init()` somehow
  skipped (build tag, dead branch) cannot pass silently.

- **Marker routing by ID prefix** (architecture decision D18):
  - `err.shared.*` → `tools/i18nmarkers/shared/active.en-US.toml`
- `err.server.*` / `msg.*` →
`tools/i18nmarkers/server/active.en-US.toml`
  - Unknown prefixes are rejected so adding a third namespace forces an
    explicit routing decision plus a `codes/registry.go` `idPattern`
    update.

- **Deterministic output** — sorted keys, `strconv`-quoted strings,
  fixed header. Re-running the extractor on an unchanged repo produces
  byte-identical files. `WriteMarkerFile` only writes when content
  differs; `-check` mode reports diffs via exit code 3 for CI use.

- **Makefile entries**:
  - `make i18n-extract` — regenerate marker files
  - `make i18n-extract-check` — CI guard (no writes; exit 3 on diff)
  - `make i18n-merge` — optional convenience that shells out to the
    upstream `goi18n` CLI

## Tests

Table-driven, all green under `-race`:

- `Register` / `register` call detection across both shapes
- Non-`Code` composite literals ignored
- Non-string-literal fields rejected
- Duplicate ID rejected
- Test files skipped
- Prefix routing including unknown-prefix error
- `RenderTOML` stability + escape correctness
- `WriteMarkerFile` idempotency (second write reports unchanged)

## Out of scope

CI integration (the lint job that fails PRs which forget to re-run
`extract`) lands in the §0.10 lint batch alongside the other 5 lint
rules. This PR ships the extractor + Makefile entrypoints only.

## Test plan

- [x] `go test -race ./pkg/i18n/cmd/octo-i18n-extract/`
- [x] `go vet ./pkg/i18n/cmd/octo-i18n-extract/`
- [x] `make i18n-extract` → writes 9 + 18 markers
- [x] `make i18n-extract` (second run) → reports unchanged
- [x] `make i18n-extract-check` → exit 0 on clean tree
- [x] `go test ./pkg/i18n/... ./pkg/errcode/...` (no regressions)

---------

Co-authored-by: an9xyz <an9xyz@users.noreply.github.com>
…glamp-OSS#187)

Add two new caller workflows per PR notification split design. Reusable
workflows in .github PR Mininglamp-OSS#49.

---------

Co-authored-by: Octo Bot <bot@mininglamp.com>
@github-actions github-actions Bot added the size/L PR size: L label May 28, 2026
an9xyz added 2 commits May 28, 2026 20:34
…etting

Promote the "禁用普通用户创建空间" toggle from env-only (DM_SPACE_DISABLE_USER_CREATE)
to a system_setting KV-backed value so SuperAdmin can switch it at runtime
without redeploying.

- common: SpaceDisableUserCreate getter (DB → env → false), schema entry
  (space, disable_user_create, bool); appConfigResp.disable_user_create_space
  surfaced on both /v1/common/appconfig branches (incl. version short-circuit).
- space: createSpace now consults SystemSettings; legacy env path preserved
  as fallback / low-level parser.
- tests: unit (getter fallback chain incl. DB empty), HTTP (appconfig default
  / DB override / version short-circuit), createSpace 403 on DB-set, plus
  end-to-end (manager write → appconfig → createSpace gate).
Jerry-Xin pointed out (PR Mininglamp-OSS#189 review) that the docstring claimed an
unknown DB literal falls back to env, but the implementation delegates
to getBool which falls back to false. Align the doc with the actual
behavior; behavior unchanged.
@an9xyz an9xyz force-pushed the appconfig-disable-user-create-space branch from 560400c to 3b0547c Compare May 28, 2026 12:34
@github-actions github-actions Bot added size/XL PR size: XL and removed size/L PR size: L labels May 28, 2026
an9xyz added a commit that referenced this pull request May 29, 2026
…n gates

## Summary

Phase 0 close-out: fills the three remaining §0.9 verification scenarios
that lacked explicit coverage, and lands the first batch of §0.10 CI
gates that Phase 2 module migrations depend on. No runtime code changes
— everything is tests, two AST lint tools under `tools/`, and CI wiring.

This is a follow-up to the i18n migration pilots (`modules/thread` PR
Mininglamp-OSS#176, `modules/user/api.go` PR Mininglamp-OSS#188).

## Commits

1. **`test(i18n): close Phase 0 §0.9 verification gaps`**
- `pkg/i18n/renderer_test.go`: TransportStatus≠SemanticStatus divergence
(D14 — wire/body status fixed at 400, `error.http_status` carries the
real semantic status); dual-envelope parity regardless of the
`X-Octo-Error-Envelope` request header (v7.2/D12 — byte-identical body
with and without the header).
- `modules/user/language_multidevice_test.go`: multi-device language
convergence (§0.9 "A 端切语言 B 端下次请求即生效") plus the clear-to-default
inverse. `fakeLangDB.UpdateLanguageByUID` now writes through to its read
map so read-after-write is observable; existing tests assert on the
separate `updates` map and are unaffected.

2. **`feat(lint): add D23 direct-error-response ratchet (0.10 Mininglamp-OSS#5)`**
- `tools/lint-direct-error-response`: AST-counts `c.AbortWithStatusJSON`
/ `c.AbortWithStatus` per non-test file and compares against a committed
`baseline.txt`. **Baseline ratchet, not a hard block** — the Phase 0.1
inventory found ~120 pre-existing sites in unmigrated modules, so a
zero-gate would red-light main. The lint fails only on regressions (a
file exceeding its count, or a new file introducing any) and emits a
tighten-advisory when a migrated file drops below baseline. Per-file
counts (not line numbers, which churn); documented net-zero-churn
limitation.

3. **`feat(lint): block inline codes bypassing the registry (0.10 #3)`**
- `tools/lint-unregistered-code`: forbids inline `codes.Code{...}`
literals as the `httperr.ResponseErrorL` code argument. Such a literal
never passes `codes.Register`, so the 0.8 extractor emits no marker and
`respond.go` silently downgrades it to `err.shared.internal`.
Single-file AST scope is documented (two-step var smuggle is still
caught at runtime + by the 0.8 recall check).

4. **`ci(i18n): wire Phase 0.10 extract-check + lint gates`**
- Two CI jobs following the existing `personal-msgsendreq-lint`
template: "i18n Extract Check" (`make i18n-extract-check`, closes the
last open Phase 0.8 item) and "i18n Lint" (the two new gates). Adds a
`make i18n-lint` target for local runs.

## Phase 0.10 scope note

This PR lands lint **#1 (extract diff)**, **#3 (unregistered code)**,
**Mininglamp-OSS#5 (AbortWithStatusJSON)**. The remaining 0.10 gates — #4 (Params
sensitive-key), Mininglamp-OSS#6 (token-cache split), #2 (bare-string) — and the
Prometheus counters follow in a sibling PR, to keep this reviewable in
one sitting.

## Test plan

- [x] `go test -race -count=1 ./pkg/i18n/
./tools/lint-direct-error-response/ ./tools/lint-unregistered-code/` —
pass
- [x] `go test -race -count=1 ./modules/user/` (full DB E2E,
drop+recreate test DB + FLUSHALL) — pass
- [x] `make i18n-extract-check` / `make i18n-lint` — all green against
current tree
- [x] `go build ./...` / `go vet ./tools/... ./pkg/i18n/...` — clean
- [x] CI YAML validated

## Baseline note for reviewers

`tools/lint-direct-error-response/baseline.txt` is seeded from the
2026-05-29 snapshot (17 files). It deliberately still tolerates the 8
`AbortWithStatusJSON` sites in `modules/user/api.go` — those are the
avatar-permission / bot-token direct responses that PR Mininglamp-OSS#188 left as a
documented follow-up (PR Mininglamp-OSS#188 migrated `ResponseError`/`ResponseErrorf`,
not `AbortWithStatusJSON`). As each module migrates, drop its row.

---------

Co-authored-by: an9xyz <an9xyz@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/XL PR size: XL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants