Skip to content
This repository was archived by the owner on Jul 28, 2026. It is now read-only.

Commit 83d23cb

Browse files
authored
Merge pull request #13 from ZeroHost-Code/beta
v1.0.2
2 parents 7595486 + fa9874f commit 83d23cb

20 files changed

Lines changed: 2945 additions & 399 deletions

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ logs/
2727
port.txt
2828
reboot.sh
2929

30+
# Uploads
31+
uploads/
32+
3033
# Secrets (if any local dump)
3134
*.pem
3235
*.key

README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,6 @@ flowchart TD
225225
T["/pyrodactyl\nPanel Redirect"] --> U["5-second auto-redirect\nto panel.zero-host.org"]
226226
227227
V["/account\nAccount Hub"] --> W["/account/edit\n(Email, Password, API Key)"]
228-
V --> X["/account/links\n(Linked Accounts)"]
229228
V --> Y["/account/dangerous\n(Delete Account, RGPD Export)"]
230229
231230
Z["/log\nActivity Log"] --> AA["Paginated list\n(50 per page)"]

config/migrate.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ const tables = {
1313
{ name: 'last_name', def: 'VARCHAR(255)' },
1414
{ name: 'password_set', def: 'TINYINT(1) NOT NULL DEFAULT 0' },
1515
{ name: 'is_admin', def: 'TINYINT(1) NOT NULL DEFAULT 0' },
16+
{ name: 'restricted', def: 'TINYINT(1) NOT NULL DEFAULT 0' },
17+
{ name: 'auth_restricted', def: 'TINYINT(1) NOT NULL DEFAULT 0' },
18+
{ name: 'token_version', def: 'INT NOT NULL DEFAULT 0' },
19+
{ name: 'avatar', def: 'VARCHAR(255) DEFAULT NULL' },
1620
{ name: 'ptero_client_api_key', def: 'VARCHAR(255) DEFAULT NULL' },
1721
{ name: 'created_at', def: 'TIMESTAMP DEFAULT CURRENT_TIMESTAMP' },
1822
],
@@ -47,6 +51,38 @@ const tables = {
4751
{ name: 'created_at', def: 'TIMESTAMP DEFAULT CURRENT_TIMESTAMP' },
4852
],
4953
},
54+
nests: {
55+
columns: [
56+
{ name: 'id', def: 'INT AUTO_INCREMENT PRIMARY KEY' },
57+
{ name: 'ptero_nest_id', def: 'INT NOT NULL UNIQUE' },
58+
{ name: 'name', def: 'VARCHAR(255) NOT NULL' },
59+
{ name: 'created_at', def: 'TIMESTAMP DEFAULT CURRENT_TIMESTAMP' },
60+
],
61+
},
62+
egg_resources: {
63+
columns: [
64+
{ name: 'id', def: 'INT AUTO_INCREMENT PRIMARY KEY' },
65+
{ name: 'ptero_nest_id', def: 'INT NOT NULL' },
66+
{ name: 'ptero_egg_id', def: 'INT NOT NULL' },
67+
{ name: 'cpu_limit', def: 'INT DEFAULT NULL' },
68+
{ name: 'memory_limit', def: 'INT DEFAULT NULL' },
69+
{ name: 'disk_limit', def: 'INT DEFAULT NULL' },
70+
{ name: 'created_at', def: 'TIMESTAMP DEFAULT CURRENT_TIMESTAMP' },
71+
{ name: 'updated_at', def: 'TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP' },
72+
],
73+
},
74+
notifications: {
75+
columns: [
76+
{ name: 'id', def: 'INT AUTO_INCREMENT PRIMARY KEY' },
77+
{ name: 'user_id', def: 'INT NOT NULL' },
78+
{ name: 'title', def: 'VARCHAR(255) NOT NULL' },
79+
{ name: 'message', def: 'TEXT NOT NULL' },
80+
{ name: 'type', def: "VARCHAR(20) NOT NULL DEFAULT 'info'" },
81+
{ name: 'link', def: 'VARCHAR(255) DEFAULT NULL' },
82+
{ name: 'is_read', def: 'TINYINT(1) NOT NULL DEFAULT 0' },
83+
{ name: 'created_at', def: 'TIMESTAMP DEFAULT CURRENT_TIMESTAMP' },
84+
],
85+
},
5086

5187
};
5288

@@ -67,6 +103,10 @@ const constraints = [
67103
{ table: 'user_ips', sql: 'ALTER TABLE user_ips ADD CONSTRAINT fk_user_ips_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE', name: 'fk_user_ips_user' },
68104
{ table: 'activity_log', sql: 'ALTER TABLE activity_log ADD INDEX idx_activity_user (user_id)', name: 'idx_activity_user' },
69105
{ table: 'activity_log', sql: 'ALTER TABLE activity_log ADD INDEX idx_activity_created (created_at)', name: 'idx_activity_created' },
106+
{ table: 'egg_resources', sql: 'ALTER TABLE egg_resources ADD UNIQUE INDEX idx_egg_resources_nest_egg (ptero_nest_id, ptero_egg_id)', name: 'idx_egg_resources_nest_egg' },
107+
{ table: 'notifications', sql: 'ALTER TABLE notifications ADD INDEX idx_notif_user (user_id)', name: 'idx_notif_user' },
108+
{ table: 'notifications', sql: 'ALTER TABLE notifications ADD INDEX idx_notif_user_read (user_id, is_read)', name: 'idx_notif_user_read' },
109+
{ table: 'notifications', sql: 'ALTER TABLE notifications ADD CONSTRAINT fk_notif_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE', name: 'fk_notif_user' },
70110
];
71111

72112
export async function migrate() {

config/pyrodactyl.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,3 @@ export const FEATURE_LIMITS = {
1717
};
1818

1919
export const DEPLOY_LOCATIONS = [1];
20-
21-
export const NEST_IDS = [5, 6, 7];

middleware/auth.js

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import jwt from 'jsonwebtoken';
2+
import { query } from '../config/db.js';
23

34
const JWT_SECRET = process.env.JWT_SECRET;
45

@@ -8,7 +9,7 @@ if (!JWT_SECRET) {
89
console.error('JWT_SECRET contains unresolved shell expansion characters ($(), backticks). Generate a proper random key (e.g. openssl rand -hex 32) and hardcode it in .env');
910
}
1011

11-
export function authenticateToken(req, res, next) {
12+
export async function authenticateToken(req, res, next) {
1213
const authHeader = req.headers['authorization'];
1314
const token = authHeader && authHeader.split(' ')[1];
1415

@@ -19,6 +20,18 @@ export function authenticateToken(req, res, next) {
1920
try {
2021
const decoded = jwt.verify(token, JWT_SECRET);
2122
req.user = decoded;
23+
24+
if (decoded.tokenVersion !== undefined) {
25+
try {
26+
const rows = await query('SELECT token_version FROM users WHERE id = ?', [decoded.userId]);
27+
if (rows.length > 0 && rows[0].token_version !== decoded.tokenVersion) {
28+
return res.status(403).json({ error: 'Session expired. Please log in again.' });
29+
}
30+
} catch {
31+
return res.status(403).json({ error: 'Session validation failed.' });
32+
}
33+
}
34+
2235
next();
2336
} catch {
2437
return res.status(403).json({ error: 'Invalid or expired token' });

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "zerohost-dashboard",
3-
"version": "1.0.1",
3+
"version": "1.0.2",
44
"private": true,
55
"type": "module",
66
"scripts": {

0 commit comments

Comments
 (0)