feat(frontend): list tokens, use formatted dates
This commit is contained in:
		
							parent
							
								
									4fbbafc763
								
							
						
					
					
						commit
						bfa810fbb2
					
				
					 10 changed files with 66 additions and 18 deletions
				
			
		|  | @ -43,6 +43,7 @@ | ||||||
| 		"base64-arraybuffer": "^1.0.2", | 		"base64-arraybuffer": "^1.0.2", | ||||||
| 		"bootstrap": "5.3.0-alpha1", | 		"bootstrap": "5.3.0-alpha1", | ||||||
| 		"bootstrap-icons": "^1.10.3", | 		"bootstrap-icons": "^1.10.3", | ||||||
|  | 		"jose": "^4.13.1", | ||||||
| 		"luxon": "^3.3.0", | 		"luxon": "^3.3.0", | ||||||
| 		"marked": "^4.2.12", | 		"marked": "^4.2.12", | ||||||
| 		"sanitize-html": "^2.10.0", | 		"sanitize-html": "^2.10.0", | ||||||
|  |  | ||||||
							
								
								
									
										6
									
								
								frontend/pnpm-lock.yaml
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										6
									
								
								frontend/pnpm-lock.yaml
									
										
									
										generated
									
									
									
								
							|  | @ -20,6 +20,7 @@ specifiers: | ||||||
|   eslint: ^8.28.0 |   eslint: ^8.28.0 | ||||||
|   eslint-config-prettier: ^8.5.0 |   eslint-config-prettier: ^8.5.0 | ||||||
|   eslint-plugin-svelte3: ^4.0.0 |   eslint-plugin-svelte3: ^4.0.0 | ||||||
|  |   jose: ^4.13.1 | ||||||
|   luxon: ^3.3.0 |   luxon: ^3.3.0 | ||||||
|   marked: ^4.2.12 |   marked: ^4.2.12 | ||||||
|   postcss: ^8.4.21 |   postcss: ^8.4.21 | ||||||
|  | @ -40,6 +41,7 @@ dependencies: | ||||||
|   base64-arraybuffer: 1.0.2 |   base64-arraybuffer: 1.0.2 | ||||||
|   bootstrap: 5.3.0-alpha1_@popperjs+core@2.11.6 |   bootstrap: 5.3.0-alpha1_@popperjs+core@2.11.6 | ||||||
|   bootstrap-icons: 1.10.3 |   bootstrap-icons: 1.10.3 | ||||||
|  |   jose: 4.13.1 | ||||||
|   luxon: 3.3.0 |   luxon: 3.3.0 | ||||||
|   marked: 4.2.12 |   marked: 4.2.12 | ||||||
|   sanitize-html: 2.10.0 |   sanitize-html: 2.10.0 | ||||||
|  | @ -1546,6 +1548,10 @@ packages: | ||||||
|     resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} |     resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} | ||||||
|     dev: true |     dev: true | ||||||
| 
 | 
 | ||||||
|  |   /jose/4.13.1: | ||||||
|  |     resolution: {integrity: sha512-MSJQC5vXco5Br38mzaQKiq9mwt7lwj2eXpgpRyQYNHYt2lq1PjkWa7DLXX0WVcQLE9HhMh3jPiufS7fhJf+CLQ==} | ||||||
|  |     dev: false | ||||||
|  | 
 | ||||||
