Issue with Adding Filter: Required Fields Not Recognized

I’m working on a firewall management web application where I can add various filters. I have set up the backend using Node.js and Express, and I’m using an external API to manage the firewall rules and filters.

Here are the details of my setup:

Backend: Node.js with Express.
Frontend: HTML, JavaScript.
External API: [API documentation](https://apidoc.dsh.gg).

Problem

When I try to add a new filter using the frontend form, I receive an error indicating that some required fields are not recognized, even though they are included in the request.

Error Message:


`{ status: "Internal Server Error", statusCode: 500, message: "Filter could not be added, error: Field 'port' does not exist but is required!nField 'max_conn_pps' does not exist but is required!" }
`

Backend Code (server.js)

const express = require('express');
const axios = require('axios');
const cors = require('cors');
const path = require('path');

const app = express();
const port = 3000;

const apiUrl = 'https://api.dsh.gg/api/v2';
const token = 'DSH-53526-dUSam7bqCoNKWUocLFuGf2ob6Qkuw8CjTUP2XQhAhqxLZqjdzEm6v3CS3CWkQ2kaAJB6RFXTCU8kw5owfadThud3TxLs3AZfXM2MAWUxAB7VQs8vDxm8w6QqdTgLvY7r';
const ipAddressForRulesAndFilters = '5.252.100.142'; // Existing IP address for rules and filters
const ipAddressForAttackHistory = '5.252.101.219'; // New IP address for attack history

app.use(cors());
app.use(express.json());
app.use(express.static('public')); // Serve static files from the "public" directory

// Log each request to the server for debugging
app.use((req, res, next) => {
    console.log(`${req.method} request for '${req.url}'`);
    next();
});

// Serve the main page
app.get('/', (req, res) => {
    res.sendFile(path.join(__dirname, 'public', 'index.html'));
});

// Serve the rules page
app.get('/rules', (req, res) => {
    res.sendFile(path.join(__dirname, 'public', 'rules.html'));
});

// Serve the filters page
app.get('/filters', (req, res) => {
    res.sendFile(path.join(__dirname, 'public', 'filters.html'));
});

// Serve the attacks page
app.get('/attacks', (req, res) => {
    res.sendFile(path.join(__dirname, 'public', 'attacks.html'));
});

// Route to fetch attack history
app.get('/api/attacks', async (req, res) => {
    try {
        const response = await axios.get(`${apiUrl}/protection/incidents/${ipAddressForAttackHistory}`, {
            headers: {
                'x-token': token
            }
        });
        console.log('Attack history response data:', response.data); // Debug log
        res.json(response.data);
    } catch (error) {
        console.error('Error fetching attack history:', error);
        res.status(500).json({ error: error.response ? error.response.data : error.message });
    }
});

// Route to list firewall rules
app.get('/api/firewall/rules', async (req, res) => {
    try {
        const response = await axios.get(`${apiUrl}/protection/rules/${ipAddressForRulesAndFilters}`, {
            headers: {
                'x-token': token
            }
        });
        res.json(response.data);
    } catch (error) {
        console.error('Error fetching rules:', error);
        res.status(500).json({ error: error.response ? error.response.data : error.message });
    }
});

// Route to list filters
app.get('/api/firewall/filters', async (req, res) => {
    try {
        const response = await axios.get(`${apiUrl}/protection/filters/${ipAddressForRulesAndFilters}`, {
            headers: {
                'x-token': token
            }
        });
        res.json(response.data);
    } catch (error) {
        console.error('Error fetching filters:', error);
        res.status(500).json({ error: error.response ? error.response.data : error.message });
    }
});

// Route to fetch available filters
app.get('/api/available-filters', async (req, res) => {
    try {
        const response = await axios.get(`${apiUrl}/protection/filters/available`, {
            headers: {
                'x-token': token
            }
        });
        res.json(response.data);
    } catch (error) {
        console.error('Error fetching available filters:', error);
        res.status(500).json({ error: error.response ? error.response.data : error.message });
    }
});

// Route to add a new filter
app.post('/api/firewall/filters', async (req, res) => {
    const { type, settings } = req.body;
    try {
        const response = await axios.post(`${apiUrl}/protection/filters/${ipAddressForRulesAndFilters}`, {
            type,
            settings
        }, {
            headers: {
                'x-token': token
            }
        });
        res.json(response.data);
    } catch (error) {
        console.error('Error adding filter:', error);
        res.status(500).json({ error: error.response ? error.response.data : error.message });
    }
});

// Route to add a new firewall rule
app.post('/api/firewall/rules', async (req, res) => {
    const rule = req.body;
    try {
        const response = await axios.post(`${apiUrl}/protection/rules/${ipAddressForRulesAndFilters}`, rule, {
            headers: {
                'x-token': token,
                'Content-Type': 'application/json'
            }
        });
        res.json(response.data);
    } catch (error) {
        console.error('Error adding rule:', error);
        res.status(500).json({ error: error.response ? error.response.data : error.message });
    }
});

// Route to delete a firewall rule by ID
app.delete('/api/firewall/rules/:id', async (req, res) => {
    const ruleId = req.params.id;
    try {
        const response = await axios.delete(`${apiUrl}/protection/rules/${ipAddressForRulesAndFilters}/${ruleId}`, {
            headers: {
                'x-token': token
            }
        });
        res.json(response.data);
    } catch (error) {
        console.error('Error deleting rule:', error);
        res.status(500).json({ error: error.response ? error.response.data : error.message });
    }
});

// Route to delete a filter by ID
app.delete('/api/firewall/filters/:id', async (req, res) => {
    const filterId = req.params.id;
    try {
        const response = await axios.delete(`${apiUrl}/protection/filters/${ipAddressForRulesAndFilters}/${filterId}`, {
            headers: {
                'x-token': token
            }
        });
        res.json(response.data);
    } catch (error) {
        console.error('Error deleting filter:', error);
        res.status(500).json({ error: error.response ? error.response.data : error.message });
    }
});

app.listen(port, () => {
    console.log(`Server running on http://localhost:${port}`);
});


Frontend Code (filters.js)

document.addEventListener('DOMContentLoaded', () => {
    fetchFilters();
    fetchAvailableFilters();
});

let filtersPerPage = 10;
let currentPage = 1;
let filtersData = [];
let availableFilters = [];
const managedIPAddress = '5.252.100.142';

async function fetchFilters() {
    try {
        const response = await fetch('/api/firewall/filters');
        const data = await response.json();
        filtersData = data.items || [];
        displayFilters();
        setupPagination();
    } catch (error) {
        console.error('Error fetching filters:', error);
    }
}

function displayFilters() {
    const filtersTable = document.getElementById('filtersTable').querySelector('tbody');
    filtersTable.innerHTML = '';

    const start = (currentPage - 1) * filtersPerPage;
    const end = filtersPerPage === 'all' ? filtersData.length : start + filtersPerPage;
    const filtersToDisplay = filtersData.slice(start, end);

    filtersToDisplay.forEach(filter => {
        const row = document.createElement('tr');

        const settings = filter.settings 
            ? Object.keys(filter.settings).map(key => {
                if (filter.settings[key] !== undefined && filter.settings[key] !== null) {
                    return `<span class="settings-badge">${key.toUpperCase()}: ${filter.settings[key]}</span>`;
                }
                return '';
              }).join('')
            : 'No settings available';

        row.innerHTML = `
            <td><input type="checkbox" class="filter-checkbox" data-id="${filter.id}" onclick="toggleDeleteCheckedButton()"></td>
            <td>${filter.id}</td>
            <td>${filter.filter_type}</td>
            <td>${settings}</td>
            <td><button class="delete-button" onclick="deleteFilter(${filter.id})">Delete</button></td>
        `;
        filtersTable.appendChild(row);
    });
}

function setupPagination() {
    const pagination = document.getElementById('pagination');
    pagination.innerHTML = '';

    if (filtersPerPage === 'all') return;

    const pageCount = Math.ceil(filtersData.length / filtersPerPage);
    for (let i = 1; i <= pageCount; i++) {
        const button = document.createElement('button');
        button.textContent = i;
        button.classList.toggle('active', i === currentPage);
        button.addEventListener('click', () => {
            currentPage = i;
            displayFilters();
            setupPagination();
        });
        pagination.appendChild(button);
    }
}

async function fetchAvailableFilters() {
    try {
        const response = await fetch('/api/available-filters', {
            headers: { 'x-token': 'YOUR_TOKEN_HERE' }
        });
        if (response.ok) {
            const data = await response.json();
            availableFilters = data.items || [];
            populateFilterTypeOptions();
        } else {
            console.error('Error fetching available filters:', response.statusText);
        }
    } catch (error) {
        console.error('Error fetching available filters:', error);
    }
}

function populateFilterTypeOptions() {
    const filterTypeSelect = document.getElementById('filterType');
    filterTypeSelect.innerHTML = '';

    availableFilters.forEach(filter => {
        const option = document.createElement('option');
        option.value = filter.name;
        option.textContent = filter.label;
        filterTypeSelect.appendChild(option);
    });

    filterTypeSelect.addEventListener('change', populateFilterFields);
}

function populateFilterFields() {
    const selectedFilterName = document.getElementById('filterType').value;
    const selectedFilter = availableFilters.find(filter => filter.name === selectedFilterName);
    const filterFieldsContainer = document.getElementById('filterFields');
    filterFieldsContainer.innerHTML = '';

    if (selectedFilter && selectedFilter.fields) {
        selectedFilter.fields.forEach(field => {
            const formGroup = document.createElement('div');
            formGroup.classList.add('form-group');

            const label = document.createElement('label');
            label.textContent = field.label;
            formGroup.appendChild(label);

            let input;
            switch (field.value.type) {
                case 'cidr':
                case 'ip':
                    input = document.createElement('input');
                    input.type = 'text';
                    input.placeholder = field.example || '';
                    input.value = managedIPAddress;
                    break;
                case 'port':
                    input = document.createElement('input');
                    input.type = 'text';
                    input.placeholder = field.example || '';
                    break;
                case 'bool':
                    input = document.createElement('input');
                    input.type = 'checkbox';
                    input.checked = field.value.default || false;
                    break;
                case 'integer':
                    input = document.createElement('input');
                    input.type = 'number';
                    input.min = field.value.min || 0;
                    input.max = field.value.max || 1000000000;
                    break;
                case 'select':
                    input = document.createElement('select');
                    field.value.options.forEach(option => {
                        const selectOption = document.createElement('option');
                        selectOption.value = option.value;
                        selectOption.textContent = option.label;
                        input.appendChild(selectOption);
                    });
                    break;
            }

            input.id = field.name;
            input.classList.add('filter-field');
            formGroup.appendChild(input);
            filterFieldsContainer.appendChild(formGroup);
        });
    }
}

async function addFilter() {
    const selectedFilterName = document.getElementById('filterType').value;
    const selectedFilter = availableFilters.find(filter => filter.name === selectedFilterName);
    const filterFields = document.querySelectorAll('.filter-field');

    const filterData = {};
    filterFields.forEach(field => {
        if (field.type === 'checkbox') {
            filterData[field.id] = field.checked;
        } else {
            filterData[field.id] = field.value;
        }
    });

    // Check if all required fields are present
    const missingFields = selectedFilter.fields.filter(field => !filterData[field.name]);
    if (missingFields.length > 0) {
        alert(`The following fields are required: ${missingFields.map(field => field.label).join(', ')}`);
        return;
    }

    console.log('Submitting filter data:', { type: selectedFilter.name, settings: filterData });

    try {
        const response = await fetch('/api/firewall/filters', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'x-token': 'YOUR_TOKEN_HERE'
            },
            body: JSON.stringify({
                type: selectedFilter.name,
                settings: filterData
            })
        });

        const data = await response.json();
        if (response.ok) {
            console.log('Filter added successfully:', data);
            closeAddFilterModal();
            fetchFilters();
        } else {
            console.error('Error adding filter:', data);
        }
    } catch (error) {
        console.error('Error adding filter:', error);
    }
}

