@@ -3,7 +3,7 @@ import argon2 from 'argon2';
33import jwt from 'jsonwebtoken' ;
44import { authenticateToken } from '../middleware/auth.js' ;
55import { query } from '../config/db.js' ;
6- import { getAllServers , getServerById , suspendPteroServer , unsuspendPteroServer , deletePteroServer , deletePteroUser } from '../services/pyrodactyl.js' ;
6+ import { getAllServers , getServerById , getEgg , getPteroNests , getPteroNestEggs , suspendPteroServer , unsuspendPteroServer , deletePteroServer , deletePteroUser } from '../services/pyrodactyl.js' ;
77import { verifyCap } from '../config/cap.js' ;
88import { logActivity } from '../services/activity.js' ;
99
@@ -482,4 +482,125 @@ router.get('/activity', authenticateToken, requireAdmin, async (req, res) => {
482482 }
483483} ) ;
484484
485+ // ─── Settings: Nests ────────────────────────────────────
486+ router . get ( '/settings/nests' , authenticateToken , requireAdmin , async ( req , res ) => {
487+ try {
488+ const nests = await query ( 'SELECT * FROM nests ORDER BY name ASC' ) ;
489+ res . json ( { nests } ) ;
490+ } catch ( err ) {
491+ res . status ( 500 ) . json ( { error : 'Failed to fetch nests' } ) ;
492+ }
493+ } ) ;
494+
495+ router . get ( '/settings/nests/available' , authenticateToken , requireAdmin , async ( req , res ) => {
496+ try {
497+ const pteroNests = await getPteroNests ( ) ;
498+ const localNests = await query ( 'SELECT ptero_nest_id FROM nests' ) ;
499+ const localIds = new Set ( localNests . map ( n => n . ptero_nest_id ) ) ;
500+ const available = pteroNests . filter ( n => ! localIds . has ( n . id ) ) ;
501+ res . json ( { nests : available } ) ;
502+ } catch ( err ) {
503+ res . status ( 500 ) . json ( { error : 'Failed to fetch available nests: ' + err . message } ) ;
504+ }
505+ } ) ;
506+
507+ router . post ( '/settings/nests' , authenticateToken , requireAdmin , async ( req , res ) => {
508+ try {
509+ const { pteroNestId, name } = req . body ;
510+ if ( ! pteroNestId ) return res . status ( 400 ) . json ( { error : 'Nest ID is required' } ) ;
511+
512+ const pteroNests = await getPteroNests ( ) ;
513+ const nest = pteroNests . find ( n => n . id === pteroNestId ) ;
514+ if ( ! nest ) return res . status ( 404 ) . json ( { error : 'Nest not found in panel' } ) ;
515+
516+ const displayName = name || nest . name ;
517+ await query ( 'INSERT INTO nests (ptero_nest_id, name) VALUES (?, ?)' , [ pteroNestId , displayName ] ) ;
518+ const [ inserted ] = await query ( 'SELECT * FROM nests WHERE ptero_nest_id = ?' , [ pteroNestId ] ) ;
519+ res . status ( 201 ) . json ( { nest : inserted } ) ;
520+ } catch ( err ) {
521+ if ( err . code === 'ER_DUP_ENTRY' ) {
522+ return res . status ( 409 ) . json ( { error : 'Nest already added' } ) ;
523+ }
524+ res . status ( 500 ) . json ( { error : 'Failed to add nest: ' + err . message } ) ;
525+ }
526+ } ) ;
527+
528+ router . put ( '/settings/nests/:id' , authenticateToken , requireAdmin , async ( req , res ) => {
529+ try {
530+ const id = parseInt ( req . params . id , 10 ) ;
531+ const { name } = req . body ;
532+ if ( ! name ) return res . status ( 400 ) . json ( { error : 'Name is required' } ) ;
533+ await query ( 'UPDATE nests SET name = ? WHERE id = ?' , [ name , id ] ) ;
534+ res . json ( { success : true } ) ;
535+ } catch ( err ) {
536+ res . status ( 500 ) . json ( { error : 'Failed to rename nest' } ) ;
537+ }
538+ } ) ;
539+
540+ router . delete ( '/settings/nests/:id' , authenticateToken , requireAdmin , async ( req , res ) => {
541+ try {
542+ const id = parseInt ( req . params . id , 10 ) ;
543+ const [ nest ] = await query ( 'SELECT ptero_nest_id FROM nests WHERE id = ?' , [ id ] ) ;
544+ if ( ! nest ) return res . status ( 404 ) . json ( { error : 'Nest not found' } ) ;
545+ await query ( 'DELETE FROM egg_resources WHERE ptero_nest_id = ?' , [ nest . ptero_nest_id ] ) ;
546+ await query ( 'DELETE FROM nests WHERE id = ?' , [ id ] ) ;
547+ res . json ( { success : true } ) ;
548+ } catch ( err ) {
549+ res . status ( 500 ) . json ( { error : 'Failed to delete nest' } ) ;
550+ }
551+ } ) ;
552+
553+ // ─── Settings: Eggs ─────────────────────────────────────
554+ router . get ( '/settings/nests/:nestId/eggs' , authenticateToken , requireAdmin , async ( req , res ) => {
555+ try {
556+ const nestId = parseInt ( req . params . nestId , 10 ) ;
557+ const pteroEggs = await getPteroNestEggs ( nestId ) ;
558+ const resources = await query ( 'SELECT * FROM egg_resources WHERE ptero_nest_id = ?' , [ nestId ] ) ;
559+ const resourceMap = { } ;
560+ for ( const r of resources ) {
561+ resourceMap [ r . ptero_egg_id ] = r ;
562+ }
563+ const eggs = pteroEggs . map ( e => ( {
564+ ...e ,
565+ customResources : resourceMap [ e . id ] || null ,
566+ } ) ) ;
567+ res . json ( { eggs } ) ;
568+ } catch ( err ) {
569+ res . status ( 500 ) . json ( { error : 'Failed to fetch eggs: ' + err . message } ) ;
570+ }
571+ } ) ;
572+
573+ router . get ( '/settings/eggs/:nestId/:eggId' , authenticateToken , requireAdmin , async ( req , res ) => {
574+ try {
575+ const { nestId, eggId } = req . params ;
576+ const [ resources ] = await query ( 'SELECT * FROM egg_resources WHERE ptero_nest_id = ? AND ptero_egg_id = ?' , [ nestId , eggId ] ) ;
577+ let eggDetails = null ;
578+ try {
579+ eggDetails = await getEgg ( nestId , eggId ) ;
580+ } catch { }
581+ res . json ( { resources : resources || null , egg : eggDetails } ) ;
582+ } catch ( err ) {
583+ res . status ( 500 ) . json ( { error : 'Failed to fetch egg settings' } ) ;
584+ }
585+ } ) ;
586+
587+ router . put ( '/settings/eggs/:nestId/:eggId' , authenticateToken , requireAdmin , async ( req , res ) => {
588+ try {
589+ const { nestId, eggId } = req . params ;
590+ const { cpu_limit, memory_limit, disk_limit } = req . body ;
591+
592+ const [ existing ] = await query ( 'SELECT id FROM egg_resources WHERE ptero_nest_id = ? AND ptero_egg_id = ?' , [ nestId , eggId ] ) ;
593+ if ( existing ) {
594+ await query ( 'UPDATE egg_resources SET cpu_limit = ?, memory_limit = ?, disk_limit = ? WHERE id = ?' ,
595+ [ cpu_limit ?? null , memory_limit ?? null , disk_limit ?? null , existing . id ] ) ;
596+ } else {
597+ await query ( 'INSERT INTO egg_resources (ptero_nest_id, ptero_egg_id, cpu_limit, memory_limit, disk_limit) VALUES (?, ?, ?, ?, ?)' ,
598+ [ nestId , eggId , cpu_limit ?? null , memory_limit ?? null , disk_limit ?? null ] ) ;
599+ }
600+ res . json ( { success : true } ) ;
601+ } catch ( err ) {
602+ res . status ( 500 ) . json ( { error : 'Failed to save egg settings' } ) ;
603+ }
604+ } ) ;
605+
485606export default router ;
0 commit comments