Initial commit: Dockerized, Postgres, CI/CD pipeline
Some checks failed
Build and Deploy / build-and-deploy (push) Failing after 3m36s
Some checks failed
Build and Deploy / build-and-deploy (push) Failing after 3m36s
This commit is contained in:
2
backend/.dockerignore
Normal file
2
backend/.dockerignore
Normal file
@@ -0,0 +1,2 @@
|
||||
node_modules
|
||||
npm-debug.log
|
||||
7
backend/Dockerfile
Normal file
7
backend/Dockerfile
Normal file
@@ -0,0 +1,7 @@
|
||||
FROM node:20-alpine
|
||||
WORKDIR /app
|
||||
COPY package*.json ./
|
||||
RUN npm install
|
||||
COPY . .
|
||||
EXPOSE 3004
|
||||
CMD ["npm", "start"]
|
||||
124
backend/index.js
Normal file
124
backend/index.js
Normal file
@@ -0,0 +1,124 @@
|
||||
const express = require('express');
|
||||
const cors = require('cors');
|
||||
const bodyParser = require('body-parser');
|
||||
const { Pool } = require('pg');
|
||||
require('dotenv').config();
|
||||
|
||||
const app = express();
|
||||
const PORT = process.env.PORT || 3004;
|
||||
const API_KEY = process.env.API_KEY || "nexstar_secret_key_123";
|
||||
|
||||
app.use(cors());
|
||||
app.use(bodyParser.json());
|
||||
|
||||
// PostgreSQL Connection Pool
|
||||
const pool = new Pool({
|
||||
connectionString: process.env.DATABASE_URL || 'postgres://graphuser:graphpassword@localhost:5432/graphdb',
|
||||
});
|
||||
|
||||
// Initialize Database Table
|
||||
const initDB = async () => {
|
||||
try {
|
||||
await pool.query(`
|
||||
CREATE TABLE IF NOT EXISTS orders (
|
||||
id SERIAL PRIMARY KEY,
|
||||
cliente_nome VARCHAR(255),
|
||||
data_pedido VARCHAR(50),
|
||||
valor_pedido NUMERIC(10, 2),
|
||||
produto_id VARCHAR(100),
|
||||
produto_descricao TEXT,
|
||||
quantidade INTEGER,
|
||||
valor_unitario NUMERIC(10, 5),
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
`);
|
||||
console.log("Database initialized successfully.");
|
||||
} catch (err) {
|
||||
console.error("Failed to initialize database:", err);
|
||||
}
|
||||
};
|
||||
|
||||
initDB();
|
||||
|
||||
// Middleware for Security
|
||||
const authenticate = (req, res, next) => {
|
||||
const apiKey = req.headers['x-api-key'];
|
||||
if (apiKey === API_KEY) {
|
||||
next();
|
||||
} else {
|
||||
res.status(401).json({ error: 'Unauthorized: Invalid API Key' });
|
||||
}
|
||||
};
|
||||
|
||||
// Helper to format rows to match the old JSON structure for the frontend
|
||||
const formatRow = (row) => ({
|
||||
Nome_Cliente: row.cliente_nome,
|
||||
Data_Pedido: row.data_pedido,
|
||||
Valor_Pedido: parseFloat(row.valor_pedido),
|
||||
ID_Produto: row.produto_id,
|
||||
Descricao_Produto: row.produto_descricao,
|
||||
Quantidade: row.quantidade,
|
||||
Valor_Unitario: parseFloat(row.valor_unitario)
|
||||
});
|
||||
|
||||
// GET data (for the frontend)
|
||||
app.get('/api/data', async (req, res) => {
|
||||
try {
|
||||
const result = await pool.query('SELECT * FROM orders ORDER BY id ASC');
|
||||
const formattedData = result.rows.map(formatRow);
|
||||
res.json(formattedData);
|
||||
} catch (error) {
|
||||
console.error("Error fetching data:", error);
|
||||
res.status(500).json({ error: 'Internal Server Error' });
|
||||
}
|
||||
});
|
||||
|
||||
// POST data (for n8n)
|
||||
app.post('/api/data', async (req, res) => {
|
||||
// Respond IMMEDIATELY to prevent slowing down n8n / WhatsApp flows
|
||||
res.status(201).json({ message: 'Data received, processing in background' });
|
||||
|
||||
const newData = req.body;
|
||||
const payload = Array.isArray(newData) ? newData : [newData];
|
||||
|
||||
// Process asynchronously
|
||||
(async () => {
|
||||
const client = await pool.connect();
|
||||
try {
|
||||
await client.query('BEGIN');
|
||||
|
||||
const insertQuery = `
|
||||
INSERT INTO orders (
|
||||
cliente_nome, data_pedido, valor_pedido,
|
||||
produto_id, produto_descricao, quantidade, valor_unitario
|
||||
) VALUES ($1, $2, $3, $4, $5, $6, $7)
|
||||
`;
|
||||
|
||||
for (const item of payload) {
|
||||
// Handle potential missing fields gracefully
|
||||
const values = [
|
||||
item.Nome_Cliente || 'Unknown',
|
||||
item.Data_Pedido || '',
|
||||
item.Valor_Pedido || 0,
|
||||
item.ID_Produto || '',
|
||||
item.Descricao_Produto || '',
|
||||
item.Quantidade || 0,
|
||||
item.Valor_Unitario || 0
|
||||
];
|
||||
await client.query(insertQuery, values);
|
||||
}
|
||||
|
||||
await client.query('COMMIT');
|
||||
} catch (error) {
|
||||
await client.query('ROLLBACK');
|
||||
console.error("Database insert error:", error);
|
||||
} finally {
|
||||
client.release();
|
||||
}
|
||||
})();
|
||||
});
|
||||
|
||||
app.listen(PORT, '0.0.0.0', () => {
|
||||
console.log(\`Nexstar Backend running at http://localhost:\${PORT}\`);
|
||||
console.log(\`Endpoint for n8n: POST http://localhost:\${PORT}/api/data\`);
|
||||
});
|
||||
1057
backend/package-lock.json
generated
Normal file
1057
backend/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
22
backend/package.json
Normal file
22
backend/package.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "backend",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"start": "node index.js",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"type": "commonjs",
|
||||
"dependencies": {
|
||||
"body-parser": "^2.2.2",
|
||||
"cors": "^2.8.6",
|
||||
"dotenv": "^17.4.2",
|
||||
"express": "^5.2.1",
|
||||
"fs-extra": "^11.3.4",
|
||||
"pg": "^8.20.0"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user