From 943f4d43d797415e9c5c799736c568b79c9e97e8 Mon Sep 17 00:00:00 2001 From: jbell Date: Sun, 6 Oct 2024 16:06:16 -0400 Subject: [PATCH] Initial commit --- .idea/.gitignore | 8 ++ .idea/dataSources.xml | 12 ++ .idea/httpClient.xml | 6 + .idea/inspectionProfiles/Project_Default.xml | 6 + .../inspectionProfiles/profiles_settings.xml | 6 + .idea/misc.xml | 7 ++ .idea/modules.xml | 8 ++ .idea/puffpastry.iml | 10 ++ .idea/vcs.xml | 6 + app/__pycache__/main.cpython-312.pyc | Bin 0 -> 2075 bytes app/api/deps.py | 0 app/api/v1/__pycache__/api.cpython-312.pyc | Bin 0 -> 537 bytes app/api/v1/api.py | 8 ++ .../__pycache__/issue.cpython-312.pyc | Bin 0 -> 3781 bytes .../__pycache__/user.cpython-312.pyc | Bin 0 -> 1049 bytes app/api/v1/endpoints/comment.py | 0 app/api/v1/endpoints/issue.py | 63 +++++++++++ app/api/v1/endpoints/user.py | 20 ++++ .../__pycache__/contract.cpython-312.pyc | Bin 0 -> 8094 bytes .../__pycache__/issue.cpython-312.pyc | Bin 0 -> 3735 bytes app/contracts/contract.py | 106 ++++++++++++++++++ app/contracts/issue.py | 42 +++++++ app/core/__pycache__/config.cpython-312.pyc | Bin 0 -> 835 bytes app/core/config.py | 15 +++ app/core/security.py | 0 app/crud/__pycache__/user.cpython-312.pyc | Bin 0 -> 1204 bytes app/crud/user.py | 17 +++ app/db/__pycache__/base.cpython-312.pyc | Bin 0 -> 750 bytes app/db/__pycache__/session.cpython-312.pyc | Bin 0 -> 1272 bytes app/db/base.py | 26 +++++ app/db/session.py | 17 +++ app/main.py | 40 +++++++ app/models/__pycache__/user.cpython-312.pyc | Bin 0 -> 747 bytes app/models/user.py | 17 +++ app/schemas/__pycache__/issue.cpython-312.pyc | Bin 0 -> 2226 bytes app/schemas/__pycache__/user.cpython-312.pyc | Bin 0 -> 858 bytes app/schemas/issue.py | 41 +++++++ app/schemas/user.py | 19 ++++ 38 files changed, 500 insertions(+) create mode 100644 .idea/.gitignore create mode 100644 .idea/dataSources.xml create mode 100644 .idea/httpClient.xml create mode 100644 .idea/inspectionProfiles/Project_Default.xml create mode 100644 .idea/inspectionProfiles/profiles_settings.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/puffpastry.iml create mode 100644 .idea/vcs.xml create mode 100644 app/__pycache__/main.cpython-312.pyc create mode 100644 app/api/deps.py create mode 100644 app/api/v1/__pycache__/api.cpython-312.pyc create mode 100644 app/api/v1/api.py create mode 100644 app/api/v1/endpoints/__pycache__/issue.cpython-312.pyc create mode 100644 app/api/v1/endpoints/__pycache__/user.cpython-312.pyc create mode 100644 app/api/v1/endpoints/comment.py create mode 100644 app/api/v1/endpoints/issue.py create mode 100644 app/api/v1/endpoints/user.py create mode 100644 app/contracts/__pycache__/contract.cpython-312.pyc create mode 100644 app/contracts/__pycache__/issue.cpython-312.pyc create mode 100644 app/contracts/contract.py create mode 100644 app/contracts/issue.py create mode 100644 app/core/__pycache__/config.cpython-312.pyc create mode 100644 app/core/config.py create mode 100644 app/core/security.py create mode 100644 app/crud/__pycache__/user.cpython-312.pyc create mode 100644 app/crud/user.py create mode 100644 app/db/__pycache__/base.cpython-312.pyc create mode 100644 app/db/__pycache__/session.cpython-312.pyc create mode 100644 app/db/base.py create mode 100644 app/db/session.py create mode 100644 app/main.py create mode 100644 app/models/__pycache__/user.cpython-312.pyc create mode 100644 app/models/user.py create mode 100644 app/schemas/__pycache__/issue.cpython-312.pyc create mode 100644 app/schemas/__pycache__/user.cpython-312.pyc create mode 100644 app/schemas/issue.py create mode 100644 app/schemas/user.py diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml new file mode 100644 index 0000000..d4c7b6b --- /dev/null +++ b/.idea/dataSources.xml @@ -0,0 +1,12 @@ + + + + + postgresql + true + org.postgresql.Driver + jdbc:postgresql://localhost:5432/postgres + $ProjectFileDir$ + + + \ No newline at end of file diff --git a/.idea/httpClient.xml b/.idea/httpClient.xml new file mode 100644 index 0000000..7417105 --- /dev/null +++ b/.idea/httpClient.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..03d9549 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..97ff0e9 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..201d106 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/puffpastry.iml b/.idea/puffpastry.iml new file mode 100644 index 0000000..2c80e12 --- /dev/null +++ b/.idea/puffpastry.iml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/__pycache__/main.cpython-312.pyc b/app/__pycache__/main.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f967db1a3717b9ed36071425c65fe1d824b2640c GIT binary patch literal 2075 zcmZux-A^1<6u)<7c4xnVT__ajM=HfG35yUYrqQNrOB-zY2#{*`WgK?y!mKm1o0*{$ zYzdae+Sr8pQq|Kwz zJ}?3wo$iXNB>w-;V!dXKE3bk-8GCfCA8kK z2AyI9qq(}dy7<07m=eb{CR=kxI-N6Ya>dqVL!Kfgn8mZQWhX}m0Y*-bjExVeilUKO z*(6}`S;V$gW6A;?kn^f!=4Nd8jiX*-OsNK;!W*(h(kz5K7%`UM-T51)^N_|0vVkJy zk*#da)tPjg+l!X4g5Kj1y34KbA9y_I8oG+Il~%m?hwXK>tj|SNnRQ58)frl=WUvI} zbRzKO2oCwc=9YYk`*#LVMAVONpM|#(UjIv|8#!yB$aftd{m6qXJj-Ep3HHj?U*RvI zS^S8f#Sigt+-tHFsb`Wvy{ONeF(lRp)6c9vo!GJ>+cFKLO(NSwk~Ix#EkK(Li{*Uy zSx?t=PA6U2Nup_8qjTwL+0;kPT$ZG5t1CZ~$>gDd=3JMY&v)svY9#V=R8$SsmXt{| z3NBbAYY?X2(e5JpsitB1>aDBG3%3_m>r0X7vVKdyf9Ao^r$fco-a>2dcagrLuW!@W z_ZN?94=yjnzP9E{z=K($FOdjJmLO9vx-3SW| z*9F16(TbDp!o~qF!)=(^+cCq(yvY;dMgk|hLmOQp!^cIi(;)0aQ`54o=8W_nZy+A< z0`Wkfos$6m%PW^dd;P62i1qi8a2Kz@wd8a%!1`ha!9^T9)d3L$tz2xg4j}buYKB;O z*)SVG4YRL}FgfM>HBk3a{N~8w$d=%Tnt1A;St0m#J#qlw!7aka!2@_c+?C4{1CC1~ zYvqi%hk9())(90Z6Vrli(V#-Cw5jIV9%-<$Seh|4C*TCd9dUt%feN`NJrRh{Mik`Lt4R-Xt0I3iU}6BZwpo&-)lS0i~YzclY!s zUV*1uJ#`4G!c$>7XW3N9=S&-15G^Mw@n#@s`R35nNt3mLiW%S?Y|>=+4Q;Gw34QPW zNThS7WwPT-YakL+2~0*toif=0rV(akWxCwWgvo9Wr~e6MGBFAFigR?Flj5|>sSXW| zj*YxIaC$--P7V!F@#4VP_~6Je4W1boKRq@$I>D3xTxin8ZfSgCjPei#LMe(=O^wzh zFI*UTYv7DDGB$W_aCn?XR3oj;D5P@3v053jP9%wnGnZ9J-Jm?2-G^ugl0*(qp7~zW zbIOcHPMWWRjd9g_4AVA`G2TKQzoC{b)U$=!w$RbXsO^cs;p5vr#5HVl91g6CPY{S5 z-ZlK`Lt>;9h!z8f3V}nVP<=7fS_rk4YMY9+ZH3ylQmmyIYcIswH={4FUfZngD}@`L z2E8J;$Ztmx7K$iZK+%#AEk&D)(T+m2qqMKN6x(;_s55N`5sp1YUd+9)`jd_?-Y#~Y zDs-OOMA7e2@@GEy;rzG!{vW}MP_uaM=K1%}-wCX?eA)Yz@Pjb0?Lk7b*$;dC2Ux4} AR{#J2 literal 0 HcmV?d00001 diff --git a/app/api/deps.py b/app/api/deps.py new file mode 100644 index 0000000..e69de29 diff --git a/app/api/v1/__pycache__/api.cpython-312.pyc b/app/api/v1/__pycache__/api.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..345d0f392e2af16603b4a401df78c5e31df664ec GIT binary patch literal 537 zcmZ`#F-yZh6n@tvZ4I;{h|s}JhtiVJ%|Qegu?|vPggQuUE^SKEguA4ePVW8y?bNke ze?Uif7X?K*5EKV-6FLY5(M#J{5IuPJzVChSeebxdR7wVZwm!d%51wB_5*=v*2Im}{ zfDSscz(!?+LL9SVwon#qu`D8p0Y<$^ds)I#Jr$WuGZyOUDT|OU=wcOJNIhX(LGaJC z_~&%|_jHiRCf`DI zmZXOD?QD`I42^o4Ym)GNY+iF+g)^mDP;f(c9kbz4B65j@zr|#(?UT_;mNpv|%hz$l z*l!w(xhqErc79=jO%-o*z>i` z_uZ_dq*B&R5NSM2Jg^}o2t2K75g-i#lPHVw0Eh?d7|G$%iXoT;Z(f5$$P?#W`zEgI zX?y7>f9Ia>ob&yjU;okNY9i2nc28K`vJvtR9GJ~gMRqSzLf$4ak!hMl@hhaMDntc} zxR@4IF)A9kl$KOW)MDV4v{ki5Z3b>l+f_%@Vc@oOlj@8*4cwk?R$Wn->W;cqPt>D& zqh3k`q6oLETSr@zmeeuxK!;!1p|mQlP9WTAx-C@bvhxjcFt6T^lZ`@`!NOec} z!qmm=vZgQ?*yXsUXh~H;oGY4^%q()iT~nMdE0JtQWATKB%tb|uEpua%8*%w+j4PZc zvzefzI}Gs)S+41xuq+#+i^|uQ6(D%OoYjLcvV5>H`y^RTV=>3gzlr4cA)7f}yF@8)TKF91ZwY((7P?-w+A; zuJE?_O^b!xpsVx}IZ3{4{fT&u-lDTX3&Z6Z8nf<>-vrq1Y&m%}qNFB+&5 zuNq6@`|_ZmySW-?S}ak0YQ4$e#88OioqBrq&xDq;suD_FRnqCug_XoooT(RBHl-vq z9?C5*Eac)`V=JL}E(bjsdUZIYWaL~nnE}4B+)!?X`S4^2ywLgc0RBbx*2q8Gd+*u3 zC3|Iu^0|_KxZocy`bSDFBWq`Oob7q3y(|*n=sx$rA~#Qk zZS*b;TcqFG0K4uC<>D;9$l|#ruG=e@Ny>=>MZpw&9z&7u1M*lD35sc?NX>{puqjEo z5I)>iiRnY;_Ys8)tHPne8D2kVJ5)Ktmt`#gbi+kz9>Q{UMN_zLTVPprN%?wEWF08m zin2|IavZD+G~UD7k$c1h0YLZ+fX}M2(Sm>Uz01G6^7AVnj2FjdivDw@mUFero2C04 zZQ*9v8}6c;J{sQv^MvJU`r#PV0PGxLEZ<8uV#)| zQf3&fbup8@rn}HNFc)zxuI6+{d|6wHVSdtGT2_mvA>Tlhl)2sBZAHb)&;(zQU@YJ6>ga0Kq%dz#vd+kTxS=_i*>Ypg|PZZlHOP0Y4shfja+>F*ES87~cn3xnYg`hGXGIaC~csStQ+-SZcx=dJUlw&6nCaItOVy`|EL z>B5QW?Y8M1Ur)(5Q1A`Bd-A8x-g@>|{iVs7!sN{Fa6#QuBPid48v@`yDyo z`s8-=lOF=AC9W%Uuk0X!fwGsl+TT+0&hEU_{Xc#dZg_L# zj7WbWMNSL9rjc>$`yyb!2>@-=Pa~7o%_A>(V6fFsBd4rePk9j^ry#R6NfAF~z|$7M zA-}&0MZiOW)0=Q^)$xX5@)&f!0pL>|52_Zsr)2YQ+x#20 zQqMEnJbOMvuq(vEhR_iwxe^S zztnqtyZ88Kn7Si1m2Jc^L-#(kPw(?%aLAkC;}I9#9HkM5v^f@b!(dCK5vQ~zSrK5xEi(Jy6UIC9+HbkXcABR%*)D3bj&|^C6?UGSs5G3=UhC zhcK{J!mJx<^3|bg%rgS8-hxD)SW?uuxd7%{hPKEsr!q{X5X_Cg2)YQhzAj-kYSfOp z2#8Ubz|o=HH)0|~{oEqE0EwZzefudW-0gQSJ~{dk{`x*5 zpTAFf?~~x)$b6B^|J~txvun)?su1ko3cMCr|Ki4(cRPP{el1X>qaO=a+OaDVVXTZ; z*=Gu!E7E{L+8B92sxnNXN^9iyqzZJRtDF#U>0wxd#_R!Iz7) Zy(~DWGylZU0|H+Z>i8Hb>^Zo?{{j9f6F~p~ literal 0 HcmV?d00001 diff --git a/app/api/v1/endpoints/__pycache__/user.cpython-312.pyc b/app/api/v1/endpoints/__pycache__/user.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1fbdae0274db562c4b855cf7085b0d87331a4859 GIT binary patch literal 1049 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 P6Etx38~-8DiQWDSmPII0 literal 0 HcmV?d00001 diff --git a/app/contracts/contract.py b/app/contracts/contract.py new file mode 100644 index 0000000..c5a686a --- /dev/null +++ b/app/contracts/contract.py @@ -0,0 +1,106 @@ +import asyncio + +from stellar_sdk import Keypair, Network, SorobanServer, TransactionBuilder, xdr as stellar_xdr +from stellar_sdk.exceptions import PrepareTransactionException +from stellar_sdk.soroban_rpc import GetTransactionStatus, SendTransactionStatus +from stellar_sdk.xdr import SCVal, SCValType + + +class SingletonMeta(type): + _instances = {} + + def __call__(cls, *args, **kwargs): + if cls not in cls._instances: + instance = super().__call__(*args, **kwargs) + cls._instances[cls] = instance + return cls._instances[cls] + + +class SmartContract(metaclass=SingletonMeta): + contract_id: str + user_key: str + def __init__(self, contract_id: str, user_key: str): + self.contract_id = contract_id + self.user_key = user_key + + async def _execute_procedure(self, procedure_name: str, parameters=None): + source_keypair = Keypair.from_secret(self.user_key) + + soroban_server = SorobanServer('https://soroban-testnet.stellar.org') + + source_account = soroban_server.load_account(source_keypair.public_key) + + built_transaction = ( + TransactionBuilder( + source_account=source_account, + base_fee=100, + network_passphrase=Network.TESTNET_NETWORK_PASSPHRASE, + ) + .append_invoke_contract_function_op( + contract_id=self.contract_id, + function_name=procedure_name, + parameters=parameters, + ) + .set_timeout(30) + .build() + ) + + try: + prepared_transaction = soroban_server.prepare_transaction(built_transaction) + except PrepareTransactionException as e: + print(f"Exception preparing transaction: {e}\n{e.simulate_transaction_response.error}") + raise e + + prepared_transaction.sign(source_keypair) + + send_response = soroban_server.send_transaction(prepared_transaction) + + if send_response.status != SendTransactionStatus.PENDING: + raise Exception("sending transaction failed") + + while True: + get_response = soroban_server.get_transaction(send_response.hash) + if get_response.status != GetTransactionStatus.NOT_FOUND: + break + await asyncio.sleep(2) + + print(f"get_transaction response: {get_response}") + + if get_response.status == GetTransactionStatus.SUCCESS: + assert get_response.result_meta_xdr is not None + + transaction_meta = stellar_xdr.TransactionMeta.from_xdr( + get_response.result_meta_xdr + ) + return_value = transaction_meta.v3.soroban_meta.return_value + output = translate_soroban_value(return_value) + return output + else: + print(f"Transaction failed: {get_response.result_xdr}") + + +def translate_soroban_value(val: SCVal) -> int or str or bool or list[int or str or bool] or None: + def sanitize(k: str) -> str: + return k.lstrip('b\'').rstrip('\'') + + type_handlers = { + SCValType.SCV_U32: lambda v: v.u32.uint32, + SCValType.SCV_I32: lambda v: v.i32.int32, + SCValType.SCV_U64: lambda v: v.u64.uint64, + SCValType.SCV_I64: lambda v: v.i64.int64, + SCValType.SCV_BOOL: lambda v: v.bool.boolean, + SCValType.SCV_STRING: lambda v: sanitize(str(v.str.sc_string)), + SCValType.SCV_SYMBOL: lambda v: sanitize(str(v.sym.sc_symbol)), + SCValType.SCV_BYTES: lambda v: bytes(v.bytes.sc_bytes), + SCValType.SCV_VEC: lambda v: [translate_soroban_value(item) for item in v.vec.sc_vec], + SCValType.SCV_MAP: lambda v: {translate_soroban_value(item.key): translate_soroban_value(item.val) for item in v.map.sc_map}, + } + + if val.type == SCValType.SCV_VOID: + return None + + handler = type_handlers.get(val.type) + if handler: + return handler(val) + else: + raise ValueError(f"Unsupported SCVal type: {val.type}") diff --git a/app/contracts/issue.py b/app/contracts/issue.py new file mode 100644 index 0000000..15d0121 --- /dev/null +++ b/app/contracts/issue.py @@ -0,0 +1,42 @@ +from uuid import uuid4 + +from stellar_sdk.xdr import SCString, SCVal, SCBytes, SCVec, SCValType +from typing_extensions import TypeVar + +from app.contracts.contract import SmartContract +from app.schemas.issue import IssuePost + +T = TypeVar("T") + +class IssueContract(SmartContract): + def __init__(self, contract_id, user_key): + super().__init__(contract_id, user_key) + + async def list_issues(self): + issues = await self._execute_procedure("list_issues") + return [IssuePost(**issue) for issue in issues] + + async def add_issue(self, title: str, paragraphs: list[str], telegram_handle: str): + issue_id = uuid4().bytes + print(f' => {telegram_handle}') + await self._execute_procedure("add_issue", [ + SCVal(type=SCValType.SCV_BYTES, bytes=SCBytes(issue_id)), + SCVal(type=SCValType.SCV_STRING, str=SCString(title.encode('utf-8'))), + SCVal(type=SCValType.SCV_VEC, vec=SCVec([SCVal(SCValType.SCV_STRING, str=SCString(paragraph.encode('utf-8'))) for paragraph in paragraphs])), + SCVal(type=SCValType.SCV_STRING, str=SCString(telegram_handle.encode('utf-8'))), + ]) + + async def get_paragraphs(self, issue_id: bytes): + return await self._execute_procedure("get_paragraphs_for_issue", [ + SCVal(type=SCValType.SCV_BYTES, bytes=SCBytes(issue_id)) + ]) + + async def increase_vote(self, issue_id: bytes): + await self._execute_procedure("increase_positive_vote", [ + SCVal(type=SCValType.SCV_BYTES, bytes=SCBytes(issue_id)) + ]) + + async def decrease_vote(self, issue_id: bytes): + await self._execute_procedure("increase_negative_vote", [ + SCVal(type=SCValType.SCV_BYTES, bytes=SCBytes(issue_id)) + ]) diff --git a/app/core/__pycache__/config.cpython-312.pyc b/app/core/__pycache__/config.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f2982cdbd36082b156254a65c79fe89dcb9b593b GIT binary patch literal 835 zcmYjP%Wm306tzuKp6Mf`Q5AK;rj=+V%sUCHsv7Ko!Pv$&U}j@E*gO)09pkixl_O=> z56BmEQRP$m1IXP(>Ibw{7L~H<;1H>Iq&w%#J#$BM@BO~D6+xhV{ecaCp$PJGMILNp z4Q^jUaD`w<715AOL_@E!`v`z%I&!_10d$Tf1N}AgH<;14+cey+pcbGS0x%5)0SyBi zS^#Wn8{m<)34+=q5YmDmtc5^C3xnu9l8kyGQFom2bU0f?`SZab!K*XJo-a1jsZ1)p zcts>gf=gJ2>I_VKk}#(UXQU@YM>h=BPE2*@lWCtOu|+VQ^4B7q$?p3wvk2ml7R$)h9!&z0=8tMQk|4=kyCN2;BbhB^TV*;=G>Scn?2`4I zcTgb!TNlX=-4d(KUYTjrtemTe`EDi876C;9s!`1I`GQQ!HJNAf!amn)=4<5zX_+%; zXzQ~x6^<#&*|}=0BQaj!>QqHiSfWXJ!44&eG{<^Tk`gNd%}Xot zgkTu1OOXmE&{dihy$He6N++X;lHkRNwp3Fjx=C?u$qRR(mXZ{j^r97xl>`E&h9pK3 z5>X~ZN@)wsH{^EJ{kubaWKMMa0k!3vLGRw!$-Rg4t&}}StQ!clBw&&Jw;xAn0=4sufm)nfnxQX{J z7(4!uflXq5CNDHw50$;Wiur;ai0(q~?u+-{-i6eEaSwvu0Tf05Vlfn;e-PilL}EK% JN*5vj?my=E=8gaW literal 0 HcmV?d00001 diff --git a/app/core/config.py b/app/core/config.py new file mode 100644 index 0000000..f726028 --- /dev/null +++ b/app/core/config.py @@ -0,0 +1,15 @@ +class Settings: + PROJECT_NAME = 'Puff Pastry' + VERSION = '0.1.0' + DESCRIPTION = 'A DAO platform on the Stellar network' + ALLOWED_ORIGINS = ('0.0.0.0', '127.0.0.1') + API_V1_STR = '/api/v1' + + AUTH_TIMEOUT = 10800 + + WALLET_ID = 'SANMQAR5UXHAQNLXGYIDZDIPFH3DADD3UCS6EFXN52QBI4AK6YEDWDJ3' + CONTRACT_ID = 'CAZNJSDVIQSGMYBLUINX3CS4WC4N6ZEDZEK64P45XDXHXPL4R7OYM4HB' + + DATABASE_URL = 'postgresql+asyncpg://mein:MxS2p37ViXtXikeb@localhost:5432/puffpastry' + +settings = Settings() \ No newline at end of file diff --git a/app/core/security.py b/app/core/security.py new file mode 100644 index 0000000..e69de29 diff --git a/app/crud/__pycache__/user.cpython-312.pyc b/app/crud/__pycache__/user.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..30048ee1e608f0252204ea58b56846885ef2213e GIT binary patch literal 1204 zcmaJ>O=uHA6rS0?Y?G#GE!H1QnnQ)ILRO(zk=jxaFP2iNr(DXqJ82Vln>e$v4Q{2h zB9s=3Ahv>F4_>4QJ?Xh;FUB4k7d=T)Z-IJ{o_w=Osz&sYd4DtWcHj5C&D&@+j6g1) zLz8N5EM_7P7F79Gu3O`+_YvVp?>hv=qdmEjvnD0be56l1V5dS;~DW ztp;(3v_Pyv94@tJ;cvXOhW*xIR+=`)31iv(v?lmUcA63D`U)d9F)TtXhnV{&R|U#eTg)Q@<&hwl&{<|>>^=)h|ChR z=n_q&ygF6+ro>#wWl#$xlgWdAeJbw|J$IAXwmw!erwrcjV0xjL$rKFc(vofz z3c5*)md*i56-vHpSk?p&Xbej6%IrL-ujt1-szeei7asR459~$~W&S(6aRSu*l}fl{ zRbSHAdv?OzyHfXisr!Jn!mWkALHrsINNKYqOpjuaS8yAQO*jAqPigD1=w{^ z0Z|_#9W|QbG6cVi_@EN?_j)!}t?T+K%k7^OWi`}*j+ngd!DMe=8 zl)=BkY@YHV1iBA5Ya`5?_hT?{DJO3co27y&3kF-VvZfmhoG-#yQr;2e|0w&gP|)vz zIsn&-HW{RG$no!w^?|C27~@Y!|AJ2Mp=4DNaB_X5iok5$*ypCI%DA1Uz-*b{xuK`v F;!kF119SiY literal 0 HcmV?d00001 diff --git a/app/crud/user.py b/app/crud/user.py new file mode 100644 index 0000000..7e1caac --- /dev/null +++ b/app/crud/user.py @@ -0,0 +1,17 @@ +from sqlalchemy.ext.asyncio import AsyncSession + +from sqlalchemy import insert, select + +from app.models.user import user + + +async def add_user(session: AsyncSession, user_data: dict): + stmt = insert(user).values(**user_data) + await session.execute(stmt) + +async def get_user(session: AsyncSession, session_id: str): + stmt = select(user).where(user.c.session_id == session_id) + result = await session.execute(stmt) + if result: + return result.first() + return None diff --git a/app/db/__pycache__/base.cpython-312.pyc b/app/db/__pycache__/base.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3d954b26411237e96c83e7c78a5c91760cdb080e GIT binary patch literal 750 zcmZ8eO>fgc5M4Wt6X&aG(-czF0}=w|5}yzkgp@)#Rj8sxLcUmAd(t@Udd;p=8cx8U zfWLs+|G*F60^)$RQp5pq$c?ByRUFuLQ&F*&*YC}n8SlLPT(4JLW0RP);1#IlSvfC|BDFm`$ClDcUMa^#Ql^gQ7|29mfiqgY zZ2#{Y+F>aypE$?XMa-+JTpOxT3M=|KsU8;>N=+%o@uJkLPio!zOtY>bn;`@QbDTzp z;O}DrT~k*3ViE^i@j(=WYy})}3O?2`AM&88s|gSyVll-pfNP%D>|O*FFAGT1C_WH- zGwsz(56~^hdcqj-6Apf&MCs&IM6W^CC_eJpA@BiVuO4?TX{&Py;mal*C6Ov%u^%u> zqf}P#IAsg$XgnZeF?`Z($aV3OU=j?0PI_>Z_O1-a3+e=N?Z1S4s)%vzA=h@v#+3}l zDYvzvFI6t#Bu>gVxBO>Mx21hykX*iM$2JQvk=CYi_ai!0 zMdwv2UNSi^-={1b6L`Qk zR2M_N;7YxIgqI*Bk!zsEmMeZ+jYi^^;MrPH%Tb-{~CtF#|8qI9Eb>7%g5T4mT|8$)t4pa??;H)hw2iXf!p*=v0w;&`4RZ+RrLaNs5b$;0OnztLd z77aoT;zA_|h?pZsP^sX8#MQp`Qd1;XtLB7|;AW9ZqyeeSTgT9Z1GCz<-tfV$wd?d6hsH>QF})Jgg#eEVzOvR>c^LuH?y8ImVK!c!_F) zuVY({z{>`iwl5U+j1j?IpG>>p0l~`#1aHV0bu7E_4OJcbr2F7+x7kL3E03o zyek5130?6+S%!5;!4u^v4pxKEfs=4;k>02C5Kc}63c#LNN2-o@dB_grBF2`&bDad4_TVbzTF3XWjcvFr zUW7K1@=JN%=l@mBm85@_9^${5hiAr>^O(3Zo-M$(fN+tr@-D{NZisCx;<=*C6skH> zY~S-%LdN@wlFWDAnq|Ko$+qhUPEm*w!T8uCbOOFZx!m3AY>iGYPF1d&Da+xw>r_(k`rF4*Dme5m_~w0dV?(*eudrl;jWe& zYWh!_{^in0NTl>T+1Za?r=eTkEZ@%RLscKB`X~)Ek9SL(rO%9^I)6)@|6>{+$rEp& z>ANEZGWq*=wYlFxNM(sS%<6-zemh$js)d1CxT8-0Ig)8|0w;a&hL%5 z)Gld_<^Br3q0+8uXC>Uk%AE44Na%Ak4Y7Nyq6^<*C8vBR5&C|He7z)^8Ksw!IL;iQ z(B3gLOHyxE;W&pB*wa&HTJAkHW6E;xG@6*~olyw)Wtsf_v}kHVKf^QhwMWebxt~Y# z$#lPUY1jjEV$X`rFO4whHP sgX1)eL`2+%`E6L(2IChfj+7MYUo^jZ>xUD!N@oUhFWv)ku*WF&Z&YL^_y7O^ literal 0 HcmV?d00001 diff --git a/app/db/base.py b/app/db/base.py new file mode 100644 index 0000000..a5ed424 --- /dev/null +++ b/app/db/base.py @@ -0,0 +1,26 @@ +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.ext.asyncio import AsyncEngine, create_async_engine +from sqlalchemy.orm import sessionmaker +from sqlalchemy.ext.asyncio import AsyncSession + +from app.core.config import settings + +# Create async engine +engine = create_async_engine( + settings.DATABASE_URL, + pool_pre_ping=True, + pool_size=10, + max_overflow=20 +) + +# Create a custom session class +AsyncSessionLocal = sessionmaker( + engine, + class_=AsyncSession, + expire_on_commit=False, + autocommit=False, + autoflush=False +) + +# Create a base class for declarative models +Base = declarative_base() diff --git a/app/db/session.py b/app/db/session.py new file mode 100644 index 0000000..e5afa3f --- /dev/null +++ b/app/db/session.py @@ -0,0 +1,17 @@ +from typing import AsyncGenerator +from sqlalchemy.exc import SQLAlchemyError +from sqlalchemy.ext.asyncio import AsyncSession + +from app.db.base import AsyncSessionLocal + + +async def get_db_session() -> AsyncGenerator[AsyncSession, None]: + async with AsyncSessionLocal() as session: + try: + yield session + await session.commit() + except SQLAlchemyError as e: + await session.rollback() + raise + finally: + await session.close() diff --git a/app/main.py b/app/main.py new file mode 100644 index 0000000..1fb0480 --- /dev/null +++ b/app/main.py @@ -0,0 +1,40 @@ +from contextlib import asynccontextmanager + +from fastapi import FastAPI +from fastapi.middleware.cors import CORSMiddleware +from app.core.config import settings +from app.api.v1.api import api_router +from app.db.base import engine, Base + +async def init_db(): + async with engine.begin() as conn: + await conn.run_sync(Base.metadata.create_all) + +@asynccontextmanager +async def lifespan(application: FastAPI): + await init_db() + yield + +app = FastAPI( + title=settings.PROJECT_NAME, + version=settings.VERSION, + description=settings.DESCRIPTION, + openapi_url=f"{settings.API_V1_STR}/openapi.json", + lifespan=lifespan, +) + +# Set up CORS middleware +app.add_middleware( + CORSMiddleware, + allow_origins=settings.ALLOWED_ORIGINS, + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + +# Include API router +app.include_router(router=api_router, prefix=settings.API_V1_STR) + +if __name__ == "__main__": + import uvicorn + uvicorn.run("main:app", host="127.0.0.1", port=8000, reload=True) diff --git a/app/models/__pycache__/user.cpython-312.pyc b/app/models/__pycache__/user.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..27b65248869236da650ab3ee1aac8774626fcc04 GIT binary patch literal 747 zcmZvYzi-n(6vxl@*}gbU%MSrUFd;ESNO1=`02Kq3kQj<85f(4jCHImz+}Y-ABYEru z6Y54s#-R2;VI)Jj1tEkGOi0-}ahJ43g`DK)@B6%W?|XM2Tde@;^%l?&*$90z$>~}< z5wl-fyh98ziz9&|B&^7?Om1^qIFTc8goPWq!i&5)cepP|L@YfLJcfacFP#qk``0yx zxqz8>=pQ%>K4%16);J8#v8L|(m#r-6Yb{}`OW4|ZcF&-YuKDmbae345$-B&|3%+f{>BkjSEp=eoh^naoF7nZ=dlPuopt zo=;*O^zfIQ^8N@!EnP!B6MPehsW)k!cUZ5})8~}t)`|!ysWznw8`BSHrYSnU7mhLk z;kXB!hdXtDM5Wk~*%OLV^;eGY>JcWdw|BQcPkzktCT3@W5SdT!c2B4V;kEouz?s!_xXYs4 zqHtF1fS(Tn(LnRj7*l@+$Za6cNu4rMS3U>ITzxdOt7}~6DMru1nx{ci#+n*y24LD) z)5ltBY?mHm25R7j-c&Y8ld|kazMg^GvON7`?h#F{88R88`_A)Xs+jZe);H- z+Qh_)TxWS!YHF;!yQ`kxB&)Puyh6;WDD#Pw9lq$vAW|)dMS9=_E|1c((`wlwj7q*O z?CZkzm#l`@X$Mi!_oNdzE8JS~0xqLsn_ssl)}?^E95%G9CA-bwsBA-Uh%~e8AW|IG zP$mdsv~%#=c^ky{^d1R@-V(BzT1ePTXIL_1}O>(y>%CLwuW(RhZv8;w`OKDjWjwY2#W$oocb;)b->hd-2y4A(7 zv1E(ZqVSe^Bal_U(`@>-3`AJ9eZMLjOT1;vs)Ip0dbM}dLqMs|Sf7&mU*Dv;G=bmVD{h1?Av?D{A zAm!=x%iEfwAHIL~If3bKgQR92s897%eZ8=)Q2j*j!aoG29hIbtC{Ax{u!KydjN4bg zx{5aQ_w{YCnSXE!P3O_7G(bF#T}*LL?;Dj+{2C`aA6L0H>~*Lmy-w#U76Gi|ju&dC z9+l2B7T3^c{6>dEPsvIyCBzt7i@@|n*>OLSH8Uk%1x%bo@fwQPQJe-5X|4k)!5T`) z#zp!DE>RqVpF9lW7U^TCl8BaPdRM;9Jk^H&q2a;);b8~+7b?(B%DY}jAL=81ma^jp zHB;ZmIMS|po*Pr%PxdXiM4&+TB-R6v&6hVWCJy13gV`bPgW17<&TjN4<6iy(dhwpX z8xFQ8?C6Hm#tsZ#7>p;{|3zNB1K02O%TEEmU9$K$~*r+95p-s&+ z;O&IWZU;_7U{S^IDS3tQyx)(kNd^+a#FQA3$xc~qF literal 0 HcmV?d00001 diff --git a/app/schemas/__pycache__/user.cpython-312.pyc b/app/schemas/__pycache__/user.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5828fc5c2278ddaf35567cba763865208da946f4 GIT binary patch literal 858 zcmb7@zi-qq6vyo(m&;vyz4i`}SP*L#GJ~oLx>O+bsF1qcVwu=SQsq}|XULo)@gKDR z0^(2MA2?ZJVq!zOW9fun+&jvI$neeQz4-arUprq%qaiX+%h89I7@_YFY@Xg7tPTxa zBZe5xP>$yqBL|%z=3F7>a{R@$fQXv)Tc#zR8R!ON!Qj2UB`Z^%p07CSTki2Ad#trWha#_<1N=@WS zCG?mOS`?+C%Jh_wO$+rItzfgaFQTN(d35ofXIXUC#0eGonJ6!KtYlQxi$!JHizcE~ z70Ec^Ih7Ht^I6phIFU{~1M;bnk7&6+ZQm}(lXkY;J7`buh2zQ9OiVW=|7|$fYz+tN zt>Hk{a3E_q&>zDezB2amLZxxH`*-}Z=2B@VWugyD2|xxQaPh>B5#z1cgAUsfOI=~0Q6`hP_VHf*lADxVFT37LQe-(!ze6Kz{Rt!c60vS=QcRBr+g$Hn{np% gu+4tD7~^l~$=dhv^waY-GOzW-$j$3tz}-rJ1NEH5i~s-t literal 0 HcmV?d00001 diff --git a/app/schemas/issue.py b/app/schemas/issue.py new file mode 100644 index 0000000..60939b2 --- /dev/null +++ b/app/schemas/issue.py @@ -0,0 +1,41 @@ +from pydantic import BaseModel, computed_field, PrivateAttr + + +class IssuePost(BaseModel): + _id: bytes = PrivateAttr() + title: str + summary: str + paragraph_count: int + positive_votes: int + negative_votes: int + telegram_handle: str + created_at: int + + def __init__(self, id: bytes, **data): + super().__init__(**data) + self._id = id + + @computed_field(return_type=str) + @property + def id(self) -> str: + return self._id.hex() + + @id.setter + def id(self, value: bytes) -> None: + self._id = value + + +class AddIssueRequest(BaseModel): + session_id: str + title: str + paragraphs: list[str] + + +class AddIssueResponse(BaseModel): + result: bool + + +class VoteIssueRequest(BaseModel): + issue_id: str + increase: bool + decrease: bool diff --git a/app/schemas/user.py b/app/schemas/user.py new file mode 100644 index 0000000..446b6c9 --- /dev/null +++ b/app/schemas/user.py @@ -0,0 +1,19 @@ +from pydantic import BaseModel + + +class User(BaseModel): + session_id: str + auth_date: int + username: str + first_name: str + last_name: str + photo_url: str + + +class AuthenticationRequest(BaseModel): + id: int + auth_date: int + username: str + first_name: str + last_name: str + photo_url: str