Charlie commited on
Commit
b200338
·
1 Parent(s): b508f56

update files

Browse files
Files changed (5) hide show
  1. dist/main.js +121 -365
  2. index.html +8 -4
  3. src/main.ts +124 -106
  4. src/oauth.ts +14 -43
  5. styles/main.css +11 -0
dist/main.js CHANGED
@@ -1,291 +1,31 @@
1
- // node_modules/@huggingface/hub/dist/index.mjs
2
- var HUB_URL = "https://huggingface.co";
3
- async function createApiError(response, opts) {
4
- const error = new HubApiError(response.url, response.status, response.headers.get("X-Request-Id") ?? opts?.requestId);
5
- error.message = `Api error with status ${error.statusCode}${opts?.message ? `. ${opts.message}` : ""}`;
6
- const trailer = [`URL: ${error.url}`, error.requestId ? `Request ID: ${error.requestId}` : void 0].filter(Boolean).join(". ");
7
- if (response.headers.get("Content-Type")?.startsWith("application/json")) {
8
- const json = await response.json();
9
- error.message = json.error || json.message || error.message;
10
- if (json.error_description) {
11
- error.message = error.message ? error.message + `: ${json.error_description}` : json.error_description;
12
- }
13
- error.data = json;
14
- } else {
15
- error.data = { message: await response.text() };
16
- }
17
- error.message += `. ${trailer}`;
18
- throw error;
19
- }
20
- var HubApiError = class extends Error {
21
- statusCode;
22
- url;
23
- requestId;
24
- data;
25
- constructor(url, statusCode, requestId, message) {
26
- super(message);
27
- this.statusCode = statusCode;
28
- this.requestId = requestId;
29
- this.url = url;
30
- }
31
- };
32
- var isBrowser = typeof window !== "undefined" && typeof window.document !== "undefined";
33
- var isWebWorker = typeof self === "object" && self.constructor && self.constructor.name === "DedicatedWorkerGlobalScope";
34
- var resolve;
35
- var waitPromise = new Promise((r) => {
36
- resolve = r;
37
- });
38
- function base64FromBytes(arr) {
39
- if (globalThis.Buffer) {
40
- return globalThis.Buffer.from(arr).toString("base64");
41
- } else {
42
- const bin = [];
43
- arr.forEach((byte) => {
44
- bin.push(String.fromCharCode(byte));
45
- });
46
- return globalThis.btoa(bin.join(""));
47
- }
48
- }
49
- var REGEX_COMMIT_HASH = new RegExp("^[0-9a-f]{40}$");
50
- async function oauthHandleRedirect(opts) {
51
- if (typeof window === "undefined" && !opts?.redirectedUrl) {
52
- throw new Error("oauthHandleRedirect is only available in the browser, unless you provide redirectedUrl");
53
- }
54
- if (typeof localStorage === "undefined" && (!opts?.nonce || !opts?.codeVerifier)) {
55
- throw new Error(
56
- "oauthHandleRedirect requires localStorage to be available, unless you provide nonce and codeVerifier"
57
- );
58
- }
59
- const redirectedUrl = opts?.redirectedUrl ?? window.location.href;
60
- const searchParams = (() => {
61
- try {
62
- return new URL(redirectedUrl).searchParams;
63
- } catch (err) {
64
- throw new Error("Failed to parse redirected URL: " + redirectedUrl);
65
- }
66
- })();
67
- const [error, errorDescription] = [searchParams.get("error"), searchParams.get("error_description")];
68
- if (error) {
69
- throw new Error(`${error}: ${errorDescription}`);
70
- }
71
- const code = searchParams.get("code");
72
- const nonce = opts?.nonce ?? localStorage.getItem("huggingface.co:oauth:nonce");
73
- if (!code) {
74
- throw new Error("Missing oauth code from query parameters in redirected URL: " + redirectedUrl);
75
- }
76
- if (!nonce) {
77
- throw new Error("Missing oauth nonce from localStorage");
78
- }
79
- const codeVerifier = opts?.codeVerifier ?? localStorage.getItem("huggingface.co:oauth:code_verifier");
80
- if (!codeVerifier) {
81
- throw new Error("Missing oauth code_verifier from localStorage");
82
- }
83
- const state = searchParams.get("state");
84
- if (!state) {
85
- throw new Error("Missing oauth state from query parameters in redirected URL");
86
- }
87
- let parsedState;
88
- try {
89
- parsedState = JSON.parse(state);
90
- } catch {
91
- throw new Error("Invalid oauth state in redirected URL, unable to parse JSON: " + state);
92
- }
93
- if (parsedState.nonce !== nonce) {
94
- throw new Error("Invalid oauth state in redirected URL");
95
- }
96
- const hubUrl = opts?.hubUrl || HUB_URL;
97
- const openidConfigUrl = `${new URL(hubUrl).origin}/.well-known/openid-configuration`;
98
- const openidConfigRes = await fetch(openidConfigUrl, {
99
- headers: {
100
- Accept: "application/json"
101
- }
102
- });
103
- if (!openidConfigRes.ok) {
104
- throw await createApiError(openidConfigRes);
105
- }
106
- const openidConfig = await openidConfigRes.json();
107
- const tokenRes = await fetch(openidConfig.token_endpoint, {
108
- method: "POST",
109
- headers: {
110
- "Content-Type": "application/x-www-form-urlencoded"
111
- },
112
- body: new URLSearchParams({
113
- grant_type: "authorization_code",
114
- code,
115
- redirect_uri: parsedState.redirectUri,
116
- code_verifier: codeVerifier
117
- }).toString()
118
- });
119
- if (!opts?.codeVerifier) {
120
- localStorage.removeItem("huggingface.co:oauth:code_verifier");
121
- }
122
- if (!opts?.nonce) {
123
- localStorage.removeItem("huggingface.co:oauth:nonce");
124
- }
125
- if (!tokenRes.ok) {
126
- throw await createApiError(tokenRes);
127
- }
128
- const token = await tokenRes.json();
129
- const accessTokenExpiresAt = new Date(Date.now() + token.expires_in * 1e3);
130
- const userInfoRes = await fetch(openidConfig.userinfo_endpoint, {
131
- headers: {
132
- Authorization: `Bearer ${token.access_token}`
133
- }
134
- });
135
- if (!userInfoRes.ok) {
136
- throw await createApiError(userInfoRes);
137
- }
138
- const userInfo = await userInfoRes.json();
139
- return {
140
- accessToken: token.access_token,
141
- accessTokenExpiresAt,
142
- userInfo,
143
- state: parsedState.state,
144
- scope: token.scope
145
- };
146
- }
147
- async function oauthHandleRedirectIfPresent(opts) {
148
- if (typeof window === "undefined" && !opts?.redirectedUrl) {
149
- throw new Error("oauthHandleRedirect is only available in the browser, unless you provide redirectedUrl");
150
- }
151
- if (typeof localStorage === "undefined" && (!opts?.nonce || !opts?.codeVerifier)) {
152
- throw new Error(
153
- "oauthHandleRedirect requires localStorage to be available, unless you provide nonce and codeVerifier"
154
- );
155
- }
156
- const searchParams = new URLSearchParams(opts?.redirectedUrl ?? window.location.search);
157
- if (searchParams.has("error")) {
158
- return oauthHandleRedirect(opts);
159
- }
160
- if (searchParams.has("code")) {
161
- if (!localStorage.getItem("huggingface.co:oauth:nonce")) {
162
- console.warn(
163
- "Missing oauth nonce from localStorage. This can happen when the user refreshes the page after logging in, without changing the URL."
164
- );
165
- return false;
166
- }
167
- return oauthHandleRedirect(opts);
168
- }
169
- return false;
170
- }
171
- async function oauthLoginUrl(opts) {
172
- if (typeof window === "undefined" && (!opts?.redirectUrl || !opts?.clientId)) {
173
- throw new Error("oauthLogin is only available in the browser, unless you provide clientId and redirectUrl");
174
- }
175
- if (typeof localStorage === "undefined" && !opts?.localStorage) {
176
- throw new Error(
177
- "oauthLogin requires localStorage to be available in the context, unless you provide a localStorage empty object as argument"
178
- );
179
- }
180
- const hubUrl = opts?.hubUrl || HUB_URL;
181
- const openidConfigUrl = `${new URL(hubUrl).origin}/.well-known/openid-configuration`;
182
- const openidConfigRes = await fetch(openidConfigUrl, {
183
- headers: {
184
- Accept: "application/json"
185
- }
186
- });
187
- if (!openidConfigRes.ok) {
188
- throw await createApiError(openidConfigRes);
189
- }
190
- const opendidConfig = await openidConfigRes.json();
191
- const newNonce = globalThis.crypto.randomUUID();
192
- const newCodeVerifier = globalThis.crypto.randomUUID() + globalThis.crypto.randomUUID();
193
- if (opts?.localStorage) {
194
- if (opts.localStorage.codeVerifier !== void 0 && opts.localStorage.codeVerifier !== null) {
195
- throw new Error(
196
- "localStorage.codeVerifier must be a initially set to null or undefined, and will be filled by oauthLoginUrl"
197
- );
198
- }
199
- if (opts.localStorage.nonce !== void 0 && opts.localStorage.nonce !== null) {
200
- throw new Error(
201
- "localStorage.nonce must be a initially set to null or undefined, and will be filled by oauthLoginUrl"
202
- );
203
- }
204
- opts.localStorage.codeVerifier = newCodeVerifier;
205
- opts.localStorage.nonce = newNonce;
206
- } else {
207
- localStorage.setItem("huggingface.co:oauth:nonce", newNonce);
208
- localStorage.setItem("huggingface.co:oauth:code_verifier", newCodeVerifier);
209
- }
210
- const redirectUri = opts?.redirectUrl || (typeof window !== "undefined" ? window.location.href : void 0);
211
- if (!redirectUri) {
212
- throw new Error("Missing redirectUrl");
213
- }
214
- const state = JSON.stringify({
215
- nonce: newNonce,
216
- redirectUri,
217
- state: opts?.state
218
- });
219
- const variables = (
220
- // @ts-expect-error window.huggingface is defined inside static Spaces.
221
- typeof window !== "undefined" ? window.huggingface?.variables ?? null : null
222
- );
223
- const clientId = opts?.clientId || variables?.OAUTH_CLIENT_ID;
224
- if (!clientId) {
225
- if (variables) {
226
- throw new Error("Missing clientId, please add hf_oauth: true to the README.md's metadata in your static Space");
227
- }
228
- throw new Error("Missing clientId");
229
- }
230
- const challenge = base64FromBytes(
231
- new Uint8Array(await globalThis.crypto.subtle.digest("SHA-256", new TextEncoder().encode(newCodeVerifier)))
232
- ).replace(/[+]/g, "-").replace(/[/]/g, "_").replace(/=/g, "");
233
- return `${opendidConfig.authorization_endpoint}?${new URLSearchParams({
234
- client_id: clientId,
235
- scope: opts?.scopes || variables?.OAUTH_SCOPES || "openid profile",
236
- response_type: "code",
237
- redirect_uri: redirectUri,
238
- state,
239
- code_challenge: challenge,
240
- code_challenge_method: "S256"
241
- }).toString()}`;
242
- }
243
-
244
  // src/oauth.ts
