mirror of
https://github.com/rowyio/rowy.git
synced 2025-12-29 00:16:39 +01:00
143 lines
4.2 KiB
TypeScript
143 lines
4.2 KiB
TypeScript
import { useState } from "react";
|
|
import { useWatch } from "react-hook-form";
|
|
|
|
import {
|
|
InputLabel,
|
|
Collapse,
|
|
FormControlLabel,
|
|
Checkbox,
|
|
Grid,
|
|
Button,
|
|
} from "@mui/material";
|
|
import InlineOpenInNewIcon from "@src/components/InlineOpenInNewIcon";
|
|
import { IFieldComponentProps } from "@rowy/form-builder";
|
|
|
|
import { useAppContext } from "@src/contexts/AppContext";
|
|
|
|
type customizationOptions = "allRead" | "authRead" | "subcollections" | "user";
|
|
|
|
export interface ISuggestedRulesProps extends IFieldComponentProps {}
|
|
|
|
export default function SuggestedRules({
|
|
useFormMethods: { control },
|
|
label,
|
|
}: ISuggestedRulesProps) {
|
|
const { projectId } = useAppContext();
|
|
|
|
const watched = useWatch({ control, name: ["collection", "roles"] } as any);
|
|
const [collection, roles] = Array.isArray(watched) ? watched : [];
|
|
|
|
const [customized, setCustomized] = useState<boolean>(false);
|
|
const [customizations, setCustomizations] = useState<customizationOptions[]>(
|
|
[]
|
|
);
|
|
const handleChange =
|
|
(option: customizationOptions) =>
|
|
(e: React.ChangeEvent<HTMLInputElement>) =>
|
|
setCustomizations((prev) => {
|
|
const set = new Set(prev || []);
|
|
if (e.target.checked) set.add(option);
|
|
else set.delete(option);
|
|
return Array.from(set);
|
|
});
|
|
|
|
const generatedRules = `match /${collection}/{${
|
|
customizations.includes("subcollections") ? "document=**" : "docId"
|
|
}} {
|
|
allow read, write: if hasAnyRole(${JSON.stringify(roles)});${
|
|
customizations.includes("allRead")
|
|
? "\n allow read: if true;"
|
|
: customizations.includes("authRead")
|
|
? "\n allow read: if request.auth != null;"
|
|
: ""
|
|
}${
|
|
customizations.includes("user")
|
|
? `\n
|
|
allow create: if request.auth != null;
|
|
allow get, update, delete: if isDocOwner(userId);`
|
|
: ""
|
|
}
|
|
}`;
|
|
|
|
return (
|
|
<>
|
|
<InputLabel sx={{ mb: 0.5, ml: 0.25 }}>{label}</InputLabel>
|
|
<pre style={{ margin: 0, userSelect: "all", whiteSpace: "pre-wrap" }}>
|
|
{generatedRules}
|
|
</pre>
|
|
|
|
<Collapse in={customized}>
|
|
<Grid container>
|
|
<Grid item xs={12} sm={6}>
|
|
<FormControlLabel
|
|
control={
|
|
<Checkbox
|
|
checked={customizations.includes("allRead")}
|
|
onChange={handleChange("allRead")}
|
|
/>
|
|
}
|
|
label="Anyone can read"
|
|
/>
|
|
</Grid>
|
|
<Grid item xs={12} sm={6}>
|
|
<FormControlLabel
|
|
control={
|
|
<Checkbox
|
|
checked={customizations.includes("authRead")}
|
|
onChange={handleChange("authRead")}
|
|
/>
|
|
}
|
|
label="All signed-in users can read"
|
|
/>
|
|
</Grid>
|
|
<Grid item xs={12} sm={6}>
|
|
<FormControlLabel
|
|
control={
|
|
<Checkbox
|
|
checked={customizations.includes("user")}
|
|
onChange={handleChange("user")}
|
|
/>
|
|
}
|
|
label="Users can create and edit docs"
|
|
/>
|
|
</Grid>
|
|
<Grid item xs={12} sm={6}>
|
|
<FormControlLabel
|
|
control={
|
|
<Checkbox
|
|
checked={customizations.includes("subcollections")}
|
|
onChange={handleChange("subcollections")}
|
|
/>
|
|
}
|
|
label="Same rules for all subcollections"
|
|
/>
|
|
</Grid>
|
|
</Grid>
|
|
</Collapse>
|
|
|
|
<Grid container spacing={1} style={{ marginTop: 0 }}>
|
|
{!customized && (
|
|
<Grid item>
|
|
<Button onClick={() => setCustomized(true)}>Customize…</Button>
|
|
</Grid>
|
|
)}
|
|
<Grid item>
|
|
<Button onClick={() => navigator.clipboard.writeText(generatedRules)}>
|
|
Copy to clipboard
|
|
</Button>
|
|
</Grid>
|
|
<Grid item>
|
|
<Button
|
|
href={`https://console.firebase.google.com/u/0/project/${projectId}/firestore/rules`}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
>
|
|
Set rules in Firebase Console
|
|
<InlineOpenInNewIcon />
|
|
</Button>
|
|
</Grid>
|
|
</Grid>
|
|
</>
|
|
);
|
|
}
|