Skip to content

Add-app-modes-context #2

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

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42,438 changes: 42,436 additions & 2 deletions dist/bundle.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/bundle.js.map

Large diffs are not rendered by default.

3,321 changes: 1,842 additions & 1,479 deletions package-lock.json

Large diffs are not rendered by default.

9 changes: 9 additions & 0 deletions src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,15 @@ button {
margin: 3px 3px;
}

button:disabled {
color: var(--textInactive);
border-color: var(--actionPrimaryBackgroundHover);
background-color: var(--backgroundInactive);
border-radius: var(--border-radius);
padding: 3px 3px 3px 3px;
margin: 3px 3px;
}

button:hover {
background-color: var(--actionPrimaryBackgroundHover);
}
Expand Down
190 changes: 190 additions & 0 deletions src/components/PermissionsMap.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
// permissionsMap.ts
export type Permission =
| 'canAccessAssets'
| 'canManageAssets'
| 'canDEAccessCanvas'
| 'canDEModifyComponents'
| 'canDEDesign'
| 'canModifyImageElements'
| 'canDECreateComponents'
| 'canDECreateStyleBlocks'
| 'canDEModifyStyleBlocks'
| 'canEdit'
| 'canDEReadPageSettings'
| 'canDEManagePageSettings'
| 'canDECreatePage'
| 'canDEReadVariables'
| 'canDEModifyVariables'

export interface Permissions {
[key: string]: boolean // This maps each Permission to a boolean
}

interface MethodPermissions {
permissions: Permission[]
}

interface PermissionsMap {
[objectType: string]: {
[methodName: string]: MethodPermissions
}
}