function openAddFilterModal() {
    document.getElementById('addFilterModal').style.display = 'block';
}

function closeAddFilterModal() {
    document.getElementById('addFilterModal').style.display = 'none';
}

Additional Information:

The required fields for each filter type are included in the response from the /api/available-filters endpoint.
The frontend dynamically generates the form based on the selected filter type and its required fields.

Question

Why am I receiving errors that required fields are missing, even though they are present in the request? How can I resolve this issue to successfully add filters?

What did you try and what were you expecting?

I implemented a dynamic form in the frontend that retrieves the available filter types and their required fields from the API. When a filter type is selected, the form is populated with the appropriate fields, and I fill in the necessary values.

Backend:
    I set up endpoints in server.js to handle fetching available filters, fetching existing filters, and adding new filters using the external API.
    I used Axios to make the API requests with the required headers, including the API token.

Frontend:
    The fetchAvailableFilters function retrieves the available filter types and their required fields.
    The populateFilterFields function dynamically creates form inputs based on the selected filter type.
    The addFilter function collects the form data and sends a POST request to the backend, which forwards it to the external API.

I expected that when I submit the form with all the required fields filled out, the filter would be successfully added. However, I am encountering an error that states certain required fields are missing, even though they are included in the request payload.

I verified that the request payload contains the required fields and values as specified by the API documentation, but the error persists.

Here is a sample of what the request payload looks like when I attempt to add a filter:

{
type: “tcp_symmetric”,
settings: {
addr: “5.252.100.142”,
port: “587”,
max_conn_pps: “1000”
}
}

Despite this, I receive the following error response:

{ status: “Internal Server Error”, statusCode: 500, message: “Filter could not be added, error: Field ‘port’ does not exist but is required!nField ‘max_conn_pps’ does not exist but is required!” }

I am looking for guidance on what might be causing this issue and how to ensure the required fields are recognized and processed correctly by the API.

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật