Allow users to generate an API key in their profile (#182)
* Add /private-users/apiKey to DB * Add field to edit API key on profile * Move API key to bottom of profile page Austin thinks this is better since most people don't care about it.
This commit is contained in:
parent
19da0c6c82
commit
72b21925e5
|
@ -35,4 +35,5 @@ export type PrivateUser = {
|
|||
unsubscribedFromGenericEmails?: boolean
|
||||
initialDeviceToken?: string
|
||||
initialIpAddress?: string
|
||||
apiKey?: string
|
||||
}
|
||||
|
|
|
@ -21,6 +21,9 @@ service cloud.firestore {
|
|||
|
||||
match /private-users/{userId} {
|
||||
allow read: if resource.data.id == request.auth.uid || isAdmin();
|
||||
allow update: if (resource.data.id == request.auth.uid || isAdmin())
|
||||
&& request.resource.data.diff(resource.data).affectedKeys()
|
||||
.hasOnly(['apiKey']);
|
||||
}
|
||||
|
||||
match /private-users/{userId}/views/{viewId} {
|
||||
|
|
|
@ -55,6 +55,13 @@ export async function updateUser(userId: string, update: Partial<User>) {
|
|||
await updateDoc(doc(db, 'users', userId), { ...update })
|
||||
}
|
||||
|
||||
export async function updatePrivateUser(
|
||||
userId: string,
|
||||
update: Partial<PrivateUser>
|
||||
) {
|
||||
await updateDoc(doc(db, 'private-users', userId), { ...update })
|
||||
}
|
||||
|
||||
export function listenForUser(
|
||||
userId: string,
|
||||
setUser: (user: User | null) => void
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { useEffect, useState } from 'react'
|
||||
import { RefreshIcon } from '@heroicons/react/outline'
|
||||
import Router from 'next/router'
|
||||
|
||||
import { AddFundsButton } from 'web/components/add-funds-button'
|
||||
|
@ -13,7 +14,7 @@ import { uploadImage } from 'web/lib/firebase/storage'
|
|||
import { Col } from 'web/components/layout/col'
|
||||
import { Row } from 'web/components/layout/row'
|
||||
import { User } from 'common/user'
|
||||
import { updateUser } from 'web/lib/firebase/users'
|
||||
import { updateUser, updatePrivateUser } from 'web/lib/firebase/users'
|
||||
import { defaultBannerUrl } from 'web/components/user-page'
|
||||
import { SiteLink } from 'web/components/site-link'
|
||||
import Textarea from 'react-expanding-textarea'
|
||||
|
@ -63,6 +64,7 @@ export default function ProfilePage() {
|
|||
const [avatarLoading, setAvatarLoading] = useState(false)
|
||||
const [name, setName] = useState(user?.name || '')
|
||||
const [username, setUsername] = useState(user?.username || '')
|
||||
const [apiKey, setApiKey] = useState(privateUser?.apiKey || '')
|
||||
|
||||
useEffect(() => {
|
||||
if (user) {
|
||||
|
@ -72,6 +74,12 @@ export default function ProfilePage() {
|
|||
}
|
||||
}, [user])
|
||||
|
||||
useEffect(() => {
|
||||
if (privateUser) {
|
||||
setApiKey(privateUser.apiKey || '')
|
||||
}
|
||||
}, [privateUser])
|
||||
|
||||
const updateDisplayName = async () => {
|
||||
const newName = cleanDisplayName(name)
|
||||
|
||||
|
@ -103,6 +111,17 @@ export default function ProfilePage() {
|
|||
}
|
||||
}
|
||||
|
||||
const updateApiKey = async (e: React.MouseEvent) => {
|
||||
const newApiKey = crypto.randomUUID()
|
||||
if (user?.id != null) {
|
||||
setApiKey(newApiKey)
|
||||
await updatePrivateUser(user.id, { apiKey: newApiKey }).catch(() => {
|
||||
setApiKey(privateUser?.apiKey || '')
|
||||
})
|
||||
}
|
||||
e.preventDefault()
|
||||
}
|
||||
|
||||
const fileHandler = async (event: any) => {
|
||||
const file = event.target.files[0]
|
||||
|
||||
|
@ -155,7 +174,6 @@ export default function ProfilePage() {
|
|||
|
||||
<div>
|
||||
<label className="label">Display name</label>
|
||||
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Display name"
|
||||
|
@ -168,7 +186,6 @@ export default function ProfilePage() {
|
|||
|
||||
<div>
|
||||
<label className="label">Username</label>
|
||||
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Username"
|
||||
|
@ -233,6 +250,25 @@ export default function ProfilePage() {
|
|||
<AddFundsButton />
|
||||
</Row>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="label">API key</label>
|
||||
<div className="input-group w-full">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Click refresh to generate key"
|
||||
className="input input-bordered w-full"
|
||||
value={apiKey}
|
||||
readOnly
|
||||
/>
|
||||
<button
|
||||
className="btn btn-primary btn-square p-2"
|
||||
onClick={updateApiKey}
|
||||
>
|
||||
<RefreshIcon />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</Col>
|
||||
</Col>
|
||||
</Page>
|
||||
|
|
Loading…
Reference in New Issue
Block a user