export const permissionsMap: PermissionsMap = {
// Assets
Asset: {
getUrl: { permissions: ['canAccessAssets'] },
getAltText: { permissions: ['canAccessAssets'] },
setAltText: { permissions: ['canManageAssets'] },
getName: { permissions: ['canAccessAssets'] },
getMimeType: { permissions: ['canAccessAssets'] },
getAssetById: { permissions: ['canAccessAssets'] },
},

// Components
Component: {
getName: { permissions: ['canDEAccessCanvas'] },
setName: { permissions: ['canDEModifyComponents'] },
getRootElement: { permissions: ['canDEAccessCanvas'] },
},

// Elements
Element: {
append: { permissions: ['canDEDesign'] },
prepend: { permissions: ['canDEDesign'] },
before: { permissions: ['canDEDesign'] },
after: { permissions: ['canDEDesign'] },
getCustomAttribute: { permissions: ['canDEAccessCanvas'] },
getAllCustomAttributes: { permissions: ['canDEAccessCanvas'] },
setCustomAttribute: { permissions: ['canDEDesign'] },
removeCustomAttribute: { permissions: ['canDEDesign'] },
getElementChildren: { permissions: ['canDEAccessCanvas'] },
getDomId: { permissions: ['canDEAccessCanvas'] },
setDomId: { permissions: ['canDEDesign'] },
setTextContent: { permissions: ['canEdit'] },
getStyles: { permissions: ['canDEAccessCanvas'] },
setStyles: { permissions: ['canDEDesign'] },
getTag: { permissions: ['canDEAccessCanvas'] },
setTag: { permissions: ['canDEDesign'] },
getAttribute: { permissions: ['canDEAccessCanvas'] },
setAttribute: { permissions: ['canDEDesign'] },
getAllAttributes: { permissions: ['canDEAccessCanvas'] },
removeAttribute: { permissions: ['canDEDesign'] },
getText: { permissions: ['canDEAccessCanvas'] },
setText: { permissions: ['canEdit'] },
getHeadingLevel: { permissions: ['canDEAccessCanvas'] },
setHeadingLevel: { permissions: ['canEdit'] },
},

// Image Elements
ImageElement: {
getAltText: { permissions: ['canDEAccessCanvas'] },
setAltText: { permissions: ['canModifyImageElements'] },
getAsset: { permissions: ['canDEAccessCanvas'] },
setAsset: { permissions: ['canModifyImageElements'] },
},

// Component Elements
ComponentElement: {
getComponent: { permissions: ['canDEAccessCanvas'] },
},

// Styles
Style: {
getName: { permissions: ['canDEAccessCanvas'] },
getProperties: { permissions: ['canDEAccessCanvas'] },
getProperty: { permissions: ['canDEAccessCanvas'] },
setProperties: { permissions: ['canDEModifyStyleBlocks'] },
setProperty: { permissions: ['canDEModifyStyleBlocks'] },
removeAllProperties: { permissions: ['canDEModifyStyleBlocks'] },
removeProperty: { permissions: ['canDEModifyStyleBlocks'] },
removeProperties: { permissions: ['canDEModifyStyleBlocks'] },
},

// Pages
Page: {
getKind: { permissions: ['canDEReadPageSettings'] },
getName: { permissions: ['canDEReadPageSettings'] },
setName: { permissions: ['canDEManagePageSettings'] },
getSlug: { permissions: ['canDEReadPageSettings'] },
setSlug: { permissions: ['canDEManagePageSettings'] },
getPublishPath: { permissions: ['canDEReadPageSettings'] },
getTitle: { permissions: ['canDEReadPageSettings'] },
setTitle: { permissions: ['canDEManagePageSettings'] },
getDescription: { permissions: ['canDEReadPageSettings'] },
setDescription: { permissions: ['canDEManagePageSettings'] },
getCollectionId: { permissions: ['canDEReadPageSettings'] },
getCollectionName: { permissions: ['canDEReadPageSettings'] },
isDraft: { permissions: ['canDEReadPageSettings'] },
setDraft: { permissions: ['canDEManagePageSettings'] },
isPasswordProtected: { permissions: ['canDEReadPageSettings'] },
getUtilityPageKey: { permissions: ['canDEReadPageSettings'] },
isHomepage: { permissions: ['canDEReadPageSettings'] },
usesTitleAsOpenGraphTitle: { permissions: ['canDEReadPageSettings'] },
setOpenGraphTitle: { permissions: ['canDEManagePageSettings'] },
usesDescriptionAsOpenGraphDescription: {
permissions: ['canDEReadPageSettings'],
},
setOpenGraphDescription: { permissions: ['canDEManagePageSettings'] },
getOpenGraphImage: { permissions: ['canDEReadPageSettings'] },
setOpenGraphImage: { permissions: ['canDEManagePageSettings'] },
isExcludedFromSearch: { permissions: ['canDEManagePageSettings'] },
excludeFromSearch: { permissions: ['canDEManagePageSettings'] },
usesTitleAsSearchTitle: { permissions: ['canDEManagePageSettings'] },
setSearchTitle: { permissions: ['canDEManagePageSettings'] },
usesDescriptionAsSearchDescription: {
permissions: ['canDEManagePageSettings'],
},
setSearchDescription: { permissions: ['canDEManagePageSettings'] },
usesOpenGraphImageAsSearchImage: {
permissions: ['canDEManagePageSettings'],
},
setSearchImage: { permissions: ['canDEManagePageSettings'] },
},

// Variables
Variable: {
remove: { permissions: ['canDEModifyVariables'] },
set: { permissions: ['canDEModifyVariables'] },
get: { permissions: ['canDEReadVariables'] },
setName: { permissions: ['canDEModifyVariables'] },
getName: { permissions: ['canDEReadVariables'] },
},

Collection: {
getName: { permissions: ['canDEReadVariables'] },
getAllVariables: { permissions: ['canDEReadVariables'] },
getVariable: { permissions: ['canDEReadVariables'] },
getVariableByName: { permissions: ['canDEReadVariables'] },
createColorVariable: { permissions: ['canDEModifyVariables'] },
createSizeVariable: { permissions: ['canDEModifyVariables'] },
createFontFamilyVariable: { permissions: ['canDEModifyVariables'] },
},

// Webflow-specific actions
webflow: {
createAsset: { permissions: ['canManageAssets'] },
getAllAssets: { permissions: ['canAccessAssets'] },
registerComponent: { permissions: ['canDECreateComponents'] },
unregisterComponent: { permissions: ['canDECreateComponents'] },
getAllComponents: { permissions: ['canDEAccessCanvas'] },
enterComponent: { permissions: ['canDEModifyComponents'] },
exitComponent: { permissions: ['canDEAccessCanvas'] },
elementBuilder: { permissions: ['canDEDesign'] },
getSelectedElement: { permissions: ['canDEAccessCanvas'] },
setSelectedElement: { permissions: ['canDEAccessCanvas'] },
getAllElements: { permissions: ['canDEAccessCanvas'] },
getRootElement: { permissions: ['canDEAccessCanvas'] },
remove: { permissions: ['canDEDesign'] },
createStyle: { permissions: ['canDECreateStyleBlocks'] },
removeStyle: { permissions: ['canDEModifyStyleBlocks'] },
getStyleByName: { permissions: ['canDEAccessCanvas'] },
getAllStyles: { permissions: ['canDEAccessCanvas'] },
createPage: { permissions: ['canDECreatePage'] },
createPageFolder: { permissions: ['canDECreatePage'] },
getCurrentPage: { permissions: ['canDEReadPageSettings'] },
getAllPagesAndFolders: { permissions: ['canDEReadPageSettings'] },
switchPage: { permissions: ['canDEAccessCanvas'] },
getDefaultVariableCollection: { permissions: ['canDEReadVariables'] },
},
}
50 changes: 50 additions & 0 deletions src/components/PermissionsProxy.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { permissionsMap, Permission } from './PermissionsMap'

