Skip to content

chore(subsite): drop parentSite fetch and switch canCreate to isOfficial gate#749

Closed
Germey wants to merge 3 commits into
mainfrom
chore/subsite-canCreate-isOfficial
Closed

chore(subsite): drop parentSite fetch and switch canCreate to isOfficial gate#749
Germey wants to merge 3 commits into
mainfrom
chore/subsite-canCreate-isOfficial

Conversation

@Germey
Copy link
Copy Markdown
Member

@Germey Germey commented May 16, 2026

Summary

The Subsite settings tab (src/components/setting/Subsite.vue) no longer
derives subdomainZone / maxSubsitesPerUser / canCreate from
$store.state.site.features.subsite. The parent Site row is fetched
elsewhere for branding purposes, but its subsite block isn't needed
here — subdomainZone is always the canonical Hub host
(BASE_HOST_HUB, e.g. studio.acedata.cloud), and the per-user quota
the backend enforces matches our default of 5.

Changes

  • src/components/setting/Subsite.vue
    • Drop the parentSite computed and its dependents (subdomainZone,
      maxSubsitesPerUser).
    • Add a local MAX_SUBSITES_PER_USER = 5 constant.
    • subdomainZone now returns BASE_HOST_HUB unconditionally.
    • Rewrite canCreate to isOfficial() && !isSubOfficial() && Boolean(user?.id) && items.length < MAX_SUBSITES_PER_USER — same gate shape as PlatformFrontend console/sites/Index.vue (PR #315).
    • Imports isOfficial, isSubOfficial from @/utils/is and BASE_HOST_HUB from @/constants.
  • change/@acedatacloud-nexior-a1b22f5a-ee2c-4499-ab9e-aba69ee0d718.json — beachball patch bump.

Why

This is the last leg of a three-PR cleanup that removes the
parent_site_id surface and the parent-site-row lookup as a
precondition for subsite UI gating:

# Repo PR What it does
A PlatformBackend #477 Stop writing metadata.parent_site_id; drop the proxy_cname resolver branch that consumed it. Quota count uses origin__endswith=.{parent.origin}.
B PlatformFrontend #315 Drop parent_site_id from ISiteMetadata / ISiteQuery / Advanced.vue descriptions; rewrite canCreate to use isOfficial().
C Nexior this PR Mirror the same isOfficial() gate; drop the parent.features.subsite lookup.

After all three land, parent_site_id is gone from the create / list
/ gate paths end-to-end. Existing rows are untouched; the field simply
stops being written or read.

Backwards compatibility

  • Existing subsites continue to load. The listing is fully scoped by
    (user_id, origin__endswith=.{BASE_HOST_HUB}), which is exactly what
    the old parent-derived subdomainZone resolved to in practice on
    studio.acedata.cloud. The leading dot excludes the parent and
    matches every <slug>.studio.acedata.cloud.
  • Subsite-tab visibility unchanged. The upstream Setting.vue gate
    already hides this tab on non-official hosts; the
    SectionNotice tone="official" banner also stays. The new
    !isSubOfficial() check is belt-and-suspenders so the Create button
    never lights up on a subsite even if the upstream gate is ever
    relaxed.
  • No router / route changes. This tab is invoked from the user
    settings dialog only.

Risks

  • The hardcoded MAX_SUBSITES_PER_USER = 5 no longer reads
    parent.features.subsite.max_subsites_per_user. The backend still
    enforces its own quota on POST /sites/, so a stale UI just gets a
    duplication / quota error from the API rather than silently
    over-creating — same as the PlatformFrontend side.
  • No new network calls. We're removing the implicit getAll of the
    parent site that this code used to depend on (it now isn't fetched
    here at all).

Verification

  • grep -rn 'parentSite\|maxSubsitesPerUser' src/ → no matches.
  • git log --oneline confirms one logical commit with the beachball
    change file alongside.

Switch fetchSubsites to push the parent-zone filter down to the new
PlatformBackend origin__endswith filter instead of fetching every
site the user owns and matching client-side on metadata.parent_site_id.

Why: the metadata-based filter silently hid superuser-created subsites
because PlatformBackend's superuser create fast path
(with_site_owner_defaults) does NOT stamp metadata.parent_site_id —
only the regular subsite-create path does. Users with subsites created
via the admin UI / shell saw an empty list.

  siteOperator.getAll({
    user_id,
    origin__endswith: `.${subdomainZone}`,
    ordering: '-created_at'
  })

The leading dot excludes the parent itself (studio.acedata.cloud)
while matching every subsite (<slug>.studio.acedata.cloud). We keep
loading parentSite for the subdomainZone / maxSubsitesPerUser /
canCreate computeds; only the listing query changes.

Also add origin__endswith + ordering to ISiteQuery so the operator
types match what we send.

Requires: AceDataCloud/PlatformBackend#474.
(user_id, origin__endswith=.{zone}) is already a closed set: the leading
dot in the suffix excludes the parent by DNS hierarchy, so the extra
client-side .filter(s => s.id !== parentId) can never trip. Keeping it
just re-introduces the parent_site_id mental model we're moving away from.

parentSite is still loaded, but only as the source of
parent.features.subsite (subdomainZone / maxSubsitesPerUser / canCreate)
— not as a filter.
…ial gate

The Subsite settings tab no longer derives `subdomainZone` /
`maxSubsitesPerUser` / `canCreate` from `$store.state.site.features.subsite`.
The parent Site row is fetched/seeded elsewhere purely for branding and
isn't actually needed here — `subdomainZone` is always the canonical Hub
host, and the per-user quota the backend enforces matches our default
of 5.

Changes
-------
* Replace `parentSite` computed + its dependents (`subdomainZone`,
  `maxSubsitesPerUser`) with constants from `@/constants` and a local
  `MAX_SUBSITES_PER_USER = 5`.
* Rewrite `canCreate` to gate on `isOfficial() && !isSubOfficial()` so
  the button is hidden on subsites themselves (which can't spawn further
  subsites) and on any non-Hub host — matching the upstream `Setting.vue`
  visibility gate.

Why
---
Pairs with:
* PlatformBackend #477 — stops writing `metadata.parent_site_id` and
  drops the proxy_cname resolver branch that consumed it.
* PlatformFrontend #315 — applies the same `isOfficial()`-based
  `canCreate` rewrite to the Sites console and removes the
  `parent_site_id` surface from `ISiteMetadata` / `ISiteQuery` /
  `Advanced.vue` descriptions.

After all three land, `parent_site_id` is gone from create / list /
gate paths end-to-end. Existing rows aren't touched; the field simply
stops being written/read.

Backwards compatibility
-----------------------
* Existing subsites continue to load — listing is fully scoped by
  `(user_id, origin__endswith=.{BASE_HOST_HUB})` which is exactly what
  the parent-derived `subdomainZone` resolved to in practice on
  `studio.acedata.cloud`.
* The `SectionNotice` "official-only hint" + upstream `Setting.vue`
  gate keep this tab from rendering on subsites, so the
  `!isSubOfficial()` check is belt-and-suspenders rather than the
  only line of defense.
@cloudflare-workers-and-pages
Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
nexior d191b18 Commit Preview URL

Branch Preview URL
May 16 2026, 12:41 AM

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d191b18205

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines 173 to 175
subdomainZone(): string {
const zone = this.parentSite?.features?.subsite?.subdomain_zone;
if (zone) return zone;
// Fall back to the bare current host so the create dialog can render
// even if `state.site` hasn't been hydrated with the subsite block yet.
if (typeof window !== 'undefined' && window.location?.host) {
return window.location.host.split(':')[0];
}
return this.parentSite?.origin || '';
},
maxSubsitesPerUser(): number {
const max = this.parentSite?.features?.subsite?.max_subsites_per_user;
return typeof max === 'number' && max > 0 ? max : 5;
return BASE_HOST_HUB;
},
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Use the studio host for subsite origins

On the only host where the Subsites tab is exposed (isMainOfficial() is studio.acedata.cloud), BASE_HOST_HUB resolves to hub.acedata.cloud (src/constants/endpoint.ts:4,9). Returning it here makes fetchSubsites() filter for origin__endswith=.hub.acedata.cloud and makes onSubmitCreate() post slug.hub.acedata.cloud, so existing *.studio.acedata.cloud subsites disappear and new creations target the wrong parent zone. Use the current/parent main host or a studio-specific constant instead of BASE_HOST_HUB.

Useful? React with 👍 / 👎.

Comment on lines 191 to +197
canCreate(): boolean {
return Boolean(this.subdomainZone) && Boolean(this.user?.id) && this.items.length < this.maxSubsitesPerUser;
return (
isOfficial() &&
!isSubOfficial() &&
Boolean(this.user?.id) &&
this.items.length < MAX_SUBSITES_PER_USER
);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Avoid classifying the studio host as a subsite

When this tab is opened on studio.acedata.cloud, isOfficial() is true but isSubOfficial() also returns true because it only compares window.location.host with BASE_HOST_HUB (hub.acedata.cloud). Since Setting.vue exposes the Subsites tab specifically on studio.acedata.cloud, the new !isSubOfficial() check keeps canCreate false for the intended host and disables the normal Create button for all users there. Gate this with the strict main-host helper or fix isSubOfficial() before using it here.

Useful? React with 👍 / 👎.

Base automatically changed from chore/subsite-list-by-origin-suffix to main May 16, 2026 04:36
@acedatacloud-dev
Copy link
Copy Markdown
Member

Closing this PR — after PR #746 squash-merged, the meaningful work in this PR (dropping the parent_site_id listing filter, scoping by origin__endswith) is already in main. What remained on this branch was only the d191b182 commit "drop parentSite fetch and switch canCreate to isOfficial gate", which I now think is a net regression rather than an improvement:

  • parentSite() was never a fetch — it just memoised this.$store.state.site, which the app already hydrates at boot. Removing it didn't save any roundtrip.
  • Hardcoding subdomainZone to BASE_HOST_HUB is the bug Codex flagged as P1 on Subsite.vue:175BASE_HOST_HUB is hub.acedata.cloud, but the subsite tab is gated by isMainOfficialHost to render only on studio.acedata.cloud. The current main reads state.site.features.subsite.subdomain_zone from the backend, which is correct.
  • Hardcoding MAX_SUBSITES_PER_USER = 5 also drops the per-user quota override that lives in state.site.features.subsite.max_subsites_per_user.
  • Using isOfficial() && !isSubOfficial() is the bug Codex flagged as P1 on Subsite.vue:197. isSubOfficial() only excludes BASE_HOST_HUB, so on the studio host where the tab actually renders it returns true, flipping the gate to false and disabling the Create button.

The companion PlatformFrontend cleanup (drop parent_site_id from ISiteQuery / ISiteMetadata / the Advanced-tab UI rows / i18n) is still useful and lives at AceDataCloud/PlatformFrontend#315. Nothing equivalent is left to do here on Nexior — parent_site_id was never modelled on the Nexior side.

Thanks to Codex for catching both bugs before they shipped.

@acedatacloud-dev acedatacloud-dev deleted the chore/subsite-canCreate-isOfficial branch May 16, 2026 04:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants