add all project files
This commit is contained in:
11
back/Dockerfile
Normal file
11
back/Dockerfile
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
FROM python:3.10
|
||||||
|
|
||||||
|
WORKDIR /code
|
||||||
|
|
||||||
|
COPY back/requirements.txt /code/requirements.txt
|
||||||
|
|
||||||
|
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
|
||||||
|
|
||||||
|
COPY back/ /code/
|
||||||
|
|
||||||
|
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--proxy-headers"]
|
||||||
17
back/Pipfile
Normal file
17
back/Pipfile
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
[[source]]
|
||||||
|
url = "https://pypi.org/simple"
|
||||||
|
verify_ssl = true
|
||||||
|
name = "pypi"
|
||||||
|
|
||||||
|
[packages]
|
||||||
|
fastapi = {extras = ["all"], version = "*"}
|
||||||
|
python-jose = "*"
|
||||||
|
passlib = {extras = ["bcrypt"], version = "*"}
|
||||||
|
uvicorn = {extras = ["standard"], version = "*"}
|
||||||
|
python-pam = "*"
|
||||||
|
pymysql = "*"
|
||||||
|
|
||||||
|
[dev-packages]
|
||||||
|
|
||||||
|
[requires]
|
||||||
|
python_version = "3.9"
|
||||||
812
back/Pipfile.lock
generated
Normal file
812
back/Pipfile.lock
generated
Normal file
@@ -0,0 +1,812 @@
|
|||||||
|
{
|
||||||
|
"_meta": {
|
||||||
|
"hash": {
|
||||||
|
"sha256": "6dc39a3cef77fec5be0cfb2841bd1d47b1aec853936be4f1195c25ecb04cc1a8"
|
||||||
|
},
|
||||||
|
"pipfile-spec": 6,
|
||||||
|
"requires": {
|
||||||
|
"python_version": "3.10"
|
||||||
|
},
|
||||||
|
"sources": [
|
||||||
|
{
|
||||||
|
"name": "pypi",
|
||||||
|
"url": "https://pypi.org/simple",
|
||||||
|
"verify_ssl": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"default": {
|
||||||
|
"anyio": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:a0aeffe2fb1fdf374a8e4b471444f0f3ac4fb9f5a5b542b48824475e0042a5a6",
|
||||||
|
"sha256:b5fa16c5ff93fa1046f2eeb5bbff2dad4d3514d6cda61d02816dba34fa8c3c2e"
|
||||||
|
],
|
||||||
|
"markers": "python_full_version >= '3.6.2'",
|
||||||
|
"version": "==3.5.0"
|
||||||
|
},
|
||||||
|
"asgiref": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:45a429524fba18aba9d512498b19d220c4d628e75b40cf5c627524dbaebc5cc1",
|
||||||
|
"sha256:fddeea3c53fa99d0cdb613c3941cc6e52d822491fc2753fba25768fb5bf4e865"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.7'",
|
||||||
|
"version": "==3.5.1"
|
||||||
|
},
|
||||||
|
"bcrypt": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:2b02d6bfc6336d1094276f3f588aa1225a598e27f8e3388f4db9948cb707b521",
|
||||||
|
"sha256:433c410c2177057705da2a9f2cd01dd157493b2a7ac14c8593a16b3dab6b6bfb",
|
||||||
|
"sha256:4e029cef560967fb0cf4a802bcf4d562d3d6b4b1bf81de5ec1abbe0f1adb027e",
|
||||||
|
"sha256:61bae49580dce88095d669226d5076d0b9d927754cedbdf76c6c9f5099ad6f26",
|
||||||
|
"sha256:6d2cb9d969bfca5bc08e45864137276e4c3d3d7de2b162171def3d188bf9d34a",
|
||||||
|
"sha256:7180d98a96f00b1050e93f5b0f556e658605dd9f524d0b0e68ae7944673f525e",
|
||||||
|
"sha256:7d9ba2e41e330d2af4af6b1b6ec9e6128e91343d0b4afb9282e54e5508f31baa",
|
||||||
|
"sha256:7ff2069240c6bbe49109fe84ca80508773a904f5a8cb960e02a977f7f519b129",
|
||||||
|
"sha256:88273d806ab3a50d06bc6a2fc7c87d737dd669b76ad955f449c43095389bc8fb",
|
||||||
|
"sha256:a2c46100e315c3a5b90fdc53e429c006c5f962529bc27e1dfd656292c20ccc40",
|
||||||
|
"sha256:cd43303d6b8a165c29ec6756afd169faba9396a9472cdff753fe9f19b96ce2fa"
|
||||||
|
],
|
||||||
|
"version": "==3.2.2"
|
||||||
|
},
|
||||||
|
"certifi": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872",
|
||||||
|
"sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569"
|
||||||
|
],
|
||||||
|
"version": "==2021.10.8"
|
||||||
|
},
|
||||||
|
"cffi": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:00c878c90cb53ccfaae6b8bc18ad05d2036553e6d9d1d9dbcf323bbe83854ca3",
|
||||||
|
"sha256:0104fb5ae2391d46a4cb082abdd5c69ea4eab79d8d44eaaf79f1b1fd806ee4c2",
|
||||||
|
"sha256:06c48159c1abed75c2e721b1715c379fa3200c7784271b3c46df01383b593636",
|
||||||
|
"sha256:0808014eb713677ec1292301ea4c81ad277b6cdf2fdd90fd540af98c0b101d20",
|
||||||
|
"sha256:10dffb601ccfb65262a27233ac273d552ddc4d8ae1bf93b21c94b8511bffe728",
|
||||||
|
"sha256:14cd121ea63ecdae71efa69c15c5543a4b5fbcd0bbe2aad864baca0063cecf27",
|
||||||
|
"sha256:17771976e82e9f94976180f76468546834d22a7cc404b17c22df2a2c81db0c66",
|
||||||
|
"sha256:181dee03b1170ff1969489acf1c26533710231c58f95534e3edac87fff06c443",
|
||||||
|
"sha256:23cfe892bd5dd8941608f93348c0737e369e51c100d03718f108bf1add7bd6d0",
|
||||||
|
"sha256:263cc3d821c4ab2213cbe8cd8b355a7f72a8324577dc865ef98487c1aeee2bc7",
|
||||||
|
"sha256:2756c88cbb94231c7a147402476be2c4df2f6078099a6f4a480d239a8817ae39",
|
||||||
|
"sha256:27c219baf94952ae9d50ec19651a687b826792055353d07648a5695413e0c605",
|
||||||
|
"sha256:2a23af14f408d53d5e6cd4e3d9a24ff9e05906ad574822a10563efcef137979a",
|
||||||
|
"sha256:31fb708d9d7c3f49a60f04cf5b119aeefe5644daba1cd2a0fe389b674fd1de37",
|
||||||
|
"sha256:3415c89f9204ee60cd09b235810be700e993e343a408693e80ce7f6a40108029",
|
||||||
|
"sha256:3773c4d81e6e818df2efbc7dd77325ca0dcb688116050fb2b3011218eda36139",
|
||||||
|
"sha256:3b96a311ac60a3f6be21d2572e46ce67f09abcf4d09344c49274eb9e0bf345fc",
|
||||||
|
"sha256:3f7d084648d77af029acb79a0ff49a0ad7e9d09057a9bf46596dac9514dc07df",
|
||||||
|
"sha256:41d45de54cd277a7878919867c0f08b0cf817605e4eb94093e7516505d3c8d14",
|
||||||
|
"sha256:4238e6dab5d6a8ba812de994bbb0a79bddbdf80994e4ce802b6f6f3142fcc880",
|
||||||
|
"sha256:45db3a33139e9c8f7c09234b5784a5e33d31fd6907800b316decad50af323ff2",
|
||||||
|
"sha256:45e8636704eacc432a206ac7345a5d3d2c62d95a507ec70d62f23cd91770482a",
|
||||||
|
"sha256:4958391dbd6249d7ad855b9ca88fae690783a6be9e86df65865058ed81fc860e",
|
||||||
|
"sha256:4a306fa632e8f0928956a41fa8e1d6243c71e7eb59ffbd165fc0b41e316b2474",
|
||||||
|
"sha256:57e9ac9ccc3101fac9d6014fba037473e4358ef4e89f8e181f8951a2c0162024",
|
||||||
|
"sha256:59888172256cac5629e60e72e86598027aca6bf01fa2465bdb676d37636573e8",
|
||||||
|
"sha256:5e069f72d497312b24fcc02073d70cb989045d1c91cbd53979366077959933e0",
|
||||||
|
"sha256:64d4ec9f448dfe041705426000cc13e34e6e5bb13736e9fd62e34a0b0c41566e",
|
||||||
|
"sha256:6dc2737a3674b3e344847c8686cf29e500584ccad76204efea14f451d4cc669a",
|
||||||
|
"sha256:74fdfdbfdc48d3f47148976f49fab3251e550a8720bebc99bf1483f5bfb5db3e",
|
||||||
|
"sha256:75e4024375654472cc27e91cbe9eaa08567f7fbdf822638be2814ce059f58032",
|
||||||
|
"sha256:786902fb9ba7433aae840e0ed609f45c7bcd4e225ebb9c753aa39725bb3e6ad6",
|
||||||
|
"sha256:8b6c2ea03845c9f501ed1313e78de148cd3f6cad741a75d43a29b43da27f2e1e",
|
||||||
|
"sha256:91d77d2a782be4274da750752bb1650a97bfd8f291022b379bb8e01c66b4e96b",
|
||||||
|
"sha256:91ec59c33514b7c7559a6acda53bbfe1b283949c34fe7440bcf917f96ac0723e",
|
||||||
|
"sha256:920f0d66a896c2d99f0adbb391f990a84091179542c205fa53ce5787aff87954",
|
||||||
|
"sha256:a5263e363c27b653a90078143adb3d076c1a748ec9ecc78ea2fb916f9b861962",
|
||||||
|
"sha256:abb9a20a72ac4e0fdb50dae135ba5e77880518e742077ced47eb1499e29a443c",
|
||||||
|
"sha256:c2051981a968d7de9dd2d7b87bcb9c939c74a34626a6e2f8181455dd49ed69e4",
|
||||||
|
"sha256:c21c9e3896c23007803a875460fb786118f0cdd4434359577ea25eb556e34c55",
|
||||||
|
"sha256:c2502a1a03b6312837279c8c1bd3ebedf6c12c4228ddbad40912d671ccc8a962",
|
||||||
|
"sha256:d4d692a89c5cf08a8557fdeb329b82e7bf609aadfaed6c0d79f5a449a3c7c023",
|
||||||
|
"sha256:da5db4e883f1ce37f55c667e5c0de439df76ac4cb55964655906306918e7363c",
|
||||||
|
"sha256:e7022a66d9b55e93e1a845d8c9eba2a1bebd4966cd8bfc25d9cd07d515b33fa6",
|
||||||
|
"sha256:ef1f279350da2c586a69d32fc8733092fd32cc8ac95139a00377841f59a3f8d8",
|
||||||
|
"sha256:f54a64f8b0c8ff0b64d18aa76675262e1700f3995182267998c31ae974fbc382",
|
||||||
|
"sha256:f5c7150ad32ba43a07c4479f40241756145a1f03b43480e058cfd862bf5041c7",
|
||||||
|
"sha256:f6f824dc3bce0edab5f427efcfb1d63ee75b6fcb7282900ccaf925be84efb0fc",
|
||||||
|
"sha256:fd8a250edc26254fe5b33be00402e6d287f562b6a5b2152dec302fa15bb3e997",
|
||||||
|
"sha256:ffaa5c925128e29efbde7301d8ecaf35c8c60ffbcd6a1ffd3a552177c8e5e796"
|
||||||
|
],
|
||||||
|
"version": "==1.15.0"
|
||||||
|
},
|
||||||
|
"charset-normalizer": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597",
|
||||||
|
"sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3'",
|
||||||
|
"version": "==2.0.12"
|
||||||
|
},
|
||||||
|
"click": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e",
|
||||||
|
"sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.7'",
|
||||||
|
"version": "==8.1.3"
|
||||||
|
},
|
||||||
|
"dnspython": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:0f7569a4a6ff151958b64304071d370daa3243d15941a7beedf0c9fe5105603e",
|
||||||
|
"sha256:a851e51367fb93e9e1361732c1d60dab63eff98712e503ea7d92e6eccb109b4f"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.6' and python_version < '4'",
|
||||||
|
"version": "==2.2.1"
|
||||||
|
},
|
||||||
|
"ecdsa": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:5cf31d5b33743abe0dfc28999036c849a69d548f994b535e527ee3cb7f3ef676",
|
||||||
|
"sha256:b9f500bb439e4153d0330610f5d26baaf18d17b8ced1bc54410d189385ea68aa"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||||
|
"version": "==0.17.0"
|
||||||
|
},
|
||||||
|
"email-validator": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:6757aea012d40516357c0ac2b1a4c31219ab2f899d26831334c5d069e8b6c3d8",
|
||||||
|
"sha256:c8589e691cf73eb99eed8d10ce0e9cbb05a0886ba920c8bcb7c82873f4c5789c"
|
||||||
|
],
|
||||||
|
"version": "==1.2.1"
|
||||||
|
},
|
||||||
|
"fastapi": {
|
||||||
|
"extras": [
|
||||||
|
"all"
|
||||||
|
],
|
||||||
|
"hashes": [
|
||||||
|
"sha256:1e05c868651e3935bd9b290c61a3661a54e37471d3a0700bc5e4380f9ed935ae",
|
||||||
|
"sha256:a5f99f6e827c7108a8efaf1d7f19d6cf2d735ad984f5e44d33ccec6ee88a7da1"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==0.76.0"
|
||||||
|
},
|
||||||
|
"greenlet": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:0051c6f1f27cb756ffc0ffbac7d2cd48cb0362ac1736871399a739b2885134d3",
|
||||||
|
"sha256:00e44c8afdbe5467e4f7b5851be223be68adb4272f44696ee71fe46b7036a711",
|
||||||
|
"sha256:013d61294b6cd8fe3242932c1c5e36e5d1db2c8afb58606c5a67efce62c1f5fd",
|
||||||
|
"sha256:049fe7579230e44daef03a259faa24511d10ebfa44f69411d99e6a184fe68073",
|
||||||
|
"sha256:14d4f3cd4e8b524ae9b8aa567858beed70c392fdec26dbdb0a8a418392e71708",
|
||||||
|
"sha256:166eac03e48784a6a6e0e5f041cfebb1ab400b394db188c48b3a84737f505b67",
|
||||||
|
"sha256:17ff94e7a83aa8671a25bf5b59326ec26da379ace2ebc4411d690d80a7fbcf23",
|
||||||
|
"sha256:1e12bdc622676ce47ae9abbf455c189e442afdde8818d9da983085df6312e7a1",
|
||||||
|
"sha256:21915eb821a6b3d9d8eefdaf57d6c345b970ad722f856cd71739493ce003ad08",
|
||||||
|
"sha256:288c6a76705dc54fba69fbcb59904ae4ad768b4c768839b8ca5fdadec6dd8cfd",
|
||||||
|
"sha256:2bde6792f313f4e918caabc46532aa64aa27a0db05d75b20edfc5c6f46479de2",
|
||||||
|
"sha256:32ca72bbc673adbcfecb935bb3fb1b74e663d10a4b241aaa2f5a75fe1d1f90aa",
|
||||||
|
"sha256:356b3576ad078c89a6107caa9c50cc14e98e3a6c4874a37c3e0273e4baf33de8",
|
||||||
|
"sha256:40b951f601af999a8bf2ce8c71e8aaa4e8c6f78ff8afae7b808aae2dc50d4c40",
|
||||||
|
"sha256:572e1787d1460da79590bf44304abbc0a2da944ea64ec549188fa84d89bba7ab",
|
||||||
|
"sha256:58df5c2a0e293bf665a51f8a100d3e9956febfbf1d9aaf8c0677cf70218910c6",
|
||||||
|
"sha256:64e6175c2e53195278d7388c454e0b30997573f3f4bd63697f88d855f7a6a1fc",
|
||||||
|
"sha256:7227b47e73dedaa513cdebb98469705ef0d66eb5a1250144468e9c3097d6b59b",
|
||||||
|
"sha256:7418b6bfc7fe3331541b84bb2141c9baf1ec7132a7ecd9f375912eca810e714e",
|
||||||
|
"sha256:7cbd7574ce8e138bda9df4efc6bf2ab8572c9aff640d8ecfece1b006b68da963",
|
||||||
|
"sha256:7ff61ff178250f9bb3cd89752df0f1dd0e27316a8bd1465351652b1b4a4cdfd3",
|
||||||
|
"sha256:833e1551925ed51e6b44c800e71e77dacd7e49181fdc9ac9a0bf3714d515785d",
|
||||||
|
"sha256:8639cadfda96737427330a094476d4c7a56ac03de7265622fcf4cfe57c8ae18d",
|
||||||
|
"sha256:8c5d5b35f789a030ebb95bff352f1d27a93d81069f2adb3182d99882e095cefe",
|
||||||
|
"sha256:8c790abda465726cfb8bb08bd4ca9a5d0a7bd77c7ac1ca1b839ad823b948ea28",
|
||||||
|
"sha256:8d2f1fb53a421b410751887eb4ff21386d119ef9cde3797bf5e7ed49fb51a3b3",
|
||||||
|
"sha256:903bbd302a2378f984aef528f76d4c9b1748f318fe1294961c072bdc7f2ffa3e",
|
||||||
|
"sha256:93f81b134a165cc17123626ab8da2e30c0455441d4ab5576eed73a64c025b25c",
|
||||||
|
"sha256:95e69877983ea39b7303570fa6760f81a3eec23d0e3ab2021b7144b94d06202d",
|
||||||
|
"sha256:9633b3034d3d901f0a46b7939f8c4d64427dfba6bbc5a36b1a67364cf148a1b0",
|
||||||
|
"sha256:97e5306482182170ade15c4b0d8386ded995a07d7cc2ca8f27958d34d6736497",
|
||||||
|
"sha256:9f3cba480d3deb69f6ee2c1825060177a22c7826431458c697df88e6aeb3caee",
|
||||||
|
"sha256:aa5b467f15e78b82257319aebc78dd2915e4c1436c3c0d1ad6f53e47ba6e2713",
|
||||||
|
"sha256:abb7a75ed8b968f3061327c433a0fbd17b729947b400747c334a9c29a9af6c58",
|
||||||
|
"sha256:aec52725173bd3a7b56fe91bc56eccb26fbdff1386ef123abb63c84c5b43b63a",
|
||||||
|
"sha256:b11548073a2213d950c3f671aa88e6f83cda6e2fb97a8b6317b1b5b33d850e06",
|
||||||
|
"sha256:b1692f7d6bc45e3200844be0dba153612103db241691088626a33ff1f24a0d88",
|
||||||
|
"sha256:b336501a05e13b616ef81ce329c0e09ac5ed8c732d9ba7e3e983fcc1a9e86965",
|
||||||
|
"sha256:b8c008de9d0daba7b6666aa5bbfdc23dcd78cafc33997c9b7741ff6353bafb7f",
|
||||||
|
"sha256:b92e29e58bef6d9cfd340c72b04d74c4b4e9f70c9fa7c78b674d1fec18896dc4",
|
||||||
|
"sha256:be5f425ff1f5f4b3c1e33ad64ab994eed12fc284a6ea71c5243fd564502ecbe5",
|
||||||
|
"sha256:dd0b1e9e891f69e7675ba5c92e28b90eaa045f6ab134ffe70b52e948aa175b3c",
|
||||||
|
"sha256:e30f5ea4ae2346e62cedde8794a56858a67b878dd79f7df76a0767e356b1744a",
|
||||||
|
"sha256:e6a36bb9474218c7a5b27ae476035497a6990e21d04c279884eb10d9b290f1b1",
|
||||||
|
"sha256:e859fcb4cbe93504ea18008d1df98dee4f7766db66c435e4882ab35cf70cac43",
|
||||||
|
"sha256:eb6ea6da4c787111adf40f697b4e58732ee0942b5d3bd8f435277643329ba627",
|
||||||
|
"sha256:ec8c433b3ab0419100bd45b47c9c8551248a5aee30ca5e9d399a0b57ac04651b",
|
||||||
|
"sha256:eff9d20417ff9dcb0d25e2defc2574d10b491bf2e693b4e491914738b7908168",
|
||||||
|
"sha256:f0214eb2a23b85528310dad848ad2ac58e735612929c8072f6093f3585fd342d",
|
||||||
|
"sha256:f276df9830dba7a333544bd41070e8175762a7ac20350786b322b714b0e654f5",
|
||||||
|
"sha256:f3acda1924472472ddd60c29e5b9db0cec629fbe3c5c5accb74d6d6d14773478",
|
||||||
|
"sha256:f70a9e237bb792c7cc7e44c531fd48f5897961701cdaa06cf22fc14965c496cf",
|
||||||
|
"sha256:f9d29ca8a77117315101425ec7ec2a47a22ccf59f5593378fc4077ac5b754fce",
|
||||||
|
"sha256:fa877ca7f6b48054f847b61d6fa7bed5cebb663ebc55e018fda12db09dcc664c",
|
||||||
|
"sha256:fdcec0b8399108577ec290f55551d926d9a1fa6cad45882093a7a07ac5ec147b"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3' and platform_machine == 'aarch64' or (platform_machine == 'ppc64le' or (platform_machine == 'x86_64' or (platform_machine == 'amd64' or (platform_machine == 'AMD64' or (platform_machine == 'win32' or platform_machine == 'WIN32')))))",
|
||||||
|
"version": "==1.1.2"
|
||||||
|
},
|
||||||
|
"h11": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:70813c1135087a248a4d38cc0e1a0181ffab2188141a93eaf567940c3957ff06",
|
||||||
|
"sha256:8ddd78563b633ca55346c8cd41ec0af27d3c79931828beffb46ce70a379e7442"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.6'",
|
||||||
|
"version": "==0.13.0"
|
||||||
|
},
|
||||||
|
"httptools": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:1a99346ebcb801b213c591540837340bdf6fd060a8687518d01c607d338b7424",
|
||||||
|
"sha256:1ee0b459257e222b878a6c09ccf233957d3a4dcb883b0847640af98d2d9aac23",
|
||||||
|
"sha256:20a45bcf22452a10fa8d58b7dbdb474381f6946bf5b8933e3662d572bc61bae4",
|
||||||
|
"sha256:29bf97a5c532da9c7a04de2c7a9c31d1d54f3abd65a464119b680206bbbb1055",
|
||||||
|
"sha256:2c9a930c378b3d15d6b695fb95ebcff81a7395b4f9775c4f10a076beb0b2c1ff",
|
||||||
|
"sha256:2db44a0b294d317199e9f80123e72c6b005c55b625b57fae36de68670090fa48",
|
||||||
|
"sha256:3194f6d6443befa8d4db16c1946b2fc428a3ceb8ab32eb6f09a59f86104dc1a0",
|
||||||
|
"sha256:34d2903dd2a3dd85d33705b6fde40bf91fc44411661283763fd0746723963c83",
|
||||||
|
"sha256:48e48530d9b995a84d1d89ae6b3ec4e59ea7d494b150ac3bbc5e2ac4acce92cd",
|
||||||
|
"sha256:54bbd295f031b866b9799dd39cb45deee81aca036c9bff9f58ca06726f6494f1",
|
||||||
|
"sha256:5d1fe6b6661022fd6cac541f54a4237496b246e6f1c0a6b41998ee08a1135afe",
|
||||||
|
"sha256:645373c070080e632480a3d251d892cb795be3d3a15f86975d0f1aca56fd230d",
|
||||||
|
"sha256:6a1a7dfc1f9c78a833e2c4904757a0f47ce25d08634dd2a52af394eefe5f9777",
|
||||||
|
"sha256:701e66b59dd21a32a274771238025d58db7e2b6ecebbab64ceff51b8e31527ae",
|
||||||
|
"sha256:72aa3fbe636b16d22e04b5a9d24711b043495e0ecfe58080addf23a1a37f3409",
|
||||||
|
"sha256:7af6bdbd21a2a25d6784f6d67f44f5df33ef39b6159543b9f9064d365c01f919",
|
||||||
|
"sha256:7ee9f226acab9085037582c059d66769862706e8e8cd2340470ceb8b3850873d",
|
||||||
|
"sha256:7f7bfb74718f52d5ed47d608d507bf66d3bc01d4a8b3e6dd7134daaae129357b",
|
||||||
|
"sha256:8e2eb957787cbb614a0f006bfc5798ff1d90ac7c4dd24854c84edbdc8c02369e",
|
||||||
|
"sha256:903f739c9fb78dab8970b0f3ea51f21955b24b45afa77b22ff0e172fc11ef111",
|
||||||
|
"sha256:98993805f1e3cdb53de4eed02b55dcc953cdf017ba7bbb2fd89226c086a6d855",
|
||||||
|
"sha256:9967d9758df505975913304c434cb9ab21e2c609ad859eb921f2f615a038c8de",
|
||||||
|
"sha256:a113789e53ac1fa26edf99856a61e4c493868e125ae0dd6354cf518948fbbd5c",
|
||||||
|
"sha256:a522d12e2ddbc2e91842ffb454a1aeb0d47607972c7d8fc88bd0838d97fb8a2a",
|
||||||
|
"sha256:abe829275cdd4174b4c4e65ad718715d449e308d59793bf3a931ee1bf7e7b86c",
|
||||||
|
"sha256:c286985b5e194ca0ebb2908d71464b9be8f17cc66d6d3e330e8d5407248f56ad",
|
||||||
|
"sha256:cd1295f52971097f757edfbfce827b6dbbfb0f7a74901ee7d4933dff5ad4c9af",
|
||||||
|
"sha256:ceafd5e960b39c7e0d160a1936b68eb87c5e79b3979d66e774f0c77d4d8faaed",
|
||||||
|
"sha256:d1f27bb0f75bef722d6e22dc609612bfa2f994541621cd2163f8c943b6463dfe",
|
||||||
|
"sha256:d3a4e165ca6204f34856b765d515d558dc84f1352033b8721e8d06c3e44930c3",
|
||||||
|
"sha256:d9b90bf58f3ba04e60321a23a8723a1ff2a9377502535e70495e5ada8e6e6722",
|
||||||
|
"sha256:f72b5d24d6730035128b238decdc4c0f2104b7056a7ca55cf047c106842ec890",
|
||||||
|
"sha256:fcddfe70553be717d9745990dfdb194e22ee0f60eb8f48c0794e7bfeda30d2d5",
|
||||||
|
"sha256:fdb9f9ed79bc6f46b021b3319184699ba1a22410a82204e6e89c774530069683"
|
||||||
|
],
|
||||||
|
"version": "==0.4.0"
|
||||||
|
},
|
||||||
|
"idna": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff",
|
||||||
|
"sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.5'",
|
||||||
|
"version": "==3.3"
|
||||||
|
},
|
||||||
|
"itsdangerous": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44",
|
||||||
|
"sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a"
|
||||||
|
],
|
||||||
|
"version": "==2.1.2"
|
||||||
|
},
|
||||||
|
"jinja2": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852",
|
||||||
|
"sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"
|
||||||
|
],
|
||||||
|
"version": "==3.1.2"
|
||||||
|
},
|
||||||
|
"markupsafe": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003",
|
||||||
|
"sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88",
|
||||||
|
"sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5",
|
||||||
|
"sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7",
|
||||||
|
"sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a",
|
||||||
|
"sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603",
|
||||||
|
"sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1",
|
||||||
|
"sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135",
|
||||||
|
"sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247",
|
||||||
|
"sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6",
|
||||||
|
"sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601",
|
||||||
|
"sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77",
|
||||||
|
"sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02",
|
||||||
|
"sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e",
|
||||||
|
"sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63",
|
||||||
|
"sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f",
|
||||||
|
"sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980",
|
||||||
|
"sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b",
|
||||||
|
"sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812",
|
||||||
|
"sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff",
|
||||||
|
"sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96",
|
||||||
|
"sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1",
|
||||||
|
"sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925",
|
||||||
|
"sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a",
|
||||||
|
"sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6",
|
||||||
|
"sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e",
|
||||||
|
"sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f",
|
||||||
|
"sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4",
|
||||||
|
"sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f",
|
||||||
|
"sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3",
|
||||||
|
"sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c",
|
||||||
|
"sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a",
|
||||||
|
"sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417",
|
||||||
|
"sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a",
|
||||||
|
"sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a",
|
||||||
|
"sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37",
|
||||||
|
"sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452",
|
||||||
|
"sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933",
|
||||||
|
"sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a",
|
||||||
|
"sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.7'",
|
||||||
|
"version": "==2.1.1"
|
||||||
|
},
|
||||||
|
"mysql-connector-python": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:047420715bbb51d3cba78de446c8a6db4666459cd23e168568009c620a3f5b90",
|
||||||
|
"sha256:1bef2a4a2b529c6e9c46414100ab7032c252244e8a9e017d2b6a41bb9cea9312",
|
||||||
|
"sha256:245087999f081b389d66621f2abfe2463e3927f63c7c4c0f70ce0f82786ccb93",
|
||||||
|
"sha256:29ec05ded856b4da4e47239f38489c03b31673ae0f46a090d0e4e29c670e6181",
|
||||||
|
"sha256:4de5959e27038cbd11dfccb1afaa2fd258c013e59d3e15709dd1992086103050",
|
||||||
|
"sha256:5eef51e48b22aadd633563bbdaf02112d98d954a4ead53f72fde283ea3f88152",
|
||||||
|
"sha256:6e2267ad75b37b5e1c480cde77cdc4f795427a54266ead30aabcdbf75ac70064",
|
||||||
|
"sha256:7be3aeff73b85eab3af2a1e80c053a98cbcb99e142192e551ebd4c1e41ce2596",
|
||||||
|
"sha256:895135cde57622edf48e1fce3beb4ed85f18332430d48f5c1d9630d49f7712b0",
|
||||||
|
"sha256:89597c091c4f25b6e023cbbcd32be73affbb0b44256761fe3b8e1d4b14d14d02",
|
||||||
|
"sha256:a7fd6a71df824f5a7d9a94060598d67b3a32eeccdc9837ee2cd98a44e2536cae",
|
||||||
|
"sha256:ab0e9d9b5fc114b78dfa9c74e8bfa30b48fcfa17dbb9241ad6faada08a589900",
|
||||||
|
"sha256:b7dccd7f72f19c97b58428ebf8e709e24eb7e9b67a408af7e77b60efde44bea4",
|
||||||
|
"sha256:bed43ea3a11f8d4e7c2e3f20c891214e68b45451314f91fddf9ca701de7a53ac",
|
||||||
|
"sha256:d5afb766b379111942d4260f29499f93355823c7241926471d843c9281fe477c",
|
||||||
|
"sha256:f353893481476a537cca7afd4e81e0ed84dd2173932b7f1721ab3e3351cbf324",
|
||||||
|
"sha256:fd608c288f596c4c8767d9a8e90f129385bd19ee6e3adaf6974ad8012c6138b8",
|
||||||
|
"sha256:fdd262d8538aa504475f8860cfda939a297d3b213c8d15f7ceed52508aeb2aa3"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==8.0.29"
|
||||||
|
},
|
||||||
|
"orjson": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:0c89b419914d3d1f65a1b0883f377abe42a6e44f6624ba1c63e8846cbfc2fa60",
|
||||||
|
"sha256:0db5c5a0c5b89f092d52f6e5a3701660a9d6ffa9e2968b3ce17c2bc4f5eb0414",
|
||||||
|
"sha256:137b539881c77866eba86ff6a11df910daf2eb9ab8f1acae62f879e83d7c38af",
|
||||||
|
"sha256:1a5fe569310bc819279bd4d5f2c349910b104ed3207936246dd5d5e0b085e74a",
|
||||||
|
"sha256:279f2d2af393fdf8601020744cb206b91b54ad60fb8401e0761819c7bda1f4e4",
|
||||||
|
"sha256:2cbd358f3b3ad539a27e36900e8e7d172d0e1b72ad9dd7d69544dcbc0f067ee7",
|
||||||
|
"sha256:32b6f26593a9eb606b40775826beb0dac152e3d224ea393688fced036045a821",
|
||||||
|
"sha256:33a82199fd42f6436f833e210ae5129c922a5c355629356ca7a8e82964da7285",
|
||||||
|
"sha256:3a287a650458de2211db03681b71c3e5cb2212b62f17a39df8ad99fc54855d0f",
|
||||||
|
"sha256:5204e25c12cea58e524fc82f7c27ed0586f592f777b33075a92ab7b3eb3687c2",
|
||||||
|
"sha256:656fbe15d9ef0733e740d9def78f4fdb4153102f4836ee774a05123499005931",
|
||||||
|
"sha256:6ab94701542d40b90903ecfc339333f458884979a01cb9268bc662cc67a5f6d8",
|
||||||
|
"sha256:77e8386393add64f959c044e0fb682364fd0e611a6f477aa13f0e6a733bd6a28",
|
||||||
|
"sha256:7be3be6153843e0f01351b1313a5ad4723595427680dac2dfff22a37e652ce02",
|
||||||
|
"sha256:81e1a6a2d67f15007dadacbf9ba5d3d79237e5e33786c028557fe5a2b72f1c9a",
|
||||||
|
"sha256:83a8424e857ae1bf53530e88b4eb2f16ca2b489073b924e655f1575cacd7f52a",
|
||||||
|
"sha256:90159ea8b9a5a2a98fa33dc7b421cfac4d2ae91ba5e1058f5909e7f059f6b467",
|
||||||
|
"sha256:9143ae2c52771525be9ad11a7a8cc8e7fd75391b107e7e644a9e0050496f6b4f",
|
||||||
|
"sha256:9d2b5e4cba9e774ac011071d9d27760f97f4b8cd46003e971d122e712f971345",
|
||||||
|
"sha256:a3dfec7950b90fb8d143743503ee53fa06b32e6068bdea792fc866284da3d71d",
|
||||||
|
"sha256:ab29c069c222248ce302a25855b4e1664f9436e8ae5a131fb0859daf31676d2b",
|
||||||
|
"sha256:afd9e329ebd3418cac3cd747769b1d52daa25fa672bbf414ab59f0e0881b32b9",
|
||||||
|
"sha256:b07c780f7345ecf5901356dc21dee0669defc489c38ce7b9ab0f5e008cc0385c",
|
||||||
|
"sha256:b890dbbada2cbb26eb29bd43a848426f007f094bb0758df10dfe7a438e1cb4b4",
|
||||||
|
"sha256:c311ec504414d22834d5b972a209619925b48263856a11a14d90230f9682d49c",
|
||||||
|
"sha256:c31c9f389be7906f978ed4192eb58a4b74a37ad60556a0b88ddc47c576697770",
|
||||||
|
"sha256:c5a3e382194c838988ec128a26b08aa92044e5e055491cc4056142af0c1c54d7",
|
||||||
|
"sha256:ccb356a47ab1067cd3549847e9db1d279a63fe0482d315b3ffd6e7abef35ef77",
|
||||||
|
"sha256:dd24f66b6697ee7424f7da575ec6cbffc8ede441114d53470949cda4d97c6e56",
|
||||||
|
"sha256:e19d23741c5de13689bb316abfccea15a19c264e3ec8eb332a5319a583595ace",
|
||||||
|
"sha256:ea32015a5d8a4ce00d348a0de5dc7040e0ad58f970a8fcbb5713a1eac129e493",
|
||||||
|
"sha256:eb22485847b9a0c4bbedc668df860126ac931edbed1d456cf41a59f3cb961ed8"
|
||||||
|
],
|
||||||
|
"version": "==3.6.8"
|
||||||
|
},
|
||||||
|
"passlib": {
|
||||||
|
"extras": [
|
||||||
|
"bcrypt"
|
||||||
|
],
|
||||||
|
"hashes": [
|
||||||
|
"sha256:aa6bca462b8d8bda89c70b382f0c298a20b5560af6cbfa2dce410c0a2fb669f1",
|
||||||
|
"sha256:defd50f72b65c5402ab2c573830a6978e5f202ad0d984793c8dde2c4152ebe04"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==1.7.4"
|
||||||
|
},
|
||||||
|
"protobuf": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:06059eb6953ff01e56a25cd02cca1a9649a75a7e65397b5b9b4e929ed71d10cf",
|
||||||
|
"sha256:097c5d8a9808302fb0da7e20edf0b8d4703274d140fd25c5edabddcde43e081f",
|
||||||
|
"sha256:284f86a6207c897542d7e956eb243a36bb8f9564c1742b253462386e96c6b78f",
|
||||||
|
"sha256:32ca378605b41fd180dfe4e14d3226386d8d1b002ab31c969c366549e66a2bb7",
|
||||||
|
"sha256:3cc797c9d15d7689ed507b165cd05913acb992d78b379f6014e013f9ecb20996",
|
||||||
|
"sha256:62f1b5c4cd6c5402b4e2d63804ba49a327e0c386c99b1675c8a0fefda23b2067",
|
||||||
|
"sha256:69ccfdf3657ba59569c64295b7d51325f91af586f8d5793b734260dfe2e94e2c",
|
||||||
|
"sha256:6f50601512a3d23625d8a85b1638d914a0970f17920ff39cec63aaef80a93fb7",
|
||||||
|
"sha256:7403941f6d0992d40161aa8bb23e12575637008a5a02283a930addc0508982f9",
|
||||||
|
"sha256:755f3aee41354ae395e104d62119cb223339a8f3276a0cd009ffabfcdd46bb0c",
|
||||||
|
"sha256:77053d28427a29987ca9caf7b72ccafee011257561259faba8dd308fda9a8739",
|
||||||
|
"sha256:7e371f10abe57cee5021797126c93479f59fccc9693dafd6bd5633ab67808a91",
|
||||||
|
"sha256:9016d01c91e8e625141d24ec1b20fed584703e527d28512aa8c8707f105a683c",
|
||||||
|
"sha256:9be73ad47579abc26c12024239d3540e6b765182a91dbc88e23658ab71767153",
|
||||||
|
"sha256:adc31566d027f45efe3f44eeb5b1f329da43891634d61c75a5944e9be6dd42c9",
|
||||||
|
"sha256:adfc6cf69c7f8c50fd24c793964eef18f0ac321315439d94945820612849c388",
|
||||||
|
"sha256:af0ebadc74e281a517141daad9d0f2c5d93ab78e9d455113719a45a49da9db4e",
|
||||||
|
"sha256:cb29edb9eab15742d791e1025dd7b6a8f6fcb53802ad2f6e3adcb102051063ab",
|
||||||
|
"sha256:cd68be2559e2a3b84f517fb029ee611546f7812b1fdd0aa2ecc9bc6ec0e4fdde",
|
||||||
|
"sha256:cdee09140e1cd184ba9324ec1df410e7147242b94b5f8b0c64fc89e38a8ba531",
|
||||||
|
"sha256:db977c4ca738dd9ce508557d4fce0f5aebd105e158c725beec86feb1f6bc20d8",
|
||||||
|
"sha256:dd5789b2948ca702c17027c84c2accb552fc30f4622a98ab5c51fcfe8c50d3e7",
|
||||||
|
"sha256:e250a42f15bf9d5b09fe1b293bdba2801cd520a9f5ea2d7fb7536d4441811d20",
|
||||||
|
"sha256:ff8d8fa42675249bb456f5db06c00de6c2f4c27a065955917b28c4f15978b9c3"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.7'",
|
||||||
|
"version": "==3.20.1"
|
||||||
|
},
|
||||||
|
"pyasn1": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:014c0e9976956a08139dc0712ae195324a75e142284d5f87f1a87ee1b068a359",
|
||||||
|
"sha256:03840c999ba71680a131cfaee6fab142e1ed9bbd9c693e285cc6aca0d555e576",
|
||||||
|
"sha256:0458773cfe65b153891ac249bcf1b5f8f320b7c2ce462151f8fa74de8934becf",
|
||||||
|
"sha256:08c3c53b75eaa48d71cf8c710312316392ed40899cb34710d092e96745a358b7",
|
||||||
|
"sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d",
|
||||||
|
"sha256:5c9414dcfede6e441f7e8f81b43b34e834731003427e5b09e4e00e3172a10f00",
|
||||||
|
"sha256:6e7545f1a61025a4e58bb336952c5061697da694db1cae97b116e9c46abcf7c8",
|
||||||
|
"sha256:78fa6da68ed2727915c4767bb386ab32cdba863caa7dbe473eaae45f9959da86",
|
||||||
|
"sha256:7ab8a544af125fb704feadb008c99a88805126fb525280b2270bb25cc1d78a12",
|
||||||
|
"sha256:99fcc3c8d804d1bc6d9a099921e39d827026409a58f2a720dcdb89374ea0c776",
|
||||||
|
"sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba",
|
||||||
|
"sha256:e89bf84b5437b532b0803ba5c9a5e054d21fec423a89952a74f87fa2c9b7bce2",
|
||||||
|
"sha256:fec3e9d8e36808a28efb59b489e4528c10ad0f480e57dcc32b4de5c9d8c9fdf3"
|
||||||
|
],
|
||||||
|
"version": "==0.4.8"
|
||||||
|
},
|
||||||
|
"pycparser": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9",
|
||||||
|
"sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"
|
||||||
|
],
|
||||||
|
"version": "==2.21"
|
||||||
|
},
|
||||||
|
"pydantic": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:085ca1de245782e9b46cefcf99deecc67d418737a1fd3f6a4f511344b613a5b3",
|
||||||
|
"sha256:086254884d10d3ba16da0588604ffdc5aab3f7f09557b998373e885c690dd398",
|
||||||
|
"sha256:0b6037175234850ffd094ca77bf60fb54b08b5b22bc85865331dd3bda7a02fa1",
|
||||||
|
"sha256:0fe476769acaa7fcddd17cadd172b156b53546ec3614a4d880e5d29ea5fbce65",
|
||||||
|
"sha256:1d5278bd9f0eee04a44c712982343103bba63507480bfd2fc2790fa70cd64cf4",
|
||||||
|
"sha256:2cc6a4cb8a118ffec2ca5fcb47afbacb4f16d0ab8b7350ddea5e8ef7bcc53a16",
|
||||||
|
"sha256:2ee7e3209db1e468341ef41fe263eb655f67f5c5a76c924044314e139a1103a2",
|
||||||
|
"sha256:3011b975c973819883842c5ab925a4e4298dffccf7782c55ec3580ed17dc464c",
|
||||||
|
"sha256:3c3b035103bd4e2e4a28da9da7ef2fa47b00ee4a9cf4f1a735214c1bcd05e0f6",
|
||||||
|
"sha256:4c68c3bc88dbda2a6805e9a142ce84782d3930f8fdd9655430d8576315ad97ce",
|
||||||
|
"sha256:574936363cd4b9eed8acdd6b80d0143162f2eb654d96cb3a8ee91d3e64bf4cf9",
|
||||||
|
"sha256:5a79330f8571faf71bf93667d3ee054609816f10a259a109a0738dac983b23c3",
|
||||||
|
"sha256:5e48ef4a8b8c066c4a31409d91d7ca372a774d0212da2787c0d32f8045b1e034",
|
||||||
|
"sha256:6c5b77947b9e85a54848343928b597b4f74fc364b70926b3c4441ff52620640c",
|
||||||
|
"sha256:742645059757a56ecd886faf4ed2441b9c0cd406079c2b4bee51bcc3fbcd510a",
|
||||||
|
"sha256:7bdfdadb5994b44bd5579cfa7c9b0e1b0e540c952d56f627eb227851cda9db77",
|
||||||
|
"sha256:815ddebb2792efd4bba5488bc8fde09c29e8ca3227d27cf1c6990fc830fd292b",
|
||||||
|
"sha256:8b5ac0f1c83d31b324e57a273da59197c83d1bb18171e512908fe5dc7278a1d6",
|
||||||
|
"sha256:96f240bce182ca7fe045c76bcebfa0b0534a1bf402ed05914a6f1dadff91877f",
|
||||||
|
"sha256:a733965f1a2b4090a5238d40d983dcd78f3ecea221c7af1497b845a9709c1721",
|
||||||
|
"sha256:ab624700dc145aa809e6f3ec93fb8e7d0f99d9023b713f6a953637429b437d37",
|
||||||
|
"sha256:b2571db88c636d862b35090ccf92bf24004393f85c8870a37f42d9f23d13e032",
|
||||||
|
"sha256:bbbc94d0c94dd80b3340fc4f04fd4d701f4b038ebad72c39693c794fd3bc2d9d",
|
||||||
|
"sha256:c0727bda6e38144d464daec31dff936a82917f431d9c39c39c60a26567eae3ed",
|
||||||
|
"sha256:c556695b699f648c58373b542534308922c46a1cda06ea47bc9ca45ef5b39ae6",
|
||||||
|
"sha256:c86229333cabaaa8c51cf971496f10318c4734cf7b641f08af0a6fbf17ca3054",
|
||||||
|
"sha256:c8d7da6f1c1049eefb718d43d99ad73100c958a5367d30b9321b092771e96c25",
|
||||||
|
"sha256:c8e9dcf1ac499679aceedac7e7ca6d8641f0193c591a2d090282aaf8e9445a46",
|
||||||
|
"sha256:cb23bcc093697cdea2708baae4f9ba0e972960a835af22560f6ae4e7e47d33f5",
|
||||||
|
"sha256:d1e4c28f30e767fd07f2ddc6f74f41f034d1dd6bc526cd59e63a82fe8bb9ef4c",
|
||||||
|
"sha256:d9c9bdb3af48e242838f9f6e6127de9be7063aad17b32215ccc36a09c5cf1070",
|
||||||
|
"sha256:dee5ef83a76ac31ab0c78c10bd7d5437bfdb6358c95b91f1ba7ff7b76f9996a1",
|
||||||
|
"sha256:e0896200b6a40197405af18828da49f067c2fa1f821491bc8f5bde241ef3f7d7",
|
||||||
|
"sha256:f5a64b64ddf4c99fe201ac2724daada8595ada0d102ab96d019c1555c2d6441d",
|
||||||
|
"sha256:f947352c3434e8b937e3aa8f96f47bdfe6d92779e44bb3f41e4c213ba6a32145"
|
||||||
|
],
|
||||||
|
"markers": "python_full_version >= '3.6.1'",
|
||||||
|
"version": "==1.9.0"
|
||||||
|
},
|
||||||
|
"python-dotenv": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:b7e3b04a59693c42c36f9ab1cc2acc46fa5df8c78e178fc33a8d4cd05c8d498f",
|
||||||
|
"sha256:d92a187be61fe482e4fd675b6d52200e7be63a12b724abbf931a40ce4fa92938"
|
||||||
|
],
|
||||||
|
"version": "==0.20.0"
|
||||||
|
},
|
||||||
|
"python-jose": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:55779b5e6ad599c6336191246e95eb2293a9ddebd555f796a65f838f07e5d78a",
|
||||||
|
"sha256:9b1376b023f8b298536eedd47ae1089bcdb848f1535ab30555cd92002d78923a"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==3.3.0"
|
||||||
|
},
|
||||||
|
"python-multipart": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:f7bb5f611fc600d15fa47b3974c8aa16e93724513b49b5f95c81e6624c83fa43"
|
||||||
|
],
|
||||||
|
"version": "==0.0.5"
|
||||||
|
},
|
||||||
|
"pyyaml": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293",
|
||||||
|
"sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b",
|
||||||
|
"sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57",
|
||||||
|
"sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b",
|
||||||
|
"sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4",
|
||||||
|
"sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07",
|
||||||
|
"sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba",
|
||||||
|
"sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9",
|
||||||
|
"sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287",
|
||||||
|
"sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513",
|
||||||
|
"sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0",
|
||||||
|
"sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0",
|
||||||
|
"sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92",
|
||||||
|
"sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f",
|
||||||
|
"sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2",
|
||||||
|
"sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc",
|
||||||
|
"sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c",
|
||||||
|
"sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86",
|
||||||
|
"sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4",
|
||||||
|
"sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c",
|
||||||
|
"sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34",
|
||||||
|
"sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b",
|
||||||
|
"sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c",
|
||||||
|
"sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb",
|
||||||
|
"sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737",
|
||||||
|
"sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3",
|
||||||
|
"sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d",
|
||||||
|
"sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53",
|
||||||
|
"sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78",
|
||||||
|
"sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803",
|
||||||
|
"sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a",
|
||||||
|
"sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174",
|
||||||
|
"sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"
|
||||||
|
],
|
||||||
|
"version": "==6.0"
|
||||||
|
},
|
||||||
|
"requests": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61",
|
||||||
|
"sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d"
|
||||||
|
],
|
||||||
|
"version": "==2.27.1"
|
||||||
|
},
|
||||||
|
"rsa": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:5c6bd9dc7a543b7fe4304a631f8a8a3b674e2bbfc49c2ae96200cdbe55df6b17",
|
||||||
|
"sha256:95c5d300c4e879ee69708c428ba566c59478fd653cc3a22243eeb8ed846950bb"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.6' and python_version < '4'",
|
||||||
|
"version": "==4.8"
|
||||||
|
},
|
||||||
|
"six": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
|
||||||
|
"sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||||
|
"version": "==1.16.0"
|
||||||
|
},
|
||||||
|
"sniffio": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:471b71698eac1c2112a40ce2752bb2f4a4814c22a54a3eed3676bc0f5ca9f663",
|
||||||
|
"sha256:c4666eecec1d3f50960c6bdf61ab7bc350648da6c126e3cf6898d8cd4ddcd3de"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.5'",
|
||||||
|
"version": "==1.2.0"
|
||||||
|
},
|
||||||
|
"sqlalchemy": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:09c606d8238feae2f360b8742ffbe67741937eb0a05b57f536948d198a3def96",
|
||||||
|
"sha256:166a3887ec355f7d2f12738f7fa25dc8ac541867147a255f790f2f41f614cb44",
|
||||||
|
"sha256:16abf35af37a3d5af92725fc9ec507dd9e9183d261c2069b6606d60981ed1c6e",
|
||||||
|
"sha256:2e885548da361aa3f8a9433db4cfb335b2107e533bf314359ae3952821d84b3e",
|
||||||
|
"sha256:2ec89bf98cc6a0f5d1e28e3ad28e9be6f3b4bdbd521a4053c7ae8d5e1289a8a1",
|
||||||
|
"sha256:2ecac4db8c1aa4a269f5829df7e706639a24b780d2ac46b3e485cbbd27ec0028",
|
||||||
|
"sha256:316c7e5304dda3e3ad711569ac5d02698bbc71299b168ac56a7076b86259f7ea",
|
||||||
|
"sha256:5041474dcab7973baa91ec1f3112049a9dd4652898d6a95a6a895ff5c58beb6b",
|
||||||
|
"sha256:53d2d9ee93970c969bc4e3c78b1277d7129554642f6ffea039c282c7dc4577bc",
|
||||||
|
"sha256:5864a83bd345871ad9699ce466388f836db7572003d67d9392a71998092210e3",
|
||||||
|
"sha256:5c90ef955d429966d84326d772eb34333178737ebb669845f1d529eb00c75e72",
|
||||||
|
"sha256:5d50cb71c1dbed70646d521a0975fb0f92b7c3f84c61fa59e07be23a1aaeecfc",
|
||||||
|
"sha256:64678ac321d64a45901ef2e24725ec5e783f1f4a588305e196431447e7ace243",
|
||||||
|
"sha256:64d796e9af522162f7f2bf7a3c5531a0a550764c426782797bbeed809d0646c5",
|
||||||
|
"sha256:6cb4c4f57a20710cea277edf720d249d514e587f796b75785ad2c25e1c0fed26",
|
||||||
|
"sha256:6e1fe00ee85c768807f2a139b83469c1e52a9ffd58a6eb51aa7aeb524325ab18",
|
||||||
|
"sha256:6e859fa96605027bd50d8e966db1c4e1b03e7b3267abbc4b89ae658c99393c58",
|
||||||
|
"sha256:7a052bd9f53004f8993c624c452dfad8ec600f572dd0ed0445fbe64b22f5570e",
|
||||||
|
"sha256:81e53bd383c2c33de9d578bfcc243f559bd3801a0e57f2bcc9a943c790662e0c",
|
||||||
|
"sha256:83cf3077712be9f65c9aaa0b5bc47bc1a44789fd45053e2e3ecd59ff17c63fe9",
|
||||||
|
"sha256:8b20c4178ead9bc398be479428568ff31b6c296eb22e75776273781a6551973f",
|
||||||
|
"sha256:8d07fe2de0325d06e7e73281e9a9b5e259fbd7cbfbe398a0433cbb0082ad8fa7",
|
||||||
|
"sha256:a0ae3aa2e86a4613f2d4c49eb7da23da536e6ce80b2bfd60bbb2f55fc02b0b32",
|
||||||
|
"sha256:af2587ae11400157753115612d6c6ad255143efba791406ad8a0cbcccf2edcb3",
|
||||||
|
"sha256:b3db741beaa983d4cbf9087558620e7787106319f7e63a066990a70657dd6b35",
|
||||||
|
"sha256:be094460930087e50fd08297db9d7aadaed8408ad896baf758e9190c335632da",
|
||||||
|
"sha256:cb441ca461bf97d00877b607f132772644b623518b39ced54da433215adce691",
|
||||||
|
"sha256:ce20f5da141f8af26c123ebaa1b7771835ca6c161225ce728962a79054f528c3",
|
||||||
|
"sha256:d57ac32f8dc731fddeb6f5d1358b4ca5456e72594e664769f0a9163f13df2a31",
|
||||||
|
"sha256:dce3468bf1fc12374a1a732c9efd146ce034f91bb0482b602a9311cb6166a920",
|
||||||
|
"sha256:e12532c4d3f614678623da5d852f038ace1f01869b89f003ed6fe8c793f0c6a3",
|
||||||
|
"sha256:e74ce103b81c375c3853b436297952ef8d7863d801dcffb6728d01544e5191b5",
|
||||||
|
"sha256:f0394a3acfb8925db178f7728adb38c027ed7e303665b225906bfa8099dc1ce8",
|
||||||
|
"sha256:f522214f6749bc073262529c056f7dfd660f3b5ec4180c5354d985eb7219801e",
|
||||||
|
"sha256:fbf8c09fe9728168f8cc1b40c239eab10baf9c422c18be7f53213d70434dea43",
|
||||||
|
"sha256:fca8322e04b2dde722fcb0558682740eebd3bd239bea7a0d0febbc190e99dc15"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==1.4.36"
|
||||||
|
},
|
||||||
|
"starlette": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:377d64737a0e03560cb8eaa57604afee143cea5a4996933242798a7820e64f53",
|
||||||
|
"sha256:b45c6e9a617ecb5caf7e6446bd8d767b0084d6217e8e1b08187ca5191e10f097"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.6'",
|
||||||
|
"version": "==0.18.0"
|
||||||
|
},
|
||||||
|
"typing-extensions": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:6657594ee297170d19f67d55c05852a874e7eb634f4f753dbd667855e07c1708",
|
||||||
|
"sha256:f1c24655a0da0d1b67f07e17a5e6b2a105894e6824b92096378bb3668ef02376"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.7'",
|
||||||
|
"version": "==4.2.0"
|
||||||
|
},
|
||||||
|
"ujson": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:04a8c388b2d16316df3365c81f368955662581f6a4ff033e9aba2dd1ffc9e05e",
|
||||||
|
"sha256:080da13f81740c076e5f16c254a10d0e32f45d225a5e6b0687a86493cfcfbafb",
|
||||||
|
"sha256:0b47a138203bb06bdac03b2a89ac9b2993fd32cb7daded06c966dd84300a5786",
|
||||||
|
"sha256:102b8eb5e15e6c5537426414d180c28dbf0489e51f7c22b706511ac84aae4458",
|
||||||
|
"sha256:11f735870f189bff1841c720115226894415ab6a7796dee8ab46bc767ea2e743",
|
||||||
|
"sha256:163191b88842d874e081707d35de2e205e0e396e70fd068d1038879bca8b17ad",
|
||||||
|
"sha256:25522c674b35c33f375586ac98d92ce731e79059424507ecbccbfcbce832d597",
|
||||||
|
"sha256:27a254a150e46980608b16ef3b609e703173492cfa738f4644c81d7e7d77494c",
|
||||||
|
"sha256:2c04456de1fc92cc7062904c176c74e6ea220469b949508be42e819646a28457",
|
||||||
|
"sha256:2c7712da662b92f80442a8efc0df09cea3a5efb42b0dd6a642e36b1b40a260d4",
|
||||||
|
"sha256:350a3010db0045e1306bbdf889d1bdaee9bb095856c317716f0a74108cf4afe9",
|
||||||
|
"sha256:468d7d8dcbafc3fd40cc73e4a533a7a1d4f935f605c15ae6cac32c6d53c4c6aa",
|
||||||
|
"sha256:489d495431c80dc0048c4551a0d6cdbf1209e2d274f47c3f72415c91842eeb68",
|
||||||
|
"sha256:49ce8521b0cdf210481bd89887fd1bd0a975f66088b1256dafc77c67c8ccb89d",
|
||||||
|
"sha256:4d1ed3897e45477b2a4a1371186df299b13938d4d44d850953a4bb0ea4cb38f3",
|
||||||
|
"sha256:54ee7c46615b42f7ae9dca90f54d204a4d2041a4c926b08fffa953aa3a246e54",
|
||||||
|
"sha256:584c558c23ddc21f5b07d2c54ee527731bd9716101c27829023ab7f3ffbaa8fc",
|
||||||
|
"sha256:6227597d0201ceadc902d1a8edaffaeb244050b197368ed25e6f6be0df170a6f",
|
||||||
|
"sha256:6677bee8690c71f5e6cf519a6d8400f04fbd3ff9f6c50f35f1b664bc94546f84",
|
||||||
|
"sha256:6b455a62bd20e890b2124a65df45313b4292dbea851ef38574e5e2de94691ad5",
|
||||||
|
"sha256:6c5bbe6de6c9a5fe8dca56e36fb5c4a42e1a01d4aae1ac20cd8d7d82ccff9430",
|
||||||
|
"sha256:729af63e4de30c54b527b54b4100266f79833c1e8ba35e784f01b44c2aca88d8",
|
||||||
|
"sha256:754e9da96a24535ae5ab2a52e1d1dfc65a6a717c14063855b83f327fdf2173ea",
|
||||||
|
"sha256:75a886bd89d8e5a004a39a6c5dc8a43bb7fcf05129d2dccd16a59602a612823a",
|
||||||
|
"sha256:8c3f7578a62d9255650ef32e78d3345e98262e064c9ba3f205311b4c9eb507a6",
|
||||||
|
"sha256:90de04391916c5adc7bbcc69bd778e263ed45cc83c070099cb07ed25068d6a12",
|
||||||
|
"sha256:940f35e9a0969440621445dbb6adffaa2cea77d0262abc74fce78704120c4534",
|
||||||
|
"sha256:9acc874128baddeff908736db251597e4cbd007a384730377a59a61b08886599",
|
||||||
|
"sha256:a1a55b3310632661a03ce68ccfb92264031aea21626d6fa5c8f6c32e769be7b6",
|
||||||
|
"sha256:a3c6798035b574ceba747de83f3223a622622b7ab77a24f8b4fbea2cb92f14b0",
|
||||||
|
"sha256:a5e374e793b0a3c7df20ee4c8234e89859ddb2b2821cc3300ae94ab5b08fa6d0",
|
||||||
|
"sha256:a6f3ad3b11578bc4e25d5bd256c938fe2c7c015d8f504bc7835f127ed26a0818",
|
||||||
|
"sha256:b3671e1dfc49a4b4453d89fd7438aa9d7cca28afe329c70eba84e2a5778dbf3f",
|
||||||
|
"sha256:b5fcbaabf3d115cb816eb165f3fa5de5c5bc795473a554ae55620d134ddf2d36",
|
||||||
|
"sha256:bc1a619bad9894dad144184b735c98179c7d92d7b40fbda28eb8b0857bdfdf52",
|
||||||
|
"sha256:be909514a47b6272e34cd1213feee324ca35a354e07f1ae3aba12d3694a5279f",
|
||||||
|
"sha256:c519743a53bbe8aac6b743bcf50eb83057d1e0341e1ca8f8491f729a885af640",
|
||||||
|
"sha256:c549d5a7652c3a0dd00ef6ff910fb01878bc116c66c94ac455a55cffa32cc229",
|
||||||
|
"sha256:d1e5c635b7c3465ab8d2e3dc97c341ef1801c53a378f1d1d4cb934f6c90ec66c",
|
||||||
|
"sha256:d2357ce7d93eadd29b6efbe72228809948cc59ec6682c20fa6de08aeef1703f8",
|
||||||
|
"sha256:d38c2a58c892c680080b22b59eebd77b7c6f4ae24361111fba115f9ed3651dcf",
|
||||||
|
"sha256:d57a87bbc77d66b8a2b74bab66357c3bb6194f5d248f1053fb8044787abde73f",
|
||||||
|
"sha256:d9b1c3d2b22c040a81ff4e5927ce307919f7ac8bf888afded714d925edc8d0a4",
|
||||||
|
"sha256:dc5fd1d5b48edd3cc64e89ea94abe231509fdc938bdeafafe9aef3a05810159f",
|
||||||
|
"sha256:dc71ead5706e81fdf1054c8c11e4aaab43527da450a2701213c20717852d1a51",
|
||||||
|
"sha256:e53388fb092197cb8f956673792aca994872917d897ca42a0abf7a35e293575a",
|
||||||
|
"sha256:e991b7b3a08ac9e9d3a51589ef1c359c8d44ece730351cfac055684bf3787372",
|
||||||
|
"sha256:ed78a5b169ece75a1e1368935ce6ab051dcbcd5c158b9796b2f1fa6cc467a651",
|
||||||
|
"sha256:ef868bf01851869a26c0ca5f88036903836c3a6b463c74d96b37f294f6bdeea4",
|
||||||
|
"sha256:fb4555df1fe018806ba14cc38786269c8e213930103c6d0ac81e506d09d1de7e"
|
||||||
|
],
|
||||||
|
"version": "==5.2.0"
|
||||||
|
},
|
||||||
|
"urllib3": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:44ece4d53fb1706f667c9bd1c648f5469a2ec925fcf3a776667042d645472c14",
|
||||||
|
"sha256:aabaf16477806a5e1dd19aa41f8c2b7950dd3c746362d7e3223dbe6de6ac448e"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'",
|
||||||
|
"version": "==1.26.9"
|
||||||
|
},
|
||||||
|
"uvicorn": {
|
||||||
|
"extras": [
|
||||||
|
"standard"
|
||||||
|
],
|
||||||
|
"hashes": [
|
||||||
|
"sha256:19e2a0e96c9ac5581c01eb1a79a7d2f72bb479691acd2b8921fce48ed5b961a6",
|
||||||
|
"sha256:5180f9d059611747d841a4a4c4ab675edf54c8489e97f96d0583ee90ac3bfc23"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==0.17.6"
|
||||||
|
},
|
||||||
|
"uvloop": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:04ff57aa137230d8cc968f03481176041ae789308b4d5079118331ab01112450",
|
||||||
|
"sha256:089b4834fd299d82d83a25e3335372f12117a7d38525217c2258e9b9f4578897",
|
||||||
|
"sha256:1e5f2e2ff51aefe6c19ee98af12b4ae61f5be456cd24396953244a30880ad861",
|
||||||
|
"sha256:30ba9dcbd0965f5c812b7c2112a1ddf60cf904c1c160f398e7eed3a6b82dcd9c",
|
||||||
|
"sha256:3a19828c4f15687675ea912cc28bbcb48e9bb907c801873bd1519b96b04fb805",
|
||||||
|
"sha256:6224f1401025b748ffecb7a6e2652b17768f30b1a6a3f7b44660e5b5b690b12d",
|
||||||
|
"sha256:647e481940379eebd314c00440314c81ea547aa636056f554d491e40503c8464",
|
||||||
|
"sha256:6ccd57ae8db17d677e9e06192e9c9ec4bd2066b77790f9aa7dede2cc4008ee8f",
|
||||||
|
"sha256:772206116b9b57cd625c8a88f2413df2fcfd0b496eb188b82a43bed7af2c2ec9",
|
||||||
|
"sha256:8e0d26fa5875d43ddbb0d9d79a447d2ace4180d9e3239788208527c4784f7cab",
|
||||||
|
"sha256:98d117332cc9e5ea8dfdc2b28b0a23f60370d02e1395f88f40d1effd2cb86c4f",
|
||||||
|
"sha256:b572256409f194521a9895aef274cea88731d14732343da3ecdb175228881638",
|
||||||
|
"sha256:bd53f7f5db562f37cd64a3af5012df8cac2c464c97e732ed556800129505bd64",
|
||||||
|
"sha256:bd8f42ea1ea8f4e84d265769089964ddda95eb2bb38b5cbe26712b0616c3edee",
|
||||||
|
"sha256:e814ac2c6f9daf4c36eb8e85266859f42174a4ff0d71b99405ed559257750382",
|
||||||
|
"sha256:f74bc20c7b67d1c27c72601c78cf95be99d5c2cdd4514502b4f3eb0933ff1228"
|
||||||
|
],
|
||||||
|
"version": "==0.16.0"
|
||||||
|
},
|
||||||
|
"watchgod": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:2f3e8137d98f493ff58af54ea00f4d1433a6afe2ed08ab331a657df468c6bfce",
|
||||||
|
"sha256:cb11ff66657befba94d828e3b622d5fb76f22fbda1376f355f3e6e51e97d9450"
|
||||||
|
],
|
||||||
|
"version": "==0.8.2"
|
||||||
|
},
|
||||||
|
"websockets": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:07cdc0a5b2549bcfbadb585ad8471ebdc7bdf91e32e34ae3889001c1c106a6af",
|
||||||
|
"sha256:210aad7fdd381c52e58777560860c7e6110b6174488ef1d4b681c08b68bf7f8c",
|
||||||
|
"sha256:28dd20b938a57c3124028680dc1600c197294da5db4292c76a0b48efb3ed7f76",
|
||||||
|
"sha256:2f94fa3ae454a63ea3a19f73b95deeebc9f02ba2d5617ca16f0bbdae375cda47",
|
||||||
|
"sha256:31564a67c3e4005f27815634343df688b25705cccb22bc1db621c781ddc64c69",
|
||||||
|
"sha256:347974105bbd4ea068106ec65e8e8ebd86f28c19e529d115d89bd8cc5cda3079",
|
||||||
|
"sha256:379e03422178436af4f3abe0aa8f401aa77ae2487843738542a75faf44a31f0c",
|
||||||
|
"sha256:3eda1cb7e9da1b22588cefff09f0951771d6ee9fa8dbe66f5ae04cc5f26b2b55",
|
||||||
|
"sha256:51695d3b199cd03098ae5b42833006a0f43dc5418d3102972addc593a783bc02",
|
||||||
|
"sha256:54c000abeaff6d8771a4e2cef40900919908ea7b6b6a30eae72752607c6db559",
|
||||||
|
"sha256:5b936bf552e4f6357f5727579072ff1e1324717902127ffe60c92d29b67b7be3",
|
||||||
|
"sha256:6075fd24df23133c1b078e08a9b04a3bc40b31a8def4ee0b9f2c8865acce913e",
|
||||||
|
"sha256:661f641b44ed315556a2fa630239adfd77bd1b11cb0b9d96ed8ad90b0b1e4978",
|
||||||
|
"sha256:6ea6b300a6bdd782e49922d690e11c3669828fe36fc2471408c58b93b5535a98",
|
||||||
|
"sha256:6ed1d6f791eabfd9808afea1e068f5e59418e55721db8b7f3bfc39dc831c42ae",
|
||||||
|
"sha256:7934e055fd5cd9dee60f11d16c8d79c4567315824bacb1246d0208a47eca9755",
|
||||||
|
"sha256:7ab36e17af592eec5747c68ef2722a74c1a4a70f3772bc661079baf4ae30e40d",
|
||||||
|
"sha256:7f6d96fdb0975044fdd7953b35d003b03f9e2bcf85f2d2cf86285ece53e9f991",
|
||||||
|
"sha256:83e5ca0d5b743cde3d29fda74ccab37bdd0911f25bd4cdf09ff8b51b7b4f2fa1",
|
||||||
|
"sha256:85506b3328a9e083cc0a0fb3ba27e33c8db78341b3eb12eb72e8afd166c36680",
|
||||||
|
"sha256:8af75085b4bc0b5c40c4a3c0e113fa95e84c60f4ed6786cbb675aeb1ee128247",
|
||||||
|
"sha256:8b1359aba0ff810d5830d5ab8e2c4a02bebf98a60aa0124fb29aa78cfdb8031f",
|
||||||
|
"sha256:8fbd7d77f8aba46d43245e86dd91a8970eac4fb74c473f8e30e9c07581f852b2",
|
||||||
|
"sha256:907e8247480f287aa9bbc9391bd6de23c906d48af54c8c421df84655eef66af7",
|
||||||
|
"sha256:93d5ea0b5da8d66d868b32c614d2b52d14304444e39e13a59566d4acb8d6e2e4",
|
||||||
|
"sha256:97bc9d41e69a7521a358f9b8e44871f6cdeb42af31815c17aed36372d4eec667",
|
||||||
|
"sha256:994cdb1942a7a4c2e10098d9162948c9e7b235df755de91ca33f6e0481366fdb",
|
||||||
|
"sha256:a141de3d5a92188234afa61653ed0bbd2dde46ad47b15c3042ffb89548e77094",
|
||||||
|
"sha256:a1e15b230c3613e8ea82c9fc6941b2093e8eb939dd794c02754d33980ba81e36",
|
||||||
|
"sha256:aad5e300ab32036eb3fdc350ad30877210e2f51bceaca83fb7fef4d2b6c72b79",
|
||||||
|
"sha256:b529fdfa881b69fe563dbd98acce84f3e5a67df13de415e143ef053ff006d500",
|
||||||
|
"sha256:b9c77f0d1436ea4b4dc089ed8335fa141e6a251a92f75f675056dac4ab47a71e",
|
||||||
|
"sha256:bb621ec2dbbbe8df78a27dbd9dd7919f9b7d32a73fafcb4d9252fc4637343582",
|
||||||
|
"sha256:c7250848ce69559756ad0086a37b82c986cd33c2d344ab87fea596c5ac6d9442",
|
||||||
|
"sha256:c8d1d14aa0f600b5be363077b621b1b4d1eb3fbf90af83f9281cda668e6ff7fd",
|
||||||
|
"sha256:d1655a6fc7aecd333b079d00fb3c8132d18988e47f19740c69303bf02e9883c6",
|
||||||
|
"sha256:d6353ba89cfc657a3f5beabb3b69be226adbb5c6c7a66398e17809b0ce3c4731",
|
||||||
|
"sha256:da4377904a3379f0c1b75a965fff23b28315bcd516d27f99a803720dfebd94d4",
|
||||||
|
"sha256:e49ea4c1a9543d2bd8a747ff24411509c29e4bdcde05b5b0895e2120cb1a761d",
|
||||||
|
"sha256:e4e08305bfd76ba8edab08dcc6496f40674f44eb9d5e23153efa0a35750337e8",
|
||||||
|
"sha256:e6fa05a680e35d0fcc1470cb070b10e6fe247af54768f488ed93542e71339d6f",
|
||||||
|
"sha256:e7e6f2d6fd48422071cc8a6f8542016f350b79cc782752de531577d35e9bd677",
|
||||||
|
"sha256:e904c0381c014b914136c492c8fa711ca4cced4e9b3d110e5e7d436d0fc289e8",
|
||||||
|
"sha256:ec2b0ab7edc8cd4b0eb428b38ed89079bdc20c6bdb5f889d353011038caac2f9",
|
||||||
|
"sha256:ef5ce841e102278c1c2e98f043db99d6755b1c58bde475516aef3a008ed7f28e",
|
||||||
|
"sha256:f351c7d7d92f67c0609329ab2735eee0426a03022771b00102816a72715bb00b",
|
||||||
|
"sha256:fab7c640815812ed5f10fbee7abbf58788d602046b7bb3af9b1ac753a6d5e916",
|
||||||
|
"sha256:fc06cc8073c8e87072138ba1e431300e2d408f054b27047d047b549455066ff4"
|
||||||
|
],
|
||||||
|
"version": "==10.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"develop": {}
|
||||||
|
}
|
||||||
189
back/crud.py
Normal file
189
back/crud.py
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
from database import connection
|
||||||
|
|
||||||
|
def get_books(page, limit=50, order="Auteur", search=""):
|
||||||
|
"""
|
||||||
|
:param limit: Item limit
|
||||||
|
:type limit: int
|
||||||
|
:param page: current page
|
||||||
|
:type page: int
|
||||||
|
:param order: Book fields are : Auteur, Titre, Editeur, Type
|
||||||
|
:type order: str
|
||||||
|
"""
|
||||||
|
cursor = connection.cursor()
|
||||||
|
connection.ping(reconnect=True)
|
||||||
|
if search == "":
|
||||||
|
t = f"""
|
||||||
|
SELECT * from Books order by %s asc LIMIT %s OFFSET %s;
|
||||||
|
"""
|
||||||
|
nb = f"""
|
||||||
|
SELECT COUNT(*) as count from Books order by %s asc;
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
cursor.execute(t, (order, limit, limit * page))
|
||||||
|
r = cursor.fetchall()
|
||||||
|
cursor.execute(nb, (order))
|
||||||
|
n = cursor.fetchall()
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
search = search.replace(' ', ', ')
|
||||||
|
t = f"""
|
||||||
|
SELECT * FROM `Books`
|
||||||
|
WHERE `Titre` LIKE %s
|
||||||
|
OR `Auteur` LIKE %s
|
||||||
|
OR `Editeur` LIKE %s
|
||||||
|
OR `Type` LIKE %s
|
||||||
|
ORDER BY %s asc LIMIT %s OFFSET %s;
|
||||||
|
"""
|
||||||
|
nb = f"""
|
||||||
|
SELECT COUNT(*) as count from `Books`
|
||||||
|
WHERE `Titre` LIKE %s
|
||||||
|
OR `Auteur` LIKE %s
|
||||||
|
OR `Editeur` LIKE %s
|
||||||
|
OR `Type` LIKE %s
|
||||||
|
ORDER BY %s asc;
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
cursor.execute(t, (f'%{search}%', f'%{search}%', f'%{search}%', f'%{search}%', order, limit, limit * page))
|
||||||
|
r = cursor.fetchall()
|
||||||
|
cursor.execute(nb, (f'%{search}%', f'%{search}%', f'%{search}%', f'%{search}%', order))
|
||||||
|
n = cursor.fetchall()
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
return False
|
||||||
|
return r, n
|
||||||
|
|
||||||
|
def remove_book(id):
|
||||||
|
cursor = connection.cursor()
|
||||||
|
connection.ping(reconnect=True)
|
||||||
|
t = f"""
|
||||||
|
DELETE FROM Books where biblio_Index=%s;
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
cursor.execute(t, (id))
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
connection.commit()
|
||||||
|
return True
|
||||||
|
|
||||||
|
def edit_book(id, newContent):
|
||||||
|
"""
|
||||||
|
:param id: item to update
|
||||||
|
:type id: str
|
||||||
|
:param newContent: New values to update
|
||||||
|
:type newContent: dict
|
||||||
|
"""
|
||||||
|
cursor = connection.cursor()
|
||||||
|
connection.ping(reconnect=True)
|
||||||
|
t = f"""
|
||||||
|
UPDATE Books
|
||||||
|
SET Auteur=%s, Titre=%s, Editeur=%s, Type=%s
|
||||||
|
WHERE biblio_Index=%s;
|
||||||
|
"""
|
||||||
|
print(newContent)
|
||||||
|
try:
|
||||||
|
cursor.execute(t, (newContent['author'], newContent['title'], newContent['editor'], newContent['type'], id))
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
connection.commit()
|
||||||
|
return True
|
||||||
|
|
||||||
|
def add_book(newContent):
|
||||||
|
"""
|
||||||
|
:param newContent: New values to add
|
||||||
|
:type newContent: dict
|
||||||
|
"""
|
||||||
|
connection.ping(reconnect=True)
|
||||||
|
cursor = connection.cursor()
|
||||||
|
t = f"""
|
||||||
|
INSERT INTO Books (Auteur, Titre, Editeur, Type)
|
||||||
|
VALUES (%s, %s, %s, %s);
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
cursor.execute(t, (newContent['author'], newContent['title'], newContent['editor'], newContent['type']))
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
connection.commit()
|
||||||
|
return True
|
||||||
|
|
||||||
|
def add_film(newContent):
|
||||||
|
connection.ping(reconnect=True)
|
||||||
|
cursor = connection.cursor()
|
||||||
|
t = f"""
|
||||||
|
INSERT INTO Films (Title, Director, Producer, Actors, Length, Type)
|
||||||
|
VALUES (%s, %s, %s, %s, %s, %s);
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
cursor.execute(t, (newContent['title'], newContent['director'], newContent['producer'], newContent['actors'], newContent['length'], newContent['type']))
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
connection.commit()
|
||||||
|
return True
|
||||||
|
|
||||||
|
def remove_film(id):
|
||||||
|
cursor = connection.cursor()
|
||||||
|
t = f"""
|
||||||
|
DELETE FROM Films where Number=%s;
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
cursor.execute(t, (id))
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
connection.commit()
|
||||||
|
return True
|
||||||
|
|
||||||
|
def edit_film(id, newContent):
|
||||||
|
connection.ping(reconnect=True)
|
||||||
|
cursor = connection.cursor()
|
||||||
|
t = f"""
|
||||||
|
UPDATE Films
|
||||||
|
SET Title=%s, Director=%s, Producer=%s, Actors=%s, Length=%s, Type=%s
|
||||||
|
WHERE Number=%s;
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
cursor.execute(t, (newContent['title'], newContent['director'], newContent['producer'], newContent['actors'], newContent['length'], newContent['type'], id))
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
connection.commit()
|
||||||
|
return True
|
||||||
|
|
||||||
|
def get_films(page, limit=50, order="Director"):
|
||||||
|
connection.ping(reconnect=True)
|
||||||
|
cursor = connection.cursor()
|
||||||
|
t = f"""
|
||||||
|
SELECT * from Films order by %s asc LIMIT %s OFFSET %s;
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
cursor.execute(t, (order, limit, limit * page))
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
return cursor.fetchall()
|
||||||
|
|
||||||
|
def get_field_values(table, field):
|
||||||
|
connection.ping(reconnect=True)
|
||||||
|
cursor = connection.cursor()
|
||||||
|
t = f"""
|
||||||
|
SELECT DISTINCT {field} FROM {table} ORDER BY {field} asc;
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
cursor.execute(t)
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
return False
|
||||||
|
res = cursor.fetchall()
|
||||||
|
return [a[field] for a in res]
|
||||||
|
|
||||||
|
def get_user(username):
|
||||||
|
connection.ping(reconnect=True)
|
||||||
|
cursor = connection.cursor()
|
||||||
|
t = f"""
|
||||||
|
SELECT * from Users WHERE username=%s;
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
cursor.execute(t, (username))
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
return False
|
||||||
|
res = cursor.fetchall()
|
||||||
|
return res
|
||||||
17
back/database.py
Normal file
17
back/database.py
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
from secret import host, user, password, database
|
||||||
|
|
||||||
|
import pymysql.cursors
|
||||||
|
|
||||||
|
connection = pymysql.connect(
|
||||||
|
host=host,
|
||||||
|
user=user,
|
||||||
|
password=password,
|
||||||
|
database=database,
|
||||||
|
charset='utf8mb4',
|
||||||
|
cursorclass=pymysql.cursors.DictCursor
|
||||||
|
)
|
||||||
|
|
||||||
|
cursor = connection.cursor()
|
||||||
|
# cursor.execute(sql, ())
|
||||||
|
# cursor.fetchall()
|
||||||
|
# connection.commit()
|
||||||
226
back/main.py
Normal file
226
back/main.py
Normal file
@@ -0,0 +1,226 @@
|
|||||||
|
from fastapi import Depends, FastAPI, HTTPException, Response
|
||||||
|
from fastapi.responses import JSONResponse
|
||||||
|
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
|
||||||
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
|
|
||||||
|
import json
|
||||||
|
from typing import Optional
|
||||||
|
from crud import get_books, remove_book, edit_book, add_book, get_films, remove_film, edit_film, add_film, get_field_values, get_user
|
||||||
|
from pydantic import BaseModel
|
||||||
|
from passlib.context import CryptContext
|
||||||
|
from secret import SECRET_KEY, ALGORITHM, ACCESS_TOKEN_EXPIRE_MINUTES, origins
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
from jose import JWTError, jwt
|
||||||
|
|
||||||
|
class TokenData(BaseModel):
|
||||||
|
username: Optional[str] = None
|
||||||
|
|
||||||
|
class Token(BaseModel):
|
||||||
|
access_token: str
|
||||||
|
token_type: str
|
||||||
|
|
||||||
|
class Book(BaseModel):
|
||||||
|
title: str
|
||||||
|
author: str
|
||||||
|
type: str
|
||||||
|
editor: str
|
||||||
|
|
||||||
|
class Film(BaseModel):
|
||||||
|
title: str
|
||||||
|
actors: str
|
||||||
|
director: str
|
||||||
|
type: str
|
||||||
|
producer: str
|
||||||
|
length: int
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
app.add_middleware(
|
||||||
|
CORSMiddleware,
|
||||||
|
allow_origins=origins,
|
||||||
|
allow_credentials=True,
|
||||||
|
allow_methods=["*"],
|
||||||
|
allow_headers=["*"],
|
||||||
|
expose_headers=['x-nbpage']
|
||||||
|
)
|
||||||
|
|
||||||
|
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
|
||||||
|
|
||||||
|
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
|
||||||
|
|
||||||
|
def verify_password(plain_password, hashed_password):
|
||||||
|
return pwd_context.verify(plain_password, hashed_password)
|
||||||
|
|
||||||
|
def get_password_hash(password):
|
||||||
|
return pwd_context.hash(password)
|
||||||
|
|
||||||
|
def authenticate_user(name, password):
|
||||||
|
user = get_user(name)
|
||||||
|
if len(user) == 0:
|
||||||
|
return False
|
||||||
|
if verify_password(password, user[0]['password']):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
async def get_current_user(token: str = Depends(oauth2_scheme)):
|
||||||
|
try:
|
||||||
|
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
|
||||||
|
username: str = payload.get("sub")
|
||||||
|
if username is None:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=401,
|
||||||
|
detail="Could not validate credentials",
|
||||||
|
headers={"WWW-Authenticate": "Bearer"},
|
||||||
|
)
|
||||||
|
token_data = TokenData(username=username)
|
||||||
|
except JWTError:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=401,
|
||||||
|
detail="Could not validate credentials",
|
||||||
|
headers={"WWW-Authenticate": "Bearer"},
|
||||||
|
)
|
||||||
|
return {'username': username}
|
||||||
|
|
||||||
|
def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
|
||||||
|
to_encode = data.copy()
|
||||||
|
if expires_delta:
|
||||||
|
expire = datetime.utcnow() + expires_delta
|
||||||
|
else:
|
||||||
|
expire = datetime.utcnow() + timedelta(minutes=15)
|
||||||
|
to_encode.update({"exp": expire})
|
||||||
|
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
|
||||||
|
return encoded_jwt
|
||||||
|
|
||||||
|
@app.post("/api/token", response_model=Token)
|
||||||
|
async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends()):
|
||||||
|
user = authenticate_user(form_data.username, form_data.password)
|
||||||
|
username = form_data.username
|
||||||
|
if not user:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=401,
|
||||||
|
detail="Incorrect username or password",
|
||||||
|
headers={"WWW-Authenticate": "Bearer"},
|
||||||
|
)
|
||||||
|
access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
|
||||||
|
access_token = create_access_token(
|
||||||
|
data={"sub": username}, expires_delta=access_token_expires
|
||||||
|
)
|
||||||
|
return JSONResponse(content={"access_token": access_token, "token_type": "bearer"})
|
||||||
|
|
||||||
|
@app.post('/api/books')
|
||||||
|
async def createBook(book: Book, current_user: dict = Depends(get_current_user)):
|
||||||
|
if not current_user:
|
||||||
|
raise HTTPException(status_code=401, detail="User not allowed")
|
||||||
|
res = add_book({
|
||||||
|
'author': book.author,
|
||||||
|
'title': book.title,
|
||||||
|
'type': book.type,
|
||||||
|
'editor': book.editor
|
||||||
|
})
|
||||||
|
if res == None:
|
||||||
|
raise HTTPException(status_code=400, detail="Badly formated body")
|
||||||
|
return res
|
||||||
|
|
||||||
|
@app.get('/api/books')
|
||||||
|
async def readBook(page: int = 0, limit: int = 50, sort: str = "", search: str = "", current_user: dict = Depends(get_current_user)):
|
||||||
|
if not current_user:
|
||||||
|
raise HTTPException(status_code=401, detail="User not allowed")
|
||||||
|
res, nb = get_books(page, limit=limit, search=search)
|
||||||
|
if res is False or not nb:
|
||||||
|
raise HTTPException(status_code=400, detail="Badly formated")
|
||||||
|
header = {'x-nbpage': str(int(nb[0]['count'] / limit))}
|
||||||
|
content = {'result': [dict(r) for r in res]}
|
||||||
|
return JSONResponse(content=content, headers=header)
|
||||||
|
|
||||||
|
@app.put('/api/book/{id}')
|
||||||
|
async def updateBook(id: str, book: Book, current_user: dict = Depends(get_current_user)):
|
||||||
|
if not current_user:
|
||||||
|
raise HTTPException(status_code=401, detail="User not allowed")
|
||||||
|
res = edit_book(id, {
|
||||||
|
'author': book.author,
|
||||||
|
'title': book.title,
|
||||||
|
'type': book.type,
|
||||||
|
'editor': book.editor
|
||||||
|
})
|
||||||
|
if res == None:
|
||||||
|
raise HTTPException(status_code=400, detail="Badly formated body")
|
||||||
|
return res
|
||||||
|
|
||||||
|
@app.delete('/api/book/{id}')
|
||||||
|
async def deleteBook(id: str, current_user: dict = Depends(get_current_user)):
|
||||||
|
if not current_user:
|
||||||
|
raise HTTPException(status_code=401, detail="User not allowed")
|
||||||
|
res = remove_book(id)
|
||||||
|
_, nb = get_books(0)
|
||||||
|
header = {'x-nbpage': str(int(nb[0]['count'] / 50))}
|
||||||
|
if res == None:
|
||||||
|
raise HTTPException(status_code=400, detail="Badly formated body")
|
||||||
|
return JSONResponse(content=res, headers=header)
|
||||||
|
|
||||||
|
@app.post('/api/films')
|
||||||
|
async def createFilm(current_user: dict = Depends(get_current_user)):
|
||||||
|
if not current_user:
|
||||||
|
raise HTTPException(status_code=401, detail="User not allowed")
|
||||||
|
res = add_film({
|
||||||
|
'title': film.title,
|
||||||
|
'director': film.director,
|
||||||
|
'producer': film.producer,
|
||||||
|
'actors': film.actors,
|
||||||
|
'length': film.length,
|
||||||
|
'type': film.type
|
||||||
|
})
|
||||||
|
if res == None:
|
||||||
|
raise HTTPException(status_code=400, detail="Badly formated body")
|
||||||
|
return res
|
||||||
|
|
||||||
|
@app.get('/api/films')
|
||||||
|
async def readFilm(page: int = 0, limit: int = 50, current_user: dict = Depends(get_current_user)):
|
||||||
|
if not current_user:
|
||||||
|
raise HTTPException(status_code=401, detail="User not allowed")
|
||||||
|
res = get_films(page, limit=limit)
|
||||||
|
if res == None:
|
||||||
|
raise HTTPException(status_code=400, detail="Badly formated body")
|
||||||
|
return res
|
||||||
|
|
||||||
|
@app.put('/api/film/{id}')
|
||||||
|
async def updateFilm(id: str, film: Film, current_user: dict = Depends(get_current_user)):
|
||||||
|
if not current_user:
|
||||||
|
raise HTTPException(status_code=401, detail="User not allowed")
|
||||||
|
res = edit_film(id, {
|
||||||
|
'title': film.title,
|
||||||
|
'director': film.director,
|
||||||
|
'producer': film.producer,
|
||||||
|
'actors': film.actors,
|
||||||
|
'length': film.length,
|
||||||
|
'type': film.type
|
||||||
|
})
|
||||||
|
if res == None:
|
||||||
|
raise HTTPException(status_code=400, detail="Badly formated body")
|
||||||
|
return res
|
||||||
|
|
||||||
|
@app.delete('/api/film/{id}')
|
||||||
|
async def deleteFilm(id: str, current_user: dict = Depends(get_current_user)):
|
||||||
|
if not current_user:
|
||||||
|
raise HTTPException(status_code=401, detail="User not allowed")
|
||||||
|
res = remove_film(id)
|
||||||
|
if res == None:
|
||||||
|
raise HTTPException(status_code=400, detail="Badly formated body")
|
||||||
|
return res
|
||||||
|
|
||||||
|
@app.get('/api/books/{field}')
|
||||||
|
async def getBookFields(field: str, current_user: dict = Depends(get_current_user)):
|
||||||
|
if not current_user:
|
||||||
|
raise HTTPException(status_code=401, detail="User not allowed")
|
||||||
|
if field == "":
|
||||||
|
raise HTTPException(status_code=400, detail="Badly formated body")
|
||||||
|
res = get_field_values('Books', field)
|
||||||
|
return res
|
||||||
|
|
||||||
|
@app.get('/api/films/{field}')
|
||||||
|
async def getFilmFields(field: str, current_user: dict = Depends(get_current_user)):
|
||||||
|
if not current_user:
|
||||||
|
raise HTTPException(status_code=401, detail="User not allowed")
|
||||||
|
if field == "":
|
||||||
|
raise HTTPException(status_code=400, detail="Badly formated body")
|
||||||
|
res = get_field_values('Films', field)
|
||||||
|
return res
|
||||||
37
back/requirements.txt
Normal file
37
back/requirements.txt
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
anyio==3.6.1
|
||||||
|
bcrypt==4.0.0
|
||||||
|
certifi==2022.9.24
|
||||||
|
charset-normalizer==2.1.1
|
||||||
|
click==8.1.3
|
||||||
|
dnspython==2.2.1
|
||||||
|
ecdsa==0.18.0
|
||||||
|
email-validator==1.3.0
|
||||||
|
fastapi==0.85.0
|
||||||
|
h11==0.14.0
|
||||||
|
httptools==0.5.0
|
||||||
|
idna==3.4
|
||||||
|
itsdangerous==2.1.2
|
||||||
|
Jinja2==3.1.2
|
||||||
|
MarkupSafe==2.1.1
|
||||||
|
orjson==3.8.0
|
||||||
|
passlib==1.7.4
|
||||||
|
pyasn1==0.4.8
|
||||||
|
pydantic==1.10.2
|
||||||
|
PyMySQL==1.0.2
|
||||||
|
python-dotenv==0.21.0
|
||||||
|
python-jose==3.3.0
|
||||||
|
python-multipart==0.0.5
|
||||||
|
python-pam==2.0.2
|
||||||
|
PyYAML==6.0
|
||||||
|
requests==2.28.1
|
||||||
|
rsa==4.9
|
||||||
|
six==1.16.0
|
||||||
|
sniffio==1.3.0
|
||||||
|
starlette==0.20.4
|
||||||
|
typing_extensions==4.3.0
|
||||||
|
ujson==5.5.0
|
||||||
|
urllib3==1.26.12
|
||||||
|
uvicorn==0.18.3
|
||||||
|
uvloop==0.17.0
|
||||||
|
watchfiles==0.17.0
|
||||||
|
websockets==10.3
|
||||||
34
docker-compose.yml
Normal file
34
docker-compose.yml
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
version: "2"
|
||||||
|
services:
|
||||||
|
nginx:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: front/Dockerfile
|
||||||
|
ports:
|
||||||
|
- 80:80
|
||||||
|
depends_on:
|
||||||
|
- back
|
||||||
|
back:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: back/Dockerfile
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- 8000:8000
|
||||||
|
depends_on:
|
||||||
|
- database
|
||||||
|
database:
|
||||||
|
image: mariadb
|
||||||
|
restart: always
|
||||||
|
environment:
|
||||||
|
MARIADB_USER: ${MARIADB_USER}
|
||||||
|
MARIADB_PASSWORD: ${MARIADB_PASSWORD}
|
||||||
|
MARIADB_ROOT_PASSWORD: ${MARIADB_ROOT_PASSWORD}
|
||||||
|
MARIADB_DATABASE: ${MARIADB_DATABASE}
|
||||||
|
volumes:
|
||||||
|
- db:/var/lib/mysql
|
||||||
|
- ./back/db/:/docker-entrypoint-initdb.d/
|
||||||
|
ports:
|
||||||
|
- "3306:3306"
|
||||||
|
volumes:
|
||||||
|
db:
|
||||||
17
front/Dockerfile
Normal file
17
front/Dockerfile
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
FROM node:19.1-alpine AS build
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY front/package.json front/package-lock.json /app/
|
||||||
|
RUN npm install
|
||||||
|
|
||||||
|
COPY front/ .
|
||||||
|
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
FROM nginx:latest
|
||||||
|
|
||||||
|
COPY --from=build /app/dist /srv/www/bookshelf
|
||||||
|
|
||||||
|
RUN rm /etc/nginx/conf.d/default.conf
|
||||||
|
COPY --from=build /app/nginx/default.conf /etc/nginx/conf.d/default.conf
|
||||||
24
front/README.md
Normal file
24
front/README.md
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# front
|
||||||
|
|
||||||
|
## Project setup
|
||||||
|
```
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
|
||||||
|
### Compiles and hot-reloads for development
|
||||||
|
```
|
||||||
|
npm run serve
|
||||||
|
```
|
||||||
|
|
||||||
|
### Compiles and minifies for production
|
||||||
|
```
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
### Lints and fixes files
|
||||||
|
```
|
||||||
|
npm run lint
|
||||||
|
```
|
||||||
|
|
||||||
|
### Customize configuration
|
||||||
|
See [Configuration Reference](https://cli.vuejs.org/config/).
|
||||||
5
front/babel.config.js
Normal file
5
front/babel.config.js
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
module.exports = {
|
||||||
|
presets: [
|
||||||
|
'@vue/cli-plugin-babel/preset'
|
||||||
|
]
|
||||||
|
}
|
||||||
1
front/dist/css/app.f7195e87.css
vendored
Normal file
1
front/dist/css/app.f7195e87.css
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
.red{background-color:#de1656;color:#fff}.red:hover{background-color:#961656}.green{background-color:#16de56}.green:hover{background-color:#169656}header{width:60.5vw}.header,header{display:flex}body{margin:0}.button{border:none;padding:.2rem;margin:.3rem}.view{display:flex;flex-direction:column;align-items:center;margin-top:5rem}.router{flex:1 1 auto;background-color:#de1656;border:none;color:#fff;text-align:center;text-decoration:none;display:inline-block;font-size:16px;transition:background-color .5s ease-in}.router:focus,.router:hover{background-color:#961656}#app{font-family:Avenir,Helvetica,Arial,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;text-align:center;color:#2c3e50}nav a{padding:1rem;margin:0}table{table-layout:fixed}.button-panel{display:flex;flex-flow:row}tr:nth-child(odd){background-color:hsla(0,0%,4%,.1)}tr{text-align:left}tr:hover{background-color:hsla(0,0%,4%,.1)}h1{flex:1 1 auto;text-align:left}td{overflow:hidden;text-overflow:ellipsis;word-wrap:break-word}.AddButton{border:none;margin-right:.2rem;justify-content:flex-end;align-self:center}.AddButton,.edit{height:2rem;width:2rem}.AddForm{display:flex;flex-direction:column;overflow:hidden;-webkit-animation-duration:.3s;animation-duration:.3s;-webkit-animation-name:lineInsertedIn;animation-name:lineInsertedIn;transition:height .3s}.AddForm input{margin:1rem}.AddForm .button{align-self:center;align-self:flex-end}.AddForm label{align-self:flex-start}th[data-v-aa4cb3dc]{text-align:left}.elipsis[data-v-6b7294ba]{font-size:3rem}.page[data-v-6b7294ba]{border:none;padding:1rem;margin:.2rem}footer[data-v-6b7294ba]{display:flex;padding:.2rem;align-items:center}.searchBox[data-v-559755d8]{display:flex}.search[data-v-559755d8]{display:flex;min-width:95%;margin-top:1rem;margin-bottom:1rem}.searchButton[data-v-559755d8]{color:hsla(0,0%,4%,.8);margin-left:-1.1rem;width:1rem;height:1rem;align-self:center;align-items:center;font-size:.6rem;border:none}select[data-v-1faa4947]{max-width:14.5rem}.dropdown[data-v-1faa4947]{background-color:transparent}i[data-v-1faa4947]:hover{color:#de1656}.dropdown-content[data-v-1faa4947]{position:absolute;background-color:#f8f8f8;min-width:14em;max-width:15rem;max-height:15rem;margin-top:.5rem;border:1px solid #de1656;box-shadow:0 -8px 34px 0 rgba(0,0,0,.05);overflow:auto;z-index:1}.dropdown-input[data-v-1faa4947]{width:14em;margin-right:1em}.dropdown-item[data-v-1faa4947]{color:#000;line-height:1em;text-decoration:none;padding:.5em;display:block;cursor:pointer}main[data-v-68f1643b]{max-width:60.5vw}td input[data-v-68f1643b]{width:14.5rem}@-webkit-keyframes lineInsertedIn-68f1643b{0%{height:0}to{height:23rem}}@keyframes lineInsertedIn-68f1643b{0%{height:0}to{height:23rem}}a[data-v-f3fce040]{text-decoration:none}h2[data-v-f3fce040],p[data-v-f3fce040]{text-align:left}a[data-v-31c3b6b4]{text-decoration:none}h2[data-v-31c3b6b4],p[data-v-31c3b6b4]{text-align:left}main[data-v-590e11e2]{width:80vw}table[data-v-590e11e2]{margin-left:auto;margin-right:auto}.elipsis[data-v-590e11e2]{font-size:3rem}.page[data-v-590e11e2]{border:none;padding:1rem;margin:.2rem}footer[data-v-590e11e2]{display:flex;padding:.2rem;align-items:center}button[data-v-3cf27bac]{margin-top:.5rem}label[data-v-3cf27bac]{margin:.2rem;text-align:left}input[data-v-3cf27bac]{border:none}input[data-v-3cf27bac],input[data-v-3cf27bac]:hover{background-color:rgba(0,0,0,.1)}main[data-v-3cf27bac]{width:20rem;align-self:center;align-content:center}div[data-v-3cf27bac],main[data-v-3cf27bac]{display:flex;flex-direction:column}
|
||||||
BIN
front/dist/favicon.ico
vendored
Normal file
BIN
front/dist/favicon.ico
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
1
front/dist/index.html
vendored
Normal file
1
front/dist/index.html
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<!doctype html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="/favicon.ico"><script src="https://kit.fontawesome.com/08db035529.js" crossorigin="anonymous"></script><title>Aldon's Home</title><script defer="defer" src="/js/chunk-vendors.6bc46727.js"></script><script defer="defer" src="/js/app.10597b7c.js"></script><link href="/css/app.f7195e87.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but Aldon's Home doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div></body></html>
|
||||||
2
front/dist/js/app.10597b7c.js
vendored
Normal file
2
front/dist/js/app.10597b7c.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
front/dist/js/app.10597b7c.js.map
vendored
Normal file
1
front/dist/js/app.10597b7c.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
14
front/dist/js/chunk-vendors.6bc46727.js
vendored
Normal file
14
front/dist/js/chunk-vendors.6bc46727.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
front/dist/js/chunk-vendors.6bc46727.js.map
vendored
Normal file
1
front/dist/js/chunk-vendors.6bc46727.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
19
front/jsconfig.json
Normal file
19
front/jsconfig.json
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es5",
|
||||||
|
"module": "esnext",
|
||||||
|
"baseUrl": "./",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"paths": {
|
||||||
|
"@/*": [
|
||||||
|
"src/*"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"lib": [
|
||||||
|
"esnext",
|
||||||
|
"dom",
|
||||||
|
"dom.iterable",
|
||||||
|
"scripthost"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
26
front/nginx/default.conf
Normal file
26
front/nginx/default.conf
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
server {
|
||||||
|
listen 80 default_server;
|
||||||
|
listen [::]:80 default_server;
|
||||||
|
|
||||||
|
server_name localhost;
|
||||||
|
root /srv/www/bookshelf;
|
||||||
|
index index.html;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
try_files $uri /index.html;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /api/ {
|
||||||
|
proxy_set_header Host $http_host;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
|
||||||
|
proxy_redirect off;
|
||||||
|
proxy_buffering off;
|
||||||
|
proxy_pass http://backend;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
upstream backend {
|
||||||
|
server back:8000;
|
||||||
|
}
|
||||||
19672
front/package-lock.json
generated
Normal file
19672
front/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
45
front/package.json
Normal file
45
front/package.json
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
{
|
||||||
|
"name": "front",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"serve": "vue-cli-service serve",
|
||||||
|
"build": "vue-cli-service build",
|
||||||
|
"lint": "vue-cli-service lint"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"core-js": "^3.8.3",
|
||||||
|
"vue": "^3.2.13",
|
||||||
|
"vue-router": "^4.0.14",
|
||||||
|
"vuex": "^4.0.2"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/core": "^7.12.16",
|
||||||
|
"@babel/eslint-parser": "^7.12.16",
|
||||||
|
"@vue/cli-plugin-babel": "~5.0.0",
|
||||||
|
"@vue/cli-plugin-eslint": "~5.0.0",
|
||||||
|
"@vue/cli-service": "~5.0.0",
|
||||||
|
"eslint": "^7.32.0",
|
||||||
|
"eslint-plugin-vue": "^8.0.3"
|
||||||
|
},
|
||||||
|
"eslintConfig": {
|
||||||
|
"root": true,
|
||||||
|
"env": {
|
||||||
|
"node": true
|
||||||
|
},
|
||||||
|
"extends": [
|
||||||
|
"plugin:vue/vue3-essential",
|
||||||
|
"eslint:recommended"
|
||||||
|
],
|
||||||
|
"parserOptions": {
|
||||||
|
"parser": "@babel/eslint-parser"
|
||||||
|
},
|
||||||
|
"rules": {}
|
||||||
|
},
|
||||||
|
"browserslist": [
|
||||||
|
"> 1%",
|
||||||
|
"last 2 versions",
|
||||||
|
"not dead",
|
||||||
|
"not ie 11"
|
||||||
|
]
|
||||||
|
}
|
||||||
BIN
front/public/favicon.ico
Normal file
BIN
front/public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
17
front/public/index.html
Normal file
17
front/public/index.html
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||||
|
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||||
|
<script src="https://kit.fontawesome.com/08db035529.js" crossorigin="anonymous"></script>
|
||||||
|
<title>Aldon's Home</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<noscript>
|
||||||
|
<strong>We're sorry but Aldon's Home doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
||||||
|
</noscript>
|
||||||
|
<div id="app"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
192
front/src/App.vue
Normal file
192
front/src/App.vue
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<nav class="header">
|
||||||
|
<router-link class="router" to="/">Home</router-link>
|
||||||
|
<router-link class="router" to="/about">About</router-link>
|
||||||
|
<router-link class="router" to="/films">Film library</router-link>
|
||||||
|
<router-link class="router" to="/books">Book library</router-link>
|
||||||
|
<router-link v-if="!$store.state.token" class="router" to="/login">Login</router-link>
|
||||||
|
<a class="router" v-else @click="logout">Log out</a>
|
||||||
|
</nav>
|
||||||
|
<router-view class="view"/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { routerLink, routerView} from 'vue-router';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'App',
|
||||||
|
components: {
|
||||||
|
routerLink,
|
||||||
|
routerView
|
||||||
|
},
|
||||||
|
beforeMount() {
|
||||||
|
let token = localStorage.getItem('token')
|
||||||
|
if (token) {
|
||||||
|
this.$store.state.token = localStorage.getItem('token')
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.$store.state.token = ""
|
||||||
|
this.$router.push('/login')
|
||||||
|
}
|
||||||
|
this.$store.dispatch('getBooks', {page: 0, search: "", order: ""})
|
||||||
|
this.$store.dispatch('getFilms', {page: 0, search: "", order: ""})
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
logout() {
|
||||||
|
this.$store.dispatch('logout')
|
||||||
|
this.$router.push('/login')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.red {
|
||||||
|
background-color: rgba(222, 22, 86);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.red:hover {
|
||||||
|
background-color: rgba(150, 22, 86);
|
||||||
|
}
|
||||||
|
|
||||||
|
.green {
|
||||||
|
background-color: rgba(22, 222, 86);
|
||||||
|
}
|
||||||
|
|
||||||
|
.green:hover {
|
||||||
|
background-color: rgba(22, 150, 86);
|
||||||
|
|
||||||
|
}
|
||||||
|
header {
|
||||||
|
width: 60.5vw;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
/* width: 100%; */
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
margin: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
border: none;
|
||||||
|
padding: 0.2rem;
|
||||||
|
margin: 0.3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.view {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: 5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.router {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
background-color: rgba(222, 22, 86);
|
||||||
|
border: none;
|
||||||
|
color: white;
|
||||||
|
text-align: center;
|
||||||
|
text-decoration: none;
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 16px;
|
||||||
|
transition: background-color 0.5s ease-in;
|
||||||
|
}
|
||||||
|
|
||||||
|
.router:focus {
|
||||||
|
background-color: rgba(150, 22, 86);
|
||||||
|
}
|
||||||
|
|
||||||
|
.router:hover {
|
||||||
|
background-color: rgba(150, 22, 86);
|
||||||
|
}
|
||||||
|
|
||||||
|
#app {
|
||||||
|
font-family: Avenir, Helvetica, Arial, sans-serif;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
text-align: center;
|
||||||
|
color: #2c3e50;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav a {
|
||||||
|
padding: 1rem;
|
||||||
|
margin: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
table-layout: fixed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-panel {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr:nth-child(2n+1) {
|
||||||
|
background-color: rgba(11, 11, 11, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
tr {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr:hover {
|
||||||
|
background-color: rgba(11, 11, 11, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
td {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
word-wrap: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.AddButton {
|
||||||
|
border: none;
|
||||||
|
height: 2rem;
|
||||||
|
width: 2rem;
|
||||||
|
margin-right: 0.2rem;
|
||||||
|
justify-content: flex-end;
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit {
|
||||||
|
width: 2rem;
|
||||||
|
height: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.AddForm {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
animation-duration: 0.3s;
|
||||||
|
animation-name: lineInsertedIn;
|
||||||
|
transition: height 0.3s;
|
||||||
|
}
|
||||||
|
.AddForm input {
|
||||||
|
margin: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.AddForm .button {
|
||||||
|
align-self: center;
|
||||||
|
/* max-width: 5rem;
|
||||||
|
min-width: 5rem; */
|
||||||
|
align-self: flex-end
|
||||||
|
}
|
||||||
|
|
||||||
|
.AddForm label {
|
||||||
|
align-self: flex-start;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
BIN
front/src/assets/logo.png
Normal file
BIN
front/src/assets/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.7 KiB |
109
front/src/components/DropDown.vue
Normal file
109
front/src/components/DropDown.vue
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<input
|
||||||
|
class="dropdown-input"
|
||||||
|
@focus="showOption"
|
||||||
|
@blur="hideOption"
|
||||||
|
v-model="inputVal" />
|
||||||
|
<button
|
||||||
|
class="dropdown button"
|
||||||
|
@blur="hideOption"
|
||||||
|
@click="toggleOption" >
|
||||||
|
<i class="fa-solid fa-angle-down" ></i>
|
||||||
|
</button>
|
||||||
|
<div class="dropdown-content" v-show="showOptions">
|
||||||
|
<div
|
||||||
|
class="dropdown-item"
|
||||||
|
v-for="val in vals"
|
||||||
|
@mousedown="selectValue(val)"
|
||||||
|
:key="val">{{ val }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
field: String,
|
||||||
|
values: Array,
|
||||||
|
inputValue: String,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
inputVal: "",
|
||||||
|
showOptions: false,
|
||||||
|
selected: "",
|
||||||
|
vals: [],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.inputVal = this.inputValue;
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
inputVal: function() {
|
||||||
|
if (!this.inputVal)
|
||||||
|
return
|
||||||
|
this.vals = this.values.filter(word => word.toLowerCase().includes(this.inputVal.toLowerCase(), 0))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
selectValue(value) {
|
||||||
|
this.selected = value;
|
||||||
|
this.inputVal = value;
|
||||||
|
this.$emit('selected', this.selected);
|
||||||
|
},
|
||||||
|
toggleOption() {
|
||||||
|
this.showOptions = !this.showOptions
|
||||||
|
},
|
||||||
|
showOption() {
|
||||||
|
this.showOptions = true;
|
||||||
|
},
|
||||||
|
hideOption() {
|
||||||
|
this.showOptions = false;
|
||||||
|
this.selected = this.inputVal;
|
||||||
|
this.$emit('selected', this.selected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style scoped>
|
||||||
|
select {
|
||||||
|
max-width: 14.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown {
|
||||||
|
background-color: rgba(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
i:hover {
|
||||||
|
color: rgba(222, 22, 86, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-content {
|
||||||
|
position: absolute;
|
||||||
|
background-color: #f8f8F8;
|
||||||
|
min-width: 14em;
|
||||||
|
max-width: 15rem;
|
||||||
|
max-height: 15rem;
|
||||||
|
margin-top: .5rem;
|
||||||
|
border: 1px solid rgba(222, 22, 86, 1);
|
||||||
|
/* border-top-color: rgba(0,0,0,0); */
|
||||||
|
box-shadow: 0px -8px 34px 0px rgba(0,0,0,0.05);
|
||||||
|
overflow: auto;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-input {
|
||||||
|
width: 14em;
|
||||||
|
margin-right: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-item {
|
||||||
|
color: black;
|
||||||
|
line-height: 1em;
|
||||||
|
text-decoration: none;
|
||||||
|
padding: 0.5em;
|
||||||
|
display: block;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
40
front/src/components/PaginationFooter.vue
Normal file
40
front/src/components/PaginationFooter.vue
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<footer v-if="$store.getters.getBookPageNb <= 5">
|
||||||
|
<button class="page" v-for="index in $store.getters.getBookCurrentPage + 1" :key="index">{{ index }}</button>
|
||||||
|
</footer>
|
||||||
|
<footer v-else>
|
||||||
|
<button @click="$store.dispatch('fetchPageBooks', {page: 0, nooption: false})" class="page"> {{ '<<' }} </button>
|
||||||
|
<button
|
||||||
|
class="page"
|
||||||
|
:class="{ 'red': $store.getters.getBookCurrentPage - Math.max(0, Math.min($store.getters.getBookCurrentPage - 2, $store.getters.getBookPageNb - 5)) === index}"
|
||||||
|
v-for="(n, index) in (Math.max(5, Math.min($store.getters.getBookCurrentPage + 2, $store.getters.getBookPageNb)) - Math.max(0, Math.min($store.getters.getBookCurrentPage - 2, $store.getters.getBookPageNb - 5)))"
|
||||||
|
:key="index"
|
||||||
|
@click="$store.dispatch('fetchPageBooks', {page: Math.max(0, Math.min($store.getters.getBookCurrentPage - 2, $store.getters.getBookPageNb - 5)) + index, nooption: false})">
|
||||||
|
{{ Math.max(0, Math.min($store.getters.getBookCurrentPage - 2, $store.getters.getBookPageNb - 5)) + index }}
|
||||||
|
</button>
|
||||||
|
<p v-if="$store.getters.getBookCurrentPage !== $store.getters.getBookPageNb" class="elipsis">...</p>
|
||||||
|
<button v-if="$store.getters.getBookCurrentPage !== $store.getters.getBookPageNb" class="page" @click="$store.dispatch('fetchPageBooks', {page: $store.getters.getBookPageNb, nooption: false})">{{ $store.getters.getBookPageNb }}</button>
|
||||||
|
<button @click="$store.dispatch('fetchPageBooks', {page: $store.getters.getBookPageNb, nooption: false})" class="page"> >> </button>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<style scoped>
|
||||||
|
.elipsis {
|
||||||
|
font-size: 3rem
|
||||||
|
}
|
||||||
|
|
||||||
|
.page {
|
||||||
|
border: none;
|
||||||
|
padding: 1rem;
|
||||||
|
margin: 0.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
display: flex;
|
||||||
|
padding: 0.2rem;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
</style>
|
||||||
54
front/src/components/SearchHeader.vue
Normal file
54
front/src/components/SearchHeader.vue
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
<template>
|
||||||
|
<div class="searchBox">
|
||||||
|
<input class="search" type="text" v-model="search"/>
|
||||||
|
<button class="searchButton" @click="clearSearch" >x</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
page: String,
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
search: function() {
|
||||||
|
this.$emit('changeSearch', this.search);
|
||||||
|
this.$store.dispatch('fetchPage'+this.page, {page: 0, nooption: false, search: this.search, order: this.order})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
clearSearch() {
|
||||||
|
this.$store.dispatch('fetchPage'+this.page, {page: 0, nooption: true})
|
||||||
|
this.search = ""
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
search: "",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style scoped>
|
||||||
|
.searchBox {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search {
|
||||||
|
display: flex;
|
||||||
|
min-width: 95%;
|
||||||
|
margin-top: 1rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.searchButton {
|
||||||
|
color: rgba(11, 11, 11, 0.8);
|
||||||
|
margin-left: -1.1rem;
|
||||||
|
width: 1rem;
|
||||||
|
height: 1rem;
|
||||||
|
align-self: center;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 0.6rem;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
23
front/src/components/TableView.vue
Normal file
23
front/src/components/TableView.vue
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<template>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th v-bind:style="{ 'min-width': this.width }" v-for="head in headings" :key="head">{{ head }}</th>
|
||||||
|
</tr>
|
||||||
|
<slot></slot>
|
||||||
|
</table>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "TableView",
|
||||||
|
props: {
|
||||||
|
headings: Array,
|
||||||
|
width: String
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style scoped>
|
||||||
|
th {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
6
front/src/config.js
Normal file
6
front/src/config.js
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
const ROOT_FQDN = 'https://bookshelf.aldon.fr/api';
|
||||||
|
// const ROOT_FQDN = 'http://localhost/api';
|
||||||
|
|
||||||
|
export {
|
||||||
|
ROOT_FQDN
|
||||||
|
};
|
||||||
19
front/src/main.js
Normal file
19
front/src/main.js
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import { createApp } from 'vue';
|
||||||
|
import App from './App.vue';
|
||||||
|
import router from './router';
|
||||||
|
import store from './store';
|
||||||
|
|
||||||
|
const app = createApp(App)
|
||||||
|
app.use(router)
|
||||||
|
app.use(store)
|
||||||
|
app.mount('#app')
|
||||||
|
|
||||||
|
|
||||||
|
// new Vue({
|
||||||
|
// router,
|
||||||
|
// store,
|
||||||
|
// el: '#app',
|
||||||
|
// components: { App },
|
||||||
|
// render: h => h(App)
|
||||||
|
|
||||||
|
// })
|
||||||
28
front/src/router/index.js
Normal file
28
front/src/router/index.js
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import { createRouter, createWebHistory } from 'vue-router';
|
||||||
|
import TheBooks from '../views/TheBooks.vue';
|
||||||
|
import TheAbout from '../views/TheAbout.vue';
|
||||||
|
import TheHome from '../views/TheHome.vue';
|
||||||
|
import TheFilms from '../views/TheFilms.vue';
|
||||||
|
import TheLogin from '../views/TheLogin.vue';
|
||||||
|
import store from '../store';
|
||||||
|
|
||||||
|
const routes = [
|
||||||
|
{ name: 'TheHome', path: '/', component: TheHome },
|
||||||
|
{ name: 'TheFilms', path: '/films', component: TheFilms },
|
||||||
|
{ name: 'TheBooks', path: '/books', component: TheBooks },
|
||||||
|
{ name: 'TheAbout', path: '/about', component: TheAbout },
|
||||||
|
{ name: 'Login', path: '/login', component: TheLogin },
|
||||||
|
]
|
||||||
|
|
||||||
|
const router = new createRouter({
|
||||||
|
history: createWebHistory(process.env.BASE_URL),
|
||||||
|
routes,
|
||||||
|
});
|
||||||
|
|
||||||
|
router.beforeEach(async (to) => {
|
||||||
|
if (!store.state.token && to.name !== 'Login') {
|
||||||
|
return { name: 'Login', query: {redirect: to.path} }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
export default router;
|
||||||
389
front/src/store/index.js
Normal file
389
front/src/store/index.js
Normal file
@@ -0,0 +1,389 @@
|
|||||||
|
import { createStore } from 'vuex';
|
||||||
|
import { ROOT_FQDN } from '@/config';
|
||||||
|
|
||||||
|
const store = createStore({
|
||||||
|
state() {
|
||||||
|
return {
|
||||||
|
books: [
|
||||||
|
],
|
||||||
|
films: [
|
||||||
|
],
|
||||||
|
token: "",
|
||||||
|
bookPageNb: 0,
|
||||||
|
bookCurrentPage: 0,
|
||||||
|
filmPageNb: 0,
|
||||||
|
filmCurrentPage: 0,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mutations: {
|
||||||
|
setBooks(state, books) {
|
||||||
|
state.books = books.result
|
||||||
|
},
|
||||||
|
setFilms(state, films) {
|
||||||
|
state.films = films
|
||||||
|
},
|
||||||
|
logout(state) {
|
||||||
|
state.token = ""
|
||||||
|
},
|
||||||
|
addToken(state, token) {
|
||||||
|
state.token = token
|
||||||
|
},
|
||||||
|
removeBook(state, id) {
|
||||||
|
let index = state.books.findIndex(book => book.biblio_Index === id)
|
||||||
|
state.books.splice(index, 1)
|
||||||
|
},
|
||||||
|
editBook(state, payload) {
|
||||||
|
let index = state.books.findIndex(book => book.biblio_Index === payload.id)
|
||||||
|
state.books.splice(index, 1, payload.newBook)
|
||||||
|
},
|
||||||
|
addBook(state, book) {
|
||||||
|
state.books.push(book);
|
||||||
|
},
|
||||||
|
removeFilm(state, id) {
|
||||||
|
let index = state.films.findIndex(film => film.Number === id)
|
||||||
|
state.films.splice(index, 1)
|
||||||
|
},
|
||||||
|
editFilm(state, payload) {
|
||||||
|
let index = state.films.findIndex(film => film.Number === payload.id)
|
||||||
|
state.films.splice(index, 1, payload.newfilm)
|
||||||
|
},
|
||||||
|
addFilm(state, film) {
|
||||||
|
state.films.push(film);
|
||||||
|
},
|
||||||
|
setBookPageNb(state, nb) {
|
||||||
|
state.bookPageNb = nb
|
||||||
|
},
|
||||||
|
setBookCurrentPage(state, page) {
|
||||||
|
state.bookCurrentPage = page
|
||||||
|
},
|
||||||
|
setFilmCurrentPage(state, page) {
|
||||||
|
state.filmCurrentPage = page
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getters: {
|
||||||
|
getBooks(state) {
|
||||||
|
return state.books;
|
||||||
|
},
|
||||||
|
getBookCurrentPage(state) {
|
||||||
|
return state.bookCurrentPage
|
||||||
|
},
|
||||||
|
getBookPageNb(state) {
|
||||||
|
return state.bookPageNb
|
||||||
|
},
|
||||||
|
getFilms(state) {
|
||||||
|
return state.films;
|
||||||
|
},
|
||||||
|
getFilmCurrentPage(state) {
|
||||||
|
return state.filmCurrentPage
|
||||||
|
},
|
||||||
|
getFilmPageNb(state) {
|
||||||
|
return state.filmPageNb
|
||||||
|
},
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
logout(ctx) {
|
||||||
|
ctx.commit('logout')
|
||||||
|
localStorage.setItem('token', '')
|
||||||
|
},
|
||||||
|
async login(ctx, info) {
|
||||||
|
let username = info.username
|
||||||
|
let password = info.password
|
||||||
|
const data = new FormData()
|
||||||
|
data.append('username', username)
|
||||||
|
data.append('password', password)
|
||||||
|
return await fetch(ROOT_FQDN + '/token', {
|
||||||
|
method: 'POST',
|
||||||
|
body: data
|
||||||
|
}).then((response) => {
|
||||||
|
if (response.ok) {
|
||||||
|
return response.json()
|
||||||
|
} else {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}).then(res => {
|
||||||
|
if (res !== null) {
|
||||||
|
ctx.commit('addToken', res.access_token)
|
||||||
|
localStorage.setItem('token', res.access_token)
|
||||||
|
ctx.dispatch('getBooks', {page: 0, search: "", order:""})
|
||||||
|
ctx.dispatch('getFilms', {page: 0, search: "", order:""})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
async removeBook({dispatch, commit, state}, id) {
|
||||||
|
await fetch(ROOT_FQDN+'/book/'+id, {
|
||||||
|
method: 'DELETE',
|
||||||
|
headers: {
|
||||||
|
"Authorization": 'Bearer ' + state.token,
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
},
|
||||||
|
}).then((response) => {
|
||||||
|
if (response.ok) {
|
||||||
|
commit('setBookPageNb', response.headers.get('x-nbpage'))
|
||||||
|
return response.json()
|
||||||
|
} else
|
||||||
|
return null
|
||||||
|
}).then(res => {
|
||||||
|
if (res === null)
|
||||||
|
{
|
||||||
|
dispatch('logout')
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
})
|
||||||
|
commit('removeBook', id)
|
||||||
|
},
|
||||||
|
async editBook({dispatch, commit, state}, payload) {
|
||||||
|
let data = {
|
||||||
|
title: payload.newBook.title,
|
||||||
|
author: payload.newBook.author,
|
||||||
|
type: payload.newBook.type,
|
||||||
|
editor: payload.newBook.editor
|
||||||
|
}
|
||||||
|
await fetch(ROOT_FQDN+'/book/'+payload.id, {
|
||||||
|
method: 'PUT',
|
||||||
|
headers: {
|
||||||
|
"Authorization": 'Bearer ' + state.token,
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
},
|
||||||
|
body: JSON.stringify(data)
|
||||||
|
}).then(res => {
|
||||||
|
if (res.ok) {
|
||||||
|
return res.json()
|
||||||
|
} else
|
||||||
|
return null
|
||||||
|
}).then((res) => {
|
||||||
|
if (res === null)
|
||||||
|
dispatch('logout')
|
||||||
|
dispatch('getBooks', {page: 0, search: payload.search, order:""})
|
||||||
|
})
|
||||||
|
commit('editBook', payload)
|
||||||
|
},
|
||||||
|
async addBook({dispatch, commit, state}, book) {
|
||||||
|
let data = {
|
||||||
|
title: book.Titre,
|
||||||
|
author: book.Auteur,
|
||||||
|
type: book.Type,
|
||||||
|
editor: book.Editeur
|
||||||
|
}
|
||||||
|
await fetch(ROOT_FQDN+'/books', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
"Authorization": 'Bearer ' + state.token,
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
},
|
||||||
|
body: JSON.stringify(data)
|
||||||
|
}).then((response) => {
|
||||||
|
commit('setBookPageNb', response.headers.get('x-nbpage'))
|
||||||
|
if (response.ok)
|
||||||
|
return response.json()
|
||||||
|
else
|
||||||
|
return null
|
||||||
|
}).then(res => {
|
||||||
|
if (res === null)
|
||||||
|
{
|
||||||
|
dispatch('logout')
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
})
|
||||||
|
commit('addBook', book)
|
||||||
|
},
|
||||||
|
async getBooks({dispatch, commit, state}, payload) {
|
||||||
|
let test = payload.page ? "?page=" + payload.page : ""
|
||||||
|
if (payload.page)
|
||||||
|
test += '&'
|
||||||
|
else
|
||||||
|
test += '?'
|
||||||
|
test += payload.search ? "search="+payload.search : ""
|
||||||
|
await fetch(ROOT_FQDN+'/books'+test, {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
"Authorization": 'Bearer ' + state.token,
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
}
|
||||||
|
}).then((response) => {
|
||||||
|
commit('setBookPageNb', response.headers.get('x-nbpage'))
|
||||||
|
if (response.ok)
|
||||||
|
return response.json()
|
||||||
|
else
|
||||||
|
return null
|
||||||
|
}).then(res => {
|
||||||
|
if (res === null)
|
||||||
|
{
|
||||||
|
dispatch('logout')
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
commit('setBooks', res)
|
||||||
|
return res
|
||||||
|
})
|
||||||
|
},
|
||||||
|
async removeFilm({dispatch, commit, state}, id) {
|
||||||
|
await fetch(ROOT_FQDN+'/film/'+id, {
|
||||||
|
method: 'DELETE',
|
||||||
|
headers: {
|
||||||
|
"Authorization": 'Bearer ' + state.token,
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
},
|
||||||
|
}).then((response) => {
|
||||||
|
if (response.ok) {
|
||||||
|
commit('setFilmPageNb', response.headers.get('x-nbpage'))
|
||||||
|
return response.json()
|
||||||
|
} else
|
||||||
|
return null
|
||||||
|
}).then(res => {
|
||||||
|
if (res === null)
|
||||||
|
{
|
||||||
|
dispatch('logout')
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
})
|
||||||
|
commit('removeFilm', id)
|
||||||
|
},
|
||||||
|
async editFilm({dispatch, commit, state}, payload) {
|
||||||
|
let data = {
|
||||||
|
title: payload.newfilm.Title,
|
||||||
|
actors: payload.newfilm.Actors,
|
||||||
|
director: payload.newfilm.Director,
|
||||||
|
type: payload.newfilm.Type,
|
||||||
|
producer: payload.newfilm.Producer,
|
||||||
|
length: payload.newfilm.Length
|
||||||
|
}
|
||||||
|
await fetch(ROOT_FQDN+'/film/'+payload.id, {
|
||||||
|
method: 'PUT',
|
||||||
|
headers: {
|
||||||
|
"Authorization": 'Bearer ' + state.token,
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
},
|
||||||
|
body: JSON.stringify(data)
|
||||||
|
}).then(res => {
|
||||||
|
if (res.ok) {
|
||||||
|
return res.json()
|
||||||
|
} else
|
||||||
|
return null
|
||||||
|
}).then((res) => {
|
||||||
|
if (res === null)
|
||||||
|
dispatch('logout')
|
||||||
|
dispatch('getFilms', {page: 0, search:"", order:""})
|
||||||
|
})
|
||||||
|
commit('editFilm', payload)
|
||||||
|
},
|
||||||
|
async addFilm({dispatch, commit, state}, film) {
|
||||||
|
let data = {
|
||||||
|
title: film.Title,
|
||||||
|
actors: film.Actors,
|
||||||
|
director: film.Director,
|
||||||
|
type: film.Type,
|
||||||
|
producer: film.Producer,
|
||||||
|
length: film.Length
|
||||||
|
}
|
||||||
|
await fetch(ROOT_FQDN+'/films', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
"Authorization": 'Bearer ' + state.token,
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
},
|
||||||
|
body: JSON.stringify(data)
|
||||||
|
}).then((response) => {
|
||||||
|
commit('setFilmPageNb', response.headers.get('x-nbpage'))
|
||||||
|
if (response.ok)
|
||||||
|
return response.json()
|
||||||
|
else
|
||||||
|
return null
|
||||||
|
}).then(res => {
|
||||||
|
if (res === null)
|
||||||
|
{
|
||||||
|
dispatch('logout')
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
})
|
||||||
|
commit('addFilm', film)
|
||||||
|
},
|
||||||
|
async getFilms({dispatch, commit, state}, payload) {
|
||||||
|
let test = payload.page ? "?page=" + payload.page : ""
|
||||||
|
if (payload.page)
|
||||||
|
test += '&'
|
||||||
|
else
|
||||||
|
test += '?'
|
||||||
|
test += payload.search ? "search="+payload.search : ""
|
||||||
|
await fetch(ROOT_FQDN+'/films'+test, {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
"Authorization": 'Bearer ' + state.token,
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
}
|
||||||
|
}).then((response) => {
|
||||||
|
if (response.ok)
|
||||||
|
return response.json()
|
||||||
|
else
|
||||||
|
return null
|
||||||
|
}).then(res => {
|
||||||
|
commit('setFilms', res)
|
||||||
|
if (res === null) {
|
||||||
|
dispatch('logout');
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
})
|
||||||
|
},
|
||||||
|
async fetchPageBooks({dispatch, commit}, payload) {
|
||||||
|
commit('setBookCurrentPage', payload.page)
|
||||||
|
if (payload.nooption) {
|
||||||
|
dispatch('getBooks', {page: payload.page, search: "", order: ""})
|
||||||
|
} else {
|
||||||
|
dispatch('getBooks', {page: payload.page, search: payload.search, order: payload.sort})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async fetchPageFilms({dispatch, commit}, payload) {
|
||||||
|
commit('setFilmCurrentPage', payload.page)
|
||||||
|
if (payload.nooption) {
|
||||||
|
dispatch('getFilms', {page: payload.page, search: "", order: ""})
|
||||||
|
} else {
|
||||||
|
dispatch('getFilms', {page: payload.page, search: payload.search, order: payload.sort})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async getBookField({dispatch, state}, field) {
|
||||||
|
return await fetch(ROOT_FQDN+'/books/' + field, {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
"Authorization": 'Bearer ' + state.token,
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
}
|
||||||
|
}).then((response) => {
|
||||||
|
if (response.ok)
|
||||||
|
return response.json()
|
||||||
|
else
|
||||||
|
return null
|
||||||
|
}).then(res => {
|
||||||
|
if (res === null) {
|
||||||
|
dispatch('logout');
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
})
|
||||||
|
},
|
||||||
|
async getFilmField({dispatch, state}, field) {
|
||||||
|
return await fetch(ROOT_FQDN+'/films/' + field, {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
"Authorization": 'Bearer ' + state.token,
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
}
|
||||||
|
}).then((response) => {
|
||||||
|
if (response.ok)
|
||||||
|
return response.json()
|
||||||
|
else
|
||||||
|
return null
|
||||||
|
}).then(res => {
|
||||||
|
if (res === null) {
|
||||||
|
dispatch('logout');
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
export default store;
|
||||||
40
front/src/views/TheAbout.vue
Normal file
40
front/src/views/TheAbout.vue
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<template>
|
||||||
|
<div class="about">
|
||||||
|
<header>
|
||||||
|
<h1>About</h1>
|
||||||
|
</header>
|
||||||
|
<main>
|
||||||
|
<h2>Home Library</h2>
|
||||||
|
<p>
|
||||||
|
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Made with <a href="https://vuejs.org/"><b>VueJS</b></a> and <a href="https://fastapi.tiangolo.com/"><b>FastAPI</b></a> by <a href="https://julien.aldon.fr/"><b><em>Julien Aldon</em></b></a>
|
||||||
|
</p>
|
||||||
|
</main>
|
||||||
|
<footer>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style scoped>
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
206
front/src/views/TheBooks.vue
Normal file
206
front/src/views/TheBooks.vue
Normal file
@@ -0,0 +1,206 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<header>
|
||||||
|
<h1>Books</h1>
|
||||||
|
<button v-if="!addMode" class="AddButton" v-show="!addMode" @click="toggleEditMode"><i class="fas fa-plus"></i></button>
|
||||||
|
<button v-else class="AddButton red" @click="addMode=!addMode">X</button>
|
||||||
|
</header>
|
||||||
|
<main>
|
||||||
|
<form ref="form" class="AddForm collapsed" v-on:submit.prevent="addElem" v-if="addMode">
|
||||||
|
<label for="author">Author</label>
|
||||||
|
<input id="author" v-model="formAdd.author" placeholder="Author"/>
|
||||||
|
<label for="title">Title</label>
|
||||||
|
<input id="title" v-model="formAdd.title" placeholder="Title"/>
|
||||||
|
<label for="editor">Editor</label>
|
||||||
|
<input id="editor" v-model="formAdd.editor" placeholder="Editor"/>
|
||||||
|
<label for="type">Type</label>
|
||||||
|
<input id="type" v-model="formAdd.type" placeholder="Type"/>
|
||||||
|
<button class="button edit">
|
||||||
|
<i class="fa-solid fa-arrow-right"></i>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
<SearchHeader page="Books" @changeSearch="(s) => {editSearch(s)}"/>
|
||||||
|
<TableView ref="table" :headings="headings" width="15rem">
|
||||||
|
<tr v-for="book in this.$store.getters.getBooks" :key="book">
|
||||||
|
<td v-if="editRow === book.biblio_Index">
|
||||||
|
<DropDown
|
||||||
|
:values="this.bookFields.authors"
|
||||||
|
field="Auteur"
|
||||||
|
:inputValue="book.Auteur"
|
||||||
|
@selected="(n) => {formEdit.author = n}"
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
<td v-else>{{ book.Auteur }}</td>
|
||||||
|
<td v-if="editRow === book.biblio_Index">
|
||||||
|
<DropDown
|
||||||
|
:values="this.bookFields.titles"
|
||||||
|
field="Titre"
|
||||||
|
:inputValue="book.Titre"
|
||||||
|
@selected="(n) => {formEdit.title = n}"
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
<td v-else>{{ book.Titre }}</td>
|
||||||
|
<td v-if="editRow === book.biblio_Index">
|
||||||
|
<DropDown
|
||||||
|
:values="this.bookFields.editors"
|
||||||
|
field="Editeur"
|
||||||
|
:inputValue="book.Editeur"
|
||||||
|
@selected="(n) => {formEdit.editor = n}"
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
<td v-else>{{ book.Editeur }}</td>
|
||||||
|
<td v-if="editRow === book.biblio_Index">
|
||||||
|
<DropDown
|
||||||
|
:values="this.bookFields.types"
|
||||||
|
field="Type"
|
||||||
|
:inputValue="book.Type"
|
||||||
|
@selected="(n) => {formEdit.type = n}"
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
<td v-else>{{ book.Type }}</td>
|
||||||
|
<div class="button-panel">
|
||||||
|
<button class="button edit" @click="editElem(book.biblio_Index)">
|
||||||
|
<i v-if="editRow === book.biblio_Index" class="fa fa-check" aria-hidden="true"></i>
|
||||||
|
<i v-else class="fa fa-pencil-square-o" aria-hidden="true"></i>
|
||||||
|
</button>
|
||||||
|
<button class="button edit" @click="removeElem(book.biblio_Index)"><i class="fa fa-trash-o" aria-hidden="true"></i></button>
|
||||||
|
</div>
|
||||||
|
</tr>
|
||||||
|
</TableView>
|
||||||
|
</main>
|
||||||
|
<PaginationFooter/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import TableView from '../components/TableView.vue';
|
||||||
|
import PaginationFooter from '../components/PaginationFooter.vue';
|
||||||
|
import SearchHeader from '../components/SearchHeader.vue';
|
||||||
|
import DropDown from '../components/DropDown.vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
TableView,
|
||||||
|
PaginationFooter,
|
||||||
|
SearchHeader,
|
||||||
|
DropDown
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
headings: ['Author', 'Title', 'Editor', 'Type'],
|
||||||
|
editMode: false,
|
||||||
|
editRow: undefined,
|
||||||
|
addMode: false,
|
||||||
|
formEdit: {},
|
||||||
|
formAdd: {},
|
||||||
|
search: "",
|
||||||
|
sort: "",
|
||||||
|
bookFields: {},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.$store.dispatch('getBookField', 'Auteur').then((res) => {
|
||||||
|
if (res === null) {
|
||||||
|
this.$store.dispatch('logout');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.bookFields.authors = [...res];
|
||||||
|
|
||||||
|
})
|
||||||
|
this.$store.dispatch('getBookField', 'Titre').then((res) => {
|
||||||
|
if (res === null) {
|
||||||
|
this.$store.dispatch('logout');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.bookFields.titles = [...res];
|
||||||
|
})
|
||||||
|
this.$store.dispatch('getBookField', 'Editeur').then((res) => {
|
||||||
|
if (res === null) {
|
||||||
|
this.$store.dispatch('logout');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.bookFields.editors = [...res];
|
||||||
|
})
|
||||||
|
this.$store.dispatch('getBookField', 'Type').then((res) => {
|
||||||
|
if (res === null) {
|
||||||
|
this.$store.dispatch('logout');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.bookFields.types = [...res];
|
||||||
|
})
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
removeElem(id) {
|
||||||
|
this.$store.dispatch('removeBook', id)
|
||||||
|
},
|
||||||
|
toggleEditMode() {
|
||||||
|
this.addMode =! this.addMode
|
||||||
|
},
|
||||||
|
editSearch(search) {
|
||||||
|
this.search = search;
|
||||||
|
},
|
||||||
|
addElem() {
|
||||||
|
if (!this.formAdd.title || !this.formAdd.author)
|
||||||
|
return
|
||||||
|
this.$store.dispatch('addBook', {
|
||||||
|
'Titre': this.formAdd.title,
|
||||||
|
'Auteur': this.formAdd.author,
|
||||||
|
'Editeur': this.formAdd.editor,
|
||||||
|
'Type': this.formAdd.type,
|
||||||
|
'biblio_Index': new Date().valueOf()
|
||||||
|
})
|
||||||
|
this.addMode = true
|
||||||
|
this.formAdd = {}
|
||||||
|
},
|
||||||
|
editElem(id) {
|
||||||
|
this.editMode =! this.editMode
|
||||||
|
if (this.editMode || this.editRow !== id) {
|
||||||
|
let currentBook = this.$store.getters.getBooks.filter(b => b.biblio_Index === id)
|
||||||
|
this.formEdit.author = currentBook[0].Auteur
|
||||||
|
this.formEdit.title = currentBook[0].Titre
|
||||||
|
this.formEdit.editor = currentBook[0].Editeur
|
||||||
|
this.formEdit.type = currentBook[0].Type
|
||||||
|
this.editRow = id
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.editRow = undefined
|
||||||
|
let book = this.$store.getters.getBooks.filter(b => b.biblio_Index === id)[0]
|
||||||
|
let newObj = {author: '', title: '', type: '', editor: ''}
|
||||||
|
if (book) {
|
||||||
|
newObj.author = this.formEdit.author ? this.formEdit.author : book.Auteur
|
||||||
|
newObj.title = this.formEdit.title ? this.formEdit.title : book.Titre
|
||||||
|
newObj.type = this.formEdit.type ? this.formEdit.type : book.Type
|
||||||
|
newObj.editor = this.formEdit.editor ? this.formEdit.editor : book.Editeur
|
||||||
|
newObj.id = id
|
||||||
|
} else {
|
||||||
|
newObj.author = this.formEdit.author ? this.formEdit.author : ""
|
||||||
|
newObj.title = this.formEdit.title ? this.formEdit.title : ""
|
||||||
|
newObj.type = this.formEdit.type ? this.formEdit.type : ""
|
||||||
|
newObj.editor = this.formEdit.editor ? this.formEdit.editor : ""
|
||||||
|
newObj.id = id;
|
||||||
|
}
|
||||||
|
this.$store.dispatch('editBook', {id: id, newBook: newObj, search: this.search})
|
||||||
|
this.formEdit = {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style scoped>
|
||||||
|
main {
|
||||||
|
max-width: 60.5vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
td input {
|
||||||
|
width: 14.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes lineInsertedIn {
|
||||||
|
0% {
|
||||||
|
height: 0rem;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
height: 23rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
243
front/src/views/TheFilms.vue
Normal file
243
front/src/views/TheFilms.vue
Normal file
@@ -0,0 +1,243 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<header>
|
||||||
|
<h1>Films</h1>
|
||||||
|
<button v-if="!addMode" class="AddButton" v-show="!addMode" @click="toggleEditMode"><i class="fas fa-plus"></i></button>
|
||||||
|
<button v-else class="AddButton red" @click="addMode=!addMode">X</button>
|
||||||
|
</header>
|
||||||
|
<main>
|
||||||
|
<form ref="form" class="AddForm collapsed" v-on:submit.prevent="addElem" v-if="addMode">
|
||||||
|
<label for="Director">Director</label>
|
||||||
|
<input id="Director" v-model="formAdd.director" placeholder="director"/>
|
||||||
|
<label for="Title">Title</label>
|
||||||
|
<input id="Title" v-model="formAdd.title" placeholder="title"/>
|
||||||
|
<label for="Actors">Actors</label>
|
||||||
|
<input id="Actors" v-model="formAdd.actors" placeholder="Actors"/>
|
||||||
|
<label for="Length">Length</label>
|
||||||
|
<input id="Length" type="number" v-model="formAdd.length" placeholder="Lenght (in min)"/>
|
||||||
|
<label for="Producer">Producer</label>
|
||||||
|
<input id="Producer" v-model="formAdd.producer" placeholder="Producer"/>
|
||||||
|
<label for="Type">Type</label>
|
||||||
|
<input id="Type" v-model="formAdd.type" placeholder="type"/>
|
||||||
|
<button class="button edit">
|
||||||
|
<i class="fa-solid fa-arrow-right"></i>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
<SearchHeader page="Films"/>
|
||||||
|
<TableView ref="table" :headings="headings" width="10rem">
|
||||||
|
<tr :key="film" v-for="film in this.$store.getters.getFilms">
|
||||||
|
<td v-if="editRow === film.Number">
|
||||||
|
<DropDown
|
||||||
|
:values="this.filmFields.directors"
|
||||||
|
field="Director"
|
||||||
|
:inputValue="film.Director"
|
||||||
|
@selected="(n) => {formEdit.director = n}"
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
<td v-else>{{ film.Director }}</td>
|
||||||
|
<td v-if="editRow === film.Number">
|
||||||
|
<DropDown
|
||||||
|
:values="this.filmFields.titles"
|
||||||
|
field="Title"
|
||||||
|
:inputValue="film.Title"
|
||||||
|
@selected="(n) => {formEdit.title = n}"
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
<td v-else>{{ film.Title }}</td>
|
||||||
|
<td v-if="editRow === film.Number">
|
||||||
|
<DropDown
|
||||||
|
:values="this.filmFields.actors"
|
||||||
|
field="Actors"
|
||||||
|
:inputValue="film.Actors"
|
||||||
|
@selected="(n) => {formEdit.actors = n}"
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
<td v-else>{{ film.Actors }}</td>
|
||||||
|
<td v-if="editRow === film.Number">
|
||||||
|
<input :placeholder="film.Length" v-model="formEdit.length"/>
|
||||||
|
</td>
|
||||||
|
<td v-else>{{ film.Length }}</td>
|
||||||
|
<td v-if="editRow === film.Number">
|
||||||
|
<DropDown
|
||||||
|
:values="this.filmFields.producers"
|
||||||
|
field="Producer"
|
||||||
|
:inputValue="film.Producer"
|
||||||
|
@selected="(n) => {formEdit.producer = n}"
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
<td v-else>{{ film.Producer }}</td>
|
||||||
|
<td v-if="editRow === film.Number">
|
||||||
|
<DropDown
|
||||||
|
:values="this.filmFields.types"
|
||||||
|
field="Type"
|
||||||
|
:inputValue="film.Type"
|
||||||
|
@selected="(n) => {formEdit.type = n}"
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
<td v-else>{{ film.Type }}</td>
|
||||||
|
<div class="button-panel">
|
||||||
|
<button class="button edit" @click="editElem(film.Number)">
|
||||||
|
<i v-if="editRow === film.Number" class="fa fa-check" aria-hidden="true"></i>
|
||||||
|
<i v-else class="fa fa-pencil-square-o" aria-hidden="true"></i>
|
||||||
|
</button>
|
||||||
|
<button class="button edit" @click="removeElem(film.Number)"><i class="fa fa-trash-o" aria-hidden="true"></i></button>
|
||||||
|
</div>
|
||||||
|
</tr>
|
||||||
|
</TableView>
|
||||||
|
</main>
|
||||||
|
<footer v-if="$store.getters.getFilmPageNb <= 5">
|
||||||
|
<button class="page" v-for="index in $store.getters.getFilmCurrentPage + 1" :key="index">{{ index }}</button>
|
||||||
|
</footer>
|
||||||
|
<footer v-else>
|
||||||
|
<button @click="fetchPage(0)" class="page"> {{ '<<' }} </button>
|
||||||
|
<button
|
||||||
|
class="page"
|
||||||
|
:class="{ 'red': $store.getters.getFilmCurrentPage - Math.max(0, Math.min($store.getters.getFilmCurrentPage - 2, $store.getters.getFilmPageNb - 5)) === index}"
|
||||||
|
v-for="(n, index) in (Math.max(5, Math.min($store.getters.getFilmCurrentPage + 2, $store.getters.getFilmPageNb)) - Math.max(0, Math.min($store.getters.getFilmCurrentPage - 2, $store.getters.getFilmPageNb - 5)))"
|
||||||
|
:key="index"
|
||||||
|
@click="fetchPage(Math.max(0, Math.min($store.getters.getFilmCurrentPage - 2, $store.getters.getFilmPageNb - 5)) + index)">
|
||||||
|
{{ Math.max(0, Math.min($store.getters.getFilmCurrentPage - 2, $store.getters.getFilmPageNb - 5)) + index }}
|
||||||
|
</button>
|
||||||
|
<p v-if="$store.getters.getFilmCurrentPage !== $store.getters.getFilmPageNb" class="elipsis">...</p>
|
||||||
|
<button v-if="$store.getters.getFilmCurrentPage !== $store.getters.getFilmPageNb" class="page" @click="fetchPage($store.getters.getFilmPageNb)">{{ $store.getters.getFilmPageNb }}</button>
|
||||||
|
<button @click="fetchPage($store.getters.getFilmPageNb)" class="page"> >> </button>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import TableView from '../components/TableView.vue';
|
||||||
|
import DropDown from '../components/DropDown.vue';
|
||||||
|
import SearchHeader from '../components/SearchHeader.vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
TableView,
|
||||||
|
DropDown,
|
||||||
|
SearchHeader
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
headings: ['Director', 'Title', 'Actors', 'Length', 'Producer', 'Type'],
|
||||||
|
editMode: false,
|
||||||
|
editRow: undefined,
|
||||||
|
addMode: false,
|
||||||
|
formEdit: {},
|
||||||
|
formAdd: {},
|
||||||
|
search: "",
|
||||||
|
sort: "",
|
||||||
|
filmFields: {},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.$store.dispatch('getFilmField', 'Director').then((res) => {
|
||||||
|
this.filmFields.directors = [...res];
|
||||||
|
})
|
||||||
|
this.$store.dispatch('getFilmField', 'Title').then((res) => {
|
||||||
|
this.filmFields.titles = [...res];
|
||||||
|
})
|
||||||
|
this.$store.dispatch('getFilmField', 'Actors').then((res) => {
|
||||||
|
this.filmFields.actors = [...res];
|
||||||
|
})
|
||||||
|
this.$store.dispatch('getFilmField', 'Producer').then((res) => {
|
||||||
|
this.filmFields.producers = [...res];
|
||||||
|
})
|
||||||
|
this.$store.dispatch('getFilmField', 'Type').then((res) => {
|
||||||
|
this.filmFields.types = [...res];
|
||||||
|
})
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
removeElem(id) {
|
||||||
|
this.$store.dispatch('removeFilm', id)
|
||||||
|
},
|
||||||
|
toggleEditMode() {
|
||||||
|
this.addMode =! this.addMode
|
||||||
|
},
|
||||||
|
addElem() {
|
||||||
|
if (!this.formAdd.title || !this.formAdd.director
|
||||||
|
|| !this.formAdd.producer || !this.formAdd.type
|
||||||
|
|| !this.formAdd.actors || !this.formAdd.length) {
|
||||||
|
//TODO: error handling
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.$store.dispatch('addFilm', {
|
||||||
|
'Title': this.formAdd.title,
|
||||||
|
'Director': this.formAdd.director,
|
||||||
|
'Producer': this.formAdd.producer,
|
||||||
|
'Type': this.formAdd.type,
|
||||||
|
'Actors': this.formAdd.actors,
|
||||||
|
'Length': this.formAdd.length,
|
||||||
|
'Number': new Date().valueOf()
|
||||||
|
})
|
||||||
|
this.addMode = true
|
||||||
|
this.formAdd = {}
|
||||||
|
},
|
||||||
|
editElem(id) {
|
||||||
|
this.editMode =! this.editMode
|
||||||
|
if (this.editMode || this.editRow !== id) {
|
||||||
|
this.editRow = id
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.editRow = undefined;
|
||||||
|
let ele = this.$store.getters.getFilms.filter(test => test.Number === id)[0]
|
||||||
|
let newObj = {Director: '', Title: '', Producer: '', Type: '', Length: '', Actors: ''}
|
||||||
|
if (ele) {
|
||||||
|
newObj.Director = this.formEdit.director ? this.formEdit.director : ele.Director
|
||||||
|
newObj.Producer = this.formEdit.producer ? this.formEdit.producer : ele.Producer
|
||||||
|
newObj.Title = this.formEdit.title ? this.formEdit.title : ele.Title
|
||||||
|
newObj.Type = this.formEdit.type ? this.formEdit.type : ele.Type
|
||||||
|
newObj.Length = this.formEdit.length ? this.formEdit.length : ele.Length
|
||||||
|
newObj.Actors = this.formEdit.actors ? this.formEdit.actors : ele.Actors
|
||||||
|
newObj.Number = id
|
||||||
|
} else {
|
||||||
|
newObj.Director = this.formEdit.director ? this.formEdit.director : ""
|
||||||
|
newObj.Producer = this.formEdit.producer ? this.formEdit.producer : ""
|
||||||
|
newObj.Title = this.formEdit.title ? this.formEdit.title : ""
|
||||||
|
newObj.Type = this.formEdit.type ? this.formEdit.type : ""
|
||||||
|
newObj.Length = this.formEdit.length ? this.formEdit.length : ""
|
||||||
|
newObj.Actors = this.formEdit.actors ? this.formEdit.actors : ""
|
||||||
|
newObj.Number = id
|
||||||
|
}
|
||||||
|
this.$store.dispatch('editFilm', {id: id, newfilm: newObj})
|
||||||
|
this.formEdit = {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fetchPage(page, nooption=false) {
|
||||||
|
this.$store.commit('setFilmCurrentPage', page)
|
||||||
|
if (nooption) {
|
||||||
|
this.$store.dispatch('getFilms', {page: page, search: "", order: ""})
|
||||||
|
} else {
|
||||||
|
this.$store.dispatch('getFilms', {page: page, search: this.search, order: this.sort})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style scoped>
|
||||||
|
main {
|
||||||
|
width: 80vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.elipsis {
|
||||||
|
font-size: 3rem
|
||||||
|
}
|
||||||
|
|
||||||
|
.page {
|
||||||
|
border: none;
|
||||||
|
padding: 1rem;
|
||||||
|
margin: 0.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
display: flex;
|
||||||
|
padding: 0.2rem;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
</style>
|
||||||
33
front/src/views/TheHome.vue
Normal file
33
front/src/views/TheHome.vue
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<header>
|
||||||
|
<h1>Home</h1>
|
||||||
|
</header>
|
||||||
|
<main>
|
||||||
|
<h2>Welcome to the Library</h2>
|
||||||
|
<p>You can manage, edit, delete book references from the home library</p>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style scoped>
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
64
front/src/views/TheLogin.vue
Normal file
64
front/src/views/TheLogin.vue
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<header>
|
||||||
|
<h1>Login</h1>
|
||||||
|
</header>
|
||||||
|
<main @keydown.enter="login()">
|
||||||
|
<label for="username">Username</label>
|
||||||
|
<input id="username" type="text" v-model="username"/>
|
||||||
|
<label for="password">Password</label>
|
||||||
|
<input id="password" type="password" v-model="password"/>
|
||||||
|
<button @click="login">Login</button>
|
||||||
|
</main>
|
||||||
|
<footer>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
username: "",
|
||||||
|
password: ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
login() {
|
||||||
|
if (this.username !== "" && this.password !== "")
|
||||||
|
this.$store.dispatch('login', {'username': this.username, 'password': this.password}).then(() => {
|
||||||
|
this.username = ""
|
||||||
|
this.password = ""
|
||||||
|
this.$router.push(this.$route.query.redirect || '/books')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style scoped>
|
||||||
|
button {
|
||||||
|
margin-top: 0.5rem
|
||||||
|
}
|
||||||
|
label {
|
||||||
|
margin: 0.2rem;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
input {
|
||||||
|
background-color: rgba(0.1, 0.1, 0.1, 0.1);
|
||||||
|
border: none
|
||||||
|
}
|
||||||
|
input:hover {
|
||||||
|
background-color: rgba(0.1, 0.1, 0.1, 0.1);
|
||||||
|
}
|
||||||
|
main {
|
||||||
|
width : 20rem;
|
||||||
|
align-self: center;
|
||||||
|
display: flex;
|
||||||
|
align-content: center;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
div {
|
||||||
|
flex-direction: column;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
4
front/vue.config.js
Normal file
4
front/vue.config.js
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
const { defineConfig } = require('@vue/cli-service')
|
||||||
|
module.exports = defineConfig({
|
||||||
|
transpileDependencies: true
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user