mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-19 07:01:23 +02:00
feat(spaces): move access tier from user to space
Migration from user-level tier to Space-level tier, following the
Spaces foundation plan. User-visible effect: the tier that gates
module access now belongs to the active Space, not the user account.
Personal Spaces inherit the user's old tier on signup so nothing
downgrades.
shared-types:
- New SpaceTier type ('guest' | 'public' | 'beta' | 'alpha' | 'founder').
- New spaceTierMeets(actual, required) helper.
- SpaceMetadata gains an optional `tier` field.
mana-auth:
- createPersonalSpaceFor reads user.accessTier and stamps it into the
personal Space's metadata.tier. A founder-tier user setting up their
first Space keeps founder access in that Space.
- databaseHooks.user.create.after now forwards accessTier into the
personal-space creator.
apps/web (scope layer):
- ActiveSpace gains a required `tier: SpaceTier`; rawToActiveSpace
reads it from organization.metadata, defaulting to 'public' if
missing or invalid.
- New getEffectiveTier(userFallback) helper resolves the tier to use
for gating: prefers the active Space's tier, falls back to the
caller-supplied user tier during the boot window.
apps/web ((app) layout):
- `effectiveTier` $derived replaces every authStore.user?.tier reference
in the layout's access-gating logic (appItems, routeBlocked,
routeTierLabels). AuthGate deeper in the UI keeps using user.tier as
its own fallback — the tier move is additive, not destructive.
What this does NOT do yet:
- The user.accessTier column still exists and is still the initial
source for personal-space tier. Removing it is a later cleanup once
every code path reads through the Space primitive.
- No admin API for setting tier on a Space (PUT /api/v1/admin/spaces/
:id/tier). Follow-up when admin tooling needs it — today admins still
set user.accessTier, which flows to the personal space on next
signup.
Resolves the MANA_APPS-tier-patch workaround memory: future sessions
can adjust tier per Space instead of per User.
0 errors across 7151 files. 10/10 scope tests pass.
Plan: docs/plans/spaces-foundation.md
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
88e3adb9d3
commit
79a6da3e2e
8 changed files with 117 additions and 15 deletions
|
|
@ -203,8 +203,55 @@ export function isModuleAllowedInSpace(moduleId: SpaceModuleId, spaceType: Space
|
|||
* for our Space extension. `type` is required; other fields accumulate as
|
||||
* features land (voiceDoc, legalEntity, uid, aiPersonaId, …).
|
||||
*/
|
||||
/**
|
||||
* The access tiers a Space can have. Gates module access via
|
||||
* `requiredTier` on each ManaApp.
|
||||
*
|
||||
* Ordered from least to most access. A higher tier implies access to
|
||||
* everything a lower tier can reach.
|
||||
*/
|
||||
export type SpaceTier = 'guest' | 'public' | 'beta' | 'alpha' | 'founder';
|
||||
|
||||
export const SPACE_TIERS: readonly SpaceTier[] = [
|
||||
'guest',
|
||||
'public',
|
||||
'beta',
|
||||
'alpha',
|
||||
'founder',
|
||||
] as const;
|
||||
|
||||
const TIER_LEVEL: Record<SpaceTier, number> = {
|
||||
guest: 0,
|
||||
public: 1,
|
||||
beta: 2,
|
||||
alpha: 3,
|
||||
founder: 4,
|
||||
};
|
||||
|
||||
export function isSpaceTier(value: unknown): value is SpaceTier {
|
||||
return typeof value === 'string' && (SPACE_TIERS as readonly string[]).includes(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a Space's tier is high enough to meet a required tier.
|
||||
* Both undefined/invalid tiers are treated as 'guest' (least access).
|
||||
*/
|
||||
export function spaceTierMeets(actual: SpaceTier | undefined, required: SpaceTier): boolean {
|
||||
const a = actual && isSpaceTier(actual) ? TIER_LEVEL[actual] : 0;
|
||||
const r = TIER_LEVEL[required];
|
||||
return a >= r;
|
||||
}
|
||||
|
||||
export interface SpaceMetadata {
|
||||
type: SpaceType;
|
||||
/**
|
||||
* Access tier for this Space. Gates which modules / features the
|
||||
* Space can use via ManaApp.requiredTier. Defaults to 'public'.
|
||||
* The signup hook stamps the user's prior user-level tier onto the
|
||||
* personal Space so no one loses access during the user→space tier
|
||||
* migration.
|
||||
*/
|
||||
tier?: SpaceTier;
|
||||
voiceDoc?: string;
|
||||
legalEntity?: string;
|
||||
uid?: string;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue