Fix jwt-token
This commit is contained in:
@@ -462,7 +462,9 @@ const CreateAlertRuleModal = ({ onClose, onSave }) => {
|
|||||||
device_ids: [],
|
device_ids: [],
|
||||||
drone_types: [],
|
drone_types: [],
|
||||||
min_rssi: '',
|
min_rssi: '',
|
||||||
max_rssi: ''
|
max_rssi: '',
|
||||||
|
sms_phone_number: '',
|
||||||
|
webhook_url: ''
|
||||||
});
|
});
|
||||||
const [saving, setSaving] = useState(false);
|
const [saving, setSaving] = useState(false);
|
||||||
const [devices, setDevices] = useState([]);
|
const [devices, setDevices] = useState([]);
|
||||||
@@ -503,6 +505,11 @@ const CreateAlertRuleModal = ({ onClose, onSave }) => {
|
|||||||
if (!payload.device_ids || payload.device_ids.length === 0) payload.device_ids = null;
|
if (!payload.device_ids || payload.device_ids.length === 0) payload.device_ids = null;
|
||||||
if (!payload.drone_types || payload.drone_types.length === 0) payload.drone_types = null;
|
if (!payload.drone_types || payload.drone_types.length === 0) payload.drone_types = null;
|
||||||
|
|
||||||
|
// Only include webhook_url if webhook channel is selected
|
||||||
|
if (!payload.alert_channels || !payload.alert_channels.includes('webhook')) {
|
||||||
|
delete payload.webhook_url;
|
||||||
|
}
|
||||||
|
|
||||||
await api.post('/alerts/rules', payload);
|
await api.post('/alerts/rules', payload);
|
||||||
onSave();
|
onSave();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -733,6 +740,46 @@ const CreateAlertRuleModal = ({ onClose, onSave }) => {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Conditional channel-specific fields */}
|
||||||
|
{formData.alert_channels.includes('sms') && (
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||||
|
SMS Phone Number
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="tel"
|
||||||
|
name="sms_phone_number"
|
||||||
|
placeholder="+1234567890"
|
||||||
|
className="w-full border border-gray-300 rounded-md px-3 py-2 focus:ring-primary-500 focus:border-primary-500"
|
||||||
|
value={formData.sms_phone_number || ''}
|
||||||
|
onChange={handleChange}
|
||||||
|
/>
|
||||||
|
<div className="text-xs text-gray-500 mt-1">
|
||||||
|
Include country code (e.g., +1 for US)
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{formData.alert_channels.includes('webhook') && (
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||||
|
Webhook URL *
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="url"
|
||||||
|
name="webhook_url"
|
||||||
|
placeholder="https://your-webhook-endpoint.com/alerts"
|
||||||
|
required={formData.alert_channels.includes('webhook')}
|
||||||
|
className="w-full border border-gray-300 rounded-md px-3 py-2 focus:ring-primary-500 focus:border-primary-500"
|
||||||
|
value={formData.webhook_url || ''}
|
||||||
|
onChange={handleChange}
|
||||||
|
/>
|
||||||
|
<div className="text-xs text-gray-500 mt-1">
|
||||||
|
Must be a valid HTTPS URL
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ const alertRuleSchema = Joi.object({
|
|||||||
min_detections: Joi.number().integer().min(1).default(1),
|
min_detections: Joi.number().integer().min(1).default(1),
|
||||||
cooldown_period: Joi.number().integer().min(0).default(600),
|
cooldown_period: Joi.number().integer().min(0).default(600),
|
||||||
alert_channels: Joi.array().items(Joi.string().valid('sms', 'email', 'webhook')).default(['sms']),
|
alert_channels: Joi.array().items(Joi.string().valid('sms', 'email', 'webhook')).default(['sms']),
|
||||||
webhook_url: Joi.string().uri().optional(),
|
webhook_url: Joi.string().uri().allow('').optional(),
|
||||||
active_hours: Joi.object({
|
active_hours: Joi.object({
|
||||||
start: Joi.string().pattern(/^\d{2}:\d{2}$/).optional(),
|
start: Joi.string().pattern(/^\d{2}:\d{2}$/).optional(),
|
||||||
end: Joi.string().pattern(/^\d{2}:\d{2}$/).optional()
|
end: Joi.string().pattern(/^\d{2}:\d{2}$/).optional()
|
||||||
@@ -66,6 +66,21 @@ router.get('/rules', authenticateToken, async (req, res) => {
|
|||||||
// POST /api/alerts/rules - Create new alert rule
|
// POST /api/alerts/rules - Create new alert rule
|
||||||
router.post('/rules', authenticateToken, validateRequest(alertRuleSchema), async (req, res) => {
|
router.post('/rules', authenticateToken, validateRequest(alertRuleSchema), async (req, res) => {
|
||||||
try {
|
try {
|
||||||
|
// Custom validation for webhook URL when webhook channel is selected
|
||||||
|
if (req.body.alert_channels && req.body.alert_channels.includes('webhook')) {
|
||||||
|
if (!req.body.webhook_url || req.body.webhook_url.trim() === '') {
|
||||||
|
return res.status(400).json({
|
||||||
|
success: false,
|
||||||
|
message: 'Validation error',
|
||||||
|
errors: [{
|
||||||
|
field: 'webhook_url',
|
||||||
|
message: 'Webhook URL is required when webhook channel is selected',
|
||||||
|
value: req.body.webhook_url || ''
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const alertRule = await AlertRule.create({
|
const alertRule = await AlertRule.create({
|
||||||
...req.body,
|
...req.body,
|
||||||
user_id: req.user.id
|
user_id: req.user.id
|
||||||
|
|||||||
Reference in New Issue
Block a user