245
- var OAuthManager = class {
246
  constructor() {
247
- this.oauthResult = null;
248
- const storedOAuth = localStorage.getItem("oauth");
249
- if (storedOAuth) {
250
- try {
251
- this.oauthResult = JSON.parse(storedOAuth);
252
- } catch {
253
- this.oauthResult = null;
254
- }
255
  }
256
  }
257
- async handleRedirect() {
258
- this.oauthResult ||= await oauthHandleRedirectIfPresent();
259
- if (this.oauthResult !== null && this.oauthResult !== false) {
260
- localStorage.setItem("oauth", JSON.stringify(this.oauthResult));
261
- }
262
- return this.oauthResult;
263
- }
264
- async initiateLogin() {
265
- const loginUrl = await oauthLoginUrl({
266
- scopes: window.huggingface?.variables?.OAUTH_SCOPES ?? ""
267
- });
268
- window.location.href = `${loginUrl}&prompt=consent`;
269
  }
270
  logout() {
271
- localStorage.removeItem("oauth");
272
- window.location.href = window.location.href.replace(/\?.*$/, "");
273
  window.location.reload();
274
  }
275
  getAccessToken() {
276
- if (this.oauthResult && typeof this.oauthResult === "object") {
277
- return this.oauthResult.accessToken ?? null;
278
- }
279
- return null;
280
  }
281
  isAuthenticated() {
282
- return this.oauthResult !== null && this.oauthResult !== false;
283
  }
284
  };
