@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
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { t as MonacoEditor } from "./MonacoEditor-COZqrIJ1.js";
|
|
2
2
|
import { ErrorView, useRebaseContext, useSnackbarController, useStudioCollectionRegistry, useTranslation } from "@rebasepro/core";
|
|
3
3
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
4
|
-
import { Alert, AlertTriangleIcon, Button, Chip, CircularProgress, Dialog, DialogActions, DialogContent, DialogTitle, HelpCircleIcon, IconButton, KeyIcon, MultiSelect, MultiSelectItem, Paper, RefreshCwIcon, ResizablePanels, Select, SelectItem, ShieldIcon, Tab, Tabs, TextField, Tooltip, Trash2Icon, Typography, cls, defaultBorderMixin, iconSize } from "@rebasepro/ui";
|
|
4
|
+
import { Alert, AlertTriangleIcon, Button, Chip, CircularProgress, DatabaseIcon, Dialog, DialogActions, DialogContent, DialogTitle, HelpCircleIcon, IconButton, KeyIcon, Link2Icon, LockIcon, MultiSelect, MultiSelectItem, Paper, RefreshCwIcon, ResizablePanels, Select, SelectItem, ShieldIcon, Tab, Tabs, TextField, Tooltip, Trash2Icon, Typography, cls, defaultBorderMixin, iconSize } from "@rebasepro/ui";
|
|
5
5
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
6
6
|
import { isPostgresCollection } from "@rebasepro/types";
|
|
7
|
+
import { JUNCTION_TABLES_SQL, REBASE_INTERNAL_PREFIXES, REBASE_INTERNAL_SCHEMAS } from "@rebasepro/common";
|
|
7
8
|
//#region src/components/RLSEditor/PolicyEditor.tsx
|
|
8
9
|
var COMMAND_OPTIONS = [
|
|
9
10
|
"ALL",
|
|
@@ -373,199 +374,212 @@ var PolicyEditor = ({ policy, schema, table, onSave, onCancel }) => {
|
|
|
373
374
|
open: helpOpen,
|
|
374
375
|
onOpenChange: setHelpOpen,
|
|
375
376
|
maxWidth: "3xl",
|
|
376
|
-
children: [
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
className: "text-text-secondary dark:text-text-secondary-dark",
|
|
399
|
-
children: t("studio_policy_help_step1_desc")
|
|
400
|
-
})]
|
|
401
|
-
}),
|
|
402
|
-
/* @__PURE__ */ jsxs(Paper, {
|
|
403
|
-
className: cls("p-4 sm:p-5 flex flex-col gap-1", defaultBorderMixin),
|
|
404
|
-
children: [
|
|
405
|
-
/* @__PURE__ */ jsx(Typography, {
|
|
406
|
-
variant: "subtitle2",
|
|
407
|
-
className: "text-primary dark:text-primary-light font-medium",
|
|
408
|
-
children: t("studio_policy_help_step2_title")
|
|
409
|
-
}),
|
|
410
|
-
/* @__PURE__ */ jsx(Typography, {
|
|
411
|
-
variant: "body2",
|
|
412
|
-
className: "text-text-secondary dark:text-text-secondary-dark mb-1",
|
|
413
|
-
children: t("studio_policy_help_step2_desc")
|
|
414
|
-
}),
|
|
415
|
-
/* @__PURE__ */ jsxs("ul", {
|
|
416
|
-
className: "list-disc pl-5 space-y-1 text-sm text-text-secondary dark:text-text-secondary-dark",
|
|
417
|
-
children: [
|
|
418
|
-
/* @__PURE__ */ jsxs("li", { children: [
|
|
419
|
-
/* @__PURE__ */ jsx("strong", { children: "public" }),
|
|
420
|
-
": ",
|
|
421
|
-
t("studio_policy_help_role_public")
|
|
422
|
-
] }),
|
|
423
|
-
/* @__PURE__ */ jsxs("li", { children: [
|
|
424
|
-
/* @__PURE__ */ jsx("strong", { children: "authenticated" }),
|
|
425
|
-
": ",
|
|
426
|
-
t("studio_policy_help_role_authenticated")
|
|
427
|
-
] }),
|
|
428
|
-
/* @__PURE__ */ jsxs("li", { children: [
|
|
429
|
-
/* @__PURE__ */ jsx("strong", { children: "anon" }),
|
|
430
|
-
": ",
|
|
431
|
-
t("studio_policy_help_role_anon")
|
|
432
|
-
] })
|
|
433
|
-
]
|
|
434
|
-
})
|
|
435
|
-
]
|
|
436
|
-
}),
|
|
437
|
-
/* @__PURE__ */ jsxs(Paper, {
|
|
438
|
-
className: cls("p-4 sm:p-5 flex flex-col gap-1", defaultBorderMixin),
|
|
439
|
-
children: [
|
|
440
|
-
/* @__PURE__ */ jsx(Typography, {
|
|
441
|
-
variant: "subtitle2",
|
|
442
|
-
className: "text-primary dark:text-primary-light font-medium",
|
|
443
|
-
children: t("studio_policy_help_step3_title")
|
|
444
|
-
}),
|
|
445
|
-
/* @__PURE__ */ jsx(Typography, {
|
|
446
|
-
variant: "body2",
|
|
447
|
-
className: "text-text-secondary dark:text-text-secondary-dark mb-1",
|
|
448
|
-
children: t("studio_policy_help_step3_desc")
|
|
449
|
-
}),
|
|
450
|
-
/* @__PURE__ */ jsx("div", {
|
|
451
|
-
className: cls("bg-surface-100 dark:bg-surface-950 px-3 py-2 rounded-md font-mono text-sm my-2", defaultBorderMixin),
|
|
452
|
-
children: "Example: auth.uid() = user_id"
|
|
453
|
-
}),
|
|
454
|
-
/* @__PURE__ */ jsx(Typography, {
|
|
455
|
-
variant: "caption",
|
|
456
|
-
className: "text-text-secondary dark:text-text-secondary-dark",
|
|
457
|
-
children: t("studio_policy_help_step3_example")
|
|
458
|
-
})
|
|
459
|
-
]
|
|
460
|
-
}),
|
|
461
|
-
/* @__PURE__ */ jsxs(Paper, {
|
|
462
|
-
className: cls("p-4 sm:p-5 flex flex-col gap-1", defaultBorderMixin),
|
|
463
|
-
children: [
|
|
464
|
-
/* @__PURE__ */ jsx(Typography, {
|
|
465
|
-
variant: "subtitle2",
|
|
466
|
-
className: "text-primary dark:text-primary-light font-medium",
|
|
467
|
-
children: t("studio_policy_help_step4_title")
|
|
468
|
-
}),
|
|
469
|
-
/* @__PURE__ */ jsx(Typography, {
|
|
470
|
-
variant: "body2",
|
|
471
|
-
className: "text-text-secondary dark:text-text-secondary-dark mb-1",
|
|
472
|
-
children: t("studio_policy_help_step4_desc")
|
|
473
|
-
}),
|
|
474
|
-
/* @__PURE__ */ jsx("div", {
|
|
475
|
-
className: cls("bg-surface-100 dark:bg-surface-950 px-3 py-2 rounded-md font-mono text-sm my-2", defaultBorderMixin),
|
|
476
|
-
children: "Example: auth.uid() = user_id"
|
|
477
|
-
}),
|
|
478
|
-
/* @__PURE__ */ jsx(Typography, {
|
|
479
|
-
variant: "caption",
|
|
480
|
-
className: "text-text-secondary dark:text-text-secondary-dark",
|
|
481
|
-
children: t("studio_policy_help_step4_example")
|
|
482
|
-
})
|
|
483
|
-
]
|
|
484
|
-
}),
|
|
485
|
-
/* @__PURE__ */ jsxs(Paper, {
|
|
486
|
-
className: cls("p-4 sm:p-5 flex flex-col gap-2 bg-primary/5 dark:bg-primary-bg-dark/10", defaultBorderMixin),
|
|
487
|
-
children: [
|
|
488
|
-
/* @__PURE__ */ jsx(Typography, {
|
|
377
|
+
children: [
|
|
378
|
+
/* @__PURE__ */ jsx(DialogTitle, {
|
|
379
|
+
hidden: true,
|
|
380
|
+
children: "Row-Level Security Help"
|
|
381
|
+
}),
|
|
382
|
+
/* @__PURE__ */ jsxs(DialogContent, {
|
|
383
|
+
className: "p-4 sm:p-6 lg:p-8 flex flex-col gap-6",
|
|
384
|
+
children: [
|
|
385
|
+
/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx(Typography, {
|
|
386
|
+
variant: "h5",
|
|
387
|
+
className: "mb-2",
|
|
388
|
+
children: t("studio_policy_help_title")
|
|
389
|
+
}), /* @__PURE__ */ jsx(Typography, {
|
|
390
|
+
className: "text-text-secondary dark:text-text-secondary-dark",
|
|
391
|
+
children: t("studio_policy_help_intro")
|
|
392
|
+
})] }),
|
|
393
|
+
/* @__PURE__ */ jsxs("div", {
|
|
394
|
+
className: "flex flex-col gap-4",
|
|
395
|
+
children: [
|
|
396
|
+
/* @__PURE__ */ jsxs(Paper, {
|
|
397
|
+
className: cls("p-4 sm:p-5 flex flex-col gap-1", defaultBorderMixin),
|
|
398
|
+
children: [/* @__PURE__ */ jsx(Typography, {
|
|
489
399
|
variant: "subtitle2",
|
|
490
400
|
className: "text-primary dark:text-primary-light font-medium",
|
|
491
|
-
children: t("
|
|
492
|
-
}),
|
|
493
|
-
/* @__PURE__ */ jsx(Typography, {
|
|
401
|
+
children: t("studio_policy_help_step1_title")
|
|
402
|
+
}), /* @__PURE__ */ jsx(Typography, {
|
|
494
403
|
variant: "body2",
|
|
495
404
|
className: "text-text-secondary dark:text-text-secondary-dark",
|
|
496
|
-
children: t("
|
|
497
|
-
})
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
})
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
})
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
405
|
+
children: t("studio_policy_help_step1_desc")
|
|
406
|
+
})]
|
|
407
|
+
}),
|
|
408
|
+
/* @__PURE__ */ jsxs(Paper, {
|
|
409
|
+
className: cls("p-4 sm:p-5 flex flex-col gap-1", defaultBorderMixin),
|
|
410
|
+
children: [
|
|
411
|
+
/* @__PURE__ */ jsx(Typography, {
|
|
412
|
+
variant: "subtitle2",
|
|
413
|
+
className: "text-primary dark:text-primary-light font-medium",
|
|
414
|
+
children: t("studio_policy_help_step2_title")
|
|
415
|
+
}),
|
|
416
|
+
/* @__PURE__ */ jsx(Typography, {
|
|
417
|
+
variant: "body2",
|
|
418
|
+
className: "text-text-secondary dark:text-text-secondary-dark mb-1",
|
|
419
|
+
children: t("studio_policy_help_step2_desc")
|
|
420
|
+
}),
|
|
421
|
+
/* @__PURE__ */ jsxs("ul", {
|
|
422
|
+
className: "list-disc pl-5 space-y-1 text-sm text-text-secondary dark:text-text-secondary-dark",
|
|
423
|
+
children: [
|
|
424
|
+
/* @__PURE__ */ jsxs("li", { children: [
|
|
425
|
+
/* @__PURE__ */ jsx("strong", { children: "public" }),
|
|
426
|
+
": ",
|
|
427
|
+
t("studio_policy_help_role_public")
|
|
428
|
+
] }),
|
|
429
|
+
/* @__PURE__ */ jsxs("li", { children: [
|
|
430
|
+
/* @__PURE__ */ jsx("strong", { children: "authenticated" }),
|
|
431
|
+
": ",
|
|
432
|
+
t("studio_policy_help_role_authenticated")
|
|
433
|
+
] }),
|
|
434
|
+
/* @__PURE__ */ jsxs("li", { children: [
|
|
435
|
+
/* @__PURE__ */ jsx("strong", { children: "anon" }),
|
|
436
|
+
": ",
|
|
437
|
+
t("studio_policy_help_role_anon")
|
|
438
|
+
] })
|
|
439
|
+
]
|
|
440
|
+
})
|
|
441
|
+
]
|
|
442
|
+
}),
|
|
443
|
+
/* @__PURE__ */ jsxs(Paper, {
|
|
444
|
+
className: cls("p-4 sm:p-5 flex flex-col gap-1", defaultBorderMixin),
|
|
445
|
+
children: [
|
|
446
|
+
/* @__PURE__ */ jsx(Typography, {
|
|
447
|
+
variant: "subtitle2",
|
|
448
|
+
className: "text-primary dark:text-primary-light font-medium",
|
|
449
|
+
children: t("studio_policy_help_step3_title")
|
|
450
|
+
}),
|
|
451
|
+
/* @__PURE__ */ jsx(Typography, {
|
|
452
|
+
variant: "body2",
|
|
453
|
+
className: "text-text-secondary dark:text-text-secondary-dark mb-1",
|
|
454
|
+
children: t("studio_policy_help_step3_desc")
|
|
455
|
+
}),
|
|
456
|
+
/* @__PURE__ */ jsx("div", {
|
|
457
|
+
className: cls("bg-surface-100 dark:bg-surface-950 px-3 py-2 rounded-md font-mono text-sm my-2", defaultBorderMixin),
|
|
458
|
+
children: "Example: auth.uid() = user_id"
|
|
459
|
+
}),
|
|
460
|
+
/* @__PURE__ */ jsx(Typography, {
|
|
461
|
+
variant: "caption",
|
|
462
|
+
className: "text-text-secondary dark:text-text-secondary-dark",
|
|
463
|
+
children: t("studio_policy_help_step3_example")
|
|
464
|
+
})
|
|
465
|
+
]
|
|
466
|
+
}),
|
|
467
|
+
/* @__PURE__ */ jsxs(Paper, {
|
|
468
|
+
className: cls("p-4 sm:p-5 flex flex-col gap-1", defaultBorderMixin),
|
|
469
|
+
children: [
|
|
470
|
+
/* @__PURE__ */ jsx(Typography, {
|
|
471
|
+
variant: "subtitle2",
|
|
472
|
+
className: "text-primary dark:text-primary-light font-medium",
|
|
473
|
+
children: t("studio_policy_help_step4_title")
|
|
474
|
+
}),
|
|
475
|
+
/* @__PURE__ */ jsx(Typography, {
|
|
476
|
+
variant: "body2",
|
|
477
|
+
className: "text-text-secondary dark:text-text-secondary-dark mb-1",
|
|
478
|
+
children: t("studio_policy_help_step4_desc")
|
|
479
|
+
}),
|
|
480
|
+
/* @__PURE__ */ jsx("div", {
|
|
481
|
+
className: cls("bg-surface-100 dark:bg-surface-950 px-3 py-2 rounded-md font-mono text-sm my-2", defaultBorderMixin),
|
|
482
|
+
children: "Example: auth.uid() = user_id"
|
|
483
|
+
}),
|
|
484
|
+
/* @__PURE__ */ jsx(Typography, {
|
|
485
|
+
variant: "caption",
|
|
486
|
+
className: "text-text-secondary dark:text-text-secondary-dark",
|
|
487
|
+
children: t("studio_policy_help_step4_example")
|
|
488
|
+
})
|
|
489
|
+
]
|
|
490
|
+
}),
|
|
491
|
+
/* @__PURE__ */ jsxs(Paper, {
|
|
492
|
+
className: cls("p-4 sm:p-5 flex flex-col gap-2 bg-primary/5 dark:bg-primary-bg-dark/10", defaultBorderMixin),
|
|
493
|
+
children: [
|
|
494
|
+
/* @__PURE__ */ jsx(Typography, {
|
|
495
|
+
variant: "subtitle2",
|
|
496
|
+
className: "text-primary dark:text-primary-light font-medium",
|
|
497
|
+
children: t("studio_policy_help_auth_vars_title")
|
|
498
|
+
}),
|
|
499
|
+
/* @__PURE__ */ jsx(Typography, {
|
|
500
|
+
variant: "body2",
|
|
501
|
+
className: "text-text-secondary dark:text-text-secondary-dark",
|
|
502
|
+
children: t("studio_policy_help_auth_vars_desc")
|
|
503
|
+
}),
|
|
504
|
+
/* @__PURE__ */ jsxs("ul", {
|
|
505
|
+
className: "list-disc pl-5 space-y-2 text-sm text-text-secondary dark:text-text-secondary-dark font-normal",
|
|
506
|
+
children: [
|
|
507
|
+
/* @__PURE__ */ jsxs("li", { children: [/* @__PURE__ */ jsx("code", {
|
|
508
|
+
className: "bg-surface-100 dark:bg-surface-950 px-1.5 py-0.5 rounded mr-1 whitespace-nowrap",
|
|
509
|
+
children: "auth.uid()"
|
|
510
|
+
}), /* @__PURE__ */ jsxs("span", {
|
|
511
|
+
className: "block mt-0.5",
|
|
512
|
+
children: ["Returns the current user's ID as text. Example: ", /* @__PURE__ */ jsx("code", {
|
|
513
|
+
className: "bg-surface-100 dark:bg-surface-950 px-1 py-0.5 rounded text-[11px]",
|
|
514
|
+
children: "auth.uid() = user_id"
|
|
515
|
+
})]
|
|
516
|
+
})] }),
|
|
517
|
+
/* @__PURE__ */ jsxs("li", { children: [/* @__PURE__ */ jsx("code", {
|
|
518
|
+
className: "bg-surface-100 dark:bg-surface-950 px-1.5 py-0.5 rounded mr-1 whitespace-nowrap",
|
|
519
|
+
children: "auth.jwt()"
|
|
520
|
+
}), /* @__PURE__ */ jsxs("span", {
|
|
521
|
+
className: "block mt-0.5",
|
|
522
|
+
children: ["Returns the full JWT payload as JSONB so you can check custom claims. Example: ", /* @__PURE__ */ jsx("code", {
|
|
523
|
+
className: "bg-surface-100 dark:bg-surface-950 px-1 py-0.5 rounded text-[11px]",
|
|
524
|
+
children: "auth.jwt() ->> 'email' = 'admin@example.com'"
|
|
525
|
+
})]
|
|
526
|
+
})] }),
|
|
527
|
+
/* @__PURE__ */ jsxs("li", { children: [/* @__PURE__ */ jsx("code", {
|
|
528
|
+
className: "bg-surface-100 dark:bg-surface-950 px-1.5 py-0.5 rounded mr-1 whitespace-nowrap",
|
|
529
|
+
children: "auth.roles()"
|
|
530
|
+
}), /* @__PURE__ */ jsxs("span", {
|
|
531
|
+
className: "block mt-0.5",
|
|
532
|
+
children: ["Returns the user's role IDs as a comma-separated string. Best used with: ", /* @__PURE__ */ jsx("code", {
|
|
533
|
+
className: "bg-surface-100 dark:bg-surface-950 px-1 py-0.5 rounded text-[11px]",
|
|
534
|
+
children: "string_to_array(auth.roles(), ',') @> ARRAY['admin']"
|
|
535
|
+
})]
|
|
536
|
+
})] })
|
|
537
|
+
]
|
|
538
|
+
})
|
|
539
|
+
]
|
|
540
|
+
})
|
|
541
|
+
]
|
|
542
|
+
}),
|
|
543
|
+
/* @__PURE__ */ jsxs("div", {
|
|
544
|
+
className: cls("mt-2 flex flex-col sm:flex-row justify-between items-start sm:items-center bg-primary/5 dark:bg-primary-bg-dark/10 p-4 rounded-xl border border-primary/10 dark:border-primary/20", defaultBorderMixin),
|
|
545
|
+
children: [/* @__PURE__ */ jsx(Typography, {
|
|
546
|
+
variant: "body2",
|
|
547
|
+
className: "text-primary dark:text-primary-light mb-4 sm:mb-0 max-w-md",
|
|
548
|
+
children: t("studio_policy_help_docs_cta")
|
|
549
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
550
|
+
component: "a",
|
|
551
|
+
href: "https://www.postgresql.org/docs/current/sql-createpolicy.html",
|
|
552
|
+
target: "_blank",
|
|
553
|
+
variant: "outlined",
|
|
554
|
+
color: "primary",
|
|
555
|
+
size: "small",
|
|
556
|
+
className: "whitespace-nowrap flex-shrink-0",
|
|
557
|
+
children: t("studio_policy_help_read_docs")
|
|
558
|
+
})]
|
|
559
|
+
})
|
|
560
|
+
]
|
|
561
|
+
}),
|
|
562
|
+
/* @__PURE__ */ jsx(DialogActions, {
|
|
563
|
+
className: "p-4 sm:px-6 sm:pb-6 pt-0 border-t-0",
|
|
564
|
+
children: /* @__PURE__ */ jsx(Button, {
|
|
565
|
+
onClick: () => setHelpOpen(false),
|
|
566
|
+
variant: "filled",
|
|
567
|
+
color: "primary",
|
|
568
|
+
children: t("studio_policy_help_got_it")
|
|
553
569
|
})
|
|
554
|
-
]
|
|
555
|
-
}), /* @__PURE__ */ jsx(DialogActions, {
|
|
556
|
-
className: "p-4 sm:px-6 sm:pb-6 pt-0 border-t-0",
|
|
557
|
-
children: /* @__PURE__ */ jsx(Button, {
|
|
558
|
-
onClick: () => setHelpOpen(false),
|
|
559
|
-
variant: "filled",
|
|
560
|
-
color: "primary",
|
|
561
|
-
children: t("studio_policy_help_got_it")
|
|
562
570
|
})
|
|
563
|
-
|
|
571
|
+
]
|
|
564
572
|
})
|
|
565
573
|
] });
|
|
566
574
|
};
|
|
567
575
|
//#endregion
|
|
568
576
|
//#region src/components/RLSEditor/RLSEditor.tsx
|
|
577
|
+
function classifyTableClient(tableName, schemaName, junctionTableNames, isMappedToCollection) {
|
|
578
|
+
if (REBASE_INTERNAL_SCHEMAS.includes(schemaName) || REBASE_INTERNAL_PREFIXES.some((prefix) => tableName.startsWith(prefix))) return "internal";
|
|
579
|
+
if (isMappedToCollection) return "collection";
|
|
580
|
+
if (junctionTableNames.has(tableName)) return "junction";
|
|
581
|
+
return "other";
|
|
582
|
+
}
|
|
569
583
|
/**
|
|
570
584
|
* Validates and double-quotes a SQL identifier to prevent injection.
|
|
571
585
|
* Only allows safe Postgres identifiers (letters, digits, underscores).
|
|
@@ -575,6 +589,81 @@ function sanitizeSqlIdentifier(name) {
|
|
|
575
589
|
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)) throw new Error(`Invalid SQL identifier: "${name}". Only letters, digits, and underscores are allowed.`);
|
|
576
590
|
return `"${name}"`;
|
|
577
591
|
}
|
|
592
|
+
function SidebarSection({ title, icon, expanded, onToggle, count, children }) {
|
|
593
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
594
|
+
className: "mb-2",
|
|
595
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
596
|
+
className: "flex items-center p-1.5 cursor-pointer hover:bg-surface-100 dark:hover:bg-surface-900 rounded transition-colors",
|
|
597
|
+
onClick: onToggle,
|
|
598
|
+
children: [
|
|
599
|
+
/* @__PURE__ */ jsx("svg", {
|
|
600
|
+
className: cls("w-3 h-3 mr-1.5 transition-transform text-text-disabled dark:text-text-disabled-dark", expanded ? "rotate-90" : ""),
|
|
601
|
+
fill: "currentColor",
|
|
602
|
+
viewBox: "0 0 20 20",
|
|
603
|
+
children: /* @__PURE__ */ jsx("path", { d: "M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" })
|
|
604
|
+
}),
|
|
605
|
+
icon,
|
|
606
|
+
/* @__PURE__ */ jsx(Typography, {
|
|
607
|
+
variant: "body2",
|
|
608
|
+
className: "text-text-primary dark:text-text-primary-dark font-medium text-xs truncate flex-grow ml-1.5",
|
|
609
|
+
children: title
|
|
610
|
+
}),
|
|
611
|
+
count !== void 0 && /* @__PURE__ */ jsx("span", {
|
|
612
|
+
className: "text-[10px] text-text-disabled dark:text-text-disabled-dark font-medium tabular-nums mr-1",
|
|
613
|
+
children: count
|
|
614
|
+
})
|
|
615
|
+
]
|
|
616
|
+
}), expanded && /* @__PURE__ */ jsx("div", {
|
|
617
|
+
className: "ml-3 mt-0.5 space-y-0.5",
|
|
618
|
+
children
|
|
619
|
+
})]
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
function SidebarTableRow({ table, isSelected, onSelect, badge, dimmed, t }) {
|
|
623
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
624
|
+
onClick: onSelect,
|
|
625
|
+
className: 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"),
|
|
626
|
+
children: [
|
|
627
|
+
/* @__PURE__ */ jsx("svg", {
|
|
628
|
+
className: "w-3.5 h-3.5 mr-1 shrink-0 text-text-disabled dark:text-text-disabled-dark",
|
|
629
|
+
fill: "none",
|
|
630
|
+
stroke: "currentColor",
|
|
631
|
+
viewBox: "0 0 24 24",
|
|
632
|
+
children: /* @__PURE__ */ jsx("path", {
|
|
633
|
+
strokeLinecap: "round",
|
|
634
|
+
strokeLinejoin: "round",
|
|
635
|
+
strokeWidth: 2,
|
|
636
|
+
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"
|
|
637
|
+
})
|
|
638
|
+
}),
|
|
639
|
+
/* @__PURE__ */ jsx(Typography, {
|
|
640
|
+
variant: "body2",
|
|
641
|
+
className: "text-xs truncate flex-1 min-w-0",
|
|
642
|
+
children: table.tableName
|
|
643
|
+
}),
|
|
644
|
+
/* @__PURE__ */ jsxs("div", {
|
|
645
|
+
className: "flex items-center gap-1.5 shrink-0 ml-2",
|
|
646
|
+
children: [
|
|
647
|
+
badge && /* @__PURE__ */ jsx("span", {
|
|
648
|
+
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",
|
|
649
|
+
children: badge
|
|
650
|
+
}),
|
|
651
|
+
table.rlsEnabled ? /* @__PURE__ */ jsx(Tooltip, {
|
|
652
|
+
title: t("studio_rls_enabled"),
|
|
653
|
+
children: /* @__PURE__ */ jsx("div", { className: "w-1.5 h-1.5 rounded-full bg-green-500" })
|
|
654
|
+
}) : /* @__PURE__ */ jsx(Tooltip, {
|
|
655
|
+
title: t("studio_rls_disabled"),
|
|
656
|
+
children: /* @__PURE__ */ jsx("div", { className: "w-1.5 h-1.5 rounded-full bg-orange-400 opacity-50" })
|
|
657
|
+
}),
|
|
658
|
+
/* @__PURE__ */ jsx("span", {
|
|
659
|
+
className: "text-[10px] opacity-40 group-hover:opacity-100 min-w-[1.2rem] text-right font-medium",
|
|
660
|
+
children: table.policies.length
|
|
661
|
+
})
|
|
662
|
+
]
|
|
663
|
+
})
|
|
664
|
+
]
|
|
665
|
+
});
|
|
666
|
+
}
|
|
578
667
|
var RLSEditor = ({ apiUrl = "" }) => {
|
|
579
668
|
const { databaseAdmin } = useRebaseContext();
|
|
580
669
|
const snackbarController = useSnackbarController();
|
|
@@ -585,6 +674,7 @@ var RLSEditor = ({ apiUrl = "" }) => {
|
|
|
585
674
|
const [tables, setTables] = useState([]);
|
|
586
675
|
const [selectedTable, setSelectedTable] = useState(null);
|
|
587
676
|
const [activeTab, setActiveTab] = useState(0);
|
|
677
|
+
const [junctionTableNames, setJunctionTableNames] = useState(/* @__PURE__ */ new Set());
|
|
588
678
|
const [editingPolicy, setEditingPolicy] = useState(null);
|
|
589
679
|
const [sidebarSize, setSidebarSize] = useState(() => {
|
|
590
680
|
try {
|
|
@@ -594,7 +684,11 @@ var RLSEditor = ({ apiUrl = "" }) => {
|
|
|
594
684
|
return 20;
|
|
595
685
|
}
|
|
596
686
|
});
|
|
597
|
-
const [
|
|
687
|
+
const [expandedSections, setExpandedSections] = useState({
|
|
688
|
+
collection: true,
|
|
689
|
+
other: true,
|
|
690
|
+
internal: false
|
|
691
|
+
});
|
|
598
692
|
const [sidebarTab, setSidebarTab] = useState("tables");
|
|
599
693
|
useEffect(() => {
|
|
600
694
|
try {
|
|
@@ -676,7 +770,15 @@ var RLSEditor = ({ apiUrl = "" }) => {
|
|
|
676
770
|
});
|
|
677
771
|
const sortedTables = Object.values(tableMap).sort((a, b) => a.tableName.localeCompare(b.tableName));
|
|
678
772
|
setTables(sortedTables);
|
|
679
|
-
|
|
773
|
+
try {
|
|
774
|
+
const jRows = extractRows(await databaseAdmin.executeSql(JUNCTION_TABLES_SQL));
|
|
775
|
+
setJunctionTableNames(new Set(jRows.map((r) => r.table_name).filter(Boolean)));
|
|
776
|
+
} catch {}
|
|
777
|
+
if (sortedTables.length > 0 && !selectedTable) {
|
|
778
|
+
const firstUserTable = sortedTables.find((t) => !REBASE_INTERNAL_SCHEMAS.includes(t.schemaName));
|
|
779
|
+
if (firstUserTable) setSelectedTable(`${firstUserTable.schemaName}.${firstUserTable.tableName}`);
|
|
780
|
+
else setSelectedTable(`${sortedTables[0].schemaName}.${sortedTables[0].tableName}`);
|
|
781
|
+
}
|
|
680
782
|
} catch (e) {
|
|
681
783
|
console.error("RLS fetch error:", e);
|
|
682
784
|
setError("Failed to fetch RLS policies: " + (e instanceof Error ? e.message : String(e)));
|
|
@@ -694,18 +796,38 @@ var RLSEditor = ({ apiUrl = "" }) => {
|
|
|
694
796
|
if (!selectedTable) return null;
|
|
695
797
|
return tables.find((t) => `${t.schemaName}.${t.tableName}` === selectedTable) || null;
|
|
696
798
|
}, [selectedTable, tables]);
|
|
697
|
-
|
|
698
|
-
|
|
799
|
+
/** Categorize tables into 4 buckets for the sidebar. */
|
|
800
|
+
const categorizedTables = useMemo(() => {
|
|
801
|
+
const groups = {
|
|
802
|
+
collection: [],
|
|
803
|
+
junction: [],
|
|
804
|
+
internal: [],
|
|
805
|
+
other: []
|
|
806
|
+
};
|
|
699
807
|
tables.forEach((table) => {
|
|
700
|
-
|
|
701
|
-
groups[table.schemaName].push(table);
|
|
808
|
+
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);
|
|
809
|
+
groups[classifyTableClient(table.tableName, table.schemaName, junctionTableNames, isMapped)].push(table);
|
|
702
810
|
});
|
|
703
811
|
return groups;
|
|
704
|
-
}, [
|
|
812
|
+
}, [
|
|
813
|
+
tables,
|
|
814
|
+
junctionTableNames,
|
|
815
|
+
collectionRegistry.collections
|
|
816
|
+
]);
|
|
705
817
|
const activeCollection = useMemo(() => {
|
|
706
818
|
if (!activeTableData) return null;
|
|
707
819
|
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;
|
|
708
820
|
}, [activeTableData, collectionRegistry.collections]);
|
|
821
|
+
/** The category of the currently selected table. */
|
|
822
|
+
const activeTableCategory = useMemo(() => {
|
|
823
|
+
if (!activeTableData) return null;
|
|
824
|
+
const isMapped = !!activeCollection;
|
|
825
|
+
return classifyTableClient(activeTableData.tableName, activeTableData.schemaName, junctionTableNames, isMapped);
|
|
826
|
+
}, [
|
|
827
|
+
activeTableData,
|
|
828
|
+
activeCollection,
|
|
829
|
+
junctionTableNames
|
|
830
|
+
]);
|
|
709
831
|
const mergedPolicies = useMemo(() => {
|
|
710
832
|
if (!activeTableData) return [];
|
|
711
833
|
const policiesMap = {};
|
|
@@ -803,74 +925,78 @@ var RLSEditor = ({ apiUrl = "" }) => {
|
|
|
803
925
|
children: isLoading && tables.length === 0 ? /* @__PURE__ */ jsx("div", {
|
|
804
926
|
className: "flex justify-center p-4",
|
|
805
927
|
children: /* @__PURE__ */ jsx(CircularProgress, { size: "small" })
|
|
806
|
-
}) :
|
|
928
|
+
}) : tables.length === 0 ? /* @__PURE__ */ jsx("div", {
|
|
807
929
|
className: "p-4 text-center",
|
|
808
930
|
children: /* @__PURE__ */ jsx(Typography, {
|
|
809
931
|
variant: "caption",
|
|
810
932
|
className: "text-text-disabled dark:text-text-disabled-dark italic",
|
|
811
933
|
children: t("studio_rls_no_tables")
|
|
812
934
|
})
|
|
813
|
-
}) :
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
935
|
+
}) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
936
|
+
categorizedTables.collection.length > 0 && /* @__PURE__ */ jsx(SidebarSection, {
|
|
937
|
+
title: "Schema Collections",
|
|
938
|
+
icon: /* @__PURE__ */ jsx(DatabaseIcon, {
|
|
939
|
+
size: 12,
|
|
940
|
+
className: "text-primary dark:text-primary-light"
|
|
941
|
+
}),
|
|
942
|
+
expanded: expandedSections.collection ?? true,
|
|
943
|
+
onToggle: () => setExpandedSections((prev) => ({
|
|
818
944
|
...prev,
|
|
819
|
-
|
|
945
|
+
collection: !(prev.collection ?? true)
|
|
820
946
|
})),
|
|
821
|
-
children:
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
}
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
})
|
|
872
|
-
})
|
|
873
|
-
}
|
|
947
|
+
children: categorizedTables.collection.map((table) => /* @__PURE__ */ jsx(SidebarTableRow, {
|
|
948
|
+
table,
|
|
949
|
+
isSelected: selectedTable === `${table.schemaName}.${table.tableName}`,
|
|
950
|
+
onSelect: () => setSelectedTable(`${table.schemaName}.${table.tableName}`),
|
|
951
|
+
t
|
|
952
|
+
}, `${table.schemaName}.${table.tableName}`))
|
|
953
|
+
}),
|
|
954
|
+
(categorizedTables.other.length > 0 || categorizedTables.junction.length > 0) && /* @__PURE__ */ jsxs(SidebarSection, {
|
|
955
|
+
title: "Other Tables",
|
|
956
|
+
icon: /* @__PURE__ */ jsx(AlertTriangleIcon, {
|
|
957
|
+
size: 12,
|
|
958
|
+
className: "text-yellow-500 dark:text-yellow-400"
|
|
959
|
+
}),
|
|
960
|
+
expanded: expandedSections.other ?? true,
|
|
961
|
+
onToggle: () => setExpandedSections((prev) => ({
|
|
962
|
+
...prev,
|
|
963
|
+
other: !(prev.other ?? true)
|
|
964
|
+
})),
|
|
965
|
+
children: [categorizedTables.other.map((table) => /* @__PURE__ */ jsx(SidebarTableRow, {
|
|
966
|
+
table,
|
|
967
|
+
isSelected: selectedTable === `${table.schemaName}.${table.tableName}`,
|
|
968
|
+
onSelect: () => setSelectedTable(`${table.schemaName}.${table.tableName}`),
|
|
969
|
+
t
|
|
970
|
+
}, `${table.schemaName}.${table.tableName}`)), categorizedTables.junction.map((table) => /* @__PURE__ */ jsx(SidebarTableRow, {
|
|
971
|
+
table,
|
|
972
|
+
isSelected: selectedTable === `${table.schemaName}.${table.tableName}`,
|
|
973
|
+
onSelect: () => setSelectedTable(`${table.schemaName}.${table.tableName}`),
|
|
974
|
+
badge: "Junction",
|
|
975
|
+
dimmed: true,
|
|
976
|
+
t
|
|
977
|
+
}, `${table.schemaName}.${table.tableName}`))]
|
|
978
|
+
}),
|
|
979
|
+
categorizedTables.internal.length > 0 && /* @__PURE__ */ jsx(SidebarSection, {
|
|
980
|
+
title: "Rebase Internal",
|
|
981
|
+
icon: /* @__PURE__ */ jsx(LockIcon, {
|
|
982
|
+
size: 12,
|
|
983
|
+
className: "text-text-disabled dark:text-text-disabled-dark"
|
|
984
|
+
}),
|
|
985
|
+
expanded: expandedSections.internal ?? false,
|
|
986
|
+
onToggle: () => setExpandedSections((prev) => ({
|
|
987
|
+
...prev,
|
|
988
|
+
internal: !(prev.internal ?? false)
|
|
989
|
+
})),
|
|
990
|
+
count: categorizedTables.internal.length,
|
|
991
|
+
children: categorizedTables.internal.map((table) => /* @__PURE__ */ jsx(SidebarTableRow, {
|
|
992
|
+
table,
|
|
993
|
+
isSelected: selectedTable === `${table.schemaName}.${table.tableName}`,
|
|
994
|
+
onSelect: () => setSelectedTable(`${table.schemaName}.${table.tableName}`),
|
|
995
|
+
dimmed: true,
|
|
996
|
+
t
|
|
997
|
+
}, `${table.schemaName}.${table.tableName}`))
|
|
998
|
+
})
|
|
999
|
+
] })
|
|
874
1000
|
})]
|
|
875
1001
|
}), sidebarTab === "info" && /* @__PURE__ */ jsxs("div", {
|
|
876
1002
|
className: "flex flex-col h-full",
|
|
@@ -1033,10 +1159,11 @@ var RLSEditor = ({ apiUrl = "" }) => {
|
|
|
1033
1159
|
size: "small",
|
|
1034
1160
|
onClick: async () => {
|
|
1035
1161
|
const table = activeTableData.tableName;
|
|
1162
|
+
const qualifiedTable = `${sanitizeSqlIdentifier(activeTableData.schemaName)}.${sanitizeSqlIdentifier(table)}`;
|
|
1036
1163
|
const action = activeTableData.rlsEnabled ? "DISABLE" : "ENABLE";
|
|
1037
1164
|
if (!confirm(`Are you sure you want to ${action.toLowerCase()} Row Level Security on "${table}"?`)) return;
|
|
1038
1165
|
try {
|
|
1039
|
-
await databaseAdmin.executeSql(`ALTER TABLE ${
|
|
1166
|
+
await databaseAdmin.executeSql(`ALTER TABLE ${qualifiedTable} ${action} ROW LEVEL SECURITY`);
|
|
1040
1167
|
snackbarController.open({
|
|
1041
1168
|
type: "success",
|
|
1042
1169
|
message: `RLS ${action.toLowerCase()}d on ${table}`
|
|
@@ -1063,7 +1190,6 @@ var RLSEditor = ({ apiUrl = "" }) => {
|
|
|
1063
1190
|
/* @__PURE__ */ jsx(Button, {
|
|
1064
1191
|
size: "small",
|
|
1065
1192
|
color: "primary",
|
|
1066
|
-
disabled: !activeCollection,
|
|
1067
1193
|
onClick: () => setEditingPolicy("new"),
|
|
1068
1194
|
children: t("studio_rls_create_policy")
|
|
1069
1195
|
})
|
|
@@ -1104,31 +1230,57 @@ var RLSEditor = ({ apiUrl = "" }) => {
|
|
|
1104
1230
|
schema: activeTableData.schemaName,
|
|
1105
1231
|
table: activeTableData.tableName,
|
|
1106
1232
|
onSave: async (newPolicy) => {
|
|
1107
|
-
if (
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1233
|
+
if (activeCollection) {
|
|
1234
|
+
const rule = {
|
|
1235
|
+
name: newPolicy.policyname,
|
|
1236
|
+
operation: newPolicy.cmd?.toLowerCase(),
|
|
1237
|
+
mode: newPolicy.permissive?.toLowerCase(),
|
|
1238
|
+
using: newPolicy.qual || void 0,
|
|
1239
|
+
withCheck: newPolicy.with_check || void 0,
|
|
1240
|
+
roles: newPolicy.roles
|
|
1241
|
+
};
|
|
1242
|
+
const existingRules = (isPostgresCollection(activeCollection) ? activeCollection.securityRules : void 0) || [];
|
|
1243
|
+
let newRules;
|
|
1244
|
+
if (editingPolicy === "new") newRules = [...existingRules, rule];
|
|
1245
|
+
else newRules = existingRules.map((r) => r.name === editingPolicy.policyname ? rule : r);
|
|
1246
|
+
try {
|
|
1247
|
+
if (!(await fetch(`${apiUrl}/api/schema-editor/collection/save`, {
|
|
1248
|
+
method: "POST",
|
|
1249
|
+
headers: { "Content-Type": "application/json" },
|
|
1250
|
+
body: JSON.stringify({
|
|
1251
|
+
collectionId: activeCollection.id || activeCollection.path || activeCollection.alias || activeTableData.tableName,
|
|
1252
|
+
collectionData: { securityRules: newRules }
|
|
1253
|
+
})
|
|
1254
|
+
})).ok) throw new Error("Failed to save policy");
|
|
1255
|
+
snackbarController.open({
|
|
1256
|
+
type: "success",
|
|
1257
|
+
message: "Policy saved successfully"
|
|
1258
|
+
});
|
|
1259
|
+
setEditingPolicy(null);
|
|
1260
|
+
fetchRLSData();
|
|
1261
|
+
} catch (e) {
|
|
1262
|
+
snackbarController.open({
|
|
1263
|
+
type: "error",
|
|
1264
|
+
message: e instanceof Error ? e.message : String(e)
|
|
1265
|
+
});
|
|
1266
|
+
}
|
|
1267
|
+
} else try {
|
|
1268
|
+
const qualifiedTable = `${sanitizeSqlIdentifier(activeTableData.schemaName)}.${sanitizeSqlIdentifier(activeTableData.tableName)}`;
|
|
1269
|
+
const policyName = sanitizeSqlIdentifier(newPolicy.policyname || "unnamed_policy");
|
|
1270
|
+
const cmd = newPolicy.cmd || "ALL";
|
|
1271
|
+
const permissive = (newPolicy.permissive || "PERMISSIVE") === "PERMISSIVE" ? "PERMISSIVE" : "RESTRICTIVE";
|
|
1272
|
+
const roles = newPolicy.roles && newPolicy.roles.length > 0 ? newPolicy.roles.map((r) => sanitizeSqlIdentifier(r)).join(", ") : "public";
|
|
1273
|
+
if (editingPolicy !== "new") await databaseAdmin.executeSql(`DROP POLICY IF EXISTS ${policyName} ON ${qualifiedTable}`);
|
|
1274
|
+
let sql = `CREATE POLICY ${policyName} ON ${qualifiedTable}`;
|
|
1275
|
+
sql += ` AS ${permissive}`;
|
|
1276
|
+
sql += ` FOR ${cmd}`;
|
|
1277
|
+
sql += ` TO ${roles}`;
|
|
1278
|
+
if (newPolicy.qual) sql += ` USING (${newPolicy.qual})`;
|
|
1279
|
+
if (newPolicy.with_check) sql += ` WITH CHECK (${newPolicy.with_check})`;
|
|
1280
|
+
await databaseAdmin.executeSql(sql);
|
|
1129
1281
|
snackbarController.open({
|
|
1130
1282
|
type: "success",
|
|
1131
|
-
message:
|
|
1283
|
+
message: `Policy "${newPolicy.policyname}" applied directly via SQL`
|
|
1132
1284
|
});
|
|
1133
1285
|
setEditingPolicy(null);
|
|
1134
1286
|
fetchRLSData();
|
|
@@ -1147,17 +1299,59 @@ var RLSEditor = ({ apiUrl = "" }) => {
|
|
|
1147
1299
|
children: /* @__PURE__ */ jsxs("div", {
|
|
1148
1300
|
className: "max-w-4xl mx-auto flex flex-col gap-6",
|
|
1149
1301
|
children: [
|
|
1150
|
-
activeTableData &&
|
|
1302
|
+
activeTableData && activeTableCategory === "internal" && /* @__PURE__ */ jsx(Alert, {
|
|
1303
|
+
color: "info",
|
|
1304
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
1305
|
+
className: "flex items-start gap-2",
|
|
1306
|
+
children: [/* @__PURE__ */ jsx(LockIcon, {
|
|
1307
|
+
size: 16,
|
|
1308
|
+
className: "shrink-0 mt-0.5"
|
|
1309
|
+
}), /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx(Typography, {
|
|
1310
|
+
variant: "body2",
|
|
1311
|
+
className: "mb-1 font-semibold",
|
|
1312
|
+
children: "Rebase System Table"
|
|
1313
|
+
}), /* @__PURE__ */ jsx(Typography, {
|
|
1314
|
+
variant: "caption",
|
|
1315
|
+
className: "opacity-80",
|
|
1316
|
+
children: "This table is managed internally by Rebase. Its security policies are configured automatically. Editing policies on system tables is an advanced operation."
|
|
1317
|
+
})] })]
|
|
1318
|
+
})
|
|
1319
|
+
}),
|
|
1320
|
+
activeTableData && activeTableCategory === "junction" && /* @__PURE__ */ jsx(Alert, {
|
|
1321
|
+
color: "info",
|
|
1322
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
1323
|
+
className: "flex items-start gap-2",
|
|
1324
|
+
children: [/* @__PURE__ */ jsx(Link2Icon, {
|
|
1325
|
+
size: 16,
|
|
1326
|
+
className: "shrink-0 mt-0.5"
|
|
1327
|
+
}), /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx(Typography, {
|
|
1328
|
+
variant: "body2",
|
|
1329
|
+
className: "mb-1 font-semibold",
|
|
1330
|
+
children: "Junction Table"
|
|
1331
|
+
}), /* @__PURE__ */ jsx(Typography, {
|
|
1332
|
+
variant: "caption",
|
|
1333
|
+
className: "opacity-80",
|
|
1334
|
+
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."
|
|
1335
|
+
})] })]
|
|
1336
|
+
})
|
|
1337
|
+
}),
|
|
1338
|
+
activeTableData && activeTableCategory === "other" && /* @__PURE__ */ jsx(Alert, {
|
|
1151
1339
|
color: "warning",
|
|
1152
|
-
children:
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1340
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
1341
|
+
className: "flex items-start gap-2",
|
|
1342
|
+
children: [/* @__PURE__ */ jsx(AlertTriangleIcon, {
|
|
1343
|
+
size: 16,
|
|
1344
|
+
className: "shrink-0 mt-0.5"
|
|
1345
|
+
}), /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx(Typography, {
|
|
1346
|
+
variant: "body2",
|
|
1347
|
+
className: "mb-1 font-semibold",
|
|
1348
|
+
children: "Unmapped Table"
|
|
1349
|
+
}), /* @__PURE__ */ jsx(Typography, {
|
|
1350
|
+
variant: "caption",
|
|
1351
|
+
className: "opacity-80",
|
|
1352
|
+
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."
|
|
1353
|
+
})] })]
|
|
1354
|
+
})
|
|
1161
1355
|
}),
|
|
1162
1356
|
activeTableData && !activeTableData.rlsEnabled && /* @__PURE__ */ jsxs("div", {
|
|
1163
1357
|
className: cls("p-4 sm:p-5 bg-yellow-50 dark:bg-yellow-900/20 border border-yellow-200 dark:border-yellow-900/50 rounded-lg flex flex-col sm:flex-row gap-4 items-start sm:items-center justify-between", defaultBorderMixin),
|
|
@@ -1271,7 +1465,6 @@ var RLSEditor = ({ apiUrl = "" }) => {
|
|
|
1271
1465
|
variant: "text",
|
|
1272
1466
|
color: "primary",
|
|
1273
1467
|
onClick: () => setEditingPolicy(policy),
|
|
1274
|
-
disabled: !activeCollection,
|
|
1275
1468
|
children: t("studio_rls_edit")
|
|
1276
1469
|
}),
|
|
1277
1470
|
policy.status !== "code_only" && /* @__PURE__ */ jsx(Tooltip, {
|
|
@@ -1281,9 +1474,10 @@ var RLSEditor = ({ apiUrl = "" }) => {
|
|
|
1281
1474
|
size: "small",
|
|
1282
1475
|
onClick: async () => {
|
|
1283
1476
|
const table = activeTableData.tableName;
|
|
1477
|
+
const qualifiedTable = `${sanitizeSqlIdentifier(activeTableData.schemaName)}.${sanitizeSqlIdentifier(table)}`;
|
|
1284
1478
|
if (!confirm(`Drop policy "${policy.policyname}" from table "${table}"?`)) return;
|
|
1285
1479
|
try {
|
|
1286
|
-
await databaseAdmin.executeSql(`DROP POLICY ${sanitizeSqlIdentifier(policy.policyname)} ON ${
|
|
1480
|
+
await databaseAdmin.executeSql(`DROP POLICY ${sanitizeSqlIdentifier(policy.policyname)} ON ${qualifiedTable}`);
|
|
1287
1481
|
snackbarController.open({
|
|
1288
1482
|
type: "success",
|
|
1289
1483
|
message: `Policy "${policy.policyname}" dropped`
|
|
@@ -1320,7 +1514,7 @@ var RLSEditor = ({ apiUrl = "" }) => {
|
|
|
1320
1514
|
className: "text-text-disabled dark:text-text-disabled-dark max-w-sm mb-4",
|
|
1321
1515
|
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."
|
|
1322
1516
|
}),
|
|
1323
|
-
|
|
1517
|
+
/* @__PURE__ */ jsx(Button, {
|
|
1324
1518
|
size: "small",
|
|
1325
1519
|
variant: "filled",
|
|
1326
1520
|
color: "primary",
|
|
@@ -1359,4 +1553,4 @@ var RLSEditor = ({ apiUrl = "" }) => {
|
|
|
1359
1553
|
//#endregion
|
|
1360
1554
|
export { RLSEditor };
|
|
1361
1555
|
|
|
1362
|
-
//# sourceMappingURL=RLSEditor-
|
|
1556
|
+
//# sourceMappingURL=RLSEditor-BM64laoW.js.map
|