mirror of
https://github.com/fosrl/pangolin.git
synced 2026-08-06 12:41:25 +02:00
support uploading csv or txt to sudo commands and groups
This commit is contained in:
+160
-3
@@ -15,10 +15,20 @@ import {
|
||||
OptionSelect,
|
||||
type OptionSelectOption
|
||||
} from "@app/components/OptionSelect";
|
||||
import { TextFileImportDialog } from "@app/components/TextFileImportDialog";
|
||||
import { useEnvContext } from "@app/hooks/useEnvContext";
|
||||
import { usePaidStatus } from "@app/hooks/usePaidStatus";
|
||||
import { toast } from "@app/hooks/useToast";
|
||||
import { cn } from "@app/lib/cn";
|
||||
import {
|
||||
getTextImportFileType,
|
||||
isSupportedTextImportFile,
|
||||
parseTextFileItems,
|
||||
readFileAsText,
|
||||
type TextImportFileType
|
||||
} from "@app/lib/roleFormTextImport";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { useEffect } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { z } from "zod";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
@@ -98,6 +108,15 @@ type RoleFormProps = {
|
||||
formId?: string;
|
||||
};
|
||||
|
||||
type RoleTextImportField = "sshSudoCommands" | "sshUnixGroups";
|
||||
|
||||
type PendingTextImport = {
|
||||
field: RoleTextImportField;
|
||||
fileName: string;
|
||||
fileType: TextImportFileType;
|
||||
rawContent: string;
|
||||
};
|
||||
|
||||
export function RoleForm({
|
||||
variant,
|
||||
role,
|
||||
@@ -187,6 +206,10 @@ export function RoleForm({
|
||||
const sshDisabled = !isPaidUser(tierMatrix.advancedPrivateResources);
|
||||
const sshSudoMode = form.watch("sshSudoMode");
|
||||
const isAdminRole = variant === "edit" && role?.isAdmin === true;
|
||||
const [pendingImport, setPendingImport] =
|
||||
useState<PendingTextImport | null>(null);
|
||||
const [dragOverField, setDragOverField] =
|
||||
useState<RoleTextImportField | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (sshDisabled) {
|
||||
@@ -194,6 +217,78 @@ export function RoleForm({
|
||||
}
|
||||
}, [sshDisabled, form]);
|
||||
|
||||
async function handleFileDrop(
|
||||
file: File,
|
||||
field: RoleTextImportField
|
||||
): Promise<void> {
|
||||
if (!isSupportedTextImportFile(file)) {
|
||||
toast({
|
||||
variant: "destructive",
|
||||
title: t("roleTextImportInvalidFile"),
|
||||
description: t("roleTextImportInvalidFileDescription")
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const fileType = getTextImportFileType(file);
|
||||
if (!fileType) return;
|
||||
|
||||
const rawContent = await readFileAsText(file);
|
||||
const parser =
|
||||
field === "sshSudoCommands" ? parseSudoCommands : parseUnixGroups;
|
||||
const items = parseTextFileItems({
|
||||
content: rawContent,
|
||||
fileType,
|
||||
skipHeader: false,
|
||||
parser
|
||||
});
|
||||
|
||||
if (items.length === 0) {
|
||||
toast({
|
||||
variant: "destructive",
|
||||
title: t("roleTextImportEmpty"),
|
||||
description: t("roleTextImportEmptyDescription")
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
setPendingImport({
|
||||
field,
|
||||
fileName: file.name,
|
||||
fileType,
|
||||
rawContent
|
||||
});
|
||||
}
|
||||
|
||||
function getTextImportDropHandlers(field: RoleTextImportField) {
|
||||
return {
|
||||
onDragOver: (event: React.DragEvent<HTMLTextAreaElement>) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
if (!sshDisabled) {
|
||||
setDragOverField(field);
|
||||
}
|
||||
},
|
||||
onDragLeave: (event: React.DragEvent<HTMLTextAreaElement>) => {
|
||||
event.preventDefault();
|
||||
setDragOverField((current) =>
|
||||
current === field ? null : current
|
||||
);
|
||||
},
|
||||
onDrop: (event: React.DragEvent<HTMLTextAreaElement>) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
setDragOverField(null);
|
||||
if (sshDisabled) return;
|
||||
|
||||
const file = event.dataTransfer.files[0];
|
||||
if (file) {
|
||||
void handleFileDrop(file, field);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return (
|
||||
<Form {...form}>
|
||||
<form
|
||||
@@ -443,8 +538,23 @@ export function RoleForm({
|
||||
<FormControl>
|
||||
<Textarea
|
||||
{...field}
|
||||
{...getTextImportDropHandlers(
|
||||
"sshSudoCommands"
|
||||
)}
|
||||
placeholder={
|
||||
sshDisabled
|
||||
? undefined
|
||||
: t(
|
||||
"roleTextFieldPlaceholder"
|
||||
)
|
||||
}
|
||||
disabled={sshDisabled}
|
||||
className="h-20 min-h-20"
|
||||
className={cn(
|
||||
"h-20 min-h-20",
|
||||
dragOverField ===
|
||||
"sshSudoCommands" &&
|
||||
"border-primary"
|
||||
)}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormDescription>
|
||||
@@ -469,8 +579,23 @@ export function RoleForm({
|
||||
<FormControl>
|
||||
<Textarea
|
||||
{...field}
|
||||
{...getTextImportDropHandlers(
|
||||
"sshUnixGroups"
|
||||
)}
|
||||
placeholder={
|
||||
sshDisabled
|
||||
? undefined
|
||||
: t(
|
||||
"roleTextFieldPlaceholder"
|
||||
)
|
||||
}
|
||||
disabled={sshDisabled}
|
||||
className="h-20 min-h-20"
|
||||
className={cn(
|
||||
"h-20 min-h-20",
|
||||
dragOverField ===
|
||||
"sshUnixGroups" &&
|
||||
"border-primary"
|
||||
)}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormDescription>
|
||||
@@ -521,6 +646,38 @@ export function RoleForm({
|
||||
</HorizontalTabs>
|
||||
)}
|
||||
</form>
|
||||
{pendingImport && (
|
||||
<TextFileImportDialog
|
||||
key={`${pendingImport.field}-${pendingImport.fileName}`}
|
||||
open={true}
|
||||
onOpenChange={(open) => {
|
||||
if (!open) {
|
||||
setPendingImport(null);
|
||||
}
|
||||
}}
|
||||
fileName={pendingImport.fileName}
|
||||
fileType={pendingImport.fileType}
|
||||
rawContent={pendingImport.rawContent}
|
||||
currentValue={form.watch(pendingImport.field) ?? ""}
|
||||
fieldLabel={
|
||||
pendingImport.field === "sshSudoCommands"
|
||||
? t("sshSudoCommands")
|
||||
: t("sshUnixGroups")
|
||||
}
|
||||
parser={
|
||||
pendingImport.field === "sshSudoCommands"
|
||||
? parseSudoCommands
|
||||
: parseUnixGroups
|
||||
}
|
||||
onConfirm={(value) => {
|
||||
form.setValue(pendingImport.field, value, {
|
||||
shouldDirty: true,
|
||||
shouldValidate: true
|
||||
});
|
||||
setPendingImport(null);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user