jbilcke-hf HF staff commited on
Commit
f276512
1 Parent(s): 1185ec1

allow recovering the HF user ID from a slug

Browse files
.nvmrc CHANGED
@@ -1 +1 @@
1
- v20.9.0
 
1
+ v20.10.0
package-lock.json CHANGED
@@ -75,6 +75,7 @@
75
  "typescript": "5.1.6",
76
  "usehooks-ts": "^2.9.1",
77
  "uuid": "^9.0.1",
 
78
  "zustand": "^4.4.7"
79
  },
80
  "devDependencies": {
 
75
  "typescript": "5.1.6",
76
  "usehooks-ts": "^2.9.1",
77
  "uuid": "^9.0.1",
78
+ "yaml": "^2.3.4",
79
  "zustand": "^4.4.7"
80
  },
81
  "devDependencies": {
package.json CHANGED
@@ -76,6 +76,7 @@
76
  "typescript": "5.1.6",
77
  "usehooks-ts": "^2.9.1",
78
  "uuid": "^9.0.1",
 
79
  "zustand": "^4.4.7"
80
  },
81
  "devDependencies": {
 
76
  "typescript": "5.1.6",
77
  "usehooks-ts": "^2.9.1",
78
  "uuid": "^9.0.1",
79
+ "yaml": "^2.3.4",
80
  "zustand": "^4.4.7"
81
  },
