-
Notifications
You must be signed in to change notification settings - Fork 514
chore(subsite): drop parentSite fetch and switch canCreate to isOfficial gate #749
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
3fafb49
9d7a690
d191b18
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| { | ||
| "type": "patch", | ||
| "comment": "chore(subsite): list by origin suffix from backend (origin__endswith)", | ||
| "packageName": "@acedatacloud/nexior", | ||
| "email": "dev@acedata.cloud", | ||
| "dependentChangeType": "patch" | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| { | ||
| "type": "patch", | ||
| "comment": "chore(subsite): drop dead parent-id defensive filter", | ||
| "packageName": "@acedatacloud/nexior", | ||
| "email": "dev@acedata.cloud", | ||
| "dependentChangeType": "patch" | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| { | ||
| "type": "patch", | ||
| "comment": "chore(subsite): drop parentSite dependence and switch canCreate to isOfficial gate", | ||
| "packageName": "@acedatacloud/nexior", | ||
| "email": "dev@acedata.cloud", | ||
| "dependentChangeType": "patch" | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -98,9 +98,16 @@ import { Plus } from '@element-plus/icons-vue'; | |
| import { siteOperator } from '@/operators'; | ||
| import SectionNotice from '@/components/setting/SectionNotice.vue'; | ||
| import type { ISite } from '@/models'; | ||
| import { isOfficial, isSubOfficial } from '@/utils/is'; | ||
| import { BASE_HOST_HUB } from '@/constants'; | ||
|
|
||
| const SLUG_RE = /^(?!.*--)[a-z0-9][a-z0-9-]{1,30}[a-z0-9]$/; | ||
|
|
||
| /** Maximum number of subsites a single user can spin up. Mirrors the | ||
| * default the backend's subsite-create path enforces when the parent | ||
| * Site row hasn't been seeded with an explicit per-user quota. */ | ||
| const MAX_SUBSITES_PER_USER = 5; | ||
|
|
||
| /** | ||
| * Settings tab — manage white-label subsites for the parent (official) | ||
| * Site row. Logic mirrors what used to live at the standalone | ||
|
|
@@ -153,28 +160,41 @@ export default defineComponent({ | |
| }; | ||
| }, | ||
| computed: { | ||
| parentSite(): ISite | undefined { | ||
| return this.$store.state.site; | ||
| }, | ||
| user() { | ||
| return this.$store.getters.user; | ||
| }, | ||
| /** | ||
| * DNS suffix new subsites are spawned under — always the canonical | ||
| * Hub host (`BASE_HOST_HUB`, e.g. `studio.acedata.cloud`). The backend's | ||
| * subsite-create path pins ``origin = f"{slug}.{parent.origin}"`` against | ||
| * this same value, so we don't need to fetch the parent Site row just to | ||
| * read it back from ``parent.features.subsite.subdomain_zone``. | ||
| */ | ||
| 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; | ||
| }, | ||
| /** | ||
| * Whether the current user can spin up another subsite right now. | ||
| * | ||
| * * Must be on the canonical Hub host (`isOfficial()`), | ||
| * * NOT on a subsite under it (`!isSubOfficial()`) — subsites can't | ||
| * themselves spawn further subsites, | ||
| * * Must be signed in (`user.id`), | ||
| * * Must be under the per-user quota. | ||
| * | ||
| * Mirrors PlatformFrontend's Sites console gate; replaces the previous | ||
| * `parent.features.subsite.{enabled,subdomain_zone}` lookup which required | ||
| * fetching the parent Site row just to gate one button + read back a | ||
| * value (`BASE_HOST_HUB`) that's already a build-time constant on this | ||
| * side of the wire. | ||
| */ | ||
| 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 | ||
| ); | ||
|
Comment on lines
191
to
+197
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When this tab is opened on Useful? React with 👍 / 👎. |
||
| } | ||
| }, | ||
| watch: { | ||
|
|
@@ -198,17 +218,29 @@ export default defineComponent({ | |
| this.items = []; | ||
| return; | ||
| } | ||
| const zone = this.subdomainZone; | ||
| if (!zone) { | ||
| // SSR / pre-mount path — BASE_HOST_HUB is a constant so this branch | ||
| // is effectively dead, but keep the guard cheap so we never POST | ||
| // a query with a blank suffix filter and end up listing every site | ||
| // the user owns under any host. | ||
| this.items = []; | ||
| return; | ||
| } | ||
| this.loading = true; | ||
| try { | ||
| const { data } = await siteOperator.getAll({ user_id: userId }); | ||
| const all = (data?.items || []) as ISite[]; | ||
| const parentId = this.parentSite?.id; | ||
| this.items = all.filter((s) => { | ||
| if (s.id === parentId) return false; | ||
| const meta = (s.metadata || {}) as Record<string, unknown>; | ||
| if (parentId && meta.parent_site_id) return meta.parent_site_id === parentId; | ||
| return Boolean(s.origin && this.subdomainZone && s.origin.endsWith(`.${this.subdomainZone}`)); | ||
| // Listing is fully scoped by (user_id, origin__endswith=.{zone}). | ||
| // The leading dot excludes the parent (`studio.acedata.cloud`) by | ||
| // DNS-hierarchy semantics and matches every subsite | ||
| // (`<slug>.studio.acedata.cloud`). No `parent_site_id` needed — | ||
| // the superuser fast path doesn't stamp `metadata.parent_site_id` | ||
| // anyway, which is why the previous metadata filter hid rows. | ||
| const { data } = await siteOperator.getAll({ | ||
| user_id: userId, | ||
| origin__endswith: `.${zone}`, | ||
| ordering: '-created_at' | ||
| }); | ||
| this.items = (data?.items || []) as ISite[]; | ||
| } catch (e) { | ||
| console.error('failed to load subsites', e); | ||
| ElMessage.error(this.$t('subsite.message.loadFailed')); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On the only host where the Subsites tab is exposed (
isMainOfficial()isstudio.acedata.cloud),BASE_HOST_HUBresolves tohub.acedata.cloud(src/constants/endpoint.ts:4,9). Returning it here makesfetchSubsites()filter fororigin__endswith=.hub.acedata.cloudand makesonSubmitCreate()postslug.hub.acedata.cloud, so existing*.studio.acedata.cloudsubsites disappear and new creations target the wrong parent zone. Use the current/parent main host or a studio-specific constant instead ofBASE_HOST_HUB.Useful? React with 👍 / 👎.