akhaliq HF Staff commited on
Commit
98b7f49
·
1 Parent(s): cbfc118
Files changed (3) hide show
  1. .gitignore +5 -2
  2. frontend/src/lib/api.ts +177 -0
  3. frontend/src/lib/auth.ts +192 -0
.gitignore CHANGED
@@ -14,8 +14,11 @@ dist/
14
  downloads/
15
  eggs/
16
  .eggs/
17
- lib/
18
- lib64/
 
 
 
19
  parts/
20
  sdist/
21
  var/
 
14
  downloads/
15
  eggs/
16
  .eggs/
17
+ # Ignore Python lib directories but NOT frontend/src/lib
18
+ /lib/
19
+ /lib64/
20
+ venv/lib/
21
+ venv/lib64/
22
  parts/
23
  sdist/
24
  var/
frontend/src/lib/api.ts ADDED
@@ -0,0 +1,177 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // API client for AnyCoder backend
2
+
3
+ import axios, { AxiosInstance } from 'axios';
4
+ import type {
5
+ Model,
6
+ AuthStatus,
7
+ CodeGenerationRequest,
8
+ DeploymentRequest,
9
+ DeploymentResponse,
10
+ Language,
11
+ } from '@/types';
12
+
13
+ // Use relative URLs in production (Next.js rewrites will proxy to backend)
14
+ // In local dev without rewrites, use localhost:8000
15
+ const API_URL = process.env.NEXT_PUBLIC_API_URL ||
16
+ (typeof window !== 'undefined' && window.location.hostname !== 'localhost'
17
+ ? '' // Use relative URLs in production (proxied by Next.js)
18
+ : 'http://localhost:8000'); // Local development
19
+
20
+ class ApiClient {
21
+ private client: AxiosInstance;
22
+ private token: string | null = null;
23
+
24
+ constructor() {
25
+ this.client = axios.create({
26
+ baseURL: API_URL,
27
+ headers: {
28
+ 'Content-Type': 'application/json',
29
+ },
30
+ });
31
+
32
+ // Add auth token to requests if available
33
+ this.client.interceptors.request.use((config) => {
34
+ if (this.token) {
35
+ config.headers.Authorization = `Bearer ${this.token}`;
36
+ }
37
+ return config;
38
+ });
39
+
40
+ // Load token from localStorage on client side
41
+ if (typeof window !== 'undefined') {
42
+ this.token = localStorage.getItem('hf_oauth_token');
43
+ }
44
+ }
45
+
46
+ setToken(token: string | null) {
47
+ this.token = token;
48
+ // Note: OAuth token is stored by auth.ts, not here
49
+ // We just keep it in memory for API calls
50
+ }
51
+
52
+ getToken(): string | null {
53
+ return this.token;
54
+ }
55
+
56
+ async getModels(): Promise<Model[]> {
57
+ const response = await this.client.get<Model[]>('/api/models');
58
+ return response.data;
59
+ }
60
+
61
+ async getLanguages(): Promise<{ languages: Language[] }> {
62
+ const response = await this.client.get<{ languages: Language[] }>('/api/languages');
63
+ return response.data;
64
+ }
65
+
66
+ async getAuthStatus(): Promise<AuthStatus> {
67
+ try {
68
+ const response = await this.client.get<AuthStatus>('/api/auth/status');
69
+ return response.data;
70
+ } catch (error) {
71
+ return {
72
+ authenticated: false,
73
+ message: 'Not authenticated',
74
+ };
75
+ }
76
+ }
77
+
78
+ // Stream-based code generation using EventSource (Server-Sent Events)
79
+ generateCodeStream(
80
+ request: CodeGenerationRequest,
81
+ onChunk: (content: string) => void,
82
+ onComplete: (code: string) => void,
83
+ onError: (error: string) => void
84
+ ): () => void {
85
+ const url = new URL('/api/generate', API_URL);
86
+ const eventSource = new EventSource(
87
+ url.toString() + '?' + new URLSearchParams({
88
+ query: request.query,
89
+ language: request.language,
90
+ model_id: request.model_id,
91
+ provider: request.provider,
92
+ })
93
+ );
94
+
95
+ eventSource.onmessage = (event) => {
96
+ try {
97
+ const data = JSON.parse(event.data);
98
+
99
+ if (data.type === 'chunk' && data.content) {
100
+ onChunk(data.content);
101
+ } else if (data.type === 'complete' && data.code) {
102
+ onComplete(data.code);
103
+ eventSource.close();
104
+ } else if (data.type === 'error') {
105
+ onError(data.message || 'Unknown error occurred');
106
+ eventSource.close();
107
+ }
108
+ } catch (error) {
109
+ console.error('Error parsing SSE data:', error);
110
+ }
111
+ };
112
+
113
+ eventSource.onerror = (error) => {
114
+ console.error('EventSource error:', error);
115
+ onError('Connection error occurred');
116
+ eventSource.close();
117
+ };
118
+
119
+ // Return cleanup function
120
+ return () => {
121
+ eventSource.close();
122
+ };
123
+ }
124
+
125
+ // Alternative: WebSocket-based generation
126
+ generateCodeWebSocket(
127
+ request: CodeGenerationRequest,
128
+ onChunk: (content: string) => void,
129
+ onComplete: (code: string) => void,
130
+ onError: (error: string) => void
131
+ ): WebSocket {
132
+ const wsUrl = API_URL.replace('http', 'ws') + '/ws/generate';
133
+ const ws = new WebSocket(wsUrl);
134
+
135
+ ws.onopen = () => {
136
+ ws.send(JSON.stringify(request));
137
+ };
138
+
139
+ ws.onmessage = (event) => {
140
+ try {
141
+ const data = JSON.parse(event.data);
142
+
143
+ if (data.type === 'chunk' && data.content) {
144
+ onChunk(data.content);
145
+ } else if (data.type === 'complete' && data.code) {
146
+ onComplete(data.code);
147
+ ws.close();
148
+ } else if (data.type === 'error') {
149
+ onError(data.message || 'Unknown error occurred');
150
+ ws.close();
151
+ }
152
+ } catch (error) {
153
+ console.error('Error parsing WebSocket data:', error);
154
+ }
155
+ };
156
+
157
+ ws.onerror = (error) => {
158
+ console.error('WebSocket error:', error);
159
+ onError('Connection error occurred');
160
+ };
161
+
162
+ return ws;
163
+ }
164
+
165
+ async deploy(request: DeploymentRequest): Promise<DeploymentResponse> {
166
+ const response = await this.client.post<DeploymentResponse>('/api/deploy', request);
167
+ return response.data;
168
+ }
169
+
170
+ logout() {
171
+ this.token = null;
172
+ }
173
+ }
174
+
175
+ // Export singleton instance
176
+ export const apiClient = new ApiClient();
177
+
frontend/src/lib/auth.ts ADDED
@@ -0,0 +1,192 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // HuggingFace OAuth authentication utilities
2
+ import { oauthLoginUrl, oauthHandleRedirectIfPresent } from "@huggingface/hub";
3
+
4
+ const STORAGE_KEY = 'hf_oauth_token';
5
+ const USER_INFO_KEY = 'hf_user_info';
6
+ const DEV_MODE_KEY = 'hf_dev_mode';
7
+
8
+ // Check if we're in development mode (localhost)
9
+ const isDevelopment = typeof window !== 'undefined' &&
10
+ (window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1');
11
+
12
+ export interface OAuthUserInfo {
13
+ id: string;
14
+ name: string;
15
+ preferredUsername?: string;
16
+ avatarUrl?: string;
17
+ }
18
+
19
+ export interface OAuthResult {
20
+ accessToken: string;
21
+ accessTokenExpiresAt: Date;
22
+ userInfo: OAuthUserInfo;
23
+ }
24
+
25
+ /**
26
+ * Initialize OAuth and check if user is logged in
27
+ * Returns OAuth result if user is already logged in
28
+ */
29
+ export async function initializeOAuth(): Promise<OAuthResult | null> {
30
+ try {
31
+ // In development mode, check for dev mode login first
32
+ if (isDevelopment && isDevModeEnabled()) {
33
+ const storedToken = getStoredToken();
34
+ const storedUserInfo = getStoredUserInfo();
35
+
36
+ if (storedToken && storedUserInfo) {
37
+ return {
38
+ accessToken: storedToken,
39
+ accessTokenExpiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000),
40
+ userInfo: storedUserInfo,
41
+ };
42
+ }
43
+ return null;
44
+ }
45
+
46
+ // Check if we're handling an OAuth redirect
47
+ const oauthResult = await oauthHandleRedirectIfPresent();
48
+
49
+ if (oauthResult) {
50
+ // Store the OAuth result
51
+ storeOAuthData(oauthResult);
52
+ return oauthResult;
53
+ }
54
+
55
+ // Check if we have stored credentials
56
+ const storedToken = getStoredToken();
57
+ const storedUserInfo = getStoredUserInfo();
58
+
59
+ if (storedToken && storedUserInfo) {
60
+ return {
61
+ accessToken: storedToken,
62
+ accessTokenExpiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000), // Assume 24h
63
+ userInfo: storedUserInfo,
64
+ };
65
+ }
66
+
67
+ return null;
68
+ } catch (error) {
69
+ console.error('OAuth initialization error:', error);
70
+ return null;
71
+ }
72
+ }
73
+
74
+ /**
75
+ * Redirect to HuggingFace OAuth login page
76
+ */
77
+ export async function loginWithHuggingFace(): Promise<void> {
78
+ try {
79
+ const loginUrl = await oauthLoginUrl({
80
+ // Redirect back to the current page
81
+ redirectUrl: window.location.href,
82
+ // Request scopes - adjust as needed
83
+ scopes: "openid profile inference-api",
84
+ });
85
+
86
+ window.location.href = loginUrl;
87
+ } catch (error) {
88
+ console.error('Failed to initiate OAuth login:', error);
89
+ throw new Error('Failed to start login process');
90
+ }
91
+ }
92
+
93
+ /**
94
+ * Logout and clear stored credentials
95
+ */
96
+ export function logout(): void {
97
+ if (typeof window !== 'undefined') {
98
+ localStorage.removeItem(STORAGE_KEY);
99
+ localStorage.removeItem(USER_INFO_KEY);
100
+ localStorage.removeItem(DEV_MODE_KEY);
101
+ }
102
+ }
103
+
104
+ /**
105
+ * Store OAuth data in localStorage
106
+ */
107
+ function storeOAuthData(result: OAuthResult): void {
108
+ if (typeof window !== 'undefined') {
109
+ localStorage.setItem(STORAGE_KEY, result.accessToken);
110
+ localStorage.setItem(USER_INFO_KEY, JSON.stringify(result.userInfo));
111
+ }
112
+ }
113
+
114
+ /**
115
+ * Get stored access token
116
+ */
117
+ export function getStoredToken(): string | null {
118
+ if (typeof window !== 'undefined') {
119
+ return localStorage.getItem(STORAGE_KEY);
120
+ }
121
+ return null;
122
+ }
123
+
124
+ /**
125
+ * Get stored user info
126
+ */
127
+ export function getStoredUserInfo(): OAuthUserInfo | null {
128
+ if (typeof window !== 'undefined') {
129
+ const userInfoStr = localStorage.getItem(USER_INFO_KEY);
130
+ if (userInfoStr) {
131
+ try {
132
+ return JSON.parse(userInfoStr);
133
+ } catch {
134
+ return null;
135
+ }
136
+ }
137
+ }
138
+ return null;
139
+ }
140
+
141
+ /**
142
+ * Check if user is authenticated
143
+ */
144
+ export function isAuthenticated(): boolean {
145
+ return getStoredToken() !== null;
146
+ }
147
+
148
+ /**
149
+ * Development mode login (mock authentication)
150
+ */
151
+ export function loginDevMode(username: string): OAuthResult {
152
+ const mockToken = `dev_token_${username}_${Date.now()}`;
153
+ const mockUserInfo: OAuthUserInfo = {
154
+ id: `dev_${Date.now()}`,
155
+ name: username,
156
+ preferredUsername: username.toLowerCase().replace(/\s+/g, '_'),
157
+ avatarUrl: `https://ui-avatars.com/api/?name=${encodeURIComponent(username)}&background=random&size=128`,
158
+ };
159
+
160
+ const result: OAuthResult = {
161
+ accessToken: mockToken,
162
+ accessTokenExpiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000),
163
+ userInfo: mockUserInfo,
164
+ };
165
+
166
+ // Store the mock data
167
+ storeOAuthData(result);
168
+ // Mark as dev mode
169
+ if (typeof window !== 'undefined') {
170
+ localStorage.setItem(DEV_MODE_KEY, 'true');
171
+ }
172
+
173
+ return result;
174
+ }
175
+
176
+ /**
177
+ * Check if dev mode is enabled
178
+ */
179
+ export function isDevModeEnabled(): boolean {
180
+ if (typeof window !== 'undefined') {
181
+ return localStorage.getItem(DEV_MODE_KEY) === 'true';
182
+ }
183
+ return false;
184
+ }
185
+
186
+ /**
187
+ * Check if we're in development environment
188
+ */
189
+ export function isDevelopmentMode(): boolean {
190
+ return isDevelopment;
191
+ }
192
+