added node backend

This commit is contained in:
QkoSad
2025-07-14 10:39:01 +03:00
parent 98e689bfc5
commit ec40145c69
26 changed files with 1495 additions and 1167 deletions
+10
View File
@@ -0,0 +1,10 @@
import mysql from "mysql2/promise";
const connection = await mysql.createConnection({
host: "localhost",
user: "monty",
password: "some_pass",
database: "timelognode",
});
export default connection;
+21
View File
@@ -0,0 +1,21 @@
import express from "express";
import getAll from "./routers/getAll.js";
import getUser from "./routers/getUser.js";
import getTopTen from "./routers/getTopTen.js";
import reset from "./routers/reset.js";
import cors from "cors";
const router = express.Router();
const app = express();
app.use(express.json());
app.use(cors());
app.use("/api", getAll);
app.use("/api", getTopTen);
app.use("/api", getUser);
app.use("/api", reset);
app.use("/api", router);
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => console.log(`Server started on port ${PORT}`));
+953
View File
@@ -0,0 +1,953 @@
{
"name": "backend",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "backend",
"version": "1.0.0",
"license": "ISC",
"dependencies": {
"cors": "^2.8.5",
"express": "^4.21.1",
"mysql2": "^3.11.4"
},
"devDependencies": {
"@types/node": "^22.9.0",
"typescript": "^5.6.3"
}
},
"node_modules/@types/node": {
"version": "22.9.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz",
"integrity": "sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"undici-types": "~6.19.8"
}
},
"node_modules/accepts": {
"version": "1.3.8",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
"integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
"license": "MIT",
"dependencies": {
"mime-types": "~2.1.34",
"negotiator": "0.6.3"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/array-flatten": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
"license": "MIT"
},
"node_modules/aws-ssl-profiles": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/aws-ssl-profiles/-/aws-ssl-profiles-1.1.2.tgz",
"integrity": "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==",
"license": "MIT",
"engines": {
"node": ">= 6.0.0"
}
},
"node_modules/body-parser": {
"version": "1.20.3",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz",
"integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==",
"license": "MIT",
"dependencies": {
"bytes": "3.1.2",
"content-type": "~1.0.5",
"debug": "2.6.9",
"depd": "2.0.0",
"destroy": "1.2.0",
"http-errors": "2.0.0",
"iconv-lite": "0.4.24",
"on-finished": "2.4.1",
"qs": "6.13.0",
"raw-body": "2.5.2",
"type-is": "~1.6.18",
"unpipe": "1.0.0"
},
"engines": {
"node": ">= 0.8",
"npm": "1.2.8000 || >= 1.4.16"
}
},
"node_modules/bytes": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
"integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/call-bind": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz",
"integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==",
"license": "MIT",
"dependencies": {
"es-define-property": "^1.0.0",
"es-errors": "^1.3.0",
"function-bind": "^1.1.2",
"get-intrinsic": "^1.2.4",
"set-function-length": "^1.2.1"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/content-disposition": {
"version": "0.5.4",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
"integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
"license": "MIT",
"dependencies": {
"safe-buffer": "5.2.1"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/content-type": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
"integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/cookie": {
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz",
"integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/cookie-signature": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==",
"license": "MIT"
},
"node_modules/cors": {
"version": "2.8.5",
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
"integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
"license": "MIT",
"dependencies": {
"object-assign": "^4",
"vary": "^1"
},
"engines": {
"node": ">= 0.10"
}
},
"node_modules/debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"license": "MIT",
"dependencies": {
"ms": "2.0.0"
}
},
"node_modules/define-data-property": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
"integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
"license": "MIT",
"dependencies": {
"es-define-property": "^1.0.0",
"es-errors": "^1.3.0",
"gopd": "^1.0.1"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/denque": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz",
"integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==",
"license": "Apache-2.0",
"engines": {
"node": ">=0.10"
}
},
"node_modules/depd": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/destroy": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
"integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
"license": "MIT",
"engines": {
"node": ">= 0.8",
"npm": "1.2.8000 || >= 1.4.16"
}
},
"node_modules/ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
"license": "MIT"
},
"node_modules/encodeurl": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
"integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/es-define-property": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
"integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==",
"license": "MIT",
"dependencies": {
"get-intrinsic": "^1.2.4"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-errors": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
"license": "MIT"
},
"node_modules/etag": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
"integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/express": {
"version": "4.21.1",
"resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz",
"integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==",
"license": "MIT",
"dependencies": {
"accepts": "~1.3.8",
"array-flatten": "1.1.1",
"body-parser": "1.20.3",
"content-disposition": "0.5.4",
"content-type": "~1.0.4",
"cookie": "0.7.1",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "2.0.0",
"encodeurl": "~2.0.0",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"finalhandler": "1.3.1",
"fresh": "0.5.2",
"http-errors": "2.0.0",
"merge-descriptors": "1.0.3",
"methods": "~1.1.2",
"on-finished": "2.4.1",
"parseurl": "~1.3.3",
"path-to-regexp": "0.1.10",
"proxy-addr": "~2.0.7",
"qs": "6.13.0",
"range-parser": "~1.2.1",
"safe-buffer": "5.2.1",
"send": "0.19.0",
"serve-static": "1.16.2",
"setprototypeof": "1.2.0",
"statuses": "2.0.1",
"type-is": "~1.6.18",
"utils-merge": "1.0.1",
"vary": "~1.1.2"
},
"engines": {
"node": ">= 0.10.0"
}
},
"node_modules/finalhandler": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz",
"integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==",
"license": "MIT",
"dependencies": {
"debug": "2.6.9",
"encodeurl": "~2.0.0",
"escape-html": "~1.0.3",
"on-finished": "2.4.1",
"parseurl": "~1.3.3",
"statuses": "2.0.1",
"unpipe": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/forwarded": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
"integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/fresh": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
"integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/function-bind": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/generate-function": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz",
"integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==",
"license": "MIT",
"dependencies": {
"is-property": "^1.0.2"
}
},
"node_modules/get-intrinsic": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
"integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
"function-bind": "^1.1.2",
"has-proto": "^1.0.1",
"has-symbols": "^1.0.3",
"hasown": "^2.0.0"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/gopd": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
"integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
"license": "MIT",
"dependencies": {
"get-intrinsic": "^1.1.3"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-property-descriptors": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
"integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
"license": "MIT",
"dependencies": {
"es-define-property": "^1.0.0"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-proto": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz",
"integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-symbols": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/hasown": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
"license": "MIT",
"dependencies": {
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/http-errors": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
"integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
"license": "MIT",
"dependencies": {
"depd": "2.0.0",
"inherits": "2.0.4",
"setprototypeof": "1.2.0",
"statuses": "2.0.1",
"toidentifier": "1.0.1"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
"license": "MIT",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"license": "ISC"
},
"node_modules/ipaddr.js": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
"license": "MIT",
"engines": {
"node": ">= 0.10"
}
},
"node_modules/is-property": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz",
"integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==",
"license": "MIT"
},
"node_modules/long": {
"version": "5.2.3",
"resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz",
"integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==",
"license": "Apache-2.0"
},
"node_modules/lru-cache": {
"version": "7.18.3",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz",
"integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==",
"license": "ISC",
"engines": {
"node": ">=12"
}
},
"node_modules/lru.min": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/lru.min/-/lru.min-1.1.1.tgz",
"integrity": "sha512-FbAj6lXil6t8z4z3j0E5mfRlPzxkySotzUHwRXjlpRh10vc6AI6WN62ehZj82VG7M20rqogJ0GLwar2Xa05a8Q==",
"license": "MIT",
"engines": {
"bun": ">=1.0.0",
"deno": ">=1.30.0",
"node": ">=8.0.0"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wellwelwel"
}
},
"node_modules/media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
"integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/merge-descriptors": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
"integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/methods": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
"integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
"license": "MIT",
"bin": {
"mime": "cli.js"
},
"engines": {
"node": ">=4"
}
},
"node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"license": "MIT",
"dependencies": {
"mime-db": "1.52.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
"license": "MIT"
},
"node_modules/mysql2": {
"version": "3.11.4",
"resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.11.4.tgz",
"integrity": "sha512-Z2o3tY4Z8EvSRDwknaC40MdZ3+m0sKbpnXrShQLdxPrAvcNli7jLrD2Zd2IzsRMw4eK9Yle500FDmlkIqp+krg==",
"license": "MIT",
"dependencies": {
"aws-ssl-profiles": "^1.1.1",
"denque": "^2.1.0",
"generate-function": "^2.3.1",
"iconv-lite": "^0.6.3",
"long": "^5.2.1",
"lru.min": "^1.0.0",
"named-placeholders": "^1.1.3",
"seq-queue": "^0.0.5",
"sqlstring": "^2.3.2"
},
"engines": {
"node": ">= 8.0"
}
},
"node_modules/mysql2/node_modules/iconv-lite": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
"license": "MIT",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/named-placeholders": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.3.tgz",
"integrity": "sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w==",
"license": "MIT",
"dependencies": {
"lru-cache": "^7.14.1"
},
"engines": {
"node": ">=12.0.0"
}
},
"node_modules/negotiator": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
"integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/object-inspect": {
"version": "1.13.3",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz",
"integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/on-finished": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
"integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
"license": "MIT",
"dependencies": {
"ee-first": "1.1.1"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/parseurl": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/path-to-regexp": {
"version": "0.1.10",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz",
"integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==",
"license": "MIT"
},
"node_modules/proxy-addr": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
"integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
"license": "MIT",
"dependencies": {
"forwarded": "0.2.0",
"ipaddr.js": "1.9.1"
},
"engines": {
"node": ">= 0.10"
}
},
"node_modules/qs": {
"version": "6.13.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
"integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
"license": "BSD-3-Clause",
"dependencies": {
"side-channel": "^1.0.6"
},
"engines": {
"node": ">=0.6"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/range-parser": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/raw-body": {
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
"integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
"license": "MIT",
"dependencies": {
"bytes": "3.1.2",
"http-errors": "2.0.0",
"iconv-lite": "0.4.24",
"unpipe": "1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "MIT"
},
"node_modules/safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"license": "MIT"
},
"node_modules/send": {
"version": "0.19.0",
"resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz",
"integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==",
"license": "MIT",
"dependencies": {
"debug": "2.6.9",
"depd": "2.0.0",
"destroy": "1.2.0",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"fresh": "0.5.2",
"http-errors": "2.0.0",
"mime": "1.6.0",
"ms": "2.1.3",
"on-finished": "2.4.1",
"range-parser": "~1.2.1",
"statuses": "2.0.1"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/send/node_modules/encodeurl": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
"integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/send/node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"license": "MIT"
},
"node_modules/seq-queue": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz",
"integrity": "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q=="
},
"node_modules/serve-static": {
"version": "1.16.2",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz",
"integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==",
"license": "MIT",
"dependencies": {
"encodeurl": "~2.0.0",
"escape-html": "~1.0.3",
"parseurl": "~1.3.3",
"send": "0.19.0"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/set-function-length": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
"integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
"license": "MIT",
"dependencies": {
"define-data-property": "^1.1.4",
"es-errors": "^1.3.0",
"function-bind": "^1.1.2",
"get-intrinsic": "^1.2.4",
"gopd": "^1.0.1",
"has-property-descriptors": "^1.0.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/setprototypeof": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
"license": "ISC"
},
"node_modules/side-channel": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz",
"integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==",
"license": "MIT",
"dependencies": {
"call-bind": "^1.0.7",
"es-errors": "^1.3.0",
"get-intrinsic": "^1.2.4",
"object-inspect": "^1.13.1"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/sqlstring": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz",
"integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/statuses": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/toidentifier": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
"integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
"license": "MIT",
"engines": {
"node": ">=0.6"
}
},
"node_modules/type-is": {
"version": "1.6.18",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
"license": "MIT",
"dependencies": {
"media-typer": "0.3.0",
"mime-types": "~2.1.24"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/typescript": {
"version": "5.6.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz",
"integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==",
"dev": true,
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=14.17"
}
},
"node_modules/undici-types": {
"version": "6.19.8",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
"integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==",
"dev": true,
"license": "MIT"
},
"node_modules/unpipe": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
"integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/utils-merge": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
"integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
"license": "MIT",
"engines": {
"node": ">= 0.4.0"
}
},
"node_modules/vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
"integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
}
}
}
+20
View File
@@ -0,0 +1,20 @@
{
"name": "backend",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"start": "node index.js"
},
"author": "",
"license": "ISC",
"description": "",
"dependencies": {
"cors": "^2.8.5",
"express": "^4.21.1",
"mysql2": "^3.11.4"
},
"devDependencies": {
"@types/node": "^22.9.0",
"typescript": "^5.6.3"
}
}
+54
View File
@@ -0,0 +1,54 @@
fnameS = [
"John",
"Gringo",
"Mark",
"Lisa",
"Maria",
"Sonya",
"Philip",
"Jose",
"Lorenzo",
"George",
"Justin",
];
lnameS = [
"Johnson",
"Lamas",
"Jackson",
"Brown",
"Mason",
"Rodriguez",
"Roberts",
"Thomas",
"Rose",
"McDonalds",
];
domain = ["hotmail.com", "gmail.com", "live.com"];
fname = [];
lname = [];
email = [];
i = 0;
while (i < 100) {
fname.push(fnameS[Math.floor(Math.random() * 10)]);
lname.push(lnameS[Math.floor(Math.random() * 9)]);
email.push(
`${fname[i]}.${lname[i]}@${domain[Math.floor(Math.random() * 2)]}`,
);
i++;
}
today = new Date();
for (let i = 0; i < email.length; i++) {
for (let j = 0; j < Math.floor(Math.random() * 20); j++) {
let m = Math.random() * (8 - 0.25) + 0.25;
m.toFixed(2);
console.log(
fname[i],
m,
`${today.getFullYear()}-${today.getMonth() + 1}-${today.getDate()}`,
);
today.setDate(today.getDate() + 1);
}
}
+60
View File
@@ -0,0 +1,60 @@
import express from "express";
import connection from "../db.js";
const router = express.Router();
router.get("/getall", async (req, res) => {
let sql =
"SELECT u.f_name,u.l_name,u.mail,p.name,t.time,t.date,t.user \
FROM Timelog t \
INNER JOIN Project p ON p.id=t.project \
INNER JOIN User u ON u.id=t.user ";
const where = "WHERE t.date BETWEEN ? AND ? ";
const order = "ORDER BY ?? ";
const offsetSql = "LIMIT 10 OFFSET ?;";
let flag = 0;
// construct the sql statement based on incoming request
if (req.query.from || req.query.to) {
sql = sql + where;
flag += 1;
}
if (req.query.sortby) {
sql = sql + order;
flag += 2;
}
sql = sql + offsetSql;
let results;
let fields;
switch (flag) {
case 0:
[results, fields] = await connection.query(sql, [
parseInt(req.query.offset),
]);
break;
case 1:
[results, fields] = await connection.query(sql, [
req.query.from,
req.query.to,
parseInt(req.query.offset),
]);
break;
case 2:
[results, fields] = await connection.query(sql, [
req.query.sortby,
parseInt(req.query.offset),
]);
break;
case 3:
[results, fields] = await connection.query(sql, [
req.query.from,
req.query.to,
req.query.sortby,
parseInt(req.query.offset),
]);
break;
}
res.json(results);
});
export default router;
+35
View File
@@ -0,0 +1,35 @@
import express from "express";
import connection from "../db.js";
const router = express.Router();
router.get("/gettopten", async (req, res) => {
let from = "2020-01-01";
let to = "2029-01-01";
let filterBy = "user";
if (req.query.from) from = req.query.from;
if (req.query.to) to = req.query.to;
if (req.query.filterBy) filterBy = req.query.filterBy;
let results, fields;
let filterBySql = filterBy === "user" ? "t.user" : "t.project";
try {
[results, fields] = await connection.query(
"SELECT t.user,t.date,t.project,u.f_name,u.l_name,p.name,SUM(t.time) as total_time \
FROM Timelog t \
INNER JOIN Project p ON p.id=t.project \
INNER JOIN User u ON u.id=t.user \
WHERE t.date BETWEEN ? AND ? \
GROUP BY ?? \
ORDER BY total_time DESC\
LIMIT 10;",
[from, to, filterBySql],
);
} catch (err) {
console.log(err);
res.status(400).json({ message: "Error" });
}
res.json(results);
});
export default router;
+42
View File
@@ -0,0 +1,42 @@
import express from "express";
import connection from "../db.js";
const router = express.Router();
router.get("/getUser", async (req, res) => {
const userId = req.query.userid;
let results, fields;
try {
[results, fields] = await connection.query(
"SELECT p.name,t.time,t.project \
FROM Timelog t \
INNER JOIN Project p ON p.id=t.project \
INNER JOIN User u ON u.id=t.user \
WHERE USER = ? ;",
[userId],
);
} catch (err) {
console.log(err);
res.status(400).json("ERROR");
}
let projects = {};
let projectIdtoName = {};
for (let i = 0; i < results.length; i++) {
if (results[i].project in projects) {
projects[results[i].project] += results[i].time;
} else {
projects[results[i].project] = results[i].time;
projectIdtoName[results[i].project] = results[i].name;
}
}
// map projectIds to project names before sending the data
let respData = {};
for (let key in projects) {
if (projects.hasOwnProperty(key)) {
respData[projectIdtoName[key]] = projects[key];
}
}
res.json(respData);
});
export default router;
+16
View File
@@ -0,0 +1,16 @@
import express from "express";
import connection from "../db.js";
const router = express.Router();
router.get("/reset", async (req, res) => {
try {
await connection.query("CALL InitDb;", []);
} catch (err) {
console.log(err);
res.status(400).json({ message: "Error" });
}
res.status(200).json({ message: "Success" });
});
export default router;
+137
View File
@@ -0,0 +1,137 @@
CREATE TABLE Timelog( user INT, project INT, date DATE, time FLOAT, id INT AUTO_INCREMENT PRIMARY KEY, FOREIGN KEY (user) REFERENCES User (id));
CREATE TABLE Project( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50));
CREATE TABLE User ( f_name VARCHAR(50) , l_name VARCHAR(50) NOT NULL, mail VARCHAR(50) NOT NULL, id INT AUTO_INCREMENT PRIMARY KEY);
####
DELIMITER //
CREATE PROCEDURE InsertIntoUser(IN f_name_in VARCHAR(50), IN l_name_in VARCHAR(50), IN mail_in VARCHAR(50))
BEGIN
INSERT INTO User(f_name,l_name,mail) VALUES(f_name_in,l_name_in,mail_in);
END //
DELIMITER ;
######
DELIMITER //
CREATE PROCEDURE InsertIntoProject()
BEGIN
INSERT INTO Project(name) VALUES("My own");
INSERT INTO Project(name) VALUES("Outcons");
INSERT INTO Project(name) VALUES("Free time");
END //
DELIMITER ;
####
DELIMITER //
CREATE PROCEDURE InsertIntoTimeLog(IN user_in INT, IN project_in INT, IN time_in FLOAT, IN date_in DATE)
BEGIN
INSERT INTO Timelog(user, project, time, date) VALUES(user_in, project_in, time_in, date_in);
END //
DELIMITER ;
####
DELIMITER //
CREATE PROCEDURE CleanTables()
BEGIN
TRUNCATE TABLE Timelog;
TRUNCATE TABLE Project;
SET foreign_key_checks = 0;
TRUNCATE TABLE User;
SET foreign_key_checks = 1;
END //
DELIMITER ;
SELECT ROUTINE_NAME
FROM INFORMATION_SCHEMA.ROUTINES
WHERE ROUTINE_TYPE = 'PROCEDURE'
AND ROUTINE_SCHEMA = 'timelog';
################
CREATE TEMPORARY TABLE temp_fname (fname VARCHAR(255));
INSERT INTO temp_fname (fname) VALUES
( "John" ),
( "Gringo" ),
( "Mark" ),
( "Lisa" ),
( "Maria" ),
( "Sonya" ),
( "Philip" ),
( "Jose" ),
( "Lorenzo" ),
( "George" ),
( "Justin" );
CREATE TEMPORARY TABLE temp_lname (lname VARCHAR(255));
INSERT INTO temp_lname (lname) VALUES
( "Johnson" ),
( "Lamas" ),
( "Jackson" ),
( "Brown" ),
( "Mason" ),
( "Rodriguez" ),
( "Roberts" ),
( "Thomas" ),
( "Rose" ),
( "McDonalds" );
CREATE TEMPORARY TABLE temp_mail (mail VARCHAR(255));
INSERT INTO temp_mail (mail) VALUES
( "hotmail.com" ),
( "gmail.com" ),
( "live.com" );
INSERT INTO User (f_name, l_name, mail)
SELECT
(SELECT fname FROM temp_fname ORDER BY RAND() LIMIT 1),
(SELECT lname FROM temp_lname ORDER BY RAND() LIMIT 1),
(SELECT mail FROM temp_mail ORDER BY RAND() LIMIT 1)
FROM
(SELECT 1 FROM information_schema.tables LIMIT 100) AS temp;
UPDATE User
SET User.mail = CONCAT(User.f_name,".", User.l_name,"@", User.mail);
DELIMITER $$
CREATE PROCEDURE fill_timelog ()
BEGIN
DECLARE j INT DEFAULT 1;
DECLARE users INT DEFAULT 1;
DECLARE logs INT;
DECLARE hours FLOAT;
DECLARE project INT;
DECLARE curDate DATE DEFAULT "2024-11-18";
WHILE users <= 100 DO
SET logs = FLOOR(1+(RAND()*20));
SET j=1;
WHILE j <= logs DO
SET hours = (RAND() * (8 - 0.25)) + 0.25;
SET curDate = DATE_ADD(curDate, INTERVAL 1 DAY);
SET project = FLOOR(1+(RAND()*3));
INSERT INTO Timelog (user, project, date,time ) VALUES (users,project,curDate,hours);
SET j=j+1;
END WHILE;
SET users=users+1;
END WHILE;
END$$
DELIMITER ;
SELECT t.user,t.date,t.project,u.f_name,u.l_name,p.name,SUM(t.time) as total_time
FROM Timelog t
INNER JOIN Project p ON p.id=t.project
INNER JOIN User u ON u.id=t.user
GROUP BY t.user
ORDER BY total_time DESC
LIMIT 10;
+147
View File
@@ -0,0 +1,147 @@
CREATE TABLE User ( f_name VARCHAR(50) , l_name VARCHAR(50) NOT NULL, mail VARCHAR(50) NOT NULL, id INT AUTO_INCREMENT PRIMARY KEY);
CREATE TABLE Timelog( user INT, project INT, date DATE, time FLOAT, id INT AUTO_INCREMENT PRIMARY KEY, FOREIGN KEY (user) REFERENCES User (id));
CREATE TABLE Project( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50));
DELIMITER $$
CREATE PROCEDURE CleanTables()
BEGIN
END $$
DELIMITER ;
DELIMITER $$
CREATE PROCEDURE InitDB()
BEGIN
DECLARE i INT DEFAULT 1;
TRUNCATE TABLE Timelog;
TRUNCATE TABLE Project;
SET foreign_key_checks = 0;
TRUNCATE TABLE User;
SET foreign_key_checks = 1;
INSERT INTO Project(name) VALUES("My own"),("Outcons"),("Free Time");
CREATE TEMPORARY TABLE temp_fname (fname VARCHAR(255));
INSERT INTO temp_fname (fname) VALUES
( "John" ),
( "Gringo" ),
( "Mark" ),
( "Lisa" ),
( "Maria" ),
( "Sonya" ),
( "Philip" ),
( "Jose" ),
( "Lorenzo" ),
( "George" ),
( "Justin" );
CREATE TEMPORARY TABLE temp_lname (lname VARCHAR(255));
INSERT INTO temp_lname (lname) VALUES
( "Johnson" ),
( "Lamas" ),
( "Jackson" ),
( "Brown" ),
( "Mason" ),
( "Rodriguez" ),
( "Roberts" ),
( "Thomas" ),
( "Rose" ),
( "McDonalds" );
CREATE TEMPORARY TABLE temp_mail (mail VARCHAR(255));
INSERT INTO temp_mail (mail) VALUES
( "hotmail.com" ),
( "gmail.com" ),
( "live.com" );
WHILE i <= 100 DO
INSERT INTO User (f_name, l_name, mail)
SELECT
(SELECT fname FROM temp_fname ORDER BY RAND() LIMIT 1),
(SELECT lname FROM temp_lname ORDER BY RAND() LIMIT 1),
(SELECT mail FROM temp_mail ORDER BY RAND() LIMIT 1);
SET i = i + 1;
END WHILE;
UPDATE User
SET User.mail = CONCAT(User.f_name,".", User.l_name,"@", User.mail);
CALL fill_timelog();
DROP TABLE temp_mail;
DROP TABLE temp_fname;
DROP TABLE temp_lname;
END$$
DELIMITER ;
DELIMITER $$
CREATE PROCEDURE fill_timelog ()
BEGIN
DECLARE j INT DEFAULT 1;
DECLARE users INT DEFAULT 1;
DECLARE logs INT;
DECLARE hours FLOAT;
DECLARE project INT;
DECLARE curDate DATE DEFAULT '2024-11-18';
DECLARE h2 INT;
WHILE users <= 100 DO
SET logs = FLOOR(1+(RAND()*20));
SET j=1;
WHILE j <= logs DO
SET project = FLOOR(1+(RAND()*3));
SET curDate = DATE_ADD('2020-01-01', INTERVAL FLOOR(RAND() * DATEDIFF('2020-02-01', '2020-01-01')) DAY);
SET hours = (RAND() * (8 - 0.25)) + 0.25;
SELECT SUM(time) INTO h2
FROM Timelog
WHERE date = curdate && user = users ;
WHILE (h2+hours) > 8 DO
SET curDate = DATE_ADD('2020-01-01', INTERVAL FLOOR(RAND() * DATEDIFF('2020-02-01', '2020-01-01')) DAY);
SELECT SUM(time)INTO h2
FROM Timelog
WHERE date = curdate && user = users ;
END WHILE;
INSERT INTO Timelog (user, project, date,time ) VALUES (users,project,curDate,hours);
SET j=j+1;
END WHILE;
SET users=users+1;
END WHILE;
END$$
DELIMITER ;
##
-- get data
SELECT t.time,t.date,p.name,u.f_name,u.l_name,u.mail FROM Timelog t INNER JOIN Project p ON p.id=t.project INNER JOIN User u ON u.id=t.user;
-- old timelog with adding each on a new day
CREATE PROCEDURE fill_timelog ()
BEGIN
DECLARE j INT DEFAULT 1;
DECLARE users INT DEFAULT 1;
DECLARE logs INT;
DECLARE hours FLOAT;
DECLARE project INT;
DECLARE curDate DATE DEFAULT "2024-11-18";
WHILE users <= 100 DO
SET logs = FLOOR(1+(RAND()*20));
SET j=1;
WHILE j <= logs DO
SET hours = (RAND() * (8 - 0.25)) + 0.25;
SET project = FLOOR(1+(RAND()*3));
SET curDate = DATE_ADD(curDate, INTERVAL 1 DAY);
INSERT INTO Timelog (user, project, date,time ) VALUES (users,project,curDate,hours);
SET j=j+1;
END WHILE;
SET users=users+1;
END WHILE;
END$$
DELIMITER ;
-1
View File
@@ -1 +0,0 @@
global using Microsoft.VisualStudio.TestTools.UnitTesting;
-158
View File
@@ -1,158 +0,0 @@
namespace backendCs.Test;
using System.Net.Http;
using System.Text;
[TestClass]
public class UnitTest1
{
[TestMethod]
public async Task TestMethodReset()
{
using HttpClient client = new();
HttpResponseMessage response = await client.GetAsync("http://localhost:5000/api/reset");
Assert.AreEqual((int)response.StatusCode, 200);
}
[TestMethod]
public async Task TestMethodGetall1()
{
using HttpClient client = new();
HttpResponseMessage response = await client.GetAsync(
"http://localhost:5000/api/getall?offset=10"
);
Assert.AreEqual((int)response.StatusCode, 200);
}
[TestMethod]
public async Task TestMethodGetall2()
{
using HttpClient client = new();
HttpResponseMessage response = await client.GetAsync(
"http://localhost:5000/api/getall?offset="
);
Assert.AreEqual((int)response.StatusCode, 400);
}
[TestMethod]
public async Task TestMethodGetall3()
{
using HttpClient client = new();
HttpResponseMessage response = await client.GetAsync(
"http://localhost:5000/api/getall?offset=10&from=2020-01-01&to=2024-01-01&orderby=time&order=true"
);
Assert.AreEqual((int)response.StatusCode, 200);
}
[TestMethod]
public async Task TestMethodGettopten1()
{
using HttpClient client = new();
HttpResponseMessage response = await client.GetAsync(
"http://localhost:5000/api/gettopten?from=2000-01-01&to=2024-01-01&filterby=project"
);
Assert.AreEqual((int)response.StatusCode, 200);
}
[TestMethod]
public async Task TestMethodGettopten2()
{
using HttpClient client = new();
HttpResponseMessage response = await client.GetAsync(
"http://localhost:5000/api/gettopten?from=2000-01-01&to=2024-01-01&filterby=user"
);
Assert.AreEqual((int)response.StatusCode, 200);
}
[TestMethod]
public async Task TestMethodGettopten3()
{
using HttpClient client = new();
HttpResponseMessage response = await client.GetAsync(
"http://localhost:5000/api/gettopten?to=2024-01-01&filterby=project"
);
Assert.AreEqual((int)response.StatusCode, 400);
}
[TestMethod]
public async Task TestMethodGettopten4()
{
using HttpClient client = new();
HttpResponseMessage response = await client.GetAsync(
"http://localhost:5000/api/gettopten?from=2000-01-01&filterby=project"
);
Assert.AreEqual((int)response.StatusCode, 400);
}
[TestMethod]
public async Task TestMethodGettopten5()
{
using HttpClient client = new();
HttpResponseMessage response = await client.GetAsync(
"http://localhost:5000/api/gettopten?from=2000-01-01&to=2024-01-01"
);
Assert.AreEqual((int)response.StatusCode, 400);
}
[TestMethod]
public async Task TestMethodGetuser1()
{
using HttpClient client = new();
HttpResponseMessage response = await client.GetAsync(
"http://localhost:5000/api/getuser?userid=1"
);
Assert.AreEqual((int)response.StatusCode, 200);
}
[TestMethod]
public async Task TestMethodGetuser2()
{
using HttpClient client = new();
HttpResponseMessage response = await client.GetAsync("http://localhost:5000/api/getuser");
Assert.AreEqual((int)response.StatusCode, 400);
}
[TestMethod]
public async Task TestMethodRegister1()
{
using HttpClient client = new();
// Make a GET request to a URL
var jsonData =
"{ \"f_name\": \"donna\", \"l_name\": \"cow\", \"mail\": \"tombo@mail.com\", \"password\": \"1234567890\" }";
var content = new StringContent(jsonData, Encoding.UTF8, "application/json");
HttpResponseMessage response = await client.PostAsync(
"http://localhost:5000/api/register",
content
);
Assert.AreEqual((int)response.StatusCode, 200);
}
[TestMethod]
public async Task TestMethodLogin()
{
using HttpClient client = new();
// Make a GET request to a URL
var jsonData = "{ \"mail\": \"tombo@mail.com\", \"password\": \"1234567890\" }";
var content = new StringContent(jsonData, Encoding.UTF8, "application/json");
HttpResponseMessage response = await client.PostAsync(
"http://localhost:5000/api/login",
content
);
Assert.AreEqual((int)response.StatusCode, 200);
}
}
-23
View File
@@ -1,23 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.0" />
<PackageReference Include="MSTest.TestAdapter" Version="3.0.4" />
<PackageReference Include="MSTest.TestFramework" Version="3.0.4" />
<PackageReference Include="coverlet.collector" Version="6.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\backendCS\TimelogBackend.csproj" />
</ItemGroup>
</Project>
-122
View File
@@ -1,122 +0,0 @@
using System.Net;
using System.Text;
namespace TimelogBackend;
class Program
{
static void Main()
{
// create server
HttpListener listener = new();
// routes need to be added first
listener.Prefixes.Add("http://localhost:5000/api/getall/");
listener.Prefixes.Add("http://localhost:5000/api/gettopten/");
listener.Prefixes.Add("http://localhost:5000/api/getuser/");
listener.Prefixes.Add("http://localhost:5000/api/reset/");
listener.Prefixes.Add("http://localhost:5000/api/createp/");
listener.Prefixes.Add("http://localhost:5000/api/register/");
listener.Prefixes.Add("http://localhost:5000/api/login/");
listener.Prefixes.Add("http://localhost:5000/api/createlog/");
// listen
listener.Start();
Console.WriteLine("Server is listening on http://localhost:5000/");
while (true)
{
HttpListenerContext context = listener.GetContext();
HttpListenerRequest request = context.Request;
HttpListenerResponse response = context.Response;
response.Headers.Add("Access-Control-Allow-Origin", "http://localhost:5173");
response.Headers.Add("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
response.Headers.Add("Access-Control-Allow-Headers", "Content-Type, Authorization");
// url after localhost:5000/
string uri;
if (request != null && request.Url != null)
uri = request.Url.AbsolutePath;
else
return;
switch (request.HttpMethod)
{
case "GET":
HandleGet(uri, request, response);
break;
case "POST":
HandlePost(uri, request, response);
break;
default:
HandleMissingPath(response);
break;
}
}
}
private static void HandlePost(
string uri,
HttpListenerRequest request,
HttpListenerResponse response
)
{
if (request.HasEntityBody)
switch (uri)
{
case "/api/register":
Register.HandleRequest(request, response);
break;
case "/api/login":
Login.HandleRequest(request, response);
break;
case "/api/createlog":
CreateLog.HandleRequest(request, response);
break;
default:
HandleMissingPath(response);
break;
}
else
{
HandleMissingPath(response);
}
}
private static void HandleGet(
string uri,
HttpListenerRequest request,
HttpListenerResponse response
)
{
switch (uri)
{
case "/api/reset":
Reset.HandleRequest(response);
break;
case "/api/getall":
Getall.HandleRequest(request, response);
break;
case "/api/gettopten":
Gettopten.HandleRequest(request, response);
break;
case "/api/getuser":
Getuser.HandleRequest(request, response);
break;
case "/api/createp":
CreateProcedure.HandleRequest(response);
break;
default:
HandleMissingPath(response);
break;
}
}
private static void HandleMissingPath(HttpListenerResponse response)
{
response.StatusCode = 404;
string errorMessage = "Not Found";
byte[] buffer = Encoding.UTF8.GetBytes(errorMessage);
response.ContentType = "text/plain";
response.ContentLength64 = buffer.Length;
response.OutputStream.Write(buffer, 0, buffer.Length);
response.OutputStream.Write(buffer, 0, buffer.Length);
}
}
-47
View File
@@ -1,47 +0,0 @@
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
namespace TimelogBackend;
public abstract class Route
{
public static string connectionString =
"server=127.0.0.1;uid=monty;pwd=some_pass;database=timelog";
public static void SendError(HttpListenerResponse response, Exception ex)
{
response.StatusCode = (int)HttpStatusCode.BadRequest;
string errorMessage = $"Error: {ex.Message}";
byte[] buffer = Encoding.UTF8.GetBytes(errorMessage);
response.ContentType = "text/plain";
response.ContentLength64 = buffer.Length;
response.OutputStream.Write(buffer, 0, buffer.Length);
response.Close();
}
public static void SendSuccess(HttpListenerResponse response)
{
response.StatusCode = (int)HttpStatusCode.OK;
response.StatusDescription = "Status OK";
response.Close();
}
public static void SendSuccess(HttpListenerResponse response, string jsonResponse)
{
response.StatusCode = (int)HttpStatusCode.OK;
response.StatusDescription = "Status OK";
byte[] buffer = Encoding.UTF8.GetBytes(jsonResponse);
response.ContentType = "application/json";
response.ContentLength64 = buffer.Length;
response.OutputStream.Write(buffer, 0, buffer.Length);
response.Close();
}
public static bool ValidateDate(string date)
{
Regex regex = new(@"^\d{4}-\d{2}-\d{2}$");
return regex.IsMatch(date);
}
/* public virtual void run(MySqlConnection conn, HttpListenerRequest request, HttpListenerResponse response) { } */
}
-16
View File
@@ -1,16 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MySql.Data" Version="9.1.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.2.1" />
</ItemGroup>
</Project>
-134
View File
@@ -1,134 +0,0 @@
using System.IdentityModel.Tokens.Jwt;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using Microsoft.IdentityModel.Tokens;
using MySql.Data.MySqlClient;
using Newtonsoft.Json.Linq;
namespace TimelogBackend;
public class CreateLog : Route
{
private static readonly string secretKey =
"stronk-key-much-sercret-much-more-stronk-stronk-key-much-sercret-much-more-stronk";
public static void HandleRequest(HttpListenerRequest request, HttpListenerResponse response)
{
try
{
// check header
var headers = request.Headers;
string token = headers["token"] ?? "";
if (!string.IsNullOrEmpty(token) && !ValidateToken(token))
{
throw new Exception("Invalid token");
}
MySqlCommand cmd = new();
string body;
using (StreamReader bodyReader = new(request.InputStream, request.ContentEncoding))
{
body = bodyReader.ReadToEnd();
}
JObject jsonObject = JObject.Parse(body);
string project = jsonObject["project"]?.ToString() ?? "";
string time = jsonObject["time"]?.ToString() ?? "";
string date = jsonObject["date"]?.ToString() ?? "";
// TODO check if the hours on given date don't combine to more
// than 8
if (!ValidateTime(time))
{
throw new Exception("Incorrect date format");
}
if (!ValidateDate(date))
{
throw new Exception("Incorrect date format");
}
// validate user
string? usernameClaim = GetUserFromToken(token);
if (string.IsNullOrEmpty(usernameClaim))
{
throw new Exception("wrong user id");
}
// validate project
// TODO better project validation
if (string.IsNullOrEmpty(project))
{
throw new Exception("wrong project");
}
SaveTimeLogToDatabase(usernameClaim, project, date, time);
SendSuccess(response);
}
catch (Exception ex)
{
SendError(response, ex);
}
}
private static bool ValidateToken(string token)
{
try
{
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secretKey));
var tokenHandler = new JwtSecurityTokenHandler();
var validationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidIssuer = "TimeLogServer",
ValidAudience = "TimeLogWebsite",
IssuerSigningKey = key,
};
var principal = tokenHandler.ValidateToken(
token,
validationParameters,
out SecurityToken validatedToken
);
return validatedToken != null;
}
catch
{
return false;
}
}
private static bool ValidateTime(string time)
{
return int.TryParse(time, out int myInt) && myInt >= 0 && myInt <= 8;
}
private static string GetUserFromToken(string token)
{
var handler = new JwtSecurityTokenHandler();
var jwtToken = handler.ReadJwtToken(token);
string? usernameClaim = jwtToken.Claims.FirstOrDefault(c => c.Type == "user")?.Value;
return string.IsNullOrEmpty(usernameClaim) ? "" : usernameClaim;
}
private static void SaveTimeLogToDatabase(
string username,
string project,
string date,
string time
)
{
using MySqlConnection conn = new(connectionString);
conn.Open();
using MySqlCommand cmd = new(
@"INSERT INTO Timelog(user, project, date, time)
VALUES(@user, @project, @date, @time);",
conn
);
cmd.Parameters.AddWithValue("@user", username);
cmd.Parameters.AddWithValue("@project", project);
cmd.Parameters.AddWithValue("@date", date);
cmd.Parameters.AddWithValue("@time", time);
cmd.ExecuteNonQuery();
}
}
-126
View File
@@ -1,126 +0,0 @@
using System.Net;
using MySql.Data.MySqlClient;
namespace TimelogBackend;
public class CreateProcedure : Route
{
public static void HandleRequest(HttpListenerResponse response)
{
try
{
MySqlCommand cmd = new();
using MySqlConnection conn = new(connectionString);
conn.Open();
cmd.Connection = conn;
cmd.CommandText =
@" CREATE PROCEDURE fill_timelog ()
BEGIN
DECLARE j INT DEFAULT 1;
DECLARE users INT DEFAULT 1;
DECLARE logs INT;
DECLARE hours FLOAT;
DECLARE project INT;
DECLARE curDate DATE DEFAULT '2024-11-18';
DECLARE h2 INT;
WHILE users <= 100 DO
SET logs = FLOOR(1 + (RAND() * 20));
SET j = 1;
WHILE j <= logs DO
SET project = FLOOR(1 + (RAND() * 3));
SET curDate = DATE_ADD('2020-01-01', INTERVAL FLOOR(RAND() * DATEDIFF('2020-02-01', '2020-01-01')) DAY);
SET hours = (RAND() * (8 - 0.25)) + 0.25;
SELECT SUM(time) INTO h2
FROM Timelog
WHERE date = curdate && user = users;
WHILE(h2 + hours) > 8 DO
SET curDate = DATE_ADD('2020-01-01', INTERVAL FLOOR(RAND() * DATEDIFF('2020-02-01', '2020-01-01')) DAY);
SELECT SUM(time)INTO h2
FROM Timelog
WHERE date = curdate && user = users;
END WHILE;
INSERT INTO Timelog(user, project, date, time) VALUES(users, project, curDate, hours);
SET j = j + 1;
END WHILE;
SET users = users + 1;
END WHILE;
END;";
cmd.ExecuteNonQuery();
cmd.CommandText =
@"CREATE PROCEDURE InitDB()
BEGIN
DECLARE i INT DEFAULT 1;
TRUNCATE TABLE Timelog;
TRUNCATE TABLE Project;
SET foreign_key_checks = 0;
TRUNCATE TABLE User;
SET foreign_key_checks = 1;
INSERT INTO Project(name) VALUES('My own'),('Outcons'),('Free Time');
CREATE TEMPORARY TABLE temp_fname (fname VARCHAR(255));
INSERT INTO temp_fname (fname) VALUES
( 'John' ),
( 'Gringo' ),
( 'Mark' ),
( 'Lisa' ),
( 'Maria' ),
( 'Sonya' ),
( 'Philip' ),
( 'Jose' ),
( 'Lorenzo' ),
( 'George' ),
( 'Justin' );
CREATE TEMPORARY TABLE temp_lname (lname VARCHAR(255));
INSERT INTO temp_lname (lname) VALUES
( 'Johnson' ),
( 'Lamas' ),
( 'Jackson' ),
( 'Brown' ),
( 'Mason' ),
( 'Rodriguez' ),
( 'Roberts' ),
( 'Thomas' ),
( 'Rose' ),
( 'McDonalds' );
CREATE TEMPORARY TABLE temp_mail (mail VARCHAR(255));
INSERT INTO temp_mail (mail) VALUES
( 'hotmail.com' ),
( 'gmail.com' ),
( 'live.com' );
WHILE i <= 100 DO
INSERT INTO User (f_name, l_name, mail)
SELECT
(SELECT fname FROM temp_fname ORDER BY RAND() LIMIT 1),
(SELECT lname FROM temp_lname ORDER BY RAND() LIMIT 1),
(SELECT mail FROM temp_mail ORDER BY RAND() LIMIT 1);
SET i = i + 1;
END WHILE;
UPDATE User
SET User.mail = CONCAT(User.f_name,'.', User.l_name,'@', User.mail);
CALL fill_timelog();
DROP TABLE temp_mail;
DROP TABLE temp_fname;
DROP TABLE temp_lname;
END;";
cmd.ExecuteNonQuery();
// prepare response
SendSuccess(response);
}
catch (Exception ex)
{
SendError(response, ex);
}
}
}
-126
View File
@@ -1,126 +0,0 @@
using System.Net;
using MySql.Data.MySqlClient;
using Newtonsoft.Json;
namespace TimelogBackend;
// there should be a better way to deal with data comming from sql
public class Log
{
public object? FName { get; set; }
public object? LName { get; set; }
public object? Mail { get; set; }
public object? Name { get; set; }
public object? Time { get; set; }
public object? Date { get; set; }
public object? User { get; set; }
}
public class Getall : Route
{
private static string ConstructQuery(
string from,
string to,
string order,
string offset,
string sortby
)
{
string mainQuery =
@"SELECT u.f_name,u.l_name,u.mail,p.name,t.time,t.date,t.user
FROM Timelog t
INNER JOIN Project p ON p.id=t.project
INNER JOIN User u ON u.id=t.user ";
// this shenanigan is needed to remove the "" around group by
string offsetQuery = " LIMIT 10 OFFSET " + offset + ";";
// depending on the incoming parameters construct a Query
if (!string.IsNullOrEmpty(to) && !string.IsNullOrEmpty(from))
{
mainQuery += AddWhereClause(from, to);
}
if (!string.IsNullOrEmpty(sortby))
{
mainQuery += AddSortBy(sortby, order);
}
if (!int.TryParse(offset, out int myInt) || myInt < 0)
throw new Exception("Incorect offset");
return mainQuery + offsetQuery;
}
private static string AddWhereClause(string from, string to)
{
if (!ValidateDate(to) || !ValidateDate(from))
{
throw new Exception("Incorrect date format");
}
string whereQuery = " WHERE t.date BETWEEN @from AND @to ";
return whereQuery;
}
private static string AddSortBy(string sortby, string order)
{
List<string> validSorting = ["f_name", "l_name", "mail", "time", "date", "user", "project"];
if (!validSorting.Contains(sortby))
{
throw new Exception("Incorrect sorting value");
}
string orderQuery = " ORDER BY " + sortby + " " + order;
return orderQuery;
}
private static List<Log> ExtractDataFromDB(MySqlCommand cmd)
{
using MySqlConnection conn = new(connectionString);
conn.Open();
cmd.Connection = conn;
// execute query and read results
MySqlDataReader reader = cmd.ExecuteReader();
List<Log> entries = [];
while (reader.Read())
{
entries.Add(
new Log
{
FName = reader["f_name"],
LName = reader["l_name"],
User = reader["user"],
Date = reader["date"],
Name = reader["name"],
Time = reader["time"],
Mail = reader["mail"],
}
);
}
return entries;
}
public static void HandleRequest(HttpListenerRequest request, HttpListenerResponse response)
{
try
{
// extract data from url
var queryString = request.QueryString;
string? from = queryString["from"] ?? "";
string? to = queryString["to"] ?? "";
string? sortby = queryString["sortby"] ?? "";
string? offset = queryString["offset"] ?? "";
string? order = queryString["order"] ?? "";
order = order == "true" ? "ASC" : "DESC";
// SQL
MySqlCommand cmd = new(ConstructQuery(from, to, order, offset, sortby));
cmd.Parameters.AddWithValue("@from", from);
cmd.Parameters.AddWithValue("@to", to);
var entries = ExtractDataFromDB(cmd);
// serialize JSON
string jsonResponse = JsonConvert.SerializeObject(entries);
SendSuccess(response, jsonResponse);
}
catch (Exception ex)
{
SendError(response, ex);
}
}
}
-98
View File
@@ -1,98 +0,0 @@
using System.Net;
using MySql.Data.MySqlClient;
using Newtonsoft.Json;
namespace TimelogBackend;
public class TopTen
{
public object? User { get; set; }
public object? Date { get; set; }
public object? Project { get; set; }
public object? FName { get; set; }
public object? LName { get; set; }
public object? Name { get; set; }
public object? TotalTime { get; set; }
}
public class Gettopten : Route
{
private static List<TopTen> ExtractDataFromDB(MySqlCommand cmd)
{
using MySqlConnection conn = new(connectionString);
cmd.Connection = conn;
conn.Open();
// Execute the query and read the results
MySqlDataReader reader = cmd.ExecuteReader();
List<TopTen> entries = [];
while (reader.Read())
{
entries.Add(
new TopTen
{
User = reader["user"],
Date = reader["date"],
Project = reader["project"],
FName = reader["f_name"],
LName = reader["l_name"],
Name = reader["name"],
TotalTime = reader["total_time"],
}
);
}
return entries;
}
private static void ValidateQueryStrings(string from, string to, string filterBy)
{
if (!string.IsNullOrEmpty(to) && !string.IsNullOrEmpty(from))
{
ValidateDate(to);
ValidateDate(from);
}
else
{
throw new Exception("Empty date format");
}
if (string.IsNullOrEmpty(filterBy))
{
throw new Exception("Empty filterby");
}
if (filterBy != "user" && filterBy != "project")
{
throw new Exception("Incorrect filterby");
}
}
public static void HandleRequest(HttpListenerRequest request, HttpListenerResponse response)
{
try
{
var queryString = request.QueryString;
string? from = queryString["from"] ?? "";
string? to = queryString["to"] ?? "";
string? filterBy = queryString["filterBy"] ?? "";
ValidateQueryStrings(from, to, filterBy);
string query =
@"SELECT t.user,t.date,t.project,u.f_name,u.l_name,p.name,SUM(t.time) as total_time
FROM Timelog t
INNER JOIN Project p ON p.id=t.project
INNER JOIN User u ON u.id=t.user
WHERE t.date BETWEEN @from AND @to
GROUP BY "
+ filterBy
+ " ORDER BY total_time DESC LIMIT 10;";
MySqlCommand cmd = new(query);
cmd.Parameters.AddWithValue("@from", from);
cmd.Parameters.AddWithValue("@to", to);
var entries = ExtractDataFromDB(cmd);
string jsonResponse = JsonConvert.SerializeObject(entries);
SendSuccess(response, jsonResponse);
}
catch (Exception ex)
{
SendError(response, ex);
}
}
}
-59
View File
@@ -1,59 +0,0 @@
using System.Dynamic;
using System.Net;
using MySql.Data.MySqlClient;
using Newtonsoft.Json;
namespace TimelogBackend;
public class Getuser : Route
{
private static dynamic ExtractDataFromDB(MySqlCommand cmd)
{
using MySqlConnection conn = new(connectionString);
conn.Open();
cmd.Connection = conn;
// execute query and read results
MySqlDataReader reader = cmd.ExecuteReader();
dynamic expando = new ExpandoObject();
while (reader.Read())
{
((IDictionary<string?, object>)expando)[reader["name"].ToString()] = reader[
"SUM(t.time)"
];
}
return expando;
}
public static void HandleRequest(HttpListenerRequest request, HttpListenerResponse response)
{
try
{
var queryString = request.QueryString;
string? userid = queryString["userid"];
if (string.IsNullOrEmpty(userid))
{
throw new Exception("Missing userid");
}
// prepare SQL query
string query =
@"SELECT p.name, SUM(t.time)
FROM Timelog t
INNER JOIN Project p ON p.id=t.project
INNER JOIN User u ON u.id=t.user
WHERE User = @userid
GROUP BY name;";
MySqlCommand cmd = new(query);
cmd.Parameters.AddWithValue("@userid", userid);
var expando = ExtractDataFromDB(cmd);
// serialize JSON
string jsonResponse = JsonConvert.SerializeObject(expando);
// prepare response
SendSuccess(response, jsonResponse);
}
catch (Exception ex)
{
SendError(response, ex);
}
}
}
-119
View File
@@ -1,119 +0,0 @@
using System.IdentityModel.Tokens.Jwt;
using System.Net;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;
using Microsoft.IdentityModel.Tokens;
using MySql.Data.MySqlClient;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace TimelogBackend;
public class Login : Route
{
private static readonly string secretKey =
"stronk-key-much-sercret-much-more-stronk-stronk-key-much-sercret-much-more-stronk";
public static string GenerateToken(string user)
{
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secretKey));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(
issuer: "TimeLogServer",
audience: "TimeLogWebsite",
claims: [new Claim("user", user)],
expires: DateTime.Now.AddHours(2),
signingCredentials: creds
);
return new JwtSecurityTokenHandler().WriteToken(token);
}
public static bool VerifyPassword(string enteredPassword, string storedHash)
{
byte[] hashBytes = Convert.FromBase64String(storedHash);
byte[] salt = new byte[16];
Array.Copy(hashBytes, 0, salt, 0, 16);
using var pbkdf2 = new Rfc2898DeriveBytes(
enteredPassword,
salt,
10000,
HashAlgorithmName.SHA256
);
byte[] newHash = pbkdf2.GetBytes(32);
for (int i = 0; i < 32; i++)
{
if (newHash[i] != hashBytes[i + 16])
return false;
}
return true;
}
private static string ExtractDataFromDB(MySqlCommand cmd, string password)
{
using MySqlConnection conn = new(connectionString);
cmd.Connection = conn;
conn.Open();
// execute query and read results
MySqlDataReader reader = cmd.ExecuteReader();
string? userId = "";
string? hashedPass = "";
while (reader.Read())
{
userId = Convert.ToString(reader["id"]);
hashedPass = reader.GetString("password");
}
// check username
if (string.IsNullOrEmpty(userId))
{
throw new Exception("Invalid Username or Password");
}
//check password
if (
string.IsNullOrEmpty(password)
|| string.IsNullOrEmpty(hashedPass)
|| !VerifyPassword(password, hashedPass)
)
{
throw new Exception("Invalid Username or Password");
}
return userId;
}
public static void HandleRequest(HttpListenerRequest request, HttpListenerResponse response)
{
try
{
// extract data from body
string body;
using (StreamReader bodyReader = new(request.InputStream, request.ContentEncoding))
{
body = bodyReader.ReadToEnd();
}
JObject jsonObject = JObject.Parse(body);
string mail = jsonObject["mail"]?.ToString() ?? "";
string password = jsonObject["password"]?.ToString() ?? "";
// prepare SQL query
string query =
@"SELECT u.id, password FROM User u
INNER JOIN Password p ON p.user=u.id
WHERE mail=@mail;";
MySqlCommand cmd = new(query);
cmd.Parameters.AddWithValue("@mail", mail);
var userId = ExtractDataFromDB(cmd, password);
string? jsonResponse = JsonConvert.SerializeObject(GenerateToken(userId));
// prepare response
SendSuccess(response, jsonResponse);
}
catch (Exception ex)
{
SendError(response, ex);
}
}
}
-95
View File
@@ -1,95 +0,0 @@
using System.Net;
using System.Security.Cryptography;
using MySql.Data.MySqlClient;
using Newtonsoft.Json.Linq;
namespace TimelogBackend;
public class Register : Route
{
private static string HashPassword(string password)
{
byte[] salt = new byte[16];
RandomNumberGenerator.Fill(salt);
using var pbkdf2 = new Rfc2898DeriveBytes(password, salt, 10000, HashAlgorithmName.SHA256);
byte[] hash = pbkdf2.GetBytes(32);
byte[] hashBytes = new byte[48]; // 16 (salt) + 32 (hash)
Array.Copy(salt, 0, hashBytes, 0, 16);
Array.Copy(hash, 0, hashBytes, 16, 32);
return Convert.ToBase64String(hashBytes);
}
public static void HandleRequest(HttpListenerRequest request, HttpListenerResponse response)
{
MySqlTransaction? transaction = null;
try
{
// extract parameters from req body
string body;
using (StreamReader bodyReader = new(request.InputStream, request.ContentEncoding))
{
body = bodyReader.ReadToEnd();
}
JObject jsonObject = JObject.Parse(body);
string f_name = jsonObject["f_name"]?.ToString() ?? "";
string l_name = jsonObject["l_name"]?.ToString() ?? "";
string password = jsonObject["password"]?.ToString() ?? "";
string mail = jsonObject["mail"]?.ToString() ?? "";
// validate parameters
if (
string.IsNullOrEmpty(f_name)
|| f_name.Length > 30
|| f_name.Length < 2
|| string.IsNullOrEmpty(l_name)
|| l_name.Length > 30
|| l_name.Length < 2
|| string.IsNullOrEmpty(mail)
|| mail.Length > 50
|| mail.Length < 6
|| string.IsNullOrEmpty(password)
|| password.Length > 30
|| password.Length < 10
)
{
throw new Exception("Wrong parameters");
}
// TODO Validate dupes of email
string query = "INSERT INTO User(f_name,l_name,mail) VALUES(@f_name,@l_name,@mail)";
MySqlCommand cmd = new(query);
cmd.Parameters.AddWithValue("@f_name", f_name);
cmd.Parameters.AddWithValue("@l_name", l_name);
cmd.Parameters.AddWithValue("@mail", mail);
using MySqlConnection conn = new(connectionString);
conn.Open();
transaction = conn.BeginTransaction();
cmd.Connection = conn;
cmd.ExecuteNonQuery();
// Get user ID
cmd.CommandText = "SELECT id FROM User WHERE mail=@mail;";
MySqlDataReader reader = cmd.ExecuteReader();
reader.Read();
var id = reader["id"];
reader.Close();
// Insert into password
cmd.CommandText = "INSERT INTO Password(user,password) VALUES(@id,@password)";
cmd.Parameters.AddWithValue("@password", HashPassword(password));
cmd.Parameters.AddWithValue("@id", id);
cmd.ExecuteNonQuery();
transaction.Commit();
SendSuccess(response);
}
catch (Exception ex)
{
transaction?.Rollback();
SendError(response, ex);
}
}
}
-30
View File
@@ -1,30 +0,0 @@
using System.Net;
using MySql.Data.MySqlClient;
namespace TimelogBackend;
public class Reset : Route
{
public static void HandleRequest(HttpListenerResponse response)
{
try
{
// prepare SQL query
MySqlCommand cmd = new() { CommandText = "CALL InitDB" };
using (MySqlConnection conn = new MySqlConnection(connectionString))
{
cmd.Connection = conn;
// open connection
conn.Open();
// execute query
cmd.ExecuteNonQuery();
// set up and send response
SendSuccess(response);
}
}
catch (Exception ex)
{
SendError(response, ex);
}
}
}
-13
View File
@@ -1,13 +0,0 @@
#!/bin/bash
url="localhost:5000/" # Replace with your URL
# Loop to send 1000 requests
for i in {1..10000}
do
curl -s $url > /dev/null & # Send the request in the background
done
# Wait for all background processes to finish
wait
echo "1000 requests sent!"