2026-03-07 13:49:44 +00:00
|
|
|
import EasyModal, { useModal } from "ez-modal-react";
|
2026-03-08 02:33:24 +00:00
|
|
|
import { useState, useEffect } from "react";
|
2026-03-07 13:49:44 +00:00
|
|
|
import Modal from "react-bootstrap/Modal";
|
|
|
|
|
import { Button } from "src/components";
|
2026-03-08 02:33:24 +00:00
|
|
|
import type { WgInterface } from "src/api/backend/wireguard";
|
2026-03-07 13:49:44 +00:00
|
|
|
|
2026-03-08 02:33:24 +00:00
|
|
|
interface WireGuardClientModalProps {
|
|
|
|
|
interfaces: WgInterface[];
|
|
|
|
|
defaultInterfaceId?: number;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const WireGuardClientModal = EasyModal.create(({ interfaces, defaultInterfaceId }: WireGuardClientModalProps) => {
|
2026-03-07 13:49:44 +00:00
|
|
|
const modal = useModal<any>();
|
|
|
|
|
const [name, setName] = useState("");
|
2026-03-08 02:33:24 +00:00
|
|
|
const [selectedInterfaceId, setSelectedInterfaceId] = useState<number>(0);
|
2026-03-10 06:09:51 +00:00
|
|
|
const [storageLimitMb, setStorageLimitMb] = useState<number>(500);
|
|
|
|
|
const [txLimit, setTxLimit] = useState<number>(0);
|
|
|
|
|
const [rxLimit, setRxLimit] = useState<number>(0);
|
2026-03-08 02:33:24 +00:00
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (defaultInterfaceId) {
|
|
|
|
|
setSelectedInterfaceId(defaultInterfaceId);
|
|
|
|
|
} else if (interfaces && interfaces.length > 0) {
|
|
|
|
|
setSelectedInterfaceId(interfaces[0].id);
|
|
|
|
|
}
|
|
|
|
|
}, [interfaces, defaultInterfaceId]);
|
2026-03-07 13:49:44 +00:00
|
|
|
|
|
|
|
|
const handleSubmit = (e: React.FormEvent) => {
|
|
|
|
|
e.preventDefault();
|
2026-03-08 02:33:24 +00:00
|
|
|
if (name.trim() && selectedInterfaceId) {
|
|
|
|
|
modal.resolve({
|
|
|
|
|
name: name.trim(),
|
2026-03-10 06:09:51 +00:00
|
|
|
interface_id: selectedInterfaceId,
|
|
|
|
|
storage_limit_mb: storageLimitMb,
|
|
|
|
|
tx_limit: txLimit,
|
|
|
|
|
rx_limit: rxLimit
|
2026-03-08 02:33:24 +00:00
|
|
|
});
|
2026-03-07 13:49:44 +00:00
|
|
|
modal.hide();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleClose = () => {
|
|
|
|
|
modal.resolve(null);
|
|
|
|
|
modal.hide();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<Modal show={modal.visible} onHide={handleClose} backdrop="static">
|
|
|
|
|
<form onSubmit={(e) => {
|
|
|
|
|
e.stopPropagation();
|
|
|
|
|
handleSubmit(e);
|
|
|
|
|
}}>
|
|
|
|
|
<Modal.Header closeButton>
|
|
|
|
|
<Modal.Title>New WireGuard Client</Modal.Title>
|
|
|
|
|
</Modal.Header>
|
|
|
|
|
<Modal.Body>
|
|
|
|
|
<div className="mb-3">
|
2026-03-08 02:33:24 +00:00
|
|
|
<label htmlFor="wg-client-name" className="form-label required">
|
2026-03-07 13:49:44 +00:00
|
|
|
Client Name
|
|
|
|
|
</label>
|
|
|
|
|
<input
|
|
|
|
|
type="text"
|
|
|
|
|
className="form-control"
|
|
|
|
|
id="wg-client-name"
|
|
|
|
|
placeholder="e.g. My Phone, Laptop, ..."
|
|
|
|
|
value={name}
|
|
|
|
|
onChange={(e) => setName(e.target.value)}
|
|
|
|
|
autoFocus
|
|
|
|
|
required
|
|
|
|
|
/>
|
|
|
|
|
<div className="form-text">
|
|
|
|
|
A friendly name to identify this client.
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2026-03-08 02:33:24 +00:00
|
|
|
|
|
|
|
|
{interfaces && interfaces.length > 0 && (
|
|
|
|
|
<div className="mb-3">
|
|
|
|
|
<label htmlFor="wg-server-select" className="form-label required">
|
|
|
|
|
WireGuard Server
|
|
|
|
|
</label>
|
|
|
|
|
<select
|
|
|
|
|
className="form-select"
|
|
|
|
|
id="wg-server-select"
|
|
|
|
|
value={selectedInterfaceId}
|
|
|
|
|
onChange={(e) => setSelectedInterfaceId(Number(e.target.value))}
|
|
|
|
|
required
|
|
|
|
|
>
|
|
|
|
|
{interfaces.map(iface => (
|
|
|
|
|
<option key={iface.id} value={iface.id}>
|
|
|
|
|
{iface.name} ({iface.ipv4Cidr})
|
|
|
|
|
</option>
|
|
|
|
|
))}
|
|
|
|
|
</select>
|
|
|
|
|
<div className="form-text">
|
|
|
|
|
Select which server this client will connect to.
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
2026-03-10 06:09:51 +00:00
|
|
|
<hr />
|
|
|
|
|
<h5 className="mb-3">Limits & Quotas</h5>
|
|
|
|
|
|
|
|
|
|
<div className="mb-3">
|
|
|
|
|
<label htmlFor="wg-client-storage" className="form-label required">
|
|
|
|
|
Storage Partition Limit (MB)
|
|
|
|
|
</label>
|
|
|
|
|
<input
|
|
|
|
|
type="number"
|
|
|
|
|
className="form-control"
|
|
|
|
|
id="wg-client-storage"
|
|
|
|
|
value={storageLimitMb}
|
|
|
|
|
onChange={(e) => setStorageLimitMb(Number(e.target.value))}
|
|
|
|
|
min="0"
|
|
|
|
|
required
|
|
|
|
|
/>
|
|
|
|
|
<div className="form-text">
|
|
|
|
|
Maximum size of encrypted file storage per client. 0 = Unlimited.
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div className="row">
|
|
|
|
|
<div className="col-md-6 mb-3">
|
|
|
|
|
<label htmlFor="wg-client-tx" className="form-label">
|
|
|
|
|
Upload Bandwidth Limit (Bytes)
|
|
|
|
|
</label>
|
|
|
|
|
<input
|
|
|
|
|
type="number"
|
|
|
|
|
className="form-control"
|
|
|
|
|
id="wg-client-tx"
|
|
|
|
|
value={txLimit}
|
|
|
|
|
onChange={(e) => setTxLimit(Number(e.target.value))}
|
|
|
|
|
min="0"
|
|
|
|
|
required
|
|
|
|
|
/>
|
|
|
|
|
<div className="form-text">Optional. 0 = Unlimited.</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="col-md-6 mb-3">
|
|
|
|
|
<label htmlFor="wg-client-rx" className="form-label">
|
|
|
|
|
Download Bandwidth Limit (Bytes)
|
|
|
|
|
</label>
|
|
|
|
|
<input
|
|
|
|
|
type="number"
|
|
|
|
|
className="form-control"
|
|
|
|
|
id="wg-client-rx"
|
|
|
|
|
value={rxLimit}
|
|
|
|
|
onChange={(e) => setRxLimit(Number(e.target.value))}
|
|
|
|
|
min="0"
|
|
|
|
|
required
|
|
|
|
|
/>
|
|
|
|
|
<div className="form-text">Optional. 0 = Unlimited.</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
2026-03-07 13:49:44 +00:00
|
|
|
</Modal.Body>
|
|
|
|
|
<Modal.Footer>
|
|
|
|
|
<Button data-bs-dismiss="modal" onClick={handleClose}>
|
|
|
|
|
Cancel
|
|
|
|
|
</Button>
|
2026-03-08 02:33:24 +00:00
|
|
|
<Button type="submit" className="ms-auto btn-primary" disabled={!name.trim() || !selectedInterfaceId}>
|
2026-03-07 13:49:44 +00:00
|
|
|
Create Client
|
|
|
|
|
</Button>
|
|
|
|
|
</Modal.Footer>
|
|
|
|
|
</form>
|
|
|
|
|
</Modal>
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
export default WireGuardClientModal;
|