@rebasepro/studio 0.6.0 → 0.7.0
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/ApiKeysView-D-_FSlNL.js +684 -0
- package/dist/ApiKeysView-D-_FSlNL.js.map +1 -0
- package/dist/{BranchesView-DncIRcZt.js → BranchesView-Dlg78EQ8.js} +4 -5
- package/dist/{BranchesView-DncIRcZt.js.map → BranchesView-Dlg78EQ8.js.map} +1 -1
- package/dist/{JSEditor-BhAbEjCP.js → JSEditor-DSucz6wV.js} +4 -4
- package/dist/{JSEditor-BhAbEjCP.js.map → JSEditor-DSucz6wV.js.map} +1 -1
- package/dist/{LogsExplorer-CqtKILj8.js → LogsExplorer-J4xfsuv3.js} +49 -83
- package/dist/LogsExplorer-J4xfsuv3.js.map +1 -0
- package/dist/{RLSEditor-CTxYbBdW.js → RLSEditor-BM64laoW.js} +483 -289
- package/dist/RLSEditor-BM64laoW.js.map +1 -0
- package/dist/{SQLEditor-BLuq_zDM.js → SQLEditor-CuAhR-zr.js} +4 -4
- package/dist/{SQLEditor-BLuq_zDM.js.map → SQLEditor-CuAhR-zr.js.map} +1 -1
- package/dist/{SchemaVisualizer-BJK2u3C0.js → SchemaVisualizer-OibKoD3g.js} +2 -2
- package/dist/{SchemaVisualizer-BJK2u3C0.js.map → SchemaVisualizer-OibKoD3g.js.map} +1 -1
- package/dist/{StorageView-nDaC2foF.js → StorageView-CvrnHmDG.js} +89 -76
- package/dist/StorageView-CvrnHmDG.js.map +1 -0
- package/dist/components/ApiKeys/ApiKeysView.d.ts +2 -0
- package/dist/index.es.js +38 -19
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +1358 -489
- package/dist/index.umd.js.map +1 -1
- package/package.json +17 -17
- package/src/components/ApiKeys/ApiKeysView.tsx +580 -0
- package/src/components/Branches/BranchesView.tsx +3 -4
- package/src/components/JSEditor/JSEditorSidebar.tsx +3 -3
- package/src/components/LogsExplorer/LogsExplorer.tsx +46 -84
- package/src/components/RLSEditor/PolicyEditor.tsx +1 -0
- package/src/components/RLSEditor/RLSEditor.tsx +354 -117
- package/src/components/RebaseStudio.tsx +10 -1
- package/src/components/SQLEditor/SchemaBrowser.tsx +3 -3
- package/src/components/StorageView/StorageView.tsx +3 -3
- package/src/components/StudioHomePage.tsx +5 -1
- package/dist/LogsExplorer-CqtKILj8.js.map +0 -1
- package/dist/RLSEditor-CTxYbBdW.js.map +0 -1
- package/dist/StorageView-nDaC2foF.js.map +0 -1
package/dist/index.umd.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
(function(global, factory) {
|
|
2
|
-
typeof exports === "object" && typeof module !== "undefined" ? factory(exports, require("@rebasepro/core"), require("react"), require("@rebasepro/ui"), require("react/jsx-runtime"), require("react-router-dom"), require("react-dom"), require("@monaco-editor/react"), require("pgsql-ast-parser"), require("@rebasepro/utils"), require("prism-react-renderer"), require("@rebasepro/client"), require("@rebasepro/types"), require("react-dropzone"), require("@xyflow/react"), require("@xyflow/react/dist/style.css"), require("
|
|
2
|
+
typeof exports === "object" && typeof module !== "undefined" ? factory(exports, require("@rebasepro/core"), require("react"), require("@rebasepro/ui"), require("react/jsx-runtime"), require("react-router-dom"), require("react-dom"), require("@monaco-editor/react"), require("pgsql-ast-parser"), require("@rebasepro/utils"), require("prism-react-renderer"), require("@rebasepro/client"), require("@rebasepro/types"), require("@rebasepro/common"), require("react-dropzone"), require("@xyflow/react"), require("@xyflow/react/dist/style.css"), require("dagre")) : typeof define === "function" && define.amd ? define([
|
|
3
3
|
"exports",
|
|
4
4
|
"@rebasepro/core",
|
|
5
5
|
"react",
|
|
@@ -13,13 +13,13 @@
|
|
|
13
13
|
"prism-react-renderer",
|
|
14
14
|
"@rebasepro/client",
|
|
15
15
|
"@rebasepro/types",
|
|
16
|
+
"@rebasepro/common",
|
|
16
17
|
"react-dropzone",
|
|
17
18
|
"@xyflow/react",
|
|
18
19
|
"@xyflow/react/dist/style.css",
|
|
19
|
-
"@rebasepro/common",
|
|
20
20
|
"dagre"
|
|
21
|
-
], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global["Rebase collection editor"] = {}, global._rebasepro_core, global.react, global._rebasepro_ui, global.react_jsx_runtime, global.react_router_dom, global.react_dom, global._monaco_editor_react, global.pgsql_ast_parser, global._rebasepro_utils, global.prism_react_renderer, global._rebasepro_client, global._rebasepro_types, global.
|
|
22
|
-
})(this, function(exports, _rebasepro_core, react, _rebasepro_ui, react_jsx_runtime, react_router_dom, react_dom, _monaco_editor_react, pgsql_ast_parser, _rebasepro_utils, prism_react_renderer, _rebasepro_client, _rebasepro_types, react_dropzone, _xyflow_react, _xyflow_react_dist_style_css,
|
|
21
|
+
], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global["Rebase collection editor"] = {}, global._rebasepro_core, global.react, global._rebasepro_ui, global.react_jsx_runtime, global.react_router_dom, global.react_dom, global._monaco_editor_react, global.pgsql_ast_parser, global._rebasepro_utils, global.prism_react_renderer, global._rebasepro_client, global._rebasepro_types, global._rebasepro_common, global.react_dropzone, global._xyflow_react, global._xyflow_react_dist_style_css, global.dagre));
|
|
22
|
+
})(this, function(exports, _rebasepro_core, react, _rebasepro_ui, react_jsx_runtime, react_router_dom, react_dom, _monaco_editor_react, pgsql_ast_parser, _rebasepro_utils, prism_react_renderer, _rebasepro_client, _rebasepro_types, _rebasepro_common, react_dropzone, _xyflow_react, _xyflow_react_dist_style_css, dagre) {
|
|
23
23
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
24
24
|
//#region \0rolldown/runtime.js
|
|
25
25
|
var __create = Object.create;
|
|
@@ -143,17 +143,26 @@
|
|
|
143
143
|
label: "Access Control",
|
|
144
144
|
dotColor: "bg-rose-400",
|
|
145
145
|
iconColor: "text-rose-400",
|
|
146
|
-
tools: [
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
146
|
+
tools: [
|
|
147
|
+
{
|
|
148
|
+
path: "/users",
|
|
149
|
+
name: "Users",
|
|
150
|
+
description: "Manage developers and assign roles in your workspace",
|
|
151
|
+
icon: "group"
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
path: "/roles",
|
|
155
|
+
name: "Roles",
|
|
156
|
+
description: "Create and configure fine-grained access permissions",
|
|
157
|
+
icon: "ShieldEllipsis"
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
path: "/api-keys",
|
|
161
|
+
name: "API Keys",
|
|
162
|
+
description: "Create and manage scoped keys for machine-to-machine access",
|
|
163
|
+
icon: "KeyRound"
|
|
164
|
+
}
|
|
165
|
+
]
|
|
157
166
|
}
|
|
158
167
|
];
|
|
159
168
|
function StudioHomePage({ additionalActions, additionalChildrenStart, additionalChildrenEnd, sections, hiddenGroups }) {
|
|
@@ -896,7 +905,7 @@
|
|
|
896
905
|
}) : Object.entries(schemas).map(([schemaName, tables]) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
897
906
|
className: "mb-2",
|
|
898
907
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
899
|
-
className: "flex items-center p-1 cursor-pointer hover:bg-surface-100 dark:hover:bg-surface-
|
|
908
|
+
className: "flex items-center p-1 cursor-pointer hover:bg-surface-100 dark:hover:bg-surface-900 rounded transition-colors",
|
|
900
909
|
onClick: () => setExpandedSchemas((prev) => ({
|
|
901
910
|
...prev,
|
|
902
911
|
[schemaName]: !prev[schemaName]
|
|
@@ -914,7 +923,7 @@
|
|
|
914
923
|
}), expandedSchemas[schemaName] && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
915
924
|
className: "ml-3 mt-1 space-y-1",
|
|
916
925
|
children: tables.map((table) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
917
|
-
className: "flex items-center p-1 cursor-pointer hover:bg-surface-100 dark:hover:bg-surface-
|
|
926
|
+
className: "flex items-center p-1 cursor-pointer hover:bg-surface-100 dark:hover:bg-surface-900 rounded transition-colors group relative",
|
|
918
927
|
onClick: () => setExpandedTables((prev) => ({
|
|
919
928
|
...prev,
|
|
920
929
|
[`${schemaName}.${table.tableName}`]: !prev[`${schemaName}.${table.tableName}`]
|
|
@@ -1017,7 +1026,7 @@
|
|
|
1017
1026
|
}), expandedTables[`${schemaName}.${table.tableName}`] && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1018
1027
|
className: (0, _rebasepro_ui.cls)("ml-5 mt-1 space-y-0.5 border-l", _rebasepro_ui.defaultBorderMixin),
|
|
1019
1028
|
children: table.columns.map((col) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1020
|
-
className: "flex items-center p-1 group pl-2 hover:bg-surface-50 dark:hover:bg-surface-
|
|
1029
|
+
className: "flex items-center p-1 group pl-2 hover:bg-surface-50 dark:hover:bg-surface-900 rounded-r relative min-h-[28px]",
|
|
1021
1030
|
children: [
|
|
1022
1031
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
1023
1032
|
className: "w-3 h-3 mr-1.5 text-text-disabled dark:text-text-disabled-dark shrink-0",
|
|
@@ -3130,7 +3139,7 @@ declare const context: JSEditorContext;
|
|
|
3130
3139
|
function CollectionItem({ collection, onInsertCode }) {
|
|
3131
3140
|
const [expanded, setExpanded] = (0, react.useState)(false);
|
|
3132
3141
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3133
|
-
className: "flex items-center p-1 cursor-pointer hover:bg-surface-100 dark:hover:bg-surface-
|
|
3142
|
+
className: "flex items-center p-1 cursor-pointer hover:bg-surface-100 dark:hover:bg-surface-900 rounded transition-colors group relative",
|
|
3134
3143
|
onClick: () => setExpanded(!expanded),
|
|
3135
3144
|
children: [
|
|
3136
3145
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
@@ -3172,7 +3181,7 @@ declare const context: JSEditorContext;
|
|
|
3172
3181
|
}), expanded && collection.properties.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3173
3182
|
className: (0, _rebasepro_ui.cls)("ml-5 mt-1 space-y-0.5 border-l", _rebasepro_ui.defaultBorderMixin),
|
|
3174
3183
|
children: collection.properties.map((prop) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3175
|
-
className: "flex items-center p-1 pl-2 hover:bg-surface-50 dark:hover:bg-surface-
|
|
3184
|
+
className: "flex items-center p-1 pl-2 hover:bg-surface-50 dark:hover:bg-surface-900 rounded-r cursor-pointer transition-colors group/prop relative min-h-[28px]",
|
|
3176
3185
|
onClick: () => onInsertCode(`"${prop}"`),
|
|
3177
3186
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
3178
3187
|
className: "w-3 h-3 mr-1.5 text-text-disabled dark:text-text-disabled-dark shrink-0",
|
|
@@ -3455,7 +3464,7 @@ declare const context: JSEditorContext;
|
|
|
3455
3464
|
children: "No execution history yet"
|
|
3456
3465
|
})
|
|
3457
3466
|
}) : [...history].reverse().map((code, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3458
|
-
className: "p-2 py-1.5 rounded hover:bg-surface-100 dark:hover:bg-surface-
|
|
3467
|
+
className: "p-2 py-1.5 rounded hover:bg-surface-100 dark:hover:bg-surface-900 cursor-pointer group transition-colors flex items-start",
|
|
3459
3468
|
onClick: () => onSelectSnippet(code),
|
|
3460
3469
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
3461
3470
|
className: "w-3 h-3 mt-1 mr-2 text-text-disabled dark:text-text-disabled-dark group-hover:text-primary transition-colors flex-shrink-0",
|
|
@@ -4819,194 +4828,201 @@ return result;
|
|
|
4819
4828
|
open: helpOpen,
|
|
4820
4829
|
onOpenChange: setHelpOpen,
|
|
4821
4830
|
maxWidth: "3xl",
|
|
4822
|
-
children: [
|
|
4823
|
-
|
|
4824
|
-
|
|
4825
|
-
|
|
4826
|
-
|
|
4827
|
-
|
|
4828
|
-
|
|
4829
|
-
|
|
4830
|
-
|
|
4831
|
-
|
|
4832
|
-
|
|
4833
|
-
|
|
4834
|
-
|
|
4835
|
-
|
|
4836
|
-
|
|
4837
|
-
|
|
4838
|
-
|
|
4839
|
-
|
|
4840
|
-
|
|
4841
|
-
|
|
4842
|
-
|
|
4843
|
-
|
|
4844
|
-
className: "text-text-secondary dark:text-text-secondary-dark",
|
|
4845
|
-
children: t("studio_policy_help_step1_desc")
|
|
4846
|
-
})]
|
|
4847
|
-
}),
|
|
4848
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.Paper, {
|
|
4849
|
-
className: (0, _rebasepro_ui.cls)("p-4 sm:p-5 flex flex-col gap-1", _rebasepro_ui.defaultBorderMixin),
|
|
4850
|
-
children: [
|
|
4851
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
|
|
4852
|
-
variant: "subtitle2",
|
|
4853
|
-
className: "text-primary dark:text-primary-light font-medium",
|
|
4854
|
-
children: t("studio_policy_help_step2_title")
|
|
4855
|
-
}),
|
|
4856
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
|
|
4857
|
-
variant: "body2",
|
|
4858
|
-
className: "text-text-secondary dark:text-text-secondary-dark mb-1",
|
|
4859
|
-
children: t("studio_policy_help_step2_desc")
|
|
4860
|
-
}),
|
|
4861
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("ul", {
|
|
4862
|
-
className: "list-disc pl-5 space-y-1 text-sm text-text-secondary dark:text-text-secondary-dark",
|
|
4863
|
-
children: [
|
|
4864
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", { children: [
|
|
4865
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: "public" }),
|
|
4866
|
-
": ",
|
|
4867
|
-
t("studio_policy_help_role_public")
|
|
4868
|
-
] }),
|
|
4869
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", { children: [
|
|
4870
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: "authenticated" }),
|
|
4871
|
-
": ",
|
|
4872
|
-
t("studio_policy_help_role_authenticated")
|
|
4873
|
-
] }),
|
|
4874
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", { children: [
|
|
4875
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: "anon" }),
|
|
4876
|
-
": ",
|
|
4877
|
-
t("studio_policy_help_role_anon")
|
|
4878
|
-
] })
|
|
4879
|
-
]
|
|
4880
|
-
})
|
|
4881
|
-
]
|
|
4882
|
-
}),
|
|
4883
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.Paper, {
|
|
4884
|
-
className: (0, _rebasepro_ui.cls)("p-4 sm:p-5 flex flex-col gap-1", _rebasepro_ui.defaultBorderMixin),
|
|
4885
|
-
children: [
|
|
4886
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
|
|
4887
|
-
variant: "subtitle2",
|
|
4888
|
-
className: "text-primary dark:text-primary-light font-medium",
|
|
4889
|
-
children: t("studio_policy_help_step3_title")
|
|
4890
|
-
}),
|
|
4891
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
|
|
4892
|
-
variant: "body2",
|
|
4893
|
-
className: "text-text-secondary dark:text-text-secondary-dark mb-1",
|
|
4894
|
-
children: t("studio_policy_help_step3_desc")
|
|
4895
|
-
}),
|
|
4896
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4897
|
-
className: (0, _rebasepro_ui.cls)("bg-surface-100 dark:bg-surface-950 px-3 py-2 rounded-md font-mono text-sm my-2", _rebasepro_ui.defaultBorderMixin),
|
|
4898
|
-
children: "Example: auth.uid() = user_id"
|
|
4899
|
-
}),
|
|
4900
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
|
|
4901
|
-
variant: "caption",
|
|
4902
|
-
className: "text-text-secondary dark:text-text-secondary-dark",
|
|
4903
|
-
children: t("studio_policy_help_step3_example")
|
|
4904
|
-
})
|
|
4905
|
-
]
|
|
4906
|
-
}),
|
|
4907
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.Paper, {
|
|
4908
|
-
className: (0, _rebasepro_ui.cls)("p-4 sm:p-5 flex flex-col gap-1", _rebasepro_ui.defaultBorderMixin),
|
|
4909
|
-
children: [
|
|
4910
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
|
|
4911
|
-
variant: "subtitle2",
|
|
4912
|
-
className: "text-primary dark:text-primary-light font-medium",
|
|
4913
|
-
children: t("studio_policy_help_step4_title")
|
|
4914
|
-
}),
|
|
4915
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
|
|
4916
|
-
variant: "body2",
|
|
4917
|
-
className: "text-text-secondary dark:text-text-secondary-dark mb-1",
|
|
4918
|
-
children: t("studio_policy_help_step4_desc")
|
|
4919
|
-
}),
|
|
4920
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4921
|
-
className: (0, _rebasepro_ui.cls)("bg-surface-100 dark:bg-surface-950 px-3 py-2 rounded-md font-mono text-sm my-2", _rebasepro_ui.defaultBorderMixin),
|
|
4922
|
-
children: "Example: auth.uid() = user_id"
|
|
4923
|
-
}),
|
|
4924
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
|
|
4925
|
-
variant: "caption",
|
|
4926
|
-
className: "text-text-secondary dark:text-text-secondary-dark",
|
|
4927
|
-
children: t("studio_policy_help_step4_example")
|
|
4928
|
-
})
|
|
4929
|
-
]
|
|
4930
|
-
}),
|
|
4931
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.Paper, {
|
|
4932
|
-
className: (0, _rebasepro_ui.cls)("p-4 sm:p-5 flex flex-col gap-2 bg-primary/5 dark:bg-primary-bg-dark/10", _rebasepro_ui.defaultBorderMixin),
|
|
4933
|
-
children: [
|
|
4934
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
|
|
4831
|
+
children: [
|
|
4832
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.DialogTitle, {
|
|
4833
|
+
hidden: true,
|
|
4834
|
+
children: "Row-Level Security Help"
|
|
4835
|
+
}),
|
|
4836
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.DialogContent, {
|
|
4837
|
+
className: "p-4 sm:p-6 lg:p-8 flex flex-col gap-6",
|
|
4838
|
+
children: [
|
|
4839
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
|
|
4840
|
+
variant: "h5",
|
|
4841
|
+
className: "mb-2",
|
|
4842
|
+
children: t("studio_policy_help_title")
|
|
4843
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
|
|
4844
|
+
className: "text-text-secondary dark:text-text-secondary-dark",
|
|
4845
|
+
children: t("studio_policy_help_intro")
|
|
4846
|
+
})] }),
|
|
4847
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4848
|
+
className: "flex flex-col gap-4",
|
|
4849
|
+
children: [
|
|
4850
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.Paper, {
|
|
4851
|
+
className: (0, _rebasepro_ui.cls)("p-4 sm:p-5 flex flex-col gap-1", _rebasepro_ui.defaultBorderMixin),
|
|
4852
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
|
|
4935
4853
|
variant: "subtitle2",
|
|
4936
4854
|
className: "text-primary dark:text-primary-light font-medium",
|
|
4937
|
-
children: t("
|
|
4938
|
-
}),
|
|
4939
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
|
|
4855
|
+
children: t("studio_policy_help_step1_title")
|
|
4856
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
|
|
4940
4857
|
variant: "body2",
|
|
4941
4858
|
className: "text-text-secondary dark:text-text-secondary-dark",
|
|
4942
|
-
children: t("
|
|
4943
|
-
})
|
|
4944
|
-
|
|
4945
|
-
|
|
4946
|
-
|
|
4947
|
-
|
|
4948
|
-
|
|
4949
|
-
|
|
4950
|
-
|
|
4951
|
-
|
|
4952
|
-
|
|
4953
|
-
|
|
4954
|
-
|
|
4955
|
-
|
|
4956
|
-
|
|
4957
|
-
|
|
4958
|
-
|
|
4959
|
-
|
|
4960
|
-
|
|
4961
|
-
|
|
4962
|
-
|
|
4963
|
-
|
|
4964
|
-
|
|
4965
|
-
})
|
|
4966
|
-
|
|
4967
|
-
|
|
4968
|
-
|
|
4969
|
-
|
|
4970
|
-
|
|
4971
|
-
|
|
4972
|
-
|
|
4973
|
-
|
|
4974
|
-
|
|
4975
|
-
})
|
|
4976
|
-
|
|
4977
|
-
|
|
4978
|
-
|
|
4979
|
-
|
|
4980
|
-
|
|
4981
|
-
|
|
4982
|
-
|
|
4983
|
-
|
|
4984
|
-
|
|
4985
|
-
|
|
4986
|
-
|
|
4987
|
-
|
|
4988
|
-
|
|
4989
|
-
|
|
4990
|
-
|
|
4991
|
-
|
|
4992
|
-
|
|
4993
|
-
|
|
4994
|
-
|
|
4995
|
-
|
|
4996
|
-
|
|
4997
|
-
|
|
4998
|
-
|
|
4859
|
+
children: t("studio_policy_help_step1_desc")
|
|
4860
|
+
})]
|
|
4861
|
+
}),
|
|
4862
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.Paper, {
|
|
4863
|
+
className: (0, _rebasepro_ui.cls)("p-4 sm:p-5 flex flex-col gap-1", _rebasepro_ui.defaultBorderMixin),
|
|
4864
|
+
children: [
|
|
4865
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
|
|
4866
|
+
variant: "subtitle2",
|
|
4867
|
+
className: "text-primary dark:text-primary-light font-medium",
|
|
4868
|
+
children: t("studio_policy_help_step2_title")
|
|
4869
|
+
}),
|
|
4870
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
|
|
4871
|
+
variant: "body2",
|
|
4872
|
+
className: "text-text-secondary dark:text-text-secondary-dark mb-1",
|
|
4873
|
+
children: t("studio_policy_help_step2_desc")
|
|
4874
|
+
}),
|
|
4875
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("ul", {
|
|
4876
|
+
className: "list-disc pl-5 space-y-1 text-sm text-text-secondary dark:text-text-secondary-dark",
|
|
4877
|
+
children: [
|
|
4878
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", { children: [
|
|
4879
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: "public" }),
|
|
4880
|
+
": ",
|
|
4881
|
+
t("studio_policy_help_role_public")
|
|
4882
|
+
] }),
|
|
4883
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", { children: [
|
|
4884
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: "authenticated" }),
|
|
4885
|
+
": ",
|
|
4886
|
+
t("studio_policy_help_role_authenticated")
|
|
4887
|
+
] }),
|
|
4888
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", { children: [
|
|
4889
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: "anon" }),
|
|
4890
|
+
": ",
|
|
4891
|
+
t("studio_policy_help_role_anon")
|
|
4892
|
+
] })
|
|
4893
|
+
]
|
|
4894
|
+
})
|
|
4895
|
+
]
|
|
4896
|
+
}),
|
|
4897
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.Paper, {
|
|
4898
|
+
className: (0, _rebasepro_ui.cls)("p-4 sm:p-5 flex flex-col gap-1", _rebasepro_ui.defaultBorderMixin),
|
|
4899
|
+
children: [
|
|
4900
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
|
|
4901
|
+
variant: "subtitle2",
|
|
4902
|
+
className: "text-primary dark:text-primary-light font-medium",
|
|
4903
|
+
children: t("studio_policy_help_step3_title")
|
|
4904
|
+
}),
|
|
4905
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
|
|
4906
|
+
variant: "body2",
|
|
4907
|
+
className: "text-text-secondary dark:text-text-secondary-dark mb-1",
|
|
4908
|
+
children: t("studio_policy_help_step3_desc")
|
|
4909
|
+
}),
|
|
4910
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4911
|
+
className: (0, _rebasepro_ui.cls)("bg-surface-100 dark:bg-surface-950 px-3 py-2 rounded-md font-mono text-sm my-2", _rebasepro_ui.defaultBorderMixin),
|
|
4912
|
+
children: "Example: auth.uid() = user_id"
|
|
4913
|
+
}),
|
|
4914
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
|
|
4915
|
+
variant: "caption",
|
|
4916
|
+
className: "text-text-secondary dark:text-text-secondary-dark",
|
|
4917
|
+
children: t("studio_policy_help_step3_example")
|
|
4918
|
+
})
|
|
4919
|
+
]
|
|
4920
|
+
}),
|
|
4921
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.Paper, {
|
|
4922
|
+
className: (0, _rebasepro_ui.cls)("p-4 sm:p-5 flex flex-col gap-1", _rebasepro_ui.defaultBorderMixin),
|
|
4923
|
+
children: [
|
|
4924
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
|
|
4925
|
+
variant: "subtitle2",
|
|
4926
|
+
className: "text-primary dark:text-primary-light font-medium",
|
|
4927
|
+
children: t("studio_policy_help_step4_title")
|
|
4928
|
+
}),
|
|
4929
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
|
|
4930
|
+
variant: "body2",
|
|
4931
|
+
className: "text-text-secondary dark:text-text-secondary-dark mb-1",
|
|
4932
|
+
children: t("studio_policy_help_step4_desc")
|
|
4933
|
+
}),
|
|
4934
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4935
|
+
className: (0, _rebasepro_ui.cls)("bg-surface-100 dark:bg-surface-950 px-3 py-2 rounded-md font-mono text-sm my-2", _rebasepro_ui.defaultBorderMixin),
|
|
4936
|
+
children: "Example: auth.uid() = user_id"
|
|
4937
|
+
}),
|
|
4938
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
|
|
4939
|
+
variant: "caption",
|
|
4940
|
+
className: "text-text-secondary dark:text-text-secondary-dark",
|
|
4941
|
+
children: t("studio_policy_help_step4_example")
|
|
4942
|
+
})
|
|
4943
|
+
]
|
|
4944
|
+
}),
|
|
4945
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.Paper, {
|
|
4946
|
+
className: (0, _rebasepro_ui.cls)("p-4 sm:p-5 flex flex-col gap-2 bg-primary/5 dark:bg-primary-bg-dark/10", _rebasepro_ui.defaultBorderMixin),
|
|
4947
|
+
children: [
|
|
4948
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
|
|
4949
|
+
variant: "subtitle2",
|
|
4950
|
+
className: "text-primary dark:text-primary-light font-medium",
|
|
4951
|
+
children: t("studio_policy_help_auth_vars_title")
|
|
4952
|
+
}),
|
|
4953
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
|
|
4954
|
+
variant: "body2",
|
|
4955
|
+
className: "text-text-secondary dark:text-text-secondary-dark",
|
|
4956
|
+
children: t("studio_policy_help_auth_vars_desc")
|
|
4957
|
+
}),
|
|
4958
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("ul", {
|
|
4959
|
+
className: "list-disc pl-5 space-y-2 text-sm text-text-secondary dark:text-text-secondary-dark font-normal",
|
|
4960
|
+
children: [
|
|
4961
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
|
|
4962
|
+
className: "bg-surface-100 dark:bg-surface-950 px-1.5 py-0.5 rounded mr-1 whitespace-nowrap",
|
|
4963
|
+
children: "auth.uid()"
|
|
4964
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
4965
|
+
className: "block mt-0.5",
|
|
4966
|
+
children: ["Returns the current user's ID as text. Example: ", /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
|
|
4967
|
+
className: "bg-surface-100 dark:bg-surface-950 px-1 py-0.5 rounded text-[11px]",
|
|
4968
|
+
children: "auth.uid() = user_id"
|
|
4969
|
+
})]
|
|
4970
|
+
})] }),
|
|
4971
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
|
|
4972
|
+
className: "bg-surface-100 dark:bg-surface-950 px-1.5 py-0.5 rounded mr-1 whitespace-nowrap",
|
|
4973
|
+
children: "auth.jwt()"
|
|
4974
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
4975
|
+
className: "block mt-0.5",
|
|
4976
|
+
children: ["Returns the full JWT payload as JSONB so you can check custom claims. Example: ", /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
|
|
4977
|
+
className: "bg-surface-100 dark:bg-surface-950 px-1 py-0.5 rounded text-[11px]",
|
|
4978
|
+
children: "auth.jwt() ->> 'email' = 'admin@example.com'"
|
|
4979
|
+
})]
|
|
4980
|
+
})] }),
|
|
4981
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
|
|
4982
|
+
className: "bg-surface-100 dark:bg-surface-950 px-1.5 py-0.5 rounded mr-1 whitespace-nowrap",
|
|
4983
|
+
children: "auth.roles()"
|
|
4984
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
4985
|
+
className: "block mt-0.5",
|
|
4986
|
+
children: ["Returns the user's role IDs as a comma-separated string. Best used with: ", /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
|
|
4987
|
+
className: "bg-surface-100 dark:bg-surface-950 px-1 py-0.5 rounded text-[11px]",
|
|
4988
|
+
children: "string_to_array(auth.roles(), ',') @> ARRAY['admin']"
|
|
4989
|
+
})]
|
|
4990
|
+
})] })
|
|
4991
|
+
]
|
|
4992
|
+
})
|
|
4993
|
+
]
|
|
4994
|
+
})
|
|
4995
|
+
]
|
|
4996
|
+
}),
|
|
4997
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4998
|
+
className: (0, _rebasepro_ui.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", _rebasepro_ui.defaultBorderMixin),
|
|
4999
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
|
|
5000
|
+
variant: "body2",
|
|
5001
|
+
className: "text-primary dark:text-primary-light mb-4 sm:mb-0 max-w-md",
|
|
5002
|
+
children: t("studio_policy_help_docs_cta")
|
|
5003
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Button, {
|
|
5004
|
+
component: "a",
|
|
5005
|
+
href: "https://www.postgresql.org/docs/current/sql-createpolicy.html",
|
|
5006
|
+
target: "_blank",
|
|
5007
|
+
variant: "outlined",
|
|
5008
|
+
color: "primary",
|
|
5009
|
+
size: "small",
|
|
5010
|
+
className: "whitespace-nowrap flex-shrink-0",
|
|
5011
|
+
children: t("studio_policy_help_read_docs")
|
|
5012
|
+
})]
|
|
5013
|
+
})
|
|
5014
|
+
]
|
|
5015
|
+
}),
|
|
5016
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.DialogActions, {
|
|
5017
|
+
className: "p-4 sm:px-6 sm:pb-6 pt-0 border-t-0",
|
|
5018
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Button, {
|
|
5019
|
+
onClick: () => setHelpOpen(false),
|
|
5020
|
+
variant: "filled",
|
|
5021
|
+
color: "primary",
|
|
5022
|
+
children: t("studio_policy_help_got_it")
|
|
4999
5023
|
})
|
|
5000
|
-
]
|
|
5001
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.DialogActions, {
|
|
5002
|
-
className: "p-4 sm:px-6 sm:pb-6 pt-0 border-t-0",
|
|
5003
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Button, {
|
|
5004
|
-
onClick: () => setHelpOpen(false),
|
|
5005
|
-
variant: "filled",
|
|
5006
|
-
color: "primary",
|
|
5007
|
-
children: t("studio_policy_help_got_it")
|
|
5008
5024
|
})
|
|
5009
|
-
|
|
5025
|
+
]
|
|
5010
5026
|
})
|
|
5011
5027
|
] });
|
|
5012
5028
|
};
|
|
@@ -5014,6 +5030,12 @@ return result;
|
|
|
5014
5030
|
//#endregion
|
|
5015
5031
|
//#region src/components/RLSEditor/RLSEditor.tsx
|
|
5016
5032
|
var RLSEditor_exports = /* @__PURE__ */ __exportAll({ RLSEditor: () => RLSEditor$1 });
|
|
5033
|
+
function classifyTableClient(tableName, schemaName, junctionTableNames, isMappedToCollection) {
|
|
5034
|
+
if (_rebasepro_common.REBASE_INTERNAL_SCHEMAS.includes(schemaName) || _rebasepro_common.REBASE_INTERNAL_PREFIXES.some((prefix) => tableName.startsWith(prefix))) return "internal";
|
|
5035
|
+
if (isMappedToCollection) return "collection";
|
|
5036
|
+
if (junctionTableNames.has(tableName)) return "junction";
|
|
5037
|
+
return "other";
|
|
5038
|
+
}
|
|
5017
5039
|
/**
|
|
5018
5040
|
* Validates and double-quotes a SQL identifier to prevent injection.
|
|
5019
5041
|
* Only allows safe Postgres identifiers (letters, digits, underscores).
|
|
@@ -5023,6 +5045,81 @@ return result;
|
|
|
5023
5045
|
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)) throw new Error(`Invalid SQL identifier: "${name}". Only letters, digits, and underscores are allowed.`);
|
|
5024
5046
|
return `"${name}"`;
|
|
5025
5047
|
}
|
|
5048
|
+
function SidebarSection({ title, icon, expanded, onToggle, count, children }) {
|
|
5049
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5050
|
+
className: "mb-2",
|
|
5051
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5052
|
+
className: "flex items-center p-1.5 cursor-pointer hover:bg-surface-100 dark:hover:bg-surface-900 rounded transition-colors",
|
|
5053
|
+
onClick: onToggle,
|
|
5054
|
+
children: [
|
|
5055
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
5056
|
+
className: (0, _rebasepro_ui.cls)("w-3 h-3 mr-1.5 transition-transform text-text-disabled dark:text-text-disabled-dark", expanded ? "rotate-90" : ""),
|
|
5057
|
+
fill: "currentColor",
|
|
5058
|
+
viewBox: "0 0 20 20",
|
|
5059
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.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" })
|
|
5060
|
+
}),
|
|
5061
|
+
icon,
|
|
5062
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
|
|
5063
|
+
variant: "body2",
|
|
5064
|
+
className: "text-text-primary dark:text-text-primary-dark font-medium text-xs truncate flex-grow ml-1.5",
|
|
5065
|
+
children: title
|
|
5066
|
+
}),
|
|
5067
|
+
count !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5068
|
+
className: "text-[10px] text-text-disabled dark:text-text-disabled-dark font-medium tabular-nums mr-1",
|
|
5069
|
+
children: count
|
|
5070
|
+
})
|
|
5071
|
+
]
|
|
5072
|
+
}), expanded && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5073
|
+
className: "ml-3 mt-0.5 space-y-0.5",
|
|
5074
|
+
children
|
|
5075
|
+
})]
|
|
5076
|
+
});
|
|
5077
|
+
}
|
|
5078
|
+
function SidebarTableRow({ table, isSelected, onSelect, badge, dimmed, t }) {
|
|
5079
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5080
|
+
onClick: onSelect,
|
|
5081
|
+
className: (0, _rebasepro_ui.cls)("flex items-center p-1 cursor-pointer rounded transition-colors group relative", isSelected ? "bg-primary/10 text-primary dark:bg-primary/20 dark:text-primary-light" : "hover:bg-surface-100 dark:hover:bg-surface-900 text-text-secondary dark:text-text-secondary-dark", dimmed && !isSelected && "opacity-60"),
|
|
5082
|
+
children: [
|
|
5083
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
5084
|
+
className: "w-3.5 h-3.5 mr-1 shrink-0 text-text-disabled dark:text-text-disabled-dark",
|
|
5085
|
+
fill: "none",
|
|
5086
|
+
stroke: "currentColor",
|
|
5087
|
+
viewBox: "0 0 24 24",
|
|
5088
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
5089
|
+
strokeLinecap: "round",
|
|
5090
|
+
strokeLinejoin: "round",
|
|
5091
|
+
strokeWidth: 2,
|
|
5092
|
+
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"
|
|
5093
|
+
})
|
|
5094
|
+
}),
|
|
5095
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
|
|
5096
|
+
variant: "body2",
|
|
5097
|
+
className: "text-xs truncate flex-1 min-w-0",
|
|
5098
|
+
children: table.tableName
|
|
5099
|
+
}),
|
|
5100
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5101
|
+
className: "flex items-center gap-1.5 shrink-0 ml-2",
|
|
5102
|
+
children: [
|
|
5103
|
+
badge && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5104
|
+
className: "text-[9px] uppercase tracking-wider font-semibold text-text-disabled dark:text-text-disabled-dark bg-surface-200 dark:bg-surface-800 rounded px-1 py-px",
|
|
5105
|
+
children: badge
|
|
5106
|
+
}),
|
|
5107
|
+
table.rlsEnabled ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Tooltip, {
|
|
5108
|
+
title: t("studio_rls_enabled"),
|
|
5109
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "w-1.5 h-1.5 rounded-full bg-green-500" })
|
|
5110
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Tooltip, {
|
|
5111
|
+
title: t("studio_rls_disabled"),
|
|
5112
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "w-1.5 h-1.5 rounded-full bg-orange-400 opacity-50" })
|
|
5113
|
+
}),
|
|
5114
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5115
|
+
className: "text-[10px] opacity-40 group-hover:opacity-100 min-w-[1.2rem] text-right font-medium",
|
|
5116
|
+
children: table.policies.length
|
|
5117
|
+
})
|
|
5118
|
+
]
|
|
5119
|
+
})
|
|
5120
|
+
]
|
|
5121
|
+
});
|
|
5122
|
+
}
|
|
5026
5123
|
var RLSEditor$1;
|
|
5027
5124
|
var init_RLSEditor = __esmMin((() => {
|
|
5028
5125
|
init_PolicyEditor();
|
|
@@ -5036,6 +5133,7 @@ return result;
|
|
|
5036
5133
|
const [tables, setTables] = (0, react.useState)([]);
|
|
5037
5134
|
const [selectedTable, setSelectedTable] = (0, react.useState)(null);
|
|
5038
5135
|
const [activeTab, setActiveTab] = (0, react.useState)(0);
|
|
5136
|
+
const [junctionTableNames, setJunctionTableNames] = (0, react.useState)(/* @__PURE__ */ new Set());
|
|
5039
5137
|
const [editingPolicy, setEditingPolicy] = (0, react.useState)(null);
|
|
5040
5138
|
const [sidebarSize, setSidebarSize] = (0, react.useState)(() => {
|
|
5041
5139
|
try {
|
|
@@ -5045,7 +5143,11 @@ return result;
|
|
|
5045
5143
|
return 20;
|
|
5046
5144
|
}
|
|
5047
5145
|
});
|
|
5048
|
-
const [
|
|
5146
|
+
const [expandedSections, setExpandedSections] = (0, react.useState)({
|
|
5147
|
+
collection: true,
|
|
5148
|
+
other: true,
|
|
5149
|
+
internal: false
|
|
5150
|
+
});
|
|
5049
5151
|
const [sidebarTab, setSidebarTab] = (0, react.useState)("tables");
|
|
5050
5152
|
(0, react.useEffect)(() => {
|
|
5051
5153
|
try {
|
|
@@ -5127,7 +5229,15 @@ return result;
|
|
|
5127
5229
|
});
|
|
5128
5230
|
const sortedTables = Object.values(tableMap).sort((a, b) => a.tableName.localeCompare(b.tableName));
|
|
5129
5231
|
setTables(sortedTables);
|
|
5130
|
-
|
|
5232
|
+
try {
|
|
5233
|
+
const jRows = extractRows(await databaseAdmin.executeSql(_rebasepro_common.JUNCTION_TABLES_SQL));
|
|
5234
|
+
setJunctionTableNames(new Set(jRows.map((r) => r.table_name).filter(Boolean)));
|
|
5235
|
+
} catch {}
|
|
5236
|
+
if (sortedTables.length > 0 && !selectedTable) {
|
|
5237
|
+
const firstUserTable = sortedTables.find((t) => !_rebasepro_common.REBASE_INTERNAL_SCHEMAS.includes(t.schemaName));
|
|
5238
|
+
if (firstUserTable) setSelectedTable(`${firstUserTable.schemaName}.${firstUserTable.tableName}`);
|
|
5239
|
+
else setSelectedTable(`${sortedTables[0].schemaName}.${sortedTables[0].tableName}`);
|
|
5240
|
+
}
|
|
5131
5241
|
} catch (e) {
|
|
5132
5242
|
console.error("RLS fetch error:", e);
|
|
5133
5243
|
setError("Failed to fetch RLS policies: " + (e instanceof Error ? e.message : String(e)));
|
|
@@ -5145,18 +5255,38 @@ return result;
|
|
|
5145
5255
|
if (!selectedTable) return null;
|
|
5146
5256
|
return tables.find((t) => `${t.schemaName}.${t.tableName}` === selectedTable) || null;
|
|
5147
5257
|
}, [selectedTable, tables]);
|
|
5148
|
-
|
|
5149
|
-
|
|
5258
|
+
/** Categorize tables into 4 buckets for the sidebar. */
|
|
5259
|
+
const categorizedTables = (0, react.useMemo)(() => {
|
|
5260
|
+
const groups = {
|
|
5261
|
+
collection: [],
|
|
5262
|
+
junction: [],
|
|
5263
|
+
internal: [],
|
|
5264
|
+
other: []
|
|
5265
|
+
};
|
|
5150
5266
|
tables.forEach((table) => {
|
|
5151
|
-
|
|
5152
|
-
groups[table.schemaName].push(table);
|
|
5267
|
+
const isMapped = !!collectionRegistry.collections?.find((c) => c.id === table.tableName || c.path === table.tableName || c.table === table.tableName || c.slug === table.tableName || c.collectionId === table.tableName);
|
|
5268
|
+
groups[classifyTableClient(table.tableName, table.schemaName, junctionTableNames, isMapped)].push(table);
|
|
5153
5269
|
});
|
|
5154
5270
|
return groups;
|
|
5155
|
-
}, [
|
|
5271
|
+
}, [
|
|
5272
|
+
tables,
|
|
5273
|
+
junctionTableNames,
|
|
5274
|
+
collectionRegistry.collections
|
|
5275
|
+
]);
|
|
5156
5276
|
const activeCollection = (0, react.useMemo)(() => {
|
|
5157
5277
|
if (!activeTableData) return null;
|
|
5158
5278
|
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;
|
|
5159
5279
|
}, [activeTableData, collectionRegistry.collections]);
|
|
5280
|
+
/** The category of the currently selected table. */
|
|
5281
|
+
const activeTableCategory = (0, react.useMemo)(() => {
|
|
5282
|
+
if (!activeTableData) return null;
|
|
5283
|
+
const isMapped = !!activeCollection;
|
|
5284
|
+
return classifyTableClient(activeTableData.tableName, activeTableData.schemaName, junctionTableNames, isMapped);
|
|
5285
|
+
}, [
|
|
5286
|
+
activeTableData,
|
|
5287
|
+
activeCollection,
|
|
5288
|
+
junctionTableNames
|
|
5289
|
+
]);
|
|
5160
5290
|
const mergedPolicies = (0, react.useMemo)(() => {
|
|
5161
5291
|
if (!activeTableData) return [];
|
|
5162
5292
|
const policiesMap = {};
|
|
@@ -5254,74 +5384,78 @@ return result;
|
|
|
5254
5384
|
children: isLoading && tables.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5255
5385
|
className: "flex justify-center p-4",
|
|
5256
5386
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.CircularProgress, { size: "small" })
|
|
5257
|
-
}) :
|
|
5387
|
+
}) : tables.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5258
5388
|
className: "p-4 text-center",
|
|
5259
5389
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
|
|
5260
5390
|
variant: "caption",
|
|
5261
5391
|
className: "text-text-disabled dark:text-text-disabled-dark italic",
|
|
5262
5392
|
children: t("studio_rls_no_tables")
|
|
5263
5393
|
})
|
|
5264
|
-
}) :
|
|
5265
|
-
|
|
5266
|
-
|
|
5267
|
-
|
|
5268
|
-
|
|
5394
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
5395
|
+
categorizedTables.collection.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SidebarSection, {
|
|
5396
|
+
title: "Schema Collections",
|
|
5397
|
+
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.DatabaseIcon, {
|
|
5398
|
+
size: 12,
|
|
5399
|
+
className: "text-primary dark:text-primary-light"
|
|
5400
|
+
}),
|
|
5401
|
+
expanded: expandedSections.collection ?? true,
|
|
5402
|
+
onToggle: () => setExpandedSections((prev) => ({
|
|
5269
5403
|
...prev,
|
|
5270
|
-
|
|
5404
|
+
collection: !(prev.collection ?? true)
|
|
5271
5405
|
})),
|
|
5272
|
-
children:
|
|
5273
|
-
|
|
5274
|
-
|
|
5275
|
-
|
|
5276
|
-
|
|
5277
|
-
}
|
|
5278
|
-
|
|
5279
|
-
|
|
5280
|
-
|
|
5281
|
-
|
|
5282
|
-
|
|
5283
|
-
|
|
5284
|
-
|
|
5285
|
-
|
|
5286
|
-
|
|
5287
|
-
|
|
5288
|
-
|
|
5289
|
-
|
|
5290
|
-
|
|
5291
|
-
|
|
5292
|
-
|
|
5293
|
-
|
|
5294
|
-
|
|
5295
|
-
|
|
5296
|
-
|
|
5297
|
-
|
|
5298
|
-
|
|
5299
|
-
|
|
5300
|
-
|
|
5301
|
-
|
|
5302
|
-
|
|
5303
|
-
|
|
5304
|
-
|
|
5305
|
-
|
|
5306
|
-
|
|
5307
|
-
|
|
5308
|
-
|
|
5309
|
-
|
|
5310
|
-
|
|
5311
|
-
|
|
5312
|
-
|
|
5313
|
-
|
|
5314
|
-
|
|
5315
|
-
|
|
5316
|
-
|
|
5317
|
-
|
|
5318
|
-
|
|
5319
|
-
|
|
5320
|
-
|
|
5321
|
-
|
|
5322
|
-
})
|
|
5323
|
-
})
|
|
5324
|
-
}
|
|
5406
|
+
children: categorizedTables.collection.map((table) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SidebarTableRow, {
|
|
5407
|
+
table,
|
|
5408
|
+
isSelected: selectedTable === `${table.schemaName}.${table.tableName}`,
|
|
5409
|
+
onSelect: () => setSelectedTable(`${table.schemaName}.${table.tableName}`),
|
|
5410
|
+
t
|
|
5411
|
+
}, `${table.schemaName}.${table.tableName}`))
|
|
5412
|
+
}),
|
|
5413
|
+
(categorizedTables.other.length > 0 || categorizedTables.junction.length > 0) && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(SidebarSection, {
|
|
5414
|
+
title: "Other Tables",
|
|
5415
|
+
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.AlertTriangleIcon, {
|
|
5416
|
+
size: 12,
|
|
5417
|
+
className: "text-yellow-500 dark:text-yellow-400"
|
|
5418
|
+
}),
|
|
5419
|
+
expanded: expandedSections.other ?? true,
|
|
5420
|
+
onToggle: () => setExpandedSections((prev) => ({
|
|
5421
|
+
...prev,
|
|
5422
|
+
other: !(prev.other ?? true)
|
|
5423
|
+
})),
|
|
5424
|
+
children: [categorizedTables.other.map((table) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SidebarTableRow, {
|
|
5425
|
+
table,
|
|
5426
|
+
isSelected: selectedTable === `${table.schemaName}.${table.tableName}`,
|
|
5427
|
+
onSelect: () => setSelectedTable(`${table.schemaName}.${table.tableName}`),
|
|
5428
|
+
t
|
|
5429
|
+
}, `${table.schemaName}.${table.tableName}`)), categorizedTables.junction.map((table) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SidebarTableRow, {
|
|
5430
|
+
table,
|
|
5431
|
+
isSelected: selectedTable === `${table.schemaName}.${table.tableName}`,
|
|
5432
|
+
onSelect: () => setSelectedTable(`${table.schemaName}.${table.tableName}`),
|
|
5433
|
+
badge: "Junction",
|
|
5434
|
+
dimmed: true,
|
|
5435
|
+
t
|
|
5436
|
+
}, `${table.schemaName}.${table.tableName}`))]
|
|
5437
|
+
}),
|
|
5438
|
+
categorizedTables.internal.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SidebarSection, {
|
|
5439
|
+
title: "Rebase Internal",
|
|
5440
|
+
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.LockIcon, {
|
|
5441
|
+
size: 12,
|
|
5442
|
+
className: "text-text-disabled dark:text-text-disabled-dark"
|
|
5443
|
+
}),
|
|
5444
|
+
expanded: expandedSections.internal ?? false,
|
|
5445
|
+
onToggle: () => setExpandedSections((prev) => ({
|
|
5446
|
+
...prev,
|
|
5447
|
+
internal: !(prev.internal ?? false)
|
|
5448
|
+
})),
|
|
5449
|
+
count: categorizedTables.internal.length,
|
|
5450
|
+
children: categorizedTables.internal.map((table) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SidebarTableRow, {
|
|
5451
|
+
table,
|
|
5452
|
+
isSelected: selectedTable === `${table.schemaName}.${table.tableName}`,
|
|
5453
|
+
onSelect: () => setSelectedTable(`${table.schemaName}.${table.tableName}`),
|
|
5454
|
+
dimmed: true,
|
|
5455
|
+
t
|
|
5456
|
+
}, `${table.schemaName}.${table.tableName}`))
|
|
5457
|
+
})
|
|
5458
|
+
] })
|
|
5325
5459
|
})]
|
|
5326
5460
|
}), sidebarTab === "info" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5327
5461
|
className: "flex flex-col h-full",
|
|
@@ -5484,10 +5618,11 @@ return result;
|
|
|
5484
5618
|
size: "small",
|
|
5485
5619
|
onClick: async () => {
|
|
5486
5620
|
const table = activeTableData.tableName;
|
|
5621
|
+
const qualifiedTable = `${sanitizeSqlIdentifier(activeTableData.schemaName)}.${sanitizeSqlIdentifier(table)}`;
|
|
5487
5622
|
const action = activeTableData.rlsEnabled ? "DISABLE" : "ENABLE";
|
|
5488
5623
|
if (!confirm(`Are you sure you want to ${action.toLowerCase()} Row Level Security on "${table}"?`)) return;
|
|
5489
5624
|
try {
|
|
5490
|
-
await databaseAdmin.executeSql(`ALTER TABLE ${
|
|
5625
|
+
await databaseAdmin.executeSql(`ALTER TABLE ${qualifiedTable} ${action} ROW LEVEL SECURITY`);
|
|
5491
5626
|
snackbarController.open({
|
|
5492
5627
|
type: "success",
|
|
5493
5628
|
message: `RLS ${action.toLowerCase()}d on ${table}`
|
|
@@ -5514,7 +5649,6 @@ return result;
|
|
|
5514
5649
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Button, {
|
|
5515
5650
|
size: "small",
|
|
5516
5651
|
color: "primary",
|
|
5517
|
-
disabled: !activeCollection,
|
|
5518
5652
|
onClick: () => setEditingPolicy("new"),
|
|
5519
5653
|
children: t("studio_rls_create_policy")
|
|
5520
5654
|
})
|
|
@@ -5555,31 +5689,57 @@ return result;
|
|
|
5555
5689
|
schema: activeTableData.schemaName,
|
|
5556
5690
|
table: activeTableData.tableName,
|
|
5557
5691
|
onSave: async (newPolicy) => {
|
|
5558
|
-
if (
|
|
5559
|
-
|
|
5560
|
-
|
|
5561
|
-
|
|
5562
|
-
|
|
5563
|
-
|
|
5564
|
-
|
|
5565
|
-
|
|
5566
|
-
|
|
5567
|
-
|
|
5568
|
-
|
|
5569
|
-
|
|
5570
|
-
|
|
5571
|
-
|
|
5572
|
-
|
|
5573
|
-
|
|
5574
|
-
|
|
5575
|
-
|
|
5576
|
-
|
|
5577
|
-
|
|
5578
|
-
|
|
5579
|
-
|
|
5580
|
-
|
|
5692
|
+
if (activeCollection) {
|
|
5693
|
+
const rule = {
|
|
5694
|
+
name: newPolicy.policyname,
|
|
5695
|
+
operation: newPolicy.cmd?.toLowerCase(),
|
|
5696
|
+
mode: newPolicy.permissive?.toLowerCase(),
|
|
5697
|
+
using: newPolicy.qual || void 0,
|
|
5698
|
+
withCheck: newPolicy.with_check || void 0,
|
|
5699
|
+
roles: newPolicy.roles
|
|
5700
|
+
};
|
|
5701
|
+
const existingRules = ((0, _rebasepro_types.isPostgresCollection)(activeCollection) ? activeCollection.securityRules : void 0) || [];
|
|
5702
|
+
let newRules;
|
|
5703
|
+
if (editingPolicy === "new") newRules = [...existingRules, rule];
|
|
5704
|
+
else newRules = existingRules.map((r) => r.name === editingPolicy.policyname ? rule : r);
|
|
5705
|
+
try {
|
|
5706
|
+
if (!(await fetch(`${apiUrl}/api/schema-editor/collection/save`, {
|
|
5707
|
+
method: "POST",
|
|
5708
|
+
headers: { "Content-Type": "application/json" },
|
|
5709
|
+
body: JSON.stringify({
|
|
5710
|
+
collectionId: activeCollection.id || activeCollection.path || activeCollection.alias || activeTableData.tableName,
|
|
5711
|
+
collectionData: { securityRules: newRules }
|
|
5712
|
+
})
|
|
5713
|
+
})).ok) throw new Error("Failed to save policy");
|
|
5714
|
+
snackbarController.open({
|
|
5715
|
+
type: "success",
|
|
5716
|
+
message: "Policy saved successfully"
|
|
5717
|
+
});
|
|
5718
|
+
setEditingPolicy(null);
|
|
5719
|
+
fetchRLSData();
|
|
5720
|
+
} catch (e) {
|
|
5721
|
+
snackbarController.open({
|
|
5722
|
+
type: "error",
|
|
5723
|
+
message: e instanceof Error ? e.message : String(e)
|
|
5724
|
+
});
|
|
5725
|
+
}
|
|
5726
|
+
} else try {
|
|
5727
|
+
const qualifiedTable = `${sanitizeSqlIdentifier(activeTableData.schemaName)}.${sanitizeSqlIdentifier(activeTableData.tableName)}`;
|
|
5728
|
+
const policyName = sanitizeSqlIdentifier(newPolicy.policyname || "unnamed_policy");
|
|
5729
|
+
const cmd = newPolicy.cmd || "ALL";
|
|
5730
|
+
const permissive = (newPolicy.permissive || "PERMISSIVE") === "PERMISSIVE" ? "PERMISSIVE" : "RESTRICTIVE";
|
|
5731
|
+
const roles = newPolicy.roles && newPolicy.roles.length > 0 ? newPolicy.roles.map((r) => sanitizeSqlIdentifier(r)).join(", ") : "public";
|
|
5732
|
+
if (editingPolicy !== "new") await databaseAdmin.executeSql(`DROP POLICY IF EXISTS ${policyName} ON ${qualifiedTable}`);
|
|
5733
|
+
let sql = `CREATE POLICY ${policyName} ON ${qualifiedTable}`;
|
|
5734
|
+
sql += ` AS ${permissive}`;
|
|
5735
|
+
sql += ` FOR ${cmd}`;
|
|
5736
|
+
sql += ` TO ${roles}`;
|
|
5737
|
+
if (newPolicy.qual) sql += ` USING (${newPolicy.qual})`;
|
|
5738
|
+
if (newPolicy.with_check) sql += ` WITH CHECK (${newPolicy.with_check})`;
|
|
5739
|
+
await databaseAdmin.executeSql(sql);
|
|
5740
|
+
snackbarController.open({
|
|
5581
5741
|
type: "success",
|
|
5582
|
-
message:
|
|
5742
|
+
message: `Policy "${newPolicy.policyname}" applied directly via SQL`
|
|
5583
5743
|
});
|
|
5584
5744
|
setEditingPolicy(null);
|
|
5585
5745
|
fetchRLSData();
|
|
@@ -5598,17 +5758,59 @@ return result;
|
|
|
5598
5758
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5599
5759
|
className: "max-w-4xl mx-auto flex flex-col gap-6",
|
|
5600
5760
|
children: [
|
|
5601
|
-
activeTableData &&
|
|
5761
|
+
activeTableData && activeTableCategory === "internal" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Alert, {
|
|
5762
|
+
color: "info",
|
|
5763
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5764
|
+
className: "flex items-start gap-2",
|
|
5765
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.LockIcon, {
|
|
5766
|
+
size: 16,
|
|
5767
|
+
className: "shrink-0 mt-0.5"
|
|
5768
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
|
|
5769
|
+
variant: "body2",
|
|
5770
|
+
className: "mb-1 font-semibold",
|
|
5771
|
+
children: "Rebase System Table"
|
|
5772
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
|
|
5773
|
+
variant: "caption",
|
|
5774
|
+
className: "opacity-80",
|
|
5775
|
+
children: "This table is managed internally by Rebase. Its security policies are configured automatically. Editing policies on system tables is an advanced operation."
|
|
5776
|
+
})] })]
|
|
5777
|
+
})
|
|
5778
|
+
}),
|
|
5779
|
+
activeTableData && activeTableCategory === "junction" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Alert, {
|
|
5780
|
+
color: "info",
|
|
5781
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5782
|
+
className: "flex items-start gap-2",
|
|
5783
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Link2Icon, {
|
|
5784
|
+
size: 16,
|
|
5785
|
+
className: "shrink-0 mt-0.5"
|
|
5786
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
|
|
5787
|
+
variant: "body2",
|
|
5788
|
+
className: "mb-1 font-semibold",
|
|
5789
|
+
children: "Junction Table"
|
|
5790
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
|
|
5791
|
+
variant: "caption",
|
|
5792
|
+
className: "opacity-80",
|
|
5793
|
+
children: "This is an auto-generated junction table for a many-to-many relation. Its access is typically managed through the related collections' policies. You can still add RLS policies directly if needed."
|
|
5794
|
+
})] })]
|
|
5795
|
+
})
|
|
5796
|
+
}),
|
|
5797
|
+
activeTableData && activeTableCategory === "other" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Alert, {
|
|
5602
5798
|
color: "warning",
|
|
5603
|
-
children:
|
|
5604
|
-
|
|
5605
|
-
|
|
5606
|
-
|
|
5607
|
-
|
|
5608
|
-
|
|
5609
|
-
|
|
5610
|
-
|
|
5611
|
-
|
|
5799
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5800
|
+
className: "flex items-start gap-2",
|
|
5801
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.AlertTriangleIcon, {
|
|
5802
|
+
size: 16,
|
|
5803
|
+
className: "shrink-0 mt-0.5"
|
|
5804
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
|
|
5805
|
+
variant: "body2",
|
|
5806
|
+
className: "mb-1 font-semibold",
|
|
5807
|
+
children: "Unmapped Table"
|
|
5808
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
|
|
5809
|
+
variant: "caption",
|
|
5810
|
+
className: "opacity-80",
|
|
5811
|
+
children: "This table exists in the database but isn't mapped to a collection definition. Import it into a Schema configuration file to manage security policies visually."
|
|
5812
|
+
})] })]
|
|
5813
|
+
})
|
|
5612
5814
|
}),
|
|
5613
5815
|
activeTableData && !activeTableData.rlsEnabled && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5614
5816
|
className: (0, _rebasepro_ui.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", _rebasepro_ui.defaultBorderMixin),
|
|
@@ -5722,7 +5924,6 @@ return result;
|
|
|
5722
5924
|
variant: "text",
|
|
5723
5925
|
color: "primary",
|
|
5724
5926
|
onClick: () => setEditingPolicy(policy),
|
|
5725
|
-
disabled: !activeCollection,
|
|
5726
5927
|
children: t("studio_rls_edit")
|
|
5727
5928
|
}),
|
|
5728
5929
|
policy.status !== "code_only" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Tooltip, {
|
|
@@ -5732,9 +5933,10 @@ return result;
|
|
|
5732
5933
|
size: "small",
|
|
5733
5934
|
onClick: async () => {
|
|
5734
5935
|
const table = activeTableData.tableName;
|
|
5936
|
+
const qualifiedTable = `${sanitizeSqlIdentifier(activeTableData.schemaName)}.${sanitizeSqlIdentifier(table)}`;
|
|
5735
5937
|
if (!confirm(`Drop policy "${policy.policyname}" from table "${table}"?`)) return;
|
|
5736
5938
|
try {
|
|
5737
|
-
await databaseAdmin.executeSql(`DROP POLICY ${sanitizeSqlIdentifier(policy.policyname)} ON ${
|
|
5939
|
+
await databaseAdmin.executeSql(`DROP POLICY ${sanitizeSqlIdentifier(policy.policyname)} ON ${qualifiedTable}`);
|
|
5738
5940
|
snackbarController.open({
|
|
5739
5941
|
type: "success",
|
|
5740
5942
|
message: `Policy "${policy.policyname}" dropped`
|
|
@@ -5771,7 +5973,7 @@ return result;
|
|
|
5771
5973
|
className: "text-text-disabled dark:text-text-disabled-dark max-w-sm mb-4",
|
|
5772
5974
|
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."
|
|
5773
5975
|
}),
|
|
5774
|
-
|
|
5976
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Button, {
|
|
5775
5977
|
size: "small",
|
|
5776
5978
|
variant: "filled",
|
|
5777
5979
|
color: "primary",
|
|
@@ -6180,30 +6382,37 @@ return result;
|
|
|
6180
6382
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.Dialog, {
|
|
6181
6383
|
open: deleteDialogOpen,
|
|
6182
6384
|
onOpenChange: setDeleteDialogOpen,
|
|
6183
|
-
children: [
|
|
6184
|
-
|
|
6185
|
-
|
|
6186
|
-
|
|
6187
|
-
|
|
6188
|
-
|
|
6189
|
-
|
|
6190
|
-
|
|
6191
|
-
|
|
6192
|
-
|
|
6193
|
-
|
|
6194
|
-
|
|
6195
|
-
|
|
6196
|
-
|
|
6197
|
-
|
|
6198
|
-
|
|
6199
|
-
|
|
6200
|
-
|
|
6201
|
-
|
|
6202
|
-
setDeleteDialogOpen(false)
|
|
6203
|
-
|
|
6204
|
-
},
|
|
6205
|
-
|
|
6206
|
-
|
|
6385
|
+
children: [
|
|
6386
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.DialogTitle, {
|
|
6387
|
+
hidden: true,
|
|
6388
|
+
children: "Delete File"
|
|
6389
|
+
}),
|
|
6390
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.DialogContent, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
|
|
6391
|
+
variant: "subtitle1",
|
|
6392
|
+
className: "mb-2",
|
|
6393
|
+
children: "Delete File?"
|
|
6394
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.Typography, {
|
|
6395
|
+
className: "text-surface-accent-600 dark:text-surface-accent-400",
|
|
6396
|
+
children: [
|
|
6397
|
+
"Are you sure you want to delete \"",
|
|
6398
|
+
file.name,
|
|
6399
|
+
"\"? This action cannot be undone."
|
|
6400
|
+
]
|
|
6401
|
+
})] }),
|
|
6402
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.DialogActions, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Button, {
|
|
6403
|
+
variant: "text",
|
|
6404
|
+
onClick: () => setDeleteDialogOpen(false),
|
|
6405
|
+
children: "Cancel"
|
|
6406
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Button, {
|
|
6407
|
+
variant: "filled",
|
|
6408
|
+
color: "error",
|
|
6409
|
+
onClick: () => {
|
|
6410
|
+
setDeleteDialogOpen(false);
|
|
6411
|
+
onDelete();
|
|
6412
|
+
},
|
|
6413
|
+
children: "Delete"
|
|
6414
|
+
})] })
|
|
6415
|
+
]
|
|
6207
6416
|
})] });
|
|
6208
6417
|
}
|
|
6209
6418
|
var StorageView$1;
|
|
@@ -7094,31 +7303,38 @@ return result;
|
|
|
7094
7303
|
setDeleteDialogTarget(null);
|
|
7095
7304
|
}
|
|
7096
7305
|
},
|
|
7097
|
-
children: [
|
|
7098
|
-
|
|
7099
|
-
|
|
7100
|
-
|
|
7101
|
-
|
|
7102
|
-
|
|
7103
|
-
|
|
7104
|
-
|
|
7105
|
-
|
|
7106
|
-
|
|
7107
|
-
|
|
7108
|
-
|
|
7109
|
-
|
|
7110
|
-
},
|
|
7111
|
-
|
|
7112
|
-
|
|
7113
|
-
|
|
7114
|
-
|
|
7115
|
-
|
|
7116
|
-
|
|
7117
|
-
|
|
7118
|
-
|
|
7119
|
-
|
|
7120
|
-
|
|
7121
|
-
|
|
7306
|
+
children: [
|
|
7307
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.DialogTitle, {
|
|
7308
|
+
hidden: true,
|
|
7309
|
+
children: "Delete Confirmation"
|
|
7310
|
+
}),
|
|
7311
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.DialogContent, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
|
|
7312
|
+
variant: "subtitle1",
|
|
7313
|
+
className: "font-semibold mb-2",
|
|
7314
|
+
children: deleteDialogTarget === "selection" ? `Delete ${selectedPaths.size} item${selectedPaths.size !== 1 ? "s" : ""}?` : deleteDialogTarget ? `Delete folder "${deleteDialogTarget.name}"?` : "Delete?"
|
|
7315
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
|
|
7316
|
+
variant: "body2",
|
|
7317
|
+
color: "secondary",
|
|
7318
|
+
children: deleteDialogTarget === "selection" ? "This will permanently delete all selected files and folders, including their contents. This action cannot be undone." : "This will permanently delete the folder and all of its contents. This action cannot be undone."
|
|
7319
|
+
})] }),
|
|
7320
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.DialogActions, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Button, {
|
|
7321
|
+
variant: "text",
|
|
7322
|
+
onClick: () => {
|
|
7323
|
+
setDeleteDialogOpen(false);
|
|
7324
|
+
setDeleteDialogTarget(null);
|
|
7325
|
+
},
|
|
7326
|
+
disabled: deleting,
|
|
7327
|
+
children: "Cancel"
|
|
7328
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.LoadingButton, {
|
|
7329
|
+
color: "error",
|
|
7330
|
+
loading: deleting,
|
|
7331
|
+
onClick: deleteDialogTarget === "selection" ? handleBulkDelete : handleConfirmDeleteFolder,
|
|
7332
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Trash2Icon, {
|
|
7333
|
+
size: 14,
|
|
7334
|
+
className: "mr-1"
|
|
7335
|
+
}), "Delete"]
|
|
7336
|
+
})] })
|
|
7337
|
+
]
|
|
7122
7338
|
}),
|
|
7123
7339
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.Dialog, {
|
|
7124
7340
|
open: newFolderDialogOpen,
|
|
@@ -7128,13 +7344,12 @@ return result;
|
|
|
7128
7344
|
setNewFolderName("");
|
|
7129
7345
|
}
|
|
7130
7346
|
},
|
|
7131
|
-
children: [
|
|
7132
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.
|
|
7133
|
-
|
|
7134
|
-
className: "font-semibold mb-4",
|
|
7347
|
+
children: [
|
|
7348
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.DialogTitle, {
|
|
7349
|
+
hidden: true,
|
|
7135
7350
|
children: "New Folder"
|
|
7136
7351
|
}),
|
|
7137
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.TextField, {
|
|
7352
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.DialogContent, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.TextField, {
|
|
7138
7353
|
autoFocus: true,
|
|
7139
7354
|
size: "small",
|
|
7140
7355
|
label: "Folder name",
|
|
@@ -7148,8 +7363,7 @@ return result;
|
|
|
7148
7363
|
},
|
|
7149
7364
|
disabled: creatingFolder,
|
|
7150
7365
|
placeholder: "Enter folder name"
|
|
7151
|
-
}),
|
|
7152
|
-
currentPath && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.Typography, {
|
|
7366
|
+
}), currentPath && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.Typography, {
|
|
7153
7367
|
variant: "caption",
|
|
7154
7368
|
color: "secondary",
|
|
7155
7369
|
className: "mt-2",
|
|
@@ -7161,25 +7375,26 @@ return result;
|
|
|
7161
7375
|
"/"
|
|
7162
7376
|
]
|
|
7163
7377
|
})]
|
|
7164
|
-
})
|
|
7165
|
-
|
|
7166
|
-
|
|
7167
|
-
|
|
7168
|
-
|
|
7169
|
-
|
|
7170
|
-
|
|
7171
|
-
|
|
7172
|
-
|
|
7173
|
-
|
|
7174
|
-
|
|
7175
|
-
|
|
7176
|
-
|
|
7177
|
-
|
|
7178
|
-
|
|
7179
|
-
|
|
7180
|
-
|
|
7181
|
-
|
|
7182
|
-
|
|
7378
|
+
})] }),
|
|
7379
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.DialogActions, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Button, {
|
|
7380
|
+
variant: "text",
|
|
7381
|
+
onClick: () => {
|
|
7382
|
+
setNewFolderDialogOpen(false);
|
|
7383
|
+
setNewFolderName("");
|
|
7384
|
+
},
|
|
7385
|
+
disabled: creatingFolder,
|
|
7386
|
+
children: "Cancel"
|
|
7387
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.LoadingButton, {
|
|
7388
|
+
color: "primary",
|
|
7389
|
+
loading: creatingFolder,
|
|
7390
|
+
disabled: !newFolderName.trim(),
|
|
7391
|
+
onClick: handleCreateFolder,
|
|
7392
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.FolderPlusIcon, {
|
|
7393
|
+
size: 14,
|
|
7394
|
+
className: "mr-1"
|
|
7395
|
+
}), "Create"]
|
|
7396
|
+
})] })
|
|
7397
|
+
]
|
|
7183
7398
|
})
|
|
7184
7399
|
]
|
|
7185
7400
|
});
|
|
@@ -7193,7 +7408,7 @@ return result;
|
|
|
7193
7408
|
if (ms < 6e4) return `${(ms / 1e3).toFixed(1)}s`;
|
|
7194
7409
|
return `${(ms / 6e4).toFixed(1)}m`;
|
|
7195
7410
|
}
|
|
7196
|
-
function formatRelative$
|
|
7411
|
+
function formatRelative$2(iso) {
|
|
7197
7412
|
if (!iso) return "—";
|
|
7198
7413
|
const d = new Date(iso);
|
|
7199
7414
|
const now = Date.now();
|
|
@@ -7481,20 +7696,20 @@ return result;
|
|
|
7481
7696
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7482
7697
|
className: "grid grid-cols-2 md:grid-cols-4 gap-3",
|
|
7483
7698
|
children: [
|
|
7484
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard$
|
|
7699
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard$2, {
|
|
7485
7700
|
label: "Schedule",
|
|
7486
7701
|
value: selectedJob.schedule,
|
|
7487
7702
|
mono: true
|
|
7488
7703
|
}),
|
|
7489
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard$
|
|
7704
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard$2, {
|
|
7490
7705
|
label: "Last Run",
|
|
7491
|
-
value: formatRelative$
|
|
7706
|
+
value: formatRelative$2(selectedJob.lastRunAt)
|
|
7492
7707
|
}),
|
|
7493
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard$
|
|
7708
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard$2, {
|
|
7494
7709
|
label: "Next Run",
|
|
7495
|
-
value: selectedJob.enabled ? formatRelative$
|
|
7710
|
+
value: selectedJob.enabled ? formatRelative$2(selectedJob.nextRunAt) : "Paused"
|
|
7496
7711
|
}),
|
|
7497
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard$
|
|
7712
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard$2, {
|
|
7498
7713
|
label: "Duration",
|
|
7499
7714
|
value: selectedJob.lastDurationMs !== void 0 ? formatDuration(selectedJob.lastDurationMs) : "—"
|
|
7500
7715
|
})
|
|
@@ -7503,16 +7718,16 @@ return result;
|
|
|
7503
7718
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7504
7719
|
className: "grid grid-cols-3 gap-3 mt-3",
|
|
7505
7720
|
children: [
|
|
7506
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard$
|
|
7721
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard$2, {
|
|
7507
7722
|
label: "Status",
|
|
7508
7723
|
value: selectedJob.state.toUpperCase(),
|
|
7509
7724
|
chipColor: selectedJob.state === "error" ? "red" : selectedJob.state === "disabled" ? "gray" : "green"
|
|
7510
7725
|
}),
|
|
7511
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard$
|
|
7726
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard$2, {
|
|
7512
7727
|
label: "Total Runs",
|
|
7513
7728
|
value: String(selectedJob.totalRuns)
|
|
7514
7729
|
}),
|
|
7515
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard$
|
|
7730
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard$2, {
|
|
7516
7731
|
label: "Failures",
|
|
7517
7732
|
value: String(selectedJob.totalFailures),
|
|
7518
7733
|
highlight: selectedJob.totalFailures > 0
|
|
@@ -7579,7 +7794,7 @@ return result;
|
|
|
7579
7794
|
})]
|
|
7580
7795
|
});
|
|
7581
7796
|
}
|
|
7582
|
-
function StatCard$
|
|
7797
|
+
function StatCard$2({ label, value, mono, chipColor, highlight }) {
|
|
7583
7798
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7584
7799
|
className: (0, _rebasepro_ui.cls)("px-3 py-2 rounded-lg border bg-white dark:bg-surface-900", _rebasepro_ui.defaultBorderMixin),
|
|
7585
7800
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
|
|
@@ -8736,7 +8951,7 @@ return result;
|
|
|
8736
8951
|
if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
8737
8952
|
return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`;
|
|
8738
8953
|
}
|
|
8739
|
-
function formatRelative(date) {
|
|
8954
|
+
function formatRelative$1(date) {
|
|
8740
8955
|
if (!date) return "—";
|
|
8741
8956
|
const d = date instanceof Date ? date : new Date(date);
|
|
8742
8957
|
if (isNaN(d.getTime())) return "—";
|
|
@@ -8938,7 +9153,7 @@ return result;
|
|
|
8938
9153
|
"from ",
|
|
8939
9154
|
branch.parentDatabase,
|
|
8940
9155
|
" · ",
|
|
8941
|
-
formatRelative(branch.createdAt)
|
|
9156
|
+
formatRelative$1(branch.createdAt)
|
|
8942
9157
|
]
|
|
8943
9158
|
})]
|
|
8944
9159
|
}),
|
|
@@ -9002,21 +9217,21 @@ return result;
|
|
|
9002
9217
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
9003
9218
|
className: "grid grid-cols-2 md:grid-cols-4 gap-3",
|
|
9004
9219
|
children: [
|
|
9005
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard, {
|
|
9220
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard$1, {
|
|
9006
9221
|
label: "Branch Name",
|
|
9007
9222
|
value: selected.name,
|
|
9008
9223
|
mono: true
|
|
9009
9224
|
}),
|
|
9010
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard, {
|
|
9225
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard$1, {
|
|
9011
9226
|
label: "Source Database",
|
|
9012
9227
|
value: selected.parentDatabase,
|
|
9013
9228
|
mono: true
|
|
9014
9229
|
}),
|
|
9015
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard, {
|
|
9230
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard$1, {
|
|
9016
9231
|
label: "Created",
|
|
9017
|
-
value: formatRelative(selected.createdAt)
|
|
9232
|
+
value: formatRelative$1(selected.createdAt)
|
|
9018
9233
|
}),
|
|
9019
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard, {
|
|
9234
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard$1, {
|
|
9020
9235
|
label: "Size",
|
|
9021
9236
|
value: formatSize(selected.sizeBytes)
|
|
9022
9237
|
})
|
|
@@ -9118,13 +9333,12 @@ return result;
|
|
|
9118
9333
|
}) }),
|
|
9119
9334
|
branches.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.Select, {
|
|
9120
9335
|
label: "Source Database",
|
|
9121
|
-
value: sourceBranch ?? "",
|
|
9122
|
-
onValueChange: (v) => setSourceBranch(v
|
|
9336
|
+
value: sourceBranch ?? "__main__",
|
|
9337
|
+
onValueChange: (v) => setSourceBranch(v === "__main__" ? void 0 : v),
|
|
9123
9338
|
placeholder: "Default (main database)",
|
|
9124
9339
|
size: "small",
|
|
9125
|
-
renderValue: (v) => v || "Default (main database)",
|
|
9126
9340
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.SelectItem, {
|
|
9127
|
-
value: "",
|
|
9341
|
+
value: "__main__",
|
|
9128
9342
|
children: "Default (main database)"
|
|
9129
9343
|
}), branches.map((b) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.SelectItem, {
|
|
9130
9344
|
value: b.name,
|
|
@@ -9165,7 +9379,7 @@ return result;
|
|
|
9165
9379
|
]
|
|
9166
9380
|
});
|
|
9167
9381
|
}
|
|
9168
|
-
function StatCard({ label, value, mono }) {
|
|
9382
|
+
function StatCard$1({ label, value, mono }) {
|
|
9169
9383
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
9170
9384
|
className: (0, _rebasepro_ui.cls)("px-3 py-2 rounded-lg border bg-white dark:bg-surface-900", _rebasepro_ui.defaultBorderMixin),
|
|
9171
9385
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
|
|
@@ -10154,16 +10368,16 @@ return result;
|
|
|
10154
10368
|
var LogsExplorer_exports = /* @__PURE__ */ __exportAll({ LogsExplorer: () => LogsExplorer$1 });
|
|
10155
10369
|
function LogsExplorer$1() {
|
|
10156
10370
|
const [logs, setLogs] = (0, react.useState)([]);
|
|
10157
|
-
const [level, setLevel] = (0, react.useState)("");
|
|
10158
|
-
const [source, setSource] = (0, react.useState)("");
|
|
10371
|
+
const [level, setLevel] = (0, react.useState)("all");
|
|
10372
|
+
const [source, setSource] = (0, react.useState)("all");
|
|
10159
10373
|
const [search, setSearch] = (0, react.useState)("");
|
|
10160
10374
|
const [autoScroll, setAutoScroll] = (0, react.useState)(true);
|
|
10161
10375
|
const containerRef = (0, react.useRef)(null);
|
|
10162
10376
|
const fetchLogs = (0, react.useCallback)(async () => {
|
|
10163
10377
|
try {
|
|
10164
10378
|
const params = new URLSearchParams();
|
|
10165
|
-
if (level) params.set("level", level);
|
|
10166
|
-
if (source) params.set("source", source);
|
|
10379
|
+
if (level && level !== "all") params.set("level", level);
|
|
10380
|
+
if (source && source !== "all") params.set("source", source);
|
|
10167
10381
|
if (search) params.set("search", search);
|
|
10168
10382
|
params.set("limit", "200");
|
|
10169
10383
|
const resp = await fetch(`/api/logs?${params}`);
|
|
@@ -10200,22 +10414,9 @@ return result;
|
|
|
10200
10414
|
if (autoScroll && containerRef.current) containerRef.current.scrollTop = containerRef.current.scrollHeight;
|
|
10201
10415
|
}, [logs, autoScroll]);
|
|
10202
10416
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10203
|
-
|
|
10204
|
-
display: "flex",
|
|
10205
|
-
flexDirection: "column",
|
|
10206
|
-
height: "calc(100vh - 64px)",
|
|
10207
|
-
background: "#1e1e2e",
|
|
10208
|
-
color: "#cdd6f4"
|
|
10209
|
-
},
|
|
10417
|
+
className: "flex flex-col h-[calc(100vh-64px)] w-full bg-surface-50 dark:bg-surface-800",
|
|
10210
10418
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10211
|
-
|
|
10212
|
-
display: "flex",
|
|
10213
|
-
gap: 8,
|
|
10214
|
-
padding: "8px 16px",
|
|
10215
|
-
borderBottom: "1px solid #313244",
|
|
10216
|
-
alignItems: "center",
|
|
10217
|
-
flexWrap: "wrap"
|
|
10218
|
-
},
|
|
10419
|
+
className: (0, _rebasepro_ui.cls)("flex gap-2 p-3 border-b items-center flex-wrap shrink-0", _rebasepro_ui.defaultBorderMixin),
|
|
10219
10420
|
children: [
|
|
10220
10421
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.Select, {
|
|
10221
10422
|
value: level,
|
|
@@ -10224,7 +10425,7 @@ return result;
|
|
|
10224
10425
|
placeholder: "All Levels",
|
|
10225
10426
|
children: [
|
|
10226
10427
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.SelectItem, {
|
|
10227
|
-
value: "",
|
|
10428
|
+
value: "all",
|
|
10228
10429
|
children: "All Levels"
|
|
10229
10430
|
}),
|
|
10230
10431
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.SelectItem, {
|
|
@@ -10252,7 +10453,7 @@ return result;
|
|
|
10252
10453
|
placeholder: "All Sources",
|
|
10253
10454
|
children: [
|
|
10254
10455
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.SelectItem, {
|
|
10255
|
-
value: "",
|
|
10456
|
+
value: "all",
|
|
10256
10457
|
children: "All Sources"
|
|
10257
10458
|
}),
|
|
10258
10459
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.SelectItem, {
|
|
@@ -10285,7 +10486,7 @@ return result;
|
|
|
10285
10486
|
className: "flex-1 min-w-[200px]"
|
|
10286
10487
|
}),
|
|
10287
10488
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10288
|
-
className: "flex items-center gap-1.5 cursor-pointer",
|
|
10489
|
+
className: "flex items-center gap-1.5 cursor-pointer ml-2",
|
|
10289
10490
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Checkbox, {
|
|
10290
10491
|
id: "auto-scroll",
|
|
10291
10492
|
checked: autoScroll,
|
|
@@ -10294,79 +10495,58 @@ return result;
|
|
|
10294
10495
|
padding: false
|
|
10295
10496
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Label, {
|
|
10296
10497
|
htmlFor: "auto-scroll",
|
|
10297
|
-
className: "text-xs select-none cursor-pointer",
|
|
10498
|
+
className: "text-xs select-none cursor-pointer text-surface-600 dark:text-surface-400",
|
|
10298
10499
|
children: "Auto-scroll"
|
|
10299
10500
|
})]
|
|
10300
10501
|
}),
|
|
10301
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
10302
|
-
|
|
10303
|
-
|
|
10304
|
-
|
|
10305
|
-
|
|
10306
|
-
|
|
10502
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
10503
|
+
className: "ml-auto pl-4",
|
|
10504
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.Typography, {
|
|
10505
|
+
variant: "caption",
|
|
10506
|
+
color: "secondary",
|
|
10507
|
+
children: [logs.length, " entries"]
|
|
10508
|
+
})
|
|
10307
10509
|
})
|
|
10308
10510
|
]
|
|
10309
10511
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10310
10512
|
ref: containerRef,
|
|
10311
|
-
|
|
10312
|
-
flex: 1,
|
|
10313
|
-
overflow: "auto",
|
|
10314
|
-
fontFamily: "monospace",
|
|
10315
|
-
fontSize: 12,
|
|
10316
|
-
padding: "8px 0"
|
|
10317
|
-
},
|
|
10513
|
+
className: "flex-1 overflow-auto py-2",
|
|
10318
10514
|
children: [logs.map((log) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10319
|
-
|
|
10320
|
-
padding: "2px 16px",
|
|
10321
|
-
display: "flex",
|
|
10322
|
-
gap: 8,
|
|
10323
|
-
borderBottom: "1px solid #181825"
|
|
10324
|
-
},
|
|
10515
|
+
className: (0, _rebasepro_ui.cls)("flex gap-4 px-4 py-[6px] border-b hover:bg-surface-100 dark:hover:bg-surface-900 transition-colors", _rebasepro_ui.defaultBorderMixin),
|
|
10325
10516
|
children: [
|
|
10326
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
10327
|
-
|
|
10328
|
-
|
|
10329
|
-
|
|
10330
|
-
},
|
|
10517
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
|
|
10518
|
+
variant: "body2",
|
|
10519
|
+
color: "secondary",
|
|
10520
|
+
className: "w-[72px] shrink-0 font-mono",
|
|
10331
10521
|
children: new Date(log.timestamp).toLocaleTimeString()
|
|
10332
10522
|
}),
|
|
10333
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
10334
|
-
|
|
10335
|
-
|
|
10336
|
-
width: 40,
|
|
10337
|
-
flexShrink: 0,
|
|
10338
|
-
textTransform: "uppercase",
|
|
10339
|
-
fontWeight: 600
|
|
10340
|
-
},
|
|
10523
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
|
|
10524
|
+
variant: "body2",
|
|
10525
|
+
className: (0, _rebasepro_ui.cls)("w-[48px] shrink-0 uppercase font-semibold font-mono", LEVEL_COLORS[log.level] || "text-surface-500"),
|
|
10341
10526
|
children: log.level
|
|
10342
10527
|
}),
|
|
10343
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(
|
|
10344
|
-
|
|
10345
|
-
|
|
10346
|
-
width: 64,
|
|
10347
|
-
flexShrink: 0
|
|
10348
|
-
},
|
|
10528
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.Typography, {
|
|
10529
|
+
variant: "body2",
|
|
10530
|
+
className: (0, _rebasepro_ui.cls)("w-[80px] shrink-0 font-mono", SOURCE_COLORS[log.source] || "text-surface-500"),
|
|
10349
10531
|
children: [
|
|
10350
10532
|
"[",
|
|
10351
10533
|
log.source,
|
|
10352
10534
|
"]"
|
|
10353
10535
|
]
|
|
10354
10536
|
}),
|
|
10355
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
10356
|
-
|
|
10357
|
-
|
|
10358
|
-
flex: 1
|
|
10359
|
-
},
|
|
10537
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
|
|
10538
|
+
variant: "body2",
|
|
10539
|
+
className: "flex-1 font-mono break-all whitespace-pre-wrap text-surface-900 dark:text-surface-100",
|
|
10360
10540
|
children: log.message
|
|
10361
10541
|
})
|
|
10362
10542
|
]
|
|
10363
10543
|
}, log.id)), logs.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
10364
|
-
|
|
10365
|
-
|
|
10366
|
-
|
|
10367
|
-
color: "
|
|
10368
|
-
|
|
10369
|
-
|
|
10544
|
+
className: "p-8 text-center",
|
|
10545
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
|
|
10546
|
+
variant: "body2",
|
|
10547
|
+
color: "secondary",
|
|
10548
|
+
children: "No log entries yet. Logs will appear here as requests come in."
|
|
10549
|
+
})
|
|
10370
10550
|
})]
|
|
10371
10551
|
})]
|
|
10372
10552
|
});
|
|
@@ -10374,20 +10554,699 @@ return result;
|
|
|
10374
10554
|
var LEVEL_COLORS, SOURCE_COLORS;
|
|
10375
10555
|
var init_LogsExplorer = __esmMin((() => {
|
|
10376
10556
|
LEVEL_COLORS = {
|
|
10377
|
-
debug: "
|
|
10378
|
-
info: "
|
|
10379
|
-
warn: "
|
|
10380
|
-
error: "
|
|
10557
|
+
debug: "text-surface-500",
|
|
10558
|
+
info: "text-blue-600 dark:text-blue-500",
|
|
10559
|
+
warn: "text-amber-600 dark:text-amber-500",
|
|
10560
|
+
error: "text-red-600 dark:text-red-500"
|
|
10381
10561
|
};
|
|
10382
10562
|
SOURCE_COLORS = {
|
|
10383
|
-
api: "
|
|
10384
|
-
auth: "
|
|
10385
|
-
storage: "
|
|
10386
|
-
realtime: "
|
|
10387
|
-
system: "
|
|
10563
|
+
api: "text-sky-600 dark:text-sky-400",
|
|
10564
|
+
auth: "text-purple-600 dark:text-purple-400",
|
|
10565
|
+
storage: "text-green-600 dark:text-green-500",
|
|
10566
|
+
realtime: "text-orange-600 dark:text-orange-400",
|
|
10567
|
+
system: "text-surface-600 dark:text-surface-400"
|
|
10388
10568
|
};
|
|
10389
10569
|
}));
|
|
10390
10570
|
//#endregion
|
|
10571
|
+
//#region src/components/ApiKeys/ApiKeysView.tsx
|
|
10572
|
+
var ApiKeysView_exports = /* @__PURE__ */ __exportAll({ ApiKeysView: () => ApiKeysView$1 });
|
|
10573
|
+
function formatRelative(iso) {
|
|
10574
|
+
if (!iso) return "—";
|
|
10575
|
+
const d = new Date(iso);
|
|
10576
|
+
const now = Date.now();
|
|
10577
|
+
const diff = d.getTime() - now;
|
|
10578
|
+
const abs = Math.abs(diff);
|
|
10579
|
+
if (abs < 6e4) return diff > 0 ? "in <1m" : "<1m ago";
|
|
10580
|
+
if (abs < 36e5) {
|
|
10581
|
+
const m = Math.round(abs / 6e4);
|
|
10582
|
+
return diff > 0 ? `in ${m}m` : `${m}m ago`;
|
|
10583
|
+
}
|
|
10584
|
+
if (abs < 864e5) {
|
|
10585
|
+
const h = Math.round(abs / 36e5);
|
|
10586
|
+
return diff > 0 ? `in ${h}h` : `${h}h ago`;
|
|
10587
|
+
}
|
|
10588
|
+
return d.toLocaleDateString();
|
|
10589
|
+
}
|
|
10590
|
+
function permissionSummary(perms) {
|
|
10591
|
+
if (perms.length === 0) return "No permissions";
|
|
10592
|
+
const wildcard = perms.find((p) => p.collection === "*");
|
|
10593
|
+
if (wildcard) return `All collections (${wildcard.operations.join(", ")})`;
|
|
10594
|
+
if (perms.length === 1) return `${perms[0].collection} (${perms[0].operations.join(", ")})`;
|
|
10595
|
+
return `${perms.length} collections`;
|
|
10596
|
+
}
|
|
10597
|
+
function isExpired(key) {
|
|
10598
|
+
return !!(key.expires_at && new Date(key.expires_at) < /* @__PURE__ */ new Date());
|
|
10599
|
+
}
|
|
10600
|
+
function keyStatus(key) {
|
|
10601
|
+
if (key.revoked_at) return {
|
|
10602
|
+
label: "Revoked",
|
|
10603
|
+
color: "text-red-500"
|
|
10604
|
+
};
|
|
10605
|
+
if (isExpired(key)) return {
|
|
10606
|
+
label: "Expired",
|
|
10607
|
+
color: "text-amber-500"
|
|
10608
|
+
};
|
|
10609
|
+
return {
|
|
10610
|
+
label: "Active",
|
|
10611
|
+
color: "text-emerald-500"
|
|
10612
|
+
};
|
|
10613
|
+
}
|
|
10614
|
+
function ApiKeysView$1() {
|
|
10615
|
+
const client = (0, _rebasepro_core.useRebaseClient)();
|
|
10616
|
+
const snackbar = (0, _rebasepro_core.useSnackbarController)();
|
|
10617
|
+
const [keys, setKeys] = (0, react.useState)([]);
|
|
10618
|
+
const [loading, setLoading] = (0, react.useState)(true);
|
|
10619
|
+
const [selectedId, setSelectedId] = (0, react.useState)(null);
|
|
10620
|
+
const [showCreate, setShowCreate] = (0, react.useState)(false);
|
|
10621
|
+
const [showSecret, setShowSecret] = (0, react.useState)(null);
|
|
10622
|
+
const [revoking, setRevoking] = (0, react.useState)(null);
|
|
10623
|
+
const clientRef = (0, react.useRef)(client);
|
|
10624
|
+
clientRef.current = client;
|
|
10625
|
+
const snackbarRef = (0, react.useRef)(snackbar);
|
|
10626
|
+
snackbarRef.current = snackbar;
|
|
10627
|
+
const loadKeys = (0, react.useCallback)(async () => {
|
|
10628
|
+
const c = clientRef.current;
|
|
10629
|
+
if (!c?.apiKeys) {
|
|
10630
|
+
setLoading(false);
|
|
10631
|
+
return;
|
|
10632
|
+
}
|
|
10633
|
+
try {
|
|
10634
|
+
setKeys((await c.apiKeys.listKeys()).keys);
|
|
10635
|
+
} catch (e) {
|
|
10636
|
+
snackbarRef.current.open({
|
|
10637
|
+
type: "error",
|
|
10638
|
+
message: e instanceof Error ? e.message : String(e)
|
|
10639
|
+
});
|
|
10640
|
+
} finally {
|
|
10641
|
+
setLoading(false);
|
|
10642
|
+
}
|
|
10643
|
+
}, []);
|
|
10644
|
+
(0, react.useEffect)(() => {
|
|
10645
|
+
loadKeys();
|
|
10646
|
+
}, [loadKeys]);
|
|
10647
|
+
const handleRevoke = async (id) => {
|
|
10648
|
+
const c = clientRef.current;
|
|
10649
|
+
if (!c?.apiKeys) return;
|
|
10650
|
+
setRevoking(id);
|
|
10651
|
+
try {
|
|
10652
|
+
await c.apiKeys.revokeKey(id);
|
|
10653
|
+
snackbarRef.current.open({
|
|
10654
|
+
type: "success",
|
|
10655
|
+
message: "API key revoked"
|
|
10656
|
+
});
|
|
10657
|
+
await loadKeys();
|
|
10658
|
+
if (selectedId === id) setSelectedId(null);
|
|
10659
|
+
} catch (e) {
|
|
10660
|
+
snackbarRef.current.open({
|
|
10661
|
+
type: "error",
|
|
10662
|
+
message: e instanceof Error ? e.message : String(e)
|
|
10663
|
+
});
|
|
10664
|
+
} finally {
|
|
10665
|
+
setRevoking(null);
|
|
10666
|
+
}
|
|
10667
|
+
};
|
|
10668
|
+
const handleCreated = (keyWithSecret) => {
|
|
10669
|
+
setShowCreate(false);
|
|
10670
|
+
setShowSecret(keyWithSecret);
|
|
10671
|
+
loadKeys();
|
|
10672
|
+
};
|
|
10673
|
+
const selectedKey = keys.find((k) => k.id === selectedId);
|
|
10674
|
+
const activeKeys = keys.filter((k) => !k.revoked_at && !isExpired(k));
|
|
10675
|
+
const inactiveKeys = keys.filter((k) => k.revoked_at || isExpired(k));
|
|
10676
|
+
if (loading) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
10677
|
+
className: "flex items-center justify-center h-full",
|
|
10678
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.CircularProgress, {})
|
|
10679
|
+
});
|
|
10680
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
10681
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10682
|
+
className: "flex h-full w-full overflow-hidden bg-white dark:bg-surface-950",
|
|
10683
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10684
|
+
className: (0, _rebasepro_ui.cls)("flex flex-col w-[340px] min-w-[280px] border-r h-full", _rebasepro_ui.defaultBorderMixin),
|
|
10685
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10686
|
+
className: (0, _rebasepro_ui.cls)("flex items-center justify-between px-4 py-2.5 border-b bg-surface-50 dark:bg-surface-900 min-h-[48px]", _rebasepro_ui.defaultBorderMixin),
|
|
10687
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10688
|
+
className: "flex items-center gap-2",
|
|
10689
|
+
children: [
|
|
10690
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.KeyRoundIcon, {
|
|
10691
|
+
size: _rebasepro_ui.iconSize.smallest,
|
|
10692
|
+
className: "text-primary"
|
|
10693
|
+
}),
|
|
10694
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
|
|
10695
|
+
variant: "subtitle2",
|
|
10696
|
+
className: "font-semibold",
|
|
10697
|
+
children: "API Keys"
|
|
10698
|
+
}),
|
|
10699
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Chip, {
|
|
10700
|
+
size: "smallest",
|
|
10701
|
+
className: "bg-surface-200 dark:bg-surface-700 text-surface-600 dark:text-surface-300",
|
|
10702
|
+
children: activeKeys.length
|
|
10703
|
+
})
|
|
10704
|
+
]
|
|
10705
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10706
|
+
className: "flex items-center gap-1",
|
|
10707
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.IconButton, {
|
|
10708
|
+
size: "small",
|
|
10709
|
+
onClick: loadKeys,
|
|
10710
|
+
title: "Refresh",
|
|
10711
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.RefreshCwIcon, { size: _rebasepro_ui.iconSize.smallest })
|
|
10712
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Button, {
|
|
10713
|
+
size: "small",
|
|
10714
|
+
color: "primary",
|
|
10715
|
+
onClick: () => setShowCreate(true),
|
|
10716
|
+
startIcon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.PlusIcon, { size: _rebasepro_ui.iconSize.smallest }),
|
|
10717
|
+
children: "New"
|
|
10718
|
+
})]
|
|
10719
|
+
})]
|
|
10720
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10721
|
+
className: "flex-1 overflow-y-auto p-2 space-y-1",
|
|
10722
|
+
children: [
|
|
10723
|
+
activeKeys.length === 0 && inactiveKeys.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10724
|
+
className: "flex flex-col items-center justify-center h-full gap-3 text-center p-6",
|
|
10725
|
+
children: [
|
|
10726
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.KeyRoundIcon, {
|
|
10727
|
+
size: _rebasepro_ui.iconSize.medium,
|
|
10728
|
+
className: "text-surface-300 dark:text-surface-600"
|
|
10729
|
+
}),
|
|
10730
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
|
|
10731
|
+
variant: "body2",
|
|
10732
|
+
color: "secondary",
|
|
10733
|
+
children: "No API keys yet"
|
|
10734
|
+
}),
|
|
10735
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
|
|
10736
|
+
variant: "caption",
|
|
10737
|
+
color: "disabled",
|
|
10738
|
+
children: "Create a key to enable machine-to-machine authentication"
|
|
10739
|
+
})
|
|
10740
|
+
]
|
|
10741
|
+
}),
|
|
10742
|
+
activeKeys.map((key) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(KeyListItem, {
|
|
10743
|
+
apiKey: key,
|
|
10744
|
+
selected: selectedId === key.id,
|
|
10745
|
+
onClick: () => setSelectedId(key.id)
|
|
10746
|
+
}, key.id)),
|
|
10747
|
+
inactiveKeys.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
10748
|
+
className: "px-2 pt-3 pb-1",
|
|
10749
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
|
|
10750
|
+
variant: "caption",
|
|
10751
|
+
color: "disabled",
|
|
10752
|
+
className: "text-[10px] uppercase tracking-wider font-medium",
|
|
10753
|
+
children: "Revoked / Expired"
|
|
10754
|
+
})
|
|
10755
|
+
}), inactiveKeys.map((key) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(KeyListItem, {
|
|
10756
|
+
apiKey: key,
|
|
10757
|
+
selected: selectedId === key.id,
|
|
10758
|
+
onClick: () => setSelectedId(key.id)
|
|
10759
|
+
}, key.id))] })
|
|
10760
|
+
]
|
|
10761
|
+
})]
|
|
10762
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
10763
|
+
className: "flex-1 flex flex-col min-w-0 h-full overflow-hidden",
|
|
10764
|
+
children: !selectedKey ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
10765
|
+
className: "flex items-center justify-center h-full",
|
|
10766
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
|
|
10767
|
+
variant: "body2",
|
|
10768
|
+
color: "disabled",
|
|
10769
|
+
children: "Select an API key to view details"
|
|
10770
|
+
})
|
|
10771
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
10772
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10773
|
+
className: (0, _rebasepro_ui.cls)("flex items-center justify-between px-5 py-3 border-b bg-white dark:bg-surface-950 min-h-[56px]", _rebasepro_ui.defaultBorderMixin),
|
|
10774
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10775
|
+
className: "flex items-center gap-3 min-w-0",
|
|
10776
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.KeyRoundIcon, {
|
|
10777
|
+
size: _rebasepro_ui.iconSize.small,
|
|
10778
|
+
className: "text-primary shrink-0"
|
|
10779
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10780
|
+
className: "min-w-0",
|
|
10781
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
|
|
10782
|
+
variant: "subtitle1",
|
|
10783
|
+
className: "font-semibold truncate",
|
|
10784
|
+
children: selectedKey.name
|
|
10785
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.Typography, {
|
|
10786
|
+
variant: "caption",
|
|
10787
|
+
color: "secondary",
|
|
10788
|
+
className: "font-mono text-[11px]",
|
|
10789
|
+
children: [selectedKey.key_prefix, "•••"]
|
|
10790
|
+
})]
|
|
10791
|
+
})]
|
|
10792
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
10793
|
+
className: "flex items-center gap-2 shrink-0",
|
|
10794
|
+
children: !selectedKey.revoked_at && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Button, {
|
|
10795
|
+
size: "small",
|
|
10796
|
+
color: "error",
|
|
10797
|
+
variant: "outlined",
|
|
10798
|
+
onClick: () => handleRevoke(selectedKey.id),
|
|
10799
|
+
disabled: revoking === selectedKey.id,
|
|
10800
|
+
startIcon: revoking === selectedKey.id ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.CircularProgress, { size: "smallest" }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Trash2Icon, { size: _rebasepro_ui.iconSize.smallest }),
|
|
10801
|
+
children: "Revoke"
|
|
10802
|
+
})
|
|
10803
|
+
})]
|
|
10804
|
+
}),
|
|
10805
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10806
|
+
className: "px-5 py-4 bg-surface-50 dark:bg-surface-900/50",
|
|
10807
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10808
|
+
className: "grid grid-cols-2 md:grid-cols-4 gap-3",
|
|
10809
|
+
children: [
|
|
10810
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard, {
|
|
10811
|
+
label: "Status",
|
|
10812
|
+
value: keyStatus(selectedKey).label,
|
|
10813
|
+
className: keyStatus(selectedKey).color
|
|
10814
|
+
}),
|
|
10815
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard, {
|
|
10816
|
+
label: "Created",
|
|
10817
|
+
value: formatRelative(selectedKey.created_at)
|
|
10818
|
+
}),
|
|
10819
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard, {
|
|
10820
|
+
label: "Last Used",
|
|
10821
|
+
value: formatRelative(selectedKey.last_used_at)
|
|
10822
|
+
}),
|
|
10823
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard, {
|
|
10824
|
+
label: "Expires",
|
|
10825
|
+
value: selectedKey.expires_at ? formatRelative(selectedKey.expires_at) : "Never"
|
|
10826
|
+
})
|
|
10827
|
+
]
|
|
10828
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10829
|
+
className: "grid grid-cols-2 gap-3 mt-3",
|
|
10830
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard, {
|
|
10831
|
+
label: "Rate Limit",
|
|
10832
|
+
value: selectedKey.rate_limit ? `${selectedKey.rate_limit}/15min` : "Default (1000/15min)"
|
|
10833
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard, {
|
|
10834
|
+
label: "Created By",
|
|
10835
|
+
value: selectedKey.created_by,
|
|
10836
|
+
mono: true
|
|
10837
|
+
})]
|
|
10838
|
+
})]
|
|
10839
|
+
}),
|
|
10840
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10841
|
+
className: (0, _rebasepro_ui.cls)("flex items-center gap-2 px-5 py-2 border-y bg-white dark:bg-surface-950", _rebasepro_ui.defaultBorderMixin),
|
|
10842
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
|
|
10843
|
+
variant: "subtitle2",
|
|
10844
|
+
className: "font-semibold text-[13px]",
|
|
10845
|
+
children: "Permissions"
|
|
10846
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Chip, {
|
|
10847
|
+
size: "smallest",
|
|
10848
|
+
className: "bg-surface-200 dark:bg-surface-700 text-surface-600 dark:text-surface-300",
|
|
10849
|
+
children: selectedKey.permissions.length
|
|
10850
|
+
})]
|
|
10851
|
+
}),
|
|
10852
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
10853
|
+
className: "flex-1 overflow-y-auto px-5 py-3",
|
|
10854
|
+
children: selectedKey.permissions.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
|
|
10855
|
+
variant: "body2",
|
|
10856
|
+
color: "disabled",
|
|
10857
|
+
children: "No permissions configured"
|
|
10858
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
10859
|
+
className: "space-y-2",
|
|
10860
|
+
children: selectedKey.permissions.map((perm, idx) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10861
|
+
className: (0, _rebasepro_ui.cls)("flex items-center gap-3 px-3 py-2 rounded-lg border", _rebasepro_ui.defaultBorderMixin),
|
|
10862
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
|
|
10863
|
+
variant: "body2",
|
|
10864
|
+
className: "font-mono text-[13px] font-medium flex-1",
|
|
10865
|
+
children: perm.collection === "*" ? "* (all collections)" : perm.collection
|
|
10866
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
10867
|
+
className: "flex items-center gap-1",
|
|
10868
|
+
children: perm.operations.map((op) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Chip, {
|
|
10869
|
+
size: "smallest",
|
|
10870
|
+
className: (0, _rebasepro_ui.cls)(op === "read" && "bg-emerald-100 dark:bg-emerald-900/30 text-emerald-700 dark:text-emerald-300", op === "write" && "bg-blue-100 dark:bg-blue-900/30 text-blue-700 dark:text-blue-300", op === "delete" && "bg-red-100 dark:bg-red-900/30 text-red-700 dark:text-red-300"),
|
|
10871
|
+
children: op
|
|
10872
|
+
}, op))
|
|
10873
|
+
})]
|
|
10874
|
+
}, idx))
|
|
10875
|
+
})
|
|
10876
|
+
})
|
|
10877
|
+
] })
|
|
10878
|
+
})]
|
|
10879
|
+
}),
|
|
10880
|
+
showCreate && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CreateApiKeyDialog, {
|
|
10881
|
+
onClose: () => setShowCreate(false),
|
|
10882
|
+
onCreated: handleCreated
|
|
10883
|
+
}),
|
|
10884
|
+
showSecret && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SecretDisplayDialog, {
|
|
10885
|
+
keyWithSecret: showSecret,
|
|
10886
|
+
onClose: () => setShowSecret(null)
|
|
10887
|
+
})
|
|
10888
|
+
] });
|
|
10889
|
+
}
|
|
10890
|
+
function KeyListItem({ apiKey, selected, onClick }) {
|
|
10891
|
+
const status = keyStatus(apiKey);
|
|
10892
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10893
|
+
onClick,
|
|
10894
|
+
className: (0, _rebasepro_ui.cls)("flex items-center gap-3 px-3 py-2.5 rounded-lg cursor-pointer transition-all", selected ? "bg-primary/10 dark:bg-primary/15 ring-1 ring-primary/30" : "hover:bg-surface-100 dark:hover:bg-surface-950"),
|
|
10895
|
+
children: [
|
|
10896
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: (0, _rebasepro_ui.cls)("w-2 h-2 rounded-full shrink-0", status.label === "Active" ? "bg-emerald-400" : status.label === "Expired" ? "bg-amber-400" : "bg-red-400") }),
|
|
10897
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10898
|
+
className: "flex-1 min-w-0",
|
|
10899
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
|
|
10900
|
+
variant: "body2",
|
|
10901
|
+
className: "truncate font-medium text-[13px]",
|
|
10902
|
+
children: apiKey.name
|
|
10903
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.Typography, {
|
|
10904
|
+
variant: "caption",
|
|
10905
|
+
color: "secondary",
|
|
10906
|
+
className: "truncate text-[11px] font-mono",
|
|
10907
|
+
children: [apiKey.key_prefix, "•••"]
|
|
10908
|
+
})]
|
|
10909
|
+
}),
|
|
10910
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
10911
|
+
className: "shrink-0",
|
|
10912
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
|
|
10913
|
+
variant: "caption",
|
|
10914
|
+
color: "disabled",
|
|
10915
|
+
className: "text-[10px]",
|
|
10916
|
+
children: permissionSummary(apiKey.permissions)
|
|
10917
|
+
})
|
|
10918
|
+
})
|
|
10919
|
+
]
|
|
10920
|
+
});
|
|
10921
|
+
}
|
|
10922
|
+
function StatCard({ label, value, mono, className }) {
|
|
10923
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10924
|
+
className: (0, _rebasepro_ui.cls)("px-3 py-2 rounded-lg border bg-white dark:bg-surface-900", _rebasepro_ui.defaultBorderMixin),
|
|
10925
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
|
|
10926
|
+
variant: "caption",
|
|
10927
|
+
color: "secondary",
|
|
10928
|
+
className: "text-[10px] uppercase tracking-wider font-medium",
|
|
10929
|
+
children: label
|
|
10930
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
|
|
10931
|
+
variant: "body2",
|
|
10932
|
+
className: (0, _rebasepro_ui.cls)("mt-0.5 font-semibold text-[13px]", mono && "font-mono", className),
|
|
10933
|
+
children: value
|
|
10934
|
+
})]
|
|
10935
|
+
});
|
|
10936
|
+
}
|
|
10937
|
+
function CreateApiKeyDialog({ onClose, onCreated }) {
|
|
10938
|
+
const client = (0, _rebasepro_core.useRebaseClient)();
|
|
10939
|
+
const snackbar = (0, _rebasepro_core.useSnackbarController)();
|
|
10940
|
+
const [name, setName] = (0, react.useState)("");
|
|
10941
|
+
const [permissions, setPermissions] = (0, react.useState)([{
|
|
10942
|
+
collection: "*",
|
|
10943
|
+
read: true,
|
|
10944
|
+
write: false,
|
|
10945
|
+
delete: false
|
|
10946
|
+
}]);
|
|
10947
|
+
const [rateLimit, setRateLimit] = (0, react.useState)("");
|
|
10948
|
+
const [expiresIn, setExpiresIn] = (0, react.useState)("never");
|
|
10949
|
+
const [creating, setCreating] = (0, react.useState)(false);
|
|
10950
|
+
const addRow = () => setPermissions([...permissions, {
|
|
10951
|
+
collection: "",
|
|
10952
|
+
read: true,
|
|
10953
|
+
write: false,
|
|
10954
|
+
delete: false
|
|
10955
|
+
}]);
|
|
10956
|
+
const removeRow = (idx) => setPermissions(permissions.filter((_, i) => i !== idx));
|
|
10957
|
+
const updateRow = (idx, field, value) => {
|
|
10958
|
+
setPermissions(permissions.map((row, i) => i === idx ? {
|
|
10959
|
+
...row,
|
|
10960
|
+
[field]: value
|
|
10961
|
+
} : row));
|
|
10962
|
+
};
|
|
10963
|
+
const handleCreate = async () => {
|
|
10964
|
+
if (!client?.apiKeys || !name.trim()) return;
|
|
10965
|
+
const apiPerms = permissions.filter((r) => r.collection.trim()).map((r) => ({
|
|
10966
|
+
collection: r.collection.trim(),
|
|
10967
|
+
operations: [
|
|
10968
|
+
...r.read ? ["read"] : [],
|
|
10969
|
+
...r.write ? ["write"] : [],
|
|
10970
|
+
...r.delete ? ["delete"] : []
|
|
10971
|
+
]
|
|
10972
|
+
})).filter((p) => p.operations.length > 0);
|
|
10973
|
+
if (apiPerms.length === 0) {
|
|
10974
|
+
snackbar.open({
|
|
10975
|
+
type: "error",
|
|
10976
|
+
message: "At least one permission is required"
|
|
10977
|
+
});
|
|
10978
|
+
return;
|
|
10979
|
+
}
|
|
10980
|
+
let expires_at = null;
|
|
10981
|
+
if (expiresIn === "7d") expires_at = new Date(Date.now() + 7 * 864e5).toISOString();
|
|
10982
|
+
else if (expiresIn === "30d") expires_at = new Date(Date.now() + 30 * 864e5).toISOString();
|
|
10983
|
+
else if (expiresIn === "90d") expires_at = new Date(Date.now() + 90 * 864e5).toISOString();
|
|
10984
|
+
else if (expiresIn === "1y") expires_at = new Date(Date.now() + 365 * 864e5).toISOString();
|
|
10985
|
+
setCreating(true);
|
|
10986
|
+
try {
|
|
10987
|
+
onCreated((await client.apiKeys.createKey({
|
|
10988
|
+
name: name.trim(),
|
|
10989
|
+
permissions: apiPerms,
|
|
10990
|
+
rate_limit: rateLimit ? parseInt(rateLimit, 10) : null,
|
|
10991
|
+
expires_at
|
|
10992
|
+
})).key);
|
|
10993
|
+
} catch (e) {
|
|
10994
|
+
snackbar.open({
|
|
10995
|
+
type: "error",
|
|
10996
|
+
message: e instanceof Error ? e.message : String(e)
|
|
10997
|
+
});
|
|
10998
|
+
} finally {
|
|
10999
|
+
setCreating(false);
|
|
11000
|
+
}
|
|
11001
|
+
};
|
|
11002
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.Dialog, {
|
|
11003
|
+
open: true,
|
|
11004
|
+
onOpenChange: (open) => {
|
|
11005
|
+
if (!open) onClose();
|
|
11006
|
+
},
|
|
11007
|
+
maxWidth: "lg",
|
|
11008
|
+
children: [
|
|
11009
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.DialogTitle, { children: "Create API Key" }),
|
|
11010
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.DialogContent, {
|
|
11011
|
+
className: "space-y-4",
|
|
11012
|
+
children: [
|
|
11013
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.TextField, {
|
|
11014
|
+
label: "Name",
|
|
11015
|
+
value: name,
|
|
11016
|
+
onChange: (e) => setName(e.target.value),
|
|
11017
|
+
placeholder: "e.g. Analytics Pipeline",
|
|
11018
|
+
size: "small",
|
|
11019
|
+
autoFocus: true
|
|
11020
|
+
}),
|
|
11021
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [
|
|
11022
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
|
|
11023
|
+
variant: "caption",
|
|
11024
|
+
color: "secondary",
|
|
11025
|
+
className: "text-[11px] uppercase tracking-wider font-medium mb-2 block",
|
|
11026
|
+
children: "Permissions"
|
|
11027
|
+
}),
|
|
11028
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
11029
|
+
className: "space-y-2",
|
|
11030
|
+
children: permissions.map((row, idx) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
11031
|
+
className: (0, _rebasepro_ui.cls)("flex items-center gap-2 p-2 rounded-lg border", _rebasepro_ui.defaultBorderMixin),
|
|
11032
|
+
children: [
|
|
11033
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.TextField, {
|
|
11034
|
+
size: "small",
|
|
11035
|
+
value: row.collection,
|
|
11036
|
+
onChange: (e) => updateRow(idx, "collection", e.target.value),
|
|
11037
|
+
placeholder: "Collection slug or *",
|
|
11038
|
+
className: "flex-1"
|
|
11039
|
+
}),
|
|
11040
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
11041
|
+
className: "flex items-center gap-1 text-xs cursor-pointer",
|
|
11042
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Checkbox, {
|
|
11043
|
+
size: "small",
|
|
11044
|
+
checked: row.read,
|
|
11045
|
+
onCheckedChange: (v) => updateRow(idx, "read", !!v)
|
|
11046
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
11047
|
+
className: "text-emerald-600 dark:text-emerald-400",
|
|
11048
|
+
children: "read"
|
|
11049
|
+
})]
|
|
11050
|
+
}),
|
|
11051
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
11052
|
+
className: "flex items-center gap-1 text-xs cursor-pointer",
|
|
11053
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Checkbox, {
|
|
11054
|
+
size: "small",
|
|
11055
|
+
checked: row.write,
|
|
11056
|
+
onCheckedChange: (v) => updateRow(idx, "write", !!v)
|
|
11057
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
11058
|
+
className: "text-blue-600 dark:text-blue-400",
|
|
11059
|
+
children: "write"
|
|
11060
|
+
})]
|
|
11061
|
+
}),
|
|
11062
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
11063
|
+
className: "flex items-center gap-1 text-xs cursor-pointer",
|
|
11064
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Checkbox, {
|
|
11065
|
+
size: "small",
|
|
11066
|
+
checked: row.delete,
|
|
11067
|
+
onCheckedChange: (v) => updateRow(idx, "delete", !!v)
|
|
11068
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
11069
|
+
className: "text-red-600 dark:text-red-400",
|
|
11070
|
+
children: "delete"
|
|
11071
|
+
})]
|
|
11072
|
+
}),
|
|
11073
|
+
permissions.length > 1 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.IconButton, {
|
|
11074
|
+
size: "small",
|
|
11075
|
+
onClick: () => removeRow(idx),
|
|
11076
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Trash2Icon, { size: _rebasepro_ui.iconSize.smallest })
|
|
11077
|
+
})
|
|
11078
|
+
]
|
|
11079
|
+
}, idx))
|
|
11080
|
+
}),
|
|
11081
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Button, {
|
|
11082
|
+
size: "small",
|
|
11083
|
+
variant: "text",
|
|
11084
|
+
onClick: addRow,
|
|
11085
|
+
className: "mt-1",
|
|
11086
|
+
startIcon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.PlusIcon, { size: _rebasepro_ui.iconSize.smallest }),
|
|
11087
|
+
children: "Add collection"
|
|
11088
|
+
})
|
|
11089
|
+
] }),
|
|
11090
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
11091
|
+
className: "flex gap-4",
|
|
11092
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
11093
|
+
className: "flex-1",
|
|
11094
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.Select, {
|
|
11095
|
+
label: "Expires",
|
|
11096
|
+
value: expiresIn,
|
|
11097
|
+
onValueChange: setExpiresIn,
|
|
11098
|
+
size: "small",
|
|
11099
|
+
renderValue: (v) => v === "never" ? "Never" : v === "7d" ? "7 days" : v === "30d" ? "30 days" : v === "90d" ? "90 days" : "1 year",
|
|
11100
|
+
children: [
|
|
11101
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.SelectItem, {
|
|
11102
|
+
value: "never",
|
|
11103
|
+
children: "Never"
|
|
11104
|
+
}),
|
|
11105
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.SelectItem, {
|
|
11106
|
+
value: "7d",
|
|
11107
|
+
children: "7 days"
|
|
11108
|
+
}),
|
|
11109
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.SelectItem, {
|
|
11110
|
+
value: "30d",
|
|
11111
|
+
children: "30 days"
|
|
11112
|
+
}),
|
|
11113
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.SelectItem, {
|
|
11114
|
+
value: "90d",
|
|
11115
|
+
children: "90 days"
|
|
11116
|
+
}),
|
|
11117
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.SelectItem, {
|
|
11118
|
+
value: "1y",
|
|
11119
|
+
children: "1 year"
|
|
11120
|
+
})
|
|
11121
|
+
]
|
|
11122
|
+
})
|
|
11123
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
11124
|
+
className: "flex-1",
|
|
11125
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.TextField, {
|
|
11126
|
+
label: "Rate Limit (per 15 min)",
|
|
11127
|
+
value: rateLimit,
|
|
11128
|
+
onChange: (e) => setRateLimit(e.target.value.replace(/\D/g, "")),
|
|
11129
|
+
placeholder: "Default: 1000",
|
|
11130
|
+
size: "small"
|
|
11131
|
+
})
|
|
11132
|
+
})]
|
|
11133
|
+
})
|
|
11134
|
+
]
|
|
11135
|
+
}),
|
|
11136
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.DialogActions, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Button, {
|
|
11137
|
+
variant: "text",
|
|
11138
|
+
onClick: onClose,
|
|
11139
|
+
children: "Cancel"
|
|
11140
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Button, {
|
|
11141
|
+
color: "primary",
|
|
11142
|
+
onClick: handleCreate,
|
|
11143
|
+
disabled: creating || !name.trim(),
|
|
11144
|
+
startIcon: creating ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.CircularProgress, { size: "smallest" }) : void 0,
|
|
11145
|
+
children: "Create Key"
|
|
11146
|
+
})] })
|
|
11147
|
+
]
|
|
11148
|
+
});
|
|
11149
|
+
}
|
|
11150
|
+
function SecretDisplayDialog({ keyWithSecret, onClose }) {
|
|
11151
|
+
const snackbar = (0, _rebasepro_core.useSnackbarController)();
|
|
11152
|
+
const [copied, setCopied] = (0, react.useState)(false);
|
|
11153
|
+
const handleCopy = async () => {
|
|
11154
|
+
try {
|
|
11155
|
+
await navigator.clipboard.writeText(keyWithSecret.key);
|
|
11156
|
+
setCopied(true);
|
|
11157
|
+
snackbar.open({
|
|
11158
|
+
type: "success",
|
|
11159
|
+
message: "API key copied to clipboard"
|
|
11160
|
+
});
|
|
11161
|
+
setTimeout(() => setCopied(false), 2e3);
|
|
11162
|
+
} catch {
|
|
11163
|
+
snackbar.open({
|
|
11164
|
+
type: "error",
|
|
11165
|
+
message: "Failed to copy"
|
|
11166
|
+
});
|
|
11167
|
+
}
|
|
11168
|
+
};
|
|
11169
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.Dialog, {
|
|
11170
|
+
open: true,
|
|
11171
|
+
onOpenChange: (open) => {
|
|
11172
|
+
if (!open) onClose();
|
|
11173
|
+
},
|
|
11174
|
+
maxWidth: "md",
|
|
11175
|
+
children: [
|
|
11176
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.DialogTitle, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
11177
|
+
className: "flex items-center gap-2",
|
|
11178
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.CheckCircleIcon, {
|
|
11179
|
+
size: _rebasepro_ui.iconSize.small,
|
|
11180
|
+
className: "text-emerald-500"
|
|
11181
|
+
}), "API Key Created"]
|
|
11182
|
+
}) }),
|
|
11183
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.DialogContent, { children: [
|
|
11184
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
11185
|
+
className: "p-3 rounded-lg bg-amber-50 dark:bg-amber-900/20 border border-amber-200 dark:border-amber-800/50 mb-4",
|
|
11186
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
11187
|
+
className: "flex items-center gap-2 mb-1",
|
|
11188
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.AlertCircleIcon, {
|
|
11189
|
+
size: _rebasepro_ui.iconSize.smallest,
|
|
11190
|
+
className: "text-amber-600 dark:text-amber-400"
|
|
11191
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
|
|
11192
|
+
variant: "caption",
|
|
11193
|
+
className: "font-semibold text-amber-700 dark:text-amber-400",
|
|
11194
|
+
children: "Copy your key now — it won't be shown again"
|
|
11195
|
+
})]
|
|
11196
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
|
|
11197
|
+
variant: "caption",
|
|
11198
|
+
className: "text-amber-600 dark:text-amber-300",
|
|
11199
|
+
children: "This is the only time the full API key will be displayed. Store it securely."
|
|
11200
|
+
})]
|
|
11201
|
+
}),
|
|
11202
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
11203
|
+
className: (0, _rebasepro_ui.cls)("flex items-center gap-2 p-3 rounded-lg border bg-surface-50 dark:bg-surface-900", _rebasepro_ui.defaultBorderMixin),
|
|
11204
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
|
|
11205
|
+
className: "flex-1 text-[12px] font-mono break-all text-surface-700 dark:text-surface-300 select-all",
|
|
11206
|
+
children: keyWithSecret.key
|
|
11207
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Tooltip, {
|
|
11208
|
+
title: copied ? "Copied!" : "Copy",
|
|
11209
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.IconButton, {
|
|
11210
|
+
size: "small",
|
|
11211
|
+
onClick: handleCopy,
|
|
11212
|
+
children: copied ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.CheckCircleIcon, {
|
|
11213
|
+
size: _rebasepro_ui.iconSize.smallest,
|
|
11214
|
+
className: "text-emerald-500"
|
|
11215
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.CopyIcon, { size: _rebasepro_ui.iconSize.smallest })
|
|
11216
|
+
})
|
|
11217
|
+
})]
|
|
11218
|
+
}),
|
|
11219
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
11220
|
+
className: "mt-4 space-y-1",
|
|
11221
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.Typography, {
|
|
11222
|
+
variant: "caption",
|
|
11223
|
+
color: "secondary",
|
|
11224
|
+
children: [
|
|
11225
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: "Name:" }),
|
|
11226
|
+
" ",
|
|
11227
|
+
keyWithSecret.name
|
|
11228
|
+
]
|
|
11229
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.Typography, {
|
|
11230
|
+
variant: "caption",
|
|
11231
|
+
color: "secondary",
|
|
11232
|
+
children: [
|
|
11233
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: "Permissions:" }),
|
|
11234
|
+
" ",
|
|
11235
|
+
permissionSummary(keyWithSecret.permissions)
|
|
11236
|
+
]
|
|
11237
|
+
})]
|
|
11238
|
+
})
|
|
11239
|
+
] }),
|
|
11240
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.DialogActions, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Button, {
|
|
11241
|
+
color: "primary",
|
|
11242
|
+
onClick: onClose,
|
|
11243
|
+
children: "Done"
|
|
11244
|
+
}) })
|
|
11245
|
+
]
|
|
11246
|
+
});
|
|
11247
|
+
}
|
|
11248
|
+
var init_ApiKeysView = __esmMin((() => {}));
|
|
11249
|
+
//#endregion
|
|
10391
11250
|
//#region src/components/RebaseStudio.tsx
|
|
10392
11251
|
var SQLEditor = (0, react.lazy)(() => Promise.resolve().then(() => (init_SQLEditor(), SQLEditor_exports)).then((m) => ({ default: m.SQLEditor })));
|
|
10393
11252
|
var JSEditor = (0, react.lazy)(() => Promise.resolve().then(() => (init_JSEditor(), JSEditor_exports)).then((m) => ({ default: m.JSEditor })));
|
|
@@ -10398,6 +11257,7 @@ return result;
|
|
|
10398
11257
|
var BranchesView = (0, react.lazy)(() => Promise.resolve().then(() => (init_BranchesView(), BranchesView_exports)).then((m) => ({ default: m.BranchesView })));
|
|
10399
11258
|
var ApiExplorer = (0, react.lazy)(() => Promise.resolve().then(() => (init_ApiExplorer(), ApiExplorer_exports)).then((m) => ({ default: m.ApiExplorer })));
|
|
10400
11259
|
var LogsExplorer = (0, react.lazy)(() => Promise.resolve().then(() => (init_LogsExplorer(), LogsExplorer_exports)).then((m) => ({ default: m.LogsExplorer })));
|
|
11260
|
+
var ApiKeysView = (0, react.lazy)(() => Promise.resolve().then(() => (init_ApiKeysView(), ApiKeysView_exports)).then((m) => ({ default: m.ApiKeysView })));
|
|
10401
11261
|
/**
|
|
10402
11262
|
* Declarative component to configure the Studio in Rebase.
|
|
10403
11263
|
* Renders nothing — purely registers config into the RebaseRegistry.
|
|
@@ -10421,7 +11281,8 @@ return result;
|
|
|
10421
11281
|
"schema-visualizer",
|
|
10422
11282
|
"branches",
|
|
10423
11283
|
"api",
|
|
10424
|
-
"logs"
|
|
11284
|
+
"logs",
|
|
11285
|
+
"api-keys"
|
|
10425
11286
|
];
|
|
10426
11287
|
const suspense = (el) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react.Suspense, {
|
|
10427
11288
|
fallback: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.CircularProgressCenter, {}),
|
|
@@ -10499,6 +11360,14 @@ return result;
|
|
|
10499
11360
|
description: "Real-time system and query logs",
|
|
10500
11361
|
view: suspense(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(LogsExplorer, {}))
|
|
10501
11362
|
});
|
|
11363
|
+
if (activeTools.includes("api-keys")) views.push({
|
|
11364
|
+
slug: "api-keys",
|
|
11365
|
+
name: "API Keys",
|
|
11366
|
+
group: "Access Control",
|
|
11367
|
+
icon: "KeyRound",
|
|
11368
|
+
description: "Create and manage scoped API keys",
|
|
11369
|
+
view: suspense(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ApiKeysView, {}))
|
|
11370
|
+
});
|
|
10502
11371
|
return views;
|
|
10503
11372
|
}, [tools]);
|
|
10504
11373
|
const homePageRef = react.default.useRef(resolvedHomePage);
|