diff --git a/server/app.py b/server/app.py index 6f503943..f79b7c91 100644 --- a/server/app.py +++ b/server/app.py @@ -1,7 +1,7 @@ import logging import os -from flask import Flask, session +from flask import Flask, jsonify from flask_cors import CORS from flask_sqlalchemy import SQLAlchemy @@ -13,6 +13,21 @@ app, allow_headers=["Authorization", "X-Requested-With"], supports_credentials=True ) + +@app.errorhandler(404) +def page_not_found(error): + response = jsonify({"code": -1, "msg": error.description}) + response.status_code = 404 + return response + + +@app.errorhandler(400) +def bad_request(error): + response = jsonify({"code": -1, "msg": error.description}) + response.status_code = 400 + return response + + 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 index ae3cd27f..6214167c 100644 --- a/server/model/schema.py +++ b/server/model/schema.py @@ -1,11 +1,13 @@ import json import logging from datetime import datetime +from time import time import bson import click from app import app, db from flask.cli import with_appcontext +from flask.json.provider import DefaultJSONProvider from sqlalchemy import BINARY, ForeignKey, String, text @@ -318,6 +320,20 @@ class ChatGroup(Base): ) +class CustomJsonProvider(DefaultJSONProvider): + @staticmethod + def default(value): + if isinstance(value, db.Model): + return value.__dict__ + elif isinstance(value, datetime): + return value.strftime("%Y-%m-%d %H:%M:%S") + return str(value) + + +app.json_provider_class = CustomJsonProvider +app.json = CustomJsonProvider(app) + + # create command function @click.command(name="create") @with_appcontext diff --git a/server/model/team.py b/server/model/team.py new file mode 100644 index 00000000..69b8f85d --- /dev/null +++ b/server/model/team.py @@ -0,0 +1,109 @@ +from flask import abort +from sqlalchemy import and_, or_ +from utils.utils import query_one_page + +from .schema import Team, TeamMember, db + + +def get_team_list_by_user_id(user_id, page=1, size=100): + query = db.session.query(Team).filter( + or_( + Team.user_id == user_id, # 管理员 + and_( + TeamMember.team_id == Team.id, + TeamMember.code_user_id == user_id, # 属于某个team的员工 + TeamMember.status == 0, + ), + ), + Team.status == 0, + ) + total = query.count() + if total == 0: + return [], 0 + return query_one_page(query, page, size), total + + +def is_team_admin(team_id, user_id): + return ( + True + if db.session.query(Team.id) + .filter( + Team.user_id == user_id, + Team.status == 0, + ) + .limit(1) + .scalar() + else False + ) + + +def get_team_by_id(team_id, user_id): + team = ( + db.session.query(Team) + .filter( + or_( + Team.user_id == user_id, # 管理员 + and_( + TeamMember.team_id == Team.id, + TeamMember.code_user_id == user_id, # 属于某个team的员工 + TeamMember.status == 0, + ), + ), + Team.status == 0, + ) + .first() + ) + if not team: + return abort(404, "can not found team by id") + return team + + +def get_platform_info_by_team_id(team_id): + # TODO + return None, None + + +def get_team_member(team_id, user_id, page=1, size=20): + query = db.session.query(TeamMember).filter( + TeamMember.team_id == team_id, + TeamMember.status == 0, + ) + # admin can get all users in current_team + if not is_team_admin(team_id, user_id): + query = query.filter( + TeamMember.code_user_id == user_id, + ) + total = query.count() + if total == 0: + return [], 0 + return query_one_page(query, page, size), total + + +def get_im_user_by_team_id(team_id, page=1, size=20): + query = ( + db.session.query(BindUser) + .join( + IMPlatform, + IMPlatform.id == BindUser.platform_id, + ) + .filter( + IMPlatform.team_id == team_id, + IMPlatform.status == 0, + BindUser.status == 0, + ) + ) + total = query.count() + if total == 0: + return [], 0 + return query_one_page(query, page, size), total + + data, total = get_im_user_by_team_id(team_id, page, size) + + +def set_team_member(team_id, code_user_id, im_user_id): + db.session.query(TeamMember).filter( + TeamMember.team_id == team_id, + TeamMember.code_user_id == code_user_id, + TeamMember.status == 0, + ).update(dict(im_user_id=im_user_id)) + db.session.commit() diff --git a/server/model/user.py b/server/model/user.py new file mode 100644 index 00000000..cc630f8c --- /dev/null +++ b/server/model/user.py @@ -0,0 +1,14 @@ +from sqlalchemy import and_, or_ + +from .schema import User, db + + +def get_user_by_id(user_id): + return ( + db.session.query(User) + .filter( + User.id == user_id, + User.status == 0, + ) + .first() + ) diff --git a/server/routes/__init__.py b/server/routes/__init__.py index a7105778..031d8f3d 100644 --- a/server/routes/__init__.py +++ b/server/routes/__init__.py @@ -1,3 +1,4 @@ from .github import * from .lark import * from .team import * +from .user import * diff --git a/server/routes/lark.py b/server/routes/lark.py index c1664e69..d40c9183 100644 --- a/server/routes/lark.py +++ b/server/routes/lark.py @@ -1,10 +1,10 @@ -import logging import os -from app import app, session +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 +from flask import session from model.lark import get_bot_by_app_id diff --git a/server/routes/team.py b/server/routes/team.py index f32ed6da..13f72ff7 100644 --- a/server/routes/team.py +++ b/server/routes/team.py @@ -1,7 +1,14 @@ -import logging - from app import app -from flask import Blueprint, abort, jsonify, redirect, request +from flask import Blueprint, abort, jsonify, redirect, request, session +from model.team import ( + get_im_user_by_team_id, + get_platform_info_by_team_id, + get_team_by_id, + get_team_list_by_user_id, + get_team_member, + is_team_admin, + set_team_member, +) from utils.auth import authenticated bp = Blueprint("team", __name__, url_prefix="/api/team") @@ -14,7 +21,83 @@ def get_team_list(): get team list TODO """ - return jsonify({"code": 0, "msg": "success", "data": [], "total": 0}) + data, total = get_team_list_by_user_id(session["user_id"]) + return jsonify({"code": 0, "msg": "success", "data": data, "total": total}) + + +@bp.route("/", methods=["GET"]) +@authenticated +def get_team_detail(team_id): + team = get_team_by_id(team_id, session["user_id"]) + code_platform, im_platform = get_platform_info_by_team_id(team_id) + return jsonify( + { + "code": 0, + "msg": "success", + "data": { + "team": team, + "code_platform": code_platform, + "im_platform": im_platform, + }, + } + ) + + +@bp.route("//member", methods=["GET"]) +@authenticated +def get_team_member_by_team_id(team_id): + page = request.args.get("page", default=1, type=int) + size = request.args.get("size", default=20, type=int) + + current_user = session["user_id"] + data, total = get_team_member(team_id, current_user, page, size) + return jsonify({"code": 0, "msg": "success", "data": data, "total": total}) + + +@bp.route("///user", methods=["GET"]) +@authenticated +def get_im_user_by_team_id_and_platform(team_id, platform): + page = request.args.get("page", default=1, type=int) + size = request.args.get("size", default=20, type=int) + + if platform not in ["lark"]: # TODO lark/slack... + return abort(400, "params error") + + current_user = session["user_id"] + data, total = get_im_user_by_team_id(team_id, page, size) + return jsonify( + { + "code": 0, + "msg": "success", + "data": [ + { + "value": i.user_id, + "label": i.name or i.email, + "email": i.email, + "avatar": i.avatar, + } + for i in data + ], + "total": total, + } + ) + + +@bp.route("//member", methods=["POST"]) +@authenticated +def save_team_member_by_team_id(team_id): + code_user_id = request.json.get("code_user_id") + im_user_id = request.json.get("im_user_id") + if not code_user_id or not im_user_id: + return abort(400, "params error") + + current_user = session["user_id"] + is_admin = is_team_admin(team_id, current_user) + if current_user != code_user_id and not is_admin: + return abort(400, "permission error") + + set_team_member(team_id, code_user_id, im_user_id) + return jsonify({"code": 0, "msg": "success"}) app.register_blueprint(bp) diff --git a/server/routes/user.py b/server/routes/user.py new file mode 100644 index 00000000..bf9d6724 --- /dev/null +++ b/server/routes/user.py @@ -0,0 +1,33 @@ +from app import app +from flask import Blueprint, jsonify, session +from model.team import get_team_list_by_user_id, is_team_admin +from model.user import get_user_by_id +from utils.auth import authenticated + +bp = Blueprint("user", __name__, url_prefix="/api") + + +@bp.route("/account", methods=["GET"]) +@authenticated +def get_account(): + current_user = session["user_id"] + user = get_user_by_id(current_user) + teams, _ = get_team_list_by_user_id(current_user) + current_team = session.get("team_id") + if not current_team and len(teams) > 0: + current_team = teams[0].id + is_admin = is_team_admin(current_team, current_user) if current_team else False + return jsonify( + { + "code": 0, + "msg": "success", + "data": { + "user": user, + "current_team": current_team, + "is_team_admin": is_admin, + }, + } + ) + + +app.register_blueprint(bp) diff --git a/server/utils/auth.py b/server/utils/auth.py index e008d6e5..f0ecf01b 100644 --- a/server/utils/auth.py +++ b/server/utils/auth.py @@ -6,7 +6,7 @@ def authenticated(func): @wraps(func) def wrapper(*args, **kwargs): - if "user_id" not in session: + if not session.get("user_id"): return abort(401) return func(*args, **kwargs) diff --git a/server/utils/user.py b/server/utils/user.py index 36ff98fa..50edc4e4 100644 --- a/server/utils/user.py +++ b/server/utils/user.py @@ -35,7 +35,7 @@ def register(code: str) -> str | None: ) db.session.add(new_user) - db.session.commit() + db.session.flush() new_bind_user = BindUser( id=ObjID.new_id(), diff --git a/server/utils/utils.py b/server/utils/utils.py new file mode 100644 index 00000000..6e2d621f --- /dev/null +++ b/server/utils/utils.py @@ -0,0 +1,5 @@ +def query_one_page(query, page, size): + offset = (page - 1) * int(size) + return ( + query.offset(offset if offset > 0 else 0).limit(size if size > 0 else 0).all() + )