interface Permissions {
[key: string]: boolean
}

function createPermissionsProxy<T extends object>(
target: T,
permissions: Permissions,
): T {
return new Proxy(target, {
get(obj, prop: string | symbol, receiver: any) {
const originalMethod = obj[prop as keyof T]

if (typeof originalMethod === 'function') {
return function (...args: any[]) {
const objectType = target.constructor.name
const methodName = prop.toString()

const methodPermissions = permissionsMap[objectType]?.[methodName]

// Check Permissisons
if (methodPermissions) {
const hasPermission = methodPermissions.permissions.every(
(permission) => permissions[permission],
)

if (!hasPermission) {
console.error(
`You do not have permission to execute ${objectType}.${methodName}`,
)
return // Prevent execution if permissions are insufficient
}
} else {
console.warn(
`No permissions defined for method: ${objectType}.${methodName}`,
)
}

// Call the original method
return (originalMethod as Function).apply(receiver, args)
}
}

return originalMethod
},
})
}

export default createPermissionsProxy
34 changes: 34 additions & 0 deletions src/components/classProxy.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import createPermissionsProxy from './PermissionsProxy'
import { Permissions } from './PermissionsMap' // Import the correct Permissions type

/**
* Wraps a class constructor with a proxy to ensure that all methods on instances
* of the class are checked for permissions before being executed.
*
* @param ClassConstructor - The class constructor to wrap with the proxy.
* @param permissions - The permissions object that maps permissions to boolean values.
* @returns A proxied class constructor.
*/
function createClassProxy<T extends new (...args: any[]) => any>(
ClassConstructor: T,
permissions: Permissions,
): T {
return new Proxy(ClassConstructor, {
/**
* Intercepts the construction of a new instance of the class.
*
* @param target - The original class constructor.
* @param args - The arguments passed to the constructor.
* @returns A proxied instance of the class with permission checks applied.
*/
construct(target, args) {
// Create the original instance by calling the class constructor with the provided arguments.
const instance = new target(...args)

// Wrap the created instance with the permissions proxy to intercept method calls.
return createPermissionsProxy(instance, permissions)
},
})
}

export default createClassProxy
44 changes: 44 additions & 0 deletions src/components/functionSelector.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import React from 'react'
import Dropdown from './dropdown'

// Define the type for the options used in the dropdowns
interface Option {
value: string
label: string
}

// Define the props for the FunctionSelector component
interface FunctionSelectorProps {
exampleCategories: Option[] // Array of options for categories
functionSelections: Option[] // Array of options for functions
selectedExampleCategory: string // The currently selected category
selectedFunctionName: string // The currently selected function name
onCategoryChange: (value: string) => void // Handler for when the category changes
onFunctionChange: (value: string) => void // Handler for when the function changes
}

const FunctionSelector: React.FC<FunctionSelectorProps> = ({
exampleCategories,
functionSelections,
selectedExampleCategory,
selectedFunctionName,
onCategoryChange,
onFunctionChange,
}) => (
<>
<p>Select an API category</p>
<Dropdown
options={exampleCategories}
selectedValue={selectedExampleCategory}
onValueChange={onCategoryChange}
/>
<p>Select an API method</p>
<Dropdown
options={functionSelections}
selectedValue={selectedFunctionName}
onValueChange={onFunctionChange}
/>
</>
)

export default FunctionSelector
Loading