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:
Marshall Polaris 2022-05-15 20:41:07 -07:00 committed by GitHub
parent 19da0c6c82
commit 72b21925e5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 50 additions and 3 deletions

View File

@ -35,4 +35,5 @@ export type PrivateUser = {
unsubscribedFromGenericEmails?: boolean
initialDeviceToken?: string
initialIpAddress?: string
apiKey?: string
}

View File

@ -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} {

View File

@ -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

View File

@ -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>