@@ -0,0 +1,29 @@
|
|||||||
|
name: Deploy Node App
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Deploy to VPS
|
||||||
|
uses: appleboy/ssh-action@master
|
||||||
|
|
||||||
|
with:
|
||||||
|
host: ${{ secrets.HOST }}
|
||||||
|
username: ${{ secrets.USERNAME }}
|
||||||
|
key: ${{ secrets.SSH_KEY }}
|
||||||
|
port: ${{ secrets.PORT }}
|
||||||
|
|
||||||
|
script: |
|
||||||
|
cd /root/IR-Node-Setup
|
||||||
|
|
||||||
|
git pull origin main
|
||||||
|
|
||||||
|
npm install
|
||||||
|
|
||||||
|
pm2 restart IR-Node-Setup
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
/node_modules
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
const axios = require('axios');
|
||||||
|
const { GoogleGenerativeAI } = require('@google/generative-ai');
|
||||||
|
const ai = new GoogleGenerativeAI({ apiKey: process.env.GEMINI_API_KEY });
|
||||||
|
|
||||||
|
const fetchWrenData = async (prompt, tenantId) => {
|
||||||
|
try {
|
||||||
|
const url = process.env.WREN_AI_URL || 'http://localhost:5555/api/v1/text-to-sql';
|
||||||
|
|
||||||
|
const response = await axios.post(url,
|
||||||
|
{ prompt: prompt },
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
|
||||||
|
'X-Wren-Session-Properties': `@user_org_id=${tenantId}`,
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Wren AI Integration Error:', error.message);
|
||||||
|
throw new Error('Failed to fetch data payload from Wren AI endpoint');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const generateVegaSchema = async (question, dataArray) => {
|
||||||
|
try {
|
||||||
|
const model = ai.getGenerativeModel({
|
||||||
|
model: 'gemini-2.5-flash',
|
||||||
|
generationConfig: { responseMimeType: 'application/json' }
|
||||||
|
});
|
||||||
|
|
||||||
|
const systemPrompt = `You are a data visualization expert. I will provide a user's question and a JSON array of data.
|
||||||
|
Your task is to generate a strictly valid Vega-Lite JSON specification to visualize this data.
|
||||||
|
The data array will be provided to the Vega spec internally. Map the JSON keys to the correct x, y, and color axes.
|
||||||
|
Choose the best chart type (bar, line, arc) based on the question.`;
|
||||||
|
|
||||||
|
const userPrompt = `User Question: "${question}"\nData JSON: ${JSON.stringify(dataArray)}`;
|
||||||
|
|
||||||
|
const result = await model.generateContent({
|
||||||
|
contents: [{ role: 'user', parts: [{ text: `${systemPrompt}\n\n${userPrompt}` }] }]
|
||||||
|
});
|
||||||
|
|
||||||
|
return JSON.parse(result.response.text());
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Gemini Engine Error:', error.message);
|
||||||
|
throw new Error('Failed to transform data architecture into valid Vega-Lite spec');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const handleOrchestration = async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { prompt } = req.body;
|
||||||
|
|
||||||
|
|
||||||
|
const tenant_id = req.user ? req.user.client_id : null;
|
||||||
|
return res.status(200).json("ok");
|
||||||
|
|
||||||
|
if (!prompt) {
|
||||||
|
return res.status(400).json({ error: 'Prompt field is required in request body' });
|
||||||
|
}
|
||||||
|
if (!tenant_id) {
|
||||||
|
return res.status(400).json({ error: 'Tenant context (client_id) missing from auth token' });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const wrenData = await fetchWrenData(prompt, tenant_id);
|
||||||
|
|
||||||
|
|
||||||
|
const vegaSchema = await generateVegaSchema(prompt, wrenData);
|
||||||
|
|
||||||
|
|
||||||
|
if (!vegaSchema || !vegaSchema.$schema) {
|
||||||
|
return res.status(500).json({ error: 'Egress Pipeline Validation Failure: Output missing standard Vega $schema identifier' });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return res.status(200).json(vegaSchema);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('API Gateway Orchestrator Crash:', error.message);
|
||||||
|
return res.status(500).json({ error: error.message });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = { handleOrchestration };
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
const clickhouse = require('../database/clickhouse');
|
||||||
|
const jwt = require('jsonwebtoken');
|
||||||
|
|
||||||
|
const JWT_SECRET = 'secretkey';
|
||||||
|
|
||||||
|
|
||||||
|
const loginUser = async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { username, password } = req.body;
|
||||||
|
|
||||||
|
if (!username || !password) {
|
||||||
|
return res.status(400).json({
|
||||||
|
success: false,
|
||||||
|
message: "Username and password are required"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const resultSet = await clickhouse.query({
|
||||||
|
query: 'SELECT * FROM usertable WHERE username = {user: String} AND password = {pass: String}',
|
||||||
|
query_params: {
|
||||||
|
user: username,
|
||||||
|
pass: password
|
||||||
|
},
|
||||||
|
format: 'JSONEachRow',
|
||||||
|
});
|
||||||
|
|
||||||
|
const rows = await resultSet.json();
|
||||||
|
|
||||||
|
|
||||||
|
if (rows.length === 0) {
|
||||||
|
return res.status(401).json({
|
||||||
|
success: false,
|
||||||
|
message: "Invalid username or password"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = rows[0];
|
||||||
|
|
||||||
|
|
||||||
|
const token = jwt.sign(
|
||||||
|
{
|
||||||
|
client_id: user.client_id,
|
||||||
|
username: user.username
|
||||||
|
},
|
||||||
|
JWT_SECRET,
|
||||||
|
{ expiresIn: '24h' }
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
success: true,
|
||||||
|
message: "Login successful",
|
||||||
|
token: token,
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Login Error:", error);
|
||||||
|
res.status(500).json({
|
||||||
|
success: false,
|
||||||
|
message: "Internal server error",
|
||||||
|
error: error.message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = { loginUser };
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
const { createClient } = require('@clickhouse/client');
|
||||||
|
|
||||||
|
const clickhouse = createClient({
|
||||||
|
url: 'http://172.188.12.194:8123',
|
||||||
|
username: 'default',
|
||||||
|
password: 'dipanshu_k',
|
||||||
|
database: 'userdetails',
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = clickhouse;
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
const express = require('express');
|
||||||
|
const app = express();
|
||||||
|
const port = 3000;
|
||||||
|
const route = require('./route/route.js');
|
||||||
|
const cors = require('cors');
|
||||||
|
|
||||||
|
app.use(cors());
|
||||||
|
app.use(express.static('public'));
|
||||||
|
app.use(express.json());
|
||||||
|
app.use(express.urlencoded({ extended: true }));
|
||||||
|
app.use(route);
|
||||||
|
|
||||||
|
app.listen(port, () => {
|
||||||
|
console.log(`Server is running on http://localhost:${port}`);
|
||||||
|
});
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
const jwt = require('jsonwebtoken');
|
||||||
|
|
||||||
|
const authMiddleware = (req, res, next) => {
|
||||||
|
const JWT_SECRET = 'secretkey';
|
||||||
|
const authHeader = req.headers.authorization;
|
||||||
|
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
||||||
|
return res.status(401).json({ error: 'Unauthorized: Missing or invalid token format' });
|
||||||
|
}
|
||||||
|
const token = authHeader.split(' ')[1];
|
||||||
|
try {
|
||||||
|
const decoded = jwt.verify(token, JWT_SECRET);
|
||||||
|
req.user = decoded;
|
||||||
|
next();
|
||||||
|
} catch (error) {
|
||||||
|
|
||||||
|
return res.status(401).json({ error: 'Unauthorized: Invalid token' });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = authMiddleware;
|
||||||
Generated
+1505
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"name": "ir-node-setup",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"author": "",
|
||||||
|
"type": "commonjs",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
|
"start": "nodemon index.js"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@clickhouse/client": "^1.18.5",
|
||||||
|
"@google/generative-ai": "^0.24.1",
|
||||||
|
"axios": "^1.16.1",
|
||||||
|
"cors": "^2.8.6",
|
||||||
|
"express": "^5.2.1",
|
||||||
|
"jsonwebtoken": "^9.0.3",
|
||||||
|
"nodemon": "^3.1.14"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
const express = require('express');
|
||||||
|
const router = express.Router();
|
||||||
|
const loginUser = require('../controller/loginUser.js');
|
||||||
|
const authMiddleware = require('../middleware/authMiddleware.js');
|
||||||
|
const handleOrchestration = require('../controller/handleOrchestration.js');
|
||||||
|
|
||||||
|
router.post('/loginUser', loginUser.loginUser);
|
||||||
|
router.post('/handleOrchestration', authMiddleware, handleOrchestration.handleOrchestration);
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
Reference in New Issue
Block a user