| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | import { oauthLoginUrl, oauthHandleRedirectIfPresent, uploadFile, createRepo } from "https://cdn.jsdelivr.net/npm/@huggingface/hub@0.15.2/+esm"; |
| |
|
| | const HF_API = 'https://huggingface.co/api'; |
| | const NEW_SPACE_NAME = 'reachy_mini_remote_control'; |
| |
|
| | |
| | let currentUser = null; |
| | let userToken = null; |
| | let oauthResult = null; |
| |
|
| | |
| | |
| | |
| | async function initAuth() { |
| | try { |
| | |
| | oauthResult = await oauthHandleRedirectIfPresent(); |
| |
|
| | if (oauthResult) { |
| | |
| | console.log('OAuth result:', oauthResult); |
| | console.log('User info:', oauthResult.userInfo); |
| | console.log('All userInfo keys:', Object.keys(oauthResult.userInfo)); |
| |
|
| | |
| | currentUser = oauthResult.userInfo.fullname; |
| | userToken = oauthResult.accessToken; |
| |
|
| | console.log('Extracted username:', currentUser); |
| | console.log('Extracted token:', userToken ? 'present' : 'missing'); |
| |
|
| | |
| | sessionStorage.setItem('hf_token', userToken); |
| | sessionStorage.setItem('hf_username', currentUser); |
| | sessionStorage.setItem('hf_token_expires', oauthResult.accessTokenExpiresAt); |
| |
|
| | console.log('OAuth authentication successful:', currentUser); |
| | showAuthenticatedView(); |
| | await checkForExistingSpace(); |
| | } else { |
| | |
| | const storedToken = sessionStorage.getItem('hf_token'); |
| | const storedUser = sessionStorage.getItem('hf_username'); |
| | const tokenExpires = sessionStorage.getItem('hf_token_expires'); |
| |
|
| | if (storedToken && storedUser && tokenExpires && new Date(tokenExpires) > new Date()) { |
| | |
| | userToken = storedToken; |
| | currentUser = storedUser; |
| | showAuthenticatedView(); |
| | await checkForExistingSpace(); |
| | } |
| | } |
| | } catch (error) { |
| | console.error('OAuth initialization error:', error); |
| | |
| | showLoginView(); |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | async function loginToHuggingFace() { |
| | try { |
| | |
| | const loginUrl = await oauthLoginUrl(); |
| | window.location.href = loginUrl; |
| | } catch (error) { |
| | console.error('OAuth login error:', error); |
| | alert('Failed to initiate login. Please try again.'); |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | function showLoginView() { |
| | document.getElementById('login-view').style.display = 'block'; |
| | document.getElementById('authenticated-view').style.display = 'none'; |
| | } |
| |
|
| | |
| | |
| | |
| | function showAuthenticatedView() { |
| | console.log('Showing authenticated view for user:', currentUser); |
| | document.getElementById('login-view').style.display = 'none'; |
| | document.getElementById('authenticated-view').style.display = 'block'; |
| | |
| | document.getElementById('username').textContent = `@${currentUser}`; |
| | } |
| |
|
| | |
| | |
| | |
| | async function checkForExistingSpace() { |
| | try { |
| | const response = await fetch(`${HF_API}/spaces/${currentUser}/${NEW_SPACE_NAME}`, { |
| | headers: { |
| | 'Authorization': `Bearer ${userToken}` |
| | } |
| | }); |
| |
|
| | if (response.ok) { |
| | showExistingSpaceView(); |
| | } else { |
| | showNoSpaceView(); |
| | } |
| | } catch (error) { |
| | console.error('Error checking for space:', error); |
| | showNoSpaceView(); |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | function showNoSpaceView() { |
| | document.getElementById('no-space-view').style.display = 'block'; |
| | document.getElementById('has-space-view').style.display = 'none'; |
| | } |
| |
|
| | |
| | |
| | |
| | function showExistingSpaceView() { |
| | document.getElementById('no-space-view').style.display = 'none'; |
| | document.getElementById('has-space-view').style.display = 'block'; |
| |
|
| | |
| | const spaceUrl = `${currentUser}/${NEW_SPACE_NAME}`; |
| | |
| | |
| | const wsUri = `wss://${currentUser}-${NEW_SPACE_NAME.replace(/_/g, '-')}.hf.space`; |
| |
|
| | document.getElementById('space-url-display').textContent = spaceUrl; |
| | document.getElementById('private-uri').textContent = wsUri; |
| | document.getElementById('space-link').href = `https://huggingface.co/spaces/${spaceUrl}`; |
| | } |
| |
|
| | |
| | |
| | |
| | async function cloneSpace() { |
| | const btn = document.querySelector('#no-space-view .btn'); |
| | const btnText = document.getElementById('clone-btn-text'); |
| | const originalText = btnText.textContent; |
| |
|
| | try { |
| | |
| | if (!currentUser || !userToken) { |
| | throw new Error('Not authenticated. Please sign in again.'); |
| | } |
| |
|
| | |
| | btn.disabled = true; |
| | btnText.textContent = '⏳ Creating space...'; |
| |
|
| | console.log('Creating space for user:', currentUser); |
| | console.log('Creating repo:', `${currentUser}/${NEW_SPACE_NAME}`); |
| |
|
| | |
| | const createResult = await createRepo({ |
| | repo: { |
| | type: 'space', |
| | name: `${currentUser}/${NEW_SPACE_NAME}` |
| | }, |
| | accessToken: userToken, |
| | credentials: { |
| | accessToken: userToken |
| | }, |
| | hubUrl: 'https://huggingface.co', |
| | spaceHardware: 'cpu-basic', |
| | spaceSdk: 'gradio', |
| | private: true |
| | }); |
| |
|
| | console.log('Repo created successfully:', createResult); |
| | btnText.textContent = '⏳ Uploading files...'; |
| |
|
| | |
| | const files = ['README.md', 'app.py', 'requirements.txt', 'packages.txt', 'dist.zip']; |
| |
|
| | for (const fileName of files) { |
| | console.log(`Fetching ${fileName}...`); |
| | |
| | const fileResponse = await fetch(`remote_control_space/${fileName}`); |
| | if (!fileResponse.ok) { |
| | console.error(`Failed to fetch ${fileName}`); |
| | continue; |
| | } |
| | const content = await fileResponse.blob(); |
| |
|
| | console.log(`Uploading ${fileName}...`); |
| | |
| | const uploadResult = await uploadFile({ |
| | repo: { |
| | type: 'space', |
| | name: `${currentUser}/${NEW_SPACE_NAME}` |
| | }, |
| | accessToken: userToken, |
| | credentials: { |
| | accessToken: userToken |
| | }, |
| | file: { |
| | path: fileName, |
| | content: content |
| | } |
| | }); |
| | console.log(`${fileName} uploaded:`, uploadResult); |
| | } |
| |
|
| | btnText.textContent = '✓ Space created successfully!'; |
| |
|
| | |
| | setTimeout(() => { |
| | showExistingSpaceView(); |
| | }, 1500); |
| |
|
| | } catch (error) { |
| | console.error('Error creating space:', error); |
| | alert(`Failed to create space: ${error.message}\n\nPlease try again or create the space manually.`); |
| | btn.disabled = false; |
| | btnText.textContent = originalText; |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | function logout() { |
| | if (confirm('Are you sure you want to sign out?')) { |
| | localStorage.removeItem('hf_token'); |
| | localStorage.removeItem('hf_username'); |
| | currentUser = null; |
| | userToken = null; |
| |
|
| | |
| | document.getElementById('login-view').style.display = 'block'; |
| | document.getElementById('authenticated-view').style.display = 'none'; |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | function copyToClipboard(text, buttonElement) { |
| | navigator.clipboard.writeText(text).then(() => { |
| | |
| | if (buttonElement) { |
| | const originalText = buttonElement.textContent; |
| | buttonElement.textContent = '✓ Copied!'; |
| | setTimeout(() => { |
| | buttonElement.textContent = originalText; |
| | }, 2000); |
| | } |
| | }).catch(err => { |
| | console.error('Failed to copy:', err); |
| | alert('Failed to copy to clipboard'); |
| | }); |
| | } |
| |
|
| | |
| | |
| | |
| | function copyPrivateUri(event) { |
| | const uri = document.getElementById('private-uri').textContent; |
| | copyToClipboard(uri, event.currentTarget); |
| | } |
| |
|
| | |
| | window.loginToHuggingFace = loginToHuggingFace; |
| | window.cloneSpace = cloneSpace; |
| | window.logout = logout; |
| | window.copyToClipboard = copyToClipboard; |
| | window.copyPrivateUri = copyPrivateUri; |
| |
|
| | |
| | document.addEventListener('DOMContentLoaded', initAuth); |
| |
|