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/deploy/docker-compose.yml b/deploy/docker-compose.yml index 31b0e3fa..73389028 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,22 +22,9 @@ services: 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.gitmaya.com - VIRTUAL_PORT: 15672 - ports: - - "15672" - - "5672" volumes: - - ./data/rabbitmq:/data/mnesia + - ./data/redis:/data + command: redis-server --save 20 1 --loglevel warning mysql: restart: always @@ -39,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 2e649f45..65979d9a 100644 --- a/pdm.lock +++ b/pdm.lock @@ -4,8 +4,21 @@ [metadata] groups = ["default"] strategy = ["cross_platform"] -lock_version = "4.4.1" -content_hash = "sha256:c1a5d1e22693f74c3174d55040fa95f5517b735391c8b4c2eced81269267fad9" +lock_version = "4.4" +content_hash = "sha256:6aad74a90f0ad95144d877dcf48c872ce82df2a6ddbe42d04e67585c0734c5f8" + +[[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,26 @@ 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" +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" @@ -60,7 +93,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,12 +101,12 @@ 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]] name = "ca-lark-webhook" -version = "0.0.3" +version = "0.0.4" requires_python = ">=3.8" summary = "lark(feishu) client" dependencies = [ @@ -81,7 +114,28 @@ 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]] +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]] @@ -151,6 +205,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 +463,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 +516,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" @@ -481,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" @@ -507,7 +640,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 = [ @@ -549,6 +682,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 +702,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 341843a4..52ec8545 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", @@ -16,6 +16,9 @@ dependencies = [ "bson>=0.5.10", "jwt>=1.3.1", "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 acb280ed..97cff15e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,15 +1,22 @@ # This file is @generated by PDM. # Please do not edit it manually. +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 ca-lark-oauth==0.0.5 -ca-lark-sdk==0.0.7 -ca-lark-webhook==0.0.3 +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,15 +31,21 @@ 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 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 typing-extensions==4.9.0 +tzdata==2023.3 urllib3==2.1.0 +vine==5.1.0 +wcwidth==0.2.12 Werkzeug==3.0.1 diff --git a/server/celery_app.py b/server/celery_app.py new file mode 100644 index 00000000..5b6f046f --- /dev/null +++ b/server/celery_app.py @@ -0,0 +1,25 @@ +import env +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/command/lark.py b/server/command/lark.py new file mode 100644 index 00000000..c99754f7 --- /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) + db.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 + ), + ) + ) + db.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/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() + ) diff --git a/server/model/schema.py b/server/model/schema.py index cf572d93..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 @@ -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="修改时间", ) @@ -101,7 +112,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,都是一样的 # 这里如何统一? 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/lark.py b/server/routes/lark.py index 2a699e57..c1664e69 100644 --- a/server/routes/lark.py +++ b/server/routes/lark.py @@ -1,46 +1,51 @@ +import logging import os 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 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 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/tasks/__init__.py b/server/tasks/__init__.py new file mode 100644 index 00000000..5b2395dd --- /dev/null +++ b/server/tasks/__init__.py @@ -0,0 +1,11 @@ +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..606ca3e4 --- /dev/null +++ b/server/tasks/lark.py @@ -0,0 +1,9 @@ +import logging + +from celery_app import celery + + +@celery.task() +def test_task(): + logging.error("test_task") + return 1 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