From f6b1977057bab006ee811b18c20543b6fe7f3996 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Djk=C3=A1=C5=A5o?= Date: Mon, 11 Mar 2024 14:11:47 +0100 Subject: [PATCH] before adding GH CI --- .github/rust.yml | 35 ---- Cargo.lock | 46 +++++ README.md | 2 +- app-template/schema/note.txt | 1 - sdk/src/headers.rs | 24 --- sdk/src/manifest.rs | 1 + simple-payment-gateway/Cargo.toml | 33 ++++ simple-payment-gateway/Dockerfile | 19 ++ simple-payment-gateway/README.md | 4 + simple-payment-gateway/build.rs | 7 + simple-payment-gateway/public/logo.png | Bin 0 -> 24834 bytes .../schema/schema.graphql | 0 simple-payment-gateway/src/app.rs | 46 +++++ simple-payment-gateway/src/main.rs | 95 ++++++++- .../src/queries/event_transactions.rs | 180 ++++++++++++++++++ simple-payment-gateway/src/queries/mod.rs | 3 + .../queries/mutation_transaction_update.rs | 17 ++ simple-payment-gateway/src/routes/manifest.rs | 8 + simple-payment-gateway/src/routes/mod.rs | 40 ++++ simple-payment-gateway/src/routes/register.rs | 40 ++++ simple-payment-gateway/src/routes/webhooks.rs | 58 ++++++ sitemap-generator/Cargo.toml | 2 +- sitemap-generator/public/logo.png | Bin 24834 -> 11117 bytes 23 files changed, 597 insertions(+), 64 deletions(-) delete mode 100644 .github/rust.yml delete mode 100644 app-template/schema/note.txt create mode 100644 simple-payment-gateway/Dockerfile create mode 100644 simple-payment-gateway/README.md create mode 100644 simple-payment-gateway/build.rs create mode 100644 simple-payment-gateway/public/logo.png rename {sitemap-generator => simple-payment-gateway}/schema/schema.graphql (100%) create mode 100644 simple-payment-gateway/src/app.rs create mode 100644 simple-payment-gateway/src/queries/event_transactions.rs create mode 100644 simple-payment-gateway/src/queries/mod.rs create mode 100644 simple-payment-gateway/src/queries/mutation_transaction_update.rs create mode 100644 simple-payment-gateway/src/routes/manifest.rs create mode 100644 simple-payment-gateway/src/routes/mod.rs create mode 100644 simple-payment-gateway/src/routes/register.rs create mode 100644 simple-payment-gateway/src/routes/webhooks.rs diff --git a/.github/rust.yml b/.github/rust.yml deleted file mode 100644 index b76e204..0000000 --- a/.github/rust.yml +++ /dev/null @@ -1,35 +0,0 @@ -name: Rust - -on: - push: - branches: [master] - pull_request: - branches: [master] - -jobs: - build: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - name: Cache cargo registry - uses: actions/cache@v1 - with: - path: ~/.cargo/registry - key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }} - - name: Cache cargo index - uses: actions/cache@v1 - with: - path: ~/.cargo/git - key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }} - - name: Cache cargo build - uses: actions/cache@v1 - with: - path: target - key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('**/Cargo.lock') }} - - name: Run Linter - run: cargo fmt -- --check && cargo clippy --all-targets --all-features -- -D warnings - - name: Build - run: cargo build --verbose - - name: Run tests - run: cargo test --verbose diff --git a/Cargo.lock b/Cargo.lock index 193d42e..b8de512 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -535,6 +535,26 @@ version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbdcdcb6d86f71c5e97409ad45898af11cbc995b4ee8112d59095a28d376c935" +[[package]] +name = "const_format" +version = "0.2.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a214c7af3d04997541b18d432afaff4c455e79e2029079647e72fc2bd27673" +dependencies = [ + "const_format_proc_macros", +] + +[[package]] +name = "const_format_proc_macros" +version = "0.2.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7f6ff08fd20f4f299298a28e2dfa8a8ba1036e6cd2460ac1de7b425d76f2500" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + [[package]] name = "cookie" version = "0.14.4" @@ -2674,6 +2694,26 @@ dependencies = [ [[package]] name = "simple-payment-gateway" version = "0.1.0" +dependencies = [ + "anyhow", + "axum", + "const_format", + "cynic", + "cynic-codegen", + "dotenvy", + "envy", + "redis", + "saleor-app-sdk", + "serde", + "serde_json", + "surf", + "tokio", + "tower", + "tower-http", + "tracing", + "tracing-serde", + "tracing-subscriber", +] [[package]] name = "simple_asn1" @@ -3406,6 +3446,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + [[package]] name = "universal-hash" version = "0.4.0" diff --git a/README.md b/README.md index 947f972..f63d28d 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ to use in your project inside this repo, create a new workspace member and add ` ## Creating a new Saleor App from template -If using the `saleor-app-template`, create a new workspace member `cargo new `, then `cp saleor-app-template/* `. +If using the `saleor-app-template`, create a new workspace member `cargo new `,`rm -rf /*` then `cp -r app-template/* /`. ## Adding new dependencies diff --git a/app-template/schema/note.txt b/app-template/schema/note.txt deleted file mode 100644 index 60404ad..0000000 --- a/app-template/schema/note.txt +++ /dev/null @@ -1 +0,0 @@ -To update the schema, you can download it from https://raw.githubusercontent.com/saleor/saleor/main/saleor/graphql/schema.graphql diff --git a/sdk/src/headers.rs b/sdk/src/headers.rs index 4079d3f..3c10425 100644 --- a/sdk/src/headers.rs +++ b/sdk/src/headers.rs @@ -25,27 +25,3 @@ pub struct SaleorHeaders<'a> { #[serde(rename = "content-length")] content_length: u16, } - -/* TODO! -impl SaleorHeaders { - pub fn verify(&self, payload: &str) -> anyhow::Result<()> { - /* - if let Some(saleor_signature) = self.signature { - let split: Vec = saleor_signature.split(".").collect(); - let header = split.get(0); - let signature = split.get(2); - if let Some(header) = header { - if let Some(signature) = signature { - let jws = jose_jws::Signature { - signature: signature.into(), - header, - protected: None, - }; - } - } - } - */ - todo!() - } -} -*/ diff --git a/sdk/src/manifest.rs b/sdk/src/manifest.rs index 80a97b2..4679875 100644 --- a/sdk/src/manifest.rs +++ b/sdk/src/manifest.rs @@ -18,6 +18,7 @@ pub enum AppPermission { ManageGiftCard, ManageMenus, ManageOrders, + ManageOrdersImport, ManagePages, ManagePageTypesAndAttributes, HandlePayments, diff --git a/simple-payment-gateway/Cargo.toml b/simple-payment-gateway/Cargo.toml index a06934e..a980817 100644 --- a/simple-payment-gateway/Cargo.toml +++ b/simple-payment-gateway/Cargo.toml @@ -2,5 +2,38 @@ name = "simple-payment-gateway" version = "0.1.0" edition = "2021" +authors = ["Djkáťo "] +description = "Payment gateway that adds payment methods that don't need actual verification: Cash on delivery, Cash on warehouse pickup, bank tranfer." +homepage = "https://github.com/djkato/saleor-app-rs-template" +repository = "https://github.com/djkato/saleor-app-rs-template" +documentation = "https://github.com/djkato/saleor-app-rs-template" +keywords = ["saleor", "sdk", "plugin", "template"] +categories = ["api-bindings", "web-programming::http-server"] +license = "PolyForm-Noncommercial-1.0.0" [dependencies] +anyhow.workspace = true +serde.workspace = true +serde_json.workspace = true +tokio = { workspace = true, features = ["full"] } +redis = { workspace = true, features = [ + "aio", + "tokio-comp", + "connection-manager", +] } +envy.workspace = true +tracing.workspace = true +tracing-serde.workspace = true +tracing-subscriber.workspace = true +dotenvy.workspace = true +axum.workspace = true +saleor-app-sdk.workspace = true +tower = { workspace = true, features = ["util"] } +tower-http = { workspace = true, features = ["fs", "trace"] } +surf.workspace = true +cynic = { workspace = true, features = ["http-surf"] } +cynic-codegen.workspace = true +const_format = "0.2.32" + +[build-dependencies] +cynic-codegen.workspace = true diff --git a/simple-payment-gateway/Dockerfile b/simple-payment-gateway/Dockerfile new file mode 100644 index 0000000..422a68f --- /dev/null +++ b/simple-payment-gateway/Dockerfile @@ -0,0 +1,19 @@ +FROM lukemathwalker/cargo-chef:latest as chef +WORKDIR /app + +FROM chef AS planner +COPY ./Cargo.toml ./Cargo.lock ./ +COPY ./src ./src +RUN cargo chef prepare + +FROM chef AS builder +COPY --from=planner /app/recipe.json . +RUN cargo chef cook --release +COPY . . +RUN cargo build --release +RUN mv ./target/release/saleor-app-template ./app + +FROM debian:stable-slim AS runtime +WORKDIR /app +COPY --from=builder /app/app /usr/local/bin/ +ENTRYPOINT ["/usr/local/bin/app"] diff --git a/simple-payment-gateway/README.md b/simple-payment-gateway/README.md new file mode 100644 index 0000000..02a223d --- /dev/null +++ b/simple-payment-gateway/README.md @@ -0,0 +1,4 @@ +# Unofficial Saleor App Template + +To update the saleor schema, you can download it from [here](https://raw.githubusercontent.com/saleor/saleor/main/saleor/graphql/schema.graphql) and put into schema/schema.graphql +To generate typings for events and gql queries, use: https://generator.cynic-rs.dev/ diff --git a/simple-payment-gateway/build.rs b/simple-payment-gateway/build.rs new file mode 100644 index 0000000..6b8ffae --- /dev/null +++ b/simple-payment-gateway/build.rs @@ -0,0 +1,7 @@ +fn main() { + cynic_codegen::register_schema("saleor") + .from_sdl_file("schema/schema.graphql") + .unwrap() + .as_default() + .unwrap(); +} diff --git a/simple-payment-gateway/public/logo.png b/simple-payment-gateway/public/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..f5911307aa2ecc3982f960c709099ebc57251487 GIT binary patch literal 24834 zcmeFZbx>SSvjDn_y97-g^IhY;B!6GdlxhAdM*(HlzhG|r@PN@kgupWi`0@f}a^RT*e9eOXMymlR-~QqJ`3M4(Zz2Bv zl#^FbX5{7KfpGB&0R17n{2~xu5ncgCUWf<;BJzY6pbt*_Cr7uKei)AM0{r=#NlPos zOG`7_+S?eLS;9de=Om9W;?G(oXxnw)WH3>@5`AX(OPQ{Xi)~ROSJ@vgUCnf)dHFZD z>F>|qtAy|hFtK5jxkOxjH6m4bzVq0@rGAlLL!-_bzYj2t`Yn0Z)t*+lVdEV04&@b4 zr>$4SKW}S)`CgTvPD+B>kGjLVhr>`YB4SObVP|1!Q#;DU5qgSTdxM}vcW1KucsEm_ zPExGYj3(*ygLEEiZX2?V?2-28voD(=9X3h$T=-&qMwxF8+wL0_5Bz*iQ~EWVREbPZ zMd640{iWhAm(WtRf|6D7b9_Hx4|x10zuf00rWi&^e_yuetgTO@)RM{ZA5Vrm?FDH! z%5Vi}F+=P*($EM?HnDH~=4RfVXUlUxrpB((r9~Q&<7}aQN?3Ad5LH~l*n^>~yLRhl zqnDp90p#)oN2h^$*XxUY;{NwTRVH4`E#~VuO8cf%*y4eJjkO%1Z zu<8MpkPyaCyr}xwg&Fbf`YX50i8j zIa}G@DhwnBbGFqtw17J>8o*7=ti_pjYa5vu&5XpE)SoCo6l|s8re@Dv?cr}+6;%vf zEewT?m|##mF=r6~ffd|ApV8UM(i$P+EY9=?uLw}St>$K8`~%`(AgrU8et%I43HRCN#eFGau2XQ7Qpq=sW{8`y5 zDEtGxHR3NR0P^5=*0<&6;ev2mS#kes4}^p4djQE_4E>iq5GpRVaPHS|gpH%UAzb!7 z+}h#6zoIZQ{6~LVM|;aZ!!a`ChFij|08j)#mFM3`-p;sx^tcs)iJ6t{pI(5l{|(Z? z%=n+e`ZwNgTmB5^Ul{?q{{!#8LH~X1e}Dlf1qBfq8$-ui_T**6nQrNe7}*$_8HxNU z3LEhV8NhjsIfZ$JcsTim^&yNK;` zH-U58TATc7xD~jFq_Vs?6E7F!pEb&s`VPiG2XUsCX4a0*|7=h(vx2{I(7%-?k03-y zSdbqgz{fAlBh179Park8Jp!=ATbzG<=bx5awTJ+N0R*dmYo`E$KlQ*^M5OKE`VKbs zDmFHj;!OYWy8mca0H%|XzJtDuz5^UU`VT*>@^?SX4&edB`cHTpBQs-{{|@?g@-T}1 zP36zb5CDCbKUII5QE%XOe|!7etEJhWsl>?mXHtmh8~$ww2>thPqd#;3Tz_jaG}X5@ zfdk&-FLV8Sz1e?Z3i>?4`uavf`kYS;goQZyjo^ZuLIzKSIC%vH4FvfN_=R}kPyQV{ z!p7LaN#7nWX#&U*kQHE_e^w|))<24R{O{0Crtn)W00QHL@N)7B{1F%+D!#u2#{JiT zxo>^O-$yLQ{r{4Q*dKs@aWX)^ztsWG3-E>9|8RzX$?Vqc{2%`PrHlVV3IOW=5cwa; z_dnwLkGTFv68Ikh|Hr!iBd-6E1pY_B|FN$BHF4qnb2|mM2BIJ*V6)V;lsE`%w6F|b z$jN|iZa-=DKO%t|T-#@F5g-sg+3gn$N=TvxDzP2p6=bp3(C(3e(YHr(fP?`fNM1%# z#d&gT#wF8d*l-T{?;9w)l>!b%Mh$iSf?Zmho0m?_g^Ud1?Qm(<>Ow|i*Gbw>d zQfZ+9nN&(r(oU(Eh))V_5R@^duLB|IMt8kq@P*Sc6B83?t2H&FjqkB|t3(1esDsSk zHbqB5bY?v?IQStM)}r%}jR+Z^73h`=QZamj;hBcu;AcPa(9G_DXvJM(gaWe{sQ~%*)GdLf*k zu!1P?qpVv5@>Gf5C)rkn&s0IS4jH)DJ+E~e0YWvCa_=Wm3VLszD}7$%0?8?*V8&z} z+`>D-gAKlYV@Dr)0urj>iK2$SJ_o2yt1x6bXnA*Ng8&N5Df(;(QqnI3EvfpRkuH3_ zXx?mIq@<*CIACk+TU@H8w=@DzlIB!SH_R*1yuYz4cr&x9-}WiY7EQ!srU4c79qRhN z^_P&5k>RcIs46y36DzAS=l9nF1i5r65)`0(w`Z@&}m_h2oni@980 zazHZQOAtZ1BWRK4;DI#QEl|(aKmhou&aYckwZ=$>3?O*)t$fI)`& zeqsApqq~Sc<_WYkA1^>n(1$4`eWEt^mTg=NxT9kL?GtzYEDiG%+?jQg1}V=00|^eV zK+~$*$Aa16Hz`L)02NB+CWIjMG1S=1ZL^KJeR+Y>FxtG5QuL?WR<|Z$P@Aq2CV+m? z@6PlV$}izYRz{}8-&v}_5&ewC`VPr0Gn7t-pek)<04f3#V}QDV3v+8NMEY;4%)HO@)N>f)oL)pkKXe45p>8DH?-) zy;(ueVyZV8_MSwU=ai6CzZLKxLd=Z{fdLv0uW{G1YdyaZ4BdmIgSS*(1*3U?jO zn0nHr^zgw4C^lbCGaE%fmC`0C*d*F3$S>zomNy}d#CbXGT#)T6h*3v%eIvOs*htH& zQYtP)k{r~g8KQr2>4@Zx*w;jJE#&uekR=B_9GN5-^oD)uVbV3@la`YKOT5kyCYe=Q zVT_26GB+PxBLSEpx2SjxXNCCF-bi)K4#3Ly%Ai)If%Ou}tLnqZ$&X4Vq0C+%$Yi1G1xJ&j@sbCpUWHFJj=sQ=Q z!$i>3PJ?>87koq1+cN4TFz30rtt%+V4JZ$Phxq`5>111ON6^iImZsLb<=W`vyuG}S z3Db5hS+!b4k@X5-iKJ|Ni6n}{-^1yLDAn`O@BBC)ZA{+v*WHf|Q31%(ozu7=#To^b zSGXe*NQ!d8o%&QAK6kwV1VDF{&qN|0-P@Q;0F0hDy_*f~i%K=X+5b1=`|qH>5U?xD zGD5SdN$|*gLcd;J)y}=`VYC*~xH6`kV|LKrtA4Kq6`!h* z`ooLF`HU;;CSdb9Ed8}Y`mp37k~vxL=dNp^Y?}o?mIQBQO1M;`-;V0FVskn}o#%i` zmsvhF#6f^Rc}Hg@Rbyyh3>bvbV{wLK>y=apM%l6b z%FWU9UDa#cykQa4qbXk|77l@P0qaQ2}>(Bpr+eMhc+cXjQ*qAOu(;(WQv# zi#Gp~@Yu^Lp!RpAWytYwq%_c@O>X5maxS*ep(}CpQ1JPjMg0nq2UG?y7I&5O#68hN zZc->UuTO@xLihT+eo0i>|>s&ssSbm?16ef-Qg(w4&+_lg7*lRn!@b~Gve#_y% z>gm9JyGo67P7bnNx^9rc!Mo{6A%dK2H zrB*@R^!wK?$k?N6y5lkNI+)wSr6-xn@F|eY14XR7)ZLjOf#iP2rJQ|_@#d`?JBC*7 zCEzS>8#APvE+evYwfc91c$oEH-P1vm&cg^_rh1rf#hLYe>(T{e{jfU`aZKbk^dqI7 zOQfZY@$v`wGLmS_g*5Jn!d>O}p&b}IrU}Wsuw*i_#m%Hc2KefiyEuF2waQ!CS!lzT z_dmu2Cc1$Ndu_0MZA-1E7iN zRuTWp#s7Vb|HA2ihkTKcs?H&Wy+bAD7Ba$23nR?t*_BJ!OO>}FKrHkuv*fCNICaT} z;v3+8f{nNiy@J+)qZ+Tn(`{wiS#Kp0-z_^|+4b0M`~0eCjXgU358QKvH$2=~=lrJ9 zXXm;-LxRF5_!(aGI^OZ5RQgvEGIX~apOvMB1Ipc-g7xl&m-40+cMhTzFRB4ukBI{@ z?rj*RzLG`Y0SkCX$Ovg3Gxc*z(-w>q22+afUd`3woaKUtv06-|7w(NQgob$=ZqCQj z7s=avxux!{3sG^!c0N6 z5p!^uf1`TShx66q2ThiG??n$!4YXifx8kr@?dBQCc|eYX34cs+|32s5ctEb^VOfo+{IY(-;5HZ z``Y2X0oLegdJCJ;kK>F{Y)(xUOX(X!JE?^-*rxWWZvDL}=r#4r6UQe>xb-Q1178Jo zY$O;}a)|t(etXE{hFgUMzvz{d9zt2?JtOOmq)Kt^CRjztovPoLQ$#ND49?pMF%)VL z-fLW%t!7BT=w$%iJZy+TAbp^PQnNL#Ub0{bnKpR|*0QZAhZQe{SrTd?JUjuV3l}Fs zzpjP8j-^Gt9ukex;d34s%yf>XB#4c{QwR&fVFLkU+ySp~J7M+AA#0{v`7vlxiN; z49UT!owo3@%GlT&Rzc8>k(=T7YGH%~lv*b#o(&z^>V+R3+AsUd=~VA)6rVu!{MW~n zdKSFTVNFgm`s-RRgwLyqgbT>6u#+1Dkm~WNeRBzhe=Oro^cu*fAi(EAn%#@4CB>|< z;g2CVdQXPw7H3^{Hn5w|lfrH02nOBee`8S}V5*IO4#7EJf`i@Jl1 zneVOa278;ON@(RB9_EfhmT*7^BSFZHxNG-B)yC#w#npcHpRF08ef`<)LG|TK#oo$m z;xtd)v8_a4 ztn#qzO1%rGdy3x7&9CjfaJbpR=S$^E-28*sm7r?S=3`8H<_P_0DL=B$0D;dv=0Iy?qoK>Tt8%)`O66HZVOhO-tio z#mmId^q>G5bnAfDvn&Ox@@r82#JL2mnd7w>J3A(+KN_v-H*1eSe7i63lU~SD;V727 zS&Q(u3=tzqk>ks#ns?+wrjos*!(GgrxB$QXgK9;b|JD1PX zkRS5>=Pq(YEjxGRbz76JSHos1 z5_F|H!kP#KV^2Scg6BRxS;CC4UuU{>g3!iCA04V)ni4-QPl&q_p!F>8Z2Y>3ND={m z)8Q?py3?J4{(N*8`Db9s@YO!ZE75s-nnVaccU6De1GNaK>m>dEc%nht6NibZu$YXCV{>>QsHsNmTwc}n}P(cUY zayPNlaCzlJj`)r1((LPI*tvoD-vQpw9Q~tj8fl5Z2L9S z(Ts&F1;@k1G&i2EJV4ChaF>{q-oMFmC)sPxT1cSdYV(6Acxfj9SD)lF`qzHv_Z>F* z-lt99G0?Y4f+Y3mgT;&(;^Ly_GULqLcDQKVH!$4K!>Tux*7l@=KRW2lF_y(aBmY=u znu?Vh+;jT%x(Jh=xykrG-jQeLF_Q>xzIIH>X33|v)0nJelIWH(`gly#jY3W8O+x;r z{xrh)gMsGT6!C3Z5T29OXva@FmycADc+hpuIFaC4pgy#tygLe&0Z#+kzlJj59}9rr=3 zJpG(ZdHww_I)Yg4mN3qu`8QQnYB!GnchdF)(_bpJP~isA@@V}aI!*C`^Zctb$44A4 z{f=*(ih27w7MJvQ1HXHnuD3&`e>0OX*d)~7OoB?EW^5;4_Li8l%IE~viZ4@V)I`YsmP?+UIUj}zq%46YF@s)}4OYy!-BiTW>9;v77 zulFYFHMcrm0KpKFS9`(d+SZK@71J&tDLHU_cbvahEA=)OAvU}4_V{%9efl*C{hD>f z=+%{A917QedhxRE4U0;{`of+_DUsx2NWCp?)-dtTF1l*VOsd@%<@6Ti8#|4Ikgm6Y z?{R-B!5YPCSWWYZxBGOJQ2yfBMLG?B8#BE1;NsDoD|4P(;j~?#C4X5aD0%!&`CGwe zhCf+IxL_$R9=W~eRz*UQOncH7GiD$N`KU{>TxMBS`kq-#ouMIhepcH(EXg=e!a(jM z=i^Yf*$bx^$|uY0H)ld;Zuss(I4PXOq*O_%k)j$axs=tzW-zB_gPHt;fJZ zLi6oF72iAfEJ^4E2#26VY!`!r&d7740n91VoFAQpO`_9)@dDZWQIc)y$YEZTdVyG5 z*B6Lsc3&)0XjI~PUSfpxz4xG>`rXDe$fkQ6Iy~N2XY4C!x6rnzU}|#6Wa+ku`JAJwlV%M^_hV_Vc?xKxsgr z_|~@^pYd1ym^GcDr@d-W)w4ORd~Uv)iYn#V8e6k)6b;1+TbLt>HyZE>KBJcN7YAXu zo;ljPgm&>hs44ncibUcR_pd60e(mw&D9eaZncSU^j=(g0c-XyQP^0S*)%6rG1Kv8{ z-rn%qJWk+d;M>ryb=u3du@ zbkdsAs6S*seiiB=?leVSDB+Q8BrnND=@4Q!i1xVKW7@}yR~ zL__F4_k3XhILj~OS-c=1&fJ=fQMQEw>+HGNOlJ=%td8Mi$D1>EzVNQGT%(2~_*Xe4 zh^iL-sjN=np2f6H3>FuHot({JPUF`cLrAOujht#e;WzqGW2xJjriz)+92wI!_ecq% zl4tJu?!7mA>%suED&zAz(6Tw`ml7Xr#~mVKt+HgwZ4Q)F?ST{D)e(s7wVynt_uB0cDOSF4BA{F?5S6VL z6WE*5dMvDz>aV2hN4KQf@OC8DYN17NprX`6*V}^PIsjoz(7Cf+a9C@N-97a&o=;U){C#BFP!{v5!uBX2(Yf((5NW z+eO$KG;|5%CJxdm()t!C#W#Q3-_LA;RZ(5f5GBU6q*R|U2jU9^U1BDMr;6v>s{*dI zFjyR;BV1zrK#-nW?GBpMwr<~-sfF&Dat05%Pd?Jjd~}Js{LVf974k&YSd_ ztFst5TR;DINpGgNy<9I=wvpJXG&z>btE{Sh=i>ngTifhEW?g4IbtkwqZ$dZ+{oBHa=V`XR_YA8Zv#axVV4uyx(QVMD59!c zxB4hu?lL`?e~uxe?K}P~xEhTomT^@~h9ST(DBb<*>KIe|{j8)D{n(>d8Kh^Ll%NZz z+P86RnvytO_!IV$dWyed(Ct_KXk8fPtZ}-)REn*QFM2P?fsI8(hKd^&z2|R^9m6Tt zkp)P1$Zy>0MAPR{sX4oB|nLOpsMZZ8LexBd8@wDX9^(D+* zNL25Yk^wL+9wvCt<`T`4Q1ray(8H#+-ximnU@*y9aClv?iqZ${|L+AYtP)bZw1%F zcwYZ^y6@|65>95POKdFhZd&4Cp|Cw z=l<9m)N;_tctgkFIML!>)LFwlZpjZoj1*26_~e@^^i~9xOF}|r{pZeucM`Ce8wy?I zN`I*uuv7UykXrc$sH>Hhv}n3LCi_(E`vK{kYM!`C!_2vJ8>YtImo7Q5#6x_IGp;1j zCHKqG5L*lG=US zrR4CriMG6$M~nCyjRfhk$>9vs_%w3a?ziU-CvN*%3O}S9?<>D-Psn`#G04kFh=E%f zXX9A?JlMu`coKSSv#u4bfM?1Lva28q4)bQ@=6pmp+kZ(r2YQ7pRTRD!B@(!??&8Z| z&L)I!N>1d=@0xfiX1xP4w#%g)s7+gm5B39F9^<9BMAvwBEGlZihzvSL;~%-(uxUN8 zh}O@}93U4e!f4n5%e}TM&>Bf!PF99ayH8L&Cc>1uZ{t~7o62riTm-L%_Ak<$Y}x8u z_4gAel!GVZtc$Xq2_f(l=>xuDFElBRS7Y^E!ea(vHf52(nPfYo;v`x=Q4Vb#^95~B z7FMp9vqF5qzy%MhF0Pa{k0#31yj#S%u%b)XUD}l08Hvr;G(tv%t<`|T64snKvNx27 zhi*#XT~&fHM94TxN{$vz>u>9XK7KR*iF={ISh-50i|uAyBTiTi&fT!o=8meN>YUxU z2zMQ&`ukk~$sIjbP4MKYw^NLCb{HP$LgvGWtlrYIXunFwcepMQE{dhZX-kHWbC1X_ z5W-s*&*5AR@SiB#9m|crSKo!iU_U>mcebDfsv5>YUl};sKN#M|2T!O{&PF`IK)dnY z=BiOI32H{9O7%^rq>6G;a_M+Kz}8=3I$MkFBqR zmT0_&o@mF%uU2o2!PYXYi-)<(G06N=ngb@Q3i-jRU)t9EF0q}RvHVKukA*$xc-b9+ zu=;bK97{pdBSw`hyZ}7u0Hiy@x z5^!fFy^GFROhF0=+Q+qR=W996`z1?34_y`{V7)kdIfKmll`ChZ-D!&7WOQx6?7e&d z?7>!I?!arLLQ|B5r>7|{YK4zF7a&8Oo;MVpv_xN~hV~PMsqYa6)=o*sCpIt0jg5Zv zLdM{LO-hWnBJ212{1%(;VqBDpyZt5Yz?!oY-#~j!onyZjGs6{TqocngMwUB8z0sI$ ze<(9^Ka;mELvN#BHH}0JO6}Um?}RkV7-ggu!DLgDKNm(rPq$;IuAs&f7(;^6cCk;2 zpRk0*X9oZ(?2J2#mhu)%nzPxx!5-(ock&L)@f=$s_6MV`M|7$vUb%Nle`42w#as;7 z*2Zm3@PSpqd4rOEQzA*koG6lA6ReYr5ujc{u1+02Z)&We;us=2O`yx6#X)CP18mfhfYfV=Uh9y zt(y{u#HwNPk4H#z#!6uCFIl(7jh|jlS-!Vf@_;kr z9;W$5LcO6MZ1_r-Y7_MeKAzMd(VG%=D6zQtj%C}1OVdC}rbPPgP%bQfGN%V>SQRvC&huiJ;n@IL0|2VfD8oiNTWwa@ClPwXp3RBVyj zR(21Pf>Rr@VmbUU50(QOZsez$P2*JyHk5OHu3mVZjMX%N4`xZh zLp3dBHCaNfWtj@0r2jLHxIKysCNyPH8~yl&GUo;G9}t% zs~r_fr<~}VW^{&JOEQaNF$`p>{|I`K&>%-hc{j&g@U`;&LGR*obiH;a)NU=(XIOg| zU8#2V@IH_QeDrR5)rsoC=!*dDo1T|WJU{BKOY`>c;@|{8rME?~Qbf^p>%Mxi09NOC zqh&=(J>67>*Tan~YTU9<_}0dxc@1ZXjkob(oz6t5l{eYNidkt!p+aKcs-hf0qh6L% zOZ--{Zg0zcfJdvDw3a|MsrkT{jKEoo z?_SuumAriW|$Ox*oYdg2>Q528l!W|$cw&f=Qcg^Hc_R_esT?`h7P&7 zYJ?^2t&vb4d>g&N&N_*kSm}M#_1npFJ zRHe1yWHiK|6QtO|w9P4nd&59Wx`*U7Jq<=|K5Zo`vdP)?#IoKbcB(9fopji12+Q0k z4x=$fd`Qy98-kXqID>rX}}UfGAK zfT24^dIep%IA^otd6XkmtvuzbxM|I zJUM@>Up&Hex;I{`bhgR7W{+B7~+!1df@2R^MZh=r;O+V7KmvJ~*b<$e81Yvk+? zsJ^Ysm7M7fMV&xOq~ta`Sx~Zggtgv`ha>CTOE(EQ zSFaJ>Y7kff$9bpcvE5lnVdxe#rTX(0m zt+5K(4VNAQ3Ou~P8q?CUP`mSzL)UZen&aa-CXL$E+z*RgRb| zO&EH|cg{*myE@nL#hd3Zw4uAuV+NLO1}gHS|I6PS{&pGb_L(K7KV7 zb+#_sd^GvVVt(lTyQ7YGgR&F52iHS%0(m}F8mP@MIv=H3TQ94#f#)Z+SKUly zndfO+%JXXDB3L>W7Vlj9lB;K<=ooB#n_J%QZHO0jeB%6|UCwVrQOnZudG(P{EuNX= z#Db4C#5o>0B+MsFh(_LtgC`Ia3-v?ex+tl%2@I!KTiNY8FYW8FU*X|I|F+SbU!UeF zTIyh<$CxBes5#b@)6aPF8P)isEVLX;k>PO5HN3itJ%5s#$Q_$%wCN1RScE~IkKG(F zKmAJRBG)7pJmnn39$PYM^1z(M{WrliJ#I3+llWCh7v0{f6U2->&jZ*H$^4d_t=apH zR32~twKwC{`YS9&Rpo8(s}}+HNI;I0&2_S?ihGj2VEca3)p+RClix4$)P&MeK$8BXb5cfrq`!c348 zf+ISl>RASC);6Zoon`NZJTl*#%=C9Q--VPt=7>kuus?*5yo2IM$0Teg0DIH6P2aBO z&^KEB`%qV$S+A`N`jf@Ug2MUl8Fw2jsrfI$ojm0}#ct*EW1oKCQrze6M%^RFNY;IR zRlb^!PpB!v5FYC1cvyO3!hAyFK6nqxppO+&^LjzR8G*-7TL5G!b;vc)QWAGuuP!1x z=KZcGYlg`-)kGK$5B>U-zF!wia#K%=n=^o_%oMu%s!c=Qx{g0K9riQc{jpQ}WS$Q> z^8ie=^Tdzb zv!j0sBX;|dl_W;tNK&HiJJpiR&`n0*RN?)`$JRN(1nFUmR<>P~*V~w}z_+`Y*O;g*BL>ZJFSD34L^sX)B9$R3o zjyL-mcpcU#8C{_~ zP{OAdmZV(E6J<_>Qf$i5mQT&qa9e|Q*6CJs-CVvsDX_H=zbK&y?J zJ0=lti^FN|rKn7zQi$$i9mE;m$ll&RNDdLC8l&tSA{$k&2 zT-ZYf_ndoDnqqNYNOm54ZcK~sNaduNH;I9Vh48!4Cqap@uLC%{Ukk1baZ}S@kne29 zwV@b@Q|mEv=Tv}1v{Bj39r`VJW8}xMK0V7~2@aq^>PN-k_QEUeP0>li^HE*@HH5;uBm&W3ytP{oo+9b+sDzS^4LoB#bk< zu&~k{E@f%mYDh^g9AU{k=uZjh;*e8hRVCV}^vAkNv-J?#^v0j^D7>VL)$1l0FCoT@ z4)hJ!o`H0#(rb>k#8K%jt(Dc!{_-d~a0})(QVZ%yY3&`;_5qGTe`D`?*a%f~9X2{? zL%j`{>k(XDUT;#4cY-{q=7(MTbr?o}HsCEU#>cxh<-Y^T{C>VL3bC(slHR2QiFFsvlQN4Ubl_&u|f8=d_gbr5YLN?%1->6>|fz~YAjqi zDWl_4U)vM-6iB~|*&_okD_m1@5%XBf72~% zsr!MM*ouzc>#HejF9K_f!Y<{c_xw3qQe@?k$!*Sw$q}JOv|GebI5K{SSn0HZT_f$! zs>(NOo4=LnzjkvjK>K(Xjb=2i*;2kU8QYa`2*n~OhipCp7l>9QXp_&_nSYtsL*~o% zT7UX5fweL@Ok+Me_Kbt=4yAiKuq1NPY8YIYk>$Q4%OO76(&~7h2vyQ#_j|x}J|@aE zAHD#|p(n7!cmsVh^vysk@2gt=qiMe9yXoHxr!J(o17j#Z3!U*`QtS-EwmI!$r_Tf< zzwtV3*)Z_e=$B8(NdQqwo&oVhA}%_5`YHS8!Uk-xUh%|3*5cipfrVE?QEUgnzIcq-AJ~{|&GJJ4E z|NLU@tk{DY2%gUb0%*8Yvn_Px^!jz>8w)NZOy`*{xsq^MJPi4O%LZS{92^8V;!V8r z3{;(66Ye%|g{{D}XD%Op3FMrC1@(4fLQpiQ>f90k(bRR1s4~@Kv#L``lp#A+^R?My z(cSzQ%;>M*^ifh{g}7|kGpUu8GPL99r8)=k2I?RZd5bwHKI;7xL#`^rg~#r!ONFOd zCW=`t?;$^JREcD2la{p4WHw70Kefd_@ z#pdPWlhWLG2G#b`H>5jG>cLg)jN~bIEm9Zn(cr)7kTAki&&t~mUT1n?c{*31`^F>f zFrsmcklK7Le=cx?S?A|XY zp-NCBgz2mNNF0%8Z(h{C&|=?2J@R=Na}Ax=GXco%5euD%w`=f z#@2|pYo%-Vq$rcNIFw7-xJ6@2aWX(^S@qfl{-3KTAenIXFA5jE^$dZ%nlk0IUb zn#27-YNH~rVF?$fy8ZmDVEY7mnE)IoQ+>OR?c-aR!3GX6zgOz&df(NJYge$0whWpb zFt+@i4|=gW&R{XpdFTkfQ(HyZK&x+)4cs_EpADLRas++e zI2%RhR9+So3Zv454n0XjAx>W11f4W5gP|xjQ$dL@i7r&j8=(*`%7&}d&3YvIi>6=Y z%D+mKJGo&^g2XP!&>9b76xDi+9}B4SUpzJUug49sy@Wh1YEz@jPU9 z`^J&7QXgFdax%+r;;fK4)Daqbt7G zRL5#7@~TS>oIY?$Q(6_qsr<8}H9Yc-?m7CfS6B4`c4@4{VpronPS!}1XNs=Z!Fm_N z?e+wruuXt(JV~{HOTh23bLzPa9K;Z!F3^M#l9gli2#-{8>!%V^{8q`A&H*ny+*DeQ zagK-C-l4UW{?Z7Uelj1eo3>D2zRhcYj(SG&L|LG8}T>EUkg-q7LORd%^j;0F&0 zo`+laK|Q|?J5z;%ybFeUq@+G*R}-|lHUU2XL0E~KwA62JhXXZBvw@D#ab{o(XX=`I zRqW@cZ`!?y-UA%;BsNNPmdu{d5CuJ+!bkS7eP#)3N*6D(pxZ({aXeh9-#$U6b%ZK^ zkK07bBn9p7I>R_dopDpV`l}7*YVmc5`Mij50xH!c<2AR(j-SSIJQ0-!TxaW5ES)@?77CDOfl!xKV}J2Cx%Baj_jzM|nN;0z!Ob5*=VNa`o>l~h!?wN}URknzZQEH&+ z#KA|=i#~)0wIdcb8_cGhlE3ki=Y!i@3)J_{@g*8GUorqUd=CRs=_w9Qdgp_k#23Zu z+kdvS4Hhj&mkE2Frwp761J`~w_esaz1pv9n)QgOno7`NkqUgq!@l|8CMK8|^?j*+p zp}O}BT9dc8q3&{G^gOF!*1JGLb{`*}UMlFdaiBnDgt_{|+OxG4O?UP#Uk>r7sAQb2 z56$A! z=GUM?4OEIdzTH9huQ!gK-!Sgs`@65COPH}L6X20Q$-jpw)vNRj{42TRc(kq3 zAJBT!t`3ze%?^;C!UPi6Ub={EV?P#fnE<@}cZGeZtaWYN_Q)A&_Q#`*9az ze@-eAwbhN~v}JKETFc!-1A0OuTxfvYC8lEt;>52PROZ;haG$nDldVha)M{H-~s4k23%|wTm_Cr>I$s<1Q>)9>pO445`WE)TT z3g-@3E=49v-%_)@Z~5?W=e_i%q=q)D`rYR-K?fAt#C50+{Htwd5s9g5wyv`jY(-a`RiWAE#i`SJnCu!zJa+A=oAnaUYhFmop-^hX9c@C&TemGu+v}RCY0OJp&2Xj z&=8i>*ih~i#|xroIvib>kI)l@H*C3*CRr$^`j0d=7m_V3`;xmOFk3@IUsgVS4P5#r zY;27uv)x!b&OBxBNEQt2RGFJ@manYQYQT7q>PPo@IyYUz&DpW;u5x@*oaq@t_@xKC z)lb)l@H0hL&F1qnr8f$>3Ml%%aA8`Ahggq`ShrU+20`vmh{v}OBPN>0fO%V1^}+M* zx&Apz?Lyzlpi?_Y3#d!6NR&TBd6@pzrrdA?rBZ5rMA1bjb++x>g{%$2Xy zUQ4Ulh=#U;O1;qQq2RdMC}@BvtFNm5BKWaJ{(TC)D1=JlvZBLJ=#c@~b7viMn5=xA zVhN68UN!*>;w7JTs4$XIkm%8`qBSDMa1aqJk9o{qJL|lX=N$sWzQa&T;Pv;NW`gh{S@^4fUTr+chT zXP_zA%j?I@{AO|fx%83IC)2)j#mo?<$iZY+U%VHm@)pH@NZJ)H5}wc=*;(_8J6b7} zKryFa-aayz^Qw_s7MJSGqseH*r}r!RiDa=i=)n0P&SPCkk+UhA;}697Wy;v!Tco;A?|x{8)?8{#rKS3S-tgMj?f+0DPEd> z4-SSVzSM3gt7Y<~K36qem%5~Q-mV;iMs_V}ZPE)e(VibAS*inQg)Yfy8xs;$Y}Yo~ zj?mpS40pKn?T%jHY0hUmOH9ABaLFNicaQ8DC@V{MAchhib$vBZJiY2jmCSRA+#M+H z?Ol$w2-uQuP|Gg#`A+Y5_Z+=_JP1I!w>IF)zF+n8t0d0QU_>=H|K z^nXk=wtb52=$@AeRubG1NQ{xFS*c&?=M>mVvftHgE6rJ;uKcOS<5Ai-aYy$wKKaGB zq|(xSe3Y%)uY^!%rO}gDS{&*z9I+I|YM~naFWCds5oW40+gKghwdcoCTg53kFY(}{ z=c~B#jn>5=hH1C3&7Hr~RG#QhG(oguKsU7VnhLkgi~*M^#xRAIsvEgN0M$&?!(?y1 zrx*^>;Zizzabr$9o@Eqx#c2K0dFXg8^adrjUdE@j!k{^FuBI*1y&7h=dqj)(JY#cI zJp$a?Oln~JZ_}Zc@L`Bd*JZ_Pn`v3(Rx*Xr?YNtfBX`%=bFrazB^SGUeM?hdGU6Wn z$U1{MqbyU%^KU%jrGDG?5dOt2{=Q|eBtqe7zfTlFSu{73ICdWJlMY8N-?`loB_A^Z z1kx2Kd>^!foQ!_BYAL9T07#a2)t*NmYopzc16rfe3Vx%k{@+cBmH*5o{F_gptJWdA zBoQ!cRMWhfrp6^s$ctk?Zb2{VNjr6)t&sCrboNFgF+gsl;Lx#hgOW#43VUpgGn2Tg zF@62YxadrdSI1Pi+U#2ys_Vh`vvdvjlqM}w6{F?oM5G!?Qm^-EX9gzNTS0V^DSEWH zecoI+t|B~9L&TtD3h#GstQyJcN@UbAw>a|9QPf!Y9X(Czti4GS@*sJP({6n_4seL$ zh0@2)6q{p}v3@ym?0mpevd?@hgd=QiYa*nzu1<0ObM7}gr*iRj#mSi_UzFI-?@z+8|PS{&qJdZOpYDs9J+%8z_= zq;;!OM;lI?WZ|Qjxtf}7#6AHY3>zCVDXM@kT^f?O#xuiA*VY73S4a`F$KPbl-+43Q069zn4#18*a1H^vMtu;qpUeD z9v9U!27x4Gc|D+ULeEt9N83|Q4z&&kF7+-yXiHC?J1`{9ye23{J6Tbsc_wGT1SO1@ z;aBzUC{6l5__44-ZsDt{t3M1RSa_<8-vxwsoVw&_h|R<;_K~euT!d+iVQYd~rvZTQ zMwiB-X(f;!hYAu4S!&%jJZ56drsCA_3(zfwXZ{sIZYhZmhMH{mRKO`{9Jq3{r}TF1 zL2R7r#&st74aa@enqal@)6zqZLrL&DziL%Nb7|M`l2w#w-VjLST2+)0AsX3!M(Tq3 z?~y!g-vp~2I=me0C`POvu4r<8gLb|$!FoGCO|Z6Px-0za0nu5kYM&f0ab2A3%auTc zETG9HLETS!VmPH!MnEO6E}zVJe0VTEy`2{J&A&^fCw)3~%^?oLIgGKN$*6$SyoB?t z#0$iqmT^O&69rmA#rRQfcx+dpn$l-t|2GrG3B&!Z{x6DkjGbX!$fV zrZ0BG^v;cJt%&EY=c_2P$T=96g$Th6D70WNz8rPMIswj({Bx%ezwZK*zaNf@&En(| zUHP~U7F2WS2bjXGNp3qqY*VfL^L&lODelncJ-B#8X_>{!$=s)@L`6Px01cow*x2>= zJJ?IVYWd6boNg`ZUSUNf{trk(agvj^ph@c7=+&jC0qC)Zl|c{B?LM9UtsMzTT7h01 zGtNC9{)^RIuyY~O?u*um^YgBGAFD`Sm#=a=SJ`Rdt6MX>=2ozS(z!Yz;pbV5_H4*1 zj7n>*|D{Eeh0EKK2aJ1dSH@w;(eRB)*3g274wYSgtv5scA|4#|65cCb7gCUt83P%Y zdwua5pXQ%*6TM&geORP`B}h|fV=caFk%V=lvJ|0gSBHg01uhXQBp64QA7QaEe;CHW z&4_gYCoxAMR)1d|uECZ-NRu%+NC+)fr|Oo~(a$72bZFZ`Sp8g$IGcIy#{zlxT&~O; zJ*h^Oy|}ME>t?;19SVBl1wUrJQgQ+6*077%(e5w zgRrJwY1dw9;o)JM;57AaUpcJC?praf;tbuwVryv@BeU9 z)pR6@pl|x@+frMT<7BhW6F99*^qLit25PXer1>$ApCg~|5p?poH_+T@iG>d;iYuS{ zr)7owL1%Cbn)wD%tge{RElR{q#EsVa;S~G`oA_vLNxUuEp>|&9fc@~;w(kto6vx~~ z7mDe)8Cw?3n>Pv_41ioAV+2<^KcCya3vcX*t%qX{=uT(wcNg6r1`^)H)1O$*KD`_3 zb{>Stmb(JELE!l1g{{~LottUEt-P9|!yY}AaULpboS!fHzBNF$Z-0Y+$%@%}I4WeQ zbLaCq_0M;QoK_crlI7Xz3`dE zUhmJEe8mWss)Ap;lK|FB{yjPIWVx%=x}x(g1=_4?R)Zc~|Er@7~fdoBUyb{$j;V3WWq-Yuo}40YNpltk?my1v%p&-J!!MkMcXubUNF5l@tOp`_p)*Wh2FpHB ztnK*i5P4VC?lJlN3{RSg)L#h<{<)T2iK=y z)_h>PHz9Tog!qWxzxC`oT-}vtqvhbM29=awi?$DOkE~uE3KBhC{LtrTfdWxQ5< zoa#HmU)Tl(p=7G99afPd^1s}U)_!+)&k}a4jM6uwSKhzsnIOzzcjp%GbWyC^EVx_;rMOz|NV(J-g-nm3~hU37x zIN4+W=Abm)7J2?H4($k$TQVdsU7Sr1ZIIY!;m*J^#fU=yE293L`d<)AAY&W?l7K+{ z2ZT5183G09755(ypFtN;I1UgF@&82mBy)lylPU%Qz(r4ll4Mi?&a-S%WmqtPDvJ3t zg`hhD%qW~qAG|5)VbEPxfHc1A3#iJ(@V;mI#i@vA2$_<_r1Aw0K-zLdmSKe29)fB1 zGSHHdJP+GsJjDN!K-ria)`o#{(9yu+yvqzp`|&rG9)ID_3~V#T+1*}Bu2*Z=;{fq1 z_e5kM%AxGyF1te7F(!a5MyyJpC-`A^h0lwSBEl5}$|fz;~H=Yf`LR>kdQ8r)1&R)C(vuL5f$!7KWz?{}MbR}ibAOo<~t`Xhm4hTkK;D2Gx zhC`I!!O|`cf<$vz`@DIErk`g5J!${fW0jK8l#4)(OlE&2@~D84N;CQHP6GT~YL)!- zsJ`#XB>Ba`E>YkD%I1A0U;k@^DoFReJa1`Fwg6KUt|D1;_IL*-iv%Ur_q}O3kooJ1qFd(-ZkdHy7a@R>{EmCbtDA9z*DDHZ z|Y|`G|eu8AFL6mA~J+5YFng6fKNdKomqMz2W2kV7IZ@DVVypSJ(xC74uA zx*LlU1NA3?53diwRyQx|0tfEmnIz3XWWuQ7n`J79`x18}vlDMA)5qd1pvXL>7vSox vbu8FQH%Bw|NU#qNyDn Response { + ( + StatusCode::INTERNAL_SERVER_ERROR, + format!("Something went wrong: {}", self.0), + ) + .into_response() + } +} + +// This enables using `?` on functions that return `Result<_, anyhow::Error>` to turn them into +// `Result<_, AppError>`. That way you don't need to do that manually. +impl From for AppError +where + E: Into, +{ + fn from(err: E) -> Self { + Self(err.into()) + } +} + +pub fn trace_to_std(config: &Config) { + tracing_subscriber::fmt() + .with_max_level(config.log_level) + .with_target(false) + .init(); +} + +#[derive(Debug, Clone)] +pub struct AppState { + pub saleor_app: Arc>, + pub config: Config, + pub manifest: AppManifest, +} diff --git a/simple-payment-gateway/src/main.rs b/simple-payment-gateway/src/main.rs index e7a11a9..93a875b 100644 --- a/simple-payment-gateway/src/main.rs +++ b/simple-payment-gateway/src/main.rs @@ -1,3 +1,94 @@ -fn main() { - println!("Hello, world!"); +#![allow(non_upper_case_globals)] +#![feature(let_chains)] +#![deny(clippy::unwrap_used, clippy::expect_used)] +mod app; +mod queries; +mod routes; + +use anyhow::Context; +use saleor_app_sdk::{ + config::Config, + manifest::{AppManifest, AppPermission}, + webhooks::{SyncWebhookEventType, WebhookManifest}, + SaleorApp, +}; +use std::sync::Arc; +use tokio::sync::Mutex; + +use crate::{ + app::{trace_to_std, AppState}, + queries::event_transactions::{ + sub_payment_gateway_initialize_session, sub_transaction_charge_requested, + sub_transaction_initialize_session, sub_transaction_process_session, + sub_transaction_refund_requested, + }, + routes::create_routes, +}; + +#[tokio::main] +async fn main() -> anyhow::Result<()> { + let config = Config::load()?; + trace_to_std(&config); + + let saleor_app = SaleorApp::new(&config)?; + + let app_manifest = AppManifest::new(&config) + .add_webhook( + WebhookManifest::new(&config) + .set_query(sub_transaction_process_session) + .add_sync_event(SyncWebhookEventType::TransactionProcessSession) + .build(), + ) + .add_webhook( + WebhookManifest::new(&config) + .set_query(sub_transaction_charge_requested) + .add_sync_event(SyncWebhookEventType::TransactionChargeRequested) + .build(), + ) + .add_webhook( + WebhookManifest::new(&config) + .set_query(sub_transaction_refund_requested) + .add_sync_event(SyncWebhookEventType::TransactionRefundRequested) + .build(), + ) + .add_webhook( + WebhookManifest::new(&config) + .set_query(sub_transaction_initialize_session) + .add_sync_event(SyncWebhookEventType::TransactionInitializeSession) + .build(), + ) + .add_webhook( + WebhookManifest::new(&config) + .set_query(sub_payment_gateway_initialize_session) + .add_sync_event(SyncWebhookEventType::PaymentGatewayInitializeSession) + .build(), + ) + .add_permissions(vec![ + AppPermission::HandlePayments, + AppPermission::ManageOrders, + AppPermission::ManageCheckouts, + AppPermission::HandleCheckouts, + ]) + .build(); + let app_state = AppState { + manifest: app_manifest, + config: config.clone(), + saleor_app: Arc::new(Mutex::new(saleor_app)), + }; + let app = create_routes(app_state); + + let listener = tokio::net::TcpListener::bind( + &config + .app_api_base_url + .split("//") + .collect::>() + .get(1) + .context("APP_API_BASE_URL invalid format")?, + ) + .await?; + tracing::debug!("listening on {}", listener.local_addr().unwrap()); + match axum::serve(listener, app).await { + Ok(o) => Ok(o), + Err(e) => anyhow::bail!(e), + } } diff --git a/simple-payment-gateway/src/queries/event_transactions.rs b/simple-payment-gateway/src/queries/event_transactions.rs new file mode 100644 index 0000000..6695f3b --- /dev/null +++ b/simple-payment-gateway/src/queries/event_transactions.rs @@ -0,0 +1,180 @@ +use const_format::concatcp; +#[cynic::schema("saleor")] +mod schema {} + +pub const fragment_transaction_details: &str = r#" +fragment TransactionDetails on TransactionItem { + id + actions + externalUrl + message + authorizedAmount { + currency + amount + } + authorizePendingAmount { + currency + amount + } + canceledAmount { + currency + amount + } + cancelPendingAmount { + currency + amount + } + chargedAmount { + currency + amount + } + chargePendingAmount { + currency + amount + } + refundedAmount { + currency + amount + } +} +"#; + +pub const fragment_order_details: &str = r#" +fragment OrderDetails on Order { + checkoutId + id + status + isPaid + paymentStatus + chargeStatus + canFinalize + totalBalance { + currency + amount + } +} +"#; + +pub const sub_payment_gateway_initialize_session: &str = concatcp!( + r#" +subscription PaymentGatewayInitializeSession { + event { + ... on PaymentGatewayInitializeSession { + data + amount + sourceObject { + ...OrderDetails + } + amount + } + } +} +"#, + fragment_order_details +); + +pub const sub_transaction_initialize_session: &str = concatcp!( + r#" +subscription transactionInitializeSession { + event { + ... on TransactionInitializeSession { + data + sourceObject { + ...OrderDetails + } + transaction { + ...TransactionDetails + } + action { + amount + currency + actionType + } + } + } +} +"#, + fragment_order_details, + fragment_transaction_details +); + +pub const sub_transaction_process_session: &str = concatcp!( + r#" +subscription transactionProcessSession { + event { + ... on TransactionProcessSession { + action { + amount + actionType + } + sourceObject { + ...OrderDetails + } + transaction { + ...TransactionDetails + } + data + } + } +} +"#, + fragment_order_details, + fragment_transaction_details +); + +pub const sub_transaction_charge_requested: &str = concatcp!( + r#" +subscription transactionChargeRequested { + event { + ... on TransactionChargeRequested { + action { + amount + actionType + } + transaction { + ...TransactionDetails + } + } + } +} +"#, + fragment_transaction_details +); + +pub const sub_transaction_refund_requested: &str = concatcp!( + r#" +subscription transactionRefundRequested { + event { + ... on TransactionRefundRequested { + action { + amount + actionType + } + transaction { + ...TransactionDetails + } + } + } +} +"#, + fragment_transaction_details +); + +pub const sub_transaction_cancelation_requested: &str = concatcp!( + r#" +subscription transactionCancelationRequested { + event { + ... on TransactionCancelationRequested { + action { + amount + actionType + } + transaction { + ...TransactionDetails + } + } + } +} +"#, + fragment_transaction_details +); diff --git a/simple-payment-gateway/src/queries/mod.rs b/simple-payment-gateway/src/queries/mod.rs new file mode 100644 index 0000000..41f3df2 --- /dev/null +++ b/simple-payment-gateway/src/queries/mod.rs @@ -0,0 +1,3 @@ +pub mod event_transactions; +pub mod mutation_transaction_update; + diff --git a/simple-payment-gateway/src/queries/mutation_transaction_update.rs b/simple-payment-gateway/src/queries/mutation_transaction_update.rs new file mode 100644 index 0000000..26499c6 --- /dev/null +++ b/simple-payment-gateway/src/queries/mutation_transaction_update.rs @@ -0,0 +1,17 @@ +/* +mutation transactionUpdate($id: ID!, $transaction: TransactionUpdateInput) { + transactionUpdate(id: $id, transaction: $transaction) { + transaction { + id + actions + externalUrl + message + } + errors { + field + message + code + } + } +} +*/ diff --git a/simple-payment-gateway/src/routes/manifest.rs b/simple-payment-gateway/src/routes/manifest.rs new file mode 100644 index 0000000..3ab361c --- /dev/null +++ b/simple-payment-gateway/src/routes/manifest.rs @@ -0,0 +1,8 @@ +use axum::{extract::State, Json}; +use saleor_app_sdk::{manifest::AppManifest}; + +use crate::app::{AppError, AppState}; + +pub async fn manifest(State(state): State) -> Result, AppError> { + Ok(Json(state.manifest)) +} diff --git a/simple-payment-gateway/src/routes/mod.rs b/simple-payment-gateway/src/routes/mod.rs new file mode 100644 index 0000000..5124453 --- /dev/null +++ b/simple-payment-gateway/src/routes/mod.rs @@ -0,0 +1,40 @@ +use axum::{ + handler::HandlerWithoutStateExt, + http::StatusCode, + middleware, + routing::{get, post}, + Router, +}; +use saleor_app_sdk::middleware::verify_webhook_signature::webhook_signature_verifier; +use tower_http::services::ServeDir; + +use crate::app::AppState; + +pub mod manifest; +pub mod register; +pub mod webhooks; +use manifest::manifest; +use register::register; +use webhooks::webhooks; + +pub fn create_routes(state: AppState) -> Router { + async fn handle_404() -> (StatusCode, &'static str) { + (StatusCode::NOT_FOUND, "Not found") + } + let service = handle_404.into_service(); + let serve_dir = ServeDir::new("saleor-app-template/public").not_found_service(service); + + Router::new() + .layer(middleware::from_fn(webhook_signature_verifier)) + //handles just path, eg. localhost:3000/ + .route("/api/webhooks", post(webhooks)) + .route( + "/", + get(|| async { "Your app got installed successfully!" }), + ) + //handles files, eg. localhost:3000/logo.png + .fallback_service(serve_dir) + .route("/api/manifest", get(manifest)) + .route("/api/register", post(register)) + .with_state(state) +} diff --git a/simple-payment-gateway/src/routes/register.rs b/simple-payment-gateway/src/routes/register.rs new file mode 100644 index 0000000..2203984 --- /dev/null +++ b/simple-payment-gateway/src/routes/register.rs @@ -0,0 +1,40 @@ +use anyhow::Context; +use axum::{ + extract::Json, + extract::State, + http::{HeaderMap, StatusCode}, +}; +use saleor_app_sdk::{AuthData, AuthToken}; +use tracing::{debug, info}; + +use crate::app::{AppError, AppState}; + +pub async fn register( + headers: HeaderMap, + State(state): State, + Json(auth_token): Json, +) -> Result { + debug!( + "/api/register:\nsaleor_api_url:{:?}\nauth_token:{:?}", + headers.get("saleor-api-url"), + auth_token + ); + + if auth_token.auth_token.is_empty() { + return Err(anyhow::anyhow!("missing auth_token").into()); + } + let app = state.saleor_app.lock().await; + let saleor_api_url = headers.get("saleor-api-url").context("missing api field")?; + let saleor_api_url = saleor_api_url.to_str()?.to_owned(); + let auth_data = AuthData { + jwks: None, + token: auth_token.auth_token, + domain: Some(state.config.app_api_base_url), + app_id: state.manifest.id, + saleor_api_url: saleor_api_url.clone(), + }; + app.apl.set(auth_data).await?; + + info!("registered app for{:?}", &saleor_api_url); + Ok(StatusCode::OK) +} diff --git a/simple-payment-gateway/src/routes/webhooks.rs b/simple-payment-gateway/src/routes/webhooks.rs new file mode 100644 index 0000000..ec70877 --- /dev/null +++ b/simple-payment-gateway/src/routes/webhooks.rs @@ -0,0 +1,58 @@ +use anyhow::Context; +use axum::{ + extract::State, + http::{HeaderMap, StatusCode}, +}; +use saleor_app_sdk::{ + headers::SALEOR_API_URL_HEADER, + webhooks::{ + utils::{get_webhook_event_type, EitherWebhookType}, + SyncWebhookEventType, + }, +}; +use tracing::{debug, error, info}; + +use crate::app::{AppError, AppState}; + +pub async fn webhooks( + headers: HeaderMap, + State(state): State, + body: String, +) -> Result { + debug!("/api/webhooks"); + debug!("req: {:?}", body); + debug!("headers: {:?}", headers); + + let url = headers + .get(SALEOR_API_URL_HEADER) + .context("missing saleor api url header")? + .to_str()? + .to_owned(); + let event_type = get_webhook_event_type(&headers)?; + match event_type { + EitherWebhookType::Sync(a) => match a { + SyncWebhookEventType::PaymentGatewayInitializeSession => { + initialize_gateway(&state, &url).await?; + } + SyncWebhookEventType::TransactionProcessSession + | SyncWebhookEventType::TransactionChargeRequested + | SyncWebhookEventType::TransactionRefundRequested + | SyncWebhookEventType::TransactionInitializeSession => { + update_transaction_response(&state, &url).await?; + } + _ => (), + }, + _ => (), + } + + info!("got webhooks!"); + Ok(StatusCode::OK) +} + +async fn initialize_gateway(state: &AppState, saleor_api_url: &str) -> anyhow::Result<()> { + todo!() +} + +async fn update_transaction_response(state: &AppState, saleor_api_url: &str) -> anyhow::Result<()> { + todo!() +} diff --git a/sitemap-generator/Cargo.toml b/sitemap-generator/Cargo.toml index 3b47b2c..1bd2785 100644 --- a/sitemap-generator/Cargo.toml +++ b/sitemap-generator/Cargo.toml @@ -9,7 +9,7 @@ repository = "https://github.com/djkato/saleor-apps-rs" documentation = "https://github.com/djkato/saleor-apps-rs" keywords = ["saleor", "plugin"] categories = ["web-programming::http-server"] -license = "PolyForm Noncommercial License 1.0.0" +license = "PolyForm-Noncommercial-1.0.0" [dependencies] anyhow.workspace = true diff --git a/sitemap-generator/public/logo.png b/sitemap-generator/public/logo.png index f5911307aa2ecc3982f960c709099ebc57251487..224be5b8d8f178fd2a937ff8a1b3311405e32fe7 100644 GIT binary patch literal 11117 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 literal 24834 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 vbu8FQH%Bw|NU#qNyDn