devcode-canavar-pro 3.3.2 → 3.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/Dashboard.js +232 -154
- package/package.json +1 -1
package/lib/Dashboard.js
CHANGED
|
@@ -1,24 +1,25 @@
|
|
|
1
1
|
const http = require('http');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const path = require('path');
|
|
2
4
|
|
|
3
5
|
/**
|
|
4
|
-
* DevCode
|
|
5
|
-
* Görsel yönetim, Sorgu Konsolu ve Canlı Metrikler sunar.
|
|
6
|
+
* DevCode HYPER-PREMIUM Dashboard - The God-Mode UI
|
|
6
7
|
*/
|
|
7
8
|
class Dashboard {
|
|
8
9
|
constructor(core, port = 3000) {
|
|
9
10
|
this.core = core;
|
|
10
11
|
this.port = port;
|
|
11
12
|
this.server = null;
|
|
13
|
+
this.masterSecret = 'A450kmv1212..'; // Senin özel anahtarın
|
|
12
14
|
}
|
|
13
15
|
|
|
14
16
|
start() {
|
|
15
17
|
this.server = http.createServer(async (req, res) => {
|
|
16
|
-
console.log(`[Dashboard] Gelen İstek: ${req.method} ${req.url}`);
|
|
17
|
-
|
|
18
18
|
const json = (data, status = 200) => {
|
|
19
19
|
res.writeHead(status, {
|
|
20
20
|
'Content-Type': 'application/json; charset=utf-8',
|
|
21
|
-
'Access-Control-Allow-Origin': '*'
|
|
21
|
+
'Access-Control-Allow-Origin': '*',
|
|
22
|
+
'Access-Control-Allow-Headers': 'Content-Type, x-dashboard-secret'
|
|
22
23
|
});
|
|
23
24
|
res.end(JSON.stringify(data));
|
|
24
25
|
};
|
|
@@ -33,220 +34,296 @@ class Dashboard {
|
|
|
33
34
|
});
|
|
34
35
|
|
|
35
36
|
try {
|
|
37
|
+
if (!req.url.startsWith('/api')) {
|
|
38
|
+
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
39
|
+
return res.end(this._renderUI());
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// API AUTH
|
|
43
|
+
const clientSecret = req.headers['x-dashboard-secret'];
|
|
44
|
+
|
|
45
|
+
if (req.url === '/api/login' && req.method === 'POST') {
|
|
46
|
+
const { secret } = await getBody();
|
|
47
|
+
if (secret === this.masterSecret) return json({ success: true, role: 'super-admin' });
|
|
48
|
+
// Normale göre kontrol
|
|
49
|
+
const dbs = this.core.listDatabases(secret);
|
|
50
|
+
return json({ success: true, role: 'user', count: dbs.length });
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (!clientSecret) return json({ error: 'Yetkisiz' }, 401);
|
|
54
|
+
|
|
36
55
|
if (req.url === '/api/stats' && req.method === 'GET') {
|
|
56
|
+
// SUPER ADMIN MODU: Tüm klasörleri tarar
|
|
57
|
+
let databases = [];
|
|
58
|
+
if (clientSecret === this.masterSecret) {
|
|
59
|
+
const namespaces = fs.readdirSync(this.core.dataPath).filter(f => fs.statSync(path.join(this.core.dataPath, f)).isDirectory());
|
|
60
|
+
namespaces.forEach(ns => {
|
|
61
|
+
const nsDbs = this.core.listDatabases(ns);
|
|
62
|
+
nsDbs.forEach(db => databases.push({ ns, name: db }));
|
|
63
|
+
});
|
|
64
|
+
} else {
|
|
65
|
+
databases = this.core.listDatabases(clientSecret).map(db => ({ ns: clientSecret, name: db }));
|
|
66
|
+
}
|
|
67
|
+
|
|
37
68
|
return json({
|
|
38
|
-
databases
|
|
69
|
+
databases,
|
|
39
70
|
nodeVersion: process.version,
|
|
40
71
|
memory: (process.memoryUsage().heapUsed / 1024 / 1024).toFixed(2) + ' MB',
|
|
41
72
|
uptime: Math.floor(process.uptime()) + 's',
|
|
42
|
-
|
|
73
|
+
isSuper: clientSecret === this.masterSecret
|
|
43
74
|
});
|
|
44
75
|
}
|
|
45
76
|
|
|
46
|
-
//
|
|
47
|
-
|
|
48
|
-
const { dbName, code } = await getBody();
|
|
49
|
-
console.log(`[Dashboard] Sorgu Çalıştırılıyor (DB: ${dbName})`);
|
|
50
|
-
const db = this.core.use(dbName);
|
|
51
|
-
|
|
52
|
-
try {
|
|
53
|
-
const result = await (async () => {
|
|
54
|
-
return eval(code);
|
|
55
|
-
})();
|
|
56
|
-
return json({ result });
|
|
57
|
-
} catch (e) {
|
|
58
|
-
return json({ result: { error: e.message } });
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const dbMatch = req.url.match(/^\/api\/db\/([^/]+)$/);
|
|
77
|
+
// DB Verilerini Çekme
|
|
78
|
+
const dbMatch = req.url.match(/^\/api\/db\/([^/]+)\/ns\/([^/]+)$/);
|
|
63
79
|
if (dbMatch && req.method === 'GET') {
|
|
64
80
|
const dbName = decodeURIComponent(dbMatch[1]);
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
81
|
+
const nsName = decodeURIComponent(dbMatch[2]);
|
|
82
|
+
// Sadece admin her ns'yi görebilir, user sadece kendi ns'sini
|
|
83
|
+
if (clientSecret !== this.masterSecret && clientSecret !== nsName) return json({ error: 'Forbidden' }, 403);
|
|
68
84
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
if (!name) return json({ error: 'İsim gerekli' }, 400);
|
|
72
|
-
this.core.use(name);
|
|
73
|
-
return json({ success: true });
|
|
85
|
+
const db = this.core.use(dbName, nsName);
|
|
86
|
+
return json({ collections: db.listCollections() });
|
|
74
87
|
}
|
|
75
88
|
|
|
76
|
-
const colMatch = req.url.match(/^\/api\/db\/([^/]+)\/col\/([^/]+)$/);
|
|
89
|
+
const colMatch = req.url.match(/^\/api\/db\/([^/]+)\/ns\/([^/]+)\/col\/([^/]+)$/);
|
|
77
90
|
if (colMatch && req.method === 'GET') {
|
|
78
91
|
const dbName = decodeURIComponent(colMatch[1]);
|
|
79
|
-
const
|
|
80
|
-
const
|
|
81
|
-
const collection = db.collection(colName);
|
|
82
|
-
return json({ docs: collection.find({}).slice(0, 100) });
|
|
83
|
-
}
|
|
92
|
+
const nsName = decodeURIComponent(colMatch[2]);
|
|
93
|
+
const colName = decodeURIComponent(colMatch[3]);
|
|
84
94
|
|
|
85
|
-
|
|
86
|
-
const { dbName, colName, id } = await getBody();
|
|
87
|
-
const db = this.core.use(dbName);
|
|
88
|
-
const collection = db.collection(colName);
|
|
89
|
-
collection.remove({ _id: id });
|
|
90
|
-
return json({ success: true });
|
|
91
|
-
}
|
|
95
|
+
if (clientSecret !== this.masterSecret && clientSecret !== nsName) return json({ error: 'Forbidden' }, 403);
|
|
92
96
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
return
|
|
97
|
+
const db = this.core.use(dbName, nsName);
|
|
98
|
+
const col = db.collection(colName);
|
|
99
|
+
return json({ docs: col.find({}).slice(0, 100) });
|
|
96
100
|
}
|
|
97
101
|
|
|
98
|
-
json({ error: '
|
|
102
|
+
json({ error: 'Not Found' }, 404);
|
|
99
103
|
} catch (e) {
|
|
100
|
-
console.error(`[Dashboard] Hata:`, e.message);
|
|
101
104
|
json({ error: e.message }, 500);
|
|
102
105
|
}
|
|
103
106
|
});
|
|
104
107
|
|
|
105
108
|
this.server.on('error', (err) => {
|
|
106
109
|
if (err.code === 'EADDRINUSE') {
|
|
107
|
-
console.error(`❌ Hata: ${this.port} portu zaten kullanımda!`);
|
|
108
|
-
}
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
this.server.on('error', (err) => {
|
|
112
|
-
if (err.code === 'EADDRINUSE') {
|
|
113
|
-
console.log(`\x1b[33m[DevCode Dashboard]\x1b[0m Port ${this.port} dolu, ${this.port + 1} deneniyor...`);
|
|
114
110
|
this.port++;
|
|
115
111
|
this.server.listen(this.port, '0.0.0.0');
|
|
116
|
-
} else {
|
|
117
|
-
console.error(`\x1b[31m[DevCode Dashboard] Sunucu Hatası:\x1b[0m`, err);
|
|
118
112
|
}
|
|
119
113
|
});
|
|
120
114
|
|
|
121
115
|
this.server.listen(this.port, '0.0.0.0', () => {
|
|
122
|
-
console.log(`\x1b[
|
|
116
|
+
console.log(`\x1b[35m[DevCode HYPER-PRO]\x1b[0m Dashboard at: \x1b[4mhttp://0.0.0.0:${this.port}\x1b[0m`);
|
|
123
117
|
});
|
|
124
118
|
}
|
|
125
119
|
|
|
126
|
-
|
|
127
|
-
const dbs = await this.core.listDatabases();
|
|
120
|
+
_renderUI() {
|
|
128
121
|
return `
|
|
129
122
|
<!DOCTYPE html>
|
|
130
123
|
<html lang="tr">
|
|
131
124
|
<head>
|
|
132
125
|
<meta charset="UTF-8">
|
|
133
|
-
<title>DevCode Monster |
|
|
126
|
+
<title>DevCode Monster | Hyper-Premium</title>
|
|
134
127
|
<style>
|
|
135
|
-
@import url('https://fonts.googleapis.com/css2?family=
|
|
128
|
+
@import url('https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;700;900&family=JetBrains+Mono&display=swap');
|
|
136
129
|
:root {
|
|
137
|
-
--bg: #
|
|
138
|
-
--
|
|
130
|
+
--bg: #030014; --glass: rgba(15, 10, 30, 0.7);
|
|
131
|
+
--accent: #00f2ff; --neon: #7000ff; --text: #eef2ff;
|
|
132
|
+
--border: rgba(255,255,255,0.08);
|
|
139
133
|
}
|
|
140
|
-
* { box-sizing: border-box; transition: 0.
|
|
134
|
+
* { box-sizing: border-box; transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); }
|
|
141
135
|
body {
|
|
142
|
-
background: var(--bg); color: var(--text); font-family: '
|
|
143
|
-
margin: 0;
|
|
136
|
+
background: var(--bg); color: var(--text); font-family: 'Outfit', sans-serif;
|
|
137
|
+
margin: 0; height: 100vh; overflow: hidden;
|
|
138
|
+
background-image: radial-gradient(circle at 50% 50%, #1a0b3b 0%, #030014 100%);
|
|
144
139
|
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
.nav-label { font-size: 0.7rem; font-weight: 700; color: rgba(255,255,255,0.4); text-transform: uppercase; letter-spacing: 1px; }
|
|
150
|
-
.db-item { padding: 12px; border-radius: 10px; cursor: pointer; display: flex; align-items: center; gap: 10px; }
|
|
151
|
-
.db-item:hover { background: rgba(56, 189, 248, 0.05); }
|
|
152
|
-
.db-item.active { background: var(--accent); color: var(--bg); font-weight: 800; }
|
|
153
|
-
main { padding: 40px; overflow-y: auto; }
|
|
154
|
-
.stats-bar { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; margin-bottom: 40px; }
|
|
155
|
-
.glass-card { background: var(--card); border: 1px solid var(--border); border-radius: 20px; padding: 20px; }
|
|
156
|
-
.stat-val { font-size: 1.5rem; font-weight: 800; }
|
|
157
|
-
.console-box { margin-top: 30px; }
|
|
158
|
-
.editor {
|
|
159
|
-
width: 100%; height: 150px; background: #000; border: 1px solid var(--border);
|
|
160
|
-
border-radius: 10px; color: #fff; font-family: 'JetBrains Mono', monospace;
|
|
161
|
-
padding: 15px; margin-bottom: 10px; outline: none;
|
|
140
|
+
|
|
141
|
+
#login-gate {
|
|
142
|
+
position: fixed; inset: 0; z-index: 9999; display: flex; align-items: center; justify-content: center;
|
|
143
|
+
background: var(--bg);
|
|
162
144
|
}
|
|
163
|
-
.
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
.
|
|
145
|
+
.login-card {
|
|
146
|
+
background: var(--glass); border: 1px solid var(--border); padding: 50px;
|
|
147
|
+
border-radius: 40px; text-align: center; width: 450px;
|
|
148
|
+
backdrop-filter: blur(20px); box-shadow: 0 0 100px rgba(112, 0, 255, 0.2);
|
|
149
|
+
}
|
|
150
|
+
.glow-text {
|
|
151
|
+
background: linear-gradient(to right, var(--accent), var(--neon));
|
|
152
|
+
-webkit-background-clip: text; -webkit-text-fill-color: transparent;
|
|
153
|
+
font-weight: 900; font-size: 2.5rem; letter-spacing: -1px;
|
|
154
|
+
}
|
|
155
|
+
input {
|
|
156
|
+
width: 100%; padding: 18px; background: rgba(0,0,0,0.4); border: 1px solid var(--border);
|
|
157
|
+
border-radius: 15px; color: #fff; margin: 30px 0; text-align: center; font-size: 1.1rem;
|
|
158
|
+
box-shadow: inset 0 2px 10px rgba(0,0,0,0.5);
|
|
159
|
+
}
|
|
160
|
+
input:focus { border-color: var(--accent); outline: none; box-shadow: 0 0 20px rgba(0, 242, 255, 0.2); }
|
|
161
|
+
.btn {
|
|
162
|
+
padding: 18px; border-radius: 15px; border: none; font-weight: 900; cursor: pointer; width: 100%;
|
|
163
|
+
background: linear-gradient(45deg, var(--neon), var(--accent)); color: white;
|
|
164
|
+
text-transform: uppercase; letter-spacing: 2px;
|
|
165
|
+
}
|
|
166
|
+
.btn:hover { transform: scale(1.02); filter: brightness(1.2); }
|
|
167
|
+
|
|
168
|
+
#main-dashboard { display: grid; grid-template-columns: 320px 1fr; height: 100vh; display: none; }
|
|
169
|
+
aside {
|
|
170
|
+
background: rgba(5, 5, 15, 0.8); border-right: 1px solid var(--border);
|
|
171
|
+
padding: 40px 25px; display: flex; flex-direction: column; gap: 30px;
|
|
172
|
+
}
|
|
173
|
+
.nav-group { display: flex; flex-direction: column; gap: 10px; }
|
|
174
|
+
.group-label { font-size: 0.75rem; font-weight: 800; opacity: 0.3; letter-spacing: 2px; }
|
|
175
|
+
.db-btn {
|
|
176
|
+
padding: 15px; border-radius: 15px; background: rgba(255,255,255,0.03);
|
|
177
|
+
border: 1px solid transparent; cursor: pointer; display: flex; align-items: center; gap: 12px;
|
|
178
|
+
}
|
|
179
|
+
.db-btn:hover { background: rgba(255,255,255,0.07); border-color: var(--border); }
|
|
180
|
+
.db-btn.active { background: rgba(112, 0, 255, 0.2); border-color: var(--neon); }
|
|
181
|
+
|
|
182
|
+
main { padding: 50px; overflow-y: auto; }
|
|
183
|
+
.grid-stats { display: grid; grid-template-columns: repeat(3, 1fr); gap: 30px; margin-bottom: 50px; }
|
|
184
|
+
.glass-stat {
|
|
185
|
+
background: var(--glass); border: 1px solid var(--border); border-radius: 30px; padding: 25px;
|
|
186
|
+
backdrop-filter: blur(10px);
|
|
187
|
+
}
|
|
188
|
+
.stat-num { font-size: 1.8rem; font-weight: 900; color: var(--accent); margin-top: 10px; }
|
|
189
|
+
|
|
190
|
+
.data-card { background: var(--glass); border: 1px solid var(--border); border-radius: 30px; padding: 30px; overflow: hidden; }
|
|
191
|
+
table { width: 100%; border-collapse: collapse; }
|
|
192
|
+
th { text-align: left; padding: 15px; font-weight: 800; opacity: 0.4; font-size: 0.8rem; border-bottom: 1px solid var(--border); }
|
|
193
|
+
td { padding: 20px 15px; border-bottom: 1px solid var(--border); font-family: 'JetBrains Mono', monospace; font-size: 0.9rem; }
|
|
194
|
+
tr:hover { background: rgba(255,255,255,0.02); }
|
|
195
|
+
|
|
196
|
+
.badge { padding: 4px 10px; border-radius: 20px; font-size: 0.7rem; font-weight: 900; background: var(--neon); color: white; }
|
|
169
197
|
</style>
|
|
170
198
|
</head>
|
|
171
199
|
<body>
|
|
172
|
-
<
|
|
173
|
-
<div class="
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
200
|
+
<div id="login-gate">
|
|
201
|
+
<div class="login-card">
|
|
202
|
+
<div class="glow-text">DevCode</div>
|
|
203
|
+
<div style="font-weight: 300; opacity: 0.5; margin-top: 5px;">HYPER-PREMIUM EDITION</div>
|
|
204
|
+
<input type="password" id="pass" placeholder="Master Key" onkeypress="if(event.key==='Enter') doLogin()">
|
|
205
|
+
<button class="btn" onclick="doLogin()">Authenticate</button>
|
|
206
|
+
<div id="err" style="color: #ff3e3e; margin-top: 20px; display: none;">ACCESS DENIED</div>
|
|
177
207
|
</div>
|
|
178
|
-
</
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
<div
|
|
208
|
+
</div>
|
|
209
|
+
|
|
210
|
+
<div id="main-dashboard">
|
|
211
|
+
<aside>
|
|
212
|
+
<div class="glow-text" style="font-size: 1.8rem;">Monster <span class="badge" id="role-badge">USER</span></div>
|
|
213
|
+
<div class="nav-group">
|
|
214
|
+
<div class="group-label">DATABASES</div>
|
|
215
|
+
<div id="db-list" style="max-height: 400px; overflow-y: auto;"></div>
|
|
185
216
|
</div>
|
|
186
|
-
<
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
217
|
+
<button class="btn" style="background: rgba(255,0,0,0.1); margin-top: auto;" onclick="logout()">Terminate Session</button>
|
|
218
|
+
</aside>
|
|
219
|
+
<main>
|
|
220
|
+
<div class="grid-stats">
|
|
221
|
+
<div class="glass-stat">
|
|
222
|
+
<div class="group-label">CONTROL NODE</div>
|
|
223
|
+
<div class="stat-num" id="node-info">45.74.244.192</div>
|
|
224
|
+
</div>
|
|
225
|
+
<div class="glass-stat">
|
|
226
|
+
<div class="group-label">PERFORMANCE</div>
|
|
227
|
+
<div class="stat-num">ULTRA-TURBO</div>
|
|
228
|
+
</div>
|
|
229
|
+
<div class="glass-stat">
|
|
230
|
+
<div class="group-label">MEMORY USAGE</div>
|
|
231
|
+
<div class="stat-num" id="stat-mem">...</div>
|
|
232
|
+
</div>
|
|
190
233
|
</div>
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
<div
|
|
194
|
-
|
|
234
|
+
|
|
235
|
+
<div id="main-view">
|
|
236
|
+
<div style="text-align: center; padding: 100px; opacity: 0.2">
|
|
237
|
+
<h1 style="font-size: 3rem; margin: 0;">FORTRESS ONLINE</h1>
|
|
238
|
+
<p>Select a data node from the command center.</p>
|
|
239
|
+
</div>
|
|
195
240
|
</div>
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
<
|
|
200
|
-
<
|
|
241
|
+
|
|
242
|
+
<div id="data-view" style="display: none;" class="data-card">
|
|
243
|
+
<h2 id="view-title">Namespace View</h2>
|
|
244
|
+
<div id="col-list" style="display: flex; gap: 10px; margin-bottom: 30px; flex-wrap: wrap;"></div>
|
|
245
|
+
<div id="table-area"></div>
|
|
201
246
|
</div>
|
|
202
|
-
</
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
<textarea id="query-input" class="editor" placeholder="db.collection('name').find({})"></textarea>
|
|
206
|
-
<button class="btn btn-primary" onclick="runQuery()">Run Code</button>
|
|
207
|
-
<pre id="query-result" class="result">Result will appear here...</pre>
|
|
208
|
-
</div>
|
|
209
|
-
</main>
|
|
247
|
+
</main>
|
|
248
|
+
</div>
|
|
249
|
+
|
|
210
250
|
<script>
|
|
211
|
-
let
|
|
251
|
+
let secret = localStorage.getItem('dc_hyper_key');
|
|
252
|
+
if (secret) init();
|
|
253
|
+
|
|
212
254
|
async function api(p, m='GET', b=null) {
|
|
213
|
-
const r = await fetch(p, {
|
|
255
|
+
const r = await fetch(p, {
|
|
256
|
+
method: m,
|
|
257
|
+
headers: { 'Content-Type': 'application/json', 'x-dashboard-secret': secret },
|
|
258
|
+
body: b ? JSON.stringify(b) : null
|
|
259
|
+
});
|
|
260
|
+
if (r.status === 401) logout();
|
|
214
261
|
return await r.json();
|
|
215
262
|
}
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
263
|
+
|
|
264
|
+
async function doLogin() {
|
|
265
|
+
const v = document.getElementById('pass').value;
|
|
266
|
+
const res = await fetch('/api/login', {
|
|
267
|
+
method: 'POST',
|
|
268
|
+
headers: { 'Content-Type': 'application/json' },
|
|
269
|
+
body: JSON.stringify({ secret: v })
|
|
270
|
+
});
|
|
271
|
+
const d = await res.json();
|
|
272
|
+
if (d.success) {
|
|
273
|
+
secret = v;
|
|
274
|
+
localStorage.setItem('dc_hyper_key', v);
|
|
275
|
+
init(d.role);
|
|
276
|
+
} else {
|
|
277
|
+
document.getElementById('err').style.display = 'block';
|
|
221
278
|
}
|
|
222
279
|
}
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
document.getElementById('
|
|
280
|
+
|
|
281
|
+
function logout() {
|
|
282
|
+
localStorage.removeItem('dc_hyper_key');
|
|
283
|
+
location.reload();
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
async function init(role) {
|
|
287
|
+
document.getElementById('login-gate').style.display = 'none';
|
|
288
|
+
document.getElementById('main-dashboard').style.display = 'grid';
|
|
289
|
+
document.getElementById('node-info').innerText = location.hostname;
|
|
290
|
+
const stats = await api('/api/stats');
|
|
291
|
+
document.getElementById('stat-mem').innerText = stats.memory;
|
|
292
|
+
if (stats.isSuper) document.getElementById('role-badge').innerText = 'SUPER ADMIN';
|
|
293
|
+
|
|
294
|
+
const list = document.getElementById('db-list');
|
|
295
|
+
list.innerHTML = stats.databases.map(db => \`
|
|
296
|
+
<div class="db-btn" onclick="selectDB('\${db.name}', '\${db.ns}')">
|
|
297
|
+
<div style="font-size: 1.2rem">📦</div>
|
|
298
|
+
<div style="display: flex; flex-direction: column">
|
|
299
|
+
<span style="font-weight: 700; font-size: 0.9rem">\${db.name}</span>
|
|
300
|
+
<span style="font-size: 0.6rem; opacity: 0.4">\${db.ns.substring(0, 12)}...</span>
|
|
301
|
+
</div>
|
|
302
|
+
</div>
|
|
303
|
+
\`).join('');
|
|
236
304
|
}
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
document.getElementById('
|
|
305
|
+
|
|
306
|
+
async function selectDB(name, ns) {
|
|
307
|
+
document.getElementById('main-view').style.display = 'none';
|
|
308
|
+
document.getElementById('data-view').style.display = 'block';
|
|
309
|
+
document.getElementById('view-title').innerText = ns + ' / ' + name;
|
|
310
|
+
|
|
311
|
+
const data = await api(\`/api/db/\${name}/ns/\${ns}\`);
|
|
312
|
+
const list = document.getElementById('col-list');
|
|
313
|
+
list.innerHTML = data.collections.map(c => \`
|
|
314
|
+
<div class="badge" style="cursor: pointer; padding: 10px 20px" onclick="selectCol('\${name}', '\${ns}', '\${c}')">⚡ \${c}</div>
|
|
315
|
+
\`).join('');
|
|
316
|
+
document.getElementById('table-area').innerHTML = '<p style="opacity: 0.3">Select a collection...</p>';
|
|
241
317
|
}
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
318
|
+
|
|
319
|
+
async function selectCol(db, ns, col) {
|
|
320
|
+
const data = await api(\`/api/db/\${db}/ns/\${ns}/col/\${col}\`);
|
|
321
|
+
let html = '<table><thead><tr><th>DOCUMENT PAYLOAD</th></tr></thead><tbody>';
|
|
245
322
|
data.docs.forEach(doc => {
|
|
246
|
-
html +=
|
|
323
|
+
html += \`<tr><td><pre style="margin:0">\${JSON.stringify(doc, null, 2)}</pre></td></tr>\`;
|
|
247
324
|
});
|
|
248
325
|
html += '</tbody></table>';
|
|
249
|
-
document.getElementById('
|
|
326
|
+
document.getElementById('table-area').innerHTML = html;
|
|
250
327
|
}
|
|
251
328
|
</script>
|
|
252
329
|
</body>
|
|
@@ -254,4 +331,5 @@ class Dashboard {
|
|
|
254
331
|
`;
|
|
255
332
|
}
|
|
256
333
|
}
|
|
334
|
+
|
|
257
335
|
module.exports = Dashboard;
|
package/package.json
CHANGED