unknownfriend00007 commited on
Commit
fc05a6d
·
verified ·
1 Parent(s): 05e2165

Create server.js

Browse files
Files changed (1) hide show
  1. server.js +132 -0
server.js ADDED
@@ -0,0 +1,132 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const express = require('express');
2
+ const fetch = require('node-fetch');
3
+ const cors = require('cors');
4
+ const rateLimit = require('express-rate-limit');
5
+ require('dotenv').config();
6
+
7
+ const app = express();
8
+
9
+ // --- 1. SECURITY: TRUST PROXY ---
10
+ // Required for Hugging Face to see the Real IP for rate limiting
11
+ app.set('trust proxy', 1);
12
+
13
+ // --- 2. SECURITY: RATE LIMITING ---
14
+ // Limit each IP to 100 requests per 15 minutes
15
+ const limiter = rateLimit({
16
+ windowMs: 15 * 60 * 1000,
17
+ max: 150, // number of max requests
18
+ standardHeaders: true,
19
+ legacyHeaders: false,
20
+ message: { error: "Too many requests, please try again later." }
21
+ });
22
+ app.use(limiter);
23
+
24
+ // --- 3. SECURITY: STRICT CORS ---
25
+ const allowedOrigins = process.env.ALLOWED_ORIGINS ? process.env.ALLOWED_ORIGINS.split(',') : [];
26
+
27
+ app.use(cors({
28
+ origin: function (origin, callback) {
29
+ // Allow requests with no origin (like mobile apps/curl) or if list is empty
30
+ if (!origin || allowedOrigins.length === 0) return callback(null, true);
31
+
32
+ if (allowedOrigins.indexOf(origin) !== -1) {
33
+ callback(null, true);
34
+ } else {
35
+ callback(new Error('Not allowed by CORS'));
36
+ }
37
+ }
38
+ }));
39
+
40
+ app.use(express.json());
41
+
42
+ // --- 4. MULTI-INSTANCE CONFIGURATION ---
43
+ let INSTANCES = [];
44
+ try {
45
+ INSTANCES = JSON.parse(process.env.FLOWISE_INSTANCES || '[]');
46
+ } catch (e) {
47
+ console.error("CRITICAL ERROR: Could not parse FLOWISE_INSTANCES JSON", e);
48
+ }
49
+
50
+ // Master Cache: Maps "bot-name" -> { flowId, instanceUrl, apiKey }
51
+ let flowDirectory = {};
52
+ let lastCacheUpdate = 0;
53
+
54
+ // --- 5. DYNAMIC DISCOVERY ---
55
+ async function refreshFlowDirectory() {
56
+ // Cache valid for 60 seconds
57
+ if (Date.now() - lastCacheUpdate < 60000 && Object.keys(flowDirectory).length > 0) return;
58
+
59
+ console.log(`[System] Scanning ${INSTANCES.length} Flowise Instances...`);
60
+ const newDirectory = {};
61
+
62
+ // Run all fetches in parallel
63
+ const promises = INSTANCES.map(async (inst) => {
64
+ try {
65
+ const res = await fetch(`${inst.url}/api/v1/chatflows`, {
66
+ headers: { 'Authorization': `Bearer ${inst.key}` }
67
+ });
68
+ if(!res.ok) throw new Error(`Status ${res.status}`);
69
+ const flows = await res.json();
70
+
71
+ flows.forEach(flow => {
72
+ const alias = flow.name.toLowerCase().replace(/\s+/g, '-');
73
+ newDirectory[alias] = {
74
+ id: flow.id,
75
+ host: inst.url,
76
+ key: inst.key
77
+ };
78
+ });
79
+ } catch (err) {
80
+ console.error(`[Error] Failed to fetch from ${inst.url}:`, err.message);
81
+ }
82
+ });
83
+
84
+ await Promise.allSettled(promises);
85
+
86
+ flowDirectory = newDirectory;
87
+ lastCacheUpdate = Date.now();
88
+ console.log(`[System] Directory Updated. Serving ${Object.keys(flowDirectory).length} bots.`);
89
+ }
90
+
91
+ // --- 6. THE ROUTE ---
92
+ app.post('/api/v1/prediction/:botName', async (req, res) => {
93
+ const botName = req.params.botName.toLowerCase();
94
+
95
+ await refreshFlowDirectory();
96
+
97
+ const target = flowDirectory[botName];
98
+
99
+ if (!target) {
100
+ lastCacheUpdate = 0;
101
+ await refreshFlowDirectory();
102
+ if (!flowDirectory[botName]) {
103
+ return res.status(404).json({ error: `Bot '${botName}' not found.` });
104
+ }
105
+ }
106
+
107
+ const finalTarget = flowDirectory[botName];
108
+
109
+ try {
110
+ const flowiseResponse = await fetch(`${finalTarget.host}/api/v1/prediction/${finalTarget.id}`, {
111
+ method: 'POST',
112
+ headers: {
113
+ 'Content-Type': 'application/json',
114
+ 'Authorization': `Bearer ${finalTarget.key}`,
115
+ 'HTTP-Referer': req.headers.origin || 'https://huggingface.co',
116
+ 'X-Title': 'FederatedProxy'
117
+ },
118
+ body: JSON.stringify(req.body)
119
+ });
120
+
121
+ const data = await flowiseResponse.json();
122
+ res.status(flowiseResponse.status).json(data);
123
+
124
+ } catch (error) {
125
+ console.error("Proxy Forwarding Error:", error);
126
+ res.status(500).json({ error: 'Proxy forwarding failed.' });
127
+ }
128
+ });
129
+
130
+ app.get('/', (req, res) => res.send('Federated Proxy Active'));
131
+
132
+ app.listen(7860, () => console.log('Federated Proxy running on port 7860'));