Skip to content

Commit 58fbda0

Browse files
committed
feat: add pagination to /admin/servers (10 per page)
1 parent ca74f91 commit 58fbda0

3 files changed

Lines changed: 55 additions & 6 deletions

File tree

public/js/admin.js

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,8 @@ function updateAdminNav() {
512512
});
513513
}
514514

515+
let adminServersPage = 1;
516+
515517
async function renderAdminServers() {
516518
document.querySelectorAll('.admin-page').forEach(p => p.classList.remove('active'));
517519
const el = $a('#admin-page-servers');
@@ -538,16 +540,27 @@ async function renderAdminServers() {
538540
<tr><td colspan="6" style="text-align:center;padding:32px;color:var(--text-secondary)"><span class="spinner"></span> Loading...</td></tr>
539541
</tbody>
540542
</table>
543+
<div id="admin-servers-pagination" style="display:none"></div>
541544
</div>
542545
`;
543546

547+
await fetchAdminServers(adminServersPage);
548+
}
549+
550+
async function fetchAdminServers(pageNum) {
551+
pageNum = pageNum || 1;
552+
const limit = 10;
553+
const offset = (pageNum - 1) * limit;
554+
const paginationEl = $a('#admin-servers-pagination');
555+
544556
try {
545-
const data = await adminApi('/servers');
557+
const data = await adminApi(`/servers?limit=${limit}&offset=${offset}`);
546558
const tbody = $a('#admin-servers-tbody');
547559
if (!tbody) return;
548560

549561
if (data.servers.length === 0) {
550562
tbody.innerHTML = '<tr><td colspan="6" style="text-align:center;padding:32px;color:var(--text-secondary)">No servers found.</td></tr>';
563+
if (paginationEl) paginationEl.style.display = 'none';
551564
return;
552565
}
553566

@@ -574,13 +587,31 @@ async function renderAdminServers() {
574587
</tr>
575588
`;
576589
}).join('');
590+
591+
if (data.totalPages > 1) {
592+
paginationEl.innerHTML = ahtml`
593+
<div class="log-pagination">
594+
<button class="btn btn-ghost btn-sm" onclick="changeAdminServersPage(${pageNum - 1})" ${pageNum <= 1 ? 'disabled' : ''}>Previous</button>
595+
<span class="log-pagination-info">Page ${data.page} of ${data.totalPages} (${data.total} total)</span>
596+
<button class="btn btn-ghost btn-sm" onclick="changeAdminServersPage(${pageNum + 1})" ${pageNum >= data.totalPages ? 'disabled' : ''}>Next</button>
597+
</div>
598+
`;
599+
paginationEl.style.display = '';
600+
} else {
601+
paginationEl.style.display = 'none';
602+
}
577603
} catch (err) {
578604
const tbody = $a('#admin-servers-tbody');
579605
if (tbody) tbody.innerHTML = `<tr><td colspan="6" style="text-align:center;padding:32px;color:var(--accent-red)">Error: ${err.message}</td></tr>`;
580606
}
581607
initIcons();
582608
}
583609

610+
function changeAdminServersPage(pageNum) {
611+
adminServersPage = pageNum;
612+
fetchAdminServers(pageNum);
613+
}
614+
584615
async function renderAdminServerDetail(serverId) {
585616
document.querySelectorAll('.admin-page').forEach(p => p.classList.remove('active'));
586617
const detailPage = $a('#admin-page-server-detail');

routes/admin.js

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -131,14 +131,21 @@ router.get('/check', authenticateToken, requireAdmin, (req, res) => {
131131

132132
router.get('/servers', authenticateToken, requireAdmin, async (req, res) => {
133133
try {
134-
const allServers = await getAllServers();
134+
const limit = Math.min(parseInt(req.query.limit, 10) || 10, 50);
135+
const offset = Math.max(parseInt(req.query.offset, 10) || 0, 0);
136+
137+
const result = await getAllServers(limit, offset);
138+
const { servers: paginatedServers, total } = result;
139+
const page = Math.floor(offset / limit) + 1;
140+
const totalPages = Math.ceil(total / limit) || 1;
141+
135142
const users = await query('SELECT id, email, username, ptero_user_id FROM users');
136143
const userMap = {};
137144
for (const u of users) {
138145
userMap[u.ptero_user_id] = { id: u.id, email: u.email, username: u.username };
139146
}
140147

141-
for (const s of allServers) {
148+
for (const s of paginatedServers) {
142149
s.owner = userMap[s.user] || { id: null, email: 'Unknown', username: 'Unknown' };
143150
try {
144151
const meta = await query('SELECT * FROM server_meta WHERE ptero_server_id = ?', [s.id]);
@@ -148,7 +155,7 @@ router.get('/servers', authenticateToken, requireAdmin, async (req, res) => {
148155
}
149156
}
150157

151-
res.json({ servers: allServers });
158+
res.json({ servers: paginatedServers, total, page, totalPages, limit });
152159
} catch (err) {
153160
console.error('Admin servers list error:', err.message);
154161
res.status(500).json({ error: 'Failed to fetch servers' });

services/pyrodactyl.js

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ export async function getPteroNestEggs(nestId) {
143143
return data.data.map(e => e.attributes);
144144
}
145145

146-
export async function getAllServers() {
146+
export async function getAllServers(limit = null, offset = null) {
147147
let allServers = [];
148148
let page = 1;
149149
let hasMore = true;
@@ -160,7 +160,14 @@ export async function getAllServers() {
160160
}
161161
}
162162

163-
for (const server of allServers) {
163+
const total = allServers.length;
164+
165+
let serversToEnrich = allServers;
166+
if (limit !== null && offset !== null) {
167+
serversToEnrich = allServers.slice(offset, offset + limit);
168+
}
169+
170+
for (const server of serversToEnrich) {
164171
try {
165172
const node = await getNode(server.node);
166173
server.nodeFqdn = node.fqdn;
@@ -176,6 +183,10 @@ export async function getAllServers() {
176183
} catch { server.eggDetails = null; }
177184
}
178185

186+
if (limit !== null && offset !== null) {
187+
return { servers: serversToEnrich, total };
188+
}
189+
179190
return allServers;
180191
}
181192

0 commit comments

Comments
 (0)