Compare commits

...

4 Commits
main ... api

Author SHA1 Message Date
Wojciech Kwolek eb70dc325a change graph color 2021-12-04 10:50:42 +01:00
Wojciech Kwolek a52dce3be2 add average to graph 2021-12-04 10:43:09 +01:00
Wojciech Kwolek b18dfd81a5 add API key management 2021-11-19 13:19:42 +00:00
Wojciech Kwolek 2ea8f79dd9 add APIKey model 2021-11-17 20:33:39 +00:00
13 changed files with 573 additions and 203 deletions

View File

@ -15,3 +15,4 @@ flask-migrate = "*"
flask-wtf = "*" flask-wtf = "*"
flask-static-digest = "*" flask-static-digest = "*"
pytz = "*" pytz = "*"
flask-restful = "*"

420
Pipfile.lock generated
View File

@ -1,7 +1,7 @@
{ {
"_meta": { "_meta": {
"hash": { "hash": {
"sha256": "5df2ce53af8895efe513915c12c6cc3b4b86b8386b571ccdd9358a06b75c4811" "sha256": "69d1c3aaa87636cd841bbdb7f6c6c0e4f6657da8db1405f8df0c38cadcc03643"
}, },
"pipfile-spec": 6, "pipfile-spec": 6,
"requires": {}, "requires": {},
@ -16,11 +16,18 @@
"default": { "default": {
"alembic": { "alembic": {
"hashes": [ "hashes": [
"sha256:4e02ed2aa796bd179965041afa092c55b51fb077de19d61835673cc80672c01c", "sha256:7c328694a2e68f03ee971e63c3bd885846470373a5b532cf2c9f1601c413b153",
"sha256:5334f32314fb2a56d86b4c4dd1ae34b08c03cae4cb888bc699942104d66bc245" "sha256:a9dde941534e3d7573d9644e8ea62a2953541e27bc1793e166f60b777ae098b4"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "markers": "python_version >= '3.6'",
"version": "==1.4.3" "version": "==1.7.5"
},
"aniso8601": {
"hashes": [
"sha256:1d2b7ef82963909e93c4f24ce48d4de9e66009a21bf1c1e1c85bdd0812fe412f",
"sha256:72e3117667eedf66951bb2d93f4296a56b94b078a8a95905a052611fb3f1b973"
],
"version": "==9.0.1"
}, },
"bcrypt": { "bcrypt": {
"hashes": [ "hashes": [
@ -37,60 +44,74 @@
}, },
"cffi": { "cffi": {
"hashes": [ "hashes": [
"sha256:005f2bfe11b6745d726dbb07ace4d53f057de66e336ff92d61b8c7e9c8f4777d", "sha256:00c878c90cb53ccfaae6b8bc18ad05d2036553e6d9d1d9dbcf323bbe83854ca3",
"sha256:09e96138280241bd355cd585148dec04dbbedb4f46128f340d696eaafc82dd7b", "sha256:0104fb5ae2391d46a4cb082abdd5c69ea4eab79d8d44eaaf79f1b1fd806ee4c2",
"sha256:0b1ad452cc824665ddc682400b62c9e4f5b64736a2ba99110712fdee5f2505c4", "sha256:06c48159c1abed75c2e721b1715c379fa3200c7784271b3c46df01383b593636",
"sha256:0ef488305fdce2580c8b2708f22d7785ae222d9825d3094ab073e22e93dfe51f", "sha256:0808014eb713677ec1292301ea4c81ad277b6cdf2fdd90fd540af98c0b101d20",
"sha256:15f351bed09897fbda218e4db5a3d5c06328862f6198d4fb385f3e14e19decb3", "sha256:10dffb601ccfb65262a27233ac273d552ddc4d8ae1bf93b21c94b8511bffe728",
"sha256:22399ff4870fb4c7ef19fff6eeb20a8bbf15571913c181c78cb361024d574579", "sha256:14cd121ea63ecdae71efa69c15c5543a4b5fbcd0bbe2aad864baca0063cecf27",
"sha256:23e5d2040367322824605bc29ae8ee9175200b92cb5483ac7d466927a9b3d537", "sha256:17771976e82e9f94976180f76468546834d22a7cc404b17c22df2a2c81db0c66",
"sha256:2791f68edc5749024b4722500e86303a10d342527e1e3bcac47f35fbd25b764e", "sha256:181dee03b1170ff1969489acf1c26533710231c58f95534e3edac87fff06c443",
"sha256:2f9674623ca39c9ebe38afa3da402e9326c245f0f5ceff0623dccdac15023e05", "sha256:23cfe892bd5dd8941608f93348c0737e369e51c100d03718f108bf1add7bd6d0",
"sha256:3363e77a6176afb8823b6e06db78c46dbc4c7813b00a41300a4873b6ba63b171", "sha256:263cc3d821c4ab2213cbe8cd8b355a7f72a8324577dc865ef98487c1aeee2bc7",
"sha256:33c6cdc071ba5cd6d96769c8969a0531be2d08c2628a0143a10a7dcffa9719ca", "sha256:2756c88cbb94231c7a147402476be2c4df2f6078099a6f4a480d239a8817ae39",
"sha256:3b8eaf915ddc0709779889c472e553f0d3e8b7bdf62dab764c8921b09bf94522", "sha256:27c219baf94952ae9d50ec19651a687b826792055353d07648a5695413e0c605",
"sha256:3cb3e1b9ec43256c4e0f8d2837267a70b0e1ca8c4f456685508ae6106b1f504c", "sha256:2a23af14f408d53d5e6cd4e3d9a24ff9e05906ad574822a10563efcef137979a",
"sha256:3eeeb0405fd145e714f7633a5173318bd88d8bbfc3dd0a5751f8c4f70ae629bc", "sha256:31fb708d9d7c3f49a60f04cf5b119aeefe5644daba1cd2a0fe389b674fd1de37",
"sha256:44f60519595eaca110f248e5017363d751b12782a6f2bd6a7041cba275215f5d", "sha256:3415c89f9204ee60cd09b235810be700e993e343a408693e80ce7f6a40108029",
"sha256:4d7c26bfc1ea9f92084a1d75e11999e97b62d63128bcc90c3624d07813c52808", "sha256:3773c4d81e6e818df2efbc7dd77325ca0dcb688116050fb2b3011218eda36139",
"sha256:529c4ed2e10437c205f38f3691a68be66c39197d01062618c55f74294a4a4828", "sha256:3b96a311ac60a3f6be21d2572e46ce67f09abcf4d09344c49274eb9e0bf345fc",
"sha256:6642f15ad963b5092d65aed022d033c77763515fdc07095208f15d3563003869", "sha256:3f7d084648d77af029acb79a0ff49a0ad7e9d09057a9bf46596dac9514dc07df",
"sha256:85ba797e1de5b48aa5a8427b6ba62cf69607c18c5d4eb747604b7302f1ec382d", "sha256:41d45de54cd277a7878919867c0f08b0cf817605e4eb94093e7516505d3c8d14",
"sha256:8f0f1e499e4000c4c347a124fa6a27d37608ced4fe9f7d45070563b7c4c370c9", "sha256:4238e6dab5d6a8ba812de994bbb0a79bddbdf80994e4ce802b6f6f3142fcc880",
"sha256:a624fae282e81ad2e4871bdb767e2c914d0539708c0f078b5b355258293c98b0", "sha256:45db3a33139e9c8f7c09234b5784a5e33d31fd6907800b316decad50af323ff2",
"sha256:b0358e6fefc74a16f745afa366acc89f979040e0cbc4eec55ab26ad1f6a9bfbc", "sha256:45e8636704eacc432a206ac7345a5d3d2c62d95a507ec70d62f23cd91770482a",
"sha256:bbd2f4dfee1079f76943767fce837ade3087b578aeb9f69aec7857d5bf25db15", "sha256:4958391dbd6249d7ad855b9ca88fae690783a6be9e86df65865058ed81fc860e",
"sha256:bf39a9e19ce7298f1bd6a9758fa99707e9e5b1ebe5e90f2c3913a47bc548747c", "sha256:4a306fa632e8f0928956a41fa8e1d6243c71e7eb59ffbd165fc0b41e316b2474",
"sha256:c11579638288e53fc94ad60022ff1b67865363e730ee41ad5e6f0a17188b327a", "sha256:57e9ac9ccc3101fac9d6014fba037473e4358ef4e89f8e181f8951a2c0162024",
"sha256:c150eaa3dadbb2b5339675b88d4573c1be3cb6f2c33a6c83387e10cc0bf05bd3", "sha256:59888172256cac5629e60e72e86598027aca6bf01fa2465bdb676d37636573e8",
"sha256:c53af463f4a40de78c58b8b2710ade243c81cbca641e34debf3396a9640d6ec1", "sha256:5e069f72d497312b24fcc02073d70cb989045d1c91cbd53979366077959933e0",
"sha256:cb763ceceae04803adcc4e2d80d611ef201c73da32d8f2722e9d0ab0c7f10768", "sha256:64d4ec9f448dfe041705426000cc13e34e6e5bb13736e9fd62e34a0b0c41566e",
"sha256:cc75f58cdaf043fe6a7a6c04b3b5a0e694c6a9e24050967747251fb80d7bce0d", "sha256:6dc2737a3674b3e344847c8686cf29e500584ccad76204efea14f451d4cc669a",
"sha256:d80998ed59176e8cba74028762fbd9b9153b9afc71ea118e63bbf5d4d0f9552b", "sha256:74fdfdbfdc48d3f47148976f49fab3251e550a8720bebc99bf1483f5bfb5db3e",
"sha256:de31b5164d44ef4943db155b3e8e17929707cac1e5bd2f363e67a56e3af4af6e", "sha256:75e4024375654472cc27e91cbe9eaa08567f7fbdf822638be2814ce059f58032",
"sha256:e66399cf0fc07de4dce4f588fc25bfe84a6d1285cc544e67987d22663393926d", "sha256:786902fb9ba7433aae840e0ed609f45c7bcd4e225ebb9c753aa39725bb3e6ad6",
"sha256:f0620511387790860b249b9241c2f13c3a80e21a73e0b861a2df24e9d6f56730", "sha256:8b6c2ea03845c9f501ed1313e78de148cd3f6cad741a75d43a29b43da27f2e1e",
"sha256:f4eae045e6ab2bb54ca279733fe4eb85f1effda392666308250714e01907f394", "sha256:91d77d2a782be4274da750752bb1650a97bfd8f291022b379bb8e01c66b4e96b",
"sha256:f92cdecb618e5fa4658aeb97d5eb3d2f47aa94ac6477c6daf0f306c5a3b9e6b1", "sha256:91ec59c33514b7c7559a6acda53bbfe1b283949c34fe7440bcf917f96ac0723e",
"sha256:f92f789e4f9241cd262ad7a555ca2c648a98178a953af117ef7fad46aa1d5591" "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.14.3" "version": "==1.15.0"
}, },
"click": { "click": {
"hashes": [ "hashes": [
"sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a", "sha256:353f466495adaeb40b6b5f592f9f91cb22372351c84caeb068132442a4518ef3",
"sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc" "sha256:410e932b050f5eed773c4cda94de75971c89cdb3155a72a0831139a79e5ecb5b"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", "markers": "python_version >= '3.6'",
"version": "==7.1.2" "version": "==8.0.3"
}, },
"flask": { "flask": {
"hashes": [ "hashes": [
"sha256:4efa1ae2d7c9865af48986de8aeb8504bf32c7f3d6fdc9353d34b21f4b127060", "sha256:7b2fb8e934ddd50731893bdcdb00fc8c0315916f9fcd50d22c7cc1a95ab634e2",
"sha256:8a4fdd8936eba2512e9c85df320a37e694c93945b33ef33c89946a340a238557" "sha256:cb90f62f1d8e4dc4621f52106613488b5ba826b2e1e10a33eac92f723093ab6a"
], ],
"index": "pypi", "index": "pypi",
"version": "==1.1.2" "version": "==2.0.2"
}, },
"flask-bcrypt": { "flask-bcrypt": {
"hashes": [ "hashes": [
@ -109,216 +130,249 @@
}, },
"flask-migrate": { "flask-migrate": {
"hashes": [ "hashes": [
"sha256:4dc4a5cce8cbbb06b8dc963fd86cf8136bd7d875aabe2d840302ea739b243732", "sha256:57d6060839e3a7f150eaab6fe4e726d9e3e7cffe2150fb223d73f92421c6d1d9",
"sha256:a69d508c2e09d289f6e55a417b3b8c7bfe70e640f53d2d9deb0d056a384f37ee" "sha256:a6498706241aba6be7a251078de9cf166d74307bca41a4ca3e403c9d39e2f897"
], ],
"index": "pypi", "index": "pypi",
"version": "==2.5.3" "version": "==3.1.0"
},
"flask-restful": {
"hashes": [
"sha256:4970c49b6488e46c520b325f54833374dc2b98e211f1b272bd4b0c516232afe2",
"sha256:ccec650b835d48192138c85329ae03735e6ced58e9b2d9c2146d6c84c06fa53e"
],
"index": "pypi",
"version": "==0.3.9"
}, },
"flask-sqlalchemy": { "flask-sqlalchemy": {
"hashes": [ "hashes": [
"sha256:05b31d2034dd3f2a685cbbae4cfc4ed906b2a733cff7964ada450fd5e462b84e", "sha256:2bda44b43e7cacb15d4e05ff3cc1f8bc97936cc464623424102bfc2c35e95912",
"sha256:bfc7150eaf809b1c283879302f04c42791136060c6eeb12c0c6674fb1291fae5" "sha256:f12c3d4cc5cc7fdcc148b9527ea05671718c3ea45d50c7e732cceb33f574b390"
], ],
"index": "pypi", "index": "pypi",
"version": "==2.4.4" "version": "==2.5.1"
}, },
"flask-static-digest": { "flask-static-digest": {
"hashes": [ "hashes": [
"sha256:ab342609f5c345d37e35070d2ab56a1d29da2905f892b0b2e30a1e2cdb0ed76d" "sha256:7528b08a2c12dc3a828096b66ee2612f323d50be36d01f2817b767fbb1ceca6e"
], ],
"index": "pypi", "index": "pypi",
"version": "==0.1.3" "version": "==0.2.1"
}, },
"flask-wtf": { "flask-wtf": {
"hashes": [ "hashes": [
"sha256:57b3faf6fe5d6168bda0c36b0df1d05770f8e205e18332d0376ddb954d17aef2", "sha256:01feccfc395405cea48a3f36c23f0d766e2cc6fd2a5a065ad50ad3e5827ec797",
"sha256:d417e3a0008b5ba583da1763e4db0f55a1269d9dd91dcc3eb3c026d3c5dbd720" "sha256:872fbb17b5888bfc734edbdcf45bc08fb365ca39f69d25dc752465a455517b28"
], ],
"index": "pypi", "index": "pypi",
"version": "==0.14.3" "version": "==1.0.0"
}, },
"itsdangerous": { "itsdangerous": {
"hashes": [ "hashes": [
"sha256:321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19", "sha256:5174094b9637652bdb841a3029700391451bd092ba3db90600dea710ba28e97c",
"sha256:b12271b2047cb23eeb98c8b5622e2e5c5e9abd9784a153e9d8ef9cb4dd09d749" "sha256:9e724d68fc22902a1435351f84c3fb8623f303fffcc566a4cb952df8c572cff0"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "markers": "python_version >= '3.6'",
"version": "==1.1.0" "version": "==2.0.1"
}, },
"jinja2": { "jinja2": {
"hashes": [ "hashes": [
"sha256:89aab215427ef59c34ad58735269eb58b1a5808103067f7bb9d5836c651b3bb0", "sha256:077ce6014f7b40d03b47d1f1ca4b0fc8328a692bd284016f806ed0eaca390ad8",
"sha256:f0a4641d3cf955324a89c04f3d94663aa4d638abe8f733ecd3582848e1c37035" "sha256:611bb273cd68f3b993fabdc4064fc858c5b47a973cb5aa7999ec1ba405c87cd7"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", "markers": "python_version >= '3.6'",
"version": "==2.11.2" "version": "==3.0.3"
}, },
"mako": { "mako": {
"hashes": [ "hashes": [
"sha256:8195c8c1400ceb53496064314c6736719c6f25e7479cd24c77be3d9361cddc27", "sha256:4e9e345a41924a954251b95b4b28e14a301145b544901332e658907a7464b6b2",
"sha256:93729a258e4ff0747c876bd9e20df1b9758028946e976324ccd2d68245c7b6a9" "sha256:afaf8e515d075b22fad7d7b8b30e4a1c90624ff2f3733a06ec125f5a5f043a57"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.1.3" "version": "==1.1.6"
}, },
"markupsafe": { "markupsafe": {
"hashes": [ "hashes": [
"sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473", "sha256:01a9b8ea66f1658938f65b93a85ebe8bc016e6769611be228d797c9d998dd298",
"sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161", "sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64",
"sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235", "sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b",
"sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5", "sha256:04635854b943835a6ea959e948d19dcd311762c5c0c6e1f0e16ee57022669194",
"sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42", "sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567",
"sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff", "sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff",
"sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b", "sha256:0d4b31cc67ab36e3392bbf3862cfbadac3db12bdd8b02a2731f509ed5b829724",
"sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1", "sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74",
"sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e", "sha256:168cd0a3642de83558a5153c8bd34f175a9a6e7f6dc6384b9655d2697312a646",
"sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183", "sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35",
"sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66", "sha256:1f2ade76b9903f39aa442b4aadd2177decb66525062db244b35d71d0ee8599b6",
"sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b", "sha256:20dca64a3ef2d6e4d5d615a3fd418ad3bde77a47ec8a23d984a12b5b4c74491a",
"sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1", "sha256:2a7d351cbd8cfeb19ca00de495e224dea7e7d919659c2841bbb7f420ad03e2d6",
"sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15", "sha256:2d7d807855b419fc2ed3e631034685db6079889a1f01d5d9dac950f764da3dad",
"sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1", "sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26",
"sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e", "sha256:36bc903cbb393720fad60fc28c10de6acf10dc6cc883f3e24ee4012371399a38",
"sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b", "sha256:37205cac2a79194e3750b0af2a5720d95f786a55ce7df90c3af697bfa100eaac",
"sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905", "sha256:3c112550557578c26af18a1ccc9e090bfe03832ae994343cfdacd287db6a6ae7",
"sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735", "sha256:3dd007d54ee88b46be476e293f48c85048603f5f516008bee124ddd891398ed6",
"sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d", "sha256:4296f2b1ce8c86a6aea78613c34bb1a672ea0e3de9c6ba08a960efe0b0a09047",
"sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e", "sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75",
"sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d", "sha256:49e3ceeabbfb9d66c3aef5af3a60cc43b85c33df25ce03d0031a608b0a8b2e3f",
"sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c", "sha256:4dc8f9fb58f7364b63fd9f85013b780ef83c11857ae79f2feda41e270468dd9b",
"sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21", "sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135",
"sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2", "sha256:53edb4da6925ad13c07b6d26c2a852bd81e364f95301c66e930ab2aef5b5ddd8",
"sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5", "sha256:5855f8438a7d1d458206a2466bf82b0f104a3724bf96a1c781ab731e4201731a",
"sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b", "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a",
"sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6", "sha256:5b6d930f030f8ed98e3e6c98ffa0652bdb82601e7a016ec2ab5d7ff23baa78d1",
"sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f", "sha256:5bb28c636d87e840583ee3adeb78172efc47c8b26127267f54a9c0ec251d41a9",
"sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f", "sha256:60bf42e36abfaf9aff1f50f52644b336d4f0a3fd6d8a60ca0d054ac9f713a864",
"sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2", "sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914",
"sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7", "sha256:6300b8454aa6930a24b9618fbb54b5a68135092bc666f7b06901f897fa5c2fee",
"sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be" "sha256:63f3268ba69ace99cab4e3e3b5840b03340efed0948ab8f78d2fd87ee5442a4f",
"sha256:6557b31b5e2c9ddf0de32a691f2312a32f77cd7681d8af66c2692efdbef84c18",
"sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8",
"sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2",
"sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d",
"sha256:6fcf051089389abe060c9cd7caa212c707e58153afa2c649f00346ce6d260f1b",
"sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b",
"sha256:89c687013cb1cd489a0f0ac24febe8c7a666e6e221b783e53ac50ebf68e45d86",
"sha256:8d206346619592c6200148b01a2142798c989edcb9c896f9ac9722a99d4e77e6",
"sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f",
"sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb",
"sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833",
"sha256:99df47edb6bda1249d3e80fdabb1dab8c08ef3975f69aed437cb69d0a5de1e28",
"sha256:9f02365d4e99430a12647f09b6cc8bab61a6564363f313126f775eb4f6ef798e",
"sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415",
"sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902",
"sha256:aca6377c0cb8a8253e493c6b451565ac77e98c2951c45f913e0b52facdcff83f",
"sha256:add36cb2dbb8b736611303cd3bfcee00afd96471b09cda130da3581cbdc56a6d",
"sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9",
"sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d",
"sha256:baa1a4e8f868845af802979fcdbf0bb11f94f1cb7ced4c4b8a351bb60d108145",
"sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066",
"sha256:bf5d821ffabf0ef3533c39c518f3357b171a1651c1ff6827325e4489b0e46c3c",
"sha256:c47adbc92fc1bb2b3274c4b3a43ae0e4573d9fbff4f54cd484555edbf030baf1",
"sha256:cdfba22ea2f0029c9261a4bd07e830a8da012291fbe44dc794e488b6c9bb353a",
"sha256:d6c7ebd4e944c85e2c3421e612a7057a2f48d478d79e61800d81468a8d842207",
"sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f",
"sha256:d8446c54dc28c01e5a2dbac5a25f071f6653e6e40f3a8818e8b45d790fe6ef53",
"sha256:deb993cacb280823246a026e3b2d81c493c53de6acfd5e6bfe31ab3402bb37dd",
"sha256:e0f138900af21926a02425cf736db95be9f4af72ba1bb21453432a07f6082134",
"sha256:e9936f0b261d4df76ad22f8fee3ae83b60d7c3e871292cd42f40b81b70afae85",
"sha256:f0567c4dc99f264f49fe27da5f735f414c4e7e7dd850cfd8e69f0862d7c74ea9",
"sha256:f5653a225f31e113b152e56f154ccbe59eeb1c7487b39b9d9f9cdb58e6c79dc5",
"sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94",
"sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509",
"sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51",
"sha256:fa130dd50c57d53368c9d59395cb5526eda596d3ffe36666cd81a44d56e48872"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "markers": "python_version >= '3.6'",
"version": "==1.1.1" "version": "==2.0.1"
}, },
"pycparser": { "pycparser": {
"hashes": [ "hashes": [
"sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0", "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9",
"sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705" "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.20" "version": "==2.21"
},
"python-dateutil": {
"hashes": [
"sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c",
"sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.8.1"
},
"python-editor": {
"hashes": [
"sha256:1bf6e860a8ad52a14c3ee1252d5dc25b2030618ed80c022598f00176adc8367d",
"sha256:51fda6bcc5ddbbb7063b2af7509e43bd84bfc32a4ff71349ec7847713882327b",
"sha256:5f98b069316ea1c2ed3f67e7f5df6c0d8f10b689964a4a811ff64f0106819ec8",
"sha256:c3da2053dbab6b29c94e43c486ff67206eafbe7eb52dbec7390b5e2fb05aac77",
"sha256:ea87e17f6ec459e780e4221f295411462e0d0810858e055fc514684350a2f522"
],
"version": "==1.0.4"
}, },
"pytz": { "pytz": {
"hashes": [ "hashes": [
"sha256:a494d53b6d39c3c6e44c3bec237336e14305e4f29bbf800b599253057fbb79ed", "sha256:3672058bc3453457b622aab7a1c3bfd5ab0bdae451512f6cf25f64ed37f5b87c",
"sha256:c35965d010ce31b23eeb663ed3cc8c906275d6be1a34393a1d73a41febf4a048" "sha256:acad2d8b20a1af07d4e4c9d2e9285c5ed9104354062f275f3fcd88dcef4f1326"
], ],
"index": "pypi", "index": "pypi",
"version": "==2020.1" "version": "==2021.3"
}, },
"six": { "six": {
"hashes": [ "hashes": [
"sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259", "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
"sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced" "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.15.0" "version": "==1.16.0"
}, },
"sqlalchemy": { "sqlalchemy": {
"hashes": [ "hashes": [
"sha256:072766c3bd09294d716b2d114d46ffc5ccf8ea0b714a4e1c48253014b771c6bb", "sha256:015511c52c650eebf1059ed8a21674d9d4ae567ebfd80fc73f8252faccd71864",
"sha256:107d4af989831d7b091e382d192955679ec07a9209996bf8090f1f539ffc5804", "sha256:0438bccc16349db2d5203598be6073175ce16d4e53b592d6e6cef880c197333e",
"sha256:15c0bcd3c14f4086701c33a9e87e2c7ceb3bcb4a246cd88ec54a49cf2a5bd1a6", "sha256:10230364479429437f1b819a8839f1edc5744c018bfeb8d01320930f97695bc9",
"sha256:26c5ca9d09f0e21b8671a32f7d83caad5be1f6ff45eef5ec2f6fd0db85fc5dc0", "sha256:2146ef996181e3d4dd20eaf1d7325eb62d6c8aa4dc1677c1872ddfa8561a47d9",
"sha256:276936d41111a501cf4a1a0543e25449108d87e9f8c94714f7660eaea89ae5fe", "sha256:24828c5e74882cf41516740c0b150702bee4c6817d87d5c3d3bafef2e6896f80",
"sha256:3292a28344922415f939ee7f4fc0c186f3d5a0bf02192ceabd4f1129d71b08de", "sha256:2717ceae35e71de1f58b0d1ee7e773d3aab5c403c6e79e8d262277c7f7f95269",
"sha256:33d29ae8f1dc7c75b191bb6833f55a19c932514b9b5ce8c3ab9bc3047da5db36", "sha256:2e93624d186ea7a738ada47314701c8830e0e4b021a6bce7fbe6f39b87ee1516",
"sha256:3bba2e9fbedb0511769780fe1d63007081008c5c2d7d715e91858c94dbaa260e", "sha256:435b1980c1333ffe3ab386ad28d7b209590b0fa83ea8544d853e7a22f957331b",
"sha256:465c999ef30b1c7525f81330184121521418a67189053bcf585824d833c05b66", "sha256:486f7916ef77213103467924ef25f5ea1055ae901f385fe4d707604095fdf6a9",
"sha256:51064ee7938526bab92acd049d41a1dc797422256086b39c08bafeffb9d304c6", "sha256:4ac8306e04275d382d6393e557047b0a9d7ddf9f7ca5da9b3edbd9323ea75bd9",
"sha256:5a49e8473b1ab1228302ed27365ea0fadd4bf44bc0f9e73fe38e10fdd3d6b4fc", "sha256:4d1d707b752137e6bf45720648e1b828d5e4881d690df79cca07f7217ea06365",
"sha256:618db68745682f64cedc96ca93707805d1f3a031747b5a0d8e150cfd5055ae4d", "sha256:52f23a76544ed29573c0f3ee41f0ca1aedbab3a453102b60b540cc6fa55448ad",
"sha256:6547b27698b5b3bbfc5210233bd9523de849b2bb8a0329cd754c9308fc8a05ce", "sha256:5beeff18b4e894f6cb73c8daf2c0d8768844ef40d97032bb187d75b1ec8de24b",
"sha256:6557af9e0d23f46b8cd56f8af08eaac72d2e3c632ac8d5cf4e20215a8dca7cea", "sha256:6510f4a5029643301bdfe56b61e806093af2101d347d485c42a5535847d2c699",
"sha256:73a40d4fcd35fdedce07b5885905753d5d4edf413fbe53544dd871f27d48bd4f", "sha256:6afa9e4e63f066e0fd90a21db7e95e988d96127f52bfb298a0e9bec6999357a9",
"sha256:8280f9dae4adb5889ce0bb3ec6a541bf05434db5f9ab7673078c00713d148365", "sha256:771eca9872b47a629010665ff92de1c248a6979b8d1603daced37773d6f6e365",
"sha256:83469ad15262402b0e0974e612546bc0b05f379b5aa9072ebf66d0f8fef16bea", "sha256:78943451ab3ffd0e27876f9cea2b883317518b418f06b90dadf19394534637e9",
"sha256:860d0fe234922fd5552b7f807fbb039e3e7ca58c18c8d38aa0d0a95ddf4f6c23", "sha256:8327e468b1775c0dfabc3d01f39f440585bf4d398508fcbbe2f0d931c502337d",
"sha256:883c9fb62cebd1e7126dd683222b3b919657590c3e2db33bdc50ebbad53e0338", "sha256:8dbe5f639e6d035778ebf700be6d573f82a13662c3c2c3aa0f1dba303b942806",
"sha256:8afcb6f4064d234a43fea108859942d9795c4060ed0fbd9082b0f280181a15c1", "sha256:9134e5810262203388b203c2022bbcbf1a22e89861eef9340e772a73dd9076fa",
"sha256:96f51489ac187f4bab588cf51f9ff2d40b6d170ac9a4270ffaed535c8404256b", "sha256:9369f927f4d19b58322cfea8a51710a3f7c47a0e7f3398d94a4632760ecd74f6",
"sha256:9e865835e36dfbb1873b65e722ea627c096c11b05f796831e3a9b542926e979e", "sha256:987fe2f84ceaf744fa0e48805152abe485a9d7002c9923b18a4b2529c7bff218",
"sha256:aa0554495fe06172b550098909be8db79b5accdf6ffb59611900bea345df5eba", "sha256:a5881644fc51af7b232ab8d64f75c0f32295dfe88c2ee188023795cdbd4cf99b",
"sha256:b595e71c51657f9ee3235db8b53d0b57c09eee74dfb5b77edff0e46d2218dc02", "sha256:a81e40dfa50ed3c472494adadba097640bfcf43db160ed783132045eb2093cb1",
"sha256:b6ff91356354b7ff3bd208adcf875056d3d886ed7cef90c571aef2ab8a554b12", "sha256:aadc6d1e58e14010ae4764d1ba1fd0928dbb9423b27a382ea3a1444f903f4084",
"sha256:b70bad2f1a5bd3460746c3fb3ab69e4e0eb5f59d977a23f9b66e5bdc74d97b86", "sha256:ad8ec6b69d03e395db48df8991aa15fce3cd23e378b73e01d46a26a6efd5c26d",
"sha256:c7adb1f69a80573698c2def5ead584138ca00fff4ad9785a4b0b2bf927ba308d", "sha256:b02eee1577976acb4053f83d32b7826424f8b9f70809fa756529a52c6537eda4",
"sha256:c898b3ebcc9eae7b36bd0b4bbbafce2d8076680f6868bcbacee2d39a7a9726a7", "sha256:bac949be7579fed824887eed6672f44b7c4318abbfb2004b2c6968818b535a2f",
"sha256:e49947d583fe4d29af528677e4f0aa21f5e535ca2ae69c48270ebebd0d8843c0", "sha256:c035184af4e58e154b0977eea52131edd096e0754a88f7d5a847e7ccb3510772",
"sha256:eb1d71643e4154398b02e88a42fc8b29db8c44ce4134cf0f4474bfc5cb5d4dac", "sha256:c7d0a1b1258efff7d7f2e6cfa56df580d09ba29d35a1e3f604f867e1f685feb2",
"sha256:f2e8a9c0c8813a468aa659a01af6592f71cd30237ec27c4cc0683f089f90dcfc", "sha256:cc49fb8ff103900c20e4a9c53766c82a7ebbc183377fb357a8298bad216e9cdd",
"sha256:fe7fe11019fc3e6600819775a7d55abc5446dda07e9795f5954fdbf8a49e1c37" "sha256:d768359daeb3a86644f3854c6659e4496a3e6bba2b4651ecc87ce7ad415b320c",
"sha256:d81c84c9d2523b3ea20f8e3aceea68615768a7464c0f9a9899600ce6592ec570",
"sha256:ec1c908fa721f2c5684900cc8ff75555b1a5a2ae4f5a5694eb0e37a5263cea44",
"sha256:fa52534076394af7315306a8701b726a6521b591d95e8f4e5121c82f94790e8d",
"sha256:fd421a14edf73cfe01e8f51ed8966294ee3b3db8da921cacc88e497fd6e977af"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'",
"version": "==1.3.19" "version": "==1.4.27"
}, },
"werkzeug": { "werkzeug": {
"hashes": [ "hashes": [
"sha256:2de2a5db0baeae7b2d2664949077c2ac63fbd16d98da0ff71837f7d1dea3fd43", "sha256:63d3dc1cf60e7b7e35e97fa9861f7397283b75d765afcaefd993d6046899de8f",
"sha256:6c80b1e5ad3665290ea39320b91e1be1e0d5f60652b964a3070216de83d2e47c" "sha256:aa2bb6fc8dee8d6c504c0ac1e7f5f7dc5810a9903e793b6f715a9f015bdadb9a"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", "markers": "python_version >= '3.6'",
"version": "==1.0.1" "version": "==2.0.2"
}, },
"wtforms": { "wtforms": {
"hashes": [ "hashes": [
"sha256:7b504fc724d0d1d4d5d5c114e778ec88c37ea53144683e084215eed5155ada4c", "sha256:232dbb0094847dca2f45c72136b5ca1d5dca2a3e24ccd2229823b8b74b3c6698",
"sha256:81195de0ac94fbc8368abbaf9197b88c4f3ffd6c2719b5bf5fc9da744f3d829c" "sha256:4abfbaa1d529a1d0ac927d44af8dbb9833afd910e56448a103f1893b0b176886"
], ],
"version": "==2.3.3" "markers": "python_version >= '3.6'",
"version": "==3.0.0"
} }
}, },
"develop": { "develop": {
"autopep8": { "autopep8": {
"hashes": [ "hashes": [
"sha256:d21d3901cb0da6ebd1e83fc9b0dfbde8b46afc2ede4fe32fbda0c7c6118ca094" "sha256:44f0932855039d2c15c4510d6df665e4730f2b8582704fa48f9c55bd3e17d979",
"sha256:ed77137193bbac52d029a52c59bec1b0629b5a186c495f1eb21b126ac466083f"
], ],
"index": "pypi", "index": "pypi",
"version": "==1.5.4" "version": "==1.6.0"
}, },
"pycodestyle": { "pycodestyle": {
"hashes": [ "hashes": [
"sha256:2295e7b2f6b5bd100585ebcb1f616591b652db8a741695b3d8f5d28bdc934367", "sha256:720f8b39dde8b293825e7ff02c475f3077124006db4f440dcbc9a20b76548a20",
"sha256:c58a7d2815e0e8d7972bf1803331fb0152f867bd89adf8a01dfd55085434192e" "sha256:eddd5847ef438ea1c7870ca7eb78a9d47ce0cdb4851a5523949f2601d0cbbe7f"
], ],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==2.6.0" "version": "==2.8.0"
}, },
"toml": { "toml": {
"hashes": [ "hashes": [
"sha256:926b612be1e5ce0634a2ca03470f95169cf16f939018233a670519cb4ac58b0f", "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b",
"sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88" "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"
], ],
"version": "==0.10.1" "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==0.10.2"
} }
} }
} }

View File

@ -12,6 +12,8 @@ def create_app():
app.config['SQLALCHEMY_DATABASE_URI'] = os.getenv( app.config['SQLALCHEMY_DATABASE_URI'] = os.getenv(
"DATABASE_URL", "sqlite:///app.db") "DATABASE_URL", "sqlite:///app.db")
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
if app.config['ENV'] != 'production':
app.config['SQLALCHEMY_ECHO'] = True
flask_static_digest.init_app(app) flask_static_digest.init_app(app)
@ -34,4 +36,8 @@ def create_app():
from . import settings from . import settings
app.register_blueprint(settings.blueprint) app.register_blueprint(settings.blueprint)
from . import api
api.init_app(app)
app.register_blueprint(api.blueprint)
return app return app

87
app/api.py Normal file
View File

@ -0,0 +1,87 @@
from pathlib import PosixPath
from flask import Blueprint, abort, render_template, redirect
from flask.helpers import url_for
from flask_login import current_user, login_required
from flask_restful import Api, Resource
from flask_wtf.form import FlaskForm
from wtforms.fields import SubmitField, StringField
from wtforms.validators import DataRequired, ValidationError
from .db import APIKey, db
blueprint = Blueprint('api', __name__)
api = Api()
def init_app(app):
api.init_app(app)
def colon_checker(form, field):
if ":" in field.data:
raise ValidationError('Field cannot contain colons')
class NewKeyForm(FlaskForm):
app_name = StringField('App name', validators=[
colon_checker, DataRequired()])
description = StringField('Description', validators=[
colon_checker, DataRequired()])
submit = SubmitField('Create')
@blueprint.route('/api/manage/keys', methods=['GET', 'POST'])
@login_required
def key_list():
apps = [a[0] for a in db.session.query(APIKey.app_name).filter(
APIKey.user == current_user).group_by(APIKey.app_name).all()]
keys = {app_name: APIKey.query.filter(
APIKey.user_id == current_user.id, APIKey.app_name == app_name).all() for app_name in apps}
form = NewKeyForm()
if form.validate_on_submit():
key = APIKey.create(current_user, form.app_name.data,
form.description.data)
return render_template('api/manage/key_list.html', form=NewKeyForm(), apps=sorted(apps), keys=keys, created_key=key)
return render_template('api/manage/key_list.html', form=form, apps=sorted(apps), keys=keys)
class DeleteForm(FlaskForm):
submit = SubmitField('Delete')
@blueprint.route('/api/manage/keys/<key_id>/delete', methods=['GET', 'POST'])
@login_required
def key_delete(key_id):
k = APIKey.query.get_or_404(key_id)
if k.user_id != current_user.id:
abort(403)
back_url = url_for('api.key_list')
form = DeleteForm()
if form.validate_on_submit():
db.session.delete(k)
db.session.commit()
return redirect(back_url)
return render_template('api/manage/key_delete.html', form=form, key=k, cancel=back_url)
def r(resource, endpoint: str): api.add_resource(
resource, str(PosixPath('/api/v1') / endpoint))
s = {'success': True}
def e(err): return {'success': False, 'error': err}
class Version(Resource):
def get(self):
return s | {'version': 'v1'}
r(Version, '')

View File

@ -1,10 +1,13 @@
from sqlalchemy.sql import func import secrets
from flask import Flask from datetime import datetime
from flask_sqlalchemy import SQLAlchemy from typing import Optional
from flask_bcrypt import check_password_hash, generate_password_hash
from flask_login import UserMixin from flask_login import UserMixin
from flask_bcrypt import generate_password_hash, check_password_hash
from flask_migrate import Migrate from flask_migrate import Migrate
from datetime import datetime, timedelta from flask_sqlalchemy import SQLAlchemy
from sqlalchemy.sql import func
from .days import Day from .days import Day
db = SQLAlchemy() db = SQLAlchemy()
@ -28,6 +31,9 @@ class User(UserMixin, db.Model):
accomplishments = db.relationship( accomplishments = db.relationship(
'Accomplishment', backref='user', lazy=True) 'Accomplishment', backref='user', lazy=True)
api_keys = db.relationship(
'APIKey', backref='user', lazy=True)
# TODO: set user timezone from geoip on registration # TODO: set user timezone from geoip on registration
timezone = db.Column(db.String(64), nullable=False) timezone = db.Column(db.String(64), nullable=False)
start_of_day = db.Column(db.Integer, nullable=False) start_of_day = db.Column(db.Integer, nullable=False)
@ -96,3 +102,49 @@ class Accomplishment(db.Model):
def get_today_total(user): def get_today_total(user):
today = Day.today(user) today = Day.today(user)
return Accomplishment.get_day_total(user, today) return Accomplishment.get_day_total(user, today)
class APIKey(db.Model):
id = db.Column(db.Integer, primary_key=True)
app_name = db.Column(db.String(64), nullable=False)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
hash = db.Column(db.String(128), nullable=False)
description = db.Column(db.String(128))
created_on = db.Column(db.DateTime, index=False, unique=False,
nullable=True, server_default=db.func.now())
last_use = db.Column(db.DateTime, index=False, unique=False,
nullable=True) # TODO: set on login? or remove?
@staticmethod
def create(user: User, app: str, description: str) -> str:
key = secrets.token_urlsafe(nbytes=48)
# TODO: make sure app and description don't have : in them
m = APIKey(
app_name=app,
user_id=user.id,
hash=generate_password_hash(key),
description=description
)
db.session.add(m)
db.session.commit()
return f"{app}:{user.id}:{key}"
def check(self, key: str) -> bool:
app, user_id, key = key.split(":", 2)
ok = self.user_id == int(
user_id) and self.app_name == app and check_password_hash(self.hash, key)
if ok:
self.last_use = datetime.now()
db.session.add(self)
db.session.commit()
@classmethod
def find(cls, key: str) -> Optional[User]:
app, user_id, _ = key.split(":", 2)
results = cls.query.filter(
cls.app_name == app, cls.user_id == int(user_id))
for result in results:
if result.check(key):
return result.user
return None

View File

@ -58,9 +58,13 @@ def gen_graph_data(numbers):
dots = [absolute_to_percentage_position( dots = [absolute_to_percentage_position(
n, scale_base) for n in numbers] n, scale_base) for n in numbers]
lines = list(zip(dots, dots[1:])) lines = list(zip(dots, dots[1:]))
avg = sum(dots)/len(dots)
print(dots)
print(avg)
return { return {
"dots": dots, "dots": dots,
"lines": lines, "lines": lines,
"scale": scale, "scale": scale,
"avg": avg,
} }

View File

@ -1,14 +1,14 @@
from flask import Blueprint, render_template, redirect, url_for, abort, request import time
from flask import Blueprint, abort, redirect, render_template, request, url_for
from flask_login import current_user, login_required from flask_login import current_user, login_required
from flask_wtf import FlaskForm from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField from wtforms import IntegerField, StringField, SubmitField
from wtforms.fields.html5 import IntegerField
from wtforms.widgets.html5 import NumberInput
from wtforms.validators import DataRequired, Length, NumberRange from wtforms.validators import DataRequired, Length, NumberRange
from .db import db, Accomplishment from wtforms.widgets import NumberInput
from datetime import datetime, timedelta
import time
from .days import Day from .days import Day
from .db import Accomplishment, db
main = Blueprint('main', __name__) main = Blueprint('main', __name__)

View File

@ -1,12 +1,14 @@
from flask import Blueprint, render_template
from flask_login import current_user, login_required
from flask_wtf import FlaskForm
from .db import db
from wtforms import SelectField, SubmitField
from wtforms.fields.html5 import IntegerField
from wtforms.widgets.html5 import NumberInput
from wtforms.validators import DataRequired, NumberRange
import pytz import pytz
from flask import Blueprint, abort, render_template
from flask.globals import request
from flask_login import current_user, login_required
from flask_migrate import current
from flask_wtf import FlaskForm
from wtforms import IntegerField, SelectField, SubmitField
from wtforms.validators import DataRequired, NumberRange
from wtforms.widgets import NumberInput
from .db import APIKey, db
blueprint = Blueprint('settings', __name__) blueprint = Blueprint('settings', __name__)
@ -40,3 +42,24 @@ def settings():
return render_template('settings.html', form=form, success=True) return render_template('settings.html', form=form, success=True)
return render_template('settings.html', form=form) return render_template('settings.html', form=form)
@blueprint.route('/settings/keys', methods=['GET', 'POST'])
@login_required
def api_keys():
apps = [a[0] for a in db.session.query(APIKey.app_name).filter(
APIKey.user == current_user).group_by(APIKey.app_name).all()]
keys = {app_name: APIKey.query.filter(
APIKey.user_id == current_user.id, APIKey.app_name == app_name).all() for app_name in apps}
return render_template('api_keys.html', apps=sorted(apps), keys=keys)
@blueprint.route('/settings/keys/<key_id>/delete', methods=['GET', 'POST'])
@login_required
def api_key_delete(key_id):
k = APIKey.query.get_or_404(key_id)
if k.user_id != current_user.id:
abort(403)
if request.method == 'GET':
return render_template('')

View File

@ -0,0 +1,19 @@
{% extends "_skel.html" %}
{% from "_formhelpers.html" import render_field %}
{% block title %}Delete accomplishment{% endblock %}
{% block content %}
<div class="section card">
<p><span class="font-bold">App:</span> {{ key.app_name }}</p>
<p><span class="font-bold">Description:</span> {{ key.description }}</p>
<p><span class="font-bold">Created:</span> {{ key.created_on }}</p>
<p><span class="font-bold">Last use:</span> {% if key.last_use %} {{ key.last_use }}{% else %}never{% endif %}
</div>
<div class="text-center section card">
<h3 class="text-lg">Are you sure you want to remove this API key?</h3>
<form class="mt-3" method="POST">
{{ form.csrf_token }}
{{ render_field(form.submit, label=False, mb="mb-2", class_="btn btn-red btn-wide btn-center") }}
<a href="{{ cancel }}" class="block text-xs link">cancel</a>
</form>
</div>
{% endblock %}

View File

@ -0,0 +1,51 @@
{% extends "_skel.html" %}
{% block title %}Settings{% endblock %}
{% from "_formhelpers.html" import render_field %}
{% block content %}
{% if created_key %}
<div class="section card text-center">
<p><b>Your new key is</b></p>
<p class="my-4 font-mono break-all">{{ created_key }}</p>
<p>Save it now, because it will disappear when you leave this page. <b>Keep it safe</b>, as it allows <b>access to
your account.</b></p>
</div>
{% endif %}
<div class="section card">
<form method="POST" class="form">
{{ form.csrf_token }}
{{ render_field(form.app_name) }}
{{ render_field(form.description) }}
{{ render_field(form.submit, False, class_="btn btn-blue btn-wide btn-center") }}
{% if success %}<p class="text-center text-green-800">Saved successfuly.</p>{% endif %}
</form>
</div>
{% for app in apps %}
{% set apploop = loop %}
<div class="section card">
<ul class="list-disc">
<h3 class="text-xl font-bold">{{ app }}</h3>
{% for key in keys[app] %}
<li class="ml-6 mt-2">
<div class="flex items-center">
<div class="flex-grow">
<p><span class="font-bold">Description:</span> {{ key.description }}</p>
<p><span class="font-bold">Created:</span> {{ key.created_on }}</p>
<p><span class="font-bold">Last use:</span> {% if key.last_use %} {{ key.last_use }}{% else
%}never{% endif
%}</p>
</div>
<div>
<p>
<a class="text-red-700 underline text-sm hover:text-red-500 italic"
href="{{ url_for('api.key_delete', key_id=key.id) }}">delete</a>
</p>
</div>
</div>
</li>
{% endfor %}
</ul>
</div>
{% endfor %}
{% endblock %}
<!-- TODO: make form styling consistent -->

View File

@ -13,6 +13,7 @@
{% for n in scale %} {% for n in scale %}
<line x1="10%" y1="{{ 16.6 * loop.index }}%" x2="96%" y2="{{ 16.6 * loop.index }}%" stroke="#ddd" stroke-width="2px" /> <line x1="10%" y1="{{ 16.6 * loop.index }}%" x2="96%" y2="{{ 16.6 * loop.index }}%" stroke="#ddd" stroke-width="2px" />
{% endfor %} {% endfor %}
<line x1="10%" y1="{{ avg }}%" x2="96%" y2="{{ avg }}%" stroke="#c5f0c6" stroke-width="2px" />
</g> </g>
<g class="legend" style="font-size: 0.9em; text-align:right;"> <g class="legend" style="font-size: 0.9em; text-align:right;">
{% for n in scale | reverse %} {% for n in scale | reverse %}

View File

@ -0,0 +1,36 @@
"""add created_on, last_use and description to APIKey
Revision ID: 9ce23f42b6ca
Revises: a20adaee67da
Create Date: 2021-11-17 20:59:19.926543
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '9ce23f42b6ca'
down_revision = 'a20adaee67da'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('api_key', schema=None) as batch_op:
batch_op.add_column(sa.Column('created_on', sa.DateTime(), server_default=sa.text('(CURRENT_TIMESTAMP)'), nullable=True))
batch_op.add_column(sa.Column('description', sa.String(length=128), nullable=True))
batch_op.add_column(sa.Column('last_use', sa.DateTime(), nullable=True))
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('api_key', schema=None) as batch_op:
batch_op.drop_column('last_use')
batch_op.drop_column('description')
batch_op.drop_column('created_on')
# ### end Alembic commands ###

View File

@ -0,0 +1,36 @@
"""Add api keys
Revision ID: a20adaee67da
Revises: 687170684a50
Create Date: 2021-11-17 20:21:01.069492
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'a20adaee67da'
down_revision = '687170684a50'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('api_key',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('app_name', sa.String(
length=64), nullable=False),
sa.Column('user_id', sa.Integer(), nullable=False),
sa.Column('hash', sa.String(length=128), nullable=False),
sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
sa.PrimaryKeyConstraint('id')
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table('api_key')
# ### end Alembic commands ###