From f7f5934daab30f5c5a54a8d460734c01a0cb2650 Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Thu, 28 Dec 2023 18:22:15 +0800 Subject: [PATCH 01/29] feat: init server code and deploy script --- Makefile | 10 +++++++ deploy/Dockerfile | 16 ++++++++++++ deploy/docker-compose.yml | 55 +++++++++++++++++++++++++++++++++++++++ requirements.txt | 4 +++ server/app.py | 32 +++++++++++++++++++++++ server/server.py | 12 +++++++++ 6 files changed, 129 insertions(+) create mode 100644 Makefile create mode 100644 deploy/Dockerfile create mode 100644 deploy/docker-compose.yml create mode 100644 server/app.py create mode 100644 server/server.py diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..cddf7f55 --- /dev/null +++ b/Makefile @@ -0,0 +1,10 @@ +.PHONY: build + +build: + @echo "Building..." + docker build -t gitmaya -f deploy/Dockerfile . + @echo "Done." + + +FORCE: + diff --git a/deploy/Dockerfile b/deploy/Dockerfile new file mode 100644 index 00000000..4a48af88 --- /dev/null +++ b/deploy/Dockerfile @@ -0,0 +1,16 @@ +FROM python:3.8-bullseye + +RUN sed -i "s@http://deb.debian.org@http://mirrors.aliyun.com@g" /etc/apt/sources.list +RUN sed -i "s@http://security.debian.org@http://mirrors.aliyun.com@g" /etc/apt/sources.list +RUN ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime + +ADD ./requirements.txt /tmp/requirements.txt + +RUN apt-get update && apt-get install -y libcurl4-openssl-dev libffi-dev libxml2-dev libmariadb-dev g++\ + && pip install -r /tmp/requirements.txt && pip install gunicorn gevent + +ADD ./server /server + +WORKDIR /server + +CMD ["gunicorn", "--worker-class=gevent", "--workers", "1", "--bind", "0.0.0.0:8888", "-t", "600", "--keep-alive", "60", "--log-level=debug", "server:app"] diff --git a/deploy/docker-compose.yml b/deploy/docker-compose.yml new file mode 100644 index 00000000..a3631f38 --- /dev/null +++ b/deploy/docker-compose.yml @@ -0,0 +1,55 @@ +version: '2' +services: + gitmaya: + restart: always + image: gitmaya + volumes: + - .env:/server/.env + ports: + - "8888" + environment: + - VIRTUAL_HOST=api.gitmaya.com + + redis: + restart: always + image: redis:alpine + ports: + - "6379" + + rabbitmq: + restart: always + image: rabbitmq:3.7-management-alpine + environment: + RABBITMQ_ERLANG_COOKIE: "SWQOKODSQALRPCLNMEQG" + RABBITMQ_DEFAULT_USER: "rabbitmq" + RABBITMQ_DEFAULT_PASS: "rabbitmq" + RABBITMQ_DEFAULT_VHOST: "/" + VIRTUAL_HOST: rabbitmq-manager.connectai.ai + VIRTUAL_PORT: 15672 + ports: + - "15672" + - "5672" + volumes: + - ./data/rabbitmq:/data/mnesia + + mysql: + restart: always + image: mysql:5.7 + volumes: + - ./data/mysql/data:/var/lib/mysql + - ./data/mysql/conf.d:/etc/mysql/conf.d + environment: + MYSQL_ROOT_PASSWORD: 'connectai2023' + MYSQL_DATABASE: 'connectai-manager' + TZ: 'Asia/Shanghai' + ports: + - "3306" + command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci + + proxy: + image: jwilder/nginx-proxy:alpine + ports: + - "8000:80" + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro + - ./dist:/var/www/html:ro diff --git a/requirements.txt b/requirements.txt index e39c0847..6df27364 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,6 @@ # This file is @generated by PDM. # Please do not edit it manually. + +python-dotenv==1.0.0 +ca-lark-oauth==0.0.4 +ca-lark-webhook==0.0.3 diff --git a/server/app.py b/server/app.py new file mode 100644 index 00000000..c61c8179 --- /dev/null +++ b/server/app.py @@ -0,0 +1,32 @@ +import os +from connectai.lark.oauth import Server as OauthServer +from connectai.lark.sdk import Bot, MarketBot +from connectai.lark.webhook import LarkServer +from connectai.storage import ExpiredDictStorage + + +def get_app(): + hook = LarkServer() + oauth = OauthServer() + bot = Bot( + app_id=os.environ.get("APP_ID"), + app_secret=os.environ.get("APP_SECRET"), + encrypt_key=os.environ.get("ENCRYPT_KEY"), + verification_token=os.environ.get("VERIFICATION_TOKEN") + ) + @hook.on_bot_message(message_type="text", bot=bot) + def on_text_message(bot, message_id, content, *args, **kwargs): + text = content["text"] + print("reply_text", message_id, text) + bot.reply_text(message_id, "reply: " + text) + + @oauth.on_bot_event(event_type="oauth:user_info", bot=bot) + def on_oauth_user_info(bot, event_id, user_info, *args, **kwargs): + # oauth user_info + print("oauth", user_info) + return user_info + + app = oauth.get_app() + app.register_blueprint(hook.get_blueprint()) + return app + diff --git a/server/server.py b/server/server.py new file mode 100644 index 00000000..76eaab77 --- /dev/null +++ b/server/server.py @@ -0,0 +1,12 @@ +import os +from dotenv import find_dotenv, load_dotenv +from app import get_app + +load_dotenv(find_dotenv()) +app = get_app() + + +if __name__ == "__main__": + # gunicorn -w 1 -b :8888 "server:app" + app.run(host=os.environ.get("HOST", "0.0.0.0"), port=os.environ.get("PORT", 8888)) + From d37593fe57044e65437da7f50103e18aecd61ab6 Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Thu, 28 Dec 2023 18:22:15 +0800 Subject: [PATCH 02/29] feat: init server code and deploy script. --- Makefile | 10 +++++++ deploy/Dockerfile | 16 ++++++++++++ deploy/docker-compose.yml | 55 +++++++++++++++++++++++++++++++++++++++ requirements.txt | 4 +++ server/app.py | 32 +++++++++++++++++++++++ server/server.py | 12 +++++++++ 6 files changed, 129 insertions(+) create mode 100644 Makefile create mode 100644 deploy/Dockerfile create mode 100644 deploy/docker-compose.yml create mode 100644 server/app.py create mode 100644 server/server.py diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..cddf7f55 --- /dev/null +++ b/Makefile @@ -0,0 +1,10 @@ +.PHONY: build + +build: + @echo "Building..." + docker build -t gitmaya -f deploy/Dockerfile . + @echo "Done." + + +FORCE: + diff --git a/deploy/Dockerfile b/deploy/Dockerfile new file mode 100644 index 00000000..4a48af88 --- /dev/null +++ b/deploy/Dockerfile @@ -0,0 +1,16 @@ +FROM python:3.8-bullseye + +RUN sed -i "s@http://deb.debian.org@http://mirrors.aliyun.com@g" /etc/apt/sources.list +RUN sed -i "s@http://security.debian.org@http://mirrors.aliyun.com@g" /etc/apt/sources.list +RUN ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime + +ADD ./requirements.txt /tmp/requirements.txt + +RUN apt-get update && apt-get install -y libcurl4-openssl-dev libffi-dev libxml2-dev libmariadb-dev g++\ + && pip install -r /tmp/requirements.txt && pip install gunicorn gevent + +ADD ./server /server + +WORKDIR /server + +CMD ["gunicorn", "--worker-class=gevent", "--workers", "1", "--bind", "0.0.0.0:8888", "-t", "600", "--keep-alive", "60", "--log-level=debug", "server:app"] diff --git a/deploy/docker-compose.yml b/deploy/docker-compose.yml new file mode 100644 index 00000000..a3631f38 --- /dev/null +++ b/deploy/docker-compose.yml @@ -0,0 +1,55 @@ +version: '2' +services: + gitmaya: + restart: always + image: gitmaya + volumes: + - .env:/server/.env + ports: + - "8888" + environment: + - VIRTUAL_HOST=api.gitmaya.com + + redis: + restart: always + image: redis:alpine + ports: + - "6379" + + rabbitmq: + restart: always + image: rabbitmq:3.7-management-alpine + environment: + RABBITMQ_ERLANG_COOKIE: "SWQOKODSQALRPCLNMEQG" + RABBITMQ_DEFAULT_USER: "rabbitmq" + RABBITMQ_DEFAULT_PASS: "rabbitmq" + RABBITMQ_DEFAULT_VHOST: "/" + VIRTUAL_HOST: rabbitmq-manager.connectai.ai + VIRTUAL_PORT: 15672 + ports: + - "15672" + - "5672" + volumes: + - ./data/rabbitmq:/data/mnesia + + mysql: + restart: always + image: mysql:5.7 + volumes: + - ./data/mysql/data:/var/lib/mysql + - ./data/mysql/conf.d:/etc/mysql/conf.d + environment: + MYSQL_ROOT_PASSWORD: 'connectai2023' + MYSQL_DATABASE: 'connectai-manager' + TZ: 'Asia/Shanghai' + ports: + - "3306" + command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci + + proxy: + image: jwilder/nginx-proxy:alpine + ports: + - "8000:80" + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro + - ./dist:/var/www/html:ro diff --git a/requirements.txt b/requirements.txt index e39c0847..6df27364 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,6 @@ # This file is @generated by PDM. # Please do not edit it manually. + +python-dotenv==1.0.0 +ca-lark-oauth==0.0.4 +ca-lark-webhook==0.0.3 diff --git a/server/app.py b/server/app.py new file mode 100644 index 00000000..c61c8179 --- /dev/null +++ b/server/app.py @@ -0,0 +1,32 @@ +import os +from connectai.lark.oauth import Server as OauthServer +from connectai.lark.sdk import Bot, MarketBot +from connectai.lark.webhook import LarkServer +from connectai.storage import ExpiredDictStorage + + +def get_app(): + hook = LarkServer() + oauth = OauthServer() + bot = Bot( + app_id=os.environ.get("APP_ID"), + app_secret=os.environ.get("APP_SECRET"), + encrypt_key=os.environ.get("ENCRYPT_KEY"), + verification_token=os.environ.get("VERIFICATION_TOKEN") + ) + @hook.on_bot_message(message_type="text", bot=bot) + def on_text_message(bot, message_id, content, *args, **kwargs): + text = content["text"] + print("reply_text", message_id, text) + bot.reply_text(message_id, "reply: " + text) + + @oauth.on_bot_event(event_type="oauth:user_info", bot=bot) + def on_oauth_user_info(bot, event_id, user_info, *args, **kwargs): + # oauth user_info + print("oauth", user_info) + return user_info + + app = oauth.get_app() + app.register_blueprint(hook.get_blueprint()) + return app + diff --git a/server/server.py b/server/server.py new file mode 100644 index 00000000..76eaab77 --- /dev/null +++ b/server/server.py @@ -0,0 +1,12 @@ +import os +from dotenv import find_dotenv, load_dotenv +from app import get_app + +load_dotenv(find_dotenv()) +app = get_app() + + +if __name__ == "__main__": + # gunicorn -w 1 -b :8888 "server:app" + app.run(host=os.environ.get("HOST", "0.0.0.0"), port=os.environ.get("PORT", 8888)) + From 7634e237ce277ab17d48540b98d32e7b9fbc96b7 Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Thu, 28 Dec 2023 19:28:42 +0800 Subject: [PATCH 03/29] add package by pdm --- pdm.lock | 316 ++++++++++++++++++++++++++++++++++++++++++++++- pyproject.toml | 6 +- requirements.txt | 21 +++- 3 files changed, 338 insertions(+), 5 deletions(-) diff --git a/pdm.lock b/pdm.lock index f34cebf1..d6a208cb 100644 --- a/pdm.lock +++ b/pdm.lock @@ -3,6 +3,316 @@ [metadata] groups = ["default"] -strategy = ["cross_platform", "inherit_metadata"] -lock_version = "4.4.1" -content_hash = "sha256:283d196fda874e154c8cb9a8d9c42e4fbb7d4740fea54df382447ec3c49bb5ce" +strategy = ["cross_platform"] +lock_version = "4.4" +content_hash = "sha256:e804c28637907a7db21acfb4cf9cd6516d4de53e9d951b6b8e5a106d82d01871" + +[[package]] +name = "anyio" +version = "4.2.0" +requires_python = ">=3.8" +summary = "High level compatibility layer for multiple asynchronous event loop implementations" +dependencies = [ + "exceptiongroup>=1.0.2; python_version < \"3.11\"", + "idna>=2.8", + "sniffio>=1.1", + "typing-extensions>=4.1; python_version < \"3.11\"", +] +files = [ + {file = "anyio-4.2.0-py3-none-any.whl", hash = "sha256:745843b39e829e108e518c489b31dc757de7d2131d53fac32bd8df268227bfee"}, + {file = "anyio-4.2.0.tar.gz", hash = "sha256:e1875bb4b4e2de1669f4bc7869b6d3f54231cdced71605e6e64c9be77e3be50f"}, +] + +[[package]] +name = "blinker" +version = "1.7.0" +requires_python = ">=3.8" +summary = "Fast, simple object-to-object and broadcast signaling" +files = [ + {file = "blinker-1.7.0-py3-none-any.whl", hash = "sha256:c3f865d4d54db7abc53758a01601cf343fe55b84c1de4e3fa910e420b438d5b9"}, + {file = "blinker-1.7.0.tar.gz", hash = "sha256:e6820ff6fa4e4d1d8e2747c2283749c3f547e4fee112b98555cdcdae32996182"}, +] + +[[package]] +name = "ca-lark-oauth" +version = "0.0.4" +requires_python = ">=3.8" +summary = "lark(feishu) oauth webhook" +dependencies = [ + "ca-lark-sdk", + "flask", +] +files = [ + {file = "ca-lark-oauth-0.0.4.tar.gz", hash = "sha256:71926c700da858960143640b102d1c0f74777e087107b4648cf079f74e34c208"}, +] + +[[package]] +name = "ca-lark-sdk" +version = "0.0.7" +requires_python = ">=3.8" +summary = "lark(feishu) client" +dependencies = [ + "httpx", + "pycryptodome", +] +files = [ + {file = "ca-lark-sdk-0.0.7.tar.gz", hash = "sha256:07374e6cfeb97d96aed1358becc09529a66e292d50a94bb3b904306c7d2b6c93"}, +] + +[[package]] +name = "ca-lark-webhook" +version = "0.0.3" +requires_python = ">=3.8" +summary = "lark(feishu) client" +dependencies = [ + "ca-lark-sdk", + "flask", +] +files = [ + {file = "ca-lark-webhook-0.0.3.tar.gz", hash = "sha256:79d6da1ab96b207b542b8a20eabb0d8dc6563f87b84948d468183337f543cf22"}, +] + +[[package]] +name = "certifi" +version = "2023.11.17" +requires_python = ">=3.6" +summary = "Python package for providing Mozilla's CA Bundle." +files = [ + {file = "certifi-2023.11.17-py3-none-any.whl", hash = "sha256:e036ab49d5b79556f99cfc2d9320b34cfbe5be05c5871b51de9329f0603b0474"}, + {file = "certifi-2023.11.17.tar.gz", hash = "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1"}, +] + +[[package]] +name = "click" +version = "8.1.7" +requires_python = ">=3.7" +summary = "Composable command line interface toolkit" +dependencies = [ + "colorama; platform_system == \"Windows\"", +] +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[[package]] +name = "colorama" +version = "0.4.6" +requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +summary = "Cross-platform colored terminal text." +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "exceptiongroup" +version = "1.2.0" +requires_python = ">=3.7" +summary = "Backport of PEP 654 (exception groups)" +files = [ + {file = "exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14"}, + {file = "exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"}, +] + +[[package]] +name = "flask" +version = "3.0.0" +requires_python = ">=3.8" +summary = "A simple framework for building complex web applications." +dependencies = [ + "Jinja2>=3.1.2", + "Werkzeug>=3.0.0", + "blinker>=1.6.2", + "click>=8.1.3", + "itsdangerous>=2.1.2", +] +files = [ + {file = "flask-3.0.0-py3-none-any.whl", hash = "sha256:21128f47e4e3b9d597a3e8521a329bf56909b690fcc3fa3e477725aa81367638"}, + {file = "flask-3.0.0.tar.gz", hash = "sha256:cfadcdb638b609361d29ec22360d6070a77d7463dcb3ab08d2c2f2f168845f58"}, +] + +[[package]] +name = "h11" +version = "0.14.0" +requires_python = ">=3.7" +summary = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" +files = [ + {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, + {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, +] + +[[package]] +name = "httpcore" +version = "1.0.2" +requires_python = ">=3.8" +summary = "A minimal low-level HTTP client." +dependencies = [ + "certifi", + "h11<0.15,>=0.13", +] +files = [ + {file = "httpcore-1.0.2-py3-none-any.whl", hash = "sha256:096cc05bca73b8e459a1fc3dcf585148f63e534eae4339559c9b8a8d6399acc7"}, + {file = "httpcore-1.0.2.tar.gz", hash = "sha256:9fc092e4799b26174648e54b74ed5f683132a464e95643b226e00c2ed2fa6535"}, +] + +[[package]] +name = "httpx" +version = "0.26.0" +requires_python = ">=3.8" +summary = "The next generation HTTP client." +dependencies = [ + "anyio", + "certifi", + "httpcore==1.*", + "idna", + "sniffio", +] +files = [ + {file = "httpx-0.26.0-py3-none-any.whl", hash = "sha256:8915f5a3627c4d47b73e8202457cb28f1266982d1159bd5779d86a80c0eab1cd"}, + {file = "httpx-0.26.0.tar.gz", hash = "sha256:451b55c30d5185ea6b23c2c793abf9bb237d2a7dfb901ced6ff69ad37ec1dfaf"}, +] + +[[package]] +name = "idna" +version = "3.6" +requires_python = ">=3.5" +summary = "Internationalized Domain Names in Applications (IDNA)" +files = [ + {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, + {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, +] + +[[package]] +name = "itsdangerous" +version = "2.1.2" +requires_python = ">=3.7" +summary = "Safely pass data to untrusted environments and back." +files = [ + {file = "itsdangerous-2.1.2-py3-none-any.whl", hash = "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44"}, + {file = "itsdangerous-2.1.2.tar.gz", hash = "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a"}, +] + +[[package]] +name = "jinja2" +version = "3.1.2" +requires_python = ">=3.7" +summary = "A very fast and expressive template engine." +dependencies = [ + "MarkupSafe>=2.0", +] +files = [ + {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, + {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, +] + +[[package]] +name = "markupsafe" +version = "2.1.3" +requires_python = ">=3.7" +summary = "Safely add untrusted strings to HTML/XML markup." +files = [ + {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-win32.whl", hash = "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb"}, + {file = "MarkupSafe-2.1.3.tar.gz", hash = "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad"}, +] + +[[package]] +name = "pycryptodome" +version = "3.19.1" +requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +summary = "Cryptographic library for Python" +files = [ + {file = "pycryptodome-3.19.1-cp35-abi3-macosx_10_9_universal2.whl", hash = "sha256:67939a3adbe637281c611596e44500ff309d547e932c449337649921b17b6297"}, + {file = "pycryptodome-3.19.1-cp35-abi3-macosx_10_9_x86_64.whl", hash = "sha256:11ddf6c9b52116b62223b6a9f4741bc4f62bb265392a4463282f7f34bb287180"}, + {file = "pycryptodome-3.19.1-cp35-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e3e6f89480616781d2a7f981472d0cdb09b9da9e8196f43c1234eff45c915766"}, + {file = "pycryptodome-3.19.1-cp35-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:27e1efcb68993b7ce5d1d047a46a601d41281bba9f1971e6be4aa27c69ab8065"}, + {file = "pycryptodome-3.19.1-cp35-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c6273ca5a03b672e504995529b8bae56da0ebb691d8ef141c4aa68f60765700"}, + {file = "pycryptodome-3.19.1-cp35-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:b0bfe61506795877ff974f994397f0c862d037f6f1c0bfc3572195fc00833b96"}, + {file = "pycryptodome-3.19.1-cp35-abi3-musllinux_1_1_i686.whl", hash = "sha256:f34976c5c8eb79e14c7d970fb097482835be8d410a4220f86260695ede4c3e17"}, + {file = "pycryptodome-3.19.1-cp35-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:7c9e222d0976f68d0cf6409cfea896676ddc1d98485d601e9508f90f60e2b0a2"}, + {file = "pycryptodome-3.19.1-cp35-abi3-win32.whl", hash = "sha256:4805e053571140cb37cf153b5c72cd324bb1e3e837cbe590a19f69b6cf85fd03"}, + {file = "pycryptodome-3.19.1-cp35-abi3-win_amd64.whl", hash = "sha256:a470237ee71a1efd63f9becebc0ad84b88ec28e6784a2047684b693f458f41b7"}, + {file = "pycryptodome-3.19.1-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:ed932eb6c2b1c4391e166e1a562c9d2f020bfff44a0e1b108f67af38b390ea89"}, + {file = "pycryptodome-3.19.1-pp27-pypy_73-win32.whl", hash = "sha256:81e9d23c0316fc1b45d984a44881b220062336bbdc340aa9218e8d0656587934"}, + {file = "pycryptodome-3.19.1-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:37e531bf896b70fe302f003d3be5a0a8697737a8d177967da7e23eff60d6483c"}, + {file = "pycryptodome-3.19.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cd4e95b0eb4b28251c825fe7aa941fe077f993e5ca9b855665935b86fbb1cc08"}, + {file = "pycryptodome-3.19.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c22c80246c3c880c6950d2a8addf156cee74ec0dc5757d01e8e7067a3c7da015"}, + {file = "pycryptodome-3.19.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:e70f5c839c7798743a948efa2a65d1fe96bb397fe6d7f2bde93d869fe4f0ad69"}, + {file = "pycryptodome-3.19.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:6c3df3613592ea6afaec900fd7189d23c8c28b75b550254f4bd33fe94acb84b9"}, + {file = "pycryptodome-3.19.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:08b445799d571041765e7d5c9ca09c5d3866c2f22eeb0dd4394a4169285184f4"}, + {file = "pycryptodome-3.19.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:954d156cd50130afd53f8d77f830fe6d5801bd23e97a69d358fed068f433fbfe"}, + {file = "pycryptodome-3.19.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:b7efd46b0b4ac869046e814d83244aeab14ef787f4850644119b1c8b0ec2d637"}, + {file = "pycryptodome-3.19.1.tar.gz", hash = "sha256:8ae0dd1bcfada451c35f9e29a3e5db385caabc190f98e4a80ad02a61098fb776"}, +] + +[[package]] +name = "python-dotenv" +version = "1.0.0" +requires_python = ">=3.8" +summary = "Read key-value pairs from a .env file and set them as environment variables" +files = [ + {file = "python-dotenv-1.0.0.tar.gz", hash = "sha256:a8df96034aae6d2d50a4ebe8216326c61c3eb64836776504fcca410e5937a3ba"}, + {file = "python_dotenv-1.0.0-py3-none-any.whl", hash = "sha256:f5971a9226b701070a4bf2c38c89e5a3f0d64de8debda981d1db98583009122a"}, +] + +[[package]] +name = "sniffio" +version = "1.3.0" +requires_python = ">=3.7" +summary = "Sniff out which async library your code is running under" +files = [ + {file = "sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"}, + {file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"}, +] + +[[package]] +name = "typing-extensions" +version = "4.9.0" +requires_python = ">=3.8" +summary = "Backported and Experimental Type Hints for Python 3.8+" +files = [ + {file = "typing_extensions-4.9.0-py3-none-any.whl", hash = "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd"}, + {file = "typing_extensions-4.9.0.tar.gz", hash = "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783"}, +] + +[[package]] +name = "werkzeug" +version = "3.0.1" +requires_python = ">=3.8" +summary = "The comprehensive WSGI web application library." +dependencies = [ + "MarkupSafe>=2.1.1", +] +files = [ + {file = "werkzeug-3.0.1-py3-none-any.whl", hash = "sha256:90a285dc0e42ad56b34e696398b8122ee4c681833fb35b8334a095d82c56da10"}, + {file = "werkzeug-3.0.1.tar.gz", hash = "sha256:507e811ecea72b18a404947aded4b3390e1db8f826b494d76550ef45bb3b1dcc"}, +] diff --git a/pyproject.toml b/pyproject.toml index 84b5b804..55c3340a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,11 @@ description = "Default template for PDM package" authors = [ {name = "ConnectAI", email = "hi@connectai-e.com"}, ] -dependencies = [] +dependencies = [ + "python-dotenv>=1.0.0", + "ca-lark-oauth>=0.0.4", + "ca-lark-webhook>=0.0.3", +] requires-python = ">=3.10" readme = "README.md" license = {text = "MIT"} diff --git a/requirements.txt b/requirements.txt index 6df27364..0ff0cc12 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,25 @@ # This file is @generated by PDM. # Please do not edit it manually. -python-dotenv==1.0.0 +anyio==4.2.0 +blinker==1.7.0 ca-lark-oauth==0.0.4 +ca-lark-sdk==0.0.7 ca-lark-webhook==0.0.3 +certifi==2023.11.17 +click==8.1.7 +colorama==0.4.6; platform_system == "Windows" +exceptiongroup==1.2.0; python_version < "3.11" +flask==3.0.0 +h11==0.14.0 +httpcore==1.0.2 +httpx==0.26.0 +idna==3.6 +itsdangerous==2.1.2 +Jinja2==3.1.2 +MarkupSafe==2.1.3 +pycryptodome==3.19.1 +python-dotenv==1.0.0 +sniffio==1.3.0 +typing-extensions==4.9.0; python_version < "3.11" +Werkzeug==3.0.1 From c9bc52ef3058218171c90afb8944c2f48407e130 Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Thu, 28 Dec 2023 20:38:13 +0800 Subject: [PATCH 04/29] feat: update --- pdm.lock | 6 ++--- pyproject.toml | 2 +- requirements.txt | 2 +- server/app.py | 58 ++++++++++++++++++++++++++---------------------- server/env.py | 3 +++ server/server.py | 8 ++----- 6 files changed, 41 insertions(+), 38 deletions(-) create mode 100644 server/env.py diff --git a/pdm.lock b/pdm.lock index d6a208cb..7a464ca2 100644 --- a/pdm.lock +++ b/pdm.lock @@ -5,7 +5,7 @@ groups = ["default"] strategy = ["cross_platform"] lock_version = "4.4" -content_hash = "sha256:e804c28637907a7db21acfb4cf9cd6516d4de53e9d951b6b8e5a106d82d01871" +content_hash = "sha256:c6d5001ad8007f670b048d2f4d38cf3bdcfc2b5188d7e7bb794c36e214176bcf" [[package]] name = "anyio" @@ -35,7 +35,7 @@ files = [ [[package]] name = "ca-lark-oauth" -version = "0.0.4" +version = "0.0.5" requires_python = ">=3.8" summary = "lark(feishu) oauth webhook" dependencies = [ @@ -43,7 +43,7 @@ dependencies = [ "flask", ] files = [ - {file = "ca-lark-oauth-0.0.4.tar.gz", hash = "sha256:71926c700da858960143640b102d1c0f74777e087107b4648cf079f74e34c208"}, + {file = "ca-lark-oauth-0.0.5.tar.gz", hash = "sha256:dc6b7c583642b3139716b2fd430d53b777d71a867483687c3f128f5992bd802f"}, ] [[package]] diff --git a/pyproject.toml b/pyproject.toml index 55c3340a..2b2909f9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,7 @@ authors = [ ] dependencies = [ "python-dotenv>=1.0.0", - "ca-lark-oauth>=0.0.4", + "ca-lark-oauth==0.0.5", "ca-lark-webhook>=0.0.3", ] requires-python = ">=3.10" diff --git a/requirements.txt b/requirements.txt index 0ff0cc12..b2e5575a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,7 +3,7 @@ anyio==4.2.0 blinker==1.7.0 -ca-lark-oauth==0.0.4 +ca-lark-oauth==0.0.5 ca-lark-sdk==0.0.7 ca-lark-webhook==0.0.3 certifi==2023.11.17 diff --git a/server/app.py b/server/app.py index c61c8179..3a6286c7 100644 --- a/server/app.py +++ b/server/app.py @@ -1,32 +1,36 @@ import os + from connectai.lark.oauth import Server as OauthServer from connectai.lark.sdk import Bot, MarketBot from connectai.lark.webhook import LarkServer -from connectai.storage import ExpiredDictStorage - - -def get_app(): - hook = LarkServer() - oauth = OauthServer() - bot = Bot( - app_id=os.environ.get("APP_ID"), - app_secret=os.environ.get("APP_SECRET"), - encrypt_key=os.environ.get("ENCRYPT_KEY"), - verification_token=os.environ.get("VERIFICATION_TOKEN") - ) - @hook.on_bot_message(message_type="text", bot=bot) - def on_text_message(bot, message_id, content, *args, **kwargs): - text = content["text"] - print("reply_text", message_id, text) - bot.reply_text(message_id, "reply: " + text) - - @oauth.on_bot_event(event_type="oauth:user_info", bot=bot) - def on_oauth_user_info(bot, event_id, user_info, *args, **kwargs): - # oauth user_info - print("oauth", user_info) - return user_info - - app = oauth.get_app() - app.register_blueprint(hook.get_blueprint()) - return app +from flask import Flask + +app = Flask(__name__) + +hook = LarkServer(prefix="/api/feishu/hook") +oauth = OauthServer(prefix="/api/feishu/oauth") + +bot = Bot( + app_id=os.environ.get("APP_ID"), + app_secret=os.environ.get("APP_SECRET"), + encrypt_key=os.environ.get("ENCRYPT_KEY"), + verification_token=os.environ.get("VERIFICATION_TOKEN"), +) + + +@hook.on_bot_message(message_type="text", bot=bot) +def on_text_message(bot, message_id, content, *args, **kwargs): + text = content["text"] + print("reply_text", message_id, text) + bot.reply_text(message_id, "reply: " + text) + + +@oauth.on_bot_event(event_type="oauth:user_info", bot=bot) +def on_oauth_user_info(bot, event_id, user_info, *args, **kwargs): + # oauth user_info + print("oauth", user_info) + return user_info + +app.register_blueprint(oauth.get_blueprint()) +app.register_blueprint(hook.get_blueprint()) diff --git a/server/env.py b/server/env.py new file mode 100644 index 00000000..3d5905ba --- /dev/null +++ b/server/env.py @@ -0,0 +1,3 @@ +from dotenv import find_dotenv, load_dotenv + +load_dotenv(find_dotenv()) diff --git a/server/server.py b/server/server.py index 76eaab77..b9f1902e 100644 --- a/server/server.py +++ b/server/server.py @@ -1,12 +1,8 @@ import os -from dotenv import find_dotenv, load_dotenv -from app import get_app - -load_dotenv(find_dotenv()) -app = get_app() +import env +from app import app if __name__ == "__main__": # gunicorn -w 1 -b :8888 "server:app" app.run(host=os.environ.get("HOST", "0.0.0.0"), port=os.environ.get("PORT", 8888)) - From d155e8afa93a1ed503b15ed414609fd21743061f Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Thu, 28 Dec 2023 21:07:55 +0800 Subject: [PATCH 05/29] feat: add session --- server/app.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/server/app.py b/server/app.py index 3a6286c7..5ac3ba3d 100644 --- a/server/app.py +++ b/server/app.py @@ -3,9 +3,10 @@ from connectai.lark.oauth import Server as OauthServer from connectai.lark.sdk import Bot, MarketBot from connectai.lark.webhook import LarkServer -from flask import Flask +from flask import Flask, session app = Flask(__name__) +app.secret_key = (os.environ.get("SECRET_KEY"),) hook = LarkServer(prefix="/api/feishu/hook") oauth = OauthServer(prefix="/api/feishu/oauth") @@ -29,6 +30,8 @@ def on_text_message(bot, message_id, content, *args, **kwargs): def on_oauth_user_info(bot, event_id, user_info, *args, **kwargs): # oauth user_info print("oauth", user_info) + # TODO + session["user_id"] = user_info["union_id"] return user_info From 45602e9aa136ed1d27d29d16058150eddde40219 Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Thu, 28 Dec 2023 21:27:44 +0800 Subject: [PATCH 06/29] feat: add model --- pdm.lock | 102 +++++++++++++++++++++++++++++++++++++- pyproject.toml | 2 + requirements.txt | 8 ++- server/app.py | 41 ++++----------- server/model/schema.py | 86 ++++++++++++++++++++++++++++++++ server/routes/__init__.py | 1 + server/routes/lark.py | 49 ++++++++++++++++++ server/server.py | 1 + 8 files changed, 255 insertions(+), 35 deletions(-) create mode 100644 server/model/schema.py create mode 100644 server/routes/__init__.py create mode 100644 server/routes/lark.py diff --git a/pdm.lock b/pdm.lock index 7a464ca2..8bd96cd1 100644 --- a/pdm.lock +++ b/pdm.lock @@ -5,7 +5,7 @@ groups = ["default"] strategy = ["cross_platform"] lock_version = "4.4" -content_hash = "sha256:c6d5001ad8007f670b048d2f4d38cf3bdcfc2b5188d7e7bb794c36e214176bcf" +content_hash = "sha256:d203557781e5ca17f80e94d60cc6fb8e435d752317eff4e3df384b5915c598fe" [[package]] name = "anyio" @@ -132,6 +132,68 @@ files = [ {file = "flask-3.0.0.tar.gz", hash = "sha256:cfadcdb638b609361d29ec22360d6070a77d7463dcb3ab08d2c2f2f168845f58"}, ] +[[package]] +name = "flask-cors" +version = "4.0.0" +summary = "A Flask extension adding a decorator for CORS support" +dependencies = [ + "Flask>=0.9", +] +files = [ + {file = "Flask-Cors-4.0.0.tar.gz", hash = "sha256:f268522fcb2f73e2ecdde1ef45e2fd5c71cc48fe03cffb4b441c6d1b40684eb0"}, + {file = "Flask_Cors-4.0.0-py2.py3-none-any.whl", hash = "sha256:bc3492bfd6368d27cfe79c7821df5a8a319e1a6d5eab277a3794be19bdc51783"}, +] + +[[package]] +name = "flask-sqlalchemy" +version = "3.1.1" +requires_python = ">=3.8" +summary = "Add SQLAlchemy support to your Flask application." +dependencies = [ + "flask>=2.2.5", + "sqlalchemy>=2.0.16", +] +files = [ + {file = "flask_sqlalchemy-3.1.1-py3-none-any.whl", hash = "sha256:4ba4be7f419dc72f4efd8802d69974803c37259dd42f3913b0dcf75c9447e0a0"}, + {file = "flask_sqlalchemy-3.1.1.tar.gz", hash = "sha256:e4b68bb881802dda1a7d878b2fc84c06d1ee57fb40b874d3dc97dabfa36b8312"}, +] + +[[package]] +name = "greenlet" +version = "3.0.3" +requires_python = ">=3.7" +summary = "Lightweight in-process concurrent programming" +files = [ + {file = "greenlet-3.0.3-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:9da2bd29ed9e4f15955dd1595ad7bc9320308a3b766ef7f837e23ad4b4aac31a"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d353cadd6083fdb056bb46ed07e4340b0869c305c8ca54ef9da3421acbdf6881"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dca1e2f3ca00b84a396bc1bce13dd21f680f035314d2379c4160c98153b2059b"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ed7fb269f15dc662787f4119ec300ad0702fa1b19d2135a37c2c4de6fadfd4a"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd4f49ae60e10adbc94b45c0b5e6a179acc1736cf7a90160b404076ee283cf83"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:73a411ef564e0e097dbe7e866bb2dda0f027e072b04da387282b02c308807405"}, + {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7f362975f2d179f9e26928c5b517524e89dd48530a0202570d55ad6ca5d8a56f"}, + {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:649dde7de1a5eceb258f9cb00bdf50e978c9db1b996964cd80703614c86495eb"}, + {file = "greenlet-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:68834da854554926fbedd38c76e60c4a2e3198c6fbed520b106a8986445caaf9"}, + {file = "greenlet-3.0.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:b1b5667cced97081bf57b8fa1d6bfca67814b0afd38208d52538316e9422fc61"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52f59dd9c96ad2fc0d5724107444f76eb20aaccb675bf825df6435acb7703559"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:afaff6cf5200befd5cec055b07d1c0a5a06c040fe5ad148abcd11ba6ab9b114e"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe754d231288e1e64323cfad462fcee8f0288654c10bdf4f603a39ed923bef33"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2797aa5aedac23af156bbb5a6aa2cd3427ada2972c828244eb7d1b9255846379"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7f009caad047246ed379e1c4dbcb8b020f0a390667ea74d2387be2998f58a22"}, + {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c5e1536de2aad7bf62e27baf79225d0d64360d4168cf2e6becb91baf1ed074f3"}, + {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:894393ce10ceac937e56ec00bb71c4c2f8209ad516e96033e4b3b1de270e200d"}, + {file = "greenlet-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:1ea188d4f49089fc6fb283845ab18a2518d279c7cd9da1065d7a84e991748728"}, + {file = "greenlet-3.0.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:70fb482fdf2c707765ab5f0b6655e9cfcf3780d8d87355a063547b41177599be"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4d1ac74f5c0c0524e4a24335350edad7e5f03b9532da7ea4d3c54d527784f2e"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:149e94a2dd82d19838fe4b2259f1b6b9957d5ba1b25640d2380bea9c5df37676"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15d79dd26056573940fcb8c7413d84118086f2ec1a8acdfa854631084393efcc"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b7db1ebff4ba09aaaeae6aa491daeb226c8150fc20e836ad00041bcb11230"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fcd2469d6a2cf298f198f0487e0a5b1a47a42ca0fa4dfd1b6862c999f018ebbf"}, + {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1f672519db1796ca0d8753f9e78ec02355e862d0998193038c7073045899f305"}, + {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2516a9957eed41dd8f1ec0c604f1cdc86758b587d964668b5b196a9db5bfcde6"}, + {file = "greenlet-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:bba5387a6975598857d86de9eac14210a49d554a77eb8261cc68b7d082f78ce2"}, + {file = "greenlet-3.0.3.tar.gz", hash = "sha256:43374442353259554ce33599da8b692d5aa96f8976d567d4badf263371fbe491"}, +] + [[package]] name = "h11" version = "0.14.0" @@ -294,6 +356,44 @@ files = [ {file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"}, ] +[[package]] +name = "sqlalchemy" +version = "2.0.23" +requires_python = ">=3.7" +summary = "Database Abstraction Library" +dependencies = [ + "greenlet!=0.4.17; platform_machine == \"aarch64\" or (platform_machine == \"ppc64le\" or (platform_machine == \"x86_64\" or (platform_machine == \"amd64\" or (platform_machine == \"AMD64\" or (platform_machine == \"win32\" or platform_machine == \"WIN32\")))))", + "typing-extensions>=4.2.0", +] +files = [ + {file = "SQLAlchemy-2.0.23-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:638c2c0b6b4661a4fd264f6fb804eccd392745c5887f9317feb64bb7cb03b3ea"}, + {file = "SQLAlchemy-2.0.23-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e3b5036aa326dc2df50cba3c958e29b291a80f604b1afa4c8ce73e78e1c9f01d"}, + {file = "SQLAlchemy-2.0.23-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:787af80107fb691934a01889ca8f82a44adedbf5ef3d6ad7d0f0b9ac557e0c34"}, + {file = "SQLAlchemy-2.0.23-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c14eba45983d2f48f7546bb32b47937ee2cafae353646295f0e99f35b14286ab"}, + {file = "SQLAlchemy-2.0.23-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0666031df46b9badba9bed00092a1ffa3aa063a5e68fa244acd9f08070e936d3"}, + {file = "SQLAlchemy-2.0.23-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:89a01238fcb9a8af118eaad3ffcc5dedaacbd429dc6fdc43fe430d3a941ff965"}, + {file = "SQLAlchemy-2.0.23-cp310-cp310-win32.whl", hash = "sha256:cabafc7837b6cec61c0e1e5c6d14ef250b675fa9c3060ed8a7e38653bd732ff8"}, + {file = "SQLAlchemy-2.0.23-cp310-cp310-win_amd64.whl", hash = "sha256:87a3d6b53c39cd173990de2f5f4b83431d534a74f0e2f88bd16eabb5667e65c6"}, + {file = "SQLAlchemy-2.0.23-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d5578e6863eeb998980c212a39106ea139bdc0b3f73291b96e27c929c90cd8e1"}, + {file = "SQLAlchemy-2.0.23-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:62d9e964870ea5ade4bc870ac4004c456efe75fb50404c03c5fd61f8bc669a72"}, + {file = "SQLAlchemy-2.0.23-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c80c38bd2ea35b97cbf7c21aeb129dcbebbf344ee01a7141016ab7b851464f8e"}, + {file = "SQLAlchemy-2.0.23-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75eefe09e98043cff2fb8af9796e20747ae870c903dc61d41b0c2e55128f958d"}, + {file = "SQLAlchemy-2.0.23-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bd45a5b6c68357578263d74daab6ff9439517f87da63442d244f9f23df56138d"}, + {file = "SQLAlchemy-2.0.23-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a86cb7063e2c9fb8e774f77fbf8475516d270a3e989da55fa05d08089d77f8c4"}, + {file = "SQLAlchemy-2.0.23-cp311-cp311-win32.whl", hash = "sha256:b41f5d65b54cdf4934ecede2f41b9c60c9f785620416e8e6c48349ab18643855"}, + {file = "SQLAlchemy-2.0.23-cp311-cp311-win_amd64.whl", hash = "sha256:9ca922f305d67605668e93991aaf2c12239c78207bca3b891cd51a4515c72e22"}, + {file = "SQLAlchemy-2.0.23-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d0f7fb0c7527c41fa6fcae2be537ac137f636a41b4c5a4c58914541e2f436b45"}, + {file = "SQLAlchemy-2.0.23-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7c424983ab447dab126c39d3ce3be5bee95700783204a72549c3dceffe0fc8f4"}, + {file = "SQLAlchemy-2.0.23-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f508ba8f89e0a5ecdfd3761f82dda2a3d7b678a626967608f4273e0dba8f07ac"}, + {file = "SQLAlchemy-2.0.23-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6463aa765cf02b9247e38b35853923edbf2f6fd1963df88706bc1d02410a5577"}, + {file = "SQLAlchemy-2.0.23-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e599a51acf3cc4d31d1a0cf248d8f8d863b6386d2b6782c5074427ebb7803bda"}, + {file = "SQLAlchemy-2.0.23-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fd54601ef9cc455a0c61e5245f690c8a3ad67ddb03d3b91c361d076def0b4c60"}, + {file = "SQLAlchemy-2.0.23-cp312-cp312-win32.whl", hash = "sha256:42d0b0290a8fb0165ea2c2781ae66e95cca6e27a2fbe1016ff8db3112ac1e846"}, + {file = "SQLAlchemy-2.0.23-cp312-cp312-win_amd64.whl", hash = "sha256:227135ef1e48165f37590b8bfc44ed7ff4c074bf04dc8d6f8e7f1c14a94aa6ca"}, + {file = "SQLAlchemy-2.0.23-py3-none-any.whl", hash = "sha256:31952bbc527d633b9479f5f81e8b9dfada00b91d6baba021a869095f1a97006d"}, + {file = "SQLAlchemy-2.0.23.tar.gz", hash = "sha256:c1bda93cbbe4aa2aa0aa8655c5aeda505cd219ff3e8da91d1d329e143e4aff69"}, +] + [[package]] name = "typing-extensions" version = "4.9.0" diff --git a/pyproject.toml b/pyproject.toml index 2b2909f9..3919f337 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,6 +9,8 @@ dependencies = [ "python-dotenv>=1.0.0", "ca-lark-oauth==0.0.5", "ca-lark-webhook>=0.0.3", + "flask-sqlalchemy>=3.1.1", + "flask-cors>=4.0.0", ] requires-python = ">=3.10" readme = "README.md" diff --git a/requirements.txt b/requirements.txt index b2e5575a..411b0b3d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,7 +10,10 @@ certifi==2023.11.17 click==8.1.7 colorama==0.4.6; platform_system == "Windows" exceptiongroup==1.2.0; python_version < "3.11" -flask==3.0.0 +Flask==3.0.0 +flask-cors==4.0.0 +flask-sqlalchemy==3.1.1 +greenlet==3.0.3; platform_machine == "win32" or platform_machine == "WIN32" or platform_machine == "AMD64" or platform_machine == "amd64" or platform_machine == "x86_64" or platform_machine == "ppc64le" or platform_machine == "aarch64" h11==0.14.0 httpcore==1.0.2 httpx==0.26.0 @@ -21,5 +24,6 @@ MarkupSafe==2.1.3 pycryptodome==3.19.1 python-dotenv==1.0.0 sniffio==1.3.0 -typing-extensions==4.9.0; python_version < "3.11" +sqlalchemy==2.0.23 +typing-extensions==4.9.0 Werkzeug==3.0.1 diff --git a/server/app.py b/server/app.py index 5ac3ba3d..c0ebda87 100644 --- a/server/app.py +++ b/server/app.py @@ -1,39 +1,16 @@ import os -from connectai.lark.oauth import Server as OauthServer -from connectai.lark.sdk import Bot, MarketBot -from connectai.lark.webhook import LarkServer from flask import Flask, session +from flask_cors import CORS +from flask_sqlalchemy import SQLAlchemy app = Flask(__name__) -app.secret_key = (os.environ.get("SECRET_KEY"),) - -hook = LarkServer(prefix="/api/feishu/hook") -oauth = OauthServer(prefix="/api/feishu/oauth") - -bot = Bot( - app_id=os.environ.get("APP_ID"), - app_secret=os.environ.get("APP_SECRET"), - encrypt_key=os.environ.get("ENCRYPT_KEY"), - verification_token=os.environ.get("VERIFICATION_TOKEN"), +app.secret_key = os.environ.get("SECRET_KEY") +db = SQLAlchemy(app, engine_options={"isolation_level": "AUTOCOMMIT"}) +CORS( + app, allow_headers=["Authorization", "X-Requested-With"], supports_credentials=True ) - -@hook.on_bot_message(message_type="text", bot=bot) -def on_text_message(bot, message_id, content, *args, **kwargs): - text = content["text"] - print("reply_text", message_id, text) - bot.reply_text(message_id, "reply: " + text) - - -@oauth.on_bot_event(event_type="oauth:user_info", bot=bot) -def on_oauth_user_info(bot, event_id, user_info, *args, **kwargs): - # oauth user_info - print("oauth", user_info) - # TODO - session["user_id"] = user_info["union_id"] - return user_info - - -app.register_blueprint(oauth.get_blueprint()) -app.register_blueprint(hook.get_blueprint()) +gunicorn_logger = logging.getLogger("gunicorn.error") +app.logger.handlers = gunicorn_logger.handlers +app.logger.setLevel(gunicorn_logger.level) diff --git a/server/model/schema.py b/server/model/schema.py new file mode 100644 index 00000000..b1298f2d --- /dev/null +++ b/server/model/schema.py @@ -0,0 +1,86 @@ +import json +import logging +from datetime import datetime + +import bson +from app import db +from sqlalchemy import BINARY, String, text + + +class ObjID(BINARY): + """基于bson.ObjectId用于mysql主键的自定义类型""" + + def bind_processor(self, dialect): + def processor(value): + return ( + bson.ObjectId(value).binary if bson.ObjectId.is_valid(value) else value + ) + + return processor + + def result_processor(self, dialect, coltype): + def processor(value): + if not isinstance(value, bytes): + value = bytes(value) + return str(bson.ObjectId(value)) if bson.ObjectId.is_valid(value) else value + + return processor + + @staticmethod + def new_id(): + return str(bson.ObjectId()) + + @staticmethod + def is_valid(value): + return bson.ObjectId.is_valid(value) + + +class JSONStr(String): + """自动转换 str 和 dict 的自定义类型""" + + def bind_processor(self, dialect): + def processor(value): + try: + if isinstance(value, str) and (value[0] == "%" or value[-1] == "%"): + # 使用like筛选的情况 + return value + return json.dumps(value, ensure_ascii=False) + except Exception as e: + logging.exception(e) + return value + + return processor + + def result_processor(self, dialect, coltype): + def processor(value): + try: + return json.loads(value) + except Exception as e: + logging.exception(e) + return value + + return processor + + @staticmethod + def is_valid(value): + try: + json.loads(value) + return True + except Exception as e: + logging.exception(e) + return False + + +class User(db.Model): + __tablename__ = "user" + id = db.Column(ObjID(12), primary_key=True) + openid = db.Column(db.String(128), nullable=True, comment="外部用户ID") + name = db.Column(db.String(128), nullable=True, comment="用户名") + extra = db.Column( + JSONStr(1024), nullable=True, server_default=text("'{}'"), comment="用户其他字段" + ) + status = db.Column(db.Integer, nullable=True, default=0, server_default=text("0")) + created = db.Column(db.TIMESTAMP, nullable=False, default=datetime.utcnow) + modified = db.Column( + db.TIMESTAMP, nullable=False, default=datetime.utcnow, onupdate=datetime.utcnow + ) diff --git a/server/routes/__init__.py b/server/routes/__init__.py new file mode 100644 index 00000000..460b5644 --- /dev/null +++ b/server/routes/__init__.py @@ -0,0 +1 @@ +from .lark import * diff --git a/server/routes/lark.py b/server/routes/lark.py new file mode 100644 index 00000000..0489ddcb --- /dev/null +++ b/server/routes/lark.py @@ -0,0 +1,49 @@ +import os + +from app import app +from connectai.lark.oauth import Server as OauthServerBase +from connectai.lark.sdk import Bot, MarketBot +from connectai.lark.webhook import LarkServer as LarkServerBase + +bot = Bot( + app_id=os.environ.get("APP_ID"), + app_secret=os.environ.get("APP_SECRET"), + encrypt_key=os.environ.get("ENCRYPT_KEY"), + verification_token=os.environ.get("VERIFICATION_TOKEN"), +) + + +class LarkServer(LarkServerBase): + def get_bot(self, app_id): + # TODO search in database and create Bot() + return bot + + +class OauthServer(OauthServerBase): + def get_bot(self, app_id): + # TODO search in database and create Bot() + return bot + + +hook = LarkServer(prefix="/api/feishu/hook") +oauth = OauthServer(prefix="/api/feishu/oauth") + + +@hook.on_bot_message(message_type="text", bot=bot) +def on_text_message(bot, message_id, content, *args, **kwargs): + text = content["text"] + print("reply_text", message_id, text) + bot.reply_text(message_id, "reply: " + text) + + +@oauth.on_bot_event(event_type="oauth:user_info", bot=bot) +def on_oauth_user_info(bot, event_id, user_info, *args, **kwargs): + # oauth user_info + print("oauth", user_info) + # TODO + session["user_id"] = user_info["union_id"] + return user_info + + +app.register_blueprint(oauth.get_blueprint()) +app.register_blueprint(hook.get_blueprint()) diff --git a/server/server.py b/server/server.py index b9f1902e..36b2499e 100644 --- a/server/server.py +++ b/server/server.py @@ -1,6 +1,7 @@ import os import env +import routes from app import app if __name__ == "__main__": From 31c2f4390c5759091bb98b9347f951ba13fb4ed8 Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Thu, 28 Dec 2023 21:30:47 +0800 Subject: [PATCH 07/29] feat: add model --- server/app.py | 1 + 1 file changed, 1 insertion(+) diff --git a/server/app.py b/server/app.py index c0ebda87..ab525252 100644 --- a/server/app.py +++ b/server/app.py @@ -5,6 +5,7 @@ from flask_sqlalchemy import SQLAlchemy app = Flask(__name__) +app.config.from_prefixed_env() app.secret_key = os.environ.get("SECRET_KEY") db = SQLAlchemy(app, engine_options={"isolation_level": "AUTOCOMMIT"}) CORS( From 112759b0eb98c12dbbb7609d700bd6c241991f5b Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Thu, 28 Dec 2023 21:35:48 +0800 Subject: [PATCH 08/29] feat: update config --- deploy/docker-compose.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/deploy/docker-compose.yml b/deploy/docker-compose.yml index a3631f38..31b0e3fa 100644 --- a/deploy/docker-compose.yml +++ b/deploy/docker-compose.yml @@ -24,7 +24,7 @@ services: RABBITMQ_DEFAULT_USER: "rabbitmq" RABBITMQ_DEFAULT_PASS: "rabbitmq" RABBITMQ_DEFAULT_VHOST: "/" - VIRTUAL_HOST: rabbitmq-manager.connectai.ai + VIRTUAL_HOST: rabbitmq.gitmaya.com VIRTUAL_PORT: 15672 ports: - "15672" @@ -39,8 +39,8 @@ services: - ./data/mysql/data:/var/lib/mysql - ./data/mysql/conf.d:/etc/mysql/conf.d environment: - MYSQL_ROOT_PASSWORD: 'connectai2023' - MYSQL_DATABASE: 'connectai-manager' + MYSQL_ROOT_PASSWORD: 'gitmaya' + MYSQL_DATABASE: 'gitmaya2023' TZ: 'Asia/Shanghai' ports: - "3306" From 8d24f5fa426f724c9232cba13f16a99f773d2e3c Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Thu, 28 Dec 2023 21:37:24 +0800 Subject: [PATCH 09/29] feat: update config --- pdm.lock | 12 +++++++++++- pyproject.toml | 1 + requirements.txt | 1 + server/app.py | 1 + 4 files changed, 14 insertions(+), 1 deletion(-) diff --git a/pdm.lock b/pdm.lock index 8bd96cd1..c1278ab2 100644 --- a/pdm.lock +++ b/pdm.lock @@ -5,7 +5,7 @@ groups = ["default"] strategy = ["cross_platform"] lock_version = "4.4" -content_hash = "sha256:d203557781e5ca17f80e94d60cc6fb8e435d752317eff4e3df384b5915c598fe" +content_hash = "sha256:48cb5d7b587e89f1367467b0d83411c29d3eeb92208ef7e4f520034112de8f8d" [[package]] name = "anyio" @@ -336,6 +336,16 @@ files = [ {file = "pycryptodome-3.19.1.tar.gz", hash = "sha256:8ae0dd1bcfada451c35f9e29a3e5db385caabc190f98e4a80ad02a61098fb776"}, ] +[[package]] +name = "pymysql" +version = "1.1.0" +requires_python = ">=3.7" +summary = "Pure Python MySQL Driver" +files = [ + {file = "PyMySQL-1.1.0-py3-none-any.whl", hash = "sha256:8969ec6d763c856f7073c4c64662882675702efcb114b4bcbb955aea3a069fa7"}, + {file = "PyMySQL-1.1.0.tar.gz", hash = "sha256:4f13a7df8bf36a51e81dd9f3605fede45a4878fe02f9236349fd82a3f0612f96"}, +] + [[package]] name = "python-dotenv" version = "1.0.0" diff --git a/pyproject.toml b/pyproject.toml index 3919f337..f8a1c5e5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,6 +11,7 @@ dependencies = [ "ca-lark-webhook>=0.0.3", "flask-sqlalchemy>=3.1.1", "flask-cors>=4.0.0", + "pymysql>=1.1.0", ] requires-python = ">=3.10" readme = "README.md" diff --git a/requirements.txt b/requirements.txt index 411b0b3d..a53ae4c9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -22,6 +22,7 @@ itsdangerous==2.1.2 Jinja2==3.1.2 MarkupSafe==2.1.3 pycryptodome==3.19.1 +pymysql==1.1.0 python-dotenv==1.0.0 sniffio==1.3.0 sqlalchemy==2.0.23 diff --git a/server/app.py b/server/app.py index ab525252..6f503943 100644 --- a/server/app.py +++ b/server/app.py @@ -1,3 +1,4 @@ +import logging import os from flask import Flask, session From 034790b65bba04c46754277c01acc6383754b0ab Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Thu, 28 Dec 2023 21:42:16 +0800 Subject: [PATCH 10/29] feat: hotfix --- server/routes/lark.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/routes/lark.py b/server/routes/lark.py index 0489ddcb..e294c54d 100644 --- a/server/routes/lark.py +++ b/server/routes/lark.py @@ -1,6 +1,6 @@ import os -from app import app +from app import app, session from connectai.lark.oauth import Server as OauthServerBase from connectai.lark.sdk import Bot, MarketBot from connectai.lark.webhook import LarkServer as LarkServerBase From e0378ab449eff33555de6c7d17062890b8dbee52 Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Thu, 28 Dec 2023 21:52:03 +0800 Subject: [PATCH 11/29] feat: hotfix --- server/routes/lark.py | 1 + 1 file changed, 1 insertion(+) diff --git a/server/routes/lark.py b/server/routes/lark.py index e294c54d..2a699e57 100644 --- a/server/routes/lark.py +++ b/server/routes/lark.py @@ -42,6 +42,7 @@ def on_oauth_user_info(bot, event_id, user_info, *args, **kwargs): print("oauth", user_info) # TODO session["user_id"] = user_info["union_id"] + session.permanent = True return user_info From dc46616fe512ad5b8d97b99f37be97ba317d7f5e Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Thu, 28 Dec 2023 21:53:27 +0800 Subject: [PATCH 12/29] feat: hotfix --- Makefile | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Makefile b/Makefile index cddf7f55..e2698fd3 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,3 @@ build: @echo "Building..." docker build -t gitmaya -f deploy/Dockerfile . @echo "Done." - - -FORCE: - From 96d5b7d6eddb3c5267bfb866a26178140e1ec23f Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Fri, 29 Dec 2023 12:42:34 +0800 Subject: [PATCH 13/29] feat: add mysql schema --- server/model/schema.py | 236 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 228 insertions(+), 8 deletions(-) diff --git a/server/model/schema.py b/server/model/schema.py index b1298f2d..802275eb 100644 --- a/server/model/schema.py +++ b/server/model/schema.py @@ -4,7 +4,7 @@ import bson from app import db -from sqlalchemy import BINARY, String, text +from sqlalchemy import BINARY, ForeignKey, String, text class ObjID(BINARY): @@ -71,16 +71,236 @@ def is_valid(value): return False -class User(db.Model): - __tablename__ = "user" +class Base(db.Model): id = db.Column(ObjID(12), primary_key=True) - openid = db.Column(db.String(128), nullable=True, comment="外部用户ID") - name = db.Column(db.String(128), nullable=True, comment="用户名") - extra = db.Column( - JSONStr(1024), nullable=True, server_default=text("'{}'"), comment="用户其他字段" - ) status = db.Column(db.Integer, nullable=True, default=0, server_default=text("0")) created = db.Column(db.TIMESTAMP, nullable=False, default=datetime.utcnow) modified = db.Column( db.TIMESTAMP, nullable=False, default=datetime.utcnow, onupdate=datetime.utcnow ) + + +class User(Base): + __tablename__ = "user" + email = db.Column(db.String(128), nullable=True, comment="邮箱,这里考虑一下如何做唯一的用户") + telephone = db.Column(db.String(128), nullable=True, comment="手机号") + name = db.Column(db.String(128), nullable=True, comment="用户名") + avatar = db.Column(db.String(128), nullable=True, comment="头像") + extra = db.Column( + JSONStr(1024), nullable=True, server_default=text("'{}'"), comment="用户其他字段" + ) + + +class Account(User): + passwd = Column( + String(128), nullable=True, server_default=text("''"), comment="登录密码" + ) + + +class BindUser(Base): + __tablename__ = "bint_user" + user_id = Column(ObjID(12), ForeignKey("user.id"), nullable=True, comment="用户ID") + # 这里如果是飞书租户,可能会有不同的name等,但是在github这边不管是哪一个org,都是一样的 + # 这里如何统一? + # 是不是说这里暂时不需要这个platform_id,还是说这个字段为空就好? + platform_id = Column( + ObjID(12), ForeignKey("im_platform.id"), nullable=True, comment="平台" + ) + unionid = db.Column(db.String(128), nullable=True, comment="飞书的unionid") + + # 这里还是用platform标记一下 + platform = db.Column(db.String(128), nullable=True, comment="平台:github/lark") + email = db.Column(db.String(128), nullable=True, comment="邮箱") + name = db.Column(db.String(128), nullable=True, comment="用户名") + avatar = db.Column(db.String(128), nullable=True, comment="头像") + extra = db.Column( + JSONStr(1024), nullable=True, server_default=text("'{}'"), comment="用户其他字段" + ) + + +class Team(Base): + __tablename__ = "team" + user_id = Column(ObjID(12), ForeignKey("user.id"), nullable=True, comment="用户ID") + code_platform_id = Column( + ObjID(12), ForeignKey("code_platform.id"), nullable=True, comment="代码平台" + ) + im_platform_id = Column( + ObjID(12), ForeignKey("im_platform.id"), nullable=True, comment="协同平台" + ) + + name = db.Column(db.String(128), nullable=True, comment="名称") + description = db.Column(db.String(1024), nullable=True, comment="描述") + extra = db.Column( + JSONStr(1024), + nullable=True, + server_default=text("'{}'"), + comment="其他字段,可能有一些前期没想好的配置项放这里", + ) + + +class TeamMember(Base): + __tablename__ = "team_member" + team_id = Column(ObjID(12), ForeignKey("team.id"), nullable=True, comment="属于哪一个组") + code_user_id = Column( + ObjID(12), + ForeignKey("bind_user.id"), + nullable=True, + comment="从code_platform关联过来的用户", + ) + im_user_id = Column( + ObjID(12), + ForeignKey("bind_user.id"), + nullable=True, + comment="从im_platform关联过来的用户", + ) + + +class CodePlatform(Base): + __tablename__ = "code_platform" + name = db.Column(db.String(128), nullable=True, comment="名称") + description = db.Column(db.String(1024), nullable=True, comment="描述") + extra = db.Column( + JSONStr(1024), nullable=True, server_default=text("'{}'"), comment="其他字段" + ) + + +class Repo(Base): + __tablename__ = "repo" + code_platform_id = Column( + ObjID(12), ForeignKey("code_platform.id"), nullable=True, comment="属于哪一个org" + ) + application_id = Column( + ObjID(12), + ForeignKey("code_application.id"), + nullable=True, + comment="哪一个application_id", + ) + name = db.Column(db.String(128), nullable=True, comment="名称") + description = db.Column(db.String(1024), nullable=True, comment="描述") + extra = db.Column( + JSONStr(1024), nullable=True, server_default=text("'{}'"), comment="其他字段" + ) + + +class RepoUser(Base): + __tablename__ = "repo_user" + code_platform_id = Column( + ObjID(12), ForeignKey("code_platform.id"), nullable=True, comment="属于哪一个org" + ) + application_id = Column( + ObjID(12), + ForeignKey("code_application.id"), + nullable=True, + comment="哪一个application_id", + ) + bind_user_id = Column( + ObjID(12), ForeignKey("bind_user.id"), nullable=True, comment="项目协作者" + ) + + +class IMPlatform(Base): + __tablename__ = "im_platform" + tenant_key = db.Column(db.String(128), nullable=True, comment="飞书租户id") + name = db.Column(db.String(128), nullable=True, comment="名称") + description = db.Column(db.String(1024), nullable=True, comment="描述") + extra = db.Column( + JSONStr(1024), nullable=True, server_default=text("'{}'"), comment="其他字段" + ) + + +class CodeApplication(Base): + __tablename__ = "code_application" + platform_id = Column( + ObjID(12), ForeignKey("code_platform.id"), nullable=True, comment="代码平台" + ) + installation_id = db.Column(db.String(128), nullable=True, comment="安装id") + extra = db.Column( + JSONStr(1024), nullable=True, server_default=text("'{}'"), comment="其他字段" + ) + + +class CodeEvent(Base): + __tablename__ = "code_event" + application_id = Column( + ObjID(12), ForeignKey("code_application.id"), nullable=True, comment="应用id" + ) + event_id = db.Column(db.String(128), nullable=True, comment="event_id") + event_type = db.Column(db.String(128), nullable=True, comment="event_type") + content = db.Column(db.String(128), nullable=True, comment="主要内容") + extra = db.Column( + JSONStr(1024), nullable=True, server_default=text("'{}'"), comment="其他字段" + ) + + +class CodeAction(Base): + __tablename__ = "code_action" + event_id = Column( + ObjID(12), ForeignKey("code_event.id"), nullable=True, comment="事件ID" + ) + action_type = db.Column( + db.String(128), nullable=True, comment="action_type: 主要是飞书那边的消息等" + ) + content = db.Column(db.String(128), nullable=True, comment="主要内容") + extra = db.Column( + JSONStr(1024), nullable=True, server_default=text("'{}'"), comment="其他字段" + ) + + +class IMApplication(Base): + __tablename__ = "im_application" + platform_id = Column( + ObjID(12), ForeignKey("code_platform.id"), nullable=True, comment="协同平台" + ) + app_id = db.Column(db.String(128), nullable=True, comment="app_id") + app_secret = db.Column(db.String(128), nullable=True, comment="app_id") + extra = db.Column( + JSONStr(1024), nullable=True, server_default=text("'{}'"), comment="其他字段" + ) + + +class IMEvent(Base): + __tablename__ = "im_event" + application_id = Column( + ObjID(12), ForeignKey("im_application.id"), nullable=True, comment="应用id" + ) + event_id = db.Column(db.String(128), nullable=True, comment="event_id") + event_type = db.Column(db.String(128), nullable=True, comment="event_type") + content = db.Column(db.String(128), nullable=True, comment="主要内容") + extra = db.Column( + JSONStr(1024), nullable=True, server_default=text("'{}'"), comment="其他字段" + ) + + +class IMAction(Base): + __tablename__ = "im_action" + event_id = Column( + ObjID(12), ForeignKey("im_event.id"), nullable=True, comment="事件ID" + ) + action_type = db.Column( + db.String(128), nullable=True, comment="action_type: 主要是github那边的动作等" + ) + content = db.Column(db.String(128), nullable=True, comment="主要内容") + extra = db.Column( + JSONStr(1024), nullable=True, server_default=text("'{}'"), comment="其他字段" + ) + + +class ChatGroup(Base): + __tablename__ = "chat_group" + repo_id = Column(ObjID(12), ForeignKey("repo.id"), nullable=True, comment="属于哪一个项目") + im_application_id = Column( + ObjID(12), ForeignKey("code_application.id"), nullable=True, comment="哪一个项目创建的" + ) + chat_id = db.Column(db.String(128), nullable=True, comment="chat_id") + name = db.Column(db.String(128), nullable=True, comment="群名称") + description = db.Column(db.String(128), nullable=True, comment="群描述") + extra = db.Column( + JSONStr(1024), nullable=True, server_default=text("'{}'"), comment="其他字段" + ) + + +if __name__ == "__main__": + from app import app + + with app.app_context(): + db.create_all() From 5a74fe8fd7653082cc03c48bf6de3e5138ec45b1 Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Fri, 29 Dec 2023 13:08:37 +0800 Subject: [PATCH 14/29] feat: add create command --- pdm.lock | 37 ++++++++++++++++++++++- pyproject.toml | 2 ++ requirements.txt | 3 ++ server/model/schema.py | 68 +++++++++++++++++++++++++----------------- server/server.py | 1 + 5 files changed, 82 insertions(+), 29 deletions(-) diff --git a/pdm.lock b/pdm.lock index c1278ab2..8ae18451 100644 --- a/pdm.lock +++ b/pdm.lock @@ -5,7 +5,7 @@ groups = ["default"] strategy = ["cross_platform"] lock_version = "4.4" -content_hash = "sha256:48cb5d7b587e89f1367467b0d83411c29d3eeb92208ef7e4f520034112de8f8d" +content_hash = "sha256:377060918126987ee7854a1767311674ff9e2710607aaacf0de88245cf78b1cb" [[package]] name = "anyio" @@ -33,6 +33,18 @@ files = [ {file = "blinker-1.7.0.tar.gz", hash = "sha256:e6820ff6fa4e4d1d8e2747c2283749c3f547e4fee112b98555cdcdae32996182"}, ] +[[package]] +name = "bson" +version = "0.5.10" +summary = "BSON codec for Python" +dependencies = [ + "python-dateutil>=2.4.0", + "six>=1.9.0", +] +files = [ + {file = "bson-0.5.10.tar.gz", hash = "sha256:d6511b2ab051139a9123c184de1a04227262173ad593429d21e443d6462d6590"}, +] + [[package]] name = "ca-lark-oauth" version = "0.0.5" @@ -346,6 +358,19 @@ files = [ {file = "PyMySQL-1.1.0.tar.gz", hash = "sha256:4f13a7df8bf36a51e81dd9f3605fede45a4878fe02f9236349fd82a3f0612f96"}, ] +[[package]] +name = "python-dateutil" +version = "2.8.2" +requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +summary = "Extensions to the standard Python datetime module" +dependencies = [ + "six>=1.5", +] +files = [ + {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, + {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, +] + [[package]] name = "python-dotenv" version = "1.0.0" @@ -356,6 +381,16 @@ files = [ {file = "python_dotenv-1.0.0-py3-none-any.whl", hash = "sha256:f5971a9226b701070a4bf2c38c89e5a3f0d64de8debda981d1db98583009122a"}, ] +[[package]] +name = "six" +version = "1.16.0" +requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +summary = "Python 2 and 3 compatibility utilities" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + [[package]] name = "sniffio" version = "1.3.0" diff --git a/pyproject.toml b/pyproject.toml index f8a1c5e5..17b4eef6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,6 +12,8 @@ dependencies = [ "flask-sqlalchemy>=3.1.1", "flask-cors>=4.0.0", "pymysql>=1.1.0", + "click>=8.1.7", + "bson>=0.5.10", ] requires-python = ">=3.10" readme = "README.md" diff --git a/requirements.txt b/requirements.txt index a53ae4c9..dfc5f3c3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,6 +3,7 @@ anyio==4.2.0 blinker==1.7.0 +bson==0.5.10 ca-lark-oauth==0.0.5 ca-lark-sdk==0.0.7 ca-lark-webhook==0.0.3 @@ -23,7 +24,9 @@ Jinja2==3.1.2 MarkupSafe==2.1.3 pycryptodome==3.19.1 pymysql==1.1.0 +python-dateutil==2.8.2 python-dotenv==1.0.0 +six==1.16.0 sniffio==1.3.0 sqlalchemy==2.0.23 typing-extensions==4.9.0 diff --git a/server/model/schema.py b/server/model/schema.py index 802275eb..0d734ad8 100644 --- a/server/model/schema.py +++ b/server/model/schema.py @@ -3,8 +3,11 @@ from datetime import datetime import bson -from app import db +import click +from app import app, db +from flask.cli import with_appcontext from sqlalchemy import BINARY, ForeignKey, String, text +from sqlalchemy.ext.declarative import declarative_base class ObjID(BINARY): @@ -72,6 +75,7 @@ def is_valid(value): class Base(db.Model): + __abstract__ = True id = db.Column(ObjID(12), primary_key=True) status = db.Column(db.Integer, nullable=True, default=0, server_default=text("0")) created = db.Column(db.TIMESTAMP, nullable=False, default=datetime.utcnow) @@ -92,18 +96,18 @@ class User(Base): class Account(User): - passwd = Column( - String(128), nullable=True, server_default=text("''"), comment="登录密码" + passwd = db.Column( + db.String(128), nullable=True, server_default=text("''"), comment="登录密码" ) class BindUser(Base): __tablename__ = "bint_user" - user_id = Column(ObjID(12), ForeignKey("user.id"), nullable=True, comment="用户ID") + user_id = db.Column(ObjID(12), ForeignKey("user.id"), nullable=True, comment="用户ID") # 这里如果是飞书租户,可能会有不同的name等,但是在github这边不管是哪一个org,都是一样的 # 这里如何统一? # 是不是说这里暂时不需要这个platform_id,还是说这个字段为空就好? - platform_id = Column( + platform_id = db.Column( ObjID(12), ForeignKey("im_platform.id"), nullable=True, comment="平台" ) unionid = db.Column(db.String(128), nullable=True, comment="飞书的unionid") @@ -120,11 +124,11 @@ class BindUser(Base): class Team(Base): __tablename__ = "team" - user_id = Column(ObjID(12), ForeignKey("user.id"), nullable=True, comment="用户ID") - code_platform_id = Column( + user_id = db.Column(ObjID(12), ForeignKey("user.id"), nullable=True, comment="用户ID") + code_platform_id = db.Column( ObjID(12), ForeignKey("code_platform.id"), nullable=True, comment="代码平台" ) - im_platform_id = Column( + im_platform_id = db.Column( ObjID(12), ForeignKey("im_platform.id"), nullable=True, comment="协同平台" ) @@ -140,14 +144,16 @@ class Team(Base): class TeamMember(Base): __tablename__ = "team_member" - team_id = Column(ObjID(12), ForeignKey("team.id"), nullable=True, comment="属于哪一个组") - code_user_id = Column( + team_id = db.Column( + ObjID(12), ForeignKey("team.id"), nullable=True, comment="属于哪一个组" + ) + code_user_id = db.Column( ObjID(12), ForeignKey("bind_user.id"), nullable=True, comment="从code_platform关联过来的用户", ) - im_user_id = Column( + im_user_id = db.Column( ObjID(12), ForeignKey("bind_user.id"), nullable=True, @@ -166,10 +172,10 @@ class CodePlatform(Base): class Repo(Base): __tablename__ = "repo" - code_platform_id = Column( + code_platform_id = db.Column( ObjID(12), ForeignKey("code_platform.id"), nullable=True, comment="属于哪一个org" ) - application_id = Column( + application_id = db.Column( ObjID(12), ForeignKey("code_application.id"), nullable=True, @@ -184,16 +190,16 @@ class Repo(Base): class RepoUser(Base): __tablename__ = "repo_user" - code_platform_id = Column( + code_platform_id = db.Column( ObjID(12), ForeignKey("code_platform.id"), nullable=True, comment="属于哪一个org" ) - application_id = Column( + application_id = db.Column( ObjID(12), ForeignKey("code_application.id"), nullable=True, comment="哪一个application_id", ) - bind_user_id = Column( + bind_user_id = db.Column( ObjID(12), ForeignKey("bind_user.id"), nullable=True, comment="项目协作者" ) @@ -210,7 +216,7 @@ class IMPlatform(Base): class CodeApplication(Base): __tablename__ = "code_application" - platform_id = Column( + platform_id = db.Column( ObjID(12), ForeignKey("code_platform.id"), nullable=True, comment="代码平台" ) installation_id = db.Column(db.String(128), nullable=True, comment="安装id") @@ -221,7 +227,7 @@ class CodeApplication(Base): class CodeEvent(Base): __tablename__ = "code_event" - application_id = Column( + application_id = db.Column( ObjID(12), ForeignKey("code_application.id"), nullable=True, comment="应用id" ) event_id = db.Column(db.String(128), nullable=True, comment="event_id") @@ -234,7 +240,7 @@ class CodeEvent(Base): class CodeAction(Base): __tablename__ = "code_action" - event_id = Column( + event_id = db.Column( ObjID(12), ForeignKey("code_event.id"), nullable=True, comment="事件ID" ) action_type = db.Column( @@ -248,7 +254,7 @@ class CodeAction(Base): class IMApplication(Base): __tablename__ = "im_application" - platform_id = Column( + platform_id = db.Column( ObjID(12), ForeignKey("code_platform.id"), nullable=True, comment="协同平台" ) app_id = db.Column(db.String(128), nullable=True, comment="app_id") @@ -260,7 +266,7 @@ class IMApplication(Base): class IMEvent(Base): __tablename__ = "im_event" - application_id = Column( + application_id = db.Column( ObjID(12), ForeignKey("im_application.id"), nullable=True, comment="应用id" ) event_id = db.Column(db.String(128), nullable=True, comment="event_id") @@ -273,7 +279,7 @@ class IMEvent(Base): class IMAction(Base): __tablename__ = "im_action" - event_id = Column( + event_id = db.Column( ObjID(12), ForeignKey("im_event.id"), nullable=True, comment="事件ID" ) action_type = db.Column( @@ -287,8 +293,10 @@ class IMAction(Base): class ChatGroup(Base): __tablename__ = "chat_group" - repo_id = Column(ObjID(12), ForeignKey("repo.id"), nullable=True, comment="属于哪一个项目") - im_application_id = Column( + repo_id = db.Column( + ObjID(12), ForeignKey("repo.id"), nullable=True, comment="属于哪一个项目" + ) + im_application_id = db.Column( ObjID(12), ForeignKey("code_application.id"), nullable=True, comment="哪一个项目创建的" ) chat_id = db.Column(db.String(128), nullable=True, comment="chat_id") @@ -299,8 +307,12 @@ class ChatGroup(Base): ) -if __name__ == "__main__": - from app import app +# create command function +@click.command(name="create") +@with_appcontext +def create(): + db.create_all() + - with app.app_context(): - db.create_all() +# add command function to cli commands +app.cli.add_command(create) diff --git a/server/server.py b/server/server.py index 36b2499e..61462fb0 100644 --- a/server/server.py +++ b/server/server.py @@ -3,6 +3,7 @@ import env import routes from app import app +from model.schema import * if __name__ == "__main__": # gunicorn -w 1 -b :8888 "server:app" From c4e66c29ab52b4ac667a93cf92a3fbf510e92062 Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Fri, 29 Dec 2023 16:02:09 +0800 Subject: [PATCH 15/29] add command --- server/command/lark.py | 72 ++++++++++++++++++++++++++++++++++++++++++ server/routes/lark.py | 51 ++++++++++++++++++++++-------- 2 files changed, 109 insertions(+), 14 deletions(-) create mode 100644 server/command/lark.py diff --git a/server/command/lark.py b/server/command/lark.py new file mode 100644 index 00000000..9e7cfc9c --- /dev/null +++ b/server/command/lark.py @@ -0,0 +1,72 @@ +import logging + +import click +from app import app, db +from model.schema import IMApplication, ObjID + + +# create command function +@app.cli.command(name="larkapp") +@click.option("-a", "--app-id", "app_id", required=True, prompt="Feishu(Lark) APP ID") +@click.option( + "-s", "--app-secret", "app_secret", required=True, prompt="Feishu(Lark) APP SECRET" +) +@click.option( + "-e", "--encrypt-key", "encrypt_key", default="", prompt="Feishu(Lark) ENCRYPT KEY" +) +@click.option( + "-v", + "--verification-token", + "verification_token", + default="", + prompt="Feishu(Lark) VERIFICATION TOKEN", +) +@click.option("-h", "--host", "host", default="https://testapi.gitmaya.com") +def create_lark_app(app_id, app_secret, encrypt_key, verification_token, host): + # click.echo(f'create_lark_app {app_id} {app_secret} {encrypt_key} {verification_token} {host}') + permissions = "\n\t".join( + [ + "contact:contact:readonly_as_app", + "im:chat", + "im:message", + "im:resource", + ] + ) + events = "\n\t".join( + [ + "im.message.receive_v1", + ] + ) + click.echo(f"need permissions: \n{permissions}\n") + click.echo(f"need events: \n{events}\n") + click.echo(f"webhook: \n{host}/api/feishu/oauth") + + application = ( + db.session.query(IMApplication).filter(IMApplication.app_id == app_id).first() + ) + if not application: + application = IMApplication( + id=ObjID.new_id(), + app_id=app_id, + app_secret=app_secret, + extra=dict(encrypt_key=encrypt_key, verification_token=verification_token), + ) + db.session.add(application) + dn.session.commit() + else: + db.session.query(IMApplication).filter( + IMApplication.id == application.id, + ).update( + dict( + app_id=app_id, + app_secret=app_secret, + extra=dict( + encrypt_key=encrypt_key, verification_token=verification_token + ), + ) + ) + dn.session.commit() + click.echo(f"webhook: \n{host}/api/feishu/hook/{app_id}") + + click.confirm("success to save feishu app?", abort=True) + click.echo(f"you can publish you app.") diff --git a/server/routes/lark.py b/server/routes/lark.py index 2a699e57..2e39c4fc 100644 --- a/server/routes/lark.py +++ b/server/routes/lark.py @@ -1,50 +1,73 @@ +import logging import os -from app import app, session +import click +from app import app, db, session from connectai.lark.oauth import Server as OauthServerBase from connectai.lark.sdk import Bot, MarketBot from connectai.lark.webhook import LarkServer as LarkServerBase +from flask.cli import with_appcontext +from model.lark import get_bot_by_app_id -bot = Bot( - app_id=os.environ.get("APP_ID"), - app_secret=os.environ.get("APP_SECRET"), - encrypt_key=os.environ.get("ENCRYPT_KEY"), - verification_token=os.environ.get("VERIFICATION_TOKEN"), -) + +def get_bot(app_id): + with app.app_context(): + application = get_bot_by_app_id(app_id) + if application: + return Bot( + app_id=application.app_id, + app_secret=application.app_secret, + encrypt_key=application.extra.get("encrypt_key"), + verification_token=application.extra.get("verification_token"), + ) class LarkServer(LarkServerBase): def get_bot(self, app_id): - # TODO search in database and create Bot() - return bot + return get_bot(app_id) class OauthServer(OauthServerBase): def get_bot(self, app_id): - # TODO search in database and create Bot() - return bot + return get_bot(app_id) hook = LarkServer(prefix="/api/feishu/hook") oauth = OauthServer(prefix="/api/feishu/oauth") -@hook.on_bot_message(message_type="text", bot=bot) +@hook.on_bot_message(message_type="text") def on_text_message(bot, message_id, content, *args, **kwargs): text = content["text"] print("reply_text", message_id, text) bot.reply_text(message_id, "reply: " + text) -@oauth.on_bot_event(event_type="oauth:user_info", bot=bot) +@oauth.on_bot_event(event_type="oauth:user_info") def on_oauth_user_info(bot, event_id, user_info, *args, **kwargs): # oauth user_info print("oauth", user_info) - # TODO + # TODO save bind user session["user_id"] = user_info["union_id"] session.permanent = True return user_info +# create command function +@app.cli.command(name="larkapp") +@click.option("-a", "--app-id", "app_id", required=True) +@click.option("-s", "--app-secret", "app_secret", required=True) +@click.option("-e", "--encrypt-key", "encrypt_key") +@click.option("-v", "--verification-token", "verification_token") +@click.option("-h", "--host", "host", default="https://testapi.gitmaya.com") +def create_lark_app(app_id, app_secret, encrypt_key, verification_token, host): + click.echo( + f"create_lark_app {app_id} {app_secret} {encrypt_key} {verification_token} {host}" + ) + click.echo(f"webhook: \n{host}/api/feishu/hook/{app_id}") + application = db.session.query(IMApplication).filter(IMApplication) + db.session + + app.register_blueprint(oauth.get_blueprint()) app.register_blueprint(hook.get_blueprint()) From 231455bffe36b7ac67c2f3c5fb4b7f624fe0a658 Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Fri, 29 Dec 2023 16:04:29 +0800 Subject: [PATCH 16/29] add command --- server/routes/lark.py | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/server/routes/lark.py b/server/routes/lark.py index 2e39c4fc..c1664e69 100644 --- a/server/routes/lark.py +++ b/server/routes/lark.py @@ -1,12 +1,10 @@ import logging import os -import click -from app import app, db, session +from app import app, session from connectai.lark.oauth import Server as OauthServerBase from connectai.lark.sdk import Bot, MarketBot from connectai.lark.webhook import LarkServer as LarkServerBase -from flask.cli import with_appcontext from model.lark import get_bot_by_app_id @@ -53,21 +51,5 @@ def on_oauth_user_info(bot, event_id, user_info, *args, **kwargs): return user_info -# create command function -@app.cli.command(name="larkapp") -@click.option("-a", "--app-id", "app_id", required=True) -@click.option("-s", "--app-secret", "app_secret", required=True) -@click.option("-e", "--encrypt-key", "encrypt_key") -@click.option("-v", "--verification-token", "verification_token") -@click.option("-h", "--host", "host", default="https://testapi.gitmaya.com") -def create_lark_app(app_id, app_secret, encrypt_key, verification_token, host): - click.echo( - f"create_lark_app {app_id} {app_secret} {encrypt_key} {verification_token} {host}" - ) - click.echo(f"webhook: \n{host}/api/feishu/hook/{app_id}") - application = db.session.query(IMApplication).filter(IMApplication) - db.session - - app.register_blueprint(oauth.get_blueprint()) app.register_blueprint(hook.get_blueprint()) From 8556c3d3e3e2a3c44f081a293c921b2e5b0a3c6d Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Fri, 29 Dec 2023 16:14:49 +0800 Subject: [PATCH 17/29] add missing file --- server/model/lark.py | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 server/model/lark.py diff --git a/server/model/lark.py b/server/model/lark.py new file mode 100644 index 00000000..86e1b092 --- /dev/null +++ b/server/model/lark.py @@ -0,0 +1,11 @@ +from .schema import IMApplication, db + + +def get_bot_by_app_id(app_id): + return ( + db.session.query(IMApplication) + .filter( + IMApplication.app_id == app_id, + ) + .first() + ) From 134c1e667f87a683fc03e78f8384eb688cd7bb2f Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Fri, 29 Dec 2023 16:30:12 +0800 Subject: [PATCH 18/29] update ca-lark-sdk --- pdm.lock | 10 +++++----- pyproject.toml | 1 + requirements.txt | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/pdm.lock b/pdm.lock index 2e649f45..398808e0 100644 --- a/pdm.lock +++ b/pdm.lock @@ -4,8 +4,8 @@ [metadata] groups = ["default"] strategy = ["cross_platform"] -lock_version = "4.4.1" -content_hash = "sha256:c1a5d1e22693f74c3174d55040fa95f5517b735391c8b4c2eced81269267fad9" +lock_version = "4.4" +content_hash = "sha256:60a66adf65ee59b47ebfb0bde94efd347cf4c9aeecb1bcf33c3a1459e9e98af0" [[package]] name = "anyio" @@ -60,7 +60,7 @@ files = [ [[package]] name = "ca-lark-sdk" -version = "0.0.7" +version = "0.0.8" requires_python = ">=3.8" summary = "lark(feishu) client" dependencies = [ @@ -68,7 +68,7 @@ dependencies = [ "pycryptodome", ] files = [ - {file = "ca-lark-sdk-0.0.7.tar.gz", hash = "sha256:07374e6cfeb97d96aed1358becc09529a66e292d50a94bb3b904306c7d2b6c93"}, + {file = "ca-lark-sdk-0.0.8.tar.gz", hash = "sha256:ee04d245a66d0174c28318e6dcd4d074a8c11eed96e224105d05dedb2db8b557"}, ] [[package]] @@ -507,7 +507,7 @@ version = "2.0.23" requires_python = ">=3.7" summary = "Database Abstraction Library" dependencies = [ - "greenlet!=0.4.17; platform_machine == \"win32\" or platform_machine == \"WIN32\" or platform_machine == \"AMD64\" or platform_machine == \"amd64\" or platform_machine == \"x86_64\" or platform_machine == \"ppc64le\" or platform_machine == \"aarch64\"", + "greenlet!=0.4.17; platform_machine == \"aarch64\" or (platform_machine == \"ppc64le\" or (platform_machine == \"x86_64\" or (platform_machine == \"amd64\" or (platform_machine == \"AMD64\" or (platform_machine == \"win32\" or platform_machine == \"WIN32\")))))", "typing-extensions>=4.2.0", ] files = [ diff --git a/pyproject.toml b/pyproject.toml index 341843a4..ff4f4dc0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,6 +16,7 @@ dependencies = [ "bson>=0.5.10", "jwt>=1.3.1", "urllib3>=2.1.0", + "ca-lark-sdk>=0.0.8", ] requires-python = ">=3.10" readme = "README.md" diff --git a/requirements.txt b/requirements.txt index acb280ed..ba60378d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,7 +5,7 @@ anyio==4.2.0 blinker==1.7.0 bson==0.5.10 ca-lark-oauth==0.0.5 -ca-lark-sdk==0.0.7 +ca-lark-sdk==0.0.8 ca-lark-webhook==0.0.3 certifi==2023.11.17 cffi==1.16.0 From a93d976d773858d6ae88d84ee8499ac67a71349d Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Fri, 29 Dec 2023 16:36:46 +0800 Subject: [PATCH 19/29] hotfix --- server/model/schema.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/model/schema.py b/server/model/schema.py index cf572d93..1026d3c4 100644 --- a/server/model/schema.py +++ b/server/model/schema.py @@ -101,7 +101,7 @@ class Account(User): class BindUser(Base): - __tablename__ = "bint_user" + __tablename__ = "bind_user" user_id = db.Column(ObjID(12), ForeignKey("user.id"), nullable=True, comment="用户ID") # 这里如果是飞书租户,可能会有不同的name等,但是在github这边不管是哪一个org,都是一样的 # 这里如何统一? From 23aeefc574bfb4c9a16537bc337fc3b78362a74c Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Fri, 29 Dec 2023 16:40:30 +0800 Subject: [PATCH 20/29] hotfix --- server/model/schema.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/server/model/schema.py b/server/model/schema.py index 1026d3c4..cccf26f2 100644 --- a/server/model/schema.py +++ b/server/model/schema.py @@ -77,9 +77,20 @@ class Base(db.Model): __abstract__ = True id = db.Column(ObjID(12), primary_key=True) status = db.Column(db.Integer, nullable=True, default=0, server_default=text("0")) - created = db.Column(db.TIMESTAMP, nullable=False, default=datetime.utcnow) + created = db.Column( + db.TIMESTAMP, + nullable=False, + default=datetime.utcnow, + server_default=text("CURRENT_TIMESTAMP"), + comment="创建时间", + ) modified = db.Column( - db.TIMESTAMP, nullable=False, default=datetime.utcnow, onupdate=datetime.utcnow + db.TIMESTAMP, + nullable=False, + default=datetime.utcnow, + onupdate=datetime.utcnow, + server_default=text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP"), + comment="修改时间", ) From 1aefd34bec8999d5bf93b6976aea0f62a52d8f20 Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Fri, 29 Dec 2023 16:44:24 +0800 Subject: [PATCH 21/29] hotfix --- server/command/lark.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/command/lark.py b/server/command/lark.py index 9e7cfc9c..c99754f7 100644 --- a/server/command/lark.py +++ b/server/command/lark.py @@ -52,7 +52,7 @@ def create_lark_app(app_id, app_secret, encrypt_key, verification_token, host): extra=dict(encrypt_key=encrypt_key, verification_token=verification_token), ) db.session.add(application) - dn.session.commit() + db.session.commit() else: db.session.query(IMApplication).filter( IMApplication.id == application.id, @@ -65,7 +65,7 @@ def create_lark_app(app_id, app_secret, encrypt_key, verification_token, host): ), ) ) - dn.session.commit() + db.session.commit() click.echo(f"webhook: \n{host}/api/feishu/hook/{app_id}") click.confirm("success to save feishu app?", abort=True) From e58a1e9e871aa38e2b8ac9523e31fddc90b17aa7 Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Fri, 29 Dec 2023 17:01:35 +0800 Subject: [PATCH 22/29] update version --- pdm.lock | 6 +++--- pyproject.toml | 2 +- requirements.txt | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pdm.lock b/pdm.lock index 398808e0..011d55b2 100644 --- a/pdm.lock +++ b/pdm.lock @@ -5,7 +5,7 @@ groups = ["default"] strategy = ["cross_platform"] lock_version = "4.4" -content_hash = "sha256:60a66adf65ee59b47ebfb0bde94efd347cf4c9aeecb1bcf33c3a1459e9e98af0" +content_hash = "sha256:04f23922d3a60864955c3dee31178aa308a90bd72e18f3e1d9caba742f7431ad" [[package]] name = "anyio" @@ -73,7 +73,7 @@ files = [ [[package]] name = "ca-lark-webhook" -version = "0.0.3" +version = "0.0.4" requires_python = ">=3.8" summary = "lark(feishu) client" dependencies = [ @@ -81,7 +81,7 @@ dependencies = [ "flask", ] files = [ - {file = "ca-lark-webhook-0.0.3.tar.gz", hash = "sha256:79d6da1ab96b207b542b8a20eabb0d8dc6563f87b84948d468183337f543cf22"}, + {file = "ca-lark-webhook-0.0.4.tar.gz", hash = "sha256:1ce7d6f92e61a12e434dada2fb12a2d6a18420576caa72db82afbbfaaa7ca3ce"}, ] [[package]] diff --git a/pyproject.toml b/pyproject.toml index ff4f4dc0..e3379f05 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ authors = [ dependencies = [ "python-dotenv>=1.0.0", "ca-lark-oauth==0.0.5", - "ca-lark-webhook>=0.0.3", + "ca-lark-webhook==0.0.4", "flask-sqlalchemy>=3.1.1", "flask-cors>=4.0.0", "pymysql>=1.1.0", diff --git a/requirements.txt b/requirements.txt index ba60378d..8280ca13 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,7 +6,7 @@ blinker==1.7.0 bson==0.5.10 ca-lark-oauth==0.0.5 ca-lark-sdk==0.0.8 -ca-lark-webhook==0.0.3 +ca-lark-webhook==0.0.4 certifi==2023.11.17 cffi==1.16.0 click==8.1.7 From 369e687b10bcea3f391b9935ca005d72678c890b Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Fri, 29 Dec 2023 17:10:33 +0800 Subject: [PATCH 23/29] update --- server/model/schema.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/model/schema.py b/server/model/schema.py index cccf26f2..70ec87b1 100644 --- a/server/model/schema.py +++ b/server/model/schema.py @@ -22,7 +22,7 @@ def processor(value): def result_processor(self, dialect, coltype): def processor(value): - if not isinstance(value, bytes): + if value and not isinstance(value, bytes): value = bytes(value) return str(bson.ObjectId(value)) if bson.ObjectId.is_valid(value) else value From 4b98660854ce08ae5378e09f6f4feecf4b9c9e89 Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Fri, 29 Dec 2023 18:31:11 +0800 Subject: [PATCH 24/29] add celery task --- deploy/docker-compose.yml | 14 ++++++++++++-- server/celery_app.py | 24 ++++++++++++++++++++++++ server/tasks/__init__.py | 10 ++++++++++ server/tasks/lark.py | 7 +++++++ 4 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 server/celery_app.py create mode 100644 server/tasks/__init__.py create mode 100644 server/tasks/lark.py diff --git a/deploy/docker-compose.yml b/deploy/docker-compose.yml index 31b0e3fa..4e97a92c 100644 --- a/deploy/docker-compose.yml +++ b/deploy/docker-compose.yml @@ -1,10 +1,17 @@ version: '2' services: - gitmaya: - restart: always + worker: image: gitmaya volumes: - .env:/server/.env + command: celery -A tasks.celery worker -l DEBUG -c 2 + + beat: + extends: worker + command: celery -A tasks.celery beat -l DEBUG + + gitmaya: + extends: worker ports: - "8888" environment: @@ -15,6 +22,9 @@ services: image: redis:alpine ports: - "6379" + volumes: + - ./data/redis:/data + command: redis-server --save 20 1 --loglevel warning rabbitmq: restart: always diff --git a/server/celery_app.py b/server/celery_app.py new file mode 100644 index 00000000..36419779 --- /dev/null +++ b/server/celery_app.py @@ -0,0 +1,24 @@ +from app import app +from celery import Celery + +app.config.setdefault("CELERY_BROKER_URL", "redis://redis:6379/0") +app.config.setdefault("CELERY_RESULT_BACKEND", "redis://redis:6379/0") +celery = Celery( + app.import_name, + broker=app.config["CELERY_BROKER_URL"], + backend=app.config["CELERY_RESULT_BACKEND"], +) + +celery.conf.update(app.config.get("CELERY_CONFIG", {})) +TaskBase = celery.Task + + +class ContextTask(TaskBase): + abstract = True + + def __call__(self, *args, **kwargs): + with app.app_context(): + return TaskBase.__call__(self, *args, **kwargs) + + +celery.Task = ContextTask diff --git a/server/tasks/__init__.py b/server/tasks/__init__.py new file mode 100644 index 00000000..9e600514 --- /dev/null +++ b/server/tasks/__init__.py @@ -0,0 +1,10 @@ +from ..celery_app import celery +from .lark import test_task + +celery.conf.beat_schedule = { + # "test_crontab_task": { + # "task": "tasks.crontab_task", + # "schedule": timedelta(hours=24), # 定时24hours执行一次 + # "args": (False) # 函数传参的值 + # }, +} diff --git a/server/tasks/lark.py b/server/tasks/lark.py new file mode 100644 index 00000000..88de897a --- /dev/null +++ b/server/tasks/lark.py @@ -0,0 +1,7 @@ +from ..celery_app import celery + + +@celery.task() +def test_task(): + logging.error("test_task") + return 1 From ecd0b1d58e3152e58644a1758db3f2e3b0ae49fa Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Fri, 29 Dec 2023 18:36:46 +0800 Subject: [PATCH 25/29] add celery task --- deploy/Dockerfile | 3 +- pdm.lock | 141 +++++++++++++++++++++++++++++++++++++++++++++- pyproject.toml | 1 + requirements.txt | 11 ++++ 4 files changed, 153 insertions(+), 3 deletions(-) diff --git a/deploy/Dockerfile b/deploy/Dockerfile index e5e3c07a..887a8090 100644 --- a/deploy/Dockerfile +++ b/deploy/Dockerfile @@ -1,7 +1,6 @@ FROM python:3.10-bookworm -RUN sed -i "s@http://deb.debian.org@http://mirrors.aliyun.com@g" /etc/apt/sources.list -RUN sed -i "s@http://security.debian.org@http://mirrors.aliyun.com@g" /etc/apt/sources.list +RUN sed -i "s@http://deb.debian.org@http://mirrors.aliyun.com@g" /etc/apt/sources.list.d/debian.sources RUN ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime ADD ./requirements.txt /tmp/requirements.txt diff --git a/pdm.lock b/pdm.lock index 011d55b2..a4341bdb 100644 --- a/pdm.lock +++ b/pdm.lock @@ -5,7 +5,20 @@ groups = ["default"] strategy = ["cross_platform"] lock_version = "4.4" -content_hash = "sha256:04f23922d3a60864955c3dee31178aa308a90bd72e18f3e1d9caba742f7431ad" +content_hash = "sha256:03abf006ee3055e53a3a12c9933c85107a2fc0de61462e96e18b1327f85c3866" + +[[package]] +name = "amqp" +version = "5.2.0" +requires_python = ">=3.6" +summary = "Low-level AMQP client for Python (fork of amqplib)." +dependencies = [ + "vine<6.0.0,>=5.0.0", +] +files = [ + {file = "amqp-5.2.0-py3-none-any.whl", hash = "sha256:827cb12fb0baa892aad844fd95258143bce4027fdac4fccddbc43330fd281637"}, + {file = "amqp-5.2.0.tar.gz", hash = "sha256:a1ecff425ad063ad42a486c902807d1482311481c8ad95a72694b2975e75f7fd"}, +] [[package]] name = "anyio" @@ -23,6 +36,16 @@ files = [ {file = "anyio-4.2.0.tar.gz", hash = "sha256:e1875bb4b4e2de1669f4bc7869b6d3f54231cdced71605e6e64c9be77e3be50f"}, ] +[[package]] +name = "billiard" +version = "4.2.0" +requires_python = ">=3.7" +summary = "Python multiprocessing fork with improvements and bugfixes" +files = [ + {file = "billiard-4.2.0-py3-none-any.whl", hash = "sha256:07aa978b308f334ff8282bd4a746e681b3513db5c9a514cbdd810cbbdc19714d"}, + {file = "billiard-4.2.0.tar.gz", hash = "sha256:9a3c3184cb275aa17a732f93f65b20c525d3d9f253722d26a82194803ade5a2c"}, +] + [[package]] name = "blinker" version = "1.7.0" @@ -84,6 +107,27 @@ files = [ {file = "ca-lark-webhook-0.0.4.tar.gz", hash = "sha256:1ce7d6f92e61a12e434dada2fb12a2d6a18420576caa72db82afbbfaaa7ca3ce"}, ] +[[package]] +name = "celery" +version = "5.3.6" +requires_python = ">=3.8" +summary = "Distributed Task Queue." +dependencies = [ + "billiard<5.0,>=4.2.0", + "click-didyoumean>=0.3.0", + "click-plugins>=1.1.1", + "click-repl>=0.2.0", + "click<9.0,>=8.1.2", + "kombu<6.0,>=5.3.4", + "python-dateutil>=2.8.2", + "tzdata>=2022.7", + "vine<6.0,>=5.1.0", +] +files = [ + {file = "celery-5.3.6-py3-none-any.whl", hash = "sha256:9da4ea0118d232ce97dff5ed4974587fb1c0ff5c10042eb15278487cdd27d1af"}, + {file = "celery-5.3.6.tar.gz", hash = "sha256:870cc71d737c0200c397290d730344cc991d13a057534353d124c9380267aab9"}, +] + [[package]] name = "certifi" version = "2023.11.17" @@ -151,6 +195,45 @@ files = [ {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, ] +[[package]] +name = "click-didyoumean" +version = "0.3.0" +requires_python = ">=3.6.2,<4.0.0" +summary = "Enables git-like *did-you-mean* feature in click" +dependencies = [ + "click>=7", +] +files = [ + {file = "click-didyoumean-0.3.0.tar.gz", hash = "sha256:f184f0d851d96b6d29297354ed981b7dd71df7ff500d82fa6d11f0856bee8035"}, + {file = "click_didyoumean-0.3.0-py3-none-any.whl", hash = "sha256:a0713dc7a1de3f06bc0df5a9567ad19ead2d3d5689b434768a6145bff77c0667"}, +] + +[[package]] +name = "click-plugins" +version = "1.1.1" +summary = "An extension module for click to enable registering CLI commands via setuptools entry-points." +dependencies = [ + "click>=4.0", +] +files = [ + {file = "click-plugins-1.1.1.tar.gz", hash = "sha256:46ab999744a9d831159c3411bb0c79346d94a444df9a3a3742e9ed63645f264b"}, + {file = "click_plugins-1.1.1-py2.py3-none-any.whl", hash = "sha256:5d262006d3222f5057fd81e1623d4443e41dcda5dc815c06b442aa3c02889fc8"}, +] + +[[package]] +name = "click-repl" +version = "0.3.0" +requires_python = ">=3.6" +summary = "REPL plugin for Click" +dependencies = [ + "click>=7.0", + "prompt-toolkit>=3.0.36", +] +files = [ + {file = "click-repl-0.3.0.tar.gz", hash = "sha256:17849c23dba3d667247dc4defe1757fff98694e90fe37474f3feebb69ced26a9"}, + {file = "click_repl-0.3.0-py3-none-any.whl", hash = "sha256:fb7e06deb8da8de86180a33a9da97ac316751c094c6899382da7feeeeb51b812"}, +] + [[package]] name = "colorama" version = "0.4.6" @@ -370,6 +453,20 @@ files = [ {file = "jwt-1.3.1-py3-none-any.whl", hash = "sha256:61c9170f92e736b530655e75374681d4fcca9cfa8763ab42be57353b2b203494"}, ] +[[package]] +name = "kombu" +version = "5.3.4" +requires_python = ">=3.8" +summary = "Messaging library for Python." +dependencies = [ + "amqp<6.0.0,>=5.1.1", + "vine", +] +files = [ + {file = "kombu-5.3.4-py3-none-any.whl", hash = "sha256:63bb093fc9bb80cfb3a0972336a5cec1fa7ac5f9ef7e8237c6bf8dda9469313e"}, + {file = "kombu-5.3.4.tar.gz", hash = "sha256:0bb2e278644d11dea6272c17974a3dbb9688a949f3bb60aeb5b791329c44fadc"}, +] + [[package]] name = "markupsafe" version = "2.1.3" @@ -409,6 +506,19 @@ files = [ {file = "MarkupSafe-2.1.3.tar.gz", hash = "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad"}, ] +[[package]] +name = "prompt-toolkit" +version = "3.0.43" +requires_python = ">=3.7.0" +summary = "Library for building powerful interactive command lines in Python" +dependencies = [ + "wcwidth", +] +files = [ + {file = "prompt_toolkit-3.0.43-py3-none-any.whl", hash = "sha256:a11a29cb3bf0a28a387fe5122cdb649816a957cd9261dcedf8c9f1fef33eacf6"}, + {file = "prompt_toolkit-3.0.43.tar.gz", hash = "sha256:3527b7af26106cbc65a040bcc84839a3566ec1b051bb0bfe953631e704b0ff7d"}, +] + [[package]] name = "pycparser" version = "2.21" @@ -549,6 +659,16 @@ files = [ {file = "typing_extensions-4.9.0.tar.gz", hash = "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783"}, ] +[[package]] +name = "tzdata" +version = "2023.3" +requires_python = ">=2" +summary = "Provider of IANA time zone data" +files = [ + {file = "tzdata-2023.3-py2.py3-none-any.whl", hash = "sha256:7e65763eef3120314099b6939b5546db7adce1e7d6f2e179e3df563c70511eda"}, + {file = "tzdata-2023.3.tar.gz", hash = "sha256:11ef1e08e54acb0d4f95bdb1be05da659673de4acbd21bf9c69e94cc5e907a3a"}, +] + [[package]] name = "urllib3" version = "2.1.0" @@ -559,6 +679,25 @@ files = [ {file = "urllib3-2.1.0.tar.gz", hash = "sha256:df7aa8afb0148fa78488e7899b2c59b5f4ffcfa82e6c54ccb9dd37c1d7b52d54"}, ] +[[package]] +name = "vine" +version = "5.1.0" +requires_python = ">=3.6" +summary = "Python promises." +files = [ + {file = "vine-5.1.0-py3-none-any.whl", hash = "sha256:40fdf3c48b2cfe1c38a49e9ae2da6fda88e4794c810050a728bd7413811fb1dc"}, + {file = "vine-5.1.0.tar.gz", hash = "sha256:8b62e981d35c41049211cf62a0a1242d8c1ee9bd15bb196ce38aefd6799e61e0"}, +] + +[[package]] +name = "wcwidth" +version = "0.2.12" +summary = "Measures the displayed width of unicode strings in a terminal" +files = [ + {file = "wcwidth-0.2.12-py2.py3-none-any.whl", hash = "sha256:f26ec43d96c8cbfed76a5075dac87680124fa84e0855195a6184da9c187f133c"}, + {file = "wcwidth-0.2.12.tar.gz", hash = "sha256:f01c104efdf57971bcb756f054dd58ddec5204dd15fa31d6503ea57947d97c02"}, +] + [[package]] name = "werkzeug" version = "3.0.1" diff --git a/pyproject.toml b/pyproject.toml index e3379f05..11d48a50 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,6 +17,7 @@ dependencies = [ "jwt>=1.3.1", "urllib3>=2.1.0", "ca-lark-sdk>=0.0.8", + "celery>=5.3.6", ] requires-python = ">=3.10" readme = "README.md" diff --git a/requirements.txt b/requirements.txt index 8280ca13..dd441b2c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,15 +1,21 @@ # This file is @generated by PDM. # Please do not edit it manually. +amqp==5.2.0 anyio==4.2.0 +billiard==4.2.0 blinker==1.7.0 bson==0.5.10 ca-lark-oauth==0.0.5 ca-lark-sdk==0.0.8 ca-lark-webhook==0.0.4 +celery==5.3.6 certifi==2023.11.17 cffi==1.16.0 click==8.1.7 +click-didyoumean==0.3.0 +click-plugins==1.1.1 +click-repl==0.3.0 colorama==0.4.6; platform_system == "Windows" cryptography==41.0.7 exceptiongroup==1.2.0; python_version < "3.11" @@ -24,7 +30,9 @@ idna==3.6 itsdangerous==2.1.2 Jinja2==3.1.2 jwt==1.3.1 +kombu==5.3.4 MarkupSafe==2.1.3 +prompt-toolkit==3.0.43 pycparser==2.21 pycryptodome==3.19.1 pymysql==1.1.0 @@ -34,5 +42,8 @@ six==1.16.0 sniffio==1.3.0 sqlalchemy==2.0.23 typing-extensions==4.9.0 +tzdata==2023.3 urllib3==2.1.0 +vine==5.1.0 +wcwidth==0.2.12 Werkzeug==3.0.1 From e567528b3c29a727e55e482bdc4fa964e8ca4596 Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Fri, 29 Dec 2023 20:49:36 +0800 Subject: [PATCH 26/29] remove rabbitmq --- deploy/docker-compose.yml | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/deploy/docker-compose.yml b/deploy/docker-compose.yml index 4e97a92c..3a55721e 100644 --- a/deploy/docker-compose.yml +++ b/deploy/docker-compose.yml @@ -26,22 +26,6 @@ services: - ./data/redis:/data command: redis-server --save 20 1 --loglevel warning - rabbitmq: - restart: always - image: rabbitmq:3.7-management-alpine - environment: - RABBITMQ_ERLANG_COOKIE: "SWQOKODSQALRPCLNMEQG" - RABBITMQ_DEFAULT_USER: "rabbitmq" - RABBITMQ_DEFAULT_PASS: "rabbitmq" - RABBITMQ_DEFAULT_VHOST: "/" - VIRTUAL_HOST: rabbitmq.gitmaya.com - VIRTUAL_PORT: 15672 - ports: - - "15672" - - "5672" - volumes: - - ./data/rabbitmq:/data/mnesia - mysql: restart: always image: mysql:5.7 From 726b751c4ecf3026c105e061255ac1fc0b8a6898 Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Fri, 29 Dec 2023 20:54:50 +0800 Subject: [PATCH 27/29] update --- deploy/docker-compose.yml | 4 ++-- pdm.lock | 25 ++++++++++++++++++++++++- pyproject.toml | 1 + requirements.txt | 2 ++ server/celery_app.py | 1 + server/tasks/__init__.py | 3 ++- server/tasks/lark.py | 2 +- 7 files changed, 33 insertions(+), 5 deletions(-) diff --git a/deploy/docker-compose.yml b/deploy/docker-compose.yml index 3a55721e..73389028 100644 --- a/deploy/docker-compose.yml +++ b/deploy/docker-compose.yml @@ -33,8 +33,8 @@ services: - ./data/mysql/data:/var/lib/mysql - ./data/mysql/conf.d:/etc/mysql/conf.d environment: - MYSQL_ROOT_PASSWORD: 'gitmaya' - MYSQL_DATABASE: 'gitmaya2023' + MYSQL_ROOT_PASSWORD: 'gitmaya2023' + MYSQL_DATABASE: 'gitmaya' TZ: 'Asia/Shanghai' ports: - "3306" diff --git a/pdm.lock b/pdm.lock index a4341bdb..65979d9a 100644 --- a/pdm.lock +++ b/pdm.lock @@ -5,7 +5,7 @@ groups = ["default"] strategy = ["cross_platform"] lock_version = "4.4" -content_hash = "sha256:03abf006ee3055e53a3a12c9933c85107a2fc0de61462e96e18b1327f85c3866" +content_hash = "sha256:6aad74a90f0ad95144d877dcf48c872ce82df2a6ddbe42d04e67585c0734c5f8" [[package]] name = "amqp" @@ -36,6 +36,16 @@ files = [ {file = "anyio-4.2.0.tar.gz", hash = "sha256:e1875bb4b4e2de1669f4bc7869b6d3f54231cdced71605e6e64c9be77e3be50f"}, ] +[[package]] +name = "async-timeout" +version = "4.0.3" +requires_python = ">=3.7" +summary = "Timeout context manager for asyncio programs" +files = [ + {file = "async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f"}, + {file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"}, +] + [[package]] name = "billiard" version = "4.2.0" @@ -591,6 +601,19 @@ files = [ {file = "python_dotenv-1.0.0-py3-none-any.whl", hash = "sha256:f5971a9226b701070a4bf2c38c89e5a3f0d64de8debda981d1db98583009122a"}, ] +[[package]] +name = "redis" +version = "5.0.1" +requires_python = ">=3.7" +summary = "Python client for Redis database and key-value store" +dependencies = [ + "async-timeout>=4.0.2; python_full_version <= \"3.11.2\"", +] +files = [ + {file = "redis-5.0.1-py3-none-any.whl", hash = "sha256:ed4802971884ae19d640775ba3b03aa2e7bd5e8fb8dfaed2decce4d0fc48391f"}, + {file = "redis-5.0.1.tar.gz", hash = "sha256:0dab495cd5753069d3bc650a0dde8a8f9edde16fc5691b689a566eda58100d0f"}, +] + [[package]] name = "six" version = "1.16.0" diff --git a/pyproject.toml b/pyproject.toml index 11d48a50..52ec8545 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,6 +18,7 @@ dependencies = [ "urllib3>=2.1.0", "ca-lark-sdk>=0.0.8", "celery>=5.3.6", + "redis>=5.0.1", ] requires-python = ">=3.10" readme = "README.md" diff --git a/requirements.txt b/requirements.txt index dd441b2c..97cff15e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,6 +3,7 @@ amqp==5.2.0 anyio==4.2.0 +async-timeout==4.0.3; python_full_version <= "3.11.2" billiard==4.2.0 blinker==1.7.0 bson==0.5.10 @@ -38,6 +39,7 @@ pycryptodome==3.19.1 pymysql==1.1.0 python-dateutil==2.8.2 python-dotenv==1.0.0 +redis==5.0.1 six==1.16.0 sniffio==1.3.0 sqlalchemy==2.0.23 diff --git a/server/celery_app.py b/server/celery_app.py index 36419779..5b6f046f 100644 --- a/server/celery_app.py +++ b/server/celery_app.py @@ -1,3 +1,4 @@ +import env from app import app from celery import Celery diff --git a/server/tasks/__init__.py b/server/tasks/__init__.py index 9e600514..5b2395dd 100644 --- a/server/tasks/__init__.py +++ b/server/tasks/__init__.py @@ -1,4 +1,5 @@ -from ..celery_app import celery +from celery_app import celery + from .lark import test_task celery.conf.beat_schedule = { diff --git a/server/tasks/lark.py b/server/tasks/lark.py index 88de897a..c0e094dc 100644 --- a/server/tasks/lark.py +++ b/server/tasks/lark.py @@ -1,4 +1,4 @@ -from ..celery_app import celery +from celery_app import celery @celery.task() From c91dafe1deacbf28a8032da716e2f69fa4ca4b19 Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Fri, 29 Dec 2023 21:06:01 +0800 Subject: [PATCH 28/29] add auth --- server/routes/__init__.py | 1 + server/routes/team.py | 20 ++++++++++++++++++++ server/utils/auth.py | 13 +++++++++++++ 3 files changed, 34 insertions(+) create mode 100644 server/routes/team.py create mode 100644 server/utils/auth.py diff --git a/server/routes/__init__.py b/server/routes/__init__.py index 25a22184..a7105778 100644 --- a/server/routes/__init__.py +++ b/server/routes/__init__.py @@ -1,2 +1,3 @@ from .github import * from .lark import * +from .team import * diff --git a/server/routes/team.py b/server/routes/team.py new file mode 100644 index 00000000..f32ed6da --- /dev/null +++ b/server/routes/team.py @@ -0,0 +1,20 @@ +import logging + +from app import app +from flask import Blueprint, abort, jsonify, redirect, request +from utils.auth import authenticated + +bp = Blueprint("team", __name__, url_prefix="/api/team") + + +@bp.route("/", methods=["GET"]) +@authenticated +def get_team_list(): + """ + get team list + TODO + """ + return jsonify({"code": 0, "msg": "success", "data": [], "total": 0}) + + +app.register_blueprint(bp) diff --git a/server/utils/auth.py b/server/utils/auth.py new file mode 100644 index 00000000..e008d6e5 --- /dev/null +++ b/server/utils/auth.py @@ -0,0 +1,13 @@ +from functools import wraps + +from flask import abort, session + + +def authenticated(func): + @wraps(func) + def wrapper(*args, **kwargs): + if "user_id" not in session: + return abort(401) + return func(*args, **kwargs) + + return wrapper From a8a86f556923ef257fbeaa2c423882cfd11835a8 Mon Sep 17 00:00:00 2001 From: lloydzhou Date: Fri, 29 Dec 2023 21:14:32 +0800 Subject: [PATCH 29/29] add auth --- server/tasks/lark.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server/tasks/lark.py b/server/tasks/lark.py index c0e094dc..606ca3e4 100644 --- a/server/tasks/lark.py +++ b/server/tasks/lark.py @@ -1,3 +1,5 @@ +import logging + from celery_app import celery