everydaycats commited on
Commit
6ce04ff
·
verified ·
1 Parent(s): 87d6c49

Update app.js

Browse files
Files changed (1) hide show
  1. app.js +55 -280
app.js CHANGED
@@ -41,32 +41,18 @@ try {
41
 
42
  const verifySupabaseUser = async (req, res, next) => {
43
  const debugMode = process.env.DEBUG_NO_AUTH === 'true';
44
-
45
- if (debugMode) {
46
- req.user = { id: "user_dev_01" };
47
- return next();
48
- }
49
-
50
  const authHeader = req.headers.authorization;
51
- if (!authHeader || !authHeader.startsWith('Bearer ')) {
52
- return res.status(401).json({ error: 'Missing Bearer token' });
53
- }
54
-
55
  const idToken = authHeader.split('Bearer ')[1];
56
-
57
  try {
58
  if (supabase) {
59
  const { data: { user }, error } = await supabase.auth.getUser(idToken);
60
  if (error || !user) throw new Error("Invalid Token");
61
  req.user = user;
62
  next();
63
- } else {
64
- req.user = { id: "memory_user" };
65
- next();
66
- }
67
- } catch (error) {
68
- return res.status(403).json({ error: 'Unauthorized', details: error.message });
69
- }
70
  };
71
 
72
  async function getSessionSecret(uid, projectId) {
@@ -76,25 +62,14 @@ async function getSessionSecret(uid, projectId) {
76
  session.lastAccessed = Date.now();
77
  return session.secret;
78
  }
79
-
80
  if (supabase) {
81
  try {
82
- const { data, error } = await supabase
83
- .from('projects')
84
- .select('plugin_secret')
85
- .eq('id', projectId)
86
- .eq('user_id', uid)
87
- .single();
88
-
89
  if (data && data.plugin_secret) {
90
- const secret = data.plugin_secret;
91
- activeSessions.set(cacheKey, { secret, lastAccessed: Date.now() });
92
- console.log(`💧 Hydrated secret for ${cacheKey} from DB`);
93
- return secret;
94
  }
95
- } catch (err) {
96
- console.error("DB Read Error:", err);
97
- }
98
  }
99
  return null;
100
  }
@@ -102,47 +77,21 @@ async function getSessionSecret(uid, projectId) {
102
  app.post('/key', verifySupabaseUser, (req, res) => {
103
  const { projectId } = req.body;
104
  if (!projectId) return res.status(400).json({ error: 'projectId required' });
105
-
106
  const key = `key_${uuidv4().replace(/-/g, '')}`;
107
-
108
- tempKeys.set(key, {
109
- uid: req.user.id,
110
- projectId: projectId,
111
- createdAt: Date.now()
112
- });
113
-
114
  console.log(`🔑 Generated Key for user ${req.user.id}: ${key}`);
115
  res.json({ key, expiresIn: 300 });
116
  });
117
 
118
  app.post('/redeem', async (req, res) => {
119
  const { key } = req.body;
120
-
121
- if (!key || !tempKeys.has(key)) {
122
- return res.status(404).json({ error: 'Invalid or expired key' });
123
- }
124
-
125
  const data = tempKeys.get(key);
126
  const sessionSecret = uuidv4();
127
-
128
- const token = jwt.sign(
129
- { uid: data.uid, projectId: data.projectId },
130
- sessionSecret,
131
- { expiresIn: '3d' }
132
- );
133
-
134
  const cacheKey = `${data.uid}:${data.projectId}`;
135
-
136
  activeSessions.set(cacheKey, { secret: sessionSecret, lastAccessed: Date.now() });
137
-
138
- if (supabase) {
139
- await supabase
140
- .from('projects')
141
- .update({ plugin_secret: sessionSecret })
142
- .eq('id', data.projectId)
143
- .eq('user_id', data.uid);
144
- }
145
-
146
  tempKeys.delete(key);
147
  console.log(`🚀 Redeemed JWT for ${cacheKey}`);
148
  res.json({ token });
@@ -150,291 +99,117 @@ app.post('/redeem', async (req, res) => {
150
 
151
  app.post('/verify', async (req, res) => {
152
  const { token } = req.body;
153
- if (!token) return res.status(400).json({ valid: false, error: 'Token required' });
154
-
155
  const decoded = jwt.decode(token);
156
- if (!decoded || !decoded.uid || !decoded.projectId) {
157
- return res.status(401).json({ valid: false, error: 'Malformed token' });
158
- }
159
-
160
  const secret = await getSessionSecret(decoded.uid, decoded.projectId);
161
-
162
- if (!secret) {
163
- return res.status(401).json({ valid: false, error: 'Session revoked' });
164
- }
165
-
166
- try {
167
- jwt.verify(token, secret);
168
- const threeDaysInSeconds = 3 * 24 * 60 * 60;
169
- const nowInSeconds = Math.floor(Date.now() / 1000);
170
- if (decoded.iat && (nowInSeconds - decoded.iat > threeDaysInSeconds)) {
171
- return res.status(403).json({ valid: false, error: 'Expired' });
172
- }
173
-
174
- return res.json({ valid: true });
175
- } catch (err) {
176
- return res.status(403).json({ valid: false, error: 'Invalid signature' });
177
- }
178
  });
179
 
180
  app.post('/feedback', async (req, res) => {
181
  const { token, ...pluginPayload } = req.body;
182
-
183
  if (!token) return res.status(400).json({ error: 'Token required' });
184
-
185
  const decoded = jwt.decode(token);
186
- if (!decoded || !decoded.uid || !decoded.projectId) {
187
- return res.status(401).json({ error: 'Malformed token' });
188
- }
189
-
190
  const secret = await getSessionSecret(decoded.uid, decoded.projectId);
191
  if (!secret) return res.status(404).json({ error: 'Session revoked' });
192
-
193
  try {
194
  jwt.verify(token, secret);
195
-
196
  const targetUrl = EXTERNAL_SERVER_URL.replace(/\/$/, '') + '/project/feedback';
197
-
198
- console.log(`📨 Forwarding PLUGIN feedback for ${decoded.projectId} (${decoded.uid})`);
199
-
200
- const response = await axios.post(targetUrl, {
201
- userId: decoded.uid,
202
- projectId: decoded.projectId,
203
- ...pluginPayload
204
- });
205
-
206
  return res.json({ success: true, externalResponse: response.data });
207
-
208
- } catch (err) {
209
- console.error("Feedback Forward Error:", err.message);
210
- if (err.response) {
211
- return res.status(err.response.status).json(err.response.data);
212
- }
213
- return res.status(502).json({ error: 'Failed to forward feedback to Main AI server' });
214
- }
215
  });
216
 
217
  app.post('/feedback2', verifySupabaseUser, async (req, res) => {
218
  const { projectId, prompt, images, ...otherPayload } = req.body;
219
  const userId = req.user.id;
220
-
221
- if (!projectId || !prompt) {
222
- return res.status(400).json({ error: 'Missing projectId or prompt' });
223
- }
224
-
225
  const targetUrl = EXTERNAL_SERVER_URL.replace(/\/$/, '') + '/project/feedback';
226
-
227
  try {
228
- const response = await axios.post(targetUrl, {
229
- userId: userId,
230
- projectId: projectId,
231
- prompt: prompt,
232
- images: images || [],
233
- ...otherPayload
234
- });
235
-
236
  return res.json({ success: true, externalResponse: response.data });
237
- } catch (err) {
238
- console.error("Forward Error:", err.message);
239
- return res.status(502).json({ error: 'Failed to forward' });
240
- }
241
  });
242
 
 
243
  app.post('/stream-feed', verifySupabaseUser, async (req, res) => {
244
  const { projectId } = req.body;
245
  const userId = req.user.id;
246
 
247
- if (!projectId) {
248
- return res.status(400).json({ error: 'Missing projectId' });
249
- }
 
 
 
250
 
251
  if (supabase) {
252
- const { data, error } = await supabase
253
- .from('projects')
254
- .select('id, user_id, info')
255
- .eq('id', projectId)
256
- .single();
257
-
258
- if (error || !data || data.user_id !== userId) {
259
- return res.status(403).json({ error: 'Unauthorized' });
260
- }
261
 
262
  const targetUrl = EXTERNAL_SERVER_URL.replace(/\/$/, '') + '/project/ping';
263
-
264
  try {
265
- const response = await axios.post(targetUrl, {
266
- projectId: projectId,
267
- userId: userId,
268
- isFrontend: true
269
- });
270
-
271
- return res.json({
272
- ...response.data,
273
- dbStatus: data.info?.status || 'idle'
274
- });
275
- } catch (e) {
276
- return res.status(502).json({ error: "AI Server Unreachable" });
277
- }
278
  }
279
  });
280
 
281
  app.post('/poll', async (req, res) => {
282
  const { token } = req.body;
283
-
284
  if (!token) return res.status(400).json({ error: 'Token required' });
285
-
286
  const decoded = jwt.decode(token);
287
- if (!decoded || !decoded.uid || !decoded.projectId) {
288
- return res.status(401).json({ error: 'Malformed token' });
289
- }
290
-
291
  const secret = await getSessionSecret(decoded.uid, decoded.projectId);
292
- if (!secret) return res.status(404).json({ error: 'Session revoked or not found' });
293
-
294
  try {
295
- const verifiedData = jwt.verify(token, secret);
296
-
297
- const threeDaysInSeconds = 3 * 24 * 60 * 60;
298
- const nowInSeconds = Math.floor(Date.now() / 1000);
299
- if (verifiedData.iat && (nowInSeconds - verifiedData.iat > threeDaysInSeconds)) {
300
- return res.status(403).json({ error: 'Token expired (older than 3 days)' });
301
- }
302
-
303
  const targetUrl = EXTERNAL_SERVER_URL.replace(/\/$/, '') + '/project/ping';
304
-
305
- try {
306
- const response = await axios.post(targetUrl, {
307
- projectId: verifiedData.projectId,
308
- userId: verifiedData.uid
309
- });
310
-
311
- return res.json(response.data);
312
- } catch (extError) {
313
- return res.status(502).json({ error: 'External server error' });
314
- }
315
-
316
- } catch (err) {
317
- return res.status(403).json({ error: 'Invalid Token Signature' });
318
- }
319
  });
320
 
321
  app.post('/project/delete', verifySupabaseUser, async (req, res) => {
322
  const { projectId } = req.body;
323
  const userId = req.user.id;
324
-
325
  if (!projectId) return res.status(400).json({ error: "Missing Project ID" });
326
-
327
- console.log(`🗑️ Deleting Project: ${projectId} requested by ${userId}`);
328
-
329
  try {
330
- const { data: project, error: fetchError } = await supabase
331
- .from('projects')
332
- .select('user_id')
333
- .eq('id', projectId)
334
- .single();
335
-
336
- if (fetchError || !project || project.user_id !== userId) {
337
- return res.status(403).json({ error: "Unauthorized" });
338
- }
339
-
340
- const { error: chunkError } = await supabase
341
- .from('message_chunks')
342
- .delete()
343
- .eq('project_id', projectId);
344
-
345
- if (chunkError) {
346
- console.warn(`Warning: Failed to delete message chunks: ${chunkError.message}`);
347
- }
348
-
349
- const { error: dbError } = await supabase
350
- .from('projects')
351
- .delete()
352
- .eq('id', projectId);
353
-
354
- if (dbError) throw dbError;
355
-
356
  if (STORAGE_BUCKET) {
357
  const { data: files } = await supabase.storage.from(STORAGE_BUCKET).list(projectId);
358
-
359
- if (files && files.length > 0) {
360
- const filesToRemove = files.map(f => `${projectId}/${f.name}`);
361
- await supabase.storage.from(STORAGE_BUCKET).remove(filesToRemove);
362
- }
363
  }
364
-
365
  activeSessions.delete(`${userId}:${projectId}`);
366
- for (const [key, val] of tempKeys.entries()) {
367
- if (val.projectId === projectId) tempKeys.delete(key);
368
- }
369
-
370
- console.log(`✅ Project ${projectId} deleted successfully.`);
371
  res.json({ success: true });
372
-
373
- } catch (err) {
374
- console.error("Delete Error:", err);
375
- res.status(500).json({ error: "Failed to delete project resources" });
376
- }
377
  });
378
 
379
  app.get('/cleanup', (req, res) => {
 
380
  const THRESHOLD = 1000 * 60 * 60;
381
  const now = Date.now();
382
  let cleanedCount = 0;
383
-
384
- for (const [key, value] of activeSessions.entries()) {
385
- if (now - value.lastAccessed > THRESHOLD) {
386
- activeSessions.delete(key);
387
- cleanedCount++;
388
- }
389
- }
390
- for (const [key, value] of tempKeys.entries()) {
391
- if (now - value.createdAt > (1000 * 60 * 4)) {
392
- tempKeys.delete(key);
393
- }
394
- }
395
  res.json({ message: `Cleaned ${cleanedCount} cached sessions from memory.` });
396
  });
397
 
398
  app.post('/nullify', verifySupabaseUser, async (req, res) => {
399
  const { projectId } = req.body;
400
  if (!projectId) return res.status(400).json({ error: 'projectId required' });
401
-
402
  const cacheKey = `${req.user.id}:${projectId}`;
403
- const existedInMemory = activeSessions.delete(cacheKey);
404
-
405
- let deletedTempKeys = 0;
406
- for (const [tKey, tData] of tempKeys.entries()) {
407
- if (tData.uid === req.user.id && tData.projectId === projectId) {
408
- tempKeys.delete(tKey);
409
- deletedTempKeys++;
410
- }
411
- }
412
-
413
- if (supabase) {
414
- try {
415
- await supabase
416
- .from('projects')
417
- .update({ plugin_secret: null })
418
- .eq('id', projectId)
419
- .eq('user_id', req.user.id);
420
- } catch (e) {
421
- return res.status(500).json({ error: 'Database error during nullify' });
422
- }
423
- }
424
-
425
- console.log(`☢️ NULLIFIED session for ${cacheKey}.`);
426
- res.json({
427
- success: true,
428
- message: 'Session purged.',
429
- wasCached: existedInMemory,
430
- tempKeysRemoved: deletedTempKeys
431
- });
432
  });
433
 
434
- app.get('/', (req, res) => {
435
- res.send('Plugin Auth Proxy Running (Supabase Edition)');
436
- });
437
 
438
- app.listen(PORT, () => {
439
- console.log(`🚀 Auth Proxy running on port ${PORT}`);
440
- });
 
41
 
42
  const verifySupabaseUser = async (req, res, next) => {
43
  const debugMode = process.env.DEBUG_NO_AUTH === 'true';
44
+ if (debugMode) { req.user = { id: "user_dev_01" }; return next(); }
 
 
 
 
 
45
  const authHeader = req.headers.authorization;
46
+ if (!authHeader || !authHeader.startsWith('Bearer ')) return res.status(401).json({ error: 'Missing Bearer token' });
 
 
 
47
  const idToken = authHeader.split('Bearer ')[1];
 
48
  try {
49
  if (supabase) {
50
  const { data: { user }, error } = await supabase.auth.getUser(idToken);
51
  if (error || !user) throw new Error("Invalid Token");
52
  req.user = user;
53
  next();
54
+ } else { req.user = { id: "memory_user" }; next(); }
55
+ } catch (error) { return res.status(403).json({ error: 'Unauthorized', details: error.message }); }
 
 
 
 
 
56
  };
57
 
58
  async function getSessionSecret(uid, projectId) {
 
62
  session.lastAccessed = Date.now();
63
  return session.secret;
64
  }
 
65
  if (supabase) {
66
  try {
67
+ const { data } = await supabase.from('projects').select('plugin_secret').eq('id', projectId).eq('user_id', uid).single();
 
 
 
 
 
 
68
  if (data && data.plugin_secret) {
69
+ activeSessions.set(cacheKey, { secret: data.plugin_secret, lastAccessed: Date.now() });
70
+ return data.plugin_secret;
 
 
71
  }
72
+ } catch (err) { console.error("DB Read Error:", err); }
 
 
73
  }
74
  return null;
75
  }
 
77
  app.post('/key', verifySupabaseUser, (req, res) => {
78
  const { projectId } = req.body;
79
  if (!projectId) return res.status(400).json({ error: 'projectId required' });
 
80
  const key = `key_${uuidv4().replace(/-/g, '')}`;
81
+ tempKeys.set(key, { uid: req.user.id, projectId: projectId, createdAt: Date.now() });
 
 
 
 
 
 
82
  console.log(`🔑 Generated Key for user ${req.user.id}: ${key}`);
83
  res.json({ key, expiresIn: 300 });
84
  });
85
 
86
  app.post('/redeem', async (req, res) => {
87
  const { key } = req.body;
88
+ if (!key || !tempKeys.has(key)) return res.status(404).json({ error: 'Invalid or expired key' });
 
 
 
 
89
  const data = tempKeys.get(key);
90
  const sessionSecret = uuidv4();
91
+ const token = jwt.sign({ uid: data.uid, projectId: data.projectId }, sessionSecret, { expiresIn: '3d' });
 
 
 
 
 
 
92
  const cacheKey = `${data.uid}:${data.projectId}`;
 
93
  activeSessions.set(cacheKey, { secret: sessionSecret, lastAccessed: Date.now() });
94
+ if (supabase) await supabase.from('projects').update({ plugin_secret: sessionSecret }).eq('id', data.projectId).eq('user_id', data.uid);
 
 
 
 
 
 
 
 
95
  tempKeys.delete(key);
96
  console.log(`🚀 Redeemed JWT for ${cacheKey}`);
97
  res.json({ token });
 
99
 
100
  app.post('/verify', async (req, res) => {
101
  const { token } = req.body;
102
+ if (!token) return res.status(400).json({ valid: false });
 
103
  const decoded = jwt.decode(token);
104
+ if (!decoded || !decoded.uid || !decoded.projectId) return res.status(401).json({ valid: false });
 
 
 
105
  const secret = await getSessionSecret(decoded.uid, decoded.projectId);
106
+ if (!secret) return res.status(401).json({ valid: false });
107
+ try { jwt.verify(token, secret); return res.json({ valid: true }); } catch (err) { return res.status(403).json({ valid: false }); }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
108
  });
109
 
110
  app.post('/feedback', async (req, res) => {
111
  const { token, ...pluginPayload } = req.body;
 
112
  if (!token) return res.status(400).json({ error: 'Token required' });
 
113
  const decoded = jwt.decode(token);
114
+ if (!decoded) return res.status(401).json({ error: 'Malformed token' });
 
 
 
115
  const secret = await getSessionSecret(decoded.uid, decoded.projectId);
116
  if (!secret) return res.status(404).json({ error: 'Session revoked' });
 
117
  try {
118
  jwt.verify(token, secret);
 
119
  const targetUrl = EXTERNAL_SERVER_URL.replace(/\/$/, '') + '/project/feedback';
120
+ const response = await axios.post(targetUrl, { userId: decoded.uid, projectId: decoded.projectId, ...pluginPayload });
 
 
 
 
 
 
 
 
121
  return res.json({ success: true, externalResponse: response.data });
122
+ } catch (err) { return res.status(502).json({ error: 'Failed to forward' }); }
 
 
 
 
 
 
 
123
  });
124
 
125
  app.post('/feedback2', verifySupabaseUser, async (req, res) => {
126
  const { projectId, prompt, images, ...otherPayload } = req.body;
127
  const userId = req.user.id;
128
+ if (!projectId || !prompt) return res.status(400).json({ error: 'Missing projectId or prompt' });
 
 
 
 
129
  const targetUrl = EXTERNAL_SERVER_URL.replace(/\/$/, '') + '/project/feedback';
 
130
  try {
131
+ const response = await axios.post(targetUrl, { userId: userId, projectId: projectId, prompt: prompt, images: images || [], ...otherPayload });
 
 
 
 
 
 
 
132
  return res.json({ success: true, externalResponse: response.data });
133
+ } catch (err) { return res.status(502).json({ error: 'Failed to forward' }); }
 
 
 
134
  });
135
 
136
+ // --- STREAM FEED (Optimized headers) ---
137
  app.post('/stream-feed', verifySupabaseUser, async (req, res) => {
138
  const { projectId } = req.body;
139
  const userId = req.user.id;
140
 
141
+ // Headers to disable caching for poller
142
+ res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
143
+ res.setHeader('Pragma', 'no-cache');
144
+ res.setHeader('Expires', '0');
145
+
146
+ if (!projectId) return res.status(400).json({ error: 'Missing projectId' });
147
 
148
  if (supabase) {
149
+ const { data, error } = await supabase.from('projects').select('id, user_id, info').eq('id', projectId).single();
150
+ if (error || !data || data.user_id !== userId) return res.status(403).json({ error: 'Unauthorized' });
 
 
 
 
 
 
 
151
 
152
  const targetUrl = EXTERNAL_SERVER_URL.replace(/\/$/, '') + '/project/ping';
 
153
  try {
154
+ const response = await axios.post(targetUrl, { projectId, userId, isFrontend: true });
155
+ return res.json({ ...response.data, dbStatus: data.info?.status || 'idle' });
156
+ } catch (e) { return res.status(502).json({ error: "AI Server Unreachable" }); }
 
 
 
 
 
 
 
 
 
 
157
  }
158
  });
159
 
160
  app.post('/poll', async (req, res) => {
161
  const { token } = req.body;
 
162
  if (!token) return res.status(400).json({ error: 'Token required' });
 
163
  const decoded = jwt.decode(token);
164
+ if (!decoded) return res.status(401).json({ error: 'Malformed token' });
 
 
 
165
  const secret = await getSessionSecret(decoded.uid, decoded.projectId);
166
+ if (!secret) return res.status(404).json({ error: 'Session revoked' });
 
167
  try {
168
+ jwt.verify(token, secret);
 
 
 
 
 
 
 
169
  const targetUrl = EXTERNAL_SERVER_URL.replace(/\/$/, '') + '/project/ping';
170
+ const response = await axios.post(targetUrl, { projectId: decoded.projectId, userId: decoded.uid });
171
+ return res.json(response.data);
172
+ } catch (err) { return res.status(403).json({ error: 'Invalid Token' }); }
 
 
 
 
 
 
 
 
 
 
 
 
173
  });
174
 
175
  app.post('/project/delete', verifySupabaseUser, async (req, res) => {
176
  const { projectId } = req.body;
177
  const userId = req.user.id;
 
178
  if (!projectId) return res.status(400).json({ error: "Missing Project ID" });
 
 
 
179
  try {
180
+ const { data: project, error: fetchError } = await supabase.from('projects').select('user_id').eq('id', projectId).single();
181
+ if (fetchError || !project || project.user_id !== userId) return res.status(403).json({ error: "Unauthorized" });
182
+ await supabase.from('message_chunks').delete().eq('project_id', projectId);
183
+ await supabase.from('projects').delete().eq('id', projectId);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
184
  if (STORAGE_BUCKET) {
185
  const { data: files } = await supabase.storage.from(STORAGE_BUCKET).list(projectId);
186
+ if (files && files.length > 0) { const filesToRemove = files.map(f => `${projectId}/${f.name}`); await supabase.storage.from(STORAGE_BUCKET).remove(filesToRemove); }
 
 
 
 
187
  }
 
188
  activeSessions.delete(`${userId}:${projectId}`);
189
+ for (const [key, val] of tempKeys.entries()) { if (val.projectId === projectId) tempKeys.delete(key); }
 
 
 
 
190
  res.json({ success: true });
191
+ } catch (err) { res.status(500).json({ error: "Delete failed" }); }
 
 
 
 
192
  });
193
 
194
  app.get('/cleanup', (req, res) => {
195
+ // ... (Standard cleanup) ...
196
  const THRESHOLD = 1000 * 60 * 60;
197
  const now = Date.now();
198
  let cleanedCount = 0;
199
+ for (const [key, value] of activeSessions.entries()) { if (now - value.lastAccessed > THRESHOLD) { activeSessions.delete(key); cleanedCount++; } }
200
+ for (const [key, value] of tempKeys.entries()) { if (now - value.createdAt > (1000 * 60 * 4)) { tempKeys.delete(key); } }
 
 
 
 
 
 
 
 
 
 
201
  res.json({ message: `Cleaned ${cleanedCount} cached sessions from memory.` });
202
  });
203
 
204
  app.post('/nullify', verifySupabaseUser, async (req, res) => {
205
  const { projectId } = req.body;
206
  if (!projectId) return res.status(400).json({ error: 'projectId required' });
 
207
  const cacheKey = `${req.user.id}:${projectId}`;
208
+ activeSessions.delete(cacheKey);
209
+ if (supabase) await supabase.from('projects').update({ plugin_secret: null }).eq('id', projectId).eq('user_id', req.user.id);
210
+ res.json({ success: true });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
211
  });
212
 
213
+ app.get('/', (req, res) => { res.send('Plugin Auth Proxy Running (Supabase Edition)'); });
 
 
214
 
215
+ app.listen(PORT, () => { console.log(`🚀 Auth Proxy running on port ${PORT}`); });