mirror of
https://github.com/iflytek/skillhub.git
synced 2026-09-14 23:21:08 +00:00
Self-hosted deployments want every new account to have somewhere of its
own to publish, without asking an administrator for a namespace first and
without pushing drafts into `global`.
Add an operator-controlled policy, off by default so upgrading changes no
behaviour. When enabled, an account that becomes usable gets a namespace
it owns. "Private" here means a team namespace whose only member is that
account: namespaces have no visibility flag, and skill visibility stays a
property of each skill.
Trigger points. UserActivatedEvent is published wherever an account first
becomes usable:
- LocalAuthService.register
- IdentityBindingService.bindOrCreate, for ACTIVE first logins
- AdminUserAppService.updateUserStatus, on a transition into ACTIVE
The third matters for deployments that gate access behind approval: those
accounts are created PENDING at the first OAuth attempt and only become
usable when an administrator approves them.
Why an AFTER_COMMIT listener rather than a call alongside
GlobalNamespaceMembershipService.ensureMember. Both namespace.created_by
and namespace_member.user_id reference user_account(id), which rules out
each obvious alternative:
- Joining the registration transaction lets a slug clash roll the
registration back, so a namespace failure costs the user their account
— or, on OAuth, their login.
- Suspending it with REQUIRES_NEW leaves the new transaction unable to see
the uncommitted user_account row, so the foreign key check blocks on the
outer transaction's row lock and the two wait on each other.
Provisioning after commit avoids both. The listener is deliberately not
@Async, so the namespace exists by the time the user's next request
arrives, and it swallows failures.
Naming. Two templates over ${username}, ${email_prefix} and ${user_id};
unknown placeholders are left in place so a typo is visible rather than
silently dropped. ${username} falls back to the email local part and then
to the user id. Slugs go through the existing slug rules, which is why the
console renders a live preview: underscores are not legal in a slug, so
`${username}_space` yields `alice-space`. A taken or reserved candidate
gets a numeric suffix, so `admin` becomes `admin-2`. Owning any non-global
namespace already skips provisioning, which keeps re-enabling an account
from handing out a second one.
The templates are not exposed in application.yml: they contain ${...},
which Spring would resolve as property references, and Boot 3.2 predates
placeholder escaping. Only the enable flag lives there; templates are set
in the console and default from PersonalNamespaceProvisioningProperties.
Updating the policy writes an audit entry with the before and after.
|
||
|---|---|---|
| .. | ||
| docker-entrypoint.d | ||
| e2e | ||
| public | ||
| src | ||
| .browserslistrc | ||
| .dockerignore | ||
| .eslintrc.cjs | ||
| base-path-config.test.ts | ||
| base-path-config.ts | ||
| components.json | ||
| Dockerfile | ||
| index.html | ||
| LANDING_PAGE_REDESIGN.md | ||
| nginx.conf.template | ||
| package.json | ||
| playwright.config.ts | ||
| playwright.smoke.config.ts | ||
| pnpm-lock.yaml | ||
| postcss.config.js | ||
| PREVIEW.md | ||
| runtime-config.js.template | ||
| tailwind.config.ts | ||
| TODO.md | ||
| tsconfig.json | ||
| vite.config.ts | ||