@@ -1514,21 +1514,25 @@ function renderAccountEdit() {
15141514 Add your Pyrodactyl Client API key to enable live resource monitoring and server power state detection directly in the dashboard.
15151515 Generate one at < a href ="https://panel.zero-host.org/account/api " target ="_blank "> panel.zero-host.org/account/api</ a > .
15161516 </ p >
1517- < form id ="api-key-form " style ="max-width:480px ">
1518- < div class ="form-group ">
1519- < label for ="ptero-api-key-input "> API Key</ label >
1520- < input type ="password " id ="ptero-api-key-input " placeholder ="ptla_... " autocomplete ="off " />
1521- </ div >
1522- < button type ="submit " class ="btn btn-primary btn-full " id ="save-api-key-btn "> Save</ button >
1523- </ form >
1524- < div id ="api-key-status " style ="margin-top:8px;font-size:0.82rem;color:var(--text-muted) "> </ div >
1517+ < div id ="api-key-section-content ">
1518+ < form id ="api-key-form " style ="max-width:480px ">
1519+ < div class ="form-group ">
1520+ < label for ="ptero-api-key-input "> API Key</ label >
1521+ < input type ="password " id ="ptero-api-key-input " placeholder ="ptla_... " autocomplete ="off " />
1522+ </ div >
1523+ < button type ="submit " class ="btn btn-primary btn-full " id ="save-api-key-btn "> Save</ button >
1524+ </ form >
1525+ < div id ="api-key-status " style ="margin-top:8px;font-size:0.82rem;color:var(--text-muted) "> </ div >
1526+ </ div >
15251527 </ div >
15261528 </ div >
15271529 ` ;
15281530
15291531 $ ( '#change-email-form' ) . addEventListener ( 'submit' , handleChangeEmail ) ;
15301532 $ ( '#change-password-form' ) . addEventListener ( 'submit' , handleChangePassword ) ;
15311533 $ ( '#api-key-form' ) . addEventListener ( 'submit' , handleSaveApiKey ) ;
1534+
1535+ checkApiKeyStatus ( ) ;
15321536}
15331537
15341538async function handleSaveApiKey ( e ) {
@@ -1553,10 +1557,9 @@ async function handleSaveApiKey(e) {
15531557 method : 'PUT' ,
15541558 body : JSON . stringify ( { apiKey : key } ) ,
15551559 } ) ;
1556- status . textContent = 'API key saved successfully!' ;
1557- status . style . color = 'var(--accent-green)' ;
1558- input . value = '' ;
15591560 showToast ( 'Pyrodactyl API key saved' , 'success' ) ;
1561+ renderApiKeySaved ( ) ;
1562+ return ;
15601563 } catch ( err ) {
15611564 status . textContent = err . message ;
15621565 status . style . color = 'var(--accent-red)' ;
@@ -1566,6 +1569,136 @@ async function handleSaveApiKey(e) {
15661569 }
15671570}
15681571
1572+ async function checkApiKeyStatus ( ) {
1573+ try {
1574+ const data = await api ( '/servers/client-api-key' ) ;
1575+ if ( data . hasKey ) {
1576+ renderApiKeySaved ( ) ;
1577+ }
1578+ } catch ( err ) {
1579+ // Keep default form if check fails
1580+ }
1581+ }
1582+
1583+ function renderApiKeySaved ( ) {
1584+ const section = $ ( '#api-key-section-content' ) ;
1585+ section . innerHTML = html `
1586+ < div style ="padding:8px 0 ">
1587+ < p style ="color:var(--accent-green);font-size:0.9rem;margin-bottom:16px ">
1588+ ✓ Your Pyrodactyl API key is saved and active.
1589+ </ p >
1590+ < div style ="display:flex;gap:8px ">
1591+ < button class ="btn btn-primary " id ="modify-api-key-btn "> Modify</ button >
1592+ < button class ="btn btn-danger " id ="delete-api-key-btn "> Delete</ button >
1593+ </ div >
1594+ </ div >
1595+ ` ;
1596+ $ ( '#modify-api-key-btn' ) . addEventListener ( 'click' , handleModifyApiKey ) ;
1597+ $ ( '#delete-api-key-btn' ) . addEventListener ( 'click' , handleDeleteApiKey ) ;
1598+ }
1599+
1600+ function renderApiKeyForm ( ) {
1601+ const section = $ ( '#api-key-section-content' ) ;
1602+ section . innerHTML = html `
1603+ < form id ="api-key-form " style ="max-width:480px ">
1604+ < div class ="form-group ">
1605+ < label for ="ptero-api-key-input "> API Key</ label >
1606+ < input type ="password " id ="ptero-api-key-input " placeholder ="ptla_... " autocomplete ="off " />
1607+ </ div >
1608+ < button type ="submit " class ="btn btn-primary btn-full " id ="save-api-key-btn "> Save</ button >
1609+ </ form >
1610+ < div id ="api-key-status " style ="margin-top:8px;font-size:0.82rem;color:var(--text-muted) "> </ div >
1611+ ` ;
1612+ $ ( '#api-key-form' ) . addEventListener ( 'submit' , handleSaveApiKey ) ;
1613+ }
1614+
1615+ function handleModifyApiKey ( ) {
1616+ const overlay = $ ( '#modal-overlay' ) ;
1617+ const content = $ ( '#modal-content' ) ;
1618+ content . innerHTML = html `
1619+ < div class ="modal-title "> Modify API Key</ div >
1620+ < p style ="color:var(--text-secondary);line-height:1.6;margin-bottom:16px ">
1621+ Enter your new Pyrodactyl API key. The old key will be replaced.
1622+ </ p >
1623+ < div class ="form-group ">
1624+ < label for ="modal-api-key-input "> New API Key</ label >
1625+ < input type ="password " id ="modal-api-key-input " placeholder ="ptla_... " autocomplete ="off " />
1626+ </ div >
1627+ < div class ="modal-actions ">
1628+ < button class ="btn btn-ghost btn-full modal-cancel-btn "> Cancel</ button >
1629+ < button class ="btn btn-primary btn-full " id ="confirm-modify-api-key-btn "> Save</ button >
1630+ </ div >
1631+ ` ;
1632+ overlay . classList . add ( 'open' ) ;
1633+ $ ( '#confirm-modify-api-key-btn' ) . addEventListener ( 'click' , handleConfirmModifyApiKey ) ;
1634+ }
1635+
1636+ function handleDeleteApiKey ( ) {
1637+ const overlay = $ ( '#modal-overlay' ) ;
1638+ const content = $ ( '#modal-content' ) ;
1639+ content . innerHTML = html `
1640+ < div class ="modal-title "> Delete API Key</ div >
1641+ < p style ="color:var(--text-secondary);line-height:1.6;margin-bottom:16px ">
1642+ Are you sure you want to delete your Pyrodactyl API key? Live resource monitoring and power state detection will stop working.
1643+ </ p >
1644+ < div class ="modal-actions ">
1645+ < button class ="btn btn-ghost btn-full modal-cancel-btn "> Cancel</ button >
1646+ < button class ="btn btn-danger btn-full " id ="confirm-delete-api-key-btn "> Delete</ button >
1647+ </ div >
1648+ ` ;
1649+ overlay . classList . add ( 'open' ) ;
1650+ $ ( '#confirm-delete-api-key-btn' ) . addEventListener ( 'click' , handleConfirmDeleteApiKey ) ;
1651+ }
1652+
1653+ async function handleConfirmModifyApiKey ( ) {
1654+ const btn = $ ( '#confirm-modify-api-key-btn' ) ;
1655+ const input = $ ( '#modal-api-key-input' ) ;
1656+ const key = input . value . trim ( ) ;
1657+
1658+ if ( ! key ) {
1659+ showToast ( 'Please enter an API key' , 'error' ) ;
1660+ return ;
1661+ }
1662+
1663+ btn . disabled = true ;
1664+ btn . innerHTML = '<span class="spinner"></span>' ;
1665+
1666+ try {
1667+ await api ( '/servers/client-api-key' , {
1668+ method : 'PUT' ,
1669+ body : JSON . stringify ( { apiKey : key } ) ,
1670+ } ) ;
1671+ $ ( '#modal-overlay' ) . classList . remove ( 'open' ) ;
1672+ showToast ( 'Pyrodactyl API key updated' , 'success' ) ;
1673+ renderApiKeySaved ( ) ;
1674+ } catch ( err ) {
1675+ showToast ( err . message , 'error' ) ;
1676+ } finally {
1677+ btn . disabled = false ;
1678+ btn . innerHTML = 'Save' ;
1679+ }
1680+ }
1681+
1682+ async function handleConfirmDeleteApiKey ( ) {
1683+ const btn = $ ( '#confirm-delete-api-key-btn' ) ;
1684+ btn . disabled = true ;
1685+ btn . innerHTML = '<span class="spinner"></span>' ;
1686+
1687+ try {
1688+ await api ( '/servers/client-api-key' , {
1689+ method : 'DELETE' ,
1690+ } ) ;
1691+ $ ( '#modal-overlay' ) . classList . remove ( 'open' ) ;
1692+ showToast ( 'Pyrodactyl API key deleted' , 'success' ) ;
1693+ renderApiKeyForm ( ) ;
1694+ } catch ( err ) {
1695+ showToast ( err . message , 'error' ) ;
1696+ } finally {
1697+ btn . disabled = false ;
1698+ btn . innerHTML = 'Delete' ;
1699+ }
1700+ }
1701+
15691702function renderAccountLinks ( ) {
15701703 const el = $ ( '#page-account' ) ;
15711704 el . innerHTML = html `
0 commit comments