From b4ecbeaa371d00d78af329594cb631887333875a Mon Sep 17 00:00:00 2001 From: Thastertyn Date: Tue, 5 Mar 2024 16:01:26 +0100 Subject: [PATCH] Initial commit --- .gitignore | 1 + .vscode/launch.json | 24 ++++ .vscode/settings.json | 6 + app/__init__.py | 17 +++ app/__pycache__/__init__.cpython-310.pyc | Bin 0 -> 565 bytes app/__pycache__/__init__.cpython-311.pyc | Bin 0 -> 1143 bytes app/__pycache__/config.cpython-310.pyc | Bin 0 -> 887 bytes app/__pycache__/config.cpython-311.pyc | Bin 0 -> 2244 bytes app/__pycache__/decorators.cpython-311.pyc | Bin 0 -> 1310 bytes app/__pycache__/extensions.cpython-310.pyc | Bin 0 -> 429 bytes app/__pycache__/extensions.cpython-311.pyc | Bin 0 -> 912 bytes app/api/__init__.py | 8 ++ app/api/__pycache__/__init__.cpython-310.pyc | Bin 0 -> 382 bytes app/api/__pycache__/__init__.cpython-311.pyc | Bin 0 -> 554 bytes app/api/routes/__init__.py | 1 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 259 bytes .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 307 bytes .../routes/__pycache__/error.cpython-311.pyc | Bin 0 -> 1383 bytes .../__pycache__/error_routes.cpython-310.pyc | Bin 0 -> 1512 bytes .../__pycache__/error_routes.cpython-311.pyc | Bin 0 -> 2255 bytes .../__pycache__/main_routes.cpython-310.pyc | Bin 0 -> 397 bytes .../__pycache__/main_routes.cpython-311.pyc | Bin 0 -> 555 bytes .../__pycache__/product.cpython-311.pyc | Bin 0 -> 3173 bytes .../product_routes.cpython-310.pyc | Bin 0 -> 1661 bytes .../product_routes.cpython-311.pyc | Bin 0 -> 3205 bytes .../__pycache__/user_routes.cpython-310.pyc | Bin 0 -> 1272 bytes .../__pycache__/user_routes.cpython-311.pyc | Bin 0 -> 4497 bytes app/api/routes/error_routes.py | 29 ++++ app/api/routes/main_routes.py | 7 + app/api/routes/product_routes.py | 63 +++++++++ app/api/routes/user_routes.py | 79 +++++++++++ app/config.py | 22 +++ app/extensions.py | 22 +++ app/jwt_utils.py | 15 ++ .../jwt_check_service.cpython-311.pyc | Bin 0 -> 943 bytes .../product_service.cpython-310.pyc | Bin 0 -> 2184 bytes .../product_service.cpython-311.pyc | Bin 0 -> 3687 bytes .../__pycache__/user_service.cpython-310.pyc | Bin 0 -> 3271 bytes .../__pycache__/user_service.cpython-311.pyc | Bin 0 -> 8229 bytes app/services/product_service.py | 52 +++++++ app/services/user_service.py | 129 ++++++++++++++++++ main.py | 13 ++ requirements.txt | 6 + 43 files changed, 494 insertions(+) create mode 100644 .gitignore create mode 100644 .vscode/launch.json create mode 100644 .vscode/settings.json create mode 100644 app/__init__.py create mode 100644 app/__pycache__/__init__.cpython-310.pyc create mode 100644 app/__pycache__/__init__.cpython-311.pyc create mode 100644 app/__pycache__/config.cpython-310.pyc create mode 100644 app/__pycache__/config.cpython-311.pyc create mode 100644 app/__pycache__/decorators.cpython-311.pyc create mode 100644 app/__pycache__/extensions.cpython-310.pyc create mode 100644 app/__pycache__/extensions.cpython-311.pyc create mode 100644 app/api/__init__.py create mode 100644 app/api/__pycache__/__init__.cpython-310.pyc create mode 100644 app/api/__pycache__/__init__.cpython-311.pyc create mode 100644 app/api/routes/__init__.py create mode 100644 app/api/routes/__pycache__/__init__.cpython-310.pyc create mode 100644 app/api/routes/__pycache__/__init__.cpython-311.pyc create mode 100644 app/api/routes/__pycache__/error.cpython-311.pyc create mode 100644 app/api/routes/__pycache__/error_routes.cpython-310.pyc create mode 100644 app/api/routes/__pycache__/error_routes.cpython-311.pyc create mode 100644 app/api/routes/__pycache__/main_routes.cpython-310.pyc create mode 100644 app/api/routes/__pycache__/main_routes.cpython-311.pyc create mode 100644 app/api/routes/__pycache__/product.cpython-311.pyc create mode 100644 app/api/routes/__pycache__/product_routes.cpython-310.pyc create mode 100644 app/api/routes/__pycache__/product_routes.cpython-311.pyc create mode 100644 app/api/routes/__pycache__/user_routes.cpython-310.pyc create mode 100644 app/api/routes/__pycache__/user_routes.cpython-311.pyc create mode 100644 app/api/routes/error_routes.py create mode 100644 app/api/routes/main_routes.py create mode 100644 app/api/routes/product_routes.py create mode 100644 app/api/routes/user_routes.py create mode 100644 app/config.py create mode 100644 app/extensions.py create mode 100644 app/jwt_utils.py create mode 100644 app/services/__pycache__/jwt_check_service.cpython-311.pyc create mode 100644 app/services/__pycache__/product_service.cpython-310.pyc create mode 100644 app/services/__pycache__/product_service.cpython-311.pyc create mode 100644 app/services/__pycache__/user_service.cpython-310.pyc create mode 100644 app/services/__pycache__/user_service.cpython-311.pyc create mode 100644 app/services/product_service.py create mode 100644 app/services/user_service.py create mode 100644 main.py create mode 100644 requirements.txt diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4c49bd7 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.env diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..1418522 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,24 @@ +{ + // Pro informace o možných atributech použijte technologii IntelliSense. + // Umístěním ukazatele myši zobrazíte popisy existujících atributů. + // Další informace najdete tady: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Flask Shop", + "type": "debugpy", + "request": "launch", + "program": "${workspaceFolder}/main.py", + "args": [], + "cwd": "${workspaceFolder}", + "env": { + "FLASK_APP": "main.py", + "FLASK_ENV": "development" + }, + "envFile": "${workspaceFolder}/.env", + "stopOnEntry": false, + "console": "internalConsole", + "autoReload": {"enable": true} + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..85b80f5 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "cSpell.words": [ + "dotenv", + "jsonify" + ] +} \ No newline at end of file diff --git a/app/__init__.py b/app/__init__.py new file mode 100644 index 0000000..954eefb --- /dev/null +++ b/app/__init__.py @@ -0,0 +1,17 @@ +from flask import Flask +from flask_jwt_extended import JWTManager + +def create_app(): + app = Flask(__name__) + jwt = JWTManager(app) + + from app.api import bp, bp_errors, bp_product, bp_user + app.register_blueprint(bp) + app.register_blueprint(bp_errors) + app.register_blueprint(bp_product) + app.register_blueprint(bp_user) + + from app.config import FlaskTesting, FlaskProduction + app.config.from_object(FlaskTesting) + + return app \ No newline at end of file diff --git a/app/__pycache__/__init__.cpython-310.pyc b/app/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c692374fe9751c19963d2e217986ba45318308d7 GIT binary patch literal 565 zcmZvZy-ve05Xb$Iv>~*Bn0Nq|EJW-GAt5m!Q6YhZ)Wu3`Lj!SQon1ktW`YNSk;h^6 z3Jb5m#5om+fwS)NJKJaf&uNCkl%TD@eW-Iv$a_fs8pGrW-R@$LMAC|AI;J!*tC;3v zj?88BM8=VfFG$w=#GNFgK0c`mdxwyoUR|CQb#bF$8rFWShrrQ~(d{J$M;v{mEpco~ z@Jl7dJGhQorsJ0DUK{Ct8&k3*&&iU#po)(8FGar;uL$+<5ny8^i#(eczcw*K0W@HJ zifEvb^QrTF!VKaNf_UhM#cfbGrF9C#q?#)O zW$m(_=UC$T?Y;Z2=9Rz@OTYH>S)*0%W<{uXi#k7Uq{`11Zr0Sfoi!%MV{#$Nx^zP9 znT1cMpbDo1&Id7e3q!&Zy2Dbs8WDp{oE&AbkFUcoShcj>u|P#aJvdcMCA;Rj&7GV- YVFM5F$$`e#snlV3KN}hzkO3XAZy|_*00000 literal 0 HcmV?d00001 diff --git a/app/__pycache__/__init__.cpython-311.pyc b/app/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..995c4bc7e4f7be45fab1092270eb6b1fb932306f GIT binary patch literal 1143 zcma)5OKTKC5bm1C&hG4Pg7FbZE=$%N=90mKco7c@9t1WiS9@+?;-bS!TeBwiggs=vnu-4H+eN9KK?~*mcH=Ys4{Gmb|kmlo#m%c<`KY70T z)Qh|p@+@et#>==gyCPrv6fBFr(1knY7<@Bvq)heoD_W~PUQ z%m}T_1Z3_PCvg+h)z0Vt2UEW?)3`FzyvW4$>zz_)XN9nwS!y*KmGNr#t7G)%zgxJN zqf4+4#xhrj=onIKB(l^azmMxmn2JG67T|A+)_%NGB2sg8O~ZkDj27Y&L&HE5D96kfMy)6 z2Q98{9WN_pVd##HS0BP-xR?J=4#D^evtMBLz1D;IKFsGZKk=1%u+WEv92O=%y9f0?)N`m$d{z%? zeW>M7JBGXYWrEz@xlyG`4;K5dn8V_6vAi>Pid!Jdjw#J`^%RF4oEcz>r%Kst!6Kje zgI*4Hen9vwjNvy>ofYqgcuP9$0iQ!ty}hO;A!XU` z;0IXG7B;NA>xz4Ar$hqA^3geS@63Ho(rnfUtZ(0cisu?3KTvEg9mGET=~DnsIGvM- z91^Z^{R`pxXDyRxyW|G&YROIDHC};ema==DZW6zrO;qjpEO`5dodn)cwF=&IL$~7w z-r9E555hGc^oQZvZ{P;OX@A)B+jV6#7LOK!F;!!1k?@N-@&;ocFGlmyq9v)aMEq$c zlUV5!kx4918VDvr1;GNiBC>@X;l^>M$wD~Ubd+X7W~=5O-sv=;yX literal 0 HcmV?d00001 diff --git a/app/__pycache__/config.cpython-311.pyc b/app/__pycache__/config.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0137554ead1e5edb99dcbe5f7433ffa3e81d55d8 GIT binary patch literal 2244 zcmds1&rj1(9DiNAt=m`!6O0Ze(a2$tY!N*fV=`F5UyQj92#ZNGX8f%NLZcpHHeC*QYSVRVb`U_AI<`q{Vd`#$fzen0QC_kO<@ z;daOQL)>>D^c`NTMm2(yRUB+10SQDAWsn0+Ai?nl3FIDT9OB1p8N$d=IODWs97e{4 zGj3akG%_^Kcx)L;AUG8E{)EL7^Nl@>AF}as5+vYUBFYc~$~Z)_V4WE4I-3brED(E` zaflzYWn3L_95#--0}g59&>e6n8^_ZD$7$nu1sATFTlY;?xaH!QEG^_0b?@XH_u@G} z&9Nz+G2?hF9UF~ttd)B_#igw{Ih9IV^O7-+o1IF<6JeiD^SqQ@5_w+t^8Au4tP~;c z;`!y3Y_XAX$g1uZrPaJ5OFFqImKD%JaRImiXaEm}19XBBokgQLc}a|xb6K@4D&>k4 z9g_tynyi#_vJ_Qwaw(cEm7?>;r$kB>1tc~AZhxZ_#7%^01x&B_0J;GD01Q9?pby|GKsSK#!9j?6EdaiHAH(m9-&$2Iq=n1{ zVXaFQ1ZAO}e8zBLYk|m+B2_py^e)_{mL{~?^v@idO$BNVuiL?1HroL!LpQ7!7%yhk z*GUB%WWJo2rE#5kI-BM>b}YrF`Dg5$PJtk2!~>l&)Hv|i)X<%Ac655eF3NShi|)fF zo?%mbA~wm|#kqzjC;%X&n!?Z|j)&3u+Ik_-v$?jlzOlZuPz?;$0)zWxH88yY>SN)P z^g;SEQ;o!Hk$5!_*ZQHG(!nPGp>$n_bZJp7=cUE80tHt>0Q~?rFeH?l5ZYXE3&sGh z;HRbV{=Zmw*d**UZwA=KG^8QCafr(YJEI?(|I?3@+jueor~8VbP7(xhgglzj>y(qY xS4XXI3}dGk3C2MT*HJ4R!`SIXf+h);sH66HY>b~aXT9xbnk%%K-^4Yp<1eApo!|ff literal 0 HcmV?d00001 diff --git a/app/__pycache__/decorators.cpython-311.pyc b/app/__pycache__/decorators.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..05eb1c3319f02b72e26affdf586f67bfbd2017cf GIT binary patch literal 1310 zcmaJ=&rcIU6rP>kZK)Ilu_akT=|!*{b`KJNg%AxVOe7|Fv6s5p8R*jOZk?Tikdk1+ zRih_7dN8QrAK>Vpuq90-n~<0oOuW?)FT{gymKG_9liBahyqUM}`)1yJ=;^tHpyhvm zr%y0K-^8TdU>>!@+h8^kK}2#;S;|Y27-I*Q<-A;0@=96FtC3H3V&!-~4r65l<-3SF zhjOtW;wL#NRF|1q@pE#RTwLY_MxRw}MoHMWKzTuUY(iab^Odl-?I}`|PzIrwA1w3f zBDG3$lr7m7wIW_Ekim-Yun4e;xYY84;cW>p-U9#j$r=|Ct+oYryFCGs8>rLMIa@=Aq3rbN#^_Da;v z+U}C+*u;JV$C-;Up`FD5LK$w3338z(?$9xPUN{c6Om2H_ftZ{!!8oUeipduIFjiWQ zY`p4I1~m|-L@k_)4ExWye33UR+R+W8=#{C#i;$2qzTz6w9-+pg6<+jQ!!LRj!>m*c zLM@M(++)68SqbB!9xIfs>LR1LVA}4#=<1V>XPJ&arMLDArT!woI{FnuJ;U{6rjg97 zj~!^&>)Loj8~>u|yPE!y*R_d;HWA>#{q(g)`rhZkdU~djo(b^4S5*r##;$4vsm+{wb1K71!&5WB<-Q0ptJ9xhrz{t58VwqdsFF^`kLrGL=64_ D>gh!1 literal 0 HcmV?d00001 diff --git a/app/__pycache__/extensions.cpython-310.pyc b/app/__pycache__/extensions.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cb508fd4d0a63edcc81d52bba4de66c13192264a GIT binary patch literal 429 zcmYjOu};G<5Vcb`P1B|{S*o-)q*Y>v*cId>_y-vIC9kmX z3y6VBS_EgkyLa!bca|(1$3_rG^AF}=gx;&>f0)pm0b~G)B8pQK;kp`;0o6zw8Hor+ z3~!nnlaWmeU;)|yn>Lt48QBZBsSjEd&GPkD>vQj>z{gvRHt&}Kqvlg@=q_*QcuO{GC7~rnkf7e=js_o0$ z@cLqLJ`QKUd>)Rby0_COei+`2r@?O@_%natha>IK;xWuh*TiaXzz*E47V$jX;)Dt{3J8^kdkdK` zk!tvUm;fqh0y@(0R)qeqWN8LPYVU>h^l|-R#j3^lnYCLLE7SOX*|MHjsyi{?Dc8ym z%GRzhoMwI2qeDEQwfdBS6g>=2;j+bPr^TZS5vl6#&LeBK%@prfsx^`89&$Jw=&mHn7{@^Nk zuScA|i|^B0&;uSVVxPfG5|ShZC_jo_IeVn=-M0TR1?46%T&C0(*Gc+bE$hdx<-aJchen}U`>B3OI$QR$?aelFf aq6TaEiLP$QP`wdWZ^$E*n=X124)F&lobgNm literal 0 HcmV?d00001 diff --git a/app/api/__init__.py b/app/api/__init__.py new file mode 100644 index 0000000..152f96d --- /dev/null +++ b/app/api/__init__.py @@ -0,0 +1,8 @@ +from flask import Blueprint + +bp_errors = Blueprint('errors', __name__) +bp = Blueprint('api', __name__) +bp_product = Blueprint('product', __name__, url_prefix="/product") +bp_user = Blueprint('user', __name__, url_prefix="/user") + +from . import routes \ No newline at end of file diff --git a/app/api/__pycache__/__init__.cpython-310.pyc b/app/api/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dd13cd7d6c9cbbf721ad823581038b363b1420ee GIT binary patch literal 382 zcmYjMy-ve05VoEF&?0z@E_h-?2*HXF5?dB4#aAUFiLJAPNLOYageOAs3QHdWCN8A| zXWjRsvp?Hrxhxsg^T!u_6MxR;e0X3^;J(Qk8k zP_(%mu~Qad)4qgwbb#AL&a~ryVJVu?gS%T5A=z}bdqP2jkSjIRJt!55wN-~(#M*|E z>>)j5r01-_u%JWl3{Q#n@N+I3(}VO)?L45L26! z_@~^#fk!4m7dOF4C)2YIqL26S%X@jh{K$>tAfW8z@r8Y;`dl7<%&5ToxdIn}0t!PA z!8U|I0}N7)Y1bM6cPhTEtxx*;pMGu9uQ81p_w{RC?Wm}BjLFPYGwaW+PHl=pGqTzi z1axW-xQ*9x&4to#g*oFQh%;GdLL?%Uy2k@)a*@#dFzZ^lLh3TgMM$_{N5QGAuz=Q;GkP))NJD=8Cq~wQeUCb$# zQeJwd(whz;9D~KdZ+~jc^|t%|l{IX*qlR19V`IA5@8x}eXe^D4r9vA!^S%9k=W6+( pJ8)V!2W z8zk7tYqz8tz{0BbBUm-v{i>76>GgOi;)MYE0Yh*^9Pl8p!5#4_MQe=~zHOY>;O9Y9 pR%vmaeQO8dT04qn#O}l|fsnErIxpp6oR@8Bf`6}%7}cRyPQDZuL*@Vg literal 0 HcmV?d00001 diff --git a/app/api/routes/__pycache__/__init__.cpython-311.pyc b/app/api/routes/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f0c4c3ce6cc9888188890e5b6dfa91ef57a5324b GIT binary patch literal 307 zcma)$v2MaJ5QfijK!rdbfjvv_1rSRHwyILMELK!oN+cXdXJ^Qh@Cc}H)3M6hF5N1z zF>w}T=j8i*zs~;ePV4m&I9B^#Q}F#0#T3cCIB&Q(f(9)PU_~ixQYKcG(xzo9Kqv2z z%Rj-7@^s8>)wGISAB-PYhRD&Ro;z~7{|J-LdT*xYYd#x+BzsUAF^iZpKHA z4Tie%!B7}lESxs@HiX)>=xf(u)x~V^HXM~|+9oLV(hZlVt>w`Nv!jg7;;J4Q|56C? M4aL`EhZg+j3IU2%EdT%j literal 0 HcmV?d00001 diff --git a/app/api/routes/__pycache__/error.cpython-311.pyc b/app/api/routes/__pycache__/error.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..74ba1f1ea17c2512c11dd4119c9d699d092c8fb2 GIT binary patch literal 1383 zcmb7C&rcIU6rS0ZvZVt4K!SqFV7!nR_H2SiXcV{*BV5E7nC?uotJ|46vx{j@9Q*@3 znCM@CAtatX`X?+22eT)Pr`|Bt3n$;~7O*WGnC!mHzWv_J_kHhu86F-&uwH!q$>%ME zewmMxmFsNU32Z(gh8PY|4X1t)iJy%P5(_NwHGj;7UoQTOUA-dnyQi8;CKaJpuvD zz|8V7%T+BnAivTqEl@_D^S2S#Ve|2t$BD{5$UCZuFI=eNJU&J zLh1|70JB8{pAkkws`7BjF51Nj=Gw3z#}6D&*0>XTREJ!J4dIj}U_c@e&@Vwc+@pO8e;l{HHq)e1L5!G*j|kBV#fOKC0UOehnH*hfY&(O$C`6% zp07zY5^m@lrZ8v5-U{b}_-8oM;NdJOd)$3PDing2QcSDs7s*NKr@<&b9h2MfRM(iK zB35P!2~uX9lB+O%nSPH77J!jv(v<07c4FFVV@ulQV|%V9SrqU`Y8-NA#`Qjgqa4Qg z2bzexpF=#ked_>EelG80a)8N^g)zlP$i&|{WKEyt3dox3!a7cM&>{6fp0egEs@&Et; literal 0 HcmV?d00001 diff --git a/app/api/routes/__pycache__/error_routes.cpython-310.pyc b/app/api/routes/__pycache__/error_routes.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4e400a754cdafac340541f65506f0dc5857317e3 GIT binary patch literal 1512 zcma)+yKfUg5QpzBN`Zw)Xwq2g+jV^6Zr9B2CHPvX z5eVp^%zuR3LgPBnP%*QYI4OeVlXkuPySdqKW@oJH^?47U+udLCN73_s(&qYunzxO^ ze85R~!jHYgKk|J~`rito@ZA?hQF`ya^N)&7nL%Y%%sHjxlrkz6G4GTar&Lj?iMmr} zozg&MK`c6D&M8Z%G{v%0%1&89W%W`KYX{!O`lx_V8~(7;>9-|-1~dBTdtQiR{Lq@r zc5$57a1y>KM2Oujpo>x~t3Ks|y_9cKX>7KA*pm$M4LjwAMJm()GPLo4bu}d1S}7hf z4J_2k@<>U6HYYre1QXnHpoE=yXa3NaFoSD1{oM|FI+20xabqRefeLoDkim-iU&fJ zNs-;#<57(CR{a+s#n18(X2&ZRQ5m>j=c#Zidm1{C5K?82 z$4k})oiHAT(wImcBT!`ULX1u!l$-1j1}xUc$Mh;v(4Zq7(@lsKys-QL7mDLsB)%`x z@MV>jwro$QN@NeNl?A5klE+nuLaQ!W!O~+wIBlHS2JkjMnNtIEMd~9}w5A zOUl+pW#|F*>~Ik~J&&Qsm54DR)WfcGfXWl7DuA->TwMK&E&u=k literal 0 HcmV?d00001 diff --git a/app/api/routes/__pycache__/error_routes.cpython-311.pyc b/app/api/routes/__pycache__/error_routes.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..206d2cac464885c8b10bff962f73257940f6a803 GIT binary patch literal 2255 zcmb7EPj4GV9G$V#WZnFwl(cCQbyD>bsfxvgLmN;Wi?lrups}SisB*?T<9J~`V`g?+ z>{AbX01zPf1VvT9K#zQcLF%FMsS;dr3kqC%;y1IgQ?KoVSN3T4x9`oq_h#nz`|RvR z1lLOKZ~i)q&|mhUd%?52PMa8^UlBtLhp2%MFh&`~@zq!_aU3@^o)OEKmL z8Ejz>t-p6{^IgZCT(w!@N=c=Ue}IVnUd-^?LhLe~&cOgngjpuQEL+RK1QWaM>21o$ z1O9Eyb=2M})j3hg19?I<34|||;(ipiNKL8+jUvu&5vhnTMMML^8DJjMFkpnyh^h(r z$aC}@cQDsh1$n!bugeC{M|G+ruA-L67bWBQFIrJuioCANW}Y_fKgcT?M_lKflS=Zi z(QI|5tCUsFR4Ka`tsQ+ChNtLFZqZylFy2?@(V_7U4L&@DIy(Mwycjw&WkF+5q=GhQ z?dv-<2*Fw;Nv}$x^K8@%d_zszVG4%Csmj4^XC{7UA~g>shj<@utlBGP>9+CiyxcS1 zmcd(U8tTVCjVU&_BULqEjEnY1y<1XKvO%ctbFBk$1abonYpG?de7-?SszpM1bYx!z zLM^~|ha1SjIwzAWE7}U|WqsK!?i%lN^JU3+C4)Aa>JuAHUSb+ctPR(fZ4nS}%RZVOKC>B^Sa_ zK7rlwp%vXo>an&bKocvLChs9$;>U1mMC48h|1MHjMmo5ZOjNN(&L&VxfVM7rxv!e* z4~(~G%KOIKH+Vns^6!{lKDaO71PU6GJ+2;eMLxAHqrF-X#2q;zeb5blq#)o>JCM3I zQu&xwIQec^wjR3e<(gUPYcI~k?s~3+ zlx;(u1!!K0|2h>cgdlB)atGg?A7%HYThZhLu4&jq<=VwtZ?hp;9P&@p8q^>fq{}d# zWHH9C(N)v`dxMvsF1^CbzZL()^+CQ*N27LQ0#~tTsBEUZt4C9>lFg( literal 0 HcmV?d00001 diff --git a/app/api/routes/__pycache__/main_routes.cpython-310.pyc b/app/api/routes/__pycache__/main_routes.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bc71dcaf628a14aac247afe3531880f7219611c1 GIT binary patch literal 397 zcmYjNy-ve05cWAq6HtSA1Q;VBEHNWMs)U#-B(^Ll#V!fNab&wg8Cc3Iu*1m2`*=f4 zeT7ckr2{?b{(N`e_sJ&D_Ziyz_m6tT{7cQQMc7=SyE6=uNobjd5;QM4Fv(@qMmjEI zU@HDdWc&#-k)t=Zf^sC&J62?Cyki9q>60^EGhc?Jni=cYoSq@@S-%1CKufzuv=ekU z#^8}w@&;CbfYCIhS~*ues+asmwXHchx^64?d|ZGf&@W~)OhedQirQ!;d|f%OtY3Cw zVx$tcOJAE#xZ3oh>d6~n&B7}ubk%hC+xoOewKY*BB!$4o+|qwdi!>y2VjUY5S`h(OX5fH0|YGuMVo+e|A1&Ah*cD{3WA*O>P7FeVfQRzAqpuhZA5=T zL@fL(5iH~?MJ((h=~6j+8c}fAdHePq^Jez;CZF#Iip%>Kc|!ZEJ@mr)CY?j8Cegc|0OgFxY(OFVZj}N{vIj<0 zD|_|C^LLbv-YH(#I1Y-`st1P5WBexHnKvIl z`{l=2OhnM8fBs$hC4|r)?80rq=HS-G2)#uzl5q)@@hUEpRf3UBgWwrB&RsWJ6@2Qx9_s!swdhmF zdZ^=F>OP-(pocoqrSA8slReafUFw)mJ=8-z+@+5D)DPs;9n2pdARp}kd1M9UMz0zF z9I+!?TD77UckEDpy-M|KcDr)e7T0TQHCmOo3;K1yam8Oc*DDJBRxK#Ig0!So^hH&E zZUYK`~M8H~?c*Wcqk{ANIh40YQc z?)=6I(px!)AX7VMAwv0EE{ZnbPT2>@|Gq33i)psh==T%veUhcp-E0VTZtx5SB>#9Vn+q$AjO=z>V z9Xr9cXvBW3!byLh)Vs1Zm85E6R>Ng>Ak~(^H{Kz09S0r*6 zcr$e{wa@=7llBUF7g)A~EFc<#AY9&2 z#+!Rvtmy#EHwn`ujOjzyB-qSnV483q*E6R01R#0gjGvAh$;YOUv4o64GFL+UP&*ns z9y7=g9Rlrcstu@|uWT3d1$~<;^uB9_jl6Ct)?Geo>faeyGO=V~>4t=H>~!b`!N0coD}czy?(9thkwLCC z1~ZMdv-zKFQUK^{)*) YYvNf8&)yJ>ffg7RDd6{bxSQv}KO^{X>i_@% literal 0 HcmV?d00001 diff --git a/app/api/routes/__pycache__/product_routes.cpython-310.pyc b/app/api/routes/__pycache__/product_routes.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..639a622cc2df8e49f0f7c1dd411e55f672c963af GIT binary patch literal 1661 zcma)6O>fgc5cRI(+DRQJEoo>=;m`w8gj8QRp;93##I00HPq{>Hc9XdHBdj+_iOQ+H zA};&@IQGZz2Yl_R=ZXvBz|1;d4QXJdU3=!OXWu^0Ok6A$G#K8OYyQK~v}-hGM-Gfv zFq7Y~FpZHw3rT~7v_V6?p%e7cAQu`9L%r+4N@zArqH*KQVmW4X2wV9~naT5Q$}E<@ z%w3R1L7{~?bd}i(T~%mt4s}>bp|(QHbLbjdS7=e859ZJc+fb;Z(CQrekZmfoq|hz4 zJ>vX`?ObZWw#**awc3*#P(Z8csd<*fk>4JqhIbkZnVN!s=<`I@NLo1UHG3jv{T5oz zarRp0;-lZq^?Pw!4t&?lJw~2E5xbg#0q?r+8r*NUJx|>s2vLoDlqC56?ueL z1W)>bywZdN=C9XoHx9WgyIvx>kb}rQj2U;22f#6MlWyE|y&g{A6>(qk#2wwWnVEaN zL0ZPs#%?^-gvf~nOMzO{A~qFeFow=V3pesYe(&XP7hb+3DFY$=u>cf>xYCc(90n7Z z(qjoMlt6hW*B3V6>dM(tLbC!3GP@mfwTVTQcUYZr4ZWz}_FA$pxVV?ISi^wpu#8ER zk;Lq`(|@s$OclP}!XO*CpfxN_N%RC{)AG}6poO9K-4P>?JTmukH8$bM-3bA-W(oMA z*ZCg;YTw@kGWT)<+i>Kw2?4Z*m8k+Mg8HlfmZ31+e20pITFhX}`Ro9L=Mz3?4NbQt zxF>mBF`*=MPRvm#Q2!?uC{cwtlZ04-0y8nKW??5)6!?ksqs~jf z5)2Ay`2-$EwYAh}2VU}iCYi8ZrzTYC9#pA-hOVVMXeC(-O7_O3IdWue!cg^_>d>83 ub*i*iX(#a43zT2roz=k$Nn86Nw7q~IU>8evVX0^~wW&kP#IY7lhx`FdN>g_L literal 0 HcmV?d00001 diff --git a/app/api/routes/__pycache__/product_routes.cpython-311.pyc b/app/api/routes/__pycache__/product_routes.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9d57ad6d8682d7511235437b0f8509665eb1a6a5 GIT binary patch literal 3205 zcmd5;%}*Og6rWjtu^Vh`pf06RLyY(^B;KlpM4}{zR#k2kiA1e@LB_j=rT*&dx)q2@ zMB)GvQaMx#N1v!8df*?av@7kQTB#LM#351-RH&Dn`ra&SukF-Qs~#A39y4$DH}mGV zznSNsqEU%J$^Gz3{Ut=mE9`hBc-^@Aof7hbC`6$}QleQ}60$-mkPT4u3B_P3lnwEA zpcpPivJpxIqJ}p6yor>c5?ZG}hff4TPNBL*iKt>hQ2O?QC*U~s+$1Y?pv4|&sSACp z1AVIpy1xtE-+_+yK*ze!(GK)L4|Kc>9qT|RdY}ip&;uRlA!Yb2oWHY2I@pdY$rX}* z_cijL4mgoby;9Z+TTUpqR$+z{Vd^(ERX5Vqk=ClKRaQ}IdF;iO-R6qQzSZ*TwkR)Z zWn)289y@_2pFf2+A|=(>s3^K4Xi6R=)GPsLVZADYc|i={*k-f6Lr$Q5+U$Q%cj)s0 z14QbT9oqiJ3Na$y7y+@ikD@?fqr%ebA7RdPkm0*d|GH|d=8DBttz4)u42q%bUkVJ* zg%f6~UMm_pigs-5&8PB4rKHNnMou?WW^9$^&nk*4FK@v<%d);vsmi%3R!wG=nxX3Q zHD9Z4H&fkm;<%dU=Bp-PatucQ7|brYkVb52qP}KHX;_1X>5J9i$8Gq_2>dfh8x1 z0kM7v!lS(pY3JT1{8v{}+K*wY_^xhCgsJ!MG~K$4%F$>T{gan}-4wwN-7 z)P)$^*AGSyM@?af4FYyM*&?WvE7uCSyisE+yD3DmVF=RWCyxC1qijjDo2L-~GBoc& z_4AZ&08jUA>HfdsX`sbZmBGorDFmfKNg*Clo0LSP%60e9()-8}niSM4pO#Yfk1T1@ zmL~liO&vcl6Ddm^x5aT&81L4Sk0Y&=TfbQx@rb%Pa@{>Rx`P~{nZ?F!H2mNUw*z zz5WN-%K)YHSCTgW`8g*anC;Iwd1$sj=QOc9YSYBgY$H_v&ZM$MWt+;ELYPL6hb{^H zYneX-MEKAfy^IrKkYmlkRO4CWiAm=yI%m^41P>o^@Gyd1gW!2@)DNCHdv*rkMT;)l zbdiJIl{>(Xyiq@RyrDL}H0iuWVeWZ%ONEiHY22H8oSMenM|-9N~*~!Y^ivJ zR${{|d4&XkV5H+5|90$izWJx@bXo+SXFq?7zYZb4aB}x?Ve$$>ox{Kh zr#UHTLMi4fXN8kEHg|Hj@Di`^6Tb+Oz-n&ZD4I#LXeBL51f90I$Ndw^gRhJ?c=HQc z(qc+$(CQtlcdm8OToc~qTet@Q>hj)@MB7(T6N#t^j^?9<5)#t(JNO+6`6(NV z$T5Kw9~VMtjR zfi9#6a|!Q~7|(VfeaItMwowmJ_oYyaT${Gisa~kjxa2~1aT$j{FXKsBh*(cjrG?bX zS$qH+;=?8MKa16*oX6=LBa3CZ&_cyn-l)#^=S#T_rvwlRZwpySd#nM`XLjW*h{sZ- zTHGUvRbyC^6G;9?a-%yym2Id#uBocW6|;1o0$ukm-HKg0z}H#1z}Ex5j^*oGzW%_X zF`g*WEVqPY2MVw-Hhkq43OyWd@wV$Wto3ThUJdyn?!(??ABP7x+~AcvIDG^I>I!v4 z6w+OQ)JI6TcGig(a}JPVTh%>O4A$}eT2#1MlQjXm(J8xdto6WTU>r6u&6zkI*%QD3 z4cKbka@$SB>fRf;&$fTRP7~Hu)Mq?_0~FfLcY0LrK{AN8OaNxs2Qx^l%k1F{?0_Gj z+PB8o%r>4p&QtZFF2kZkcbdLc?FT46Ycb=3OLUN}*?87k);t!Dz4{JOTQg!cjps$l c7rA%=9|uv$CL|%<0rPcPi+0-(p-X%8AM3sxRsaA1 literal 0 HcmV?d00001 diff --git a/app/api/routes/__pycache__/user_routes.cpython-311.pyc b/app/api/routes/__pycache__/user_routes.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6355a94f5b734619d65cb8a06e168c4c72942b63 GIT binary patch literal 4497 zcmd^CO>Emn7M>v~iHa#p@gL>X@wx$$Sjoo9k2mpRv%6TN3EHCd7I61aZ2>_s$B7h5 za);C=3glJ+e8>Vpk;4M@(TCmCMRM>l$2}H3kpY1i1OgZ+f<ZOhSd@0-`P&j9Ul4;B)Ffq^qZHec zDV61%Tvl>QIg*QrKG}?xW4Tya&8g*hE-v~Mb6>eH*C*N$Q!6KO3DJ(4{pDmXNr^-l zEv3RqjhLZcp%3EsV9xvS2F2L7OzuJRe%L{B`+Ma23~i?z@W|bAY@jq4DlrnhTDy6> zzj8x8GxqOlX|h*mZn#G-wVT{dOO5@#ntJRR>Crpzih2iMqF3+HJM@ZrhhL&MZH&y3 zjQ$L_KSTZ4<;8r>Vmu@JTIsr-=j@YOku$?jU14?}dx{3D*hRbU$J#x>8bdZ_*?y#O zxyo%nRSecVSImkxY!ECBA|_@MUlk2Mi&+4?81bZ#f#1 zQ8?x|-;e!K&a9eg`+b&OuxnO+zG^TFp=JWg4}Z>Ht(IBVzFM$s#_f6~djZC?AJoAy zm8^BOx|l62;#71|0>C zpg;hjOuLjiLDv8{?hc9vN_SX1h};9o`yFY3;HVKRQbQGPj?emX$u9cj8QKAg-4;{8W3N$AQtds$xwa}Dht(4 za9oKW4R(behL2$!y9A_3zE_Ewyj5SW-yQa1dUNmz9r9>;WpL&4e?D6Oi*xv_d-$w( z_^dAfTY2q+B-Kp^!1L667x0)mFr9Ss5st9~Z7ASF*nwW%h6H_qI|SGVcn}Hj;3)DTC~zG~8i<8{LL>-+g-_xDl5rgT9LS5( zIi9*R_BcNJC_cJ!)QunV;)j~Ud*yg!C9#%vGQZ!E=TmDp9uEIkb@g{V{hgO(dJu7m zY;Dnih|7wt**)&Q0o(V=bI9#ZJ`=K)+pC?N1qGe5<7lDcVAuxzM$hp9@qoD8E+x!W zQT8|(k|7Tq406xGpv?O53cH>Uf?+ETRE{1L=fbYRpanbCmfLnZ_$sOsp|uqa;kh7? zgC9rDNbt25fBUsH)iR!zX{G~A-{I!$y83X^)h~GZg;#TODs*yq z9{yVVtf6DVMQ=;_m$^FGc6CQ|o45HC`tn7N&f*}F8F-)X=IE6EFYR$^;!$d1b=*yz z^in6AW1C9KQPMjceR_T7;m5B2uBX5InvM>yJHIMNhl~CRj_!zVcl7DK9DN1{kzB;a zNhElL&t^vb7~J!w;GV}3|NMakvtV5le+BG@%_;^nID$ifHV9^ZM6d+NBd(!ng|8wm zg09TVPFzyJPz+xV8422E^al!sU^@#M6y^Vo|0m50?U3-&zGx4&18S#e}n76F3NzkwdC zQ_G^70{&JM8MG~BXICd3dfcTj)A5$H59bDgQorr=q(k=4)<)J;hn{ljDUY7&%-wBs rd$!ZS(Nb_Rq3KTY&9zHwGY*}0>9j|u_m=-1aQr;-xS3){MR5EGCLn#B literal 0 HcmV?d00001 diff --git a/app/api/routes/error_routes.py b/app/api/routes/error_routes.py new file mode 100644 index 0000000..343d9e9 --- /dev/null +++ b/app/api/routes/error_routes.py @@ -0,0 +1,29 @@ +from .. import bp_errors + +@bp_errors.app_errorhandler(400) +def bad_request(e): + return {"Bad Request": "The request was incorrectly formatted, or contained invalid data"}, 400 + +@bp_errors.app_errorhandler(401) +def unauthorized(e): + return {"Unauthorized": "Failed to authorize the request"}, 401 + +@bp_errors.app_errorhandler(403) +def forbidden(e): + return {"Forbidden": "Forbidden from accessing this resource. Try logging in"}, 403 + +@bp_errors.app_errorhandler(404) +def not_found(e): + return {"Not Found": "The requested resource was not found"}, 404 + +@bp_errors.app_errorhandler(405) +def method_not_allowed(e): + return {"Method Not Allowed": "The method used is not allowed in current context"}, 405 + +@bp_errors.app_errorhandler(500) +def internal_error(e): + return {"Internal Server Error": "An error occured on he server"}, 500 + +@bp_errors.app_errorhandler(501) +def internal_error(e): + return {"Not Implemented": "This function has not been implemented yet. Check back soon!"}, 501 \ No newline at end of file diff --git a/app/api/routes/main_routes.py b/app/api/routes/main_routes.py new file mode 100644 index 0000000..fcb06a0 --- /dev/null +++ b/app/api/routes/main_routes.py @@ -0,0 +1,7 @@ +from flask import jsonify, abort + +from .. import bp + +@bp.route('/') +def hello(): + return jsonify({'message': 'Hello, Flask!'}) \ No newline at end of file diff --git a/app/api/routes/product_routes.py b/app/api/routes/product_routes.py new file mode 100644 index 0000000..271a23b --- /dev/null +++ b/app/api/routes/product_routes.py @@ -0,0 +1,63 @@ +from flask import jsonify, abort, request + +from app.api import bp_product + +from app.services.product_service import ProductService + +@bp_product.route('/', methods=['GET']) +def all_product_info(id: int): + result = ProductService.get_all_info(id) + + if result is not None: + return jsonify(result) + else: + abort(404) + +@bp_product.route('//name', methods=['GET']) +def get_name(id: int): + result = ProductService.get_name(id) + + if result is not None: + return jsonify({"name": result}) + else: + return abort(404) + +@bp_product.route('//manufacturer', methods=['GET']) +def get_manufacturer(id: int): + result = ProductService.get_manufacturer(id) + + if result is not None: + return jsonify({"name": result}) + else: + return abort(404) + +@bp_product.route('//price', methods=['GET']) +def get_price(id: int): + result = ProductService.get_price(id) + + if result is not None: + return jsonify({"price": result}) + else: + return abort(404) + +@bp_product.route('//image', methods=['GET']) +def get_image(id: int): + result = ProductService.get_image(id) + + if result is not None: + return jsonify({"image": result}) + else: + return abort(404) + +@bp_product.route('//image_name', methods=['GET']) +def get_image_name(id: int): + result = ProductService.get_image_name(id) + + if result is not None: + return jsonify({"image_name": result}) + else: + return abort(404) + +@bp_product.route('/create', methods=['POST']) +def create_product_listing(): + return abort(501) \ No newline at end of file diff --git a/app/api/routes/user_routes.py b/app/api/routes/user_routes.py new file mode 100644 index 0000000..b33d9b8 --- /dev/null +++ b/app/api/routes/user_routes.py @@ -0,0 +1,79 @@ +from app.api import bp_user +from flask_jwt_extended import jwt_required, get_jwt_identity, get_jwt +from flask import request, abort, jsonify +from datetime import timedelta + +from app.services.user_service import UserService +from app.extensions import jwt_redis_blocklist + +@bp_user.route('/login', methods=['POST']) +def login(): + username = request.json.get('username') + password = request.json.get('password') + + if username is None or password is None: + return abort(400) + + result, status_code = UserService.login(username, password) + + return jsonify(**result), status_code + +@bp_user.route('/logout', methods=['DELETE']) +@jwt_required() +def logout(): + jti = get_jwt()["jti"] + jwt_redis_blocklist.set(jti, "", ex=timedelta(days=1)) + + return {"Success": "Successfully logged out"}, 200 + +@bp_user.route('/create', methods=['POST']) +def create_user(): + username = request.json.get('username') + email = request.json.get('email') + password = request.json.get('password') + + if username is None or email is None or password is None: + return abort(400) + + result, status_code = UserService.create_user(username, email, password) + + return jsonify(**result), status_code + +@bp_user.route('/update/email', methods=['POST']) +@jwt_required() +def update_email(): + username = get_jwt_identity() + new_mail = request.json.get('new_email') + + if new_mail is None: + return abort(400) + + result, status_code = UserService.update_email(username, new_mail) + + return jsonify(**result), status_code + +@bp_user.route('/update/username', methods=['POST']) +@jwt_required() +def update_username(): + username = get_jwt_identity() + new_username = request.json.get('new_username') + + if new_username is None: + return abort(400) + + result, status_code = UserService.update_username(username, new_username) + + return jsonify(**result), status_code + +@bp_user.route('/update/password', methods=['POST']) +@jwt_required() +def update_password(): + username = get_jwt_identity() + new_password = request.json.get('new_password') + + if new_password is None: + return abort(400) + + result, status_code = UserService.update_password(username, new_password) + + return jsonify(**result), status_code \ No newline at end of file diff --git a/app/config.py b/app/config.py new file mode 100644 index 0000000..438cda7 --- /dev/null +++ b/app/config.py @@ -0,0 +1,22 @@ +import os + +class MySqlConfig: + MYSQL_USER = os.environ.get('MYSQL_USER') + MYSQL_DATABASE = os.environ.get('MYSQL_DATABASE') + MYSQL_HOST = os.environ.get('MYSQL_HOST') + MYSQL_PORT = os.environ.get('MYSQL_PORT') + MYSQL_PASSWORD = os.environ.get('MYSQL_PASSWORD') + +class RedisConfig: + REDIS_HOST = os.environ.get('REDIS_HOST') + REDIS_PORT = os.environ.get('REDIS_PORT') + +class FlaskProduction: + DEBUG = False + JWT_SECRET_KEY = os.environ.get('JWT_SECRET_KEY') + SERVER_NAME = os.environ.get('HOST') + ':' + os.environ.get('PORT') + +class FlaskTesting: + DEBUG = True + JWT_SECRET_KEY = os.environ.get('JWT_SECRET_KEY') + SERVER_NAME = os.environ.get('HOST') + ':' + os.environ.get('PORT') \ No newline at end of file diff --git a/app/extensions.py b/app/extensions.py new file mode 100644 index 0000000..7d8db09 --- /dev/null +++ b/app/extensions.py @@ -0,0 +1,22 @@ +import mysql.connector +import redis +import os + +from app.config import RedisConfig +from app.config import MySqlConfig + +db_connection = mysql.connector.connect( + host=MySqlConfig.MYSQL_HOST, + user=MySqlConfig.MYSQL_USER, + password=MySqlConfig.MYSQL_PASSWORD, + database=MySqlConfig.MYSQL_DATABASE, +) + +db_cursor = db_connection.cursor() + +jwt_redis_blocklist = redis.StrictRedis( + host=RedisConfig.REDIS_HOST, + port=RedisConfig.REDIS_PORT, + db=0, + decode_responses=True +) diff --git a/app/jwt_utils.py b/app/jwt_utils.py new file mode 100644 index 0000000..aec66f9 --- /dev/null +++ b/app/jwt_utils.py @@ -0,0 +1,15 @@ +from app.extensions import jwt_redis_blocklist + +from flask_jwt_extended import create_access_token +from flask_jwt_extended import get_jwt +from flask_jwt_extended import jwt_required +from flask_jwt_extended import JWTManager + +@jwt.token_in_blocklist_loader +def check_if_token_is_revoked(jwt_header, jwt_payload: dict) -> bool: + jti = jwt_payload["jti"] + token_in_redis = jwt_redis_blocklist.get(jti) + + print(token_in_redis) + + return token_in_redis is not None diff --git a/app/services/__pycache__/jwt_check_service.cpython-311.pyc b/app/services/__pycache__/jwt_check_service.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cf0bee51e476932053ecf6ab78346e3b9c3aa3e3 GIT binary patch literal 943 zcmZ{i&ubGw6vyAp=11GK4VZeUAl-usDJAPws9uVQ9)hH|T!wY$rJI^$)0v6=L4_WA z^x!T3K~?%sc=VV;4rM_QM8SiQ5>Gw(W)oB6kJ;VtyqWjrz0bbgHwz1MKx6&;S7Z#} zvt9fesj)F!BclrvBx^vERVk3eBar+Uq;tXnPU#tqDwnBED5k&JWs$|R@|g%aN=GUf zMr=qJOH+Zsbj(gpLIZ+l`(b_g{#&CjM|C+{^)^O7Ah$(JYPh|DP;cp4iR7`NeT zBZAOxZJ}FJQPZ`A?rw+*6O&-w`|fS6Z1$p~ENGm?Jq$VqElge4KB#GE&>U$(dlj{>-9B_vh-WCQx)0JE};N*|j66e+K zD636aE;{idHM4|!23wjsq?3;|s#dTiCj^%>>m&RJ>N+{>Po^J}3>?PTIo#?^--A?! Q5y{_jVfTLg!;zHw35J;C3;+NC literal 0 HcmV?d00001 diff --git a/app/services/__pycache__/product_service.cpython-310.pyc b/app/services/__pycache__/product_service.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..583d184627bf317b5ca10e0f35cbaafda7052166 GIT binary patch literal 2184 zcmb_dOOF#r5bmCr$FIdK8-YU-tOTN1+N`B$gAgJS5eIUzR^sLyMzhmxY-t{*y9Zc{ zoR%{Ot^sL%+;jd-Upe{AjVMy8di)9*D_V)Ry4+P$-SyR@tFvaaMqvE;>K*^vCFC74 zo5Kd?K1}&75Jnh{$$&76*)dJ5flbLn!W`zlB+Nav1`eZ-NY|T#F6qxLpk1n~>^O*W zAu|!7d~;NQxersm2ExgJGBU8Z%_w(<7Fv1foYH~2lAtA&cq@s=Dn?RSNqkl{5`QJB zvAU5|SCR&68c7W#JG{;=!EEvR&|{a`uKChnSHP1!y>lcomPhIl7f;6#*R@H(2V)jd z0KFXOs{``^%=aB2ikwkKUywh@TWdxs^45&}PG3-FwF#VU_os)F$2?M5a04_tYqqiVJm;_8w%6_l`~!Z0gBTHtB_vNlzA@%HGoILU1rS(f(tu0z`|6d+z~h zSw8jY^y<;S!gR3I+ZrQ*n=uCW7BNV|G#`eM$^{n}i-kZiqK*XpEXWS5bPHi@9&En1 z;M4R*3E)l5{|A0&0em8$E`mw)sX$i|v^n$&dSlRDD?w{iw!)PAuz6bzD*MbS>TpKS zTxQSk);j&^j|-Q^N%;7J1^L0v)$sc}=brEpbrlJA7VRB}lHWem)#KZTJdL2@w8uF9 zm!`~kZ|t{Ww_KLk9-WrOz$k8w!K%jYZu({F_fsWn2a{z`m}76C+a_aQmSk+L_aDd@ zG;AI6S0LNcR-_E5-6lXCL)|u6q|<8?lg`SFGXISWDXsRWzb#zHOS`{W;QPzE-Cx5h zmKTf79NUq=_BY43VvbEvVC8mQ?VwfN*fgtMV^`PKR<&!^Vh?wgUE>A@IgI0AoDQ>F zpd~TH*m*qLqxjpU-e1$}B_Ut#(tZwY%)oJZ5{_f-PeLi5Wr9TrdpU58zL;DzV4>j2 zJB9Ymuw`3)ZHJP{2%h9&j7}gYRj9^M!l8RJZ37x{2@zaIvWw&z61+v?YarT| zN{H`}U3W>d!d59TZTM)&uYmZ(vRc$~OJ?H>YIM7<_zX04jXzgBmE$ax0)tmve^Ue{ UX0(5=aOY>N8gsjSh{vw=57gTrg#Z8m literal 0 HcmV?d00001 diff --git a/app/services/__pycache__/product_service.cpython-311.pyc b/app/services/__pycache__/product_service.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a473fd280ab20473d546d62a67ace8eb4fc685c2 GIT binary patch literal 3687 zcmd5;&1)M+6rY`4$y#Y0M+qfy>NW~Zld4fI(#ZKJX-nuKv;||D+Qf2#*zAsC1$}jP zmBfk*F8I)cDLDipXbL{1F}W1_Px!bBf|!LsAoO79LBTx*3Vm-@T4^hX){sE<sr7x+v%(EM}_kMp-N$ zHe&;AG*bgMhTUCLykgc2`vT+F3kGweRj-^Yn1H_Vw;E7}%zNF-jIOV3wIEN##0 zmd&`mP)>hQF(y%7^0>0nU8|jn@#=ybxytMu&bGd9D!}Y`SAmAD zj{%m*A2hN&?!>=cj{?Bcq|+@r?a=8bvG~g8&DdBgHnwzRLmjy-t&BQ!&ldC|Z-QIS zcHC0Z%eB02*fq}h&fLRQ_!T}3&>^3Xfb8x@mikAnAKZq<{sv-*6VTVE^O7@_b>f%c zY0}FrdfB0uU&1GkL%%nH&(j?~s~lo5S2bP>-1fDF<6YwoxYQ6CF1cO8V6u zrkfX`nw|gX^jetX?!{vW8h{(VqFd}_##OJJ%&@Wn`NR#I48Mb`UwBLy-_~P7gBjBL z8o*!g)9VME_%u9CI@6*v4xI^j?1?(kQb!v{R^#`QP4#d~J^WZb_DDVU;CxffwA744 zGjB{BeA4d#kw#xhB<#XS5bSxW`JHXTp{G8G30FD>Idm?-a4$g* z5*dNP#J>3_2(Kq5f~y}&a&H%fk`gF|dZm}fQn=S97$q->rJ-OeGjcBk@uW`H=Q{4h z{V>&^v{T(gm+H~(i97T6CLS_4^1CSjGXmcSkPNvBT6ML4vu;S(b)!A2Zb-;{KlU8h zjx=02da;-*l=GGP+uJOguJV28Z(RrQ!}69>S#aXF;Azsu7F~4cVqn#@!$jM2`_Rgn zjlH9dLwC+>K5W_d*%pt-}DpHEBvgUrKPs24B)i zSJo{P4$R14*-pwl34Qs82*&^{950bbc)NJa;7qq!4yRK;@GsW-bQok7?!idf(2t=X zQp>3&af|<%g!5@6Cqg!tbECOj3682o2C|mReOJ?q_^Vj5Y~3yxB?iYd)0JUC8V9m6^fOKZu$pVD1zD$~7<_u2$LJ$Nn? zd1N^yl>Ve-WQ0uXY9412?vxpmIH*Hq73YD_O6{zsGM?v*TETZ=X{VyVC tJ@F?abRZZ{Fu`nAGbP z0?*&S{*S1@?BBRpeGFJ^!$_Y1;e^wO^olp_QN3n8rr$=-fH#ZGfz`7HcF(3LXGEoe z({l#pURkfrs4}Sbs(NlkwL!gCr{tG}+r0ES;ic!S*Wh%Qw44(-kL{E8+P;Akx%R$)Sh?)VTW8V6hD& z{TC2H(4QU?2B*U042A*26qc|>Ntk_hg$M^}-{BUwKR2J#UU@Eko+Nn19W7azOO`>C z$t%37xz#z>)Z7}cYiAlu+g&221Yk-Nq(Xl@g@#p%dh=NC11jbDNoekZR! zOoiMP^0P1yxif-A><=K8V&I2S?hO4jJxV0cZ7I}9#sOw~@mMgy+ISyE`V|mGj_HIQ zGfrm|=EjW7*wj$wu{9wxnqKCN8zJQosa*nGdjfLlo4~bJxTOgL?iO(E1vhId=eW#E z6EdL_qi@U@C8}~GkDs3OM8pr_Jj9}}DW z9x85X{?Xi>zmMn<;ZOjaP_vFu%kgWB}E*;U+TQn=X^@`{s>ZheQQzeslU&xlb5Xe5fzq5Pqw-4O= zI}bk67Py-W>$Y62erqYb<;o-yUdUVS@9uv1@ZPSwc|F~77vt?#R?^LJ%guiM!lW>U znow>Sga6!D{$oEC9T%$x3&l-jag6Ir;YQ)1h{hm`rnr6t9mdDue<2=uX?LU>ExUqS z-J|AZ21HFEoLAjdm(nR$o@l*sOB_tMkZU@O=5GrZo!Wv45M$3|ASV?RE zSw(&m?G@ZxD9Wk$lF0AFr*Cg}50ZiCDv$t`Y8-dp2ameH8LNXN?xqLHusK$9+JROEoq#hTM@^ecA`^i60+$k#OrPA0JYMC;*4sv-Z zI1s_%@JL?5l>=2TR0yD0Myl1y66%{*fKGmWk#!HN0BTlSA+Lj+E`fr)feNiC*$?9z zFiqEhRG0&4+<;MI_{%P=rcKM{a_#&mo2J|hUjE*!YvR;Q7f3Tv1-byP=N7YJSdD!qs_p2#}eXnLi$uAH92N8 zGNltJqbq2bv6Hn^5v?ZG?aO3P0N4?LsaT?^`yK$4)Y&$4XUZ#ZK<@N~3ZQqy+%3GH z(h*<-9={UnhUcy3_YQnbvDzEVp$^TiuKIUc^n4G$2m3jl;tdBr!Og~!x?&UZ9Q-@P zeM)d3nY21_>X*4ejMM&---bNAoYWRQW0l1_Evi zo*VEtKw`^D0|+2s7^vKU8%eszY$Lge;1@24?WQ6>0MlK4Y&H#vrNyOUlf76jWCfSggXS= N+o5Z8P2=ji@qg_iRn7na literal 0 HcmV?d00001 diff --git a/app/services/__pycache__/user_service.cpython-311.pyc b/app/services/__pycache__/user_service.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d593e0e1b65edfcdae55efbdef6b54eaef1092b4 GIT binary patch literal 8229 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 EA6g~oxc~qF literal 0 HcmV?d00001 diff --git a/app/services/product_service.py b/app/services/product_service.py new file mode 100644 index 0000000..92e2a18 --- /dev/null +++ b/app/services/product_service.py @@ -0,0 +1,52 @@ +import base64 + +from ..extensions import db_cursor as cursor + +class ProductService: + + @staticmethod + def get_name(product_id: int): + cursor.execute(f"select name from product where product.product_id = {product_id}") + result = cursor.fetchone() + return result[0] + + @staticmethod + def get_manufacturer(product_id: int): + cursor.execute(f"select manufacturer from product where product.product_id = {product_id}") + result = cursor.fetchone() + return result[0] + + @staticmethod + def get_price(product_id: int): + cursor.execute(f"select price_pc from product where product.product_id = {product_id}") + result = cursor.fetchone() + return result[0] + + @staticmethod + def get_image(product_id: int): + cursor.execute(f"select image from product where product.product_id = {product_id}") + result = cursor.fetchone() + return base64.b64encode(result[0]).decode('utf-8') + + @staticmethod + def get_image_name(product_id: int): + cursor.execute(f"select image_name from product where product.product_id = {product_id}") + result = cursor.fetchone() + return result[0] + + @staticmethod + def get_all_info(product_id: int): + cursor.execute(f"select name,manufacturer,price_pc,image_name,image from product where product.product_id = {product_id}") + result = cursor.fetchone() + + return { + "name": result[0], + "manufacturer": result[1], + "price": result[2], + "image_name": result[3], + "image": base64.b64encode(result[4]).decode('utf-8') + } + + @staticmethod + def create_user(username: str, email: str, password: str): + print("asd") \ No newline at end of file diff --git a/app/services/user_service.py b/app/services/user_service.py new file mode 100644 index 0000000..cd779ba --- /dev/null +++ b/app/services/user_service.py @@ -0,0 +1,129 @@ +import bcrypt +import re +import jwt +import datetime +from typing import Tuple, Union + +from app.extensions import db_cursor, db_connection +from mysql.connector import Error + +from flask_jwt_extended import create_access_token + + +class UserService: + + @staticmethod + def create_user(username: str, email: str, password: str) -> Tuple[Union[dict, str], int]: + + if not UserService.__verify_username(username): + return {"Failed": "Failed to verify username. Try another username"}, 400 + + if not UserService.__verify_email(email): + return {"Failed": "Failed to verify email. Try another email"}, 400 + + if not UserService.__verify_password(password): + return {"Failed": "Failed to verify password. Try another (stronger) password"}, 400 + + # Role ID 1 => Normal user + # Role ID 2 => Seller + # Role ID 3 => Admin + + hashed_password = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt()) + + try: + db_cursor.execute("select max(user_id) from user") + last_id = db_cursor.fetchone()[0] + + if last_id < 23000: + return {"Failed": "Error occured when fetching last user id"} + + new_id = last_id + 1 + + db_cursor.execute("insert into user (username, email, password, user_id, role_id) values (%s, %s, %s, %s, 1)", (username, email, hashed_password, new_id)) + db_connection.commit() + except Error as e: + print(f"Error: {e}") + return {"Failed": "Failed to insert into database. Username or email are likely in use already"}, 500 + + return {"Success": "User created successfully"}, 200 + + @staticmethod + def login(username: str, password: str) -> Tuple[Union[dict, str], int]: + + db_cursor.execute("select user_id, password, last_change from user where username = %s", (username,)) + result = db_cursor.fetchone() + + user_id = result[0] + password_hash = result[1] + last_change = result[2] + + if user_id is None: + return {"Failed": "Username not found"}, 400 + + if not bcrypt.checkpw(password.encode('utf-8'), password_hash.encode('utf-8')): + return {"Failed": "Incorrect password"}, 401 + + expire = datetime.timedelta(days=1) + + token = create_access_token(identity=user_id, expires_delta=expire,additional_claims={"lm": last_change}) + + return {"token": token}, 200 + + @staticmethod + def update_email(user_id: str, new_email: str) -> Tuple[Union[dict, str], int]: + + if not UserService.__verify_email(new_email): + return {"Failed": "Failed to verify email. Try another email"}, 400 + + try: + db_cursor.execute("update user set email = %s where user_id = %s", (new_email, user_id)) + db_connection.commit() + except Error as e: + return {"Failed": f"Failed to update email. Email is likely in use already. Error: {e}"}, 500 + + return {"Success": "Email successfully updated"}, 200 + + @staticmethod + def update_username(user_id: str, new_username: str) -> Tuple[Union[dict, str], int]: + + if not UserService.__verify_username(new_username): + return {"Failed": "Failed to verify username. Try another one"}, 400 + + try: + db_cursor.execute("update user set username = %s where user_id = %s", (new_username, user_id)) + db_connection.commit() + except Error as e: + return {"Failed": f"Failed to update username. Username is likely in use already. Error: {e}"}, 500 + + return {"Success": "Username successfully updated"}, 200 + + @staticmethod + def update_password(user_id: str, new_password: str) -> Tuple[Union[dict, str], int]: + + if not UserService.__verify_password(new_password): + return {"Failed": "Failed to verify password. Try another (stronger) one"}, 400 + + hashed_password = bcrypt.hashpw(new_password.encode('utf-8'), bcrypt.gensalt()) + + try: + db_cursor.execute("update user set password = %s where user_id = %s", (new_username, user_id)) + db_connection.commit() + except Error as e: + return {"Failed": f"Failed to update password. Error: {e}"}, 500 + + return {"Success": "Password successfully updated"}, 200 + + @staticmethod + def __verify_email(email: str) -> bool: + email_regex = r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$" + return re.match(email_regex ,email) and len(email) <= 64 + + @staticmethod + def __verify_username(username: str) -> bool: + username_regex = r"^[a-zA-Z.-_]{1,64}$" + return re.match(username_regex, username) + + @staticmethod + def __verify_password(password: str) -> bool: + password_regex = r"^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$ %^&*-]).{8,64}$" + return re.match(password_regex, password) diff --git a/main.py b/main.py new file mode 100644 index 0000000..a76f213 --- /dev/null +++ b/main.py @@ -0,0 +1,13 @@ +from dotenv import load_dotenv +from flask_jwt_extended import JWTManager +import os + +from app import create_app + +load_dotenv() + +app = create_app() + +if __name__ == "__main__": + print("Hello, Flask") + app.run() diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..7999f0f --- /dev/null +++ b/requirements.txt @@ -0,0 +1,6 @@ +Flask==2.3.3 +gunicorn==20.1.0 +mysql-connector-python==8.3.0 +python-dotenv==1.0.1 +Flask-JWT-Extended==4.5.3 +PyJWT==2.8.0 \ No newline at end of file