@rebasepro/studio 0.5.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ApiExplorer-CdIwR9Ga.js +963 -0
- package/dist/ApiExplorer-CdIwR9Ga.js.map +1 -0
- package/dist/AuthSimulationSelector-iEZ-Or_1.js +56 -0
- package/dist/AuthSimulationSelector-iEZ-Or_1.js.map +1 -0
- package/dist/BranchesView-DncIRcZt.js +461 -0
- package/dist/BranchesView-DncIRcZt.js.map +1 -0
- package/dist/CronJobsView-4gdtJvoe.js +500 -0
- package/dist/CronJobsView-4gdtJvoe.js.map +1 -0
- package/dist/JSEditor-BhAbEjCP.js +1573 -0
- package/dist/JSEditor-BhAbEjCP.js.map +1 -0
- package/dist/LogsExplorer-CqtKILj8.js +240 -0
- package/dist/LogsExplorer-CqtKILj8.js.map +1 -0
- package/dist/MonacoEditor-COZqrIJ1.js +246 -0
- package/dist/MonacoEditor-COZqrIJ1.js.map +1 -0
- package/dist/RLSEditor-DpF1u9EC.js +1369 -0
- package/dist/RLSEditor-DpF1u9EC.js.map +1 -0
- package/dist/SQLEditor-BLuq_zDM.js +1964 -0
- package/dist/SQLEditor-BLuq_zDM.js.map +1 -0
- package/dist/SchemaVisualizer-BJK2u3C0.js +1068 -0
- package/dist/SchemaVisualizer-BJK2u3C0.js.map +1 -0
- package/dist/StorageView-CvrnHmDG.js +1395 -0
- package/dist/StorageView-CvrnHmDG.js.map +1 -0
- package/dist/{studio/src/components → components}/ApiExplorer/ApiExplorer.d.ts +2 -1
- package/dist/{studio/src/components → components}/ApiExplorer/EndpointDetail.d.ts +2 -1
- package/dist/{studio/src/components → components}/ApiExplorer/TryItPanel.d.ts +2 -1
- package/dist/{studio/src/components → components}/AuthSimulationSelector.d.ts +2 -1
- package/dist/components/Branches/BranchesView.d.ts +2 -0
- package/dist/components/CronJobs/CronJobsView.d.ts +2 -0
- package/dist/components/JSEditor/JSEditor.d.ts +2 -0
- package/dist/{studio/src/components → components}/JSEditor/JSEditorSidebar.d.ts +2 -1
- package/dist/{studio/src/components → components}/JSEditor/JSMonacoEditor.d.ts +2 -1
- package/dist/components/LogsExplorer/LogsExplorer.d.ts +2 -0
- package/dist/{studio/src/components → components}/RLSEditor/PolicyEditor.d.ts +2 -1
- package/dist/{studio/src/components → components}/RLSEditor/RLSEditor.d.ts +2 -1
- package/dist/{studio/src/components → components}/SQLEditor/MonacoEditor.d.ts +2 -1
- package/dist/{studio/src/components → components}/SQLEditor/SQLEditor.d.ts +2 -1
- package/dist/{studio/src/components → components}/SQLEditor/SQLEditorSidebar.d.ts +2 -1
- package/dist/{studio/src/components → components}/SQLEditor/SchemaBrowser.d.ts +2 -1
- package/dist/{studio/src/components → components}/SchemaVisualizer/RelationEdge.d.ts +1 -1
- package/dist/components/SchemaVisualizer/SchemaVisualizer.d.ts +3 -0
- package/dist/{studio/src/components → components}/SchemaVisualizer/TableNode.d.ts +1 -1
- package/dist/components/StorageView/StorageView.d.ts +2 -0
- package/dist/{studio/src/components → components}/StudioHomePage.d.ts +1 -1
- package/dist/index.es.js +688 -746
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +10323 -9572
- package/dist/index.umd.js.map +1 -1
- package/package.json +22 -22
- package/src/components/ApiExplorer/TryItPanel.tsx +15 -18
- package/src/components/CronJobs/CronJobsView.tsx +1 -1
- package/src/components/JSEditor/JSEditor.tsx +9 -14
- package/src/components/LogsExplorer/LogsExplorer.tsx +6 -3
- package/src/components/RLSEditor/PolicyEditor.tsx +1 -1
- package/src/components/SQLEditor/SQLEditor.tsx +40 -30
- package/src/components/StorageView/StorageView.tsx +25 -14
- package/src/components/StudioHomePage.tsx +51 -15
- package/src/utils/parseSpec.test.ts +41 -20
- package/src/utils/pgColumnToProperty.ts +1 -1
- package/dist/ApiExplorer-CGHEF1uL.js +0 -1052
- package/dist/ApiExplorer-CGHEF1uL.js.map +0 -1
- package/dist/AuthSimulationSelector-DGoXkWSg.js +0 -105
- package/dist/AuthSimulationSelector-DGoXkWSg.js.map +0 -1
- package/dist/BranchesView-BiTEwIhd.js +0 -291
- package/dist/BranchesView-BiTEwIhd.js.map +0 -1
- package/dist/CronJobsView-3PM_qR8v.js +0 -472
- package/dist/CronJobsView-3PM_qR8v.js.map +0 -1
- package/dist/JSEditor-DfwRLBZg.js +0 -1297
- package/dist/JSEditor-DfwRLBZg.js.map +0 -1
- package/dist/LogsExplorer-_4sZadKn.js +0 -162
- package/dist/LogsExplorer-_4sZadKn.js.map +0 -1
- package/dist/MonacoEditor-CMYEjiRf.js +0 -161
- package/dist/MonacoEditor-CMYEjiRf.js.map +0 -1
- package/dist/RLSEditor-CHEExeSB.js +0 -1871
- package/dist/RLSEditor-CHEExeSB.js.map +0 -1
- package/dist/SQLEditor-CQXaI0iU.js +0 -1797
- package/dist/SQLEditor-CQXaI0iU.js.map +0 -1
- package/dist/SchemaVisualizer-BGpmzyXT.js +0 -1069
- package/dist/SchemaVisualizer-BGpmzyXT.js.map +0 -1
- package/dist/StorageView-B7AsN2qX.js +0 -869
- package/dist/StorageView-B7AsN2qX.js.map +0 -1
- package/dist/common/src/collections/CollectionRegistry.d.ts +0 -56
- package/dist/common/src/collections/default-collections.d.ts +0 -9
- package/dist/common/src/collections/index.d.ts +0 -2
- package/dist/common/src/data/buildRebaseData.d.ts +0 -14
- package/dist/common/src/data/query_builder.d.ts +0 -55
- package/dist/common/src/index.d.ts +0 -4
- package/dist/common/src/util/builders.d.ts +0 -57
- package/dist/common/src/util/callbacks.d.ts +0 -6
- package/dist/common/src/util/collections.d.ts +0 -11
- package/dist/common/src/util/common.d.ts +0 -2
- package/dist/common/src/util/conditions.d.ts +0 -26
- package/dist/common/src/util/entities.d.ts +0 -58
- package/dist/common/src/util/enums.d.ts +0 -3
- package/dist/common/src/util/index.d.ts +0 -16
- package/dist/common/src/util/navigation_from_path.d.ts +0 -34
- package/dist/common/src/util/navigation_utils.d.ts +0 -20
- package/dist/common/src/util/parent_references_from_path.d.ts +0 -6
- package/dist/common/src/util/paths.d.ts +0 -14
- package/dist/common/src/util/permissions.d.ts +0 -14
- package/dist/common/src/util/references.d.ts +0 -2
- package/dist/common/src/util/relations.d.ts +0 -22
- package/dist/common/src/util/resolutions.d.ts +0 -72
- package/dist/common/src/util/storage.d.ts +0 -24
- package/dist/core/src/components/AIIcon.d.ts +0 -16
- package/dist/core/src/components/BootstrapAdminBanner.d.ts +0 -4
- package/dist/core/src/components/ConfirmationDialog.d.ts +0 -9
- package/dist/core/src/components/Debug/UIReferenceView.d.ts +0 -1
- package/dist/core/src/components/Debug/UIStyleGuide.d.ts +0 -1
- package/dist/core/src/components/ErrorTooltip.d.ts +0 -2
- package/dist/core/src/components/ErrorView.d.ts +0 -21
- package/dist/core/src/components/LanguageToggle.d.ts +0 -1
- package/dist/core/src/components/LoginView/LoginView.d.ts +0 -109
- package/dist/core/src/components/LoginView/index.d.ts +0 -2
- package/dist/core/src/components/NotFoundPage.d.ts +0 -1
- package/dist/core/src/components/RebaseAuth.d.ts +0 -10
- package/dist/core/src/components/RebaseLogo.d.ts +0 -7
- package/dist/core/src/components/UnsavedChangesDialog.d.ts +0 -9
- package/dist/core/src/components/UserDisplay.d.ts +0 -7
- package/dist/core/src/components/UserSelectPopover.d.ts +0 -62
- package/dist/core/src/components/UserSettingsView.d.ts +0 -1
- package/dist/core/src/components/common/index.d.ts +0 -6
- package/dist/core/src/components/common/table_height.d.ts +0 -5
- package/dist/core/src/components/common/types.d.ts +0 -66
- package/dist/core/src/components/common/useColumnsIds.d.ts +0 -9
- package/dist/core/src/components/common/useDataTableController.d.ts +0 -45
- package/dist/core/src/components/common/useDebouncedData.d.ts +0 -9
- package/dist/core/src/components/common/useScrollRestoration.d.ts +0 -14
- package/dist/core/src/components/index.d.ts +0 -17
- package/dist/core/src/contexts/AdminModeController.d.ts +0 -4
- package/dist/core/src/contexts/AnalyticsContext.d.ts +0 -3
- package/dist/core/src/contexts/AuthControllerContext.d.ts +0 -3
- package/dist/core/src/contexts/CustomizationControllerContext.d.ts +0 -3
- package/dist/core/src/contexts/DataDriverContext.d.ts +0 -3
- package/dist/core/src/contexts/DatabaseAdminContext.d.ts +0 -3
- package/dist/core/src/contexts/DialogsProvider.d.ts +0 -4
- package/dist/core/src/contexts/EffectiveRoleController.d.ts +0 -4
- package/dist/core/src/contexts/InternalUserManagementContext.d.ts +0 -3
- package/dist/core/src/contexts/ModeController.d.ts +0 -4
- package/dist/core/src/contexts/RebaseClientInstanceContext.d.ts +0 -6
- package/dist/core/src/contexts/RebaseDataContext.d.ts +0 -3
- package/dist/core/src/contexts/SnackbarProvider.d.ts +0 -2
- package/dist/core/src/contexts/StorageSourceContext.d.ts +0 -3
- package/dist/core/src/contexts/UserConfigurationPersistenceContext.d.ts +0 -3
- package/dist/core/src/contexts/index.d.ts +0 -13
- package/dist/core/src/core/PluginLifecycleManager.d.ts +0 -17
- package/dist/core/src/core/PluginProviderStack.d.ts +0 -21
- package/dist/core/src/core/Rebase.d.ts +0 -14
- package/dist/core/src/core/RebaseProps.d.ts +0 -147
- package/dist/core/src/core/RebaseRouter.d.ts +0 -4
- package/dist/core/src/core/RebaseRoutes.d.ts +0 -17
- package/dist/core/src/core/index.d.ts +0 -4
- package/dist/core/src/hooks/ApiConfigContext.d.ts +0 -24
- package/dist/core/src/hooks/data/delete.d.ts +0 -31
- package/dist/core/src/hooks/data/save.d.ts +0 -34
- package/dist/core/src/hooks/data/useCollectionFetch.d.ts +0 -62
- package/dist/core/src/hooks/data/useData.d.ts +0 -13
- package/dist/core/src/hooks/data/useDataOrder.d.ts +0 -12
- package/dist/core/src/hooks/data/useEntityFetch.d.ts +0 -43
- package/dist/core/src/hooks/data/useRelationSelector.d.ts +0 -52
- package/dist/core/src/hooks/data/useUserSelector.d.ts +0 -31
- package/dist/core/src/hooks/index.d.ts +0 -37
- package/dist/core/src/hooks/useAdminModeController.d.ts +0 -19
- package/dist/core/src/hooks/useAnalyticsController.d.ts +0 -5
- package/dist/core/src/hooks/useAuthController.d.ts +0 -11
- package/dist/core/src/hooks/useAuthSubscription.d.ts +0 -2
- package/dist/core/src/hooks/useBackendStorageSource.d.ts +0 -30
- package/dist/core/src/hooks/useBridgeRegistration.d.ts +0 -18
- package/dist/core/src/hooks/useBrowserTitleAndIcon.d.ts +0 -6
- package/dist/core/src/hooks/useBuildAdminModeController.d.ts +0 -6
- package/dist/core/src/hooks/useBuildEffectiveRoleController.d.ts +0 -8
- package/dist/core/src/hooks/useBuildLocalConfigurationPersistence.d.ts +0 -2
- package/dist/core/src/hooks/useBuildModeController.d.ts +0 -6
- package/dist/core/src/hooks/useClipboard.d.ts +0 -57
- package/dist/core/src/hooks/useCollapsedGroups.d.ts +0 -27
- package/dist/core/src/hooks/useCustomizationController.d.ts +0 -11
- package/dist/core/src/hooks/useDialogsController.d.ts +0 -11
- package/dist/core/src/hooks/useEffectiveRoleController.d.ts +0 -7
- package/dist/core/src/hooks/useInternalUserManagementController.d.ts +0 -12
- package/dist/core/src/hooks/useLargeLayout.d.ts +0 -1
- package/dist/core/src/hooks/useModeController.d.ts +0 -19
- package/dist/core/src/hooks/usePermissions.d.ts +0 -12
- package/dist/core/src/hooks/useRebaseClient.d.ts +0 -5
- package/dist/core/src/hooks/useRebaseContext.d.ts +0 -11
- package/dist/core/src/hooks/useRebaseRegistry.d.ts +0 -34
- package/dist/core/src/hooks/useResolvedComponent.d.ts +0 -47
- package/dist/core/src/hooks/useSlot.d.ts +0 -18
- package/dist/core/src/hooks/useSnackbarController.d.ts +0 -20
- package/dist/core/src/hooks/useStorageSource.d.ts +0 -7
- package/dist/core/src/hooks/useStudioBridge.d.ts +0 -91
- package/dist/core/src/hooks/useTranslation.d.ts +0 -17
- package/dist/core/src/hooks/useUnsavedChangesDialog.d.ts +0 -12
- package/dist/core/src/hooks/useUserConfigurationPersistence.d.ts +0 -8
- package/dist/core/src/i18n/RebaseI18nProvider.d.ts +0 -33
- package/dist/core/src/index.d.ts +0 -15
- package/dist/core/src/internal/common.d.ts +0 -3
- package/dist/core/src/internal/useRestoreScroll.d.ts +0 -6
- package/dist/core/src/locales/de.d.ts +0 -2
- package/dist/core/src/locales/en.d.ts +0 -10
- package/dist/core/src/locales/es.d.ts +0 -10
- package/dist/core/src/locales/fr.d.ts +0 -2
- package/dist/core/src/locales/hi.d.ts +0 -2
- package/dist/core/src/locales/it.d.ts +0 -2
- package/dist/core/src/locales/pt.d.ts +0 -7
- package/dist/core/src/util/constants.d.ts +0 -1
- package/dist/core/src/util/createFormexStub.d.ts +0 -2
- package/dist/core/src/util/entity_cache.d.ts +0 -22
- package/dist/core/src/util/enums.d.ts +0 -5
- package/dist/core/src/util/icon_list.d.ts +0 -5
- package/dist/core/src/util/icons.d.ts +0 -20
- package/dist/core/src/util/index.d.ts +0 -8
- package/dist/core/src/util/previews.d.ts +0 -4
- package/dist/core/src/util/useStorageUploadController.d.ts +0 -38
- package/dist/formex/src/Field.d.ts +0 -52
- package/dist/formex/src/Formex.d.ts +0 -7
- package/dist/formex/src/index.d.ts +0 -5
- package/dist/formex/src/types.d.ts +0 -40
- package/dist/formex/src/useCreateFormex.d.ts +0 -14
- package/dist/formex/src/utils.d.ts +0 -16
- package/dist/studio/src/components/Branches/BranchesView.d.ts +0 -1
- package/dist/studio/src/components/CronJobs/CronJobsView.d.ts +0 -1
- package/dist/studio/src/components/JSEditor/JSEditor.d.ts +0 -1
- package/dist/studio/src/components/LogsExplorer/LogsExplorer.d.ts +0 -1
- package/dist/studio/src/components/SchemaVisualizer/SchemaVisualizer.d.ts +0 -2
- package/dist/studio/src/components/StorageView/StorageView.d.ts +0 -1
- package/dist/types/src/controllers/analytics_controller.d.ts +0 -7
- package/dist/types/src/controllers/auth.d.ts +0 -104
- package/dist/types/src/controllers/client.d.ts +0 -168
- package/dist/types/src/controllers/collection_registry.d.ts +0 -46
- package/dist/types/src/controllers/customization_controller.d.ts +0 -60
- package/dist/types/src/controllers/data.d.ts +0 -207
- package/dist/types/src/controllers/data_driver.d.ts +0 -218
- package/dist/types/src/controllers/database_admin.d.ts +0 -11
- package/dist/types/src/controllers/dialogs_controller.d.ts +0 -36
- package/dist/types/src/controllers/effective_role.d.ts +0 -4
- package/dist/types/src/controllers/email.d.ts +0 -36
- package/dist/types/src/controllers/index.d.ts +0 -18
- package/dist/types/src/controllers/local_config_persistence.d.ts +0 -20
- package/dist/types/src/controllers/navigation.d.ts +0 -225
- package/dist/types/src/controllers/registry.d.ts +0 -63
- package/dist/types/src/controllers/side_dialogs_controller.d.ts +0 -67
- package/dist/types/src/controllers/side_entity_controller.d.ts +0 -97
- package/dist/types/src/controllers/snackbar.d.ts +0 -24
- package/dist/types/src/controllers/storage.d.ts +0 -171
- package/dist/types/src/index.d.ts +0 -4
- package/dist/types/src/rebase_context.d.ts +0 -122
- package/dist/types/src/types/auth_adapter.d.ts +0 -301
- package/dist/types/src/types/backend.d.ts +0 -571
- package/dist/types/src/types/backend_hooks.d.ts +0 -172
- package/dist/types/src/types/builders.d.ts +0 -15
- package/dist/types/src/types/chips.d.ts +0 -5
- package/dist/types/src/types/collections.d.ts +0 -961
- package/dist/types/src/types/component_ref.d.ts +0 -47
- package/dist/types/src/types/cron.d.ts +0 -102
- package/dist/types/src/types/data_source.d.ts +0 -64
- package/dist/types/src/types/database_adapter.d.ts +0 -94
- package/dist/types/src/types/entities.d.ts +0 -145
- package/dist/types/src/types/entity_actions.d.ts +0 -104
- package/dist/types/src/types/entity_callbacks.d.ts +0 -173
- package/dist/types/src/types/entity_link_builder.d.ts +0 -7
- package/dist/types/src/types/entity_overrides.d.ts +0 -10
- package/dist/types/src/types/entity_views.d.ts +0 -87
- package/dist/types/src/types/export_import.d.ts +0 -21
- package/dist/types/src/types/formex.d.ts +0 -40
- package/dist/types/src/types/index.d.ts +0 -28
- package/dist/types/src/types/locales.d.ts +0 -4
- package/dist/types/src/types/modify_collections.d.ts +0 -5
- package/dist/types/src/types/plugins.d.ts +0 -282
- package/dist/types/src/types/properties.d.ts +0 -1173
- package/dist/types/src/types/property_config.d.ts +0 -74
- package/dist/types/src/types/relations.d.ts +0 -336
- package/dist/types/src/types/slots.d.ts +0 -262
- package/dist/types/src/types/translations.d.ts +0 -900
- package/dist/types/src/types/user_management_delegate.d.ts +0 -86
- package/dist/types/src/types/websockets.d.ts +0 -78
- package/dist/types/src/users/index.d.ts +0 -1
- package/dist/types/src/users/user.d.ts +0 -50
- package/dist/ui/src/components/Alert.d.ts +0 -12
- package/dist/ui/src/components/Autocomplete.d.ts +0 -21
- package/dist/ui/src/components/Avatar.d.ts +0 -11
- package/dist/ui/src/components/Badge.d.ts +0 -8
- package/dist/ui/src/components/BooleanSwitch.d.ts +0 -14
- package/dist/ui/src/components/BooleanSwitchWithLabel.d.ts +0 -17
- package/dist/ui/src/components/Button.d.ts +0 -14
- package/dist/ui/src/components/Card.d.ts +0 -8
- package/dist/ui/src/components/CenteredView.d.ts +0 -9
- package/dist/ui/src/components/Checkbox.d.ts +0 -13
- package/dist/ui/src/components/Chip.d.ts +0 -26
- package/dist/ui/src/components/CircularProgress.d.ts +0 -5
- package/dist/ui/src/components/CircularProgressCenter.d.ts +0 -11
- package/dist/ui/src/components/Collapse.d.ts +0 -9
- package/dist/ui/src/components/ColorPicker.d.ts +0 -30
- package/dist/ui/src/components/Container.d.ts +0 -8
- package/dist/ui/src/components/DateTimeField.d.ts +0 -24
- package/dist/ui/src/components/DebouncedTextField.d.ts +0 -2
- package/dist/ui/src/components/Dialog.d.ts +0 -39
- package/dist/ui/src/components/DialogActions.d.ts +0 -7
- package/dist/ui/src/components/DialogContent.d.ts +0 -7
- package/dist/ui/src/components/DialogTitle.d.ts +0 -10
- package/dist/ui/src/components/ErrorBoundary.d.ts +0 -33
- package/dist/ui/src/components/ExpandablePanel.d.ts +0 -12
- package/dist/ui/src/components/FileUpload.d.ts +0 -23
- package/dist/ui/src/components/FilterChip.d.ts +0 -34
- package/dist/ui/src/components/IconButton.d.ts +0 -12
- package/dist/ui/src/components/InfoLabel.d.ts +0 -5
- package/dist/ui/src/components/InputLabel.d.ts +0 -11
- package/dist/ui/src/components/Label.d.ts +0 -7
- package/dist/ui/src/components/LoadingButton.d.ts +0 -7
- package/dist/ui/src/components/Markdown.d.ts +0 -10
- package/dist/ui/src/components/Menu.d.ts +0 -23
- package/dist/ui/src/components/Menubar.d.ts +0 -80
- package/dist/ui/src/components/MultiSelect.d.ts +0 -48
- package/dist/ui/src/components/Paper.d.ts +0 -6
- package/dist/ui/src/components/Popover.d.ts +0 -24
- package/dist/ui/src/components/RadioGroup.d.ts +0 -28
- package/dist/ui/src/components/ResizablePanels.d.ts +0 -18
- package/dist/ui/src/components/SearchBar.d.ts +0 -26
- package/dist/ui/src/components/Select.d.ts +0 -43
- package/dist/ui/src/components/Separator.d.ts +0 -5
- package/dist/ui/src/components/Sheet.d.ts +0 -22
- package/dist/ui/src/components/Skeleton.d.ts +0 -6
- package/dist/ui/src/components/Slider.d.ts +0 -21
- package/dist/ui/src/components/Table.d.ts +0 -34
- package/dist/ui/src/components/Tabs.d.ts +0 -19
- package/dist/ui/src/components/TextField.d.ts +0 -58
- package/dist/ui/src/components/TextareaAutosize.d.ts +0 -43
- package/dist/ui/src/components/ToggleButtonGroup.d.ts +0 -30
- package/dist/ui/src/components/Tooltip.d.ts +0 -19
- package/dist/ui/src/components/Typography.d.ts +0 -36
- package/dist/ui/src/components/VirtualTable/VirtualTable.d.ts +0 -11
- package/dist/ui/src/components/VirtualTable/VirtualTableCell.d.ts +0 -21
- package/dist/ui/src/components/VirtualTable/VirtualTableHeader.d.ts +0 -29
- package/dist/ui/src/components/VirtualTable/VirtualTableHeaderRow.d.ts +0 -2
- package/dist/ui/src/components/VirtualTable/VirtualTableProps.d.ts +0 -249
- package/dist/ui/src/components/VirtualTable/VirtualTableRow.d.ts +0 -3
- package/dist/ui/src/components/VirtualTable/index.d.ts +0 -3
- package/dist/ui/src/components/VirtualTable/types.d.ts +0 -38
- package/dist/ui/src/components/common/SelectInputLabel.d.ts +0 -5
- package/dist/ui/src/components/index.d.ts +0 -58
- package/dist/ui/src/hooks/PortalContainerContext.d.ts +0 -31
- package/dist/ui/src/hooks/index.d.ts +0 -6
- package/dist/ui/src/hooks/useDebounceCallback.d.ts +0 -1
- package/dist/ui/src/hooks/useDebounceValue.d.ts +0 -1
- package/dist/ui/src/hooks/useDebouncedCallback.d.ts +0 -1
- package/dist/ui/src/hooks/useInjectStyles.d.ts +0 -7
- package/dist/ui/src/hooks/useOutsideAlerter.d.ts +0 -5
- package/dist/ui/src/icons/GitHubIcon.d.ts +0 -2
- package/dist/ui/src/icons/HandleIcon.d.ts +0 -1
- package/dist/ui/src/icons/Icon.d.ts +0 -20
- package/dist/ui/src/icons/cool_icon_keys.d.ts +0 -1
- package/dist/ui/src/icons/icon_keys.d.ts +0 -1
- package/dist/ui/src/icons/index.d.ts +0 -8
- package/dist/ui/src/index.d.ts +0 -5
- package/dist/ui/src/styles.d.ts +0 -12
- package/dist/ui/src/util/chip_colors.d.ts +0 -4
- package/dist/ui/src/util/cls.d.ts +0 -2
- package/dist/ui/src/util/debounce.d.ts +0 -10
- package/dist/ui/src/util/hash.d.ts +0 -1
- package/dist/ui/src/util/index.d.ts +0 -4
- package/dist/ui/src/util/key_to_icon_component.d.ts +0 -1
- /package/dist/{studio/src/components → components}/ApiExplorer/parseSpec.d.ts +0 -0
- /package/dist/{studio/src/components → components}/ApiExplorer/types.d.ts +0 -0
- /package/dist/{studio/src/components → components}/RLSEditor/index.d.ts +0 -0
- /package/dist/{studio/src/components → components}/RebaseStudio.d.ts +0 -0
- /package/dist/{studio/src/components → components}/SQLEditor/ExplainVisualizer.d.ts +0 -0
- /package/dist/{studio/src/components → components}/SchemaVisualizer/schema-visualizer.utils.d.ts +0 -0
- /package/dist/{studio/src/components → components}/SchemaVisualizer/useSchemaGraph.d.ts +0 -0
- /package/dist/{studio/src/index.d.ts → index.d.ts} +0 -0
- /package/dist/{studio/src/utils → utils}/pgColumnToProperty.d.ts +0 -0
- /package/dist/{studio/src/utils → utils}/sql_utils.d.ts +0 -0
|
@@ -0,0 +1,1369 @@
|
|
|
1
|
+
import { t as MonacoEditor } from "./MonacoEditor-COZqrIJ1.js";
|
|
2
|
+
import { ErrorView, useRebaseContext, useSnackbarController, useStudioCollectionRegistry, useTranslation } from "@rebasepro/core";
|
|
3
|
+
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
4
|
+
import { Alert, AlertTriangleIcon, Button, Chip, CircularProgress, Dialog, DialogActions, DialogContent, DialogTitle, HelpCircleIcon, IconButton, KeyIcon, MultiSelect, MultiSelectItem, Paper, RefreshCwIcon, ResizablePanels, Select, SelectItem, ShieldIcon, Tab, Tabs, TextField, Tooltip, Trash2Icon, Typography, cls, defaultBorderMixin, iconSize } from "@rebasepro/ui";
|
|
5
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
import { isPostgresCollection } from "@rebasepro/types";
|
|
7
|
+
//#region src/components/RLSEditor/PolicyEditor.tsx
|
|
8
|
+
var COMMAND_OPTIONS = [
|
|
9
|
+
"ALL",
|
|
10
|
+
"SELECT",
|
|
11
|
+
"INSERT",
|
|
12
|
+
"UPDATE",
|
|
13
|
+
"DELETE"
|
|
14
|
+
];
|
|
15
|
+
var ROLE_OPTIONS = [
|
|
16
|
+
"public",
|
|
17
|
+
"authenticated",
|
|
18
|
+
"anon",
|
|
19
|
+
"admin"
|
|
20
|
+
];
|
|
21
|
+
var POLICY_PRESETS = [
|
|
22
|
+
{
|
|
23
|
+
id: "public_read",
|
|
24
|
+
label: "Enable read access to everyone",
|
|
25
|
+
description: "Anyone can read data, regardless of authentication status.",
|
|
26
|
+
policyname: "Enable read access for all users",
|
|
27
|
+
cmd: "SELECT",
|
|
28
|
+
permissive: "PERMISSIVE",
|
|
29
|
+
roles: ["public"],
|
|
30
|
+
qual: "true",
|
|
31
|
+
with_check: ""
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
id: "auth_read",
|
|
35
|
+
label: "Enable read access for authenticated users only",
|
|
36
|
+
description: "Only logged-in users are allowed to read data.",
|
|
37
|
+
policyname: "Enable read access for authenticated users",
|
|
38
|
+
cmd: "SELECT",
|
|
39
|
+
permissive: "PERMISSIVE",
|
|
40
|
+
roles: ["authenticated"],
|
|
41
|
+
qual: "true",
|
|
42
|
+
with_check: ""
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
id: "auth_insert",
|
|
46
|
+
label: "Enable insert for authenticated users only",
|
|
47
|
+
description: "Only logged-in users are allowed to insert new data.",
|
|
48
|
+
policyname: "Enable insert for authenticated users only",
|
|
49
|
+
cmd: "INSERT",
|
|
50
|
+
permissive: "PERMISSIVE",
|
|
51
|
+
roles: ["authenticated"],
|
|
52
|
+
qual: "",
|
|
53
|
+
with_check: "true"
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
id: "user_select_own",
|
|
57
|
+
label: "Users can read their own rows",
|
|
58
|
+
description: "Users can only read rows where the user_id matches their auth.uid()",
|
|
59
|
+
policyname: "Users can select their own data",
|
|
60
|
+
cmd: "SELECT",
|
|
61
|
+
permissive: "PERMISSIVE",
|
|
62
|
+
roles: ["authenticated"],
|
|
63
|
+
qual: "auth.uid() = user_id",
|
|
64
|
+
with_check: ""
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
id: "user_update_own",
|
|
68
|
+
label: "Users can update their own rows",
|
|
69
|
+
description: "Users can only update rows where the user_id matches their auth.uid()",
|
|
70
|
+
policyname: "Users can update their own data",
|
|
71
|
+
cmd: "UPDATE",
|
|
72
|
+
permissive: "PERMISSIVE",
|
|
73
|
+
roles: ["authenticated"],
|
|
74
|
+
qual: "auth.uid() = user_id",
|
|
75
|
+
with_check: "auth.uid() = user_id"
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
id: "user_delete_own",
|
|
79
|
+
label: "Users can delete their own rows",
|
|
80
|
+
description: "Users can only delete rows where the user_id matches their auth.uid()",
|
|
81
|
+
policyname: "Users can delete their own data",
|
|
82
|
+
cmd: "DELETE",
|
|
83
|
+
permissive: "PERMISSIVE",
|
|
84
|
+
roles: ["authenticated"],
|
|
85
|
+
qual: "auth.uid() = user_id",
|
|
86
|
+
with_check: ""
|
|
87
|
+
}
|
|
88
|
+
];
|
|
89
|
+
var PolicyEditor = ({ policy, schema, table, onSave, onCancel }) => {
|
|
90
|
+
const { t } = useTranslation();
|
|
91
|
+
const [name, setName] = useState("");
|
|
92
|
+
const [helpOpen, setHelpOpen] = useState(false);
|
|
93
|
+
const [behavior, setBehavior] = useState("PERMISSIVE");
|
|
94
|
+
const [command, setCommand] = useState("ALL");
|
|
95
|
+
const [roles, setRoles] = useState(["public"]);
|
|
96
|
+
const [usingExpr, setUsingExpr] = useState("");
|
|
97
|
+
const [checkExpr, setCheckExpr] = useState("");
|
|
98
|
+
const [selectedPreset, setSelectedPreset] = useState("");
|
|
99
|
+
useEffect(() => {
|
|
100
|
+
if (policy) {
|
|
101
|
+
setName(policy.policyname || "");
|
|
102
|
+
setBehavior(policy.permissive || "PERMISSIVE");
|
|
103
|
+
setCommand(policy.cmd || "ALL");
|
|
104
|
+
setRoles(policy.roles ? Array.isArray(policy.roles) ? policy.roles : [policy.roles] : ["public"]);
|
|
105
|
+
setUsingExpr(policy.qual || "");
|
|
106
|
+
setCheckExpr(policy.with_check || "");
|
|
107
|
+
} else {
|
|
108
|
+
setName("");
|
|
109
|
+
setBehavior("PERMISSIVE");
|
|
110
|
+
setCommand("ALL");
|
|
111
|
+
setRoles(["public"]);
|
|
112
|
+
setUsingExpr("");
|
|
113
|
+
setCheckExpr("");
|
|
114
|
+
setSelectedPreset("");
|
|
115
|
+
}
|
|
116
|
+
}, [policy]);
|
|
117
|
+
const handlePresetChange = (presetId) => {
|
|
118
|
+
const preset = POLICY_PRESETS.find((p) => p.id === presetId);
|
|
119
|
+
if (!preset) return;
|
|
120
|
+
setSelectedPreset(presetId);
|
|
121
|
+
setName(preset.policyname);
|
|
122
|
+
setBehavior(preset.permissive);
|
|
123
|
+
setCommand(preset.cmd);
|
|
124
|
+
setRoles(preset.roles);
|
|
125
|
+
setUsingExpr(preset.qual);
|
|
126
|
+
setCheckExpr(preset.with_check);
|
|
127
|
+
};
|
|
128
|
+
const showCheck = command === "ALL" || command === "INSERT" || command === "UPDATE";
|
|
129
|
+
const handleSave = () => {
|
|
130
|
+
onSave({
|
|
131
|
+
policyname: name,
|
|
132
|
+
permissive: behavior,
|
|
133
|
+
cmd: command,
|
|
134
|
+
roles,
|
|
135
|
+
qual: usingExpr,
|
|
136
|
+
with_check: showCheck ? checkExpr : null
|
|
137
|
+
});
|
|
138
|
+
};
|
|
139
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
140
|
+
/* @__PURE__ */ jsxs(DialogTitle, {
|
|
141
|
+
className: "flex justify-between items-center w-full",
|
|
142
|
+
variant: "h6",
|
|
143
|
+
children: [/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("div", { children: policy ? t("studio_policy_edit") : t("studio_policy_create") }), /* @__PURE__ */ jsxs("div", {
|
|
144
|
+
className: "text-sm font-normal text-text-secondary dark:text-text-secondary-dark tracking-wide mt-1",
|
|
145
|
+
children: [
|
|
146
|
+
t("studio_policy_defining_rules"),
|
|
147
|
+
" ",
|
|
148
|
+
/* @__PURE__ */ jsxs("span", {
|
|
149
|
+
className: "font-mono text-primary bg-primary-bg dark:bg-primary-bg-dark px-1 py-0.5 rounded",
|
|
150
|
+
children: [
|
|
151
|
+
schema,
|
|
152
|
+
".",
|
|
153
|
+
table
|
|
154
|
+
]
|
|
155
|
+
})
|
|
156
|
+
]
|
|
157
|
+
})] }), /* @__PURE__ */ jsx(IconButton, {
|
|
158
|
+
size: "small",
|
|
159
|
+
onClick: () => setHelpOpen(true),
|
|
160
|
+
children: /* @__PURE__ */ jsx(HelpCircleIcon, { size: iconSize.smallest })
|
|
161
|
+
})]
|
|
162
|
+
}),
|
|
163
|
+
/* @__PURE__ */ jsx(DialogContent, {
|
|
164
|
+
className: "p-4 md:p-6 border-t dark:border-surface-950 bg-surface-50 dark:bg-surface-950",
|
|
165
|
+
includeMargin: false,
|
|
166
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
167
|
+
className: "max-w-4xl mx-auto",
|
|
168
|
+
children: [/* @__PURE__ */ jsxs(Paper, {
|
|
169
|
+
className: cls("p-4 md:p-6 flex flex-col gap-6 bg-white dark:bg-surface-900 border-none sm:border-solid rounded-none sm:rounded-xl", defaultBorderMixin),
|
|
170
|
+
children: [
|
|
171
|
+
!policy && /* @__PURE__ */ jsxs("div", {
|
|
172
|
+
className: "flex flex-col gap-1.5 bg-primary/5 dark:bg-primary-bg-dark/20 p-3 sm:p-4 rounded-lg border border-primary/10 dark:border-primary/20",
|
|
173
|
+
children: [/* @__PURE__ */ jsx(Typography, {
|
|
174
|
+
variant: "caption",
|
|
175
|
+
className: "text-primary dark:text-primary-light uppercase tracking-wider",
|
|
176
|
+
children: t("studio_policy_template")
|
|
177
|
+
}), /* @__PURE__ */ jsx(Select, {
|
|
178
|
+
size: "small",
|
|
179
|
+
value: selectedPreset,
|
|
180
|
+
onValueChange: handlePresetChange,
|
|
181
|
+
position: "item-aligned",
|
|
182
|
+
placeholder: t("studio_policy_select_template"),
|
|
183
|
+
className: "bg-white dark:bg-surface-950",
|
|
184
|
+
children: POLICY_PRESETS.map((preset) => /* @__PURE__ */ jsx(SelectItem, {
|
|
185
|
+
value: preset.id,
|
|
186
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
187
|
+
className: "flex flex-col text-left",
|
|
188
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
189
|
+
className: "text-sm",
|
|
190
|
+
children: preset.label
|
|
191
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
192
|
+
className: "text-xs text-text-secondary dark:text-text-secondary-dark",
|
|
193
|
+
children: preset.description
|
|
194
|
+
})]
|
|
195
|
+
})
|
|
196
|
+
}, preset.id))
|
|
197
|
+
})]
|
|
198
|
+
}),
|
|
199
|
+
/* @__PURE__ */ jsxs("div", {
|
|
200
|
+
className: "grid grid-cols-1 md:grid-cols-2 gap-4 sm:gap-6",
|
|
201
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
202
|
+
className: "flex flex-col gap-1.5",
|
|
203
|
+
children: [/* @__PURE__ */ jsx(Typography, {
|
|
204
|
+
variant: "caption",
|
|
205
|
+
className: "uppercase tracking-wider text-text-secondary",
|
|
206
|
+
children: t("studio_policy_name")
|
|
207
|
+
}), /* @__PURE__ */ jsx(TextField, {
|
|
208
|
+
value: name,
|
|
209
|
+
onChange: (e) => setName(e.target.value),
|
|
210
|
+
placeholder: t("studio_policy_name_placeholder"),
|
|
211
|
+
className: "w-full"
|
|
212
|
+
})]
|
|
213
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
214
|
+
className: "flex flex-col gap-1.5",
|
|
215
|
+
children: [/* @__PURE__ */ jsxs(Typography, {
|
|
216
|
+
variant: "caption",
|
|
217
|
+
className: "uppercase tracking-wider text-text-secondary",
|
|
218
|
+
children: [
|
|
219
|
+
t("studio_policy_behavior"),
|
|
220
|
+
" ",
|
|
221
|
+
/* @__PURE__ */ jsx("code", {
|
|
222
|
+
className: "text-[10px] bg-surface-200 dark:bg-surface-950 text-text-secondary dark:text-text-secondary-dark px-1 py-0.5 rounded ml-1",
|
|
223
|
+
children: "AS"
|
|
224
|
+
})
|
|
225
|
+
]
|
|
226
|
+
}), /* @__PURE__ */ jsxs(Select, {
|
|
227
|
+
value: behavior,
|
|
228
|
+
onValueChange: (val) => setBehavior(val),
|
|
229
|
+
position: "item-aligned",
|
|
230
|
+
children: [/* @__PURE__ */ jsx(SelectItem, {
|
|
231
|
+
value: "PERMISSIVE",
|
|
232
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
233
|
+
className: "flex flex-col text-left",
|
|
234
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
235
|
+
className: "",
|
|
236
|
+
children: t("studio_policy_permissive")
|
|
237
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
238
|
+
className: "text-xs text-text-secondary dark:text-text-secondary-dark",
|
|
239
|
+
children: t("studio_policy_permissive_desc")
|
|
240
|
+
})]
|
|
241
|
+
})
|
|
242
|
+
}), /* @__PURE__ */ jsx(SelectItem, {
|
|
243
|
+
value: "RESTRICTIVE",
|
|
244
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
245
|
+
className: "flex flex-col text-left",
|
|
246
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
247
|
+
className: "",
|
|
248
|
+
children: t("studio_policy_restrictive")
|
|
249
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
250
|
+
className: "text-xs text-text-secondary dark:text-text-secondary-dark",
|
|
251
|
+
children: t("studio_policy_restrictive_desc")
|
|
252
|
+
})]
|
|
253
|
+
})
|
|
254
|
+
})]
|
|
255
|
+
})]
|
|
256
|
+
})]
|
|
257
|
+
}),
|
|
258
|
+
/* @__PURE__ */ jsxs("div", {
|
|
259
|
+
className: "flex flex-col gap-1.5",
|
|
260
|
+
children: [/* @__PURE__ */ jsxs(Typography, {
|
|
261
|
+
variant: "caption",
|
|
262
|
+
className: "uppercase tracking-wider text-text-secondary",
|
|
263
|
+
children: [
|
|
264
|
+
t("studio_policy_command"),
|
|
265
|
+
" ",
|
|
266
|
+
/* @__PURE__ */ jsx("code", {
|
|
267
|
+
className: "text-[10px] bg-surface-200 dark:bg-surface-950 text-text-secondary dark:text-text-secondary-dark px-1 py-0.5 rounded ml-1",
|
|
268
|
+
children: "FOR"
|
|
269
|
+
})
|
|
270
|
+
]
|
|
271
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
272
|
+
className: "flex flex-wrap gap-1.5",
|
|
273
|
+
children: COMMAND_OPTIONS.map((cmd) => /* @__PURE__ */ jsx(Button, {
|
|
274
|
+
size: "small",
|
|
275
|
+
variant: command === cmd ? "filled" : "text",
|
|
276
|
+
color: "neutral",
|
|
277
|
+
onClick: () => setCommand(cmd),
|
|
278
|
+
className: "min-w-[80px]",
|
|
279
|
+
children: cmd
|
|
280
|
+
}, cmd))
|
|
281
|
+
})]
|
|
282
|
+
}),
|
|
283
|
+
/* @__PURE__ */ jsxs("div", {
|
|
284
|
+
className: "flex flex-col gap-1.5",
|
|
285
|
+
children: [/* @__PURE__ */ jsxs(Typography, {
|
|
286
|
+
variant: "caption",
|
|
287
|
+
className: "uppercase tracking-wider text-text-secondary",
|
|
288
|
+
children: [
|
|
289
|
+
t("studio_policy_target_roles"),
|
|
290
|
+
" ",
|
|
291
|
+
/* @__PURE__ */ jsx("code", {
|
|
292
|
+
className: "text-[10px] bg-surface-200 dark:bg-surface-950 text-text-secondary dark:text-text-secondary-dark px-1 py-0.5 rounded ml-1",
|
|
293
|
+
children: "TO"
|
|
294
|
+
})
|
|
295
|
+
]
|
|
296
|
+
}), /* @__PURE__ */ jsx(MultiSelect, {
|
|
297
|
+
size: "small",
|
|
298
|
+
value: roles,
|
|
299
|
+
onValueChange: setRoles,
|
|
300
|
+
placeholder: t("studio_policy_roles_placeholder"),
|
|
301
|
+
children: ROLE_OPTIONS.map((role) => /* @__PURE__ */ jsx(MultiSelectItem, {
|
|
302
|
+
value: role,
|
|
303
|
+
children: role
|
|
304
|
+
}, role))
|
|
305
|
+
})]
|
|
306
|
+
})
|
|
307
|
+
]
|
|
308
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
309
|
+
className: "mt-8 flex flex-col gap-4",
|
|
310
|
+
children: [command !== "INSERT" && /* @__PURE__ */ jsxs("div", {
|
|
311
|
+
className: "flex flex-col gap-1.5",
|
|
312
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
313
|
+
className: "flex flex-col gap-0.5",
|
|
314
|
+
children: [/* @__PURE__ */ jsx(Typography, {
|
|
315
|
+
variant: "caption",
|
|
316
|
+
className: "uppercase tracking-wider text-text-secondary",
|
|
317
|
+
children: t("studio_policy_using_expr")
|
|
318
|
+
}), /* @__PURE__ */ jsx(Typography, {
|
|
319
|
+
variant: "caption",
|
|
320
|
+
className: "text-text-secondary opacity-70",
|
|
321
|
+
children: t("studio_policy_using_expr_desc")
|
|
322
|
+
})]
|
|
323
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
324
|
+
className: cls("h-32 border rounded-md overflow-hidden bg-white dark:bg-[#1e1e1e]", defaultBorderMixin),
|
|
325
|
+
children: /* @__PURE__ */ jsx(MonacoEditor, {
|
|
326
|
+
value: usingExpr,
|
|
327
|
+
onChange: (v) => setUsingExpr(v || ""),
|
|
328
|
+
readOnly: false,
|
|
329
|
+
autoFocus: true
|
|
330
|
+
})
|
|
331
|
+
})]
|
|
332
|
+
}), showCheck && /* @__PURE__ */ jsxs("div", {
|
|
333
|
+
className: "flex flex-col gap-1.5 mt-2",
|
|
334
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
335
|
+
className: "flex flex-col gap-0.5",
|
|
336
|
+
children: [/* @__PURE__ */ jsx(Typography, {
|
|
337
|
+
variant: "caption",
|
|
338
|
+
className: "uppercase tracking-wider text-text-secondary",
|
|
339
|
+
children: t("studio_policy_check_expr")
|
|
340
|
+
}), /* @__PURE__ */ jsx(Typography, {
|
|
341
|
+
variant: "caption",
|
|
342
|
+
className: "text-text-secondary opacity-70",
|
|
343
|
+
children: t("studio_policy_check_expr_desc")
|
|
344
|
+
})]
|
|
345
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
346
|
+
className: cls("h-32 border rounded-md overflow-hidden bg-white dark:bg-[#1e1e1e]", defaultBorderMixin),
|
|
347
|
+
children: /* @__PURE__ */ jsx(MonacoEditor, {
|
|
348
|
+
value: checkExpr,
|
|
349
|
+
onChange: (v) => setCheckExpr(v || ""),
|
|
350
|
+
readOnly: false,
|
|
351
|
+
autoFocus: command === "INSERT"
|
|
352
|
+
})
|
|
353
|
+
})]
|
|
354
|
+
})]
|
|
355
|
+
})]
|
|
356
|
+
})
|
|
357
|
+
}),
|
|
358
|
+
/* @__PURE__ */ jsxs(DialogActions, { children: [/* @__PURE__ */ jsx(Button, {
|
|
359
|
+
size: "small",
|
|
360
|
+
variant: "text",
|
|
361
|
+
color: "neutral",
|
|
362
|
+
onClick: onCancel,
|
|
363
|
+
children: t("studio_policy_cancel")
|
|
364
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
365
|
+
size: "small",
|
|
366
|
+
variant: "filled",
|
|
367
|
+
color: "primary",
|
|
368
|
+
onClick: handleSave,
|
|
369
|
+
disabled: !name,
|
|
370
|
+
children: t("studio_policy_save")
|
|
371
|
+
})] }),
|
|
372
|
+
/* @__PURE__ */ jsxs(Dialog, {
|
|
373
|
+
open: helpOpen,
|
|
374
|
+
onOpenChange: setHelpOpen,
|
|
375
|
+
maxWidth: "3xl",
|
|
376
|
+
children: [
|
|
377
|
+
/* @__PURE__ */ jsx(DialogTitle, {
|
|
378
|
+
hidden: true,
|
|
379
|
+
children: "Row-Level Security Help"
|
|
380
|
+
}),
|
|
381
|
+
/* @__PURE__ */ jsxs(DialogContent, {
|
|
382
|
+
className: "p-4 sm:p-6 lg:p-8 flex flex-col gap-6",
|
|
383
|
+
children: [
|
|
384
|
+
/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx(Typography, {
|
|
385
|
+
variant: "h5",
|
|
386
|
+
className: "mb-2",
|
|
387
|
+
children: t("studio_policy_help_title")
|
|
388
|
+
}), /* @__PURE__ */ jsx(Typography, {
|
|
389
|
+
className: "text-text-secondary dark:text-text-secondary-dark",
|
|
390
|
+
children: t("studio_policy_help_intro")
|
|
391
|
+
})] }),
|
|
392
|
+
/* @__PURE__ */ jsxs("div", {
|
|
393
|
+
className: "flex flex-col gap-4",
|
|
394
|
+
children: [
|
|
395
|
+
/* @__PURE__ */ jsxs(Paper, {
|
|
396
|
+
className: cls("p-4 sm:p-5 flex flex-col gap-1", defaultBorderMixin),
|
|
397
|
+
children: [/* @__PURE__ */ jsx(Typography, {
|
|
398
|
+
variant: "subtitle2",
|
|
399
|
+
className: "text-primary dark:text-primary-light font-medium",
|
|
400
|
+
children: t("studio_policy_help_step1_title")
|
|
401
|
+
}), /* @__PURE__ */ jsx(Typography, {
|
|
402
|
+
variant: "body2",
|
|
403
|
+
className: "text-text-secondary dark:text-text-secondary-dark",
|
|
404
|
+
children: t("studio_policy_help_step1_desc")
|
|
405
|
+
})]
|
|
406
|
+
}),
|
|
407
|
+
/* @__PURE__ */ jsxs(Paper, {
|
|
408
|
+
className: cls("p-4 sm:p-5 flex flex-col gap-1", defaultBorderMixin),
|
|
409
|
+
children: [
|
|
410
|
+
/* @__PURE__ */ jsx(Typography, {
|
|
411
|
+
variant: "subtitle2",
|
|
412
|
+
className: "text-primary dark:text-primary-light font-medium",
|
|
413
|
+
children: t("studio_policy_help_step2_title")
|
|
414
|
+
}),
|
|
415
|
+
/* @__PURE__ */ jsx(Typography, {
|
|
416
|
+
variant: "body2",
|
|
417
|
+
className: "text-text-secondary dark:text-text-secondary-dark mb-1",
|
|
418
|
+
children: t("studio_policy_help_step2_desc")
|
|
419
|
+
}),
|
|
420
|
+
/* @__PURE__ */ jsxs("ul", {
|
|
421
|
+
className: "list-disc pl-5 space-y-1 text-sm text-text-secondary dark:text-text-secondary-dark",
|
|
422
|
+
children: [
|
|
423
|
+
/* @__PURE__ */ jsxs("li", { children: [
|
|
424
|
+
/* @__PURE__ */ jsx("strong", { children: "public" }),
|
|
425
|
+
": ",
|
|
426
|
+
t("studio_policy_help_role_public")
|
|
427
|
+
] }),
|
|
428
|
+
/* @__PURE__ */ jsxs("li", { children: [
|
|
429
|
+
/* @__PURE__ */ jsx("strong", { children: "authenticated" }),
|
|
430
|
+
": ",
|
|
431
|
+
t("studio_policy_help_role_authenticated")
|
|
432
|
+
] }),
|
|
433
|
+
/* @__PURE__ */ jsxs("li", { children: [
|
|
434
|
+
/* @__PURE__ */ jsx("strong", { children: "anon" }),
|
|
435
|
+
": ",
|
|
436
|
+
t("studio_policy_help_role_anon")
|
|
437
|
+
] })
|
|
438
|
+
]
|
|
439
|
+
})
|
|
440
|
+
]
|
|
441
|
+
}),
|
|
442
|
+
/* @__PURE__ */ jsxs(Paper, {
|
|
443
|
+
className: cls("p-4 sm:p-5 flex flex-col gap-1", defaultBorderMixin),
|
|
444
|
+
children: [
|
|
445
|
+
/* @__PURE__ */ jsx(Typography, {
|
|
446
|
+
variant: "subtitle2",
|
|
447
|
+
className: "text-primary dark:text-primary-light font-medium",
|
|
448
|
+
children: t("studio_policy_help_step3_title")
|
|
449
|
+
}),
|
|
450
|
+
/* @__PURE__ */ jsx(Typography, {
|
|
451
|
+
variant: "body2",
|
|
452
|
+
className: "text-text-secondary dark:text-text-secondary-dark mb-1",
|
|
453
|
+
children: t("studio_policy_help_step3_desc")
|
|
454
|
+
}),
|
|
455
|
+
/* @__PURE__ */ jsx("div", {
|
|
456
|
+
className: cls("bg-surface-100 dark:bg-surface-950 px-3 py-2 rounded-md font-mono text-sm my-2", defaultBorderMixin),
|
|
457
|
+
children: "Example: auth.uid() = user_id"
|
|
458
|
+
}),
|
|
459
|
+
/* @__PURE__ */ jsx(Typography, {
|
|
460
|
+
variant: "caption",
|
|
461
|
+
className: "text-text-secondary dark:text-text-secondary-dark",
|
|
462
|
+
children: t("studio_policy_help_step3_example")
|
|
463
|
+
})
|
|
464
|
+
]
|
|
465
|
+
}),
|
|
466
|
+
/* @__PURE__ */ jsxs(Paper, {
|
|
467
|
+
className: cls("p-4 sm:p-5 flex flex-col gap-1", defaultBorderMixin),
|
|
468
|
+
children: [
|
|
469
|
+
/* @__PURE__ */ jsx(Typography, {
|
|
470
|
+
variant: "subtitle2",
|
|
471
|
+
className: "text-primary dark:text-primary-light font-medium",
|
|
472
|
+
children: t("studio_policy_help_step4_title")
|
|
473
|
+
}),
|
|
474
|
+
/* @__PURE__ */ jsx(Typography, {
|
|
475
|
+
variant: "body2",
|
|
476
|
+
className: "text-text-secondary dark:text-text-secondary-dark mb-1",
|
|
477
|
+
children: t("studio_policy_help_step4_desc")
|
|
478
|
+
}),
|
|
479
|
+
/* @__PURE__ */ jsx("div", {
|
|
480
|
+
className: cls("bg-surface-100 dark:bg-surface-950 px-3 py-2 rounded-md font-mono text-sm my-2", defaultBorderMixin),
|
|
481
|
+
children: "Example: auth.uid() = user_id"
|
|
482
|
+
}),
|
|
483
|
+
/* @__PURE__ */ jsx(Typography, {
|
|
484
|
+
variant: "caption",
|
|
485
|
+
className: "text-text-secondary dark:text-text-secondary-dark",
|
|
486
|
+
children: t("studio_policy_help_step4_example")
|
|
487
|
+
})
|
|
488
|
+
]
|
|
489
|
+
}),
|
|
490
|
+
/* @__PURE__ */ jsxs(Paper, {
|
|
491
|
+
className: cls("p-4 sm:p-5 flex flex-col gap-2 bg-primary/5 dark:bg-primary-bg-dark/10", defaultBorderMixin),
|
|
492
|
+
children: [
|
|
493
|
+
/* @__PURE__ */ jsx(Typography, {
|
|
494
|
+
variant: "subtitle2",
|
|
495
|
+
className: "text-primary dark:text-primary-light font-medium",
|
|
496
|
+
children: t("studio_policy_help_auth_vars_title")
|
|
497
|
+
}),
|
|
498
|
+
/* @__PURE__ */ jsx(Typography, {
|
|
499
|
+
variant: "body2",
|
|
500
|
+
className: "text-text-secondary dark:text-text-secondary-dark",
|
|
501
|
+
children: t("studio_policy_help_auth_vars_desc")
|
|
502
|
+
}),
|
|
503
|
+
/* @__PURE__ */ jsxs("ul", {
|
|
504
|
+
className: "list-disc pl-5 space-y-2 text-sm text-text-secondary dark:text-text-secondary-dark font-normal",
|
|
505
|
+
children: [
|
|
506
|
+
/* @__PURE__ */ jsxs("li", { children: [/* @__PURE__ */ jsx("code", {
|
|
507
|
+
className: "bg-surface-100 dark:bg-surface-950 px-1.5 py-0.5 rounded mr-1 whitespace-nowrap",
|
|
508
|
+
children: "auth.uid()"
|
|
509
|
+
}), /* @__PURE__ */ jsxs("span", {
|
|
510
|
+
className: "block mt-0.5",
|
|
511
|
+
children: ["Returns the current user's ID as text. Example: ", /* @__PURE__ */ jsx("code", {
|
|
512
|
+
className: "bg-surface-100 dark:bg-surface-950 px-1 py-0.5 rounded text-[11px]",
|
|
513
|
+
children: "auth.uid() = user_id"
|
|
514
|
+
})]
|
|
515
|
+
})] }),
|
|
516
|
+
/* @__PURE__ */ jsxs("li", { children: [/* @__PURE__ */ jsx("code", {
|
|
517
|
+
className: "bg-surface-100 dark:bg-surface-950 px-1.5 py-0.5 rounded mr-1 whitespace-nowrap",
|
|
518
|
+
children: "auth.jwt()"
|
|
519
|
+
}), /* @__PURE__ */ jsxs("span", {
|
|
520
|
+
className: "block mt-0.5",
|
|
521
|
+
children: ["Returns the full JWT payload as JSONB so you can check custom claims. Example: ", /* @__PURE__ */ jsx("code", {
|
|
522
|
+
className: "bg-surface-100 dark:bg-surface-950 px-1 py-0.5 rounded text-[11px]",
|
|
523
|
+
children: "auth.jwt() ->> 'email' = 'admin@example.com'"
|
|
524
|
+
})]
|
|
525
|
+
})] }),
|
|
526
|
+
/* @__PURE__ */ jsxs("li", { children: [/* @__PURE__ */ jsx("code", {
|
|
527
|
+
className: "bg-surface-100 dark:bg-surface-950 px-1.5 py-0.5 rounded mr-1 whitespace-nowrap",
|
|
528
|
+
children: "auth.roles()"
|
|
529
|
+
}), /* @__PURE__ */ jsxs("span", {
|
|
530
|
+
className: "block mt-0.5",
|
|
531
|
+
children: ["Returns the user's role IDs as a comma-separated string. Best used with: ", /* @__PURE__ */ jsx("code", {
|
|
532
|
+
className: "bg-surface-100 dark:bg-surface-950 px-1 py-0.5 rounded text-[11px]",
|
|
533
|
+
children: "string_to_array(auth.roles(), ',') @> ARRAY['admin']"
|
|
534
|
+
})]
|
|
535
|
+
})] })
|
|
536
|
+
]
|
|
537
|
+
})
|
|
538
|
+
]
|
|
539
|
+
})
|
|
540
|
+
]
|
|
541
|
+
}),
|
|
542
|
+
/* @__PURE__ */ jsxs("div", {
|
|
543
|
+
className: cls("mt-2 flex flex-col sm:flex-row justify-between items-start sm:items-center bg-primary/5 dark:bg-primary-bg-dark/10 p-4 rounded-xl border border-primary/10 dark:border-primary/20", defaultBorderMixin),
|
|
544
|
+
children: [/* @__PURE__ */ jsx(Typography, {
|
|
545
|
+
variant: "body2",
|
|
546
|
+
className: "text-primary dark:text-primary-light mb-4 sm:mb-0 max-w-md",
|
|
547
|
+
children: t("studio_policy_help_docs_cta")
|
|
548
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
549
|
+
component: "a",
|
|
550
|
+
href: "https://www.postgresql.org/docs/current/sql-createpolicy.html",
|
|
551
|
+
target: "_blank",
|
|
552
|
+
variant: "outlined",
|
|
553
|
+
color: "primary",
|
|
554
|
+
size: "small",
|
|
555
|
+
className: "whitespace-nowrap flex-shrink-0",
|
|
556
|
+
children: t("studio_policy_help_read_docs")
|
|
557
|
+
})]
|
|
558
|
+
})
|
|
559
|
+
]
|
|
560
|
+
}),
|
|
561
|
+
/* @__PURE__ */ jsx(DialogActions, {
|
|
562
|
+
className: "p-4 sm:px-6 sm:pb-6 pt-0 border-t-0",
|
|
563
|
+
children: /* @__PURE__ */ jsx(Button, {
|
|
564
|
+
onClick: () => setHelpOpen(false),
|
|
565
|
+
variant: "filled",
|
|
566
|
+
color: "primary",
|
|
567
|
+
children: t("studio_policy_help_got_it")
|
|
568
|
+
})
|
|
569
|
+
})
|
|
570
|
+
]
|
|
571
|
+
})
|
|
572
|
+
] });
|
|
573
|
+
};
|
|
574
|
+
//#endregion
|
|
575
|
+
//#region src/components/RLSEditor/RLSEditor.tsx
|
|
576
|
+
/**
|
|
577
|
+
* Validates and double-quotes a SQL identifier to prevent injection.
|
|
578
|
+
* Only allows safe Postgres identifiers (letters, digits, underscores).
|
|
579
|
+
* Throws if the identifier contains unsafe characters.
|
|
580
|
+
*/
|
|
581
|
+
function sanitizeSqlIdentifier(name) {
|
|
582
|
+
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)) throw new Error(`Invalid SQL identifier: "${name}". Only letters, digits, and underscores are allowed.`);
|
|
583
|
+
return `"${name}"`;
|
|
584
|
+
}
|
|
585
|
+
var RLSEditor = ({ apiUrl = "" }) => {
|
|
586
|
+
const { databaseAdmin } = useRebaseContext();
|
|
587
|
+
const snackbarController = useSnackbarController();
|
|
588
|
+
const collectionRegistry = useStudioCollectionRegistry();
|
|
589
|
+
const { t } = useTranslation();
|
|
590
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
591
|
+
const [error, setError] = useState(null);
|
|
592
|
+
const [tables, setTables] = useState([]);
|
|
593
|
+
const [selectedTable, setSelectedTable] = useState(null);
|
|
594
|
+
const [activeTab, setActiveTab] = useState(0);
|
|
595
|
+
const [editingPolicy, setEditingPolicy] = useState(null);
|
|
596
|
+
const [sidebarSize, setSidebarSize] = useState(() => {
|
|
597
|
+
try {
|
|
598
|
+
const saved = localStorage.getItem("rebase_rls_editor_sidebar_size");
|
|
599
|
+
return saved !== null ? parseFloat(saved) : 20;
|
|
600
|
+
} catch (e) {
|
|
601
|
+
return 20;
|
|
602
|
+
}
|
|
603
|
+
});
|
|
604
|
+
const [expandedSchemas, setExpandedSchemas] = useState({ public: true });
|
|
605
|
+
const [sidebarTab, setSidebarTab] = useState("tables");
|
|
606
|
+
useEffect(() => {
|
|
607
|
+
try {
|
|
608
|
+
localStorage.setItem("rebase_rls_editor_sidebar_size", sidebarSize.toString());
|
|
609
|
+
} catch (e) {}
|
|
610
|
+
}, [sidebarSize]);
|
|
611
|
+
const fetchRLSData = useCallback(async () => {
|
|
612
|
+
if (!databaseAdmin?.executeSql) {
|
|
613
|
+
setError(t("studio_sql_sql_not_supported"));
|
|
614
|
+
setIsLoading(false);
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
617
|
+
setIsLoading(true);
|
|
618
|
+
setError(null);
|
|
619
|
+
try {
|
|
620
|
+
const tablesResult = await databaseAdmin.executeSql(`
|
|
621
|
+
SELECT
|
|
622
|
+
schemaname,
|
|
623
|
+
tablename,
|
|
624
|
+
rowsecurity
|
|
625
|
+
FROM pg_tables
|
|
626
|
+
WHERE schemaname NOT IN ('information_schema', 'pg_catalog')
|
|
627
|
+
ORDER BY schemaname, tablename;
|
|
628
|
+
`);
|
|
629
|
+
const policiesResult = await databaseAdmin.executeSql(`
|
|
630
|
+
SELECT
|
|
631
|
+
schemaname,
|
|
632
|
+
tablename,
|
|
633
|
+
policyname,
|
|
634
|
+
permissive,
|
|
635
|
+
roles,
|
|
636
|
+
cmd,
|
|
637
|
+
qual,
|
|
638
|
+
with_check
|
|
639
|
+
FROM pg_policies
|
|
640
|
+
WHERE schemaname NOT IN ('information_schema', 'pg_catalog');
|
|
641
|
+
`);
|
|
642
|
+
const extractRows = (result) => {
|
|
643
|
+
if (result && typeof result === "object" && "rows" in result && Array.isArray(result.rows)) return result.rows;
|
|
644
|
+
if (Array.isArray(result)) return result;
|
|
645
|
+
return [];
|
|
646
|
+
};
|
|
647
|
+
const tRows = extractRows(tablesResult);
|
|
648
|
+
const pRows = extractRows(policiesResult);
|
|
649
|
+
const tableMap = {};
|
|
650
|
+
tRows.forEach((tRow) => {
|
|
651
|
+
const t = tRow;
|
|
652
|
+
const schema = t.schemaname || t.SCHEMANAME || "public";
|
|
653
|
+
const table = t.tablename || t.TABLENAME || "";
|
|
654
|
+
const rlsEnabled = t.rowsecurity || t.ROWSECURITY || false;
|
|
655
|
+
const key = `${schema}.${table}`;
|
|
656
|
+
tableMap[key] = {
|
|
657
|
+
schemaName: schema,
|
|
658
|
+
tableName: table,
|
|
659
|
+
rlsEnabled,
|
|
660
|
+
policies: []
|
|
661
|
+
};
|
|
662
|
+
});
|
|
663
|
+
pRows.forEach((pRow) => {
|
|
664
|
+
const p = pRow;
|
|
665
|
+
const schema = p.schemaname || p.SCHEMANAME || "public";
|
|
666
|
+
const table = p.tablename || p.TABLENAME || "";
|
|
667
|
+
const key = `${schema}.${table}`;
|
|
668
|
+
if (tableMap[key]) {
|
|
669
|
+
let parsedRoles = [];
|
|
670
|
+
const r = p.roles || p.ROLES;
|
|
671
|
+
if (Array.isArray(r)) parsedRoles = r;
|
|
672
|
+
else if (typeof r === "string") parsedRoles = r.replace(/^{|}$/g, "").split(",").map((s) => s.trim());
|
|
673
|
+
tableMap[key].policies.push({
|
|
674
|
+
policyname: p.policyname || p.POLICYNAME || "",
|
|
675
|
+
tablename: table,
|
|
676
|
+
permissive: p.permissive || p.PERMISSIVE || "PERMISSIVE",
|
|
677
|
+
roles: parsedRoles,
|
|
678
|
+
cmd: p.cmd || p.CMD || "ALL",
|
|
679
|
+
qual: p.qual || p.QUAL || null,
|
|
680
|
+
with_check: p.with_check || p.WITH_CHECK || null
|
|
681
|
+
});
|
|
682
|
+
}
|
|
683
|
+
});
|
|
684
|
+
const sortedTables = Object.values(tableMap).sort((a, b) => a.tableName.localeCompare(b.tableName));
|
|
685
|
+
setTables(sortedTables);
|
|
686
|
+
if (sortedTables.length > 0 && !selectedTable) setSelectedTable(`${sortedTables[0].schemaName}.${sortedTables[0].tableName}`);
|
|
687
|
+
} catch (e) {
|
|
688
|
+
console.error("RLS fetch error:", e);
|
|
689
|
+
setError("Failed to fetch RLS policies: " + (e instanceof Error ? e.message : String(e)));
|
|
690
|
+
} finally {
|
|
691
|
+
setIsLoading(false);
|
|
692
|
+
}
|
|
693
|
+
}, [databaseAdmin, selectedTable]);
|
|
694
|
+
useEffect(() => {
|
|
695
|
+
setEditingPolicy(null);
|
|
696
|
+
}, [selectedTable]);
|
|
697
|
+
useEffect(() => {
|
|
698
|
+
fetchRLSData();
|
|
699
|
+
}, [fetchRLSData]);
|
|
700
|
+
const activeTableData = useMemo(() => {
|
|
701
|
+
if (!selectedTable) return null;
|
|
702
|
+
return tables.find((t) => `${t.schemaName}.${t.tableName}` === selectedTable) || null;
|
|
703
|
+
}, [selectedTable, tables]);
|
|
704
|
+
const groupedTables = useMemo(() => {
|
|
705
|
+
const groups = {};
|
|
706
|
+
tables.forEach((table) => {
|
|
707
|
+
if (!groups[table.schemaName]) groups[table.schemaName] = [];
|
|
708
|
+
groups[table.schemaName].push(table);
|
|
709
|
+
});
|
|
710
|
+
return groups;
|
|
711
|
+
}, [tables]);
|
|
712
|
+
const activeCollection = useMemo(() => {
|
|
713
|
+
if (!activeTableData) return null;
|
|
714
|
+
return collectionRegistry.collections?.find((c) => c.id === activeTableData.tableName || c.path === activeTableData.tableName || c.table === activeTableData.tableName || c.slug === activeTableData.tableName || c.collectionId === activeTableData.tableName) || null;
|
|
715
|
+
}, [activeTableData, collectionRegistry.collections]);
|
|
716
|
+
const mergedPolicies = useMemo(() => {
|
|
717
|
+
if (!activeTableData) return [];
|
|
718
|
+
const policiesMap = {};
|
|
719
|
+
(activeTableData.policies || []).forEach((p) => {
|
|
720
|
+
policiesMap[p.policyname] = {
|
|
721
|
+
...p,
|
|
722
|
+
status: "live"
|
|
723
|
+
};
|
|
724
|
+
});
|
|
725
|
+
if (activeCollection && isPostgresCollection(activeCollection) && activeCollection.securityRules) activeCollection.securityRules.forEach((rule) => {
|
|
726
|
+
const ruleName = rule.name;
|
|
727
|
+
if (!ruleName) return;
|
|
728
|
+
if (policiesMap[ruleName]) policiesMap[ruleName] = {
|
|
729
|
+
policyname: ruleName,
|
|
730
|
+
tablename: activeTableData.tableName,
|
|
731
|
+
permissive: (rule.mode || "permissive").toUpperCase(),
|
|
732
|
+
cmd: (rule.operation || "ALL").toUpperCase(),
|
|
733
|
+
roles: rule.roles || ["public"],
|
|
734
|
+
qual: rule.using || null,
|
|
735
|
+
with_check: rule.withCheck || null,
|
|
736
|
+
status: "both"
|
|
737
|
+
};
|
|
738
|
+
else policiesMap[ruleName] = {
|
|
739
|
+
policyname: ruleName,
|
|
740
|
+
tablename: activeTableData.tableName,
|
|
741
|
+
permissive: (rule.mode || "permissive").toUpperCase(),
|
|
742
|
+
cmd: (rule.operation || "ALL").toUpperCase(),
|
|
743
|
+
roles: rule.roles || ["public"],
|
|
744
|
+
qual: rule.using || null,
|
|
745
|
+
with_check: rule.withCheck || null,
|
|
746
|
+
status: "code_only"
|
|
747
|
+
};
|
|
748
|
+
});
|
|
749
|
+
return Object.values(policiesMap).sort((a, b) => a.policyname.localeCompare(b.policyname));
|
|
750
|
+
}, [activeTableData, activeCollection]);
|
|
751
|
+
const rlsStats = useMemo(() => {
|
|
752
|
+
return {
|
|
753
|
+
total: tables.length,
|
|
754
|
+
enabled: tables.filter((t) => t.rlsEnabled).length,
|
|
755
|
+
withPolicies: tables.filter((t) => t.policies.length > 0).length,
|
|
756
|
+
totalPolicies: tables.reduce((sum, t) => sum + t.policies.length, 0)
|
|
757
|
+
};
|
|
758
|
+
}, [tables]);
|
|
759
|
+
const renderPolicyTag = (label, value) => {
|
|
760
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
761
|
+
className: "flex items-center gap-1.5 px-2 py-1 rounded-md bg-surface-100 dark:bg-surface-950 border border-surface-200 dark:border-surface-700/50",
|
|
762
|
+
children: [/* @__PURE__ */ jsxs("span", {
|
|
763
|
+
className: "text-[10px] uppercase text-text-secondary dark:text-text-secondary-dark font-medium tracking-wider",
|
|
764
|
+
children: [label, ":"]
|
|
765
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
766
|
+
className: "font-mono text-xs text-text-primary dark:text-text-primary-dark break-all",
|
|
767
|
+
children: value
|
|
768
|
+
})]
|
|
769
|
+
});
|
|
770
|
+
};
|
|
771
|
+
return /* @__PURE__ */ jsx("div", {
|
|
772
|
+
className: "flex h-full w-full bg-white dark:bg-surface-950 overflow-hidden text-text-primary dark:text-text-primary-dark",
|
|
773
|
+
children: /* @__PURE__ */ jsx(ResizablePanels, {
|
|
774
|
+
orientation: "horizontal",
|
|
775
|
+
panelSizePercent: sidebarSize,
|
|
776
|
+
onPanelSizeChange: setSidebarSize,
|
|
777
|
+
minPanelSizePx: 220,
|
|
778
|
+
firstPanel: /* @__PURE__ */ jsxs("div", {
|
|
779
|
+
className: cls("flex flex-col h-full w-full bg-white dark:bg-surface-950 border-r", defaultBorderMixin),
|
|
780
|
+
children: [/* @__PURE__ */ jsxs(Tabs, {
|
|
781
|
+
value: sidebarTab,
|
|
782
|
+
onValueChange: (v) => setSidebarTab(v),
|
|
783
|
+
variant: "boxy",
|
|
784
|
+
className: "border-b border-surface-200 dark:border-surface-950",
|
|
785
|
+
children: [/* @__PURE__ */ jsx(Tab, {
|
|
786
|
+
value: "tables",
|
|
787
|
+
children: "Tables"
|
|
788
|
+
}), /* @__PURE__ */ jsx(Tab, {
|
|
789
|
+
value: "info",
|
|
790
|
+
children: "Info"
|
|
791
|
+
})]
|
|
792
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
793
|
+
className: "flex-grow overflow-hidden relative",
|
|
794
|
+
children: [sidebarTab === "tables" && /* @__PURE__ */ jsxs("div", {
|
|
795
|
+
className: "flex flex-col h-full",
|
|
796
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
797
|
+
className: cls("flex items-center justify-between px-3 py-2 border-b bg-surface-50 dark:bg-surface-900 min-h-[48px]", defaultBorderMixin),
|
|
798
|
+
children: [/* @__PURE__ */ jsx(Typography, {
|
|
799
|
+
variant: "caption",
|
|
800
|
+
className: "font-bold uppercase tracking-wider text-text-disabled dark:text-text-disabled-dark",
|
|
801
|
+
children: t("studio_schema_tables")
|
|
802
|
+
}), /* @__PURE__ */ jsx(IconButton, {
|
|
803
|
+
size: "small",
|
|
804
|
+
onClick: fetchRLSData,
|
|
805
|
+
title: "Refresh",
|
|
806
|
+
children: /* @__PURE__ */ jsx(RefreshCwIcon, { size: iconSize.smallest })
|
|
807
|
+
})]
|
|
808
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
809
|
+
className: "flex-grow overflow-y-auto no-scrollbar p-1",
|
|
810
|
+
children: isLoading && tables.length === 0 ? /* @__PURE__ */ jsx("div", {
|
|
811
|
+
className: "flex justify-center p-4",
|
|
812
|
+
children: /* @__PURE__ */ jsx(CircularProgress, { size: "small" })
|
|
813
|
+
}) : Object.keys(groupedTables).length === 0 ? /* @__PURE__ */ jsx("div", {
|
|
814
|
+
className: "p-4 text-center",
|
|
815
|
+
children: /* @__PURE__ */ jsx(Typography, {
|
|
816
|
+
variant: "caption",
|
|
817
|
+
className: "text-text-disabled dark:text-text-disabled-dark italic",
|
|
818
|
+
children: t("studio_rls_no_tables")
|
|
819
|
+
})
|
|
820
|
+
}) : Object.entries(groupedTables).map(([schemaName, schemaTables]) => /* @__PURE__ */ jsxs("div", {
|
|
821
|
+
className: "mb-2",
|
|
822
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
823
|
+
className: "flex items-center p-1 cursor-pointer hover:bg-surface-100 dark:hover:bg-surface-950 rounded transition-colors",
|
|
824
|
+
onClick: () => setExpandedSchemas((prev) => ({
|
|
825
|
+
...prev,
|
|
826
|
+
[schemaName]: !prev[schemaName]
|
|
827
|
+
})),
|
|
828
|
+
children: [/* @__PURE__ */ jsx("svg", {
|
|
829
|
+
className: cls("w-3 h-3 mr-1 transition-transform", expandedSchemas[schemaName] ? "rotate-90" : ""),
|
|
830
|
+
fill: "currentColor",
|
|
831
|
+
viewBox: "0 0 20 20",
|
|
832
|
+
children: /* @__PURE__ */ jsx("path", { d: "M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" })
|
|
833
|
+
}), /* @__PURE__ */ jsx(Typography, {
|
|
834
|
+
variant: "body2",
|
|
835
|
+
className: "text-text-primary dark:text-text-primary-dark font-medium text-xs truncate flex-grow",
|
|
836
|
+
children: schemaName
|
|
837
|
+
})]
|
|
838
|
+
}), expandedSchemas[schemaName] && /* @__PURE__ */ jsx("div", {
|
|
839
|
+
className: "ml-3 mt-1 space-y-0.5",
|
|
840
|
+
children: schemaTables.map((table) => {
|
|
841
|
+
const key = `${table.schemaName}.${table.tableName}`;
|
|
842
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
843
|
+
onClick: () => setSelectedTable(key),
|
|
844
|
+
className: cls("flex items-center p-1 cursor-pointer rounded transition-colors group relative", selectedTable === key ? "bg-primary/10 text-primary dark:bg-primary/20 dark:text-primary-light" : "hover:bg-surface-100 dark:hover:bg-surface-950 text-text-secondary dark:text-text-secondary-dark"),
|
|
845
|
+
children: [
|
|
846
|
+
/* @__PURE__ */ jsx("svg", {
|
|
847
|
+
className: "w-3.5 h-3.5 mr-1 shrink-0 text-text-disabled dark:text-text-disabled-dark",
|
|
848
|
+
fill: "none",
|
|
849
|
+
stroke: "currentColor",
|
|
850
|
+
viewBox: "0 0 24 24",
|
|
851
|
+
children: /* @__PURE__ */ jsx("path", {
|
|
852
|
+
strokeLinecap: "round",
|
|
853
|
+
strokeLinejoin: "round",
|
|
854
|
+
strokeWidth: 2,
|
|
855
|
+
d: "M3 10h18M3 14h18m-9-4v8m-7 0h14a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"
|
|
856
|
+
})
|
|
857
|
+
}),
|
|
858
|
+
/* @__PURE__ */ jsx(Typography, {
|
|
859
|
+
variant: "body2",
|
|
860
|
+
className: "text-xs truncate flex-1 min-w-0",
|
|
861
|
+
children: table.tableName
|
|
862
|
+
}),
|
|
863
|
+
/* @__PURE__ */ jsxs("div", {
|
|
864
|
+
className: "flex items-center gap-1.5 shrink-0 ml-2",
|
|
865
|
+
children: [table.rlsEnabled ? /* @__PURE__ */ jsx(Tooltip, {
|
|
866
|
+
title: t("studio_rls_enabled"),
|
|
867
|
+
children: /* @__PURE__ */ jsx("div", { className: "w-1.5 h-1.5 rounded-full bg-green-500" })
|
|
868
|
+
}) : /* @__PURE__ */ jsx(Tooltip, {
|
|
869
|
+
title: t("studio_rls_disabled"),
|
|
870
|
+
children: /* @__PURE__ */ jsx("div", { className: "w-1.5 h-1.5 rounded-full bg-orange-400 opacity-50" })
|
|
871
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
872
|
+
className: "text-[10px] opacity-40 group-hover:opacity-100 min-w-[1.2rem] text-right font-medium",
|
|
873
|
+
children: table.policies.length
|
|
874
|
+
})]
|
|
875
|
+
})
|
|
876
|
+
]
|
|
877
|
+
}, key);
|
|
878
|
+
})
|
|
879
|
+
})]
|
|
880
|
+
}, schemaName))
|
|
881
|
+
})]
|
|
882
|
+
}), sidebarTab === "info" && /* @__PURE__ */ jsxs("div", {
|
|
883
|
+
className: "flex flex-col h-full",
|
|
884
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
885
|
+
className: cls("flex items-center justify-between px-3 py-2 border-b bg-surface-50 dark:bg-surface-900 min-h-[48px]", defaultBorderMixin),
|
|
886
|
+
children: /* @__PURE__ */ jsx(Typography, {
|
|
887
|
+
variant: "caption",
|
|
888
|
+
className: "font-bold uppercase tracking-wider text-text-disabled dark:text-text-disabled-dark",
|
|
889
|
+
children: "Overview"
|
|
890
|
+
})
|
|
891
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
892
|
+
className: "flex-grow overflow-y-auto p-3 space-y-3 no-scrollbar",
|
|
893
|
+
children: [
|
|
894
|
+
/* @__PURE__ */ jsxs("div", {
|
|
895
|
+
className: cls("p-3 rounded-lg border bg-white dark:bg-surface-900", defaultBorderMixin),
|
|
896
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
897
|
+
className: "flex items-center gap-2 mb-2",
|
|
898
|
+
children: [/* @__PURE__ */ jsx(ShieldIcon, {
|
|
899
|
+
size: iconSize.smallest,
|
|
900
|
+
className: "text-primary"
|
|
901
|
+
}), /* @__PURE__ */ jsx(Typography, {
|
|
902
|
+
variant: "body2",
|
|
903
|
+
className: "font-semibold text-[13px]",
|
|
904
|
+
children: "RLS Studio"
|
|
905
|
+
})]
|
|
906
|
+
}), /* @__PURE__ */ jsx(Typography, {
|
|
907
|
+
variant: "caption",
|
|
908
|
+
className: "text-text-secondary dark:text-text-secondary-dark text-[11px] leading-relaxed block",
|
|
909
|
+
children: "Manage Row Level Security policies for your PostgreSQL tables. Enable RLS and create fine-grained access policies."
|
|
910
|
+
})]
|
|
911
|
+
}),
|
|
912
|
+
/* @__PURE__ */ jsxs("div", {
|
|
913
|
+
className: "space-y-2",
|
|
914
|
+
children: [
|
|
915
|
+
/* @__PURE__ */ jsxs("div", {
|
|
916
|
+
className: cls("p-2.5 rounded border bg-white dark:bg-surface-900 flex items-center justify-between", defaultBorderMixin),
|
|
917
|
+
children: [/* @__PURE__ */ jsx(Typography, {
|
|
918
|
+
variant: "caption",
|
|
919
|
+
className: "text-text-secondary dark:text-text-secondary-dark text-[11px]",
|
|
920
|
+
children: "Total tables"
|
|
921
|
+
}), /* @__PURE__ */ jsx(Typography, {
|
|
922
|
+
variant: "body2",
|
|
923
|
+
className: "font-mono text-[13px] font-medium",
|
|
924
|
+
children: rlsStats.total
|
|
925
|
+
})]
|
|
926
|
+
}),
|
|
927
|
+
/* @__PURE__ */ jsxs("div", {
|
|
928
|
+
className: cls("p-2.5 rounded border bg-white dark:bg-surface-900 flex items-center justify-between", defaultBorderMixin),
|
|
929
|
+
children: [/* @__PURE__ */ jsx(Typography, {
|
|
930
|
+
variant: "caption",
|
|
931
|
+
className: "text-text-secondary dark:text-text-secondary-dark text-[11px]",
|
|
932
|
+
children: "RLS enabled"
|
|
933
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
934
|
+
className: "flex items-center gap-1.5",
|
|
935
|
+
children: [/* @__PURE__ */ jsx("div", { className: "w-1.5 h-1.5 rounded-full bg-green-500" }), /* @__PURE__ */ jsx(Typography, {
|
|
936
|
+
variant: "body2",
|
|
937
|
+
className: "font-mono text-[13px] font-medium",
|
|
938
|
+
children: rlsStats.enabled
|
|
939
|
+
})]
|
|
940
|
+
})]
|
|
941
|
+
}),
|
|
942
|
+
/* @__PURE__ */ jsxs("div", {
|
|
943
|
+
className: cls("p-2.5 rounded border bg-white dark:bg-surface-900 flex items-center justify-between", defaultBorderMixin),
|
|
944
|
+
children: [/* @__PURE__ */ jsx(Typography, {
|
|
945
|
+
variant: "caption",
|
|
946
|
+
className: "text-text-secondary dark:text-text-secondary-dark text-[11px]",
|
|
947
|
+
children: "Tables with policies"
|
|
948
|
+
}), /* @__PURE__ */ jsx(Typography, {
|
|
949
|
+
variant: "body2",
|
|
950
|
+
className: "font-mono text-[13px] font-medium",
|
|
951
|
+
children: rlsStats.withPolicies
|
|
952
|
+
})]
|
|
953
|
+
}),
|
|
954
|
+
/* @__PURE__ */ jsxs("div", {
|
|
955
|
+
className: cls("p-2.5 rounded border bg-white dark:bg-surface-900 flex items-center justify-between", defaultBorderMixin),
|
|
956
|
+
children: [/* @__PURE__ */ jsx(Typography, {
|
|
957
|
+
variant: "caption",
|
|
958
|
+
className: "text-text-secondary dark:text-text-secondary-dark text-[11px]",
|
|
959
|
+
children: "Total policies"
|
|
960
|
+
}), /* @__PURE__ */ jsx(Typography, {
|
|
961
|
+
variant: "body2",
|
|
962
|
+
className: "font-mono text-[13px] font-medium",
|
|
963
|
+
children: rlsStats.totalPolicies
|
|
964
|
+
})]
|
|
965
|
+
})
|
|
966
|
+
]
|
|
967
|
+
}),
|
|
968
|
+
rlsStats.total - rlsStats.enabled > 0 && /* @__PURE__ */ jsxs("div", {
|
|
969
|
+
className: cls("p-2.5 rounded border border-yellow-200 dark:border-yellow-900/50 bg-yellow-50 dark:bg-yellow-900/20 flex items-start gap-2", defaultBorderMixin),
|
|
970
|
+
children: [/* @__PURE__ */ jsx(AlertTriangleIcon, {
|
|
971
|
+
size: 14,
|
|
972
|
+
className: "text-yellow-600 dark:text-yellow-500 mt-0.5 shrink-0"
|
|
973
|
+
}), /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsxs(Typography, {
|
|
974
|
+
variant: "caption",
|
|
975
|
+
className: "text-yellow-800 dark:text-yellow-400 text-[11px] font-semibold block",
|
|
976
|
+
children: [
|
|
977
|
+
rlsStats.total - rlsStats.enabled,
|
|
978
|
+
" table",
|
|
979
|
+
rlsStats.total - rlsStats.enabled > 1 ? "s" : "",
|
|
980
|
+
" without RLS"
|
|
981
|
+
]
|
|
982
|
+
}), /* @__PURE__ */ jsx(Typography, {
|
|
983
|
+
variant: "caption",
|
|
984
|
+
className: "text-yellow-700 dark:text-yellow-600 text-[10px] block mt-0.5",
|
|
985
|
+
children: "These tables have no row-level access control. If auth enforcement is disabled, data may be publicly accessible."
|
|
986
|
+
})] })]
|
|
987
|
+
}),
|
|
988
|
+
rlsStats.enabled > 0 && rlsStats.enabled - rlsStats.withPolicies > 0 && /* @__PURE__ */ jsxs("div", {
|
|
989
|
+
className: cls("p-2.5 rounded border border-blue-200 dark:border-blue-900/50 bg-blue-50 dark:bg-blue-900/20 flex items-start gap-2", defaultBorderMixin),
|
|
990
|
+
children: [/* @__PURE__ */ jsx(ShieldIcon, {
|
|
991
|
+
size: 14,
|
|
992
|
+
className: "text-blue-600 dark:text-blue-400 mt-0.5 shrink-0"
|
|
993
|
+
}), /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsxs(Typography, {
|
|
994
|
+
variant: "caption",
|
|
995
|
+
className: "text-blue-800 dark:text-blue-300 text-[11px] font-semibold block",
|
|
996
|
+
children: [
|
|
997
|
+
rlsStats.enabled - rlsStats.withPolicies,
|
|
998
|
+
" table",
|
|
999
|
+
rlsStats.enabled - rlsStats.withPolicies > 1 ? "s" : "",
|
|
1000
|
+
" with RLS but no policies"
|
|
1001
|
+
]
|
|
1002
|
+
}), /* @__PURE__ */ jsx(Typography, {
|
|
1003
|
+
variant: "caption",
|
|
1004
|
+
className: "text-blue-700 dark:text-blue-500 text-[10px] block mt-0.5",
|
|
1005
|
+
children: "RLS is enabled but no permissive policies exist. All access is denied by default (Postgres deny-all)."
|
|
1006
|
+
})] })]
|
|
1007
|
+
})
|
|
1008
|
+
]
|
|
1009
|
+
})]
|
|
1010
|
+
})]
|
|
1011
|
+
})]
|
|
1012
|
+
}),
|
|
1013
|
+
secondPanel: /* @__PURE__ */ jsxs("div", {
|
|
1014
|
+
className: "flex-grow flex flex-col min-w-0 h-full w-full bg-white dark:bg-surface-950",
|
|
1015
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
1016
|
+
className: cls("flex items-center justify-between pr-2 border-b bg-white dark:bg-surface-950 min-h-[46px]", defaultBorderMixin),
|
|
1017
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
1018
|
+
className: "flex items-center flex-grow overflow-hidden px-4",
|
|
1019
|
+
children: [/* @__PURE__ */ jsx(Typography, {
|
|
1020
|
+
variant: "subtitle2",
|
|
1021
|
+
className: "font-mono text-text-secondary dark:text-text-secondary-dark truncate",
|
|
1022
|
+
children: activeTableData ? `${activeTableData.schemaName}.${activeTableData.tableName}` : t("studio_rls_select_table")
|
|
1023
|
+
}), activeTableData && /* @__PURE__ */ jsx("div", {
|
|
1024
|
+
className: "ml-3",
|
|
1025
|
+
children: activeTableData.rlsEnabled ? /* @__PURE__ */ jsx(Chip, {
|
|
1026
|
+
size: "smallest",
|
|
1027
|
+
className: "bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-400 border-green-200 dark:border-green-800",
|
|
1028
|
+
children: t("studio_rls_enabled")
|
|
1029
|
+
}) : /* @__PURE__ */ jsx(Chip, {
|
|
1030
|
+
size: "smallest",
|
|
1031
|
+
className: "bg-yellow-100 text-yellow-800 dark:bg-yellow-900/30 dark:text-yellow-400 border-yellow-200 dark:border-yellow-800",
|
|
1032
|
+
children: t("studio_rls_disabled")
|
|
1033
|
+
})
|
|
1034
|
+
})]
|
|
1035
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
1036
|
+
className: "flex shrink-0 items-center justify-end gap-1.5",
|
|
1037
|
+
children: activeTableData && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1038
|
+
/* @__PURE__ */ jsx(Button, {
|
|
1039
|
+
variant: "text",
|
|
1040
|
+
size: "small",
|
|
1041
|
+
onClick: async () => {
|
|
1042
|
+
const table = activeTableData.tableName;
|
|
1043
|
+
const action = activeTableData.rlsEnabled ? "DISABLE" : "ENABLE";
|
|
1044
|
+
if (!confirm(`Are you sure you want to ${action.toLowerCase()} Row Level Security on "${table}"?`)) return;
|
|
1045
|
+
try {
|
|
1046
|
+
await databaseAdmin.executeSql(`ALTER TABLE ${sanitizeSqlIdentifier(table)} ${action} ROW LEVEL SECURITY`);
|
|
1047
|
+
snackbarController.open({
|
|
1048
|
+
type: "success",
|
|
1049
|
+
message: `RLS ${action.toLowerCase()}d on ${table}`
|
|
1050
|
+
});
|
|
1051
|
+
fetchRLSData();
|
|
1052
|
+
} catch (e) {
|
|
1053
|
+
snackbarController.open({
|
|
1054
|
+
type: "error",
|
|
1055
|
+
message: e instanceof Error ? e.message : String(e)
|
|
1056
|
+
});
|
|
1057
|
+
}
|
|
1058
|
+
},
|
|
1059
|
+
children: activeTableData.rlsEnabled ? t("studio_rls_disable_rls") : t("studio_rls_enable_rls")
|
|
1060
|
+
}),
|
|
1061
|
+
/* @__PURE__ */ jsx("div", { className: "h-4 w-px bg-surface-200 dark:bg-surface-950 mx-1" }),
|
|
1062
|
+
/* @__PURE__ */ jsx(Button, {
|
|
1063
|
+
variant: "text",
|
|
1064
|
+
size: "small",
|
|
1065
|
+
onClick: fetchRLSData,
|
|
1066
|
+
startIcon: /* @__PURE__ */ jsx(RefreshCwIcon, { size: iconSize.smallest }),
|
|
1067
|
+
children: "Refresh"
|
|
1068
|
+
}),
|
|
1069
|
+
/* @__PURE__ */ jsx("div", { className: "h-4 w-px bg-surface-200 dark:bg-surface-950 mx-1" }),
|
|
1070
|
+
/* @__PURE__ */ jsx(Button, {
|
|
1071
|
+
size: "small",
|
|
1072
|
+
color: "primary",
|
|
1073
|
+
disabled: !activeCollection,
|
|
1074
|
+
onClick: () => setEditingPolicy("new"),
|
|
1075
|
+
children: t("studio_rls_create_policy")
|
|
1076
|
+
})
|
|
1077
|
+
] })
|
|
1078
|
+
})]
|
|
1079
|
+
}), isLoading && !activeTableData ? /* @__PURE__ */ jsx("div", {
|
|
1080
|
+
className: "flex-grow flex items-center justify-center h-full",
|
|
1081
|
+
children: /* @__PURE__ */ jsx(CircularProgress, { size: "small" })
|
|
1082
|
+
}) : error ? /* @__PURE__ */ jsx("div", {
|
|
1083
|
+
className: "p-6 h-full flex items-center justify-center",
|
|
1084
|
+
children: /* @__PURE__ */ jsx(ErrorView, {
|
|
1085
|
+
title: t("studio_rls_error"),
|
|
1086
|
+
error,
|
|
1087
|
+
onRetry: fetchRLSData
|
|
1088
|
+
})
|
|
1089
|
+
}) : !activeTableData ? /* @__PURE__ */ jsx("div", {
|
|
1090
|
+
className: "flex-grow flex items-center justify-center text-text-disabled h-full",
|
|
1091
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
1092
|
+
className: "text-center",
|
|
1093
|
+
children: [/* @__PURE__ */ jsx("svg", {
|
|
1094
|
+
className: "w-12 h-12 mx-auto mb-4 opacity-50",
|
|
1095
|
+
fill: "none",
|
|
1096
|
+
stroke: "currentColor",
|
|
1097
|
+
viewBox: "0 0 24 24",
|
|
1098
|
+
children: /* @__PURE__ */ jsx("path", {
|
|
1099
|
+
strokeLinecap: "round",
|
|
1100
|
+
strokeLinejoin: "round",
|
|
1101
|
+
strokeWidth: 1,
|
|
1102
|
+
d: "M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"
|
|
1103
|
+
})
|
|
1104
|
+
}), /* @__PURE__ */ jsx(Typography, {
|
|
1105
|
+
variant: "body2",
|
|
1106
|
+
children: t("studio_rls_select_table")
|
|
1107
|
+
})]
|
|
1108
|
+
})
|
|
1109
|
+
}) : editingPolicy ? /* @__PURE__ */ jsx(PolicyEditor, {
|
|
1110
|
+
policy: editingPolicy === "new" ? void 0 : editingPolicy,
|
|
1111
|
+
schema: activeTableData.schemaName,
|
|
1112
|
+
table: activeTableData.tableName,
|
|
1113
|
+
onSave: async (newPolicy) => {
|
|
1114
|
+
if (!activeCollection) return;
|
|
1115
|
+
const rule = {
|
|
1116
|
+
name: newPolicy.policyname,
|
|
1117
|
+
operation: newPolicy.cmd?.toLowerCase(),
|
|
1118
|
+
mode: newPolicy.permissive?.toLowerCase(),
|
|
1119
|
+
using: newPolicy.qual || void 0,
|
|
1120
|
+
withCheck: newPolicy.with_check || void 0,
|
|
1121
|
+
roles: newPolicy.roles
|
|
1122
|
+
};
|
|
1123
|
+
const existingRules = (isPostgresCollection(activeCollection) ? activeCollection.securityRules : void 0) || [];
|
|
1124
|
+
let newRules;
|
|
1125
|
+
if (editingPolicy === "new") newRules = [...existingRules, rule];
|
|
1126
|
+
else newRules = existingRules.map((r) => r.name === editingPolicy.policyname ? rule : r);
|
|
1127
|
+
try {
|
|
1128
|
+
if (!(await fetch(`${apiUrl}/api/schema-editor/collection/save`, {
|
|
1129
|
+
method: "POST",
|
|
1130
|
+
headers: { "Content-Type": "application/json" },
|
|
1131
|
+
body: JSON.stringify({
|
|
1132
|
+
collectionId: activeCollection.id || activeCollection.path || activeCollection.alias || activeTableData.tableName,
|
|
1133
|
+
collectionData: { securityRules: newRules }
|
|
1134
|
+
})
|
|
1135
|
+
})).ok) throw new Error("Failed to save policy");
|
|
1136
|
+
snackbarController.open({
|
|
1137
|
+
type: "success",
|
|
1138
|
+
message: "Policy saved successfully"
|
|
1139
|
+
});
|
|
1140
|
+
setEditingPolicy(null);
|
|
1141
|
+
fetchRLSData();
|
|
1142
|
+
} catch (e) {
|
|
1143
|
+
snackbarController.open({
|
|
1144
|
+
type: "error",
|
|
1145
|
+
message: e instanceof Error ? e.message : String(e)
|
|
1146
|
+
});
|
|
1147
|
+
}
|
|
1148
|
+
},
|
|
1149
|
+
onCancel: () => setEditingPolicy(null)
|
|
1150
|
+
}) : /* @__PURE__ */ jsx("div", {
|
|
1151
|
+
className: "flex-grow flex flex-col overflow-hidden",
|
|
1152
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
1153
|
+
className: "p-6 pt-4 flex-grow overflow-auto bg-surface-50 dark:bg-surface-900",
|
|
1154
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
1155
|
+
className: "max-w-4xl mx-auto flex flex-col gap-6",
|
|
1156
|
+
children: [
|
|
1157
|
+
activeTableData && !activeCollection && /* @__PURE__ */ jsxs(Alert, {
|
|
1158
|
+
color: "warning",
|
|
1159
|
+
children: [/* @__PURE__ */ jsx(Typography, {
|
|
1160
|
+
variant: "body2",
|
|
1161
|
+
className: "mb-1",
|
|
1162
|
+
children: "Table not managed by Rebase"
|
|
1163
|
+
}), /* @__PURE__ */ jsx(Typography, {
|
|
1164
|
+
variant: "caption",
|
|
1165
|
+
className: "opacity-80",
|
|
1166
|
+
children: "This table is not mapped to a Rebase Schema via code. To edit security policies visually, you must first import this table into a Schema configuration file."
|
|
1167
|
+
})]
|
|
1168
|
+
}),
|
|
1169
|
+
activeTableData && !activeTableData.rlsEnabled && /* @__PURE__ */ jsxs("div", {
|
|
1170
|
+
className: cls("p-4 sm:p-5 bg-yellow-50 dark:bg-yellow-900/20 border border-yellow-200 dark:border-yellow-900/50 rounded-lg flex flex-col sm:flex-row gap-4 items-start sm:items-center justify-between", defaultBorderMixin),
|
|
1171
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
1172
|
+
className: "flex gap-3 items-start",
|
|
1173
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
1174
|
+
className: "mt-1 bg-yellow-100 dark:bg-yellow-900/50 p-1.5 rounded-md shrink-0 flex items-center justify-center",
|
|
1175
|
+
children: /* @__PURE__ */ jsx(AlertTriangleIcon, { size: iconSize.smallest })
|
|
1176
|
+
}), /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx(Typography, {
|
|
1177
|
+
variant: "subtitle2",
|
|
1178
|
+
className: "text-yellow-800 dark:text-yellow-500",
|
|
1179
|
+
children: "Row Level Security (RLS) is disabled"
|
|
1180
|
+
}), /* @__PURE__ */ jsx(Typography, {
|
|
1181
|
+
variant: "body2",
|
|
1182
|
+
className: "text-yellow-700 dark:text-yellow-600/90 mt-1 max-w-2xl",
|
|
1183
|
+
children: "Your table is completely readable and writable by anyone with access privileges. Enable RLS to create policies that restrict access to specific rows."
|
|
1184
|
+
})] })]
|
|
1185
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
1186
|
+
size: "medium",
|
|
1187
|
+
variant: "filled",
|
|
1188
|
+
color: "neutral",
|
|
1189
|
+
onClick: () => setEditingPolicy("new"),
|
|
1190
|
+
className: "shrink-0 whitespace-nowrap",
|
|
1191
|
+
disabled: !activeCollection,
|
|
1192
|
+
children: t("studio_rls_create_policy")
|
|
1193
|
+
})]
|
|
1194
|
+
}),
|
|
1195
|
+
activeTableData && mergedPolicies && mergedPolicies.length > 0 && /* @__PURE__ */ jsxs("div", {
|
|
1196
|
+
className: "flex flex-col gap-3",
|
|
1197
|
+
children: [/* @__PURE__ */ jsx(Typography, {
|
|
1198
|
+
variant: "subtitle2",
|
|
1199
|
+
className: "text-text-secondary dark:text-text-secondary-dark uppercase tracking-wider mb-1",
|
|
1200
|
+
children: t("studio_rls_policies")
|
|
1201
|
+
}), mergedPolicies.map((policy) => /* @__PURE__ */ jsxs(Paper, {
|
|
1202
|
+
className: cls("p-3 sm:px-4 sm:py-3 flex flex-col sm:flex-row sm:items-center justify-between gap-4 border rounded-lg", defaultBorderMixin),
|
|
1203
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
1204
|
+
className: "flex flex-col gap-2 min-w-0",
|
|
1205
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
1206
|
+
className: "flex items-center gap-2",
|
|
1207
|
+
children: [
|
|
1208
|
+
/* @__PURE__ */ jsx(KeyIcon, {
|
|
1209
|
+
size: iconSize.smallest,
|
|
1210
|
+
className: "text-text-secondary dark:text-text-secondary-dark shrink-0"
|
|
1211
|
+
}),
|
|
1212
|
+
/* @__PURE__ */ jsx(Typography, {
|
|
1213
|
+
variant: "body2",
|
|
1214
|
+
className: "truncate",
|
|
1215
|
+
children: policy.policyname
|
|
1216
|
+
}),
|
|
1217
|
+
policy.status === "code_only" && /* @__PURE__ */ jsx(Tooltip, {
|
|
1218
|
+
title: "This policy is defined in your code but hasn't been applied to the database yet.",
|
|
1219
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
1220
|
+
className: "px-1.5 py-0.5 rounded text-[10px] uppercase bg-primary/10 text-primary border border-primary/20 shrink-0",
|
|
1221
|
+
children: "Unapplied"
|
|
1222
|
+
})
|
|
1223
|
+
}),
|
|
1224
|
+
policy.status === "live" && /* @__PURE__ */ jsx(Tooltip, {
|
|
1225
|
+
title: "This policy is live in the database but missing from your codebase schema.",
|
|
1226
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
1227
|
+
className: "px-1.5 py-0.5 rounded text-[10px] uppercase bg-orange-500/10 text-orange-600 border border-orange-500/20 shrink-0",
|
|
1228
|
+
children: "DB Only"
|
|
1229
|
+
})
|
|
1230
|
+
})
|
|
1231
|
+
]
|
|
1232
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
1233
|
+
className: "flex flex-wrap gap-1.5 text-sm",
|
|
1234
|
+
children: [renderPolicyTag("Action", policy.cmd), renderPolicyTag("Roles", Array.isArray(policy.roles) ? policy.roles.join(", ") : policy.roles)]
|
|
1235
|
+
})]
|
|
1236
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
1237
|
+
className: "flex gap-2 shrink-0 items-center",
|
|
1238
|
+
children: [
|
|
1239
|
+
policy.status === "live" && activeCollection && /* @__PURE__ */ jsx(Button, {
|
|
1240
|
+
size: "small",
|
|
1241
|
+
variant: "outlined",
|
|
1242
|
+
color: "primary",
|
|
1243
|
+
onClick: async () => {
|
|
1244
|
+
const rule = {
|
|
1245
|
+
name: policy.policyname,
|
|
1246
|
+
operation: policy.cmd?.toLowerCase(),
|
|
1247
|
+
mode: policy.permissive?.toLowerCase(),
|
|
1248
|
+
using: policy.qual || void 0,
|
|
1249
|
+
withCheck: policy.with_check || void 0,
|
|
1250
|
+
roles: policy.roles
|
|
1251
|
+
};
|
|
1252
|
+
const newRules = [...(isPostgresCollection(activeCollection) ? activeCollection.securityRules : void 0) || [], rule];
|
|
1253
|
+
try {
|
|
1254
|
+
if (!(await fetch(`${apiUrl}/api/schema-editor/collection/save`, {
|
|
1255
|
+
method: "POST",
|
|
1256
|
+
headers: { "Content-Type": "application/json" },
|
|
1257
|
+
body: JSON.stringify({
|
|
1258
|
+
collectionId: activeCollection.id || activeCollection.path || activeCollection.alias || activeTableData.tableName,
|
|
1259
|
+
collectionData: { securityRules: newRules }
|
|
1260
|
+
})
|
|
1261
|
+
})).ok) throw new Error("Failed to save policy");
|
|
1262
|
+
snackbarController.open({
|
|
1263
|
+
type: "success",
|
|
1264
|
+
message: "Policy imported successfully"
|
|
1265
|
+
});
|
|
1266
|
+
fetchRLSData();
|
|
1267
|
+
} catch (e) {
|
|
1268
|
+
snackbarController.open({
|
|
1269
|
+
type: "error",
|
|
1270
|
+
message: e instanceof Error ? e.message : String(e)
|
|
1271
|
+
});
|
|
1272
|
+
}
|
|
1273
|
+
},
|
|
1274
|
+
children: "Import to codebase"
|
|
1275
|
+
}),
|
|
1276
|
+
/* @__PURE__ */ jsx(Button, {
|
|
1277
|
+
size: "small",
|
|
1278
|
+
variant: "text",
|
|
1279
|
+
color: "primary",
|
|
1280
|
+
onClick: () => setEditingPolicy(policy),
|
|
1281
|
+
disabled: !activeCollection,
|
|
1282
|
+
children: t("studio_rls_edit")
|
|
1283
|
+
}),
|
|
1284
|
+
policy.status !== "code_only" && /* @__PURE__ */ jsx(Tooltip, {
|
|
1285
|
+
title: t("studio_rls_delete"),
|
|
1286
|
+
asChild: true,
|
|
1287
|
+
children: /* @__PURE__ */ jsx(IconButton, {
|
|
1288
|
+
size: "small",
|
|
1289
|
+
onClick: async () => {
|
|
1290
|
+
const table = activeTableData.tableName;
|
|
1291
|
+
if (!confirm(`Drop policy "${policy.policyname}" from table "${table}"?`)) return;
|
|
1292
|
+
try {
|
|
1293
|
+
await databaseAdmin.executeSql(`DROP POLICY ${sanitizeSqlIdentifier(policy.policyname)} ON ${sanitizeSqlIdentifier(table)}`);
|
|
1294
|
+
snackbarController.open({
|
|
1295
|
+
type: "success",
|
|
1296
|
+
message: `Policy "${policy.policyname}" dropped`
|
|
1297
|
+
});
|
|
1298
|
+
fetchRLSData();
|
|
1299
|
+
} catch (e) {
|
|
1300
|
+
snackbarController.open({
|
|
1301
|
+
type: "error",
|
|
1302
|
+
message: e instanceof Error ? e.message : String(e)
|
|
1303
|
+
});
|
|
1304
|
+
}
|
|
1305
|
+
},
|
|
1306
|
+
children: /* @__PURE__ */ jsx(Trash2Icon, { size: iconSize.smallest })
|
|
1307
|
+
})
|
|
1308
|
+
})
|
|
1309
|
+
]
|
|
1310
|
+
})]
|
|
1311
|
+
}, policy.policyname))]
|
|
1312
|
+
}),
|
|
1313
|
+
activeTableData && mergedPolicies.length === 0 && activeTableData.rlsEnabled && /* @__PURE__ */ jsxs("div", {
|
|
1314
|
+
className: "flex flex-col items-center justify-center py-12 text-center",
|
|
1315
|
+
children: [
|
|
1316
|
+
/* @__PURE__ */ jsx(ShieldIcon, {
|
|
1317
|
+
size: 40,
|
|
1318
|
+
className: "text-surface-300 dark:text-surface-600 mb-4"
|
|
1319
|
+
}),
|
|
1320
|
+
/* @__PURE__ */ jsx(Typography, {
|
|
1321
|
+
variant: "subtitle2",
|
|
1322
|
+
className: "text-text-secondary dark:text-text-secondary-dark mb-2",
|
|
1323
|
+
children: "No policies defined"
|
|
1324
|
+
}),
|
|
1325
|
+
/* @__PURE__ */ jsx(Typography, {
|
|
1326
|
+
variant: "caption",
|
|
1327
|
+
className: "text-text-disabled dark:text-text-disabled-dark max-w-sm mb-4",
|
|
1328
|
+
children: "RLS is enabled on this table but no policies exist. All access is denied by default (Postgres deny-all). Create a policy to allow specific access."
|
|
1329
|
+
}),
|
|
1330
|
+
activeCollection && /* @__PURE__ */ jsx(Button, {
|
|
1331
|
+
size: "small",
|
|
1332
|
+
variant: "filled",
|
|
1333
|
+
color: "primary",
|
|
1334
|
+
onClick: () => setEditingPolicy("new"),
|
|
1335
|
+
children: t("studio_rls_create_policy")
|
|
1336
|
+
})
|
|
1337
|
+
]
|
|
1338
|
+
}),
|
|
1339
|
+
activeTableData && mergedPolicies.length === 0 && !activeTableData.rlsEnabled && activeCollection && /* @__PURE__ */ jsxs("div", {
|
|
1340
|
+
className: "flex flex-col items-center justify-center py-12 text-center",
|
|
1341
|
+
children: [
|
|
1342
|
+
/* @__PURE__ */ jsx(AlertTriangleIcon, {
|
|
1343
|
+
size: 40,
|
|
1344
|
+
className: "text-yellow-400 dark:text-yellow-600 mb-4"
|
|
1345
|
+
}),
|
|
1346
|
+
/* @__PURE__ */ jsx(Typography, {
|
|
1347
|
+
variant: "subtitle2",
|
|
1348
|
+
className: "text-text-secondary dark:text-text-secondary-dark mb-2",
|
|
1349
|
+
children: "No access control"
|
|
1350
|
+
}),
|
|
1351
|
+
/* @__PURE__ */ jsx(Typography, {
|
|
1352
|
+
variant: "caption",
|
|
1353
|
+
className: "text-text-disabled dark:text-text-disabled-dark max-w-sm",
|
|
1354
|
+
children: "This table has neither RLS nor policies. Enable RLS and create policies to restrict row-level access."
|
|
1355
|
+
})
|
|
1356
|
+
]
|
|
1357
|
+
})
|
|
1358
|
+
]
|
|
1359
|
+
})
|
|
1360
|
+
})
|
|
1361
|
+
})]
|
|
1362
|
+
})
|
|
1363
|
+
})
|
|
1364
|
+
});
|
|
1365
|
+
};
|
|
1366
|
+
//#endregion
|
|
1367
|
+
export { RLSEditor };
|
|
1368
|
+
|
|
1369
|
+
//# sourceMappingURL=RLSEditor-DpF1u9EC.js.map
|