82
  "devDependencies": {
src/app/server/actions/ai-tube-hf/getPrivateChannels.ts CHANGED
@@ -10,20 +10,26 @@ export async function getPrivateChannels(options: {
10
  channelId?: string
11
  apiKey?: string
12
  owner?: string
 
13
  renewCache?: boolean
14
  } = {}): Promise<ChannelInfo[]> {
15
  // console.log("getChannels")
16
  let credentials: Credentials = adminCredentials
17
- let owner = options?.owner
 
18
 
19
  if (options?.apiKey) {
20
  try {
21
  credentials = { accessToken: options.apiKey }
22
- const { name: username } = await whoAmI({ credentials })
 
 
 
23
  if (!username) {
24
  throw new Error(`couldn't get the username`)
25
  }
26
- // everything is in order,
 
27
  owner = username
28
  } catch (err) {
29
  console.error(err)
@@ -73,7 +79,14 @@ export async function getPrivateChannels(options: {
73
 
74
  const channel = await parseChannel({
75
  ...options,
76
- id, name, likes, updatedAt
 
 
 
 
 
 
 
77
  })
78
 
79
  channels.push(channel)
 
10
  channelId?: string
11
  apiKey?: string
12
  owner?: string
13
+ // ownerId?: string
14
  renewCache?: boolean
15
  } = {}): Promise<ChannelInfo[]> {
16
  // console.log("getChannels")
17
  let credentials: Credentials = adminCredentials
18
+ let owner = options?.owner || ""
19
+ // let ownerId = options?.ownerId || ""
20
 
21
  if (options?.apiKey) {
22
  try {
23
  credentials = { accessToken: options.apiKey }
24
+ const { id: userId, name: username } = await whoAmI({ credentials })
25
+ if (!userId) {
26
+ throw new Error(`couldn't get the id`)
27
+ }
28
  if (!username) {
29
  throw new Error(`couldn't get the username`)
30
  }
31
+ // everything is in order
32
+ // ownerId = userId
33
  owner = username
34
  } catch (err) {
35
  console.error(err)
 
79
 
80
  const channel = await parseChannel({
81
  ...options,
82
+
83
+ id,
84
+ name,
85
+ likes,
86
+ updatedAt,
87
+
88
+ // nope that doesn't work, it's the wrong owner
89
+ // ownerId,
90
  })
91
 
92
  channels.push(channel)
src/app/server/actions/ai-tube-hf/parseChannel.ts CHANGED
@@ -14,20 +14,26 @@ export async function parseChannel(options: {
14
  updatedAt: Date
15
  apiKey?: string
16
  owner?: string
 
17
  renewCache?: boolean
18
  }): Promise<ChannelInfo> {
19
  // console.log("getChannels")
20
  let credentials: Credentials = adminCredentials
21
- let owner = options.owner
 
22
 
23
  if (options.apiKey) {
24
  try {
25
  credentials = { accessToken: options.apiKey }
26
- const { name: username } = await whoAmI({ credentials })
 
 
 
27
  if (!username) {
28
  throw new Error(`couldn't get the username`)
29
  }
30
- // everything is in order,
 
31
  owner = username
32
  } catch (err) {
33
  console.error(err)
@@ -115,6 +121,7 @@ export async function parseChannel(options: {
115
 
116
  const channel: ChannelInfo = {
117
  id: options.id,
 
118
  datasetUser,
119
  datasetName,
120
  slug,
 
14
  updatedAt: Date
15
  apiKey?: string
16
  owner?: string
17
+ // ownerId?: string
18
  renewCache?: boolean
19
  }): Promise<ChannelInfo> {
20
  // console.log("getChannels")
21
  let credentials: Credentials = adminCredentials
22
+ let owner = options.owner || ""
23
+ // let ownerId = options.ownerId || ""
24
 
25
  if (options.apiKey) {
26
  try {
27
  credentials = { accessToken: options.apiKey }
28
+ const { id: userId, name: username } = await whoAmI({ credentials })
29
+ if (!userId) {
30
+ throw new Error(`couldn't get the userId`)
31
+ }
32
  if (!username) {
33
  throw new Error(`couldn't get the username`)
34
  }
35
+ // everything is in order
36
+ // ownerId = userId
37
  owner = username
38
  } catch (err) {
39
  console.error(err)
 
121
 
122
  const channel: ChannelInfo = {
123
  id: options.id,
124
+ // datasetUserId: ownerId,
125
  datasetUser,
126
  datasetName,
127
  slug,
src/app/server/actions/users.ts CHANGED
@@ -23,18 +23,70 @@ export async function getCurrentUser(apiKey: string): Promise<UserInfo> {
23
  fullName: huggingFaceUser.fullname,
24
  thumbnail: huggingFaceUser.avatarUrl,
25
  channels: [],
26
- hfApiToken: apiKey,
27
  }
28
 
29
  await redis.set(`users:${id}`, user)
30
-
 
 
 
 
 
 
31
  return user
32
  }
33
 
34
- export async function getUser(id: string): Promise<UserInfo | undefined> {
35
- const maybeUser = await redis.get<UserInfo>(id)
 
 
 
 
 
 
 
 
 
 
36
 
37
  if (maybeUser?.id) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
  const publicFacingUser: UserInfo = {
39
  ...maybeUser,
40
  hfApiToken: undefined, // <-- important!
 
23
  fullName: huggingFaceUser.fullname,
24
  thumbnail: huggingFaceUser.avatarUrl,
25
  channels: [],
26
+ hfApiToken: apiKey, // <- on purpose, and safe (only this user sees their token)
27
  }
28
 
29
  await redis.set(`users:${id}`, user)
30
+
31
+ // the user id is not available in the channel info, only user name (slug)
32
+ // so we use this projection to recover the ID from a slug
33
+ // alternatively we could also use a Redis index, to avoid doing two calls
34
+ // (for get id and get user)
35
+ await redis.set(`userSlugToId:${user.userName}`, user.id)
36
+
37
  return user
38
  }
39
 
40
+ /**
41
+ * Attention this returns the *full* user, including the API key
42
+ *
43
+ * We use the API on behalf of the user, but it is confidential nevertheless,
44
+ * so we should not share it with 3rd parties unbeknownst to the user
45
+ *
46
+ * @param hfUserId
47
+ * @returns
48
+ */
49
+ export async function getUserFromId(hfUserId: string): Promise<UserInfo | undefined> {
50
+
51
+ const maybeUser = await redis.get<UserInfo>(`users:${hfUserId}`)
52
 
53
  if (maybeUser?.id) {
54
+ return maybeUser
55
+ }
56
+
57
+ return undefined
58
+ }
59
+
60
+ export async function getUserIdFromSlug(hfUserSlugName: string): Promise<string> {
61
+
62
+ // the user id is not available in the channel info, only user name (slug)
63
+ // so we use a projection to recover the ID from a slug
64
+ const maybeUserId = await redis.get<string>(`userSlugToId:${hfUserSlugName}`)
65
+ return maybeUserId || ""
66
+ }
67
+
68
+ /**
69
+ * This function doesn NOT return the user apiKey, for security reasons
70
+ *
71
+ * We use the API on behalf of the user, but it is confidential nevertheless,
72
+ * so we should not share it with 3rd parties unbeknownst to the user
73
+ *
74
+ * @param userIdOrSlugName
75
+ * @returns
76
+ */
77
+ export async function getUser(userIdOrSlugName: string): Promise<UserInfo | undefined> {
78
+ // the user id is not available in the channel info, only user name (slug)
79
+ // so we use a projection to recover the ID from a slug
80
+ // alternatively, we could also use a Redis index, to avoid doing two calls
81
+ // (for get id and get user)
82
+ let maybeUserId = await getUserIdFromSlug(userIdOrSlugName)
83
+
84
+ maybeUserId ||= userIdOrSlugName
85
+
86
+ const maybeUser = await getUserFromId(maybeUserId)
87
+
88
+ // sanitize the output a bit
89
+ if (maybeUser) {
90
  const publicFacingUser: UserInfo = {
91
  ...maybeUser,
92
  hfApiToken: undefined, // <-- important!
src/types/general.ts CHANGED
@@ -191,6 +191,14 @@ export type ChannelInfo = {
191
  */
192
  slug: string
193
 
 
 
 
 
 
 
 
 
194
  /**
195
  * username slug of the Hugging Face dataset
196
  *
 
191
  */
192
  slug: string
193
 
194
+ /**
195
+ * username id of the Hugging Face dataset
196
+ *
197
+ * ex: f9a38286ec3436a45edd2cca
198
+ */
199
+ // DISABLED FOR NOW
200
+ // datasetUserId: string
201
+
202
  /**
203
  * username slug of the Hugging Face dataset
204
  *