tinny-backend 1.0.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.
Files changed (86) hide show
  1. package/README.md +207 -0
  2. package/dist/admin/auth.d.ts +2 -0
  3. package/dist/admin/auth.d.ts.map +1 -0
  4. package/dist/admin/auth.js +2 -0
  5. package/dist/admin/auth.js.map +1 -0
  6. package/dist/admin/monitor.d.ts +2 -0
  7. package/dist/admin/monitor.d.ts.map +1 -0
  8. package/dist/admin/monitor.js +2 -0
  9. package/dist/admin/monitor.js.map +1 -0
  10. package/dist/admin/routes.d.ts +2 -0
  11. package/dist/admin/routes.d.ts.map +1 -0
  12. package/dist/admin/routes.js +2 -0
  13. package/dist/admin/routes.js.map +1 -0
  14. package/dist/app/index.d.ts +2 -0
  15. package/dist/app/index.d.ts.map +1 -0
  16. package/dist/app/index.js +19 -0
  17. package/dist/app/index.js.map +1 -0
  18. package/dist/core/Request.d.ts +4 -0
  19. package/dist/core/Request.d.ts.map +1 -0
  20. package/dist/core/Request.js +24 -0
  21. package/dist/core/Request.js.map +1 -0
  22. package/dist/core/Response.d.ts +4 -0
  23. package/dist/core/Response.d.ts.map +1 -0
  24. package/dist/core/Response.js +78 -0
  25. package/dist/core/Response.js.map +1 -0
  26. package/dist/core/Server.d.ts +30 -0
  27. package/dist/core/Server.d.ts.map +1 -0
  28. package/dist/core/Server.js +533 -0
  29. package/dist/core/Server.js.map +1 -0
  30. package/dist/core/types.d.ts +89 -0
  31. package/dist/core/types.d.ts.map +1 -0
  32. package/dist/core/types.js +3 -0
  33. package/dist/core/types.js.map +1 -0
  34. package/dist/examples/example.d.ts +2 -0
  35. package/dist/examples/example.d.ts.map +1 -0
  36. package/dist/examples/example.js +11 -0
  37. package/dist/examples/example.js.map +1 -0
  38. package/dist/index.d.ts +2 -0
  39. package/dist/index.d.ts.map +1 -0
  40. package/dist/index.js +11 -0
  41. package/dist/index.js.map +1 -0
  42. package/dist/server.d.ts +127 -0
  43. package/dist/server.d.ts.map +1 -0
  44. package/dist/server.js +625 -0
  45. package/dist/server.js.map +1 -0
  46. package/dist/src/admin/auth.d.ts +13 -0
  47. package/dist/src/admin/auth.d.ts.map +1 -0
  48. package/dist/src/admin/auth.js +60 -0
  49. package/dist/src/admin/auth.js.map +1 -0
  50. package/dist/src/admin/monitor.d.ts +3 -0
  51. package/dist/src/admin/monitor.d.ts.map +1 -0
  52. package/dist/src/admin/monitor.js +83 -0
  53. package/dist/src/admin/monitor.js.map +1 -0
  54. package/dist/src/admin/routes.d.ts +14 -0
  55. package/dist/src/admin/routes.d.ts.map +1 -0
  56. package/dist/src/admin/routes.js +150 -0
  57. package/dist/src/admin/routes.js.map +1 -0
  58. package/dist/src/core/Request.d.ts +4 -0
  59. package/dist/src/core/Request.d.ts.map +1 -0
  60. package/dist/src/core/Request.js +24 -0
  61. package/dist/src/core/Request.js.map +1 -0
  62. package/dist/src/core/Response.d.ts +4 -0
  63. package/dist/src/core/Response.d.ts.map +1 -0
  64. package/dist/src/core/Response.js +78 -0
  65. package/dist/src/core/Response.js.map +1 -0
  66. package/dist/src/core/Server.d.ts +34 -0
  67. package/dist/src/core/Server.d.ts.map +1 -0
  68. package/dist/src/core/Server.js +290 -0
  69. package/dist/src/core/Server.js.map +1 -0
  70. package/dist/src/core/types.d.ts +89 -0
  71. package/dist/src/core/types.d.ts.map +1 -0
  72. package/dist/src/core/types.js +3 -0
  73. package/dist/src/core/types.js.map +1 -0
  74. package/dist/src/index.d.ts +16 -0
  75. package/dist/src/index.d.ts.map +1 -0
  76. package/dist/src/index.js +17 -0
  77. package/dist/src/index.js.map +1 -0
  78. package/dist/src/utils/constants.d.ts +12 -0
  79. package/dist/src/utils/constants.d.ts.map +1 -0
  80. package/dist/src/utils/constants.js +12 -0
  81. package/dist/src/utils/constants.js.map +1 -0
  82. package/dist/utils/constants.d.ts +12 -0
  83. package/dist/utils/constants.d.ts.map +1 -0
  84. package/dist/utils/constants.js +12 -0
  85. package/dist/utils/constants.js.map +1 -0
  86. package/package.json +89 -0
