Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions config/db.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,4 @@ export async function query(sql, params = []) {
throw lastErr;
}

export async function getConnection() {
return await pool.getConnection();
}

export default pool;
5 changes: 1 addition & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,13 @@
},
"dependencies": {
"argon2": "^0.41.1",
"better-sqlite3": "^11.7.0",
"cookie-parser": "^1.4.7",
"cors": "^2.8.5",
"dotenv": "^16.4.7",
"express": "^4.21.1",
"express-rate-limit": "^7.4.1",
"helmet": "^8.0.0",
"jsonwebtoken": "^9.0.2",
"mariadb": "^3.4.0",
"uuid": "^11.0.3",
"zod": "^3.24.1"
"mariadb": "^3.4.0"
}
}
78 changes: 0 additions & 78 deletions public/css/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -47,21 +47,6 @@ html, body {
a { color: var(--accent-3); text-decoration: none; }
a:hover { color: var(--accent-1); }

.gradient-text {
background: linear-gradient(135deg, var(--accent-1), var(--accent-2), var(--accent-cyan));
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
background-size: 200% 200%;
animation: gradientShift 4s ease infinite;
}

@keyframes gradientShift {
0% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
100% { background-position: 0% 50%; }
}

/* ===== AUTH PAGES ===== */
.auth-page {
min-height: 100vh;
Expand Down Expand Up @@ -969,34 +954,6 @@ tbody tr:hover {
transition: width 0.5s ease;
}

/* ===== LOADING SCREEN ===== */
.loading-screen {
display: flex;
align-items: center;
justify-content: center;
min-height: 100vh;
background: var(--bg-primary);
}

.loading-content {
text-align: center;
}

.loading-spinner {
width: 40px;
height: 40px;
border: 3px solid rgba(99, 102, 241, 0.15);
border-top-color: var(--accent-1);
border-radius: 50%;
animation: spin 0.7s linear infinite;
margin: 0 auto 16px;
}

.loading-text {
font-size: 0.95rem;
color: var(--text-secondary);
}

/* ===== RESPONSIVE ===== */
.hamburger-toggle {
display: none;
Expand Down Expand Up @@ -1189,26 +1146,6 @@ tbody tr:hover {
color: var(--accent-orange);
}

.exp-col {
font-size: 0.82rem;
display: flex;
gap: 4px;
align-items: center;
}

.exp-col.expired {
color: var(--accent-red);
}

.exp-col.expiring {
color: var(--accent-orange);
}

.exp-days {
font-size: 0.75rem;
opacity: 0.8;
}

/* ===== SERVER DETAIL PAGE ===== */
.server-detail-grid {
display: grid;
Expand Down Expand Up @@ -1317,20 +1254,6 @@ tbody tr:hover {
gap: 10px;
}

.input-readonly {
width: 100%;
padding: 12px 16px;
background: var(--bg-secondary);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
color: var(--text-muted);
font-family: 'Inter', sans-serif;
font-size: 0.95rem;
outline: none;
cursor: default;
opacity: 0.7;
}

.consent-group input[type="checkbox"] {
width: 18px;
height: 18px;
Expand All @@ -1352,5 +1275,4 @@ tbody tr:hover {
text-decoration: underline;
}

/* ===== PRIVACY / LEGAL PAGES ===== */

8 changes: 1 addition & 7 deletions public/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,6 @@ const state = {
serverDetailTab: 'info',
};

function getRgpdConsent() {
return state.rgpdConsent;
}

function setRgpdConsent(preferences) {
state.rgpdConsent = preferences;
localStorage.setItem('zh_rgpd_consent', JSON.stringify(preferences));
Expand Down Expand Up @@ -50,8 +46,6 @@ function renderCookieBanner() {
}

function $(sel) { return document.querySelector(sel); }
function $$(sel) { return document.querySelectorAll(sel); }

function md5(s) {
function F(x,y,z) { return (x & y) | (~x & z); }
function G(x,y,z) { return (x & z) | (y & ~z); }
Expand Down Expand Up @@ -404,7 +398,7 @@ async function renderDashboard() {
<div style="padding:8px 12px 0;display:flex;gap:16px;justify-content:center;flex-wrap:wrap">

</div>
<div style="padding:4px 0 8px;text-align:center;font-size:0.7rem;color:var(--text-muted);letter-spacing:0.05em">v0.9.5 BETA</div>
<div style="padding:4px 0 8px;text-align:center;font-size:0.7rem;color:var(--text-muted);letter-spacing:0.05em">v0.9.6 BETA</div>
</div>
</aside>

Expand Down
52 changes: 25 additions & 27 deletions routes/auth.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
import { Router } from 'express';
import jwt from 'jsonwebtoken';
import argon2 from 'argon2';
import { randomBytes } from 'crypto';
import { query } from '../config/db.js';
import { generateToken } from '../middleware/auth.js';
import { createPteroUser, getPteroUserByEmail, updatePteroPassword, updatePteroEmail, deletePteroUser, getServersByUser, deletePteroServer } from '../services/pterodactyl.js';
import { authenticateToken } from '../middleware/auth.js';
import { generateToken, authenticateToken } from '../middleware/auth.js';
import { createPteroUser, updatePteroPassword, updatePteroEmail, deletePteroUser, getServersByUser, deletePteroServer } from '../services/pterodactyl.js';
import { verifyTurnstile } from '../config/turnstile.js';

const router = Router();

import { verifyTurnstile } from '../config/turnstile.js';
import { v4 as uuidv4 } from 'uuid';

function getClientIp(req) {
const forwarded = req.headers['x-forwarded-for'];
if (forwarded) return forwarded.split(',')[0].trim();
Expand Down Expand Up @@ -344,13 +340,14 @@ router.post('/change-email', authenticateToken, async (req, res) => {
router.post('/delete-account', authenticateToken, async (req, res) => {
try {
const { password } = req.body;
const userId = req.user?.userId;
const pteroId = req.user?.pteroId;

if (!password) {
return res.status(400).json({ error: 'Password is required' });
}

const users = await query('SELECT * FROM users WHERE ptero_user_id = ?', [pteroId]);
const users = await query('SELECT * FROM users WHERE id = ?', [userId]);
if (users.length === 0) {
return res.status(404).json({ error: 'User not found' });
}
Expand All @@ -362,30 +359,31 @@ router.post('/delete-account', authenticateToken, async (req, res) => {
return res.status(401).json({ error: 'Password is incorrect' });
}

// Delete all Pterodactyl servers first
try {
const servers = await getServersByUser(pteroId);
for (const server of servers) {
try {
await deletePteroServer(server.id);
} catch (err) {
console.error(`Failed to delete server ${server.id}:`, err.message);
// Delete from local DB first (cascades to user_ips)
await query('DELETE FROM users WHERE id = ?', [user.id]);

// Then try to clean up Pterodactyl (best effort)
if (pteroId) {
try {
const servers = await getServersByUser(pteroId);
for (const server of servers) {
try {
await deletePteroServer(server.id);
} catch (err) {
console.error(`Failed to delete server ${server.id}:`, err.message);
}
}
} catch (err) {
console.error('Failed to fetch servers for deletion:', err.message);
}
} catch (err) {
console.error('Failed to fetch servers for deletion:', err.message);
}

// Delete Pterodactyl user
try {
await deletePteroUser(pteroId);
} catch (err) {
console.error('Failed to delete Pterodactyl user:', err.message);
try {
await deletePteroUser(pteroId);
} catch (err) {
console.error('Failed to delete Pterodactyl user:', err.message);
}
}

// Delete from local DB (cascades to user_ips)
await query('DELETE FROM users WHERE id = ?', [user.id]);

res.json({ message: 'Account deleted successfully' });
} catch (err) {
console.error('Delete account error:', err.message);
Expand Down
2 changes: 0 additions & 2 deletions routes/servers.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,9 @@ import {
deletePteroServer,
reinstallPteroServer,
renamePteroServer,
suspendPteroServer,
unsuspendPteroServer,
getEgg,
getAllEggs,
getPteroUserById,
} from '../services/pterodactyl.js';
import { query } from '../config/db.js';
import { verifyTurnstile } from '../config/turnstile.js';
Expand Down
2 changes: 1 addition & 1 deletion services/pterodactyl.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export async function createPteroUser({ email, username, firstName, lastName, pa
return data.attributes;
}

export async function getPteroUserByEmail(email) {
async function getPteroUserByEmail(email) {
const data = await pteroFetch(`/users?filter[email]=${encodeURIComponent(email)}`);
if (data.data.length > 0) {
return data.data[0].attributes;
Expand Down