From 0e5052676bea840f5c849f8819d10033ae601668 Mon Sep 17 00:00:00 2001 From: Dita Aji Pratama Date: Sun, 13 Aug 2023 21:55:18 +0700 Subject: [PATCH] First commit --- .nojekyll | 0 LICENSE.md | 1 + README.md | 22 +++ _sidebar.md | 30 ++++ images/structure.png | Bin 0 -> 52149 bytes index.html | 24 +++ pages/configuration/database.md | 29 ++++ pages/configuration/directory.md | 34 +++++ pages/configuration/globalvar.md | 11 ++ pages/configuration/server.md | 27 ++++ pages/configuration/template.md | 34 +++++ pages/content/handler.md | 243 +++++++++++++++++++++++++++++++ pages/content/modules.md | 60 ++++++++ pages/core/loggorilla.md | 18 +++ pages/getting-starter.md | 48 ++++++ pages/main-process.md | 167 +++++++++++++++++++++ pages/structure.md | 17 +++ 17 files changed, 765 insertions(+) create mode 100644 .nojekyll create mode 100644 LICENSE.md create mode 100644 README.md create mode 100644 _sidebar.md create mode 100644 images/structure.png create mode 100644 index.html create mode 100644 pages/configuration/database.md create mode 100644 pages/configuration/directory.md create mode 100644 pages/configuration/globalvar.md create mode 100644 pages/configuration/server.md create mode 100644 pages/configuration/template.md create mode 100644 pages/content/handler.md create mode 100644 pages/content/modules.md create mode 100644 pages/core/loggorilla.md create mode 100644 pages/getting-starter.md create mode 100644 pages/main-process.md create mode 100644 pages/structure.md diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 0000000..e69de29 diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..03e3adf --- /dev/null +++ b/LICENSE.md @@ -0,0 +1 @@ +This work is licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported License. To view a copy of this license, visit https://creativecommons.org/licenses/by-sa/3.0/ or send a letter to Creative Commons, 171 Second Street, Suite 300, San Francisco, California, 94105, USA. diff --git a/README.md b/README.md new file mode 100644 index 0000000..6540165 --- /dev/null +++ b/README.md @@ -0,0 +1,22 @@ +# CostaPy + +Python Web Framework. Build with CherryPy and Mako. + +## License + +CostaPy + +Copyright (C) 2022 Dita Aji Pratama + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see https://www.gnu.org/licenses/. diff --git a/_sidebar.md b/_sidebar.md new file mode 100644 index 0000000..3200247 --- /dev/null +++ b/_sidebar.md @@ -0,0 +1,30 @@ +* [Home](/) +* [Getting Starter](pages/getting-starter.md) +* [Known the structure](pages/structure.md) +* core (extension script) + * html + * authentication + * [loggorilla](pages/core/loggorilla.md) + * uploading + * mailme +* Configuration + * [Server](pages/configuration/server.md) + * [Global Variable](pages/configuration/globalvar.md) + * [Directory](pages/configuration/directory.md) + * [Templating](pages/configuration/template.md) + * [Database](pages/configuration/database.md) +* content + * [Handler](pages/content/handler.md) + * [Import the modules](pages/content/handler?id=import-the-modules) + * [Routing the handler](pages/content/handler?id=routing-the-handler) + * [Add modules into handler](pages/content/handler?id=add-modules-into-handler) + * [Request with JSON](pages/content/handler?id=request-with-json) + * [Request with POST](pages/content/handler?id=request-with-post) + * [Explanation](pages/content/handler?id=explanation) + * [Session](pages/content/handler?id=session) + * [Sample](pages/content/handler?id=sample) + * [Modules](pages/content/modules.md) + * static + * error pages +* [Main Process](pages/main-process.md) +* Sessioning diff --git a/images/structure.png b/images/structure.png new file mode 100644 index 0000000000000000000000000000000000000000..195db2afcf440b6cee9bbceb5239b92f7ebeb06a GIT binary patch literal 52149 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∓YR9H1HBZg~Q{@{2eMy%IcPS8nmpeoxa` zvrthLH)x)rG;rUAEVz1$FYZwp?&17(*1>J=%{*2dh}OrP_L2^^7k;LTo_ZfXrXMG& zJZ;Ud*>D`=UsxvK@yTgH9;3nh)VDMsrk!hxj~|Ch`T7f|i@Rj^+DLwjKt+pdBZ0j^SDp#( z$W`o7EWkL(V|Lmf2T;68i=VggV@4k2IV9p}KtOHz`&95?h zNaQp>;3}s8-My~KpL8veK}yU+n^cr{keET{ZLIIUL~J{W`M}ds)eB(y?e_j?a49z% z`t#?b!6_DJEi_^luK8PNB=@E*Uf{5g2i@{A|K#<~+RNQ{q}qT+RwedUO+Uq{qV7bS zmjL|gBfAo!Bdz}On%>izMX)g`tQRKNqGlFhnO@fzWV3mi6>4ATcBq}1gDLT%ktDle z@Q&4zwUF9%Y~_=7^fhJPJt-A{fxC7FLztAemrnD2o<;E09D1@mHe2cIU<(kOqB{!h zqxA#&6easx@ZjO)c|6i8uyNVhA`C62%Em8aUP)KaVs{clwdaL+)M1^ax7mL z3^iO1rLF4*`l7x{X1;l&YC%vGL5rmGB4wb~J~E_gAi_pn@|#I3@3f;i1B34(gZUkX z$J*$?8=@*FqmC)-s|mK+%Qg>U&9&`tfaWzt#G-CM@4l%PW%&ZHs2VaZ&g61Cz*UY_ zmZrQ-nobF|SI#z4Rw>dbc`mXhFp~BEdVA}zD4*|deCh5KK~Pv)Qlv|yk*=lHMFi_RmKhNiT{o=pp@!}7!`<|I|=FFM7XI`&! zW)+N&7+wj1lnRiqyRtasZOQs&|M$iRPA7XPzz2<-KuVZY@^u4_kq6yr#m8k;_(d znUgL6T1SECD#I2rCAo5E#~~Ft{=PK)o8NO33VH){Mf^y+pISs5oo*-|TqSXnxQ;L3 zMXeV=Y&-4tTH@&>A2Y8pax~kyAQr~`=O@u#>s~j(kFX;OMYKVDmt798yuR7tRN_j* zXOUC&l%R8`R8d&$Z8+Mn*NWv6dCAC!!`mrZL9|rX<(-MoI9(-Ig=9sQ){}kws!1QD z?#Os>k|_M{aHRFzV9P-hx47^x7w@VnZd?_}G-2ryqz$C2UAfGBK8 zkv?FzO^2#Ty;^l-~$D3_o-%r5rGvhhh==VI0M%FJoou7L)Os($+M4Va#5^Iyg6RP zK?_g`^0P3cR~gmG^xebiMs`64GI?{7hbw#B%%e193{{oe+2!3Ey;3`#_jMDRmkqbS zn5MX#E#(W*9R2awzV6qf+25GsE^(U8|F8%5s2L{ODB@D&HUziD-}j zGq2|tog}OLX}Z!qn<^(b7zD0Op8i@JHHarTSKRvYZJQ>Vfv-aV+3QK*!Q*_+^OA}3<%=|sdCyUXV!ALE(I#0E>(4+&1JlkTc^79(|H+OPR> z#+gGOxIw?nhc^GE_KX3bIS?rVgZYO06^pU_5^wRgc>%3Jbp6MLw_>8 zMAb<4pfB6-R|3H;LQfY-|0nZfMV^oK#S>UTyy z#W)`bcx6S-=PZ|HVGbiX@(T3lM!8nc16yJYWo1u9kYfp6JbHe?;h&4Pi>ItDu>9vf z9MQG0JwcgG$15%`&aRLGI-|unYt{&pU4pXmf6gKdx7g7o4SwQVQK1 z3LqfL&#*vF10btKDM`1qodD!i8tV|c3v$%I3dw7xv-R|o(m6_D@Z$x$C6J$zk;jlp z{(tIhtT7(WBxG34L;XK#B>WS_ z^5R?9OhZp;VPxyGx}T0+eSP(BHY@-lVtYw&l$@LtE%)_({)gZAdI2?@nfTC&mj2&B zToBnREc9Hr5a=G^x6*dR@C%>_HHmWQ)0a-xc4)JdCBq7drlF=TIr9~Jo8||uySX4Y zzQ0RzHxK?#x{2p$SyAsV@1E|^cH`$EX!A?xWqf-}ss=zWC$F{3JWXja@K&V2Ei1kr zG(?i#RAbHbXHR5aR-vI2)5Wbe;m_Z+ceDe7#Mzi^r{`z-!uS-lMJv{1Yx6BI4C2LS?EQ869E0Mm_n_x+`KE&V1Kl`9%_s_$2|mHeA%9}hI-5!- zL)Ng;FHt-NQdB8rY)IOtT%G8+i%WS1uG}2L)E&J7POQ|w=jUOo-Vu}tBVqObk&ZH7 zb6V6zbn%G4_Y~*XAjBYWmB(SMrak2c=%4< zDA%T~_bT}>v~uw~fTdn)7r$HfY|<*8xE-;0vu*iNcRl&8ik7QH;;Q@LZvS!A<{IID z5X*~{6kgBsU8j%SEEP@1E_7Y`Qrxks#0l7TbX}KM0kDp)?ad@6CKj~}f=JhvOxAWx z(561Z1VF96UeC9;rSYPoqwOFsaczuEO-GYkGIPz`g2d#blt3$mkSc&2Z7q||&zpeY zxk3a25ePtMZgqVapp6OzjWO8~CJW!-m=Mv%00|9}|LmnMJ z5W^%EJXQi!A1Jno5q&ZQ%VZEIHnh4|s@Ba=1_LpFD!v-eXxV^rshF&@pAh1WbK&+Z=DV8e3Sb5|fZ5YiMY6 zytk-Kw-gu=otc?=m(pik(WucM*1?VutN;h~?|R3(GiZkpcWrykf`G1F8J=rA3cyw} zU}T?4_u;DdgSV?J3?(4z- z1hB~msC!(9n$`z>wA8qMB+HYVdl}P2mlF5<#)TRZpuK1BVyNk1Samk46^%Cnl;0;~ zBu!U<5t!svJ5|S`puLDcTnV}LBjv)Xtw2`K20uVW%MoVPAXYm2WA3lZaeD&VTQ_T#-l067Z8u7!XLRehw(dscN>3Zks8Kj z6n25;dR__Ga)uzHjYmMhieg%YRXg?0)>{+>2q6{U%3!ipCO*gks-7+K^uEBvobks& z^L_-DK!$QC+TZ9FLLeY&4o4pq0r<1NhzABGJG8k+1^H}vfn|WIaM^wHn8^h2eN2Lo z)MB08Vk8!=l?RY&-{+oLor&Sx?(Op29%3B=7BL5X3oH{?{@w_9vhTiFB{D&!@~LFG zn;1q|(j zi?jRWv#<`--=)b^VSEgL$ti%!pwk@fP@tiOSxBrhXXaJTN%b z1EGB_7$msC-{4j(hO~@jKy-gEJ~9APq)HNW@0Ok9J=1%brrsRUzFLlV0CNoE2rWae z^6f1E*=OGjG0c`5%IU_}_3g!cK_z?a+Z=d7Q<#fW41*1E3K1?~(rs5v(%zfH*l!Lm zcl+<#GsD}6HV{S~%HIpqrZF}EO}%|9*Z$_Hfa(8#C_tio6(U=Th$c$`*%MCL`}X?5 zGboWUOTv=peQg(;y6Nv*J%5)tJF6=GJLJ~#{J?9uEkj+(bXN*4<@hqWIkhr4|8TMD zFgv8&NQ!?D$Nrn5PzDa9pwGH^wRHz68It7Awt>q}ZeuIR_55hVu|4YwCcD!~`_qju za{F)Jgfe;&-@5FwQ;c>2AETg7l#oskJrSA8BKqe@bvM5STLcd7a1&#@IgFcp#G{9= z!JY6hr09osZqc(ejX1>wfXjEsVB%;)Ye!AN{bmIvM+LusqaM^yKYqJ`Ys&@#XA8)q zX_yA^I;Og>uQ2l8=E5HwzAoQK4d0r>>v`rx#e`EMaXq9mI8bsf=r#;iJ=!glP*47G zBuS;@BQ{7LSh^SbO*Y?ZjUX>OkXP50Si+Zi#s7Umx83uxo+uEp(EtAxvGC_;@nhz# zJ5F>7|7y`4%a$CWGvmMvGH)-r|*6kKf*+Ny#5at5_p(@_x8VB$kI{qv+P?c zYqSY1xE7Irt=;MjbP!hK#E5Y$eY3c>YZ@(>j!pq zB$e$sbTAHMDEw&*R~T!hGjZKWkgLgYnah#~Mw9XgtF zSs?j2<~k<=j0)VZ$rRHDqXvDmF|+vXgU~uIAXx|_nGK^t(Yx!IORkvu|4C@B-UE`a zs9+>xRQTUvHKkx87Do{cX9LNfF_ObDDx~y*h}w%J5xQw|WXzTm)!CT{^S!7YPl2fV zcsZN)Zz=QxVlO0;buO8Q_}Q&}9;Pw@0`{&_O)ptikZ|oMTKUO_6kSIHwa=xIR;8ocm#WL-Ahv6Gx-$OhhtMN>> zp5T?KMNtq){QfrsnOXuKHy%uzA$M-|0~F|b6!87Q?Lfd#L=13ctDcS@_e_q~yC`n` z7IsZFqx7A-Db%`+Z__Y$(B?;YMfwV!tn{a?jVZz_`?#SSEI4+b^q8%0vF6R?v?cDD zKp3tF`nTd0dDwN$G22hz>V1f}@Pzh^3J~2t4BF-*c~4Q|A~CR80dV{rQ0zMa@qw| zFWg7GEAw1>bIHoa$%XBlW8lyuH-Kh;&l(Rd+@*d(+VzLzOJ;~qFu~+G&ghR30E9F^ zKvEZAH5DNNmH26^IB#6=kQ9<-o9`gGPc~-61zRfJICd`z(azf2`LhRwKg6iPbfR&AV(LY_uU^6aH3CCTe@1M42 zAjLz(cM(hP)AepGJKqgV4*6^%mJnR|gf_fnYTLE&o&oNgy*nRY`A?5-wrp=Onug2m zhLXaDV$*{Sr0<@>5D&x7!vKlq-m0mXW>_U>QSXkLLA1EtVses4H1}XPmR$$)j~`^= z_fSl-tz>&CxIdhp!EMl{aDuG%l_u2fS=-D%1-DiFavEJuUt(bk#;x9@U^}&*F0)#n zsyRP5#jlk_g0paY`s5*Tf4KHZ}Kxm=5ENT0uxNg&0q#yO72moVd!w?tn+ox^69Bk&gaXW`)!hRM%0>cNtue{Z96mJI3tbj`}1 z$+IrNS@4{AwZSmFJbn)X0_#bdUcU|ouwwn0v%;93>z7YW}2OHKTodBV-mxUDuqNar1>ESazXG@ zngOyd*azOA8nMyvm=9JVz@xD3cdF}`{4}m?*YZA>{ZkZK7(0?_K z8#WX#`@);=cJ4{&JE|i$*`&DTXM0nQDaJ^FR2RC|w?Rz%E3ru|1q-p#3elw8ui%!a zkxwVp31beJcESae$r?!7HHM|nu_mt;TE7#;B}9+O5&PQ9Ru|H~Y_Lmx^6imLTPtFp zU}d{Du%so{)C?!=#(A(_`a2E5nrIIei{-oTO{>pO+0cV4T?K|8>kmw6~|sUxvP?buc(+D9`- zh#Vwv_fL}(pLWV}OLLMfuUqPSQ8kcISdtgOJ4^hE95fNVe zyKarIWlnbBjQ{lS+Bp@uU?4@{314*!wW&{?75^3$#e=IJ7AB_(A5B?4h%opT7PlmU zu9v*M5@m>6m{xu|Nmjz0scSSLPQTp8v{s&a9MVa-b*wA8a%HW5{-cg+V>#`5M9u(t zL4^2usO7mYoqa27gwtYH?fC~13dSlPKG>`WvRS7!G{xii#_TxOdD#Brh>gUQLZUn^ z>lA*CG_6x^bAF_{u@Q@qJC$xT&YwXQYvt0>3RVl3gY`DQbF4ETs4l~Bzl!X*Nif&U zUolkX8luYhyqUjd?5|mHt`e{7cRLh=t->qX=^Cq}Pd+s)2ZkG`P)$d&Ysb7oGyKze z*ZkYloUxowwB)}J6|m^KGP~|jPZyE={k)sP|5NJKR@WT`DRdN53~~_%W@wcBszBq3 zo_Di(07HI)=oZXmmT$iKsjK{fM7N+6*K%Hk=XGdwl5Nq`I3EF|dRkZ2%;G&FD(dl& z=J9Y>ZkxP)6s&f7Wai^ulhV{e#(s?(E8ZwzV$?D zp%|`*B(;}G*5Z8y0ysZ>Xu*Ew2$e+SadjOY8gTLm%C~*u4@bIYy-DRK8R4jWtB2%< zxjAu1lX1R+!$_-UmRzAdo7AIn5RJ5Ua|Cb^q;GU*$~7n-XW-zJ7CNcrZW={GlI<3? z0jNmtvh{MX0N(yg=jas=Y%slR8%rO_Z}mWf9@;Z|^O}KwO8Lu8^qlZ8Z$cf~ErSL~L_-ZZbAs@7Ho%W{iFKNOLAv`d8 zL1D~DY|+Q_$Z8mC9Tc`^Bte5B`B~Y39OxU+W&X)o;UZOZ6j9D!aRX$bVB$QEAw73V zU(em9p8JOuPU$v6*t8}$gzhRoUrUm|)24Eo|EOTW_LSmYZV6ZEP7~Y2{Eh2nRD}2; zvvq{O1?P799qPmkc}p!mZT_AQzI{hpV|kX zb8T2ELoam7ta?RrwTAL$F%F)jq-%f-r2jEAEm-Ceja_wVDFP{SphQJ%Ec4PmW|U|( zm0vhzRLYZ^kpXMlh;FHdqMwRdT|tl16{1W<_x-)HukZFno193BUMUkjA5snX`xX+R zfnsfoIX{<6Bq-I~bGezncsf&MR`?Ic`pBb^&Y53haQC=uQdSwo1XpyhLm|iUK?>NB zPzAIGze^t{5mtefV@#$A^(yIS8|+p2FcQ>~lPV&~_Cu2BefD$Jeg?_CoJ8EwV3one zU)~%FXyn78jCc3PIRg_P?p#qvoZ4{&^*O16$tG?5TG9YHic^m%WcxFtHUyU&hTpdM zqk~FlxxJ&rFP6PG!}bR1Ecq={OglMv>0Kh?A@c&ECwN<>w}+k$OA&F}DaRJ_g*q1w zO%u0Ees=kakI4J^eIcy9oKJYuf9u&n>z3$@>csPc#c1Z^el?&p`;v#VG^q8nX8G&S zYA2eOuA0Lb4%C@wlH6iFJC;ToOIcqQ4{R0798(Cnsx_tn6vmnHaF`=x+3>jaVpSy~N0>LK6B|%CWK*)v4L8QWM%ck>PR-*{24VF%ycGXo4GyL^ zrC*sTZrJ>Yjs-4{r=~z@b6!KC##RGN8adzlljAz(RmhG2{(uhk3F&7EY|Un?jl+(6 z{Tb>gD^O^!RQ%n95=!Bx-bX8!w&3-c?sPI$Yz_r%Z_b3xh>x%Di~9HxGD2CrUzub_ ziBeOuVNt8eU}+f7W_YL&5su#Tj_EcD)fdIZyby{3T@d=4HL8ZKq8>4^AcpM7dWEZo zz>VQlK#MeCXH8LJdndCLY>O2(W~E?Wa9meFClykXve)3oB$0pUeKlI@ejC$NLNo-b zS?Mp7iAz=eYLzk7CA1rD4=SiBXn+CX>1oR~%S7l6-f!?TZ;E5i!L$VjLXzG~#ZmhL)XeGd&d2JJq{t(C{b=jKKHnh(LKCr zQv5KdckkYnnKN1Eb7-aonrCu#a$x}@WB@0CSCT)B=EKVA7r%n?g`mmsD{+K+S9T|_ zj-Z*Y@arW(-=trkx+^r#3`UHDjSo0PdFp)&H!e0~pQPmE9w6W?tA$?5SW{Ee>fQ1O zYgDX5E4!S(7qy=U&RN|hSHp}n=Z%Dfh>wxJLWa>a5;k63d1a=NY2`}gkDxNv(G4`; z*?}@4pTTUYQFK(!omtk-H>rt9;4j#zGx89IVMa>=0f9!}yf2LnkZ!${4@O3>GfUo? z?k3Rqah83`dOrW;!@`3$jD`c4f@?OQpjYRTXA3iNxUlDx z%kfZb5ut8WY}Q7J+WHGOa=8g55cw~nmXr__U+H9p*fvTP$%~&@a8RS;@7`FIPo^7~ zam8gOSJmU7&9X8*Nn1LqDv`#Az84Z;R%~IK+3AO`5wwRWB;hE}u)SHVx;3@a@7R3B z#S?e05`NRu#+zCx!#n{#AqtT8WJjt8^KGe7zYFau~+W$fM&K5f4`wdGbj5 zwK`b3C7t6tangHi(*~|AUc>%mLuq7W#LdUI6LDweF#O5F%Qxp~yKRpDS>?z31D#k% zv5kxrU{_A^2p9G(9I_X44j+roJ|=pc1jXh192sgM{_ERh*UE2B-Pwp%DLDBz>d^VV zF77T_;q+53A9H0EOZ0Q<*4Cym+I%Q(&+E9*Ulwxb`2y9y87{s>y0sjF$v4{!zR>6O zguM52IzZHzwMYA)SYvD&znVuph#ttu5)l^^j1RS~!RHqc*o#l_AeT<^LeUspcaF%(5aasBMZ9RHk?$wm%XRwN|bP@%!A|X)|s0Ai|rt$}<#H!m1 zQ6YA*+^eHC56{cX`5$6Wm)}&$&Cka!Dk_SWJKOWJKXy?Tr1f5Xapu@c{{-hVeq{+n zL$ju&Q{OKq`1z-qFGTFQ{JOfY(<42zKWQ-TJiEuy_n7WtSE16z=`?#6r-{63eXvMV zs^3P8DvL+}?8=8LEw6H0h?C6%_754nZZr?nVpf5hmYyL7$o#o4WoFMI&@$Ub_aA=qT^GXWF-OXD*siE9clzz@>{thxR<|QJQp&@EW55+^qFLIDf4+YG`c~yM z0eu#^@l9=bnRxol7IhrP10w)S)Ro=jcE1%OHFg+!VrS-brDt@w>>a)N=T>l~u(*Zg z2Ss)J@WKKq#fpj8Zu6>d##Ky}@Vb`28t{|;FhoyySN+Y+i6Fi^@~2mIX?@Q`$SMQ` zAk5@Ho_@q_aTCYMqtx$XCG3BwgEJ%K{qa<4(D`coww&MN#-!BS{`?ljIeGoT76P=z zG}6u|EN!jMcQjg~{~jw{|IsW$v*e?#(4MYx$@a4wj5HeIqd<6o{KX_!EMJ zmceEJHpoxU`B~@p{N&1WI@aQUxpPH(Ei-!WHIkAjVr&2H`CUsV7AeneaN8e+f|&~M zollf3!JDC!L1)>G%c9;#(2+6#w`{NGpWEi=bwDAJK4zea>aJUHNw+othel{ z>|S5E4M|rcjd3DSr;9w?t<_g^OS^b2;!rW@(cMY;fv3BIo2N6JKLpMg?ZqeY`%{W* zcFP~`rPGQbCiWs6TvDX1kD6c9#$KK)G~i zx~FC?bIM`lg;8-Y<-Vq_aX$9DgO zaqtyQAwKRDPizx7+1?bI(}M>!(wb{K6if&jw@Q`sg*D1s*?oDiW?2B${9+9zYhJ0t zxa|8MVks46f{b(NTMTze(EiTdS4YjvLTGfP>G9YFzWj)MS4;cUXHLP9Zz68P#06zZ zVI5E49IPLJMQ!c#OCCF#K_G1WRh+?gTqmE-$AZ&iv}IhU?Z|Gt8PZ)x8sBbDhVI>) zj};R+SxGSw=iXeLaoKMw0$+O`xgAVb^w%6N-qc;;im!v2O+UR3a|zt5H7~aiODKb{ zE#A|s50JU06fFBuD`_{3;*Tr^cdNSKmvy1(r&tGvQbBcZr3m4Te>aGz{O6~dPHv^} zZ++~jq)9S5;H$Rjug^c&Ip@bSw#Hi}#zLzf5s#(4Zdx6R!Ec$wj^W15Uc&@AVr6a^%v%)TQ`PP{*ADsl%gsxtVu;=I*n%6p5Z&oZzSt-BvSoGM^v3I4> z1ZigN$CAb6_1xv_Za`VajwV_6vA6OUYrK7>bAm;5vBNow$2AXT2dhA)I_sy;zh-z; z@2<}6eNZibxz=DQ;Sf_F1oIoO8}qUcII{u>nr}|V$ElfvtU)u!l|;T&`0mzW;fkxW zUTC5k{L_G3YwI0jwEyyqi5^TZoJNW@c8Xk@RYz9Fe9pIhmm%-L6X2R z*IzyR9d50kn~dbIPrQyQC(rO*X{V^$@RDi?^LDBo54Q~P?m+bPDc!RfnXG9qO*Oqz z_I5H{F8wbGc3AAN!w+j@wtkA#Z(@o@JcEBcZTHw=Chr8h!F}#fnY>EYY2^lI_b=@A zzblVvM};0(%S#;Gw?9k!yEq{OcZmNsyiTKnSo$1-~iLJ#c-4 z>~EjZr@L%xOhsP2&)}&n8_ySKxW4E^0)1i!`>g!znqO=RUaeU<4PNHc1JliZsq3d{ zMa5lZcG!@Pw#x7cbktp={&q3@2U)4W)^I);!O7hWFX)O|Qr-HioV{|mY@Lso{nwnT zq53VEj4Q_#@W>qtrONjT$>lBSSz+~S%>n&zvXcpygtSSsa90rV)L< zmAGB+Ha9F!{`tqTU0z2k&B!nBw|hZbA4PX`l&eVk+TO+Yh4B4+>m+=puV0@75C2RR zljDh#?FWymci=Q`>yz8}Y~DXA9*Y_+ts0q)n9Z5-loZ~}OZ43dr>ORuqhHE)mPw`y zQvbp`=vKANcMm5X|4juJCq35HO1y>ha+lqY9+53kVka~TYNIA55R%*2@xdg4Ztq)W ze~fzFL2_1L8;Pdu5VN>x~))K zeo@IH%{ryuJhVkcUJEi>-DM3~`dH8}PGzExq*l!EV2(rFN`i*cmKn?@G@06tSvFGI zH~fkOXnJ7<9J>Ff=yjR%(SK{|OZ<;)b7}yW+nMueSoAKb_VsAcVIOvF1@%>51-wr5Nu^yC%C5cWg~1ER`am+)Z~XH(dlTB z+jf$B>`LXpvOpC+UuZadfj?|m@%8z5^hrs59*E>(ccMKJGdsi20Joa|f zSiEHioms0`>eJ_Y9;2nG31ISVGiq)-$KR-?xmr{^BTec^!I7)Sb-v^qO)gxye>uS< zszPn+J&e48ATi2mjNZ2TV4P7kc4{r9{GjHy+C44>w1brBB$YfkTVWpbvfB`&9jqLh zH7Nl+LJe{DXPjl#z#)g=2XfTmmTnmY@eGOZp8Q0OFJI`}wEvqeAgMhp|I0&NB!@s8*_{IT z&)hERVzrAy9T7OxZQU^dw1Ex!djVYCiY+=q6`U7C?j>A<_TVZA%rCYG$Atc<6jsmTy@vbHj~Hx*?CFbJyCf7&-PgcKE-{&@Jd zxnDkVOSo2oN|zoIWyClBuef{tYD$g8Gs~l5G~~j;s3T2>-q;9Ipuni zn0dv-&8&_ueq`rxMhF$PC^uP-Dn7~kLeC=E6?9u+-9G5o;yOas6egKZCO+}Lwp zB+^R>Nne0Cthe0QpKK~XpB|Jcy_A;-O~j9I|FT5j`SDS`Gs+mAxy|6n>_zgeorAq#Pz219lN1X9lreSSOohGzhv$VkvkT|rTs z_g6L?VORUr#MlkLtdyL4oG6%afwJ7c`%Z0EUtwrg7IQ=A99IA13~q9NG0mY08J zB@VxL-E=o4{IMhpcNaQjd=AT3r6=Z;z|aWG)^;=E1}*=N(Lg5EMk;?|_J6Ss!&*O^ zU_8Xw1y=1xQ0 zWoVC~qNwSm1q=YkkWp$udK&}eZ{0wSZW5l!@z_Nyt8Cn2cy;aRGzmOEd3#Sr`Ln)o?kg1EqVAqV|}6sc{2gcicQg zV4w>8E$$q6#)h>53c?!dzpe@aVU84-f@~pafDLi5Z)3LpmoK@ntUf4_#>%${ytM_LqFjBZ`l69wShJq;6oM`Z&WZm$pD*Sv_RFdJ${?tKI&3GQNrMt3RxWQNncLT8!oVEx|cWm^q7ZNb|o!q)yN{$cHjSz?0^GECs48f#R zmg4O1O;~-4WviU<>X{^P*X4B-55p&JUFrT86YRShkyie=^J5_d9LMq>47`8w19#xj z@Oa6o10ZF6HIC|YLEJ>4i$4AZqec(4VM1%jRSELCH0kpx4If+QFNC)~7&&Q3Ncm2+ z)^c{umdm#`l^-fb=5SBpL!Y!4=N*6++jJDfY8c;|^Uui&*F{qh%Iv!e?hMAzHsw9p*?+xakHo=jc+MT(2E-}(Ar zLwG{DoLtUdHzvwy>Z%dGR_Im8a|4X~K#0CXAc{CN%Pp=;; zZ?8=nTAtNR1ki0s<7!|@h(lotZ(m7SAxuJej1$kTowa<{57S2tbMogj!o$Q E0 + + + + CostaPy + + + + + + +
+ + + + + + diff --git a/pages/configuration/database.md b/pages/configuration/database.md new file mode 100644 index 0000000..32dbfe5 --- /dev/null +++ b/pages/configuration/database.md @@ -0,0 +1,29 @@ +# Database (config/database.py) + +This is the sample template for configure it + + db_default = { + 'host' : 'localhost', + 'user' : 'root', + 'password' : '', + 'database' : 'your_db', + 'autocommit' : True, + } + +You also can make more than 1 database configuration like this + + db_default = { + 'host' : 'localhost', + 'user' : 'root', + 'password' : '', + 'database' : 'your_db', + 'autocommit' : True, + } + + db_other = { + 'host' : 'localhost', + 'user' : 'root', + 'password' : '', + 'database' : 'other_db', + 'autocommit' : True, + } diff --git a/pages/configuration/directory.md b/pages/configuration/directory.md new file mode 100644 index 0000000..a405e03 --- /dev/null +++ b/pages/configuration/directory.md @@ -0,0 +1,34 @@ +# Directory (config/directory.py) + +`directory.py` is the place for storing your path. It is useful to calling the path more efficiently. there is 2 method that you can store your path. store it in variable for templating configuration, and store it as object for routing the url. + +This is for a static error pages. This variable will be use in `server` configuration. + + def erpadir(err): + return f'static/error/{err}.html' + +This is example that use for templating. This variable will be use in `template` configuration. + + user_page = "static/user/page" + user_template = "static/user/template" + + admin_page = "static/admin/page" + admin_template = "static/admin/template" + + email_page = "static/email/page" + email_template = "static/email/template" + +And this is example that use for routing the url + + dirconfig = { + '/' : + { + 'tools.sessions.on' : True , + 'tools.staticdir.root' : os.path.abspath(os.getcwd()) , + }, + '/your_dir' : + { + 'tools.staticdir.on' : True , + 'tools.staticdir.dir' : './static/your-dir' , + }, + } diff --git a/pages/configuration/globalvar.md b/pages/configuration/globalvar.md new file mode 100644 index 0000000..65769da --- /dev/null +++ b/pages/configuration/globalvar.md @@ -0,0 +1,11 @@ +# Global Variable (config/globalvar.py) + +`globalvar.py` is the place for storing your Global Variable. + +GV_base_url
+Is the variable for your base URL (without `/` in the end). + +GV_title
+Is the variable for your web title. + +You can put anything in here. like a variable or def. diff --git a/pages/configuration/server.md b/pages/configuration/server.md new file mode 100644 index 0000000..fad65c1 --- /dev/null +++ b/pages/configuration/server.md @@ -0,0 +1,27 @@ +# Server (config/server.py) + +`server` is the place to configure the server things. + +`tools.sessions.on`
+**Default:** `True`
+**Description:** Enable a sessions + +`engine.autoreload.on`
+**Default:** `False`
+**Description:** Auto Reload when source code change. Don't use it in production. + +`request.show_tracebacks`
+**Default:** `False`
+**Description:** Show traceback for debugging in development purposes. + +It have the configuration for the static error page for error code 403, 404, and 500 too. + + from config import directory + + update = { + ... + 'error_page.403' : directory.erpadir(403), + 'error_page.404' : directory.erpadir(404), + 'error_page.500' : directory.erpadir(500), + ... + } diff --git a/pages/configuration/template.md b/pages/configuration/template.md new file mode 100644 index 0000000..8ee2eef --- /dev/null +++ b/pages/configuration/template.md @@ -0,0 +1,34 @@ +# Templating (config/template.py) + +Templating is useful when you had more than 1 website template for difference use case. For an example, when you had user and admin in the use case actor, let say we can do the website for user have a navbar and footer, and the website for admin have a navbar and sidebar. + +Before you create a template, make sure your `directory` configuration is ready for storing templates and pages. For an example: + + user_page = "static/user/pages" + user_template = "static/user/template" + +To create the template, you need to insert this code in `def __init__(self)` + + self.html_user_pages = html.main.get_html(directory.user_page) + self.html_user_template = html.main.get_html(directory.user_template) + +if you had admin template or email template, you just need to add the code. for the example like this + + self.html_user_pages = html.main.get_html(directory.user_page) + self.html_user_template = html.main.get_html(directory.user_template) + + self.html_admin_pages = html.main.get_html(directory.admin_page) + self.html_admin_template = html.main.get_html(directory.admin_template) + + self.html_email_pages = html.main.get_html(directory.email_page) + self.html_email_template = html.main.get_html(directory.email_template) + +and then you need create function for each of your template in main class like this + + def user(self, page): + params_list = { + "template" : self.html_user_template ["user.html" ], + "topnav" : self.html_user_template ["user-topnav.html" ], + "container" : self.html_user_pages [page+".html" ] + } + return params_list diff --git a/pages/content/handler.md b/pages/content/handler.md new file mode 100644 index 0000000..333b5fc --- /dev/null +++ b/pages/content/handler.md @@ -0,0 +1,243 @@ +# Handling the modules + +## Import the modules + + import modules.api.jwt as api_jwt + +We can see the `modules.api.jwt` in the import, It mean `modules/api/jwt.py`. + +## Routing the handler + +The routing is starting in this class: + + @cherrypy.tools.accept(media="application/json") + class handler(pages.main): + + def __init__(self): + pages.main.__init__(self) + + def index(self, **kwargs): + ... + index.exposed = True + + def (self, **kwargs): + ... + .exposed = True + +The `index` on there is `yourdomain.com/`. + +let say the `` is `about` page. so the route for the `about` is `yourdomain.com/about`. + +How about if you want to create a route like this?: `yourdomain.com/about/profile` & `yourdomain.com/about/contact`. + +The syntax will like this: + + @cherrypy.tools.accept(media="application/json") + class handler(pages.main): + + def __init__(self): + pages.main.__init__(self) + + def index(self, **kwargs): + kwargs["params_page"] = pages.main().user("home") + return user_home.main().html(kwargs) + index.exposed = True + + class about(pages.main): + + def profile(self, **kwargs): + ... + profile.exposed = True + + def contact(self, **kwargs): + ... + contact.exposed = True + + about=about() + +## Add modules into handler + +The handler request have a 2 kind method: +- Request with JSON +- Request with POST + +### Request with JSON + +when you create the handler with JSON request, the pattern will look like this: + + def (self, **kwargs): + + + + if cherrypy.request.method == 'OPTIONS': + cherrypy_cors.preflight(allowed_methods=['GET', 'POST']) + if cherrypy.request.method == 'POST': + try: + cherrypy.serving.response.headers['Content-Type'] = 'application/json' + kwargs["body"] = cherrypy.request.body.read() + ... + + + + ... + except Exception as e: + + return + .exposed = True + +### Request with POST + +The handler with HTML request pattern look more simple than JSON request one. It look like this: + + def (self, **kwargs): + + + + + + return + .exposed = True + +You can make it simple the pattern to look like this: + + def (self, **kwargs): + + + + return + .exposed = True + +### Explanation + +The `` is where you name the page. + +`` is the optional. It looks like this: + + authentication.token_check(f"{globalvar.GV_base_url}/?message=forbidden") + +`` is optional. It will use to get the session token and bring it into modules. Here is the example: + + kwargs["session_token"] = cherrypy.session.get("token") + +`` is the optional one. It is for safely giving return. + +When you want to use a template, we can use `` on there. + +For an example we want to use `user` and `email` template, so this is a sample: + + kwargs["template_user" ] = pages.main().user("register") + kwargs["template_email" ] = pages.main().email("contact") + +The `template_user` and `template_email` now can be use in the module. + +``, ``, and `` is the custom things. + +You can create a JSON response like this: + + module = api_jwt.main().change(kwargs) + response = json.dumps(module, indent=2) + return response.encode() + +or HTML response like this: + + return page_register.main().html(kwargs) + +For the `return`, you can change it into `raise` for redirect. Here is the example: + + ... + response = api_auth.main().register(kwargs) + if response["status"] == "success": + raise cherrypy.HTTPRedirect(f"{globalvar.GV_base_url}/?message=success") + else: + raise cherrypy.HTTPRedirect(f"{globalvar.GV_base_url}/?message=failed") + ... + +### Session + +To keep session, we can use `cherrypy.session` like this: + + ... + response = api_auth.main().login(kwargs) + if response["status"] == "success": + token = response["data"]["token" ] + username = response["data"]["username" ] + try: + cherrypy.session["token" ] = str( token ) + cherrypy.session["username" ] = str( username ) + raise cherrypy.HTTPRedirect('/?message=success') + except Exception as e: + print(f"Error: {e}") + raise cherrypy.HTTPRedirect('/login?message=failed') + else: + raise cherrypy.HTTPRedirect('/login?message=failed') + ... + +And for the logout session, you can use this: + + ... + cherrypy.lib.sessions.expire() + raise cherrypy.HTTPRedirect(f"{globalvar.GV_base_url}/?message=logout") + ... + +## Sample + +### JSON method + + def change(self, **kwargs): + response = '{}' + if cherrypy.request.method == 'OPTIONS': + cherrypy_cors.preflight(allowed_methods=['GET', 'POST']) + if cherrypy.request.method == 'POST': + try: + cherrypy.serving.response.headers['Content-Type'] = 'application/json' + kwargs["body"] = cherrypy.request.body.read() + module = api_jwt.main().change(kwargs) + response = json.dumps(module, indent = 2) + except Exception as e: + response = '{}' + return response.encode() + change.exposed = True + +### POST method + +For giving JSON response: + + def change(self, **kwargs): + module = api_jwt.main().change(kwargs) + response = json.dumps(module, indent=2) + return response.encode() + change.exposed = True + +For giving HTML response with Template page: + + def register(self, **kwargs): + kwargs["params_page"] = pages.main().user("register") + return page_register.main().html(kwargs) + register.exposed = True + +as you can see in here `pages.main().user("register")`, you need to add template pages in the parameter if you want to use a template. + +### Combine method + +Here is another JSON response that use a Template page: + + def contact(self, **kwargs): + balikan = '{}' + if cherrypy.request.method == 'OPTIONS': + cherrypy_cors.preflight(allowed_methods=['GET', 'POST']) + if cherrypy.request.method == 'POST': + try: + cherrypy.serving.response.headers['Content-Type'] = 'application/json' + kwargs["body"] = cherrypy.request.body.read() + kwargs["email"] = pages.main().contact() + response = api_contact.main().contact(kwargs) + balikan = json.dumps(response, indent = 2) + except Exception as e: + print(f"DEBUG ERROR: {e}") + balikan = '{}' + return balikan.encode() + contact.exposed = True + +you can add the Template on the parameter like this: + + kwargs["email"] = pages.main().contact() diff --git a/pages/content/modules.md b/pages/content/modules.md new file mode 100644 index 0000000..ad88f6d --- /dev/null +++ b/pages/content/modules.md @@ -0,0 +1,60 @@ +# CostaPy Modules + +## 1. Create APIADDR variable for LogGorilla + + APIADDR = "/your/api/page/directory" + +## 2. Declare default response + + response = {} + +## 3. Connect database + + main_db = mariadb.connect(**database.main_db) + cursor = main_db.cursor(dictionary=True) + +## 4. Declare parameters + +For POST method + + jwt_token = params["jwt" ] + + id = params["id" ] + name = params["name" ] + email = params["email" ] + +For JSON method + + body = params["body"].decode() + form_param = json.loads(body) + + jwt_token = form_param["jwt" ] + + id = form_param["id" ] + name = form_param["name" ] + email = form_param["email" ] + +## 5. Get payload from JWT + + public_key = open('.ssh/id_rsa.pub', 'r').read() + key = serialization.load_ssh_public_key(public_key.encode()) + header_data = jwt.get_unverified_header(jwt_token) + payload = jwt.decode( + jwt = jwt_token, + key = key, + algorithms = [header_data['alg'], ] + ) + + session_id = payload["session_id"] + +## 6. Main progress (try/except) + +See `main-process.md` for the detail. + +## 7. Close database + + main_db.close() + +## 8. Return response + + return response diff --git a/pages/core/loggorilla.md b/pages/core/loggorilla.md new file mode 100644 index 0000000..82e932f --- /dev/null +++ b/pages/core/loggorilla.md @@ -0,0 +1,18 @@ +# LogGorilla + +`loggorilla` is the extension script for logging. + +There is 4 type for this logging availability: +- `prcss` for logging a process +- `fyinf` for logging a value +- `accss` for logging an access +- `error` for logging the error + +Here is the example + + APIADDR = "/your/api/page/directory" + + loggorilla.prcss(APIADDR, f"Checking authority" ) + loggorilla.fyinf(APIADDR, f"Username: {username}" ) + loggorilla.prcss(APIADDR, f"{username} try to logged in and failed" ) + loggorilla.error(APIADDR, f"Username and Password is Incorrect" ) diff --git a/pages/getting-starter.md b/pages/getting-starter.md new file mode 100644 index 0000000..285cd0c --- /dev/null +++ b/pages/getting-starter.md @@ -0,0 +1,48 @@ +# Getting Starter + +## Requirement + +You need a Python and this libraries to use CostaPy: +- cherrypy +- cherrypy-cors +- mako +- mysql-connector +- bcrypt +- pyjwt[crypto] + +## Download + +Download from repository + + git clone https://github.com/ditaAjiPratama/costapy + +## Installation + +You can install it with run this command + + sh install.sh + +Here is the completed command + + sudo apt-get install -y python3-pip + pip install --upgrade pip + pip install cherrypy + pip install cherrypy-cors + pip install mako + pip install mysql-connector + pip install bcrypt + pip install pyjwt[crypto] + +## Usage + +Use this command to start the web service + + python costa.py + +For an example like this + + python3 costa.py localhost 80 My_Service + +You can use nohup too and running it in the background like this + + nohup python3 costa.py localhost 80 My_Service & diff --git a/pages/main-process.md b/pages/main-process.md new file mode 100644 index 0000000..28c4dfa --- /dev/null +++ b/pages/main-process.md @@ -0,0 +1,167 @@ +# CostaPy Modules - Main Process + +## Giving the response + +Main response have 3 options: +- status +- desc +- data + +The response status only have 2 options: +- success +- failed + +Example for the success response: + + response["status" ] = "success" + response["desc" ] = "Product list collected" + response["data" ] = { + "product" : product_list, + "other" : "Some random text" + } + +Example for the failed response: + + response["status" ] = "failed" + response["desc" ] = "Something went wrong" + response["data" ] = { + "exception" : str(e) + } + +## Fetching data + +### fetchone + + cursor.execute(f"SELECT * FROM tablename WHERE fieldname = {id} ") + row = cursor.fetchone() + + token = row['token' ].decode() + id = row['id' ] + name = row['name' ] + email = row['email' ] + +### fetchall + + cursor.execute(f"SELECT * FROM product ") + product_list = cursor.fetchall() + +### Nested fetchall + +Variables: +- `l1` is mean `List` level `1` +- `c2` is mean `Count` level `2` +- `d3` is mean `Data` level `3` +- etc + +Sample: + + texture_list = [] + + cursor.execute(f"SELECT * FROM tableone WHERE fieldname = '{key}' ") + l1 = cursor.fetchall() + c1 = 0 + for d1 in l1: + + texture_list.append({ + "id" :d1["id" ], + "name" :d1["name" ], + "desc" :d1["desc" ] + }) + + cursor.execute(f"SELECT * FROM tabletwo WHERE keyfield = '{d1['id']}' ") + l2 = cursor.fetchone() + texture_list[c1]["owner"] = l2 + + cursor.execute(f"SELECT * FROM tablethree WHERE keyfield = '{d1['id']}' ") + l2 = cursor.fetchall() + texture_list[c1]["file"] = [] + c2 = 0 + for d2 in l2: + texture_list[c1]["file"].append({ + "id" :d2["id" ], + "filedir" :d2["filedir" ], + "filetype" :d2["filetype" ] + }) + + c2 += 1 + + c1 += 1 + +## Get the last row ID from insert query + + cursor.execute(f"INSERT INTO `product_files` VALUES (DEFAULT, '{webdir}', '{filename}' ) ") + product_files_lastrowid = cursor.lastrowid + +## Begin, Rollback, and Commit + +Begin, rollback, and commit can be useful if you use more than 1 process that cannot be separate. For example: more than 1 table insertion query, inserting query while upload success, etc. + +The pattern: + + cursor.execute("BEGIN;") + try: + # Process and response + except Exception as e: + cursor.execute("ROLLBACK;") + # Process and response when failed + cursor.execute("COMMIT;") + +Sample: + + cursor.execute("BEGIN;") + + try: + + cursor.execute(f"INSERT INTO `files` VALUES (DEFAULT, '{webdir}', '{filename}' ) ") + files_lastrowid = cursor.lastrowid + + cursor.execute(f"INSERT INTO `thumbnail` VALUES (DEFAULT, '{image}', '{files_lastrowid}' ) ") + + response["status" ] = "success" + response["desc" ] = "insert success" + + loggorilla.prcss(APIADDR, f"insert success") + + except Exception as e: + + cursor.execute("ROLLBACK;") + + response["status" ] = "failed" + response["desc" ] = "There is error when processing try. See the exception for the clue." + response["data" ] = { + "exception" : str(e) + } + + loggorilla.error(APIADDR, f"{str(e)}") + + cursor.execute("COMMIT;") + +## File management + +### Uploading + + mediafile = params["mediafile"] + name = "helloworld" + ext = pathlib.Path(mediafile.filename).suffix + dir = f"/srv/media/material/texture" + + uploading.main(mediafile, name+ext, dir) + +It will be overwrite if the file already on there. + +### Removing + + import pathlib + import glob + import os + + dir = pathlib.Path(f"/srv/media/material/texture") + name = "helloworld.png" + + for row in glob.iglob(os.path.join(dir, name)): + os.remove(row) + +It can combining with `*`, for example: + + dir = pathlib.Path(f"/srv/media/product/*/file") + name = "*.zip" diff --git a/pages/structure.md b/pages/structure.md new file mode 100644 index 0000000..808cdc4 --- /dev/null +++ b/pages/structure.md @@ -0,0 +1,17 @@ +# Known the structure + +![CostaPy structure](../images/structure.png "CostaPy have a 4 type of file") + +It have a 4 type of file: +- Main +- Core +- Configuration +- Content + +`Main` is the file that you will run with the Python. + +`Core` is the place to put the extension script. + +`Configuration` is for configuration. + +and `Content` is the place for you to create a content.