285
- var oauthManager = new OAuthManager();
286
 
287
  // src/main.ts
288
- console.log("huggingface env", window.huggingface);
289
  async function getOrganizationInfo(accessToken) {
290
  const response = await fetch("https://huggingface.co/api/whoami-v2", {
291
  headers: {
@@ -366,102 +106,118 @@ async function createRepository(accessToken, name, organization, resourceGroupNa
366
  }
367
  }
368
  var init = async () => {
369
- await oauthManager.handleRedirect();
370
- if (oauthManager.isAuthenticated()) {
371
- const accessToken = oauthManager.getAccessToken();
372
- if (!accessToken) {
373
- throw new Error("Access token not found");
374
- }
375
- const repoForm = document.getElementById("repo-form");
376
- if (repoForm) {
377
- repoForm.style.removeProperty("display");
378
- }
379
- const createRepoButton = document.getElementById("create-repo");
380
- const repoNameInput = document.getElementById(
381
- "repo-name"
382
- );
383
- const resourceGroupInput = document.getElementById(
384
- "resource-group-name"
385
- );
386
- const resourceGroupContainer = document.getElementById(
387
- "resource-group-container"
388
- );
389
- if (createRepoButton && repoNameInput) {
390
- createRepoButton.onclick = async () => {
391
- const repoName = repoNameInput.value.trim();
392
- const orgSelect2 = document.getElementById(
393
- "org-select"
394
- );
395
- if (repoName) {
396
- try {
397
- const selectedOrg = orgSelect2?.value || void 0;
398
- const resourceGroupName = selectedOrg ? resourceGroupInput?.value.trim() : void 0;
399
- console.log({ selectedOrg, resourceGroupName });
400
- await createRepository(
401
- accessToken,
402
- repoName,
403
- selectedOrg,
404
- resourceGroupName
405
- );
406
- repoNameInput.value = "";
407
- if (resourceGroupInput) {
408
- resourceGroupInput.value = "";
409
- }
410
- alert("Repository and resource group created successfully!");
411
- } catch (error) {
412
- console.error("Failed to create repository:", error);
413
- alert("Failed to create repository. Please try again.");
 
 
 
 
 
 
 
 
 
414
  }
 
 
 
 
415
  }
416
- };
417
- }
418
- const orgSelect = document.getElementById(
419
- "org-select"
420
- );
421
- if (orgSelect && resourceGroupContainer) {
422
- orgSelect.onchange = () => {
423
- if (orgSelect.value) {
424
- resourceGroupContainer.style.removeProperty("display");
425
- } else {
426
- resourceGroupContainer.style.display = "none";
427
- }
428
- };
429
- }
430
- try {
431
- const orgInfo = await getOrganizationInfo(accessToken);
432
- if (orgSelect && orgInfo.orgs) {
433
- orgInfo.orgs.forEach((org) => {
434
- const option = document.createElement("option");
435
- option.value = org.name;
436
- option.textContent = org.name;
437
- orgSelect.appendChild(option);
438
- });
439
  }
440
- const preElement = document.querySelector("pre");
441
- if (preElement) {
442
- preElement.textContent = JSON.stringify(
443
- {
444
- oauth: oauthManager.getAccessToken(),
445
- user: orgInfo
446
- },
447
- null,
448
- 2
449
- );
450
  }
451
- } catch (error) {
452
- console.error("Failed to fetch organization info:", error);
453
- }
454
- const signoutButton = document.getElementById("signout");
455
- if (signoutButton) {
456
- signoutButton.style.removeProperty("display");
457
- signoutButton.onclick = () => oauthManager.logout();
458
- }
459
- } else {
460
- const signinButton = document.getElementById("signin");
461
- if (signinButton) {
462
- signinButton.style.removeProperty("display");
463
- signinButton.onclick = () => oauthManager.initiateLogin();
464
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
465
  }
466
  };
467
  init().catch(console.error);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  // src/oauth.ts
2
+ var TokenManager = class {
3
  constructor() {
4
+ this.token = null;
5
+ const storedToken = localStorage.getItem("token");
6
+ if (storedToken) {
7
+ this.token = storedToken;
 
 
 
 
8
  }
9
  }
10
+ setToken(token) {
11
+ this.token = token;
12
+ localStorage.setItem("token", token);
 
 
 
 
 
 
 
 
 
13
  }
14
  logout() {
15
+ this.token = null;
16
+ localStorage.removeItem("token");
17
  window.location.reload();
18
  }
19
  getAccessToken() {
20
+ return this.token;
 
 
 
21
  }
22
  isAuthenticated() {
23
+ return this.token !== null;
24
  }
25
  };
26
+ var tokenManager = new TokenManager();
27
 
28
  // src/main.ts
 
29
  async function getOrganizationInfo(accessToken) {
30
  const response = await fetch("https://huggingface.co/api/whoami-v2", {
31
  headers: {
 
106
  }
107
  }
108
  var init = async () => {
109
+ if (tokenManager.isAuthenticated()) {
110
+ showAuthenticatedUI();
111
+ } else {
112
+ showUnauthenticatedUI();
113
+ }
114
+ };
115
+ var showAuthenticatedUI = async () => {
116
+ const accessToken = tokenManager.getAccessToken();
117
+ if (!accessToken) {
118
+ throw new Error("Access token not found");
119
+ }
120
+ const tokenForm = document.getElementById("token-form");
121
+ if (tokenForm) {
122
+ tokenForm.style.display = "none";
123
+ }
124
+ const repoForm = document.getElementById("repo-form");
125
+ const signoutButton = document.getElementById("signout");
126
+ if (repoForm) {
127
+ repoForm.style.removeProperty("display");
128
+ }
129
+ if (signoutButton) {
130
+ signoutButton.style.removeProperty("display");
131
+ signoutButton.onclick = () => tokenManager.logout();
132
+ }
133
+ const createRepoButton = document.getElementById("create-repo");
134
+ const repoNameInput = document.getElementById(
135
+ "repo-name"
136
+ );
137
+ const resourceGroupInput = document.getElementById(
138
+ "resource-group-name"
139
+ );
140
+ const resourceGroupContainer = document.getElementById(
141
+ "resource-group-container"
142
+ );
143
+ if (createRepoButton && repoNameInput) {
144
+ createRepoButton.onclick = async () => {
145
+ const repoName = repoNameInput.value.trim();
146
+ const orgSelect2 = document.getElementById(
147
+ "org-select"
148
+ );
149
+ if (repoName) {
150
+ try {
151
+ const selectedOrg = orgSelect2?.value || void 0;
152
+ const resourceGroupName = selectedOrg ? resourceGroupInput?.value.trim() : void 0;
153
+ console.log({ selectedOrg, resourceGroupName });
154
+ await createRepository(
155
+ accessToken,
156
+ repoName,
157
+ selectedOrg,
158
+ resourceGroupName
159
+ );
160
+ repoNameInput.value = "";
161
+ if (resourceGroupInput) {
162
+ resourceGroupInput.value = "";
163
  }
164
+ alert("Repository and resource group created successfully!");
165
+ } catch (error) {
166
+ console.error("Failed to create repository:", error);
167
+ alert("Failed to create repository. Please try again.");
168
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
169
  }
170
+ };
171
+ }
172
+ const orgSelect = document.getElementById("org-select");
173
+ if (orgSelect && resourceGroupContainer) {
174
+ orgSelect.onchange = () => {
175
+ if (orgSelect.value) {
176
+ resourceGroupContainer.style.removeProperty("display");
177
+ } else {
178
+ resourceGroupContainer.style.display = "none";
 
179
  }
180
+ };
181
+ }
182
+ try {
183
+ const orgInfo = await getOrganizationInfo(accessToken);
184
+ if (orgSelect && orgInfo.orgs) {
185
+ orgInfo.orgs.forEach((org) => {
186
+ const option = document.createElement("option");
187
+ option.value = org.name;
188
+ option.textContent = org.name;
189
+ orgSelect.appendChild(option);
190
+ });
191
+ }
192
+ const preElement = document.querySelector("pre");
193
+ if (preElement) {
194
+ preElement.textContent = JSON.stringify(orgInfo, null, 2);
195
+ }
196
+ } catch (error) {
197
+ console.error("Failed to fetch organization info:", error);
198
+ }
199
+ };
200
+ var showUnauthenticatedUI = () => {
201
+ const tokenForm = document.getElementById("token-form");
202
+ const tokenInput = document.getElementById("token-input");
203
+ const tokenSubmit = document.getElementById("token-submit");
204
+ if (tokenForm && tokenInput && tokenSubmit) {
205
+ tokenForm.style.removeProperty("display");
206
+ tokenSubmit.onclick = () => {
207
+ const token = tokenInput.value.trim();
208
+ if (token) {
209
+ tokenManager.setToken(token);
210
+ showAuthenticatedUI();
211
+ }
212
+ };
213
+ }
214
+ const repoForm = document.getElementById("repo-form");
215
+ const signoutButton = document.getElementById("signout");
216
+ if (repoForm) {
217
+ repoForm.style.display = "none";
218
+ }
219
+ if (signoutButton) {
220
+ signoutButton.style.display = "none";
221
  }
222
  };
223
  init().catch(console.error);
index.html CHANGED
@@ -4,15 +4,19 @@
4
  <head>
5
  <meta charset="UTF-8">
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
- <title>HuggingFace OAuth Demo</title>
8
  <link rel="stylesheet" href="styles/main.css">
9
  </head>
10
 
11
  <body>
12
  <div id="app">
13
- <h1>HuggingFace OAuth Demo</h1>
14
  <div class="auth-container">
15
- <button id="signin" style="display: none;" class="auth-button">Sign in with HuggingFace</button>
 
 
 
 
16
  <button id="signout" style="display: none;" class="auth-button">Sign out</button>
17
  </div>
18
  <div id="repo-form" style="display: none;" class="repo-form">
@@ -26,7 +30,7 @@
26
  <button id="create-repo" class="repo-button">Create Repository</button>
27
  </div>
28
  <div id="content">
29
- <h2>OAuth Result</h2>
30
  <pre class="oauth-result"></pre>
31
  </div>
32
  </div>
 
4
  <head>
5
  <meta charset="UTF-8">
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>HuggingFace API Demo</title>
8
  <link rel="stylesheet" href="styles/main.css">
9
  </head>
10
 
11
  <body>
12
  <div id="app">
13
+ <h1>HuggingFace API Demo</h1>
14
  <div class="auth-container">
15
+ <div id="token-form" class="token-form">
16
+ <input type="password" id="token-input" placeholder="Enter your HuggingFace API token"
17
+ class="repo-input">
18
+ <button id="token-submit" class="auth-button">Submit Token</button>
19
+ </div>
20
  <button id="signout" style="display: none;" class="auth-button">Sign out</button>
21
  </div>
22
  <div id="repo-form" style="display: none;" class="repo-form">
 
30
  <button id="create-repo" class="repo-button">Create Repository</button>
31
  </div>
32
  <div id="content">
33
+ <h2>User Info</h2>
34
  <pre class="oauth-result"></pre>
35
  </div>
36
  </div>
src/main.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { oauthManager, HuggingFaceWindow } from "./oauth";
2
 
3
  interface OrganizationInfo {
4
  type: string;
@@ -22,10 +22,6 @@ interface OrganizationMember {
22
  role: string;
23
  }
24
 
25
- declare const window: HuggingFaceWindow;
26
-
27
- console.log("huggingface env", window.huggingface);
28
-
29
  async function getOrganizationInfo(
30
  accessToken: string
31
  ): Promise<WhoAmIResponse> {
@@ -133,120 +129,142 @@ async function createRepository(
133
  }
134
 
135
  const init = async (): Promise<void> => {
136
- await oauthManager.handleRedirect();
 
 
 
 
 
137
 
138
- if (oauthManager.isAuthenticated()) {
139
- const accessToken = oauthManager.getAccessToken();
140
- if (!accessToken) {
141
- throw new Error("Access token not found");
142
- }
143
 
144
- // Show repo form when logged in
145
- const repoForm = document.getElementById("repo-form");
146
- if (repoForm) {
147
- repoForm.style.removeProperty("display");
148
- }
149
 
150
- // Add create repo functionality
151
- const createRepoButton = document.getElementById("create-repo");
152
- const repoNameInput = document.getElementById(
153
- "repo-name"
154
- ) as HTMLInputElement;
155
- const resourceGroupInput = document.getElementById(
156
- "resource-group-name"
157
- ) as HTMLInputElement;
158
- const resourceGroupContainer = document.getElementById(
159
- "resource-group-container"
160
- );
161
 
162
- if (createRepoButton && repoNameInput) {
163
- createRepoButton.onclick = async () => {
164
- const repoName = repoNameInput.value.trim();
165
- const orgSelect = document.getElementById(
166
- "org-select"
167
- ) as HTMLSelectElement;
168
- if (repoName) {
169
- try {
170
- const selectedOrg = orgSelect?.value || undefined;
171
- const resourceGroupName = selectedOrg
172
- ? resourceGroupInput?.value.trim()
173
- : undefined;
174
-
175
- console.log({ selectedOrg, resourceGroupName });
176
- await createRepository(
177
- accessToken,
178
- repoName,
179
- selectedOrg,
180
- resourceGroupName
181
- );
182
- repoNameInput.value = ""; // Clear input after success
183
- if (resourceGroupInput) {
184
- resourceGroupInput.value = ""; // Clear resource group input
185
- }
186
- alert("Repository and resource group created successfully!");
187
- } catch (error) {
188
- console.error("Failed to create repository:", error);
189
- alert("Failed to create repository. Please try again.");
 
 
 
 
 
 
 
190
  }
 
 
 
 
191
  }
192
- };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
193
  }
194
 
195
- // Handle org select change to show/hide resource group input
196
- const orgSelect = document.getElementById(
197
- "org-select"
198
- ) as HTMLSelectElement;
199
- if (orgSelect && resourceGroupContainer) {
200
- orgSelect.onchange = () => {
201
- if (orgSelect.value) {
202
- resourceGroupContainer.style.removeProperty("display");
203
- } else {
204
- resourceGroupContainer.style.display = "none";
205
- }
206
- };
207
  }
 
 
 
 
208
 
209
- // Get organization info and populate org select after successful authentication
210
- try {
211
- const orgInfo = await getOrganizationInfo(accessToken);
212
-
213
- // Populate org select
214
- if (orgSelect && orgInfo.orgs) {
215
- orgInfo.orgs.forEach((org) => {
216
- const option = document.createElement("option");
217
- option.value = org.name;
218
- option.textContent = org.name;
219
- orgSelect.appendChild(option);
220
- });
221
- }
222
 
223
- // Display full info in pre element
224
- const preElement = document.querySelector("pre");
225
- if (preElement) {
226
- preElement.textContent = JSON.stringify(
227
- {
228
- oauth: oauthManager.getAccessToken(),
229
- user: orgInfo,
230
- },
231
- null,
232
- 2
233
- );
234
  }
235
- } catch (error) {
236
- console.error("Failed to fetch organization info:", error);
237
- }
238
 
239
- const signoutButton = document.getElementById("signout");
240
- if (signoutButton) {
241
- signoutButton.style.removeProperty("display");
242
- signoutButton.onclick = () => oauthManager.logout();
243
- }
244
- } else {
245
- const signinButton = document.getElementById("signin");
246
- if (signinButton) {
247
- signinButton.style.removeProperty("display");
248
- signinButton.onclick = () => oauthManager.initiateLogin();
249
- }
250
  }
251
  };
252
 
 
1
+ import { tokenManager } from "./oauth";
2
 
3
  interface OrganizationInfo {
4
  type: string;
 
22
  role: string;
23
  }
24
 
 
 
 
 
25
  async function getOrganizationInfo(
26
  accessToken: string
27
  ): Promise<WhoAmIResponse> {
 
129
  }
130
 
131
  const init = async (): Promise<void> => {
132
+ if (tokenManager.isAuthenticated()) {
133
+ showAuthenticatedUI();
134
+ } else {
135
+ showUnauthenticatedUI();
136
+ }
137
+ };
138
 
139
+ const showAuthenticatedUI = async () => {
140
+ const accessToken = tokenManager.getAccessToken();
141
+ if (!accessToken) {
142
+ throw new Error("Access token not found");
143
+ }
144
 
145
+ // Hide token form
146
+ const tokenForm = document.getElementById("token-form");
147
+ if (tokenForm) {
148
+ tokenForm.style.display = "none";
149
+ }
150
 
151
+ // Show repo form and signout button
152
+ const repoForm = document.getElementById("repo-form");
153
+ const signoutButton = document.getElementById("signout");
154
+ if (repoForm) {
155
+ repoForm.style.removeProperty("display");
156
+ }
157
+ if (signoutButton) {
158
+ signoutButton.style.removeProperty("display");
159
+ signoutButton.onclick = () => tokenManager.logout();
160
+ }
 
161
 
162
+ // Add create repo functionality
163
+ const createRepoButton = document.getElementById("create-repo");
164
+ const repoNameInput = document.getElementById(
165
+ "repo-name"
166
+ ) as HTMLInputElement;
167
+ const resourceGroupInput = document.getElementById(
168
+ "resource-group-name"
169
+ ) as HTMLInputElement;
170
+ const resourceGroupContainer = document.getElementById(
171
+ "resource-group-container"
172
+ );
173
+
174
+ if (createRepoButton && repoNameInput) {
175
+ createRepoButton.onclick = async () => {
176
+ const repoName = repoNameInput.value.trim();
177
+ const orgSelect = document.getElementById(
178
+ "org-select"
179
+ ) as HTMLSelectElement;
180
+ if (repoName) {
181
+ try {
182
+ const selectedOrg = orgSelect?.value || undefined;
183
+ const resourceGroupName = selectedOrg
184
+ ? resourceGroupInput?.value.trim()
185
+ : undefined;
186
+
187
+ console.log({ selectedOrg, resourceGroupName });
188
+ await createRepository(
189
+ accessToken,
190
+ repoName,
191
+ selectedOrg,
192
+ resourceGroupName
193
+ );
194
+ repoNameInput.value = ""; // Clear input after success
195
+ if (resourceGroupInput) {
196
+ resourceGroupInput.value = ""; // Clear resource group input
197
  }
198
+ alert("Repository and resource group created successfully!");
199
+ } catch (error) {
200
+ console.error("Failed to create repository:", error);
201
+ alert("Failed to create repository. Please try again.");
202
  }
203
+ }
204
+ };
205
+ }
206
+
207
+ // Handle org select change to show/hide resource group input
208
+ const orgSelect = document.getElementById("org-select") as HTMLSelectElement;
209
+ if (orgSelect && resourceGroupContainer) {
210
+ orgSelect.onchange = () => {
211
+ if (orgSelect.value) {
212
+ resourceGroupContainer.style.removeProperty("display");
213
+ } else {
214
+ resourceGroupContainer.style.display = "none";
215
+ }
216
+ };
217
+ }
218
+
219
+ // Get organization info and populate org select
220
+ try {
221
+ const orgInfo = await getOrganizationInfo(accessToken);
222
+
223
+ // Populate org select
224
+ if (orgSelect && orgInfo.orgs) {
225
+ orgInfo.orgs.forEach((org) => {
226
+ const option = document.createElement("option");
227
+ option.value = org.name;
228
+ option.textContent = org.name;
229
+ orgSelect.appendChild(option);
230
+ });
231
  }
232
 
233
+ // Display user info
234
+ const preElement = document.querySelector("pre");
235
+ if (preElement) {
236
+ preElement.textContent = JSON.stringify(orgInfo, null, 2);
 
 
 
 
 
 
 
 
237
  }
238
+ } catch (error) {
239
+ console.error("Failed to fetch organization info:", error);
240
+ }
241
+ };
242
 
243
+ const showUnauthenticatedUI = () => {
244
+ // Show token form
245
+ const tokenForm = document.getElementById("token-form");
246
+ const tokenInput = document.getElementById("token-input") as HTMLInputElement;
247
+ const tokenSubmit = document.getElementById("token-submit");
 
 
 
 
 
 
 
 
248
 
249
+ if (tokenForm && tokenInput && tokenSubmit) {
250
+ tokenForm.style.removeProperty("display");
251
+ tokenSubmit.onclick = () => {
252
+ const token = tokenInput.value.trim();
253
+ if (token) {
254
+ tokenManager.setToken(token);
255
+ showAuthenticatedUI();
 
 
 
 
256
  }
257
+ };
258
+ }
 
259
 
260
+ // Hide other UI elements
261
+ const repoForm = document.getElementById("repo-form");
262
+ const signoutButton = document.getElementById("signout");
263
+ if (repoForm) {
264
+ repoForm.style.display = "none";
265
+ }
266
+ if (signoutButton) {
267
+ signoutButton.style.display = "none";
 
 
 
268
  }
269
  };
270
 
src/oauth.ts CHANGED
@@ -1,62 +1,33 @@
1
- import { oauthLoginUrl, oauthHandleRedirectIfPresent } from "@huggingface/hub";
2
 
3
- export interface HuggingFaceWindow extends Window {
4
- huggingface?: {
5
- variables: {
6
- OAUTH_SCOPES: string;
7
- };
8
- };
9
- }
10
-
11
- declare const window: HuggingFaceWindow;
12
-
13
- export type OAuthResult = Record<string, any> | null | false;
14
-
15
- export class OAuthManager {
16
- private oauthResult: OAuthResult = null;
17
 
18
  constructor() {
19
- const storedOAuth = localStorage.getItem("oauth");
20
- if (storedOAuth) {
21
- try {
22
- this.oauthResult = JSON.parse(storedOAuth);
23
- } catch {
24
- this.oauthResult = null;
25
- }
26
  }
27
  }
28
 
29
- async handleRedirect(): Promise<OAuthResult> {
30
- this.oauthResult ||= await oauthHandleRedirectIfPresent();
31
- if (this.oauthResult !== null && this.oauthResult !== false) {
32
- localStorage.setItem("oauth", JSON.stringify(this.oauthResult));
33
- }
34
- return this.oauthResult;
35
- }
36
-
37
- async initiateLogin(): Promise<void> {
38
- const loginUrl = await oauthLoginUrl({
39
- scopes: window.huggingface?.variables?.OAUTH_SCOPES ?? "",
40
- });
41
- window.location.href = `${loginUrl}&prompt=consent`;
42
  }
43
 
44
  logout(): void {
45
- localStorage.removeItem("oauth");
46
- window.location.href = window.location.href.replace(/\?.*$/, "");
47
  window.location.reload();
48
  }
49
 
50
  getAccessToken(): string | null {
51
- if (this.oauthResult && typeof this.oauthResult === "object") {
52
- return this.oauthResult.accessToken ?? null;
53
- }
54
- return null;
55
  }
56
 
57
  isAuthenticated(): boolean {
58
- return this.oauthResult !== null && this.oauthResult !== false;
59
  }
60
  }
61
 
62
- export const oauthManager = new OAuthManager();
 
1
+ export type TokenResult = string | null;
2
 
3
+ export class TokenManager {
4
+ private token: TokenResult = null;
 
 
 
 
 
 
 
 
 
 
 
 
5
 
6
  constructor() {
7
+ const storedToken = localStorage.getItem("token");
8
+ if (storedToken) {
9
+ this.token = storedToken;
 
 
 
 
10
  }
11
  }
12
 
13
+ setToken(token: string): void {
14
+ this.token = token;
15
+ localStorage.setItem("token", token);
 
 
 
 
 
 
 
 
 
 
16
  }
17
 
18
  logout(): void {
19
+ this.token = null;
20
+ localStorage.removeItem("token");
21
  window.location.reload();
22
  }
23
 
24
  getAccessToken(): string | null {
25
+ return this.token;
 
 
 
26
  }
27
 
28
  isAuthenticated(): boolean {
29
+ return this.token !== null;
30
  }
31
  }
32
 
33
+ export const tokenManager = new TokenManager();
styles/main.css CHANGED
@@ -34,6 +34,17 @@ h2 {
34
  margin: 2rem 0;
35
  }
36
 
 
 
 
 
 
 
 
 
 
 
 
37
  .auth-button {
38
  background-color: #ff9d00;
39
  color: white;
 
34
  margin: 2rem 0;
35
  }
36
 
37
+ .token-form {
38
+ max-width: 400px;
39
+ margin: 0 auto;
40
+ display: flex;
41
+ gap: 1rem;
42
+ }
43
+
44
+ .token-form input {
45
+ flex: 1;
46
+ }
47
+
48
  .auth-button {
49
  background-color: #ff9d00;
50
  color: white;