AUXteam commited on
Commit
ce0ea5d
·
verified ·
1 Parent(s): 3f729b3

Upload folder using huggingface_hub

Browse files
Files changed (5) hide show
  1. App.tsx +21 -37
  2. components/SimulationPage.tsx +9 -4
  3. package-lock.json +20 -0
  4. package.json +1 -0
  5. server.cjs +90 -2
App.tsx CHANGED
@@ -1,6 +1,5 @@
1
 
2
  import React, { useState, useEffect } from 'react';
3
- import { oauthLoginUrl, oauthHandleRedirectIfPresent } from "@huggingface/hub";
4
  import Navbar from './components/Navbar';
5
  import Hero from './components/Hero';
6
  import TrustedBy from './components/TrustedBy';
@@ -19,52 +18,36 @@ import ChatPage from './components/ChatPage';
19
  function App() {
20
  const [currentView, setCurrentView] = useState<'landing' | 'simulation' | 'conversation' | 'chat'>('simulation');
21
  const [user, setUser] = useState<any>(null);
22
- const [config, setConfig] = useState<{ clientId?: string; scopes?: string }>({});
23
  const [simulationResult, setSimulationResult] = useState<any>(null);
24
 
25
  useEffect(() => {
26
- const fetchConfig = async () => {
27
  try {
28
- const response = await fetch('/api/config');
29
- const data = await response.json();
30
- setConfig(data);
31
- } catch (error) {
32
- console.error("Failed to fetch config:", error);
33
- }
34
- };
35
- fetchConfig();
36
-
37
- const handleAuth = async () => {
38
- try {
39
- const oauthResult = await oauthHandleRedirectIfPresent();
40
- if (oauthResult) {
41
- setUser(oauthResult.userInfo);
42
- console.log("Logged in as:", oauthResult.userInfo);
43
  }
44
  } catch (error) {
45
- console.error("Auth error:", error);
46
  }
47
  };
48
- handleAuth();
 
 
 
 
 
49
  }, []);
50
 
51
- const loginWithHF = async () => {
52
- try {
53
- const url = await oauthLoginUrl({
54
- clientId: config.clientId,
55
- scopes: config.scopes,
56
- redirectUrl: window.location.origin + "/"
57
- });
58
- // Use window.top for redirecting out of the Space iframe
59
- if (window.top) {
60
- window.top.location.href = url;
61
- } else {
62
- window.location.href = url;
63
- }
64
- } catch (error) {
65
- console.error("Login error:", error);
66
- alert("Failed to initiate login. See console for details.");
67
- }
68
  };
69
 
70
  const startSimulation = () => {
@@ -96,6 +79,7 @@ function App() {
96
  onOpenChat={openChat}
97
  user={user}
98
  onLogin={loginWithHF}
 
99
  simulationResult={simulationResult}
100
  setSimulationResult={setSimulationResult}
101
  />
 
1
 
2
  import React, { useState, useEffect } from 'react';
 
3
  import Navbar from './components/Navbar';
4
  import Hero from './components/Hero';
5
  import TrustedBy from './components/TrustedBy';
 
18
  function App() {
19
  const [currentView, setCurrentView] = useState<'landing' | 'simulation' | 'conversation' | 'chat'>('simulation');
20
  const [user, setUser] = useState<any>(null);
 
21
  const [simulationResult, setSimulationResult] = useState<any>(null);
22
 
23
  useEffect(() => {
24
+ const fetchUser = async () => {
25
  try {
26
+ const response = await fetch('/api/user');
27
+ if (response.ok) {
28
+ const data = await response.json();
29
+ setUser(data);
 
 
 
 
 
 
 
 
 
 
 
30
  }
31
  } catch (error) {
32
+ console.error("Failed to fetch user:", error);
33
  }
34
  };
35
+ fetchUser();
36
+
37
+ // Listen for storage changes in case of popup login
38
+ const handleStorage = () => fetchUser();
39
+ window.addEventListener('storage', handleStorage);
40
+ return () => window.removeEventListener('storage', handleStorage);
41
  }, []);
42
 
43
+ const loginWithHF = () => {
44
+ // Open in a new tab to pop out of iframe
45
+ window.open('/login', '_blank');
46
+ };
47
+
48
+ const handleLogout = async () => {
49
+ await fetch('/api/logout');
50
+ setUser(null);
 
 
 
 
 
 
 
 
 
51
  };
52
 
53
  const startSimulation = () => {
 
79
  onOpenChat={openChat}
80
  user={user}
81
  onLogin={loginWithHF}
82
+ onLogout={handleLogout}
83
  simulationResult={simulationResult}
84
  setSimulationResult={setSimulationResult}
85
  />
components/SimulationPage.tsx CHANGED
@@ -9,6 +9,7 @@ interface SimulationPageProps {
9
  onOpenChat: () => void;
10
  user?: any;
11
  onLogin?: () => void;
 
12
  simulationResult: any;
13
  setSimulationResult: (res: any) => void;
14
  }
@@ -44,7 +45,7 @@ const VIEW_FILTERS: Record<string, Array<{ label: string; color: string }>> = {
44
  };
45
 
46
  const SimulationPage: React.FC<SimulationPageProps> = ({
47
- onBack, onOpenConversation, onOpenChat, user, onLogin, simulationResult, setSimulationResult
48
  }) => {
49
  const [society, setSociety] = useState('');
50
  const [societies, setSocieties] = useState<string[]>([]);
@@ -240,7 +241,7 @@ const SimulationPage: React.FC<SimulationPageProps> = ({
240
  <MenuItem icon={<Plus size={16}/>} label="Start Free Trial" highlight />
241
  <MenuItem icon={<MessageSquare size={16}/>} label="Leave Feedback" />
242
  <MenuItem icon={<BookOpen size={16}/>} label="Product Guide" />
243
- <MenuItem icon={<LogOut size={16}/>} label="Log Out" />
244
 
245
  <div className="pt-4 text-[10px] text-gray-600">Version 2.1</div>
246
  </div>
@@ -361,10 +362,14 @@ interface MenuItemProps {
361
  icon: React.ReactNode;
362
  label: string;
363
  highlight?: boolean;
 
364
  }
365
 
366
- const MenuItem: React.FC<MenuItemProps> = ({ icon, label, highlight = false }) => (
367
- <button className={`w-full flex items-center gap-3 px-2 py-2.5 rounded-md text-sm transition-colors ${highlight ? 'text-teal-400 hover:bg-teal-950/30' : 'text-gray-400 hover:bg-gray-800 hover:text-white'}`}>
 
 
 
368
  {icon}
369
  <span>{label}</span>
370
  </button>
 
9
  onOpenChat: () => void;
10
  user?: any;
11
  onLogin?: () => void;
12
+ onLogout?: () => void;
13
  simulationResult: any;
14
  setSimulationResult: (res: any) => void;
15
  }
 
45
  };
46
 
47
  const SimulationPage: React.FC<SimulationPageProps> = ({
48
+ onBack, onOpenConversation, onOpenChat, user, onLogin, onLogout, simulationResult, setSimulationResult
49
  }) => {
50
  const [society, setSociety] = useState('');
51
  const [societies, setSocieties] = useState<string[]>([]);
 
241
  <MenuItem icon={<Plus size={16}/>} label="Start Free Trial" highlight />
242
  <MenuItem icon={<MessageSquare size={16}/>} label="Leave Feedback" />
243
  <MenuItem icon={<BookOpen size={16}/>} label="Product Guide" />
244
+ {user && <MenuItem icon={<LogOut size={16}/>} label="Log Out" onClick={onLogout} />}
245
 
246
  <div className="pt-4 text-[10px] text-gray-600">Version 2.1</div>
247
  </div>
 
362
  icon: React.ReactNode;
363
  label: string;
364
  highlight?: boolean;
365
+ onClick?: () => void;
366
  }
367
 
368
+ const MenuItem: React.FC<MenuItemProps> = ({ icon, label, highlight = false, onClick }) => (
369
+ <button
370
+ onClick={onClick}
371
+ className={`w-full flex items-center gap-3 px-2 py-2.5 rounded-md text-sm transition-colors ${highlight ? 'text-teal-400 hover:bg-teal-950/30' : 'text-gray-400 hover:bg-gray-800 hover:text-white'}`}
372
+ >
373
  {icon}
374
  <span>{label}</span>
375
  </button>
package-lock.json CHANGED
@@ -10,6 +10,7 @@
10
  "dependencies": {
11
  "@gradio/client": "^2.0.4",
12
  "@huggingface/hub": "^2.10.1",
 
13
  "express": "^5.2.1",
14
  "lucide-react": "^0.555.0",
15
  "playwright": "^1.58.2",
@@ -2289,6 +2290,25 @@
2289
  "node": ">= 0.6"
2290
  }
2291
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2292
  "node_modules/cookie-signature": {
2293
  "version": "1.2.2",
2294
  "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz",
 
10
  "dependencies": {
11
  "@gradio/client": "^2.0.4",
12
  "@huggingface/hub": "^2.10.1",
13
+ "cookie-parser": "^1.4.7",
14
  "express": "^5.2.1",
15
  "lucide-react": "^0.555.0",
16
  "playwright": "^1.58.2",
 
2290
  "node": ">= 0.6"
2291
  }
2292
  },
2293
+ "node_modules/cookie-parser": {
2294
+ "version": "1.4.7",
2295
+ "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.7.tgz",
2296
+ "integrity": "sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw==",
2297
+ "license": "MIT",
2298
+ "dependencies": {
2299
+ "cookie": "0.7.2",
2300
+ "cookie-signature": "1.0.6"
2301
+ },
2302
+ "engines": {
2303
+ "node": ">= 0.8.0"
2304
+ }
2305
+ },
2306
+ "node_modules/cookie-parser/node_modules/cookie-signature": {
2307
+ "version": "1.0.6",
2308
+ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
2309
+ "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==",
2310
+ "license": "MIT"
2311
+ },
2312
  "node_modules/cookie-signature": {
2313
  "version": "1.2.2",
2314
  "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz",
package.json CHANGED
@@ -11,6 +11,7 @@
11
  "dependencies": {
12
  "@gradio/client": "^2.0.4",
13
  "@huggingface/hub": "^2.10.1",
 
14
  "express": "^5.2.1",
15
  "lucide-react": "^0.555.0",
16
  "playwright": "^1.58.2",
 
11
  "dependencies": {
12
  "@gradio/client": "^2.0.4",
13
  "@huggingface/hub": "^2.10.1",
14
+ "cookie-parser": "^1.4.7",
15
  "express": "^5.2.1",
16
  "lucide-react": "^0.555.0",
17
  "playwright": "^1.58.2",
server.cjs CHANGED
@@ -1,11 +1,23 @@
1
  const express = require('express');
2
  const path = require('path');
 
3
  const app = express();
4
  const port = 7860;
5
 
6
  app.use(express.json());
 
7
  app.use(express.static(path.join(__dirname, 'dist')));
8
 
 
 
 
 
 
 
 
 
 
 
9
  app.post('/api/craft', async (req, res) => {
10
  const { content, variation } = req.body;
11
  const apiKey = process.env.BLABLADOR_API_KEY;
@@ -49,11 +61,87 @@ app.post('/api/craft', async (req, res) => {
49
 
50
  app.get('/api/config', (req, res) => {
51
  res.json({
52
- clientId: process.env.OAUTH_CLIENT_ID,
53
- scopes: process.env.OAUTH_SCOPES || "openid profile",
54
  });
55
  });
56
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
  app.get('/health', (req, res) => {
58
  res.status(200).send('OK');
59
  });
 
1
  const express = require('express');
2
  const path = require('path');
3
+ const cookieParser = require('cookie-parser');
4
  const app = express();
5
  const port = 7860;
6
 
7
  app.use(express.json());
8
+ app.use(cookieParser());
9
  app.use(express.static(path.join(__dirname, 'dist')));
10
 
11
+ const OAUTH_CLIENT_ID = process.env.OAUTH_CLIENT_ID;
12
+ const OAUTH_CLIENT_SECRET = process.env.OAUTH_CLIENT_SECRET;
13
+ const OAUTH_SCOPES = process.env.OAUTH_SCOPES || "openid profile";
14
+ const OPENID_PROVIDER_URL = process.env.OPENID_PROVIDER_URL || "https://huggingface.co";
15
+ const SPACE_HOST = process.env.SPACE_HOST;
16
+
17
+ const REDIRECT_URI = SPACE_HOST
18
+ ? `https://${SPACE_HOST}/oauth/callback`
19
+ : `http://localhost:${port}/oauth/callback`;
20
+
21
  app.post('/api/craft', async (req, res) => {
22
  const { content, variation } = req.body;
23
  const apiKey = process.env.BLABLADOR_API_KEY;
 
61
 
62
  app.get('/api/config', (req, res) => {
63
  res.json({
64
+ clientId: OAUTH_CLIENT_ID,
65
+ scopes: OAUTH_SCOPES,
66
  });
67
  });
68
 
69
+ app.get('/login', (req, res) => {
70
+ if (!OAUTH_CLIENT_ID) {
71
+ return res.status(500).json({ error: "OAuth is not configured (missing OAUTH_CLIENT_ID)" });
72
+ }
73
+
74
+ const authUrl = `${OPENID_PROVIDER_URL}/oauth/authorize` +
75
+ `?client_id=${OAUTH_CLIENT_ID}` +
76
+ `?redirect_uri=${encodeURIComponent(REDIRECT_URI)}` +
77
+ `?scope=${encodeURIComponent(OAUTH_SCOPES)}` +
78
+ `?response_type=code` +
79
+ `?state=secure_random_state_string`;
80
+
81
+ res.redirect(authUrl);
82
+ });
83
+
84
+ app.get('/oauth/callback', async (req, res) => {
85
+ const { code } = req.query;
86
+ const tokenUrl = `${OPENID_PROVIDER_URL}/oauth/token`;
87
+
88
+ const authStr = Buffer.from(`${OAUTH_CLIENT_ID}:${OAUTH_CLIENT_SECRET}`).toString('base64');
89
+
90
+ try {
91
+ const tokenResp = await fetch(tokenUrl, {
92
+ method: 'POST',
93
+ headers: {
94
+ 'Authorization': `Basic ${authStr}`,
95
+ 'Content-Type': 'application/x-www-form-urlencoded'
96
+ },
97
+ body: new URLSearchParams({
98
+ grant_type: 'authorization_code',
99
+ code: code,
100
+ redirect_uri: REDIRECT_URI,
101
+ client_id: OAUTH_CLIENT_ID,
102
+ })
103
+ });
104
+
105
+ if (!tokenResp.ok) {
106
+ const errorText = await tokenResp.text();
107
+ console.error("Token exchange failed:", errorText);
108
+ return res.status(400).json({ error: "Failed to retrieve access token" });
109
+ }
110
+
111
+ const tokenData = await tokenResp.json();
112
+ const accessToken = tokenData.access_token;
113
+
114
+ const userResp = await fetch("https://huggingface.co/api/whoami-v2", {
115
+ headers: { "Authorization": `Bearer ${accessToken}` }
116
+ });
117
+ const userInfo = await userResp.json();
118
+
119
+ // Set cookie and redirect back to app
120
+ res.cookie('hf_user', JSON.stringify(userInfo), { path: '/', httpOnly: false });
121
+ res.redirect('/');
122
+ } catch (error) {
123
+ console.error("OAuth callback error:", error);
124
+ res.status(500).json({ error: "Authentication failed" });
125
+ }
126
+ });
127
+
128
+ app.get('/api/user', (req, res) => {
129
+ if (req.cookies.hf_user) {
130
+ try {
131
+ res.json(JSON.parse(req.cookies.hf_user));
132
+ } catch (e) {
133
+ res.status(400).json({ error: "Invalid user cookie" });
134
+ }
135
+ } else {
136
+ res.status(401).json({ error: "Not authenticated" });
137
+ }
138
+ });
139
+
140
+ app.get('/api/logout', (req, res) => {
141
+ res.clearCookie('hf_user');
142
+ res.redirect('/');
143
+ });
144
+
145
  app.get('/health', (req, res) => {
146
  res.status(200).send('OK');
147
  });