2024-08-31 02:59:05 +00:00
< template >
< div class = "page" >
2024-08-31 03:04:14 +00:00
< div class = "manager" >
< div class = "keys-header" >
< h2 class = "heading" > API keys < / h2 >
< div class = "keys-actions" >
< Icon
v - tooltip = "'Flush all keys'"
icon = "stack-cancel"
@ click = "flushKeys"
/ >
< button
class = "button"
@ click = "createKey"
> New key < / button >
< / div >
2024-08-31 02:59:05 +00:00
< / div >
2024-08-31 03:04:14 +00:00
< div
v - if = "newKey"
class = "newkey"
2024-08-31 02:59:05 +00:00
>
2024-08-31 03:04:14 +00:00
< p class = "key-info" >
Your new key identified by < strong > { { newKey . identifier } } < / strong > is
< input
: value = "newKey.key"
class = "input"
@ click = "copyKey"
>
< / p >
< p class = "key-info" > Please store this key securely , you will < strong > not < / strong > be able to retrieve it later . If you lose it , you must generate a new key . < / p >
< / div >
2024-08-31 02:59:05 +00:00
2024-08-31 03:04:14 +00:00
< ul
v - if = "keys.length > 0"
class = "keys nolist"
>
< li
v - for = "key in keys"
: key = "`key-${key.id}`"
class = "key"
>
< div class = "key-row key-header" >
< strong class = "key-value key-identifier ellipsis" > { { key . identifier } } < / strong >
2024-08-31 02:59:05 +00:00
2024-08-31 03:04:14 +00:00
< span class = "key-actions" >
< Icon
icon = "bin"
@ click = "removeKey(key)"
/ >
< / span >
< / div >
2024-08-31 02:59:05 +00:00
2024-08-31 03:04:14 +00:00
< div class = "key-row key-details" >
< span class = "key-value key-created" >
< Icon icon = "plus-circle" / >
2024-08-31 02:59:05 +00:00
< time
2024-08-31 03:04:14 +00:00
v - tooltip = "format(key.createdAt, 'yyyy-MM-dd hh:mm:ss')"
: datetime = "key.createdAt.toISOString()"
> { { formatDistanceToNowStrict ( key . createdAt ) } } ago < / time >
< / span >
< span class = "key-value key-used" >
< Icon icon = "history" / >
< template v-if ="key.lastUsedAt" >
< time
v - tooltip = "`${key.lastUsedIp} at ${format(key.lastUsedAt, 'yyyy-MM-dd hh:mm:ss')}`"
: datetime = "key.lastUsedAt.toISOString()"
> { { formatDistanceToNowStrict ( key . lastUsedAt ) } } ago < / time >
< / template >
< template v-else > Never < / template >
< / span >
< / div >
< / li >
< / ul >
< div
v - if = "keys.length > 0"
class = "info"
>
< h3 class = "info-heading" > HTTP headers < / h3 >
2024-08-31 02:59:05 +00:00
2024-08-31 03:04:14 +00:00
< code class = "headers" >
2024-08-31 03:08:50 +00:00
API - User : { { user . id } } < br >
API - Key : YourSecurelyStoredApiKey12345678
2024-08-31 03:04:14 +00:00
< / code >
< / div >
2024-08-31 02:59:05 +00:00
< / div >
< / div >
< / template >
< script setup >
import { ref , inject } from 'vue' ;
import { format , formatDistanceToNowStrict } from 'date-fns' ;
import { get , post , del } from '#/src/api.js' ;
import events from '#/src/events.js' ;
const pageContext = inject ( 'pageContext' ) ;
const user = pageContext . user ;
const keys = ref ( pageContext . pageProps . keys ) ;
const newKey = ref ( null ) ;
async function createKey ( ) {
const key = await post ( '/keys' , null , {
appendErrorMessage : true ,
} ) ;
newKey . value = key ;
keys . value = await get ( '/me/keys' ) ;
}
async function removeKey ( key ) {
if ( confirm ( ` Are you sure you want to remove API key ' ${ key . identifier } ' ( ${ format ( key . createdAt , 'yyyy-MM-dd hh:mm' ) } )? It can not be restored. ` ) ) { // eslint-disable-line no-restricted-globals, no-alert
newKey . value = null ;
await del ( ` /me/keys/ ${ key . identifier } ` ) ;
keys . value = await get ( '/me/keys' ) ;
}
}
async function flushKeys ( ) {
if ( confirm ( 'Are you sure you want to remove ALL your API keys? They can not be restored.' ) ) { // eslint-disable-line no-restricted-globals, no-alert
newKey . value = null ;
await del ( '/me/keys' ) ;
keys . value = [ ] ;
}
}
function copyKey ( event ) {
event . target . select ( ) ;
navigator . clipboard . writeText ( newKey . value . key ) ;
events . emit ( 'feedback' , {
type : 'success' ,
message : 'Key copied to clipboard' ,
} ) ;
}
< / script >
< style scoped >
. page {
2024-08-31 03:04:14 +00:00
display : flex ;
2024-08-31 02:59:05 +00:00
flex - grow : 1 ;
2024-08-31 03:04:14 +00:00
justify - content : center ;
}
. manager {
width : 1200 px ;
max - width : 100 % ;
box - sizing : border - box ;
2024-08-31 02:59:05 +00:00
padding : 1 rem ;
}
. keys - header {
display : flex ;
justify - content : space - between ;
align - items : center ;
margin - bottom : .5 rem ;
}
. keys - actions {
display : flex ;
gap : 1 rem ;
align - items : center ;
. icon {
padding : .5 rem 1 rem ;
}
}
. keys - actions ,
. key - actions {
. icon {
height : 100 % ;
fill : var ( -- glass ) ;
& : hover {
fill : var ( -- error ) ;
cursor : pointer ;
}
}
}
. keys {
display : grid ;
grid - template - columns : repeat ( auto - fill , minmax ( 25 rem , 1 fr ) ) ;
gap : .5 rem ;
margin - bottom : 2 rem ;
}
. key {
background : var ( -- background ) ;
box - shadow : 0 0 3 px var ( -- shadow - weak - 30 ) ;
font - size : .9 rem ;
}
. key - row {
display : flex ;
justify - content : space - between ;
overflow : hidden ;
}
. key - value {
display : flex ;
align - items : center ;
gap : .25 rem ;
box - sizing : border - box ;
. icon {
width : .9 rem ;
height : .9 rem ;
fill : var ( -- glass - strong - 10 ) ;
}
}
. key - header . key - value {
padding : .5 rem .5 rem .25 rem .5 rem ;
}
. key - details . key - value {
padding : .25 rem .5 rem .5 rem .5 rem ;
}
. key - identifier {
display : inline - block ;
width : 0 ;
flex - grow : 1 ;
}
. key - actions . icon {
padding : 0 .5 rem .5 rem .5 rem ;
}
. newkey {
display : inline - block ;
padding : .5 rem 1 rem ;
margin - bottom : 1 rem ;
background : var ( -- enabled - background ) ;
border : solid 1 px var ( -- success ) ;
border - radius : .25 rem ;
line - height : 1.5 ;
. input {
width : 24 rem ;
padding : .25 rem .75 rem ;
font - weight : bold ;
}
}
. key - info {
margin : 0 0 .5 rem 0 ;
}
. headers {
display : block ;
max - width : 100 % ;
padding : .5 rem 0 ;
white - space : nowrap ;
overflow : auto ;
}
. info - heading {
margin : 0 ;
}
@ media ( -- small - 20 ) {
. keys {
grid - template - columns : 1 fr ;
}
}
< / style >