|   /js-sdsl/4.3.0: |   /js-sdsl/4.3.0: | ||||||
|     resolution: {integrity: sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==} |     resolution: {integrity: sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==} | ||||||
|     dev: true |     dev: true | ||||||
|  |  | ||||||
|  | @ -74,7 +74,7 @@ export interface MemberPartialUser { | ||||||
| 
 | 
 | ||||||
| export interface Invite { | export interface Invite { | ||||||
|   code: string; |   code: string; | ||||||
|   created: Date; |   created: string; | ||||||
|   used: boolean; |   used: boolean; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -32,5 +32,5 @@ interface CallbackResponse { | ||||||
|   require_invite: boolean; |   require_invite: boolean; | ||||||
| 
 | 
 | ||||||
|   is_deleted: boolean; |   is_deleted: boolean; | ||||||
|   deleted_at?: Date; |   deleted_at?: string; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -46,7 +46,7 @@ | ||||||
|           active={$page.url.pathname === "/settings/tokens"} |           active={$page.url.pathname === "/settings/tokens"} | ||||||
|           href="/settings/tokens" |           href="/settings/tokens" | ||||||
|         > |         > | ||||||
|           API tokens |           Tokens | ||||||
|         </ListGroupItem> |         </ListGroupItem> | ||||||
|         <ListGroupItem |         <ListGroupItem | ||||||
|           tag="a" |           tag="a" | ||||||
|  |  | ||||||
|  | @ -5,16 +5,7 @@ | ||||||
|   import ErrorAlert from "$lib/components/ErrorAlert.svelte"; |   import ErrorAlert from "$lib/components/ErrorAlert.svelte"; | ||||||
|   import FallbackImage from "$lib/components/FallbackImage.svelte"; |   import FallbackImage from "$lib/components/FallbackImage.svelte"; | ||||||
|   import { userStore } from "$lib/store"; |   import { userStore } from "$lib/store"; | ||||||
|   import { |   import { Button, Icon, Modal, ModalBody, ModalFooter, ModalHeader, Table } from "sveltestrap"; | ||||||
|     Alert, |  | ||||||
|     Button, |  | ||||||
|     Icon, |  | ||||||
|     Modal, |  | ||||||
|     ModalBody, |  | ||||||
|     ModalFooter, |  | ||||||
|     ModalHeader, |  | ||||||
|     Table, |  | ||||||
|   } from "sveltestrap"; |  | ||||||
|   import type { PageData } from "./$types"; |   import type { PageData } from "./$types"; | ||||||
| 
 | 
 | ||||||
|   export let data: PageData; |   export let data: PageData; | ||||||
|  |  | ||||||
|  | @ -15,5 +15,5 @@ export const load = async () => { | ||||||
| 
 | 
 | ||||||
| interface ExportResponse { | interface ExportResponse { | ||||||
|   path: string; |   path: string; | ||||||
|   created_at: Date; |   created_at: string; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -2,7 +2,8 @@ | ||||||
|   import type { APIError, Invite } from "$lib/api/entities"; |   import type { APIError, Invite } from "$lib/api/entities"; | ||||||
|   import { apiFetchClient } from "$lib/api/fetch"; |   import { apiFetchClient } from "$lib/api/fetch"; | ||||||
|   import ErrorAlert from "$lib/components/ErrorAlert.svelte"; |   import ErrorAlert from "$lib/components/ErrorAlert.svelte"; | ||||||
|   import { Alert, Button, Modal, Table } from "sveltestrap"; |   import { DateTime } from "luxon"; | ||||||
|  |   import { Button, Modal, Table } from "sveltestrap"; | ||||||
|   import type { PageData } from "./$types"; |   import type { PageData } from "./$types"; | ||||||
| 
 | 
 | ||||||
|   export let data: PageData; |   export let data: PageData; | ||||||
|  | @ -43,7 +44,11 @@ | ||||||
|         {#each data.invites as invite} |         {#each data.invites as invite} | ||||||
|           <tr> |           <tr> | ||||||
|             <td><code>{invite.code}</code></td> |             <td><code>{invite.code}</code></td> | ||||||
|             <td>{invite.created}</td> |             <td | ||||||
|  |               >{DateTime.fromISO(invite.created) | ||||||
|  |                 .toLocal() | ||||||
|  |                 .toLocaleString(DateTime.DATETIME_MED)}</td | ||||||
|  |             > | ||||||
|             <td>{invite.used ? "yes" : "no"}</td> |             <td>{invite.used ? "yes" : "no"}</td> | ||||||
|           </tr> |           </tr> | ||||||
|         {/each} |         {/each} | ||||||
|  |  | ||||||
|  | @ -1,3 +1,36 @@ | ||||||
| <h1>API tokens</h1> | <script lang="ts"> | ||||||
|  |   import { DateTime } from "luxon"; | ||||||
|  |   import { Icon, Table } from "sveltestrap"; | ||||||
|  |   import type { PageData } from "./$types"; | ||||||
| 
 | 
 | ||||||
| <p>This page is a work in progress, sorry!</p> |   export let data: PageData; | ||||||
|  | 
 | ||||||
|  |   import * as jose from "jose"; | ||||||
|  |   const claims = jose.decodeJwt(localStorage.getItem("pronouns-token")!); | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <h1>Tokens ({data.tokens.length})</h1> | ||||||
|  | 
 | ||||||
|  | <Table bordered striped hover> | ||||||
|  |   <thead> | ||||||
|  |     <th>ID</th> | ||||||
|  |     <th>Created at</th> | ||||||
|  |     <th>Expires at</th> | ||||||
|  |     <th>Current?</th> | ||||||
|  |   </thead> | ||||||
|  |   <tbody> | ||||||
|  |     {#each data.tokens as token} | ||||||
|  |       <tr> | ||||||
|  |         <td><code>{token.id}</code></td> | ||||||
|  |         <td>{DateTime.fromISO(token.created).toLocal().toLocaleString(DateTime.DATETIME_MED)}</td> | ||||||
|  |         <td>{DateTime.fromISO(token.expires).toLocal().toLocaleString(DateTime.DATETIME_MED)}</td> | ||||||
|  |         <td | ||||||
|  |           >{#if claims["jti"] === token.id}<Icon name="check-lg" alt="Current token" />{:else}<Icon | ||||||
|  |               name="x-lg" | ||||||
|  |               alt="Not current token" | ||||||
|  |             />{/if}</td | ||||||
|  |         > | ||||||
|  |       </tr> | ||||||
|  |     {/each} | ||||||
|  |   </tbody> | ||||||
|  | </Table> | ||||||
|  |  | ||||||
							
								
								
									
										12
									
								
								frontend/src/routes/settings/tokens/+page.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								frontend/src/routes/settings/tokens/+page.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,12 @@ | ||||||
|  | import { apiFetchClient } from "$lib/api/fetch"; | ||||||
|  | 
 | ||||||
|  | export const load = async () => { | ||||||
|  |   const tokens = await apiFetchClient<Token[]>("/auth/tokens"); | ||||||
|  |   return { tokens }; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | interface Token { | ||||||
|  |   id: string; | ||||||
|  |   created: string; | ||||||
|  |   expires: string; | ||||||
|  | } | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue