Compare commits
1 Commits
ec40145c69
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
| e28bde1cfe |
@@ -1,10 +0,0 @@
|
|||||||
import mysql from "mysql2/promise";
|
|
||||||
|
|
||||||
const connection = await mysql.createConnection({
|
|
||||||
host: "localhost",
|
|
||||||
user: "monty",
|
|
||||||
password: "some_pass",
|
|
||||||
database: "timelognode",
|
|
||||||
});
|
|
||||||
|
|
||||||
export default connection;
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
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}`));
|
|
||||||
Generated
-953
@@ -1,953 +0,0 @@
|
|||||||
{
|
|
||||||
"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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
{
|
|
||||||
"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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
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;
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
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;
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
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;
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
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;
|
|
||||||
@@ -1,137 +0,0 @@
|
|||||||
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;
|
|
||||||
|
|
||||||
@@ -1,147 +0,0 @@
|
|||||||
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 ;
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
global using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
@@ -0,0 +1,158 @@
|
|||||||
|
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, 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
[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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
<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>
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
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) { } */
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<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>
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
using System.IdentityModel.Tokens.Jwt;
|
||||||
|
using System.Net;
|
||||||
|
using System.Text;
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,126 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,126 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
using System.Dynamic;
|
||||||
|
using System.Net;
|
||||||
|
using MySql.Data.MySqlClient;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace TimelogBackend;
|
||||||
|
|
||||||
|
public class Getuser : Route
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Executable
+13
@@ -0,0 +1,13 @@
|
|||||||
|
#!/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!"
|
||||||
@@ -11,13 +11,13 @@ import {
|
|||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
|
|
||||||
interface User {
|
interface User {
|
||||||
date: string;
|
Date: string;
|
||||||
f_name: string;
|
FName: string;
|
||||||
l_name: string;
|
LName: string;
|
||||||
mail: string;
|
Mail: string;
|
||||||
name: string;
|
Name: string;
|
||||||
time: number;
|
Time: number;
|
||||||
user: number;
|
User: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO date input should send error when there is no data in the response
|
//TODO date input should send error when there is no data in the response
|
||||||
@@ -47,6 +47,7 @@ const LeftSide = ({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function fetchData() {
|
async function fetchData() {
|
||||||
const resp = await api.get("/getall", { params });
|
const resp = await api.get("/getall", { params });
|
||||||
|
console.log(resp.data);
|
||||||
if (resp.data.length) setUsers(resp.data);
|
if (resp.data.length) setUsers(resp.data);
|
||||||
}
|
}
|
||||||
async function resetData() {
|
async function resetData() {
|
||||||
@@ -156,14 +157,14 @@ const LeftSide = ({
|
|||||||
{users.length > 0 && Array.isArray(users) ? (
|
{users.length > 0 && Array.isArray(users) ? (
|
||||||
users.map((post, idx) => (
|
users.map((post, idx) => (
|
||||||
<TableRow key={idx}>
|
<TableRow key={idx}>
|
||||||
<TableCell>{post.f_name} </TableCell>
|
<TableCell>{post.FName} </TableCell>
|
||||||
<TableCell>{post.l_name} </TableCell>
|
<TableCell>{post.LName} </TableCell>
|
||||||
<TableCell>{post.mail} </TableCell>
|
<TableCell>{post.Mail} </TableCell>
|
||||||
<TableCell>{post.name} </TableCell>
|
<TableCell>{post.Name} </TableCell>
|
||||||
<TableCell>{post.date.slice(0, 10)} </TableCell>
|
<TableCell>{post.Date.slice(0, 10)} </TableCell>
|
||||||
<TableCell>{post.time} </TableCell>
|
<TableCell>{post.Time} </TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<button onClick={() => viewProjectHours(post.user)}>
|
<button onClick={() => viewProjectHours(post.User)}>
|
||||||
Hours
|
Hours
|
||||||
</button>
|
</button>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
|||||||
@@ -28,13 +28,13 @@ const RightSide = ({ reset }: { reset: boolean }) => {
|
|||||||
// turn the data into form suitable to charts
|
// turn the data into form suitable to charts
|
||||||
if (filter === "project")
|
if (filter === "project")
|
||||||
for (let idx = 1; idx < resp.data.length; idx++) {
|
for (let idx = 1; idx < resp.data.length; idx++) {
|
||||||
resp.data[idx] = [resp.data[idx].name, resp.data[idx].total_time];
|
resp.data[idx] = [resp.data[idx].Name, resp.data[idx].TotalTime];
|
||||||
}
|
}
|
||||||
else if (filter === "user")
|
else if (filter === "user")
|
||||||
for (let idx = 1; idx < resp.data.length; idx++) {
|
for (let idx = 1; idx < resp.data.length; idx++) {
|
||||||
resp.data[idx] = [
|
resp.data[idx] = [
|
||||||
resp.data[idx].f_name + " " + resp.data[idx].l_name,
|
resp.data[idx].FName + " " + resp.data[idx].LName,
|
||||||
resp.data[idx].total_time,
|
resp.data[idx].TotalTime,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
setChartData(resp.data);
|
setChartData(resp.data);
|
||||||
|
|||||||
Reference in New Issue
Block a user