?????????????????????????????????????????
████████╗ █████╗ ███╗ ██╗ ██████╗ ███████╗██████╗ ██╗███╗ ██╗███████╗
╚══██╔══╝██╔══██╗████╗ ██║██╔════╝ ██╔════╝██╔══██╗██║████╗ ██║██╔════╝
██║ ███████║██╔██╗ ██║██║ ███╗█████╗ ██████╔╝██║██╔██╗ ██║█████╗
██║ ██╔══██║██║╚██╗██║██║ ██║██╔══╝ ██╔══██╗██║██║╚██╗██║██╔══╝
██║ ██║ ██║██║ ╚████║╚██████╔╝███████╗██║ ██║██║██║ ╚████║███████╗
╚═╝ ╚═╝ ╚═╝╚═╝ ╚═══╝ ╚═════╝ ╚══════╝╚═╝ ╚═╝╚═╝╚═╝ ╚═══╝╚══════╝
????????????????????????????????????????
Tangerine Discord : https://discord.gg/abzmthch
Tangerine을 확인해 주셔서 감사합니다! 이것은 내가 작업중 인 새로운 파이썬 프레임 워크입니다. 거의 베타 버전입니다. Flask가 당시 비동기보기 기능을 지원하지 않았기 때문에 글을 쓰기 시작했습니다. 그들은 지금 2.0 기준으로하지만 여전히 상자에서 벗어나지는 않습니다. 이 프레임 워크의 목표 중 일부 :
JavaScript에서 나오는 개발자를 위해 이해하기 가장 쉬운 Python 웹 프레임 워크입니다.
기본 API가 매우 가볍고 무인화되지 않았지만 필요한대로 상자 밖으로 작동하는 옵션 확장 기능이 있습니다. 기본적으로 몇 분 안에 정적 사이트에 서비스를 제공하기 위해 매우 가벼운 API를 부팅 할 수 있지만, 완전한 기능을 갖춘 CLI를 제공하는 플러그인 및 Ruby on Rails 또는 Django와 같이 작동하는 다른 도구를 다운로드하는 옵션도 있습니다. 현재 기본 해싱 알고리즘을 사용하거나 몇 줄의 코드로 자신의 사용을 사용하여 교체 할 수있는 매우 쉽게 사용하기 쉬운 인증 모듈이 있습니다.
상자 밖으로 Async/Await를 지원합니다.
이 프로젝트는 현재 상태에서 이러한 모든 목표를 달성하지는 못하지만 작업 중입니다. 기고에 관심이있는 사람들이있을 수 있으므로 현재 Beta 버전을 PYPI에 게시 할 수있는 사람들이있을 경우 여기에 게시하고 있습니다. 사랑스러운 하루 되세요 :)
나는 또한 이것을 위해 CLI 도구를 연구하고 있습니다. 여전히 사용하고 싶은 기본 아키텍처 및 모듈로 골격/실험을하고 있습니다. 현재 설정 작업은 지점 아키텍처 세트에 있습니다. 이와 관련된 다른 저장소가 있습니다 : Bergamot, Buddha 's Hand 및 Key Limes.
다음과 같은 링크는 다음과 같습니다. https://github.com/noraa-july-stoke/key-limes https://github.com/noraa-july-stoke/buddhas hand
I use emojis for commit messages. Here's what they mean:
# for updating any readmes or roadmaps
: <relevant comment>:
# for new features
: <relevant comment>:
# for debug commits.
?? : <relevant comment>:
# for refactoring
? : <relevant comment>:
# for documentation
? : <relevant comment>
# branch initialization commit
? : <relevant comment>
# standard merge message
? : <relevant comment>
from tangerine import Tangerine , Ctx , Router
from pymongo import MongoClient
from key_lime import KeyLime
from yuzu import Yuzu
import json
import jwt
app = Tangerine ()
client = MongoClient ( 'mongodb://localhost:27017/' )
keychain = KeyLime ({
"SECRET_KEY" : "ILOVECATS" ,
})
# Initialize Yuzu with the db funcs.
def get_user_by_email ( email ):
db = client [ 'mydatabase' ]
users = db [ 'users' ]
query = { 'email' : email }
user = users . find_one ( query )
if user :
user [ '_id' ] = str ( user [ '_id' ]) # Convert ObjectId to string
return user
def create_user ( user_data ):
db = client [ 'mydatabase' ]
users = db [ 'users' ]
result = users . insert_one ( user_data )
if result . inserted_id :
user_data [ '_id' ] = str ( result . inserted_id ) # Convert ObjectId to string
return user_data
auth = Yuzu ( keychain , get_user_by_email , create_user )
# serve static files to any request not starting with /api
app . static ( '^/(?!api).*$' , './public' )
# This is how you define a custom middleware.
def hello_middle ( ctx : Ctx , next ) -> None :
print ( "Hello from middleware!" )
ctx . hello_message = json . dumps ({ "message" : "Hello from middleware!" })
next ()
# ==================== AUTH HANDLERS ====================
def api_hello_world ( ctx : Ctx ) -> None :
ctx . body = ctx . hello_message
ctx . send ( 200 , content_type = 'application/json' )
def signup ( ctx : Ctx ) -> None :
user_data = ctx . request . body
created_user = auth . sign_up ( user_data )
if created_user :
ctx . body = json . dumps ( created_user )
ctx . send ( 201 , content_type = 'application/json' )
else :
ctx . send ( 500 , content_type = 'application/json' )
def login ( ctx : Ctx ) -> None :
user_data = ctx . request . body
email = user_data [ 'email' ]
password = user_data [ 'password' ]
user_id , token = auth . login ( email , password )
if token :
ctx . body = json . dumps ({ "message" : "Logged in successfully" , "token" : token })
ctx . set_res_header ( "Set-Cookie" , f"auth_token= { token } ; HttpOnly; Path=/" )
ctx . send ( 200 , content_type = 'application/json' )
# Set the token as a cookie or in the response headers
else :
ctx . body = json . dumps ({ "message" : "Invalid credentials" })
ctx . send ( 401 , content_type = 'application/json' )
def logout ( ctx : Ctx ) -> None :
auth . logout ()
ctx . body = json . dumps ({ "message" : "Logged out successfully" })
ctx . send ( 200 , content_type = 'application/json' )
@ Router . auth_required
def get_protected_content ( ctx : Ctx ) -> None :
ctx . body = json . dumps ({ "message" : "This is protected content. Only authenticated users can see this. I hope you feel special ???." })
ctx . send ( 200 , content_type = 'application/json' )
# ==================== API ROUTES ====================
# if you need to bind more variables to your handler, you can pass in a closure
api_router = Router ( prefix = '/api' )
api_router . post ( '/logout' , logout )
api_router . post ( '/login' , login )
api_router . post ( '/signup' , signup )
api_router . get ( '/hello' , api_hello_world )
# api_router.get('/users', get_and_delete_users)
api_router . get ( '/protected' , get_protected_content )
app . use ( hello_middle )
app . use ( auth . jwt_middleware )
app . use_router ( api_router )
app . start () from tangerine import Tangerine , Ctx , Router
from key_lime import KeyLime
from yuzu import Yuzu
import json
import jwt
import psycopg2
# ==================== If you don't have schema and tables this part will set that up for you ====================
conn = psycopg2 . connect ( "postgresql://postgres:<POSTGRESPASSWORD>@localhost:5432/local_development" )
# Open a cursor to perform database operations
cur = conn . cursor ()
# Create schema if it's not there
cur . execute ( """
CREATE SCHEMA IF NOT EXISTS tangerine;
""" )
# Execute a command: this creates a new table named 'users'
cur . execute ( """
CREATE TABLE IF NOT EXISTS tangerine.users (
id SERIAL PRIMARY KEY,
email VARCHAR(255) UNIQUE NOT NULL,
password VARCHAR(255) NOT NULL
)
""" )
# Commit the transaction
conn . commit ()
# Close the connection
cur . close ()
conn . close ()
# =================================================================================================================
app = Tangerine ()
keychain = KeyLime ({
"SECRET_KEY" : "ILOVECATS" ,
})
def get_user_by_email ( email ):
conn = psycopg2 . connect ( "postgresql://postgres:<POSTGRESPASSWORD>@localhost:5432/local_development" )
cur = conn . cursor ()
cur . execute ( "SELECT * FROM tangerine.users WHERE email = %s" , ( email ,))
user = cur . fetchone ()
cur . close ()
conn . close ()
if user :
return { '_id' : user [ 0 ], 'email' : user [ 1 ], 'password' : user [ 2 ]}
else :
return None
def create_user ( user_data ):
conn = psycopg2 . connect ( "postgresql://postgres:<POSTGRESPASSWORD>@localhost:5432/local_development" )
cur = conn . cursor ()
cur . execute ( "INSERT INTO tangerine.users (email, password) VALUES (%s, %s) RETURNING id" , ( user_data [ 'email' ], user_data [ 'password' ]))
user_id = cur . fetchone ()[ 0 ]
conn . commit ()
cur . close ()
conn . close ()
return { '_id' : user_id , 'email' : user_data [ 'email' ], 'password' : user_data [ 'password' ]}
auth = Yuzu ( keychain , get_user_by_email , create_user )
# serve static files to any request not starting with /api
app . static ( '^/(?!api).*$' , './public' )
def signup ( ctx : Ctx ) -> None :
user_data = ctx . request . body
created_user = auth . sign_up ( user_data )
if created_user :
ctx . body = json . dumps ( created_user )
ctx . send ( 201 , content_type = 'application/json' )
else :
ctx . send ( 500 , content_type = 'application/json' )
def login ( ctx : Ctx ) -> None :
user_data = ctx . request . body
email = user_data [ 'email' ]
password = user_data [ 'password' ]
user_id , token = auth . login ( email , password )
if token :
ctx . body = json . dumps ({ "message" : "Logged in successfully" , "token" : token })
ctx . set_res_header ( "Set-Cookie" , f"auth_token= { token } ; HttpOnly; Path=/" )
ctx . send ( 200 , content_type = 'application/json' )
else :
ctx . body = json . dumps ({ "message" : "Invalid credentials" })
ctx . send ( 401 , content_type = 'application/json' )
def logout ( ctx : Ctx ) -> None :
auth . logout ()
ctx . body = json . dumps ({ "message" : "Logged out successfully" })
ctx . send ( 200 , content_type = 'application/json' )
@ Router . auth_required
def get_protected_content ( ctx : Ctx ) -> None :
ctx . body = json . dumps ({ "message" : "This is protected content. Only authenticated users can see this. I hope you feel special ???." })
ctx . send ( 200 , content_type = 'application/json' )
# ==================== API ROUTES ====================
api_router = Router ( prefix = '/api' )
api_router . post ( '/logout' , logout )
api_router . post ( '/login' , login )
api_router . post ( '/signup' , signup )
api_router . get ( '/protected' , get_protected_content )
app . use ( auth . jwt_middleware )
app . use_router ( api_router )
app . start ()
## More Details TBD
## This readme is a work in progress so keep an eye out for more documentation/outlines of the project.