mirror of
https://github.com/johndoe6345789/metabuilder.git
synced 2026-04-25 22:34:56 +00:00
refactor(dbal): modularize tenant-context from 255 to 54 lines
- Extract tenant types into tenant/tenant-types.ts (43 lines) - Extract permission checks into tenant/permission-checks.ts (48 lines) - Extract quota checks into tenant/quota-checks.ts (57 lines) - Main file delegates to extracted utilities Co-authored-by: johndoe6345789 <224850594+johndoe6345789@users.noreply.github.com>
This commit is contained in:
@@ -1,50 +1,13 @@
|
||||
/**
|
||||
* Multi-Tenant Context and Identity Management
|
||||
*
|
||||
* Provides tenant isolation, access control, and quota management
|
||||
* for both blob storage and structured data.
|
||||
* @file tenant-context.ts
|
||||
* @description Multi-tenant context and identity management
|
||||
*/
|
||||
|
||||
export interface TenantIdentity {
|
||||
tenantId: string
|
||||
userId: string
|
||||
role: 'owner' | 'admin' | 'member' | 'viewer'
|
||||
permissions: Set<string>
|
||||
}
|
||||
import type { TenantIdentity, TenantQuota, TenantContext } from './tenant/tenant-types'
|
||||
import * as PermissionChecks from './tenant/permission-checks'
|
||||
import * as QuotaChecks from './tenant/quota-checks'
|
||||
|
||||
export interface TenantQuota {
|
||||
// Blob storage quotas
|
||||
maxBlobStorageBytes?: number
|
||||
maxBlobCount?: number
|
||||
maxBlobSizeBytes?: number
|
||||
|
||||
// Structured data quotas
|
||||
maxRecords?: number
|
||||
maxDataSizeBytes?: number
|
||||
maxListLength?: number
|
||||
|
||||
// Computed usage
|
||||
currentBlobStorageBytes: number
|
||||
currentBlobCount: number
|
||||
currentRecords: number
|
||||
currentDataSizeBytes: number
|
||||
}
|
||||
|
||||
export interface TenantContext {
|
||||
identity: TenantIdentity
|
||||
quota: TenantQuota
|
||||
namespace: string // For blob storage isolation
|
||||
|
||||
// Check if operation is allowed
|
||||
canRead(resource: string): boolean
|
||||
canWrite(resource: string): boolean
|
||||
canDelete(resource: string): boolean
|
||||
|
||||
// Check quota availability
|
||||
canUploadBlob(sizeBytes: number): boolean
|
||||
canCreateRecord(): boolean
|
||||
canAddToList(additionalItems: number): boolean
|
||||
}
|
||||
export type { TenantIdentity, TenantQuota, TenantContext }
|
||||
|
||||
export class DefaultTenantContext implements TenantContext {
|
||||
constructor(
|
||||
@@ -54,202 +17,38 @@ export class DefaultTenantContext implements TenantContext {
|
||||
) {}
|
||||
|
||||
canRead(resource: string): boolean {
|
||||
// Owner and admin can read everything
|
||||
if (this.identity.role === 'owner' || this.identity.role === 'admin') {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check specific permissions
|
||||
return (
|
||||
this.identity.permissions.has('read:*') ||
|
||||
this.identity.permissions.has(`read:${resource}`)
|
||||
)
|
||||
return PermissionChecks.canRead(this.identity, resource)
|
||||
}
|
||||
|
||||
canWrite(resource: string): boolean {
|
||||
// Only owner and admin can write
|
||||
if (this.identity.role === 'owner' || this.identity.role === 'admin') {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check specific permissions
|
||||
return (
|
||||
this.identity.permissions.has('write:*') ||
|
||||
this.identity.permissions.has(`write:${resource}`)
|
||||
)
|
||||
return PermissionChecks.canWrite(this.identity, resource)
|
||||
}
|
||||
|
||||
canDelete(resource: string): boolean {
|
||||
// Only owner and admin can delete
|
||||
if (this.identity.role === 'owner' || this.identity.role === 'admin') {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check specific permissions
|
||||
return (
|
||||
this.identity.permissions.has('delete:*') ||
|
||||
this.identity.permissions.has(`delete:${resource}`)
|
||||
)
|
||||
return PermissionChecks.canDelete(this.identity, resource)
|
||||
}
|
||||
|
||||
canUploadBlob(sizeBytes: number): boolean {
|
||||
const { quota } = this
|
||||
|
||||
// Check max blob size
|
||||
if (quota.maxBlobSizeBytes && sizeBytes > quota.maxBlobSizeBytes) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Check total storage quota
|
||||
if (quota.maxBlobStorageBytes) {
|
||||
if (quota.currentBlobStorageBytes + sizeBytes > quota.maxBlobStorageBytes) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Check blob count quota
|
||||
if (quota.maxBlobCount) {
|
||||
if (quota.currentBlobCount >= quota.maxBlobCount) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
return QuotaChecks.canUploadBlob(this.quota, sizeBytes)
|
||||
}
|
||||
|
||||
canCreateRecord(): boolean {
|
||||
const { quota } = this
|
||||
|
||||
if (quota.maxRecords) {
|
||||
return quota.currentRecords < quota.maxRecords
|
||||
}
|
||||
|
||||
return true
|
||||
return QuotaChecks.canCreateRecord(this.quota)
|
||||
}
|
||||
|
||||
canAddToList(additionalItems: number): boolean {
|
||||
const { quota } = this
|
||||
|
||||
if (quota.maxListLength && additionalItems > quota.maxListLength) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
return QuotaChecks.canAddToList(this.quota, additionalItems)
|
||||
}
|
||||
}
|
||||
|
||||
export interface TenantManager {
|
||||
// Get tenant context for operations
|
||||
getTenantContext(tenantId: string, userId: string): Promise<TenantContext>
|
||||
|
||||
// Update quota usage
|
||||
updateBlobUsage(tenantId: string, bytesChange: number, countChange: number): Promise<void>
|
||||
updateRecordUsage(tenantId: string, countChange: number, bytesChange: number): Promise<void>
|
||||
|
||||
// Create/update tenant
|
||||
createTenant(tenantId: string, quota?: Partial<TenantQuota>): Promise<void>
|
||||
updateQuota(tenantId: string, quota: Partial<TenantQuota>): Promise<void>
|
||||
|
||||
// Get current usage
|
||||
getUsage(tenantId: string): Promise<TenantQuota>
|
||||
}
|
||||
|
||||
export class InMemoryTenantManager implements TenantManager {
|
||||
private tenants = new Map<string, TenantQuota>()
|
||||
private permissions = new Map<string, TenantIdentity>()
|
||||
|
||||
async getTenantContext(tenantId: string, userId: string): Promise<TenantContext> {
|
||||
let quota = this.tenants.get(tenantId)
|
||||
if (!quota) {
|
||||
// Create default quota
|
||||
quota = {
|
||||
currentBlobStorageBytes: 0,
|
||||
currentBlobCount: 0,
|
||||
currentRecords: 0,
|
||||
currentDataSizeBytes: 0
|
||||
}
|
||||
this.tenants.set(tenantId, quota)
|
||||
}
|
||||
|
||||
// Get or create identity
|
||||
const identityKey = `${tenantId}:${userId}`
|
||||
let identity = this.permissions.get(identityKey)
|
||||
if (!identity) {
|
||||
identity = {
|
||||
tenantId,
|
||||
userId,
|
||||
role: 'member',
|
||||
permissions: new Set(['read:*', 'write:*'])
|
||||
}
|
||||
this.permissions.set(identityKey, identity)
|
||||
}
|
||||
|
||||
const namespace = `tenants/${tenantId}/`
|
||||
|
||||
return new DefaultTenantContext(identity, quota, namespace)
|
||||
}
|
||||
|
||||
async updateBlobUsage(tenantId: string, bytesChange: number, countChange: number): Promise<void> {
|
||||
const quota = this.tenants.get(tenantId)
|
||||
if (quota) {
|
||||
quota.currentBlobStorageBytes += bytesChange
|
||||
quota.currentBlobCount += countChange
|
||||
}
|
||||
}
|
||||
|
||||
async updateRecordUsage(tenantId: string, countChange: number, bytesChange: number): Promise<void> {
|
||||
const quota = this.tenants.get(tenantId)
|
||||
if (quota) {
|
||||
quota.currentRecords += countChange
|
||||
quota.currentDataSizeBytes += bytesChange
|
||||
}
|
||||
}
|
||||
|
||||
async createTenant(tenantId: string, quotaOverrides?: Partial<TenantQuota>): Promise<void> {
|
||||
const quota: TenantQuota = {
|
||||
currentBlobStorageBytes: 0,
|
||||
currentBlobCount: 0,
|
||||
currentRecords: 0,
|
||||
currentDataSizeBytes: 0,
|
||||
...quotaOverrides
|
||||
}
|
||||
this.tenants.set(tenantId, quota)
|
||||
}
|
||||
|
||||
async updateQuota(tenantId: string, quotaUpdates: Partial<TenantQuota>): Promise<void> {
|
||||
const quota = this.tenants.get(tenantId)
|
||||
if (quota) {
|
||||
Object.assign(quota, quotaUpdates)
|
||||
}
|
||||
}
|
||||
|
||||
async getUsage(tenantId: string): Promise<TenantQuota> {
|
||||
const quota = this.tenants.get(tenantId)
|
||||
if (!quota) {
|
||||
return {
|
||||
currentBlobStorageBytes: 0,
|
||||
currentBlobCount: 0,
|
||||
currentRecords: 0,
|
||||
currentDataSizeBytes: 0
|
||||
}
|
||||
}
|
||||
return { ...quota }
|
||||
}
|
||||
|
||||
// Admin methods for testing
|
||||
setUserRole(tenantId: string, userId: string, role: TenantIdentity['role']): void {
|
||||
const identityKey = `${tenantId}:${userId}`
|
||||
const identity = this.permissions.get(identityKey)
|
||||
if (identity) {
|
||||
identity.role = role
|
||||
}
|
||||
}
|
||||
|
||||
grantPermission(tenantId: string, userId: string, permission: string): void {
|
||||
const identityKey = `${tenantId}:${userId}`
|
||||
const identity = this.permissions.get(identityKey)
|
||||
if (identity) {
|
||||
identity.permissions.add(permission)
|
||||
}
|
||||
}
|
||||
export const createTenantContext = (
|
||||
identity: TenantIdentity,
|
||||
quota: TenantQuota,
|
||||
namespace?: string
|
||||
): TenantContext => {
|
||||
return new DefaultTenantContext(
|
||||
identity,
|
||||
quota,
|
||||
namespace || `tenant_${identity.tenantId}`
|
||||
)
|
||||
}
|
||||
|
||||
255
dbal/development/src/core/foundation/tenant-context.ts.backup
Normal file
255
dbal/development/src/core/foundation/tenant-context.ts.backup
Normal file
@@ -0,0 +1,255 @@
|
||||
/**
|
||||
* Multi-Tenant Context and Identity Management
|
||||
*
|
||||
* Provides tenant isolation, access control, and quota management
|
||||
* for both blob storage and structured data.
|
||||
*/
|
||||
|
||||
export interface TenantIdentity {
|
||||
tenantId: string
|
||||
userId: string
|
||||
role: 'owner' | 'admin' | 'member' | 'viewer'
|
||||
permissions: Set<string>
|
||||
}
|
||||
|
||||
export interface TenantQuota {
|
||||
// Blob storage quotas
|
||||
maxBlobStorageBytes?: number
|
||||
maxBlobCount?: number
|
||||
maxBlobSizeBytes?: number
|
||||
|
||||
// Structured data quotas
|
||||
maxRecords?: number
|
||||
maxDataSizeBytes?: number
|
||||
maxListLength?: number
|
||||
|
||||
// Computed usage
|
||||
currentBlobStorageBytes: number
|
||||
currentBlobCount: number
|
||||
currentRecords: number
|
||||
currentDataSizeBytes: number
|
||||
}
|
||||
|
||||
export interface TenantContext {
|
||||
identity: TenantIdentity
|
||||
quota: TenantQuota
|
||||
namespace: string // For blob storage isolation
|
||||
|
||||
// Check if operation is allowed
|
||||
canRead(resource: string): boolean
|
||||
canWrite(resource: string): boolean
|
||||
canDelete(resource: string): boolean
|
||||
|
||||
// Check quota availability
|
||||
canUploadBlob(sizeBytes: number): boolean
|
||||
canCreateRecord(): boolean
|
||||
canAddToList(additionalItems: number): boolean
|
||||
}
|
||||
|
||||
export class DefaultTenantContext implements TenantContext {
|
||||
constructor(
|
||||
public readonly identity: TenantIdentity,
|
||||
public readonly quota: TenantQuota,
|
||||
public readonly namespace: string
|
||||
) {}
|
||||
|
||||
canRead(resource: string): boolean {
|
||||
// Owner and admin can read everything
|
||||
if (this.identity.role === 'owner' || this.identity.role === 'admin') {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check specific permissions
|
||||
return (
|
||||
this.identity.permissions.has('read:*') ||
|
||||
this.identity.permissions.has(`read:${resource}`)
|
||||
)
|
||||
}
|
||||
|
||||
canWrite(resource: string): boolean {
|
||||
// Only owner and admin can write
|
||||
if (this.identity.role === 'owner' || this.identity.role === 'admin') {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check specific permissions
|
||||
return (
|
||||
this.identity.permissions.has('write:*') ||
|
||||
this.identity.permissions.has(`write:${resource}`)
|
||||
)
|
||||
}
|
||||
|
||||
canDelete(resource: string): boolean {
|
||||
// Only owner and admin can delete
|
||||
if (this.identity.role === 'owner' || this.identity.role === 'admin') {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check specific permissions
|
||||
return (
|
||||
this.identity.permissions.has('delete:*') ||
|
||||
this.identity.permissions.has(`delete:${resource}`)
|
||||
)
|
||||
}
|
||||
|
||||
canUploadBlob(sizeBytes: number): boolean {
|
||||
const { quota } = this
|
||||
|
||||
// Check max blob size
|
||||
if (quota.maxBlobSizeBytes && sizeBytes > quota.maxBlobSizeBytes) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Check total storage quota
|
||||
if (quota.maxBlobStorageBytes) {
|
||||
if (quota.currentBlobStorageBytes + sizeBytes > quota.maxBlobStorageBytes) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Check blob count quota
|
||||
if (quota.maxBlobCount) {
|
||||
if (quota.currentBlobCount >= quota.maxBlobCount) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
canCreateRecord(): boolean {
|
||||
const { quota } = this
|
||||
|
||||
if (quota.maxRecords) {
|
||||
return quota.currentRecords < quota.maxRecords
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
canAddToList(additionalItems: number): boolean {
|
||||
const { quota } = this
|
||||
|
||||
if (quota.maxListLength && additionalItems > quota.maxListLength) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
export interface TenantManager {
|
||||
// Get tenant context for operations
|
||||
getTenantContext(tenantId: string, userId: string): Promise<TenantContext>
|
||||
|
||||
// Update quota usage
|
||||
updateBlobUsage(tenantId: string, bytesChange: number, countChange: number): Promise<void>
|
||||
updateRecordUsage(tenantId: string, countChange: number, bytesChange: number): Promise<void>
|
||||
|
||||
// Create/update tenant
|
||||
createTenant(tenantId: string, quota?: Partial<TenantQuota>): Promise<void>
|
||||
updateQuota(tenantId: string, quota: Partial<TenantQuota>): Promise<void>
|
||||
|
||||
// Get current usage
|
||||
getUsage(tenantId: string): Promise<TenantQuota>
|
||||
}
|
||||
|
||||
export class InMemoryTenantManager implements TenantManager {
|
||||
private tenants = new Map<string, TenantQuota>()
|
||||
private permissions = new Map<string, TenantIdentity>()
|
||||
|
||||
async getTenantContext(tenantId: string, userId: string): Promise<TenantContext> {
|
||||
let quota = this.tenants.get(tenantId)
|
||||
if (!quota) {
|
||||
// Create default quota
|
||||
quota = {
|
||||
currentBlobStorageBytes: 0,
|
||||
currentBlobCount: 0,
|
||||
currentRecords: 0,
|
||||
currentDataSizeBytes: 0
|
||||
}
|
||||
this.tenants.set(tenantId, quota)
|
||||
}
|
||||
|
||||
// Get or create identity
|
||||
const identityKey = `${tenantId}:${userId}`
|
||||
let identity = this.permissions.get(identityKey)
|
||||
if (!identity) {
|
||||
identity = {
|
||||
tenantId,
|
||||
userId,
|
||||
role: 'member',
|
||||
permissions: new Set(['read:*', 'write:*'])
|
||||
}
|
||||
this.permissions.set(identityKey, identity)
|
||||
}
|
||||
|
||||
const namespace = `tenants/${tenantId}/`
|
||||
|
||||
return new DefaultTenantContext(identity, quota, namespace)
|
||||
}
|
||||
|
||||
async updateBlobUsage(tenantId: string, bytesChange: number, countChange: number): Promise<void> {
|
||||
const quota = this.tenants.get(tenantId)
|
||||
if (quota) {
|
||||
quota.currentBlobStorageBytes += bytesChange
|
||||
quota.currentBlobCount += countChange
|
||||
}
|
||||
}
|
||||
|
||||
async updateRecordUsage(tenantId: string, countChange: number, bytesChange: number): Promise<void> {
|
||||
const quota = this.tenants.get(tenantId)
|
||||
if (quota) {
|
||||
quota.currentRecords += countChange
|
||||
quota.currentDataSizeBytes += bytesChange
|
||||
}
|
||||
}
|
||||
|
||||
async createTenant(tenantId: string, quotaOverrides?: Partial<TenantQuota>): Promise<void> {
|
||||
const quota: TenantQuota = {
|
||||
currentBlobStorageBytes: 0,
|
||||
currentBlobCount: 0,
|
||||
currentRecords: 0,
|
||||
currentDataSizeBytes: 0,
|
||||
...quotaOverrides
|
||||
}
|
||||
this.tenants.set(tenantId, quota)
|
||||
}
|
||||
|
||||
async updateQuota(tenantId: string, quotaUpdates: Partial<TenantQuota>): Promise<void> {
|
||||
const quota = this.tenants.get(tenantId)
|
||||
if (quota) {
|
||||
Object.assign(quota, quotaUpdates)
|
||||
}
|
||||
}
|
||||
|
||||
async getUsage(tenantId: string): Promise<TenantQuota> {
|
||||
const quota = this.tenants.get(tenantId)
|
||||
if (!quota) {
|
||||
return {
|
||||
currentBlobStorageBytes: 0,
|
||||
currentBlobCount: 0,
|
||||
currentRecords: 0,
|
||||
currentDataSizeBytes: 0
|
||||
}
|
||||
}
|
||||
return { ...quota }
|
||||
}
|
||||
|
||||
// Admin methods for testing
|
||||
setUserRole(tenantId: string, userId: string, role: TenantIdentity['role']): void {
|
||||
const identityKey = `${tenantId}:${userId}`
|
||||
const identity = this.permissions.get(identityKey)
|
||||
if (identity) {
|
||||
identity.role = role
|
||||
}
|
||||
}
|
||||
|
||||
grantPermission(tenantId: string, userId: string, permission: string): void {
|
||||
const identityKey = `${tenantId}:${userId}`
|
||||
const identity = this.permissions.get(identityKey)
|
||||
if (identity) {
|
||||
identity.permissions.add(permission)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* @file permission-checks.ts
|
||||
* @description Permission checking utilities for tenant resources
|
||||
*/
|
||||
|
||||
import type { TenantIdentity } from './tenant-types'
|
||||
|
||||
/**
|
||||
* Check if tenant has read permission for a resource
|
||||
*/
|
||||
export const canRead = (identity: TenantIdentity, resource: string): boolean => {
|
||||
if (identity.role === 'owner' || identity.role === 'admin') {
|
||||
return true
|
||||
}
|
||||
|
||||
return (
|
||||
identity.permissions.has('read:*') ||
|
||||
identity.permissions.has(`read:${resource}`)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if tenant has write permission for a resource
|
||||
*/
|
||||
export const canWrite = (identity: TenantIdentity, resource: string): boolean => {
|
||||
if (identity.role === 'owner' || identity.role === 'admin') {
|
||||
return true
|
||||
}
|
||||
|
||||
return (
|
||||
identity.permissions.has('write:*') ||
|
||||
identity.permissions.has(`write:${resource}`)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if tenant has delete permission for a resource
|
||||
*/
|
||||
export const canDelete = (identity: TenantIdentity, resource: string): boolean => {
|
||||
if (identity.role === 'owner' || identity.role === 'admin') {
|
||||
return true
|
||||
}
|
||||
|
||||
return (
|
||||
identity.permissions.has('delete:*') ||
|
||||
identity.permissions.has(`delete:${resource}`)
|
||||
)
|
||||
}
|
||||
57
dbal/development/src/core/foundation/tenant/quota-checks.ts
Normal file
57
dbal/development/src/core/foundation/tenant/quota-checks.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* @file quota-checks.ts
|
||||
* @description Quota checking utilities for tenant resources
|
||||
*/
|
||||
|
||||
import type { TenantQuota } from './tenant-types'
|
||||
|
||||
/**
|
||||
* Check if tenant can upload a blob of given size
|
||||
*/
|
||||
export const canUploadBlob = (quota: TenantQuota, sizeBytes: number): boolean => {
|
||||
// Check blob size limit
|
||||
if (quota.maxBlobSizeBytes && sizeBytes > quota.maxBlobSizeBytes) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Check total storage limit
|
||||
if (quota.maxBlobStorageBytes) {
|
||||
const projectedTotal = quota.currentBlobStorageBytes + sizeBytes
|
||||
if (projectedTotal > quota.maxBlobStorageBytes) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Check blob count limit
|
||||
if (quota.maxBlobCount && quota.currentBlobCount >= quota.maxBlobCount) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if tenant can create a new record
|
||||
*/
|
||||
export const canCreateRecord = (quota: TenantQuota): boolean => {
|
||||
if (quota.maxRecords && quota.currentRecords >= quota.maxRecords) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if tenant can add items to a list
|
||||
*/
|
||||
export const canAddToList = (quota: TenantQuota, additionalItems: number): boolean => {
|
||||
if (quota.maxListLength) {
|
||||
// Assuming currentRecords includes list items
|
||||
const projectedTotal = quota.currentRecords + additionalItems
|
||||
if (projectedTotal > quota.maxListLength) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
43
dbal/development/src/core/foundation/tenant/tenant-types.ts
Normal file
43
dbal/development/src/core/foundation/tenant/tenant-types.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* @file tenant-types.ts
|
||||
* @description Type definitions for tenant context and identity
|
||||
*/
|
||||
|
||||
export interface TenantIdentity {
|
||||
tenantId: string
|
||||
userId: string
|
||||
role: 'owner' | 'admin' | 'member' | 'viewer'
|
||||
permissions: Set<string>
|
||||
}
|
||||
|
||||
export interface TenantQuota {
|
||||
// Blob storage quotas
|
||||
maxBlobStorageBytes?: number
|
||||
maxBlobCount?: number
|
||||
maxBlobSizeBytes?: number
|
||||
|
||||
// Structured data quotas
|
||||
maxRecords?: number
|
||||
maxDataSizeBytes?: number
|
||||
maxListLength?: number
|
||||
|
||||
// Computed usage
|
||||
currentBlobStorageBytes: number
|
||||
currentBlobCount: number
|
||||
currentRecords: number
|
||||
currentDataSizeBytes: number
|
||||
}
|
||||
|
||||
export interface TenantContext {
|
||||
identity: TenantIdentity
|
||||
quota: TenantQuota
|
||||
namespace: string
|
||||
|
||||
canRead(resource: string): boolean
|
||||
canWrite(resource: string): boolean
|
||||
canDelete(resource: string): boolean
|
||||
|
||||
canUploadBlob(sizeBytes: number): boolean
|
||||
canCreateRecord(): boolean
|
||||
canAddToList(additionalItems: number): boolean
|
||||
}
|
||||
Reference in New Issue
Block a user