package/dist/server.js ADDED
@@ -0,0 +1,625 @@
1
+ import { randomBytes } from "crypto";
2
+ import fs from "fs";
3
+ import path, { join } from "path";
4
+ import * as http from "http";
5
+ import * as net from "net";
6
+ import { fileURLToPath } from 'url';
7
+ import { readdir } from 'node:fs/promises';
8
+ import * as os from "node:os";
9
+ import * as JWT from "jsonwebtoken";
10
+ import * as DOTENV from "dotenv";
11
+ export const colors = {
12
+ reset: '\x1b[0m',
13
+ black: '\x1b[30m',
14
+ red: '\x1b[31m',
15
+ green: '\x1b[32m',
16
+ yellow: '\x1b[33m',
17
+ blue: '\x1b[34m',
18
+ magenta: '\x1b[35m',
19
+ cyan: '\x1b[36m',
20
+ white: '\x1b[37m',
21
+ };
22
+ export default class Server {
23
+ PORT;
24
+ hostname;
25
+ methodHandler;
26
+ decorators;
27
+ server;
28
+ sessions;
29
+ uptime;
30
+ reqCount;
31
+ logs;
32
+ ServerName;
33
+ defaultHandler;
34
+ generateKey() {
35
+ return randomBytes(16).toString('hex');
36
+ }
37
+ generateSession(ip) {
38
+ const session = {
39
+ ip,
40
+ lastReq: Date.now(),
41
+ reqCount: 0,
42
+ key: this.generateKey(),
43
+ banInterval: 60000
44
+ };
45
+ this.sessions.push(session);
46
+ return session;
47
+ }
48
+ getSession(ip) {
49
+ return this.sessions.filter((ses) => ses.ip === ip)[0];
50
+ }
51
+ getHost() {
52
+ return this.hostname;
53
+ }
54
+ getPort() {
55
+ return this.PORT;
56
+ }
57
+ add(opt) {
58
+ const newPath = opt.path.replace(/(?<=.)\/+$/, "");
59
+ const paramNames = [];
60
+ const parsedPath = newPath.replace(/:([^\/]+)/g, (_, paramName) => {
61
+ paramNames.push(paramName);
62
+ return "([^/]+)";
63
+ });
64
+ this.methodHandler.push({
65
+ method: opt.method,
66
+ handler: opt.handler,
67
+ regex: new RegExp(`^${parsedPath}$`),
68
+ path: newPath,
69
+ paramNames,
70
+ middelWares: opt.middelWares,
71
+ next: opt.next
72
+ });
73
+ this.log(`Setting a handler for ${opt.method} ${this.hostname}:${this.PORT}${opt.path.replace(/(?<=.)\/+$/, "")} [${opt.path.replace(/(?<=.)\/+$/, "")}]`);
74
+ }
75
+ decorate(name, data) {
76
+ this.decorators.push({
77
+ name,
78
+ data
79
+ });
80
+ }
81
+ useDecorator(name) {
82
+ if (this.decorators.length == 0)
83
+ return null;
84
+ return this.decorators.filter((dec) => dec.name === name)[0];
85
+ }
86
+ async readDir(DIR, prefix) {
87
+ try {
88
+ const entries = await readdir(DIR, { recursive: true, withFileTypes: true });
89
+ const result = { files: [] };
90
+ for (const entry of entries)
91
+ if (!entry.isDirectory())
92
+ result.files.push({ FileName: join(entry.parentPath, entry.name), prefix: join(prefix, entry.name) });
93
+ return result;
94
+ }
95
+ catch (error) {
96
+ this.log(`Error reading directory: ${DIR} error ${error}`, "error");
97
+ }
98
+ return null;
99
+ }
100
+ async servDir(DIR, pref, middelWares) {
101
+ let files = null;
102
+ try {
103
+ files = await this.readDir(DIR, pref);
104
+ }
105
+ catch {
106
+ this.log("an error happens when try to server a dir", "error");
107
+ }
108
+ if (!files) {
109
+ this.log("Failed to serve this dir", "error");
110
+ return;
111
+ }
112
+ let index = false;
113
+ for (let i = 0; i < files.files.length; i++) {
114
+ const filename = files.files[i]?.FileName.replace(/^\/+/, "/");
115
+ const prefix = files.files[i]?.prefix.replace(/^\/+/, "/");
116
+ if (!filename || !prefix)
117
+ break;
118
+ if ((path.basename(filename).startsWith("index") && path.dirname(filename) == DIR.replace(/\.\//g, '')) && !index) {
119
+ this.log(`setting ${filename} as default page for ${DIR}`);
120
+ this.add({
121
+ method: "GET",
122
+ path: `/${path.dirname(prefix)}`,
123
+ handler: (req, res) => {
124
+ this.SendFile(res, filename, 200);
125
+ return;
126
+ },
127
+ middelWares
128
+ });
129
+ index = true;
130
+ }
131
+ this.add({
132
+ method: "GET",
133
+ path: `/${prefix}`,
134
+ handler: async (req, res) => {
135
+ await this.SendFile(res, filename, 200);
136
+ return;
137
+ },
138
+ middelWares
139
+ });
140
+ }
141
+ if (!index) {
142
+ this.add({
143
+ method: "GET",
144
+ path: `/${pref}`,
145
+ handler: async (req, res) => {
146
+ res.send(200, {
147
+ ...files.files.map((file) => {
148
+ return {
149
+ "name": path.basename(file.FileName),
150
+ "url": `http://${this.hostname}:${this.PORT}/${file.prefix}`
151
+ };
152
+ })
153
+ });
154
+ },
155
+ middelWares
156
+ });
157
+ }
158
+ }
159
+ async SendFile(res, FILE, status) {
160
+ const __filename = fileURLToPath(import.meta.url);
161
+ const projectRoot = path.resolve(__filename, '..', '..');
162
+ const __dirname = projectRoot;
163
+ const filePath = path.join(__dirname, FILE);
164
+ const extName = String(path.extname(filePath)).toLowerCase();
165
+ const mimeTypes = {
166
+ '.txt': 'text/plain',
167
+ '.html': 'text/html',
168
+ '.css': 'text/css',
169
+ '.js': 'text/javascript',
170
+ '.png': 'image/png',
171
+ '.jpg': 'image/jpeg',
172
+ '.json': 'application/json',
173
+ '.mp4': 'video/mp4',
174
+ '.webm': 'video/webm',
175
+ '.ogg': 'video/ogg',
176
+ '.avi': 'video/x-msvideo'
177
+ };
178
+ const contentType = mimeTypes[extName] || 'application/octet-stream';
179
+ try {
180
+ await fs.promises.access(filePath, fs.constants.F_OK);
181
+ res.server.log(`sending file ${FILE} to ${res.ip}`, (status == 404 || status == 401) ? "error" : "message");
182
+ return res.sendFile(status, contentType, { PATH: filePath });
183
+ }
184
+ catch {
185
+ const errorPagePath = path.join(__dirname, 'public', 'stauts/404.html');
186
+ res.server.log(`cannot find ${FILE} to send to ${res.ip}`, "error");
187
+ return res.sendFile(404, 'text/html', { PATH: errorPagePath });
188
+ }
189
+ }
190
+ log(message, type) {
191
+ const DateObj = new Date();
192
+ const date = `${DateObj.toDateString() + " " + DateObj.toLocaleTimeString()}`;
193
+ const newType = type ?? message;
194
+ console.log(`[${colors.green}${date}${colors.reset}]`, `[${colors.yellow}${this.ServerName}${colors.reset}] =>`, `${(type == "error" ? colors.red : colors.white)}${message}${colors.reset}`);
195
+ if (this.logs.length > 100)
196
+ this.logs.shift();
197
+ this.logs.push({
198
+ message,
199
+ type: type ?? "message",
200
+ date
201
+ });
202
+ }
203
+ constructor(args) {
204
+ this.sessions = [];
205
+ this.decorators = [];
206
+ this.methodHandler = [];
207
+ this.hostname = args.hostname ?? "localhost";
208
+ this.PORT = args.port ?? 3000;
209
+ this.uptime = Date.now();
210
+ this.reqCount = 0;
211
+ this.logs = [];
212
+ this.ServerName = args.ServerName ?? "Server";
213
+ this.defaultHandler = args.DefaultHandler ?? (async (req, res) => {
214
+ this.log(`Sending defualt of this route ${req.ReqUrl?.pathname}`);
215
+ await this.SendFile(res, "public/status/404.html", 404);
216
+ });
217
+ this.server = http.createServer((req, res) => {
218
+ let body = "";
219
+ let cookies = [];
220
+ this.reqCount++;
221
+ try {
222
+ const NewUrl = "http://" + this.hostname + ":" + this.PORT + (req.url ?? "");
223
+ req.ReqUrl = URL.parse(NewUrl);
224
+ req.ReqUrl.pathname = req.ReqUrl?.pathname.replace(/(?<=.)\/+$/, "");
225
+ req.Query = new URLSearchParams(req.ReqUrl.searchParams);
226
+ req.queries = Object.fromEntries(req.Query);
227
+ }
228
+ catch {
229
+ this.log(`Cannot parse ${req.url}`, "error");
230
+ req.ReqUrl.pathname = req.url;
231
+ req.queries = {};
232
+ }
233
+ res.isClosed = false;
234
+ req.ip = req.socket.remoteAddress?.split(':').pop() ?? "DEVICE IP";
235
+ res.ip = req.ip;
236
+ this.log(`${req.ip} requested METHOD ${req.method}, PATH ${req.ReqUrl?.pathname}`);
237
+ res.getCookie = (name) => {
238
+ if (cookies.length !== 0)
239
+ for (let i = 0; i < cookies.length; i++)
240
+ if (cookies[i]?.key === name)
241
+ return cookies[i] ?? null;
242
+ return null;
243
+ };
244
+ res.setCookie = (name, value) => {
245
+ for (let i = 0; i < cookies.length; i++)
246
+ if (name === cookies[i]?.key)
247
+ cookies[i].value = value;
248
+ };
249
+ res.addCookie = (name, value) => {
250
+ if (name === "" || value === "")
251
+ return;
252
+ if (res.getCookie(name) != null)
253
+ return res.setCookie(name, value);
254
+ cookies.push({ key: name, value });
255
+ };
256
+ // ; Path=/; SameSite=Lax
257
+ res.getAllCookies = () => {
258
+ const ArrayBuffer = [];
259
+ for (let i = 0; i < cookies.length; i++)
260
+ ArrayBuffer.push(`${cookies[i]?.key}=${cookies[i]?.value};`);
261
+ return ArrayBuffer;
262
+ };
263
+ const setCookies = (cookies) => {
264
+ if (!cookies || cookies === "")
265
+ return;
266
+ const pairs = cookies.split("; ");
267
+ if (!pairs)
268
+ return;
269
+ for (let i = 0; i < pairs.length; i++)
270
+ res.addCookie(pairs[i]?.split("=")[0] ?? "", pairs[i]?.split("=")[1] ?? "");
271
+ };
272
+ setCookies(req.headers["cookie"]);
273
+ req.on("data", (chunk) => {
274
+ body += chunk.toString();
275
+ });
276
+ req.on("end", async () => {
277
+ req.server = this;
278
+ res.server = this;
279
+ res.send = (status, data, headers) => {
280
+ if (!res.isClosed) {
281
+ res.writeHead(status, { 'Content-Type': 'application/json', ...headers });
282
+ res.end(data ? JSON.stringify(data) : "");
283
+ res.isClosed = true;
284
+ }
285
+ else {
286
+ this.log(`Multipple replays on ${req.url}`, "error");
287
+ }
288
+ };
289
+ res.sendFile = async (status, ContentType, data, headers) => {
290
+ if (!res.isClosed) {
291
+ try {
292
+ const fileData = await fs.promises.readFile(data.PATH);
293
+ if (res.isClosed)
294
+ return;
295
+ res.writeHead(status, { 'Content-Type': ContentType, ...headers });
296
+ res.end(fileData);
297
+ res.isClosed = true;
298
+ }
299
+ catch {
300
+ if (res.isClosed)
301
+ return;
302
+ res.writeHead(500, { 'Content-Type': 'text/plain' });
303
+ res.end('500 - Internal Error or File Not Found');
304
+ res.isClosed = true;
305
+ return;
306
+ }
307
+ }
308
+ else
309
+ this.log(`Multipple replays on ${req.url}`, "error");
310
+ };
311
+ req.body = body ? JSON.parse(body) : null;
312
+ let handlersCount = 0;
313
+ for (let i = 0; i < this.methodHandler.length; i++) {
314
+ const match = req.ReqUrl?.pathname.match(this.methodHandler[i]?.regex ?? "");
315
+ if (this.methodHandler[i]?.method == req.method
316
+ && match) {
317
+ req.params = {};
318
+ this.methodHandler[i]?.paramNames.forEach((name, index) => {
319
+ req.params[name] = match[index + 1] || "";
320
+ });
321
+ if (!res.isClosed) {
322
+ if (this.methodHandler[i]?.middelWares && typeof this.methodHandler[i]?.middelWares != "undefined") {
323
+ for (const middelware of this.methodHandler[i]?.middelWares ?? []) {
324
+ await middelware(req, res);
325
+ if (res.isClosed)
326
+ break;
327
+ }
328
+ }
329
+ if (!res.isClosed)
330
+ await this.methodHandler[i]?.handler(req, res);
331
+ const nextFn = this.methodHandler[i]?.next;
332
+ if (typeof nextFn === "function")
333
+ await nextFn(req, res);
334
+ handlersCount++;
335
+ }
336
+ }
337
+ }
338
+ if (!handlersCount) {
339
+ this.defaultHandler(req, res);
340
+ }
341
+ });
342
+ });
343
+ this.server.on("connection", (socket) => {
344
+ const IP = socket.remoteAddress?.replace(/^.*:/, '') ?? "INVALID IP";
345
+ const RPORT = socket.remotePort ?? this.PORT;
346
+ this.log(`New connention recieved ip ${IP} on port ${RPORT}`);
347
+ socket.on("close", (hadError) => {
348
+ this.log(`Client has drop the connection ${IP}`, hadError ? "error" : "message");
349
+ });
350
+ socket.on("error", (err) => {
351
+ this.log(`Connection failed with ${IP}, cause : ${err.message}`, "error");
352
+ });
353
+ });
354
+ this.server.on("close", () => {
355
+ this.log(`Server has successfully stopped accepting connections`);
356
+ });
357
+ this.server.on("error", (err) => {
358
+ this.log(err.message, "error");
359
+ });
360
+ }
361
+ monitor(server) {
362
+ DOTENV.config();
363
+ let username = process.env.ADMIN_USERNAME || "";
364
+ let password = process.env.ADMIN_PASSWORD || "";
365
+ const key = process.env.ADMIN_KEY || "";
366
+ const sessions = [];
367
+ if (key === "" || username === "" || password === "") {
368
+ server.log(`cannot set ${key === "" ? "ADMIN_KEY" : username === "" ? "ADMIN_USERnAME" : password === "" ? "ADMIN_PASSWORD" : ""} as empty in .env, read tinny-backend doc`, "error");
369
+ return;
370
+ }
371
+ const isAuthed = (res) => {
372
+ const TOKEN = res.getCookie("token");
373
+ if (!TOKEN)
374
+ return false;
375
+ try {
376
+ const decoded = JWT.default.verify(TOKEN.value, key);
377
+ if (typeof decoded.id === undefined)
378
+ throw new Error("None valid token");
379
+ for (let i = 0; i < sessions.length; i++) {
380
+ if (sessions[i]?.id === decoded.id) {
381
+ if (!sessions[i]?.isValid)
382
+ throw new Error("None valid token");
383
+ sessions[i].request += 1;
384
+ res.currentID = decoded.id;
385
+ return true;
386
+ }
387
+ }
388
+ throw new Error("None valid UUID");
389
+ }
390
+ catch {
391
+ server.log("a user try to login to admin panel using none valid token", "error");
392
+ return false;
393
+ }
394
+ return true;
395
+ };
396
+ const Login = async (req, res) => {
397
+ let token;
398
+ try {
399
+ if (!req.body.username || req.body.username !== username || !req.body.password || req.body.password !== password)
400
+ throw new Error("User has entred a none valid cridentials");
401
+ const SESSION_ID = crypto.randomUUID();
402
+ token = JWT.default.sign({ id: SESSION_ID }, key, { expiresIn: "1h" });
403
+ res.addCookie("token", token);
404
+ sessions.push({
405
+ id: SESSION_ID,
406
+ isValid: true,
407
+ creation: Date.now(),
408
+ userAgent: req.headers["user-agent"] ?? "DEVICE",
409
+ ip: req.ip ?? "UNKNOWN",
410
+ request: 0
411
+ });
412
+ res.send(200, "./public/admin/index.html", { "set-cookie": `token=${token}; Path=/; SameSite=Lax; HttpOnly` });
413
+ }
414
+ catch (err) {
415
+ server.log(err.message, "error");
416
+ server.log("A user entred a none valid cridentionl", "error");
417
+ res.send(403, { "Error": "Access Denied" });
418
+ }
419
+ };
420
+ const Logout = async (req, res) => {
421
+ let payloud;
422
+ const TOKEN = res.getCookie("token");
423
+ if (!TOKEN)
424
+ return server.SendFile(res, "public/login/index.html", 401);
425
+ try {
426
+ payloud = JWT.default.verify(TOKEN.value, key);
427
+ if (typeof payloud.id === "undefined")
428
+ throw new Error("an Error is happens");
429
+ for (let i = 0; i < sessions.length; i++) {
430
+ if (sessions[i]?.id === payloud.id) {
431
+ if (!sessions[i]?.isValid)
432
+ throw new Error("None valid token");
433
+ sessions[i].request += 1;
434
+ sessions[i].isValid = false;
435
+ break;
436
+ }
437
+ }
438
+ return await server.SendFile(res, "public/login/index.html", 200);
439
+ }
440
+ catch (err) {
441
+ server.log(err.message, "error");
442
+ res.send(403, { "Error": "Access Denied" });
443
+ }
444
+ };
445
+ const Auth = async (req, res) => {
446
+ if (!isAuthed(res)) {
447
+ if (req.ReqUrl?.pathname == "/")
448
+ return await server.SendFile(res, "public/login/index.html", 401);
449
+ return await server.SendFile(res, "public/status/401.html", 401);
450
+ }
451
+ if (req.ReqUrl?.pathname == "/")
452
+ return await server.SendFile(res, "public/admin/index.html", 200);
453
+ };
454
+ server.add({
455
+ method: "POST",
456
+ handler: Login,
457
+ path: "/api/login"
458
+ });
459
+ server.add({
460
+ method: "GET",
461
+ middelWares: [Auth],
462
+ handler: async (req, res) => {
463
+ return await server.SendFile(res, "public/login/index.html", 200);
464
+ },
465
+ path: "/"
466
+ });
467
+ server.add({
468
+ method: "GET",
469
+ middelWares: [Auth],
470
+ handler: Logout,
471
+ path: "/api/logout"
472
+ });
473
+ server.add({
474
+ method: "GET",
475
+ middelWares: [Auth],
476
+ path: "/admin/status",
477
+ handler: async (req, res) => {
478
+ const now = Date.now();
479
+ res.send(200, {
480
+ "Machin uptime": `${Math.floor(os.uptime() / 86400)}d ${Math.floor((os.uptime() % 86400) / 3600)}h ${Math.floor((os.uptime() % 3600) / 60)}m`,
481
+ "Server uptime": `${Math.floor((now - this.uptime) / (1000 * 60 * 60 * 24)) % 60}D ${Math.floor((now - this.uptime) / (1000 * 60 * 60)) % 60}H ${Math.floor((now - this.uptime) / (1000 * 60)) % 60}M ${Math.floor((now - this.uptime) / 1000) % 60}S`,
482
+ "Arch": os.arch(),
483
+ "Platform": os.platform(),
484
+ "Memory": `${(os.totalmem() / (1024 ** 3)).toFixed(2)} GB`,
485
+ "Used Memory": `${((os.totalmem() - os.freemem()) / (1024 ** 3)).toFixed(2)} GB`,
486
+ "Free Memory": `${(os.freemem() / (1024 ** 3)).toFixed(2)} GB`,
487
+ "Cpu": os.cpus()[0],
488
+ "Connected clients": this.sessions.length,
489
+ "Total requests": this.reqCount,
490
+ "Currnet-session": res.currentID,
491
+ "sessions": sessions,
492
+ "routes": this.methodHandler,
493
+ "logs": this.logs
494
+ });
495
+ }
496
+ });
497
+ server.add({
498
+ method: "GET",
499
+ middelWares: [Auth],
500
+ path: "/messages",
501
+ handler: async (req, res) => {
502
+ res.send(200, {
503
+ "logs": this.logs
504
+ });
505
+ }
506
+ });
507
+ server.add({
508
+ method: "GET",
509
+ path: "/passwd",
510
+ handler: async (req, res) => {
511
+ return await this.SendFile(res, "./public/login/new-password.html", 200);
512
+ }
513
+ });
514
+ server.add({
515
+ method: "GET",
516
+ path: "/api/passwd/:username",
517
+ handler: async (req, res) => {
518
+ let userName = req.params.username;
519
+ if (typeof username === undefined)
520
+ return res.send(404, { "user": userName, status: "not found" });
521
+ if (userName === username)
522
+ return res.send(200, { "user": userName, status: "found" });
523
+ return res.send(404, { "user": userName, status: "not found" });
524
+ }
525
+ });
526
+ server.add({
527
+ method: "POST",
528
+ path: "/api/passwd/change",
529
+ handler: async (req, res) => {
530
+ try {
531
+ const { userName, oldpwd, newpwd } = req.body;
532
+ console.log(req.body);
533
+ if (!userName || !oldpwd || !newpwd)
534
+ return res.send(400, { "user": userName || 'unknown', status: 'missing fields', message: 'Username, old password, and new password are required' });
535
+ if (userName !== username)
536
+ return res.send(404, { "user": userName, status: 'this username not found', message: `${userName} not found` });
537
+ if (oldpwd !== password)
538
+ return res.send(401, { "user": userName, status: 'password not corect', message: `Password not corect for ${userName}` });
539
+ if (newpwd.length < 8)
540
+ return res.send(400, { "user": userName, status: 'password must be 8 char +' });
541
+ if (oldpwd === newpwd || newpwd === password)
542
+ return res.send(400, { "user": userName, status: 'new password must be different', message: 'New password must be different from current password' });
543
+ password = newpwd;
544
+ return res.send(200, { "user": userName, status: 'password changed', message: 'Password updated successfully' });
545
+ }
546
+ catch (error) {
547
+ server.log(`Password change error: ${error}`, "error");
548
+ return res.send(500, { "user": req.body?.username || 'unknown', status: 'error', message: 'Internal server error' });
549
+ }
550
+ }
551
+ });
552
+ server.add({
553
+ method: "POST",
554
+ middelWares: [Auth],
555
+ path: "/api/logout/:SessionId",
556
+ handler: async (req, res) => {
557
+ let SessionID = req.params.SessionId;
558
+ if (typeof SessionID === undefined)
559
+ return res.send(404, { status: "not found" });
560
+ for (let i = 0; i < sessions.length; i++) {
561
+ if (SessionID === sessions[i]?.id) {
562
+ sessions[i].isValid = false;
563
+ return res.send(200, { status: `session ${SessionID} logouted` });
564
+ }
565
+ }
566
+ return res.send(404, { status: "not found" });
567
+ }
568
+ });
569
+ server.add({
570
+ method: "POST",
571
+ middelWares: [Auth],
572
+ path: "/passwd/change/auth",
573
+ handler: async (req, res) => {
574
+ try {
575
+ const { oldpwd, newpwd } = req.body;
576
+ if (!oldpwd || !newpwd)
577
+ return res.send(401, { status: "all field are required !" });
578
+ if (oldpwd !== password)
579
+ return res.send(401, { status: "incorect password" });
580
+ if (oldpwd === newpwd)
581
+ return res.send(400, { status: "old pwd must be defrent from new one" });
582
+ password = newpwd;
583
+ return res.send(200, { status: "password changed with secc" });
584
+ }
585
+ catch {
586
+ res.send(500, { status: "internal server error" });
587
+ }
588
+ return res.send(401, { status: "?" });
589
+ }
590
+ });
591
+ server.add({
592
+ method: "POST",
593
+ middelWares: [Auth],
594
+ path: "/api/user/change",
595
+ handler: async (req, res) => {
596
+ try {
597
+ const { userName } = req.body;
598
+ if (!userName)
599
+ return res.send(401, { status: "all field are required !" });
600
+ if (userName.length <= 3 || userName.length >= 20)
601
+ return res.send(400, { status: "username must be between 4-19" });
602
+ username = userName;
603
+ return res.send(200, { status: "username changed with secc" });
604
+ }
605
+ catch {
606
+ res.send(500, { status: "internal server error" });
607
+ }
608
+ return res.send(401, { status: "?" });
609
+ }
610
+ });
611
+ server.servDir("./public/doc", "docs");
612
+ server.servDir("./public/imgs", "imgs");
613
+ server.servDir("./public/login", "/", [Auth]);
614
+ server.servDir("./public/admin", "admin", [Auth]);
615
+ server.listen(false);
616
+ }
617
+ listen(enableMon, callback) {
618
+ if (enableMon)
619
+ this.monitor(new Server({ port: this.PORT + 1, ServerName: "Monitor" }));
620
+ this.server.listen(this.PORT, callback ?? (() => {
621
+ this.log(`start listening in http://${this.hostname}:${this.PORT}`);
622
+ }));
623
+ }
624
+ }
625
+ //# sourceMappingURL=server.js.map