diff --git a/backend/app/api/api_v1/routers/auth.py b/backend/app/api/api_v1/routers/auth.py index d880a10..efc1a4d 100644 --- a/backend/app/api/api_v1/routers/auth.py +++ b/backend/app/api/api_v1/routers/auth.py @@ -1,6 +1,7 @@ -from fastapi import Form -from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm +from fastapi import Request +from fastapi.responses import JSONResponse +from fastapi.security import OAuth2PasswordRequestForm from fastapi import APIRouter, Depends, HTTPException, status from datetime import timedelta @@ -13,7 +14,7 @@ auth_router = r = APIRouter() @r.post("/token") -async def login(db:Session= Depends(get_db) ,form_data: OAuth2PasswordRequestForm = Depends()): +async def login(request: Request,db:Session= Depends(get_db) ,form_data: OAuth2PasswordRequestForm = Depends()): if not db : raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, @@ -45,9 +46,10 @@ async def login(db:Session= Depends(get_db) ,form_data: OAuth2PasswordRequestFor data={"sub": user.id,"roles":roles,"permissions": permissions,"tenant":user.tenantid,}, expires_delta=access_token_expires, ) - - return {"access_token": access_token, "token_type": "bearer","user_name":user.first_name + " " + user.last_name} - + return JSONResponse( + status_code=200, + content={"access_token": access_token, "token_type": "bearer","user_name":user.first_name + " " + user.last_name} + ) @r.post("/signup") async def signup( diff --git a/backend/app/api/api_v1/routers/kintone.py b/backend/app/api/api_v1/routers/kintone.py index 947c50b..5fe0622 100644 --- a/backend/app/api/api_v1/routers/kintone.py +++ b/backend/app/api/api_v1/routers/kintone.py @@ -16,18 +16,18 @@ from app.db.cruddb import domainService kinton_router = r = APIRouter() -def getkintoneenv(user = Depends(get_current_user)): - db = get_db(user.tenantid) #SessionLocal() +def getkintoneenv(user = Depends(get_current_user),db = Depends(get_db)): + #db = SessionLocal() domain = domainService.get_default_domain(db,user.id) #get_activedomain(db, user.id) - db.close() + #db.close() kintoneevn = config.KINTONE_ENV(domain) return kintoneevn -def getkintoneformat(user = Depends(get_current_user)): - db = get_db(user.tenantid)#SessionLocal() +def getkintoneformat(user = Depends(get_current_user),db = Depends(get_db)): + #db = SessionLocal() formats = get_kintoneformat(db) - db.close() + #db.close() return formats diff --git a/backend/app/core/operation.py b/backend/app/core/operation.py new file mode 100644 index 0000000..7b5d457 --- /dev/null +++ b/backend/app/core/operation.py @@ -0,0 +1,52 @@ + +from fastapi import Request +from starlette.middleware.base import BaseHTTPMiddleware +from sqlalchemy.orm import Session +from app.db.models import OperationLog,User + +from functools import wraps +from fastapi import Request + + + +def log_operation(func): + """自定义装饰器用于记录操作日志""" + @wraps(func) + async def wrapper(*args, **kwargs): + if 'request' in kwargs and isinstance(kwargs['request'], Request): + request: Request = kwargs['request'] + method = request.method + url = str(request.url) + client_ip = request.client.host + headers = dict(request.headers) + user_agent = headers.get("user-agent", "") + + if 'db' in kwargs and isinstance(kwargs['db'], Session): + db = kwargs['db'] + if 'user' in kwargs and isinstance(kwargs['user'], User): + user = kwargs['user'] + user_id = user.id + tenant_id = user.tenantid + else: + user_id = None + tenant_id = None + + + try: + response = await func(*args, **kwargs) + if db: + db_operation = OperationLog(tenantid =tenant_id, + clientip =client_ip, + useragent =user_agent, + userid = user_id, + operation = method, + function = url, + response = f"status_code:{response.status_code }" ) + + db.add(db_operation) + db.commit() + except Exception as e: + raise e + return response + return wrapper + diff --git a/backend/app/db/cruddb/dbapp.py b/backend/app/db/cruddb/dbapp.py index 92a1ce5..ca3fa13 100644 --- a/backend/app/db/cruddb/dbapp.py +++ b/backend/app/db/cruddb/dbapp.py @@ -98,6 +98,7 @@ class dbapp(crudbase): db_app = self.get_app(db,domainurl,newversion.appid) if db_app: db_app.version = dbappversion.get_app_latestversion(db,domainurl,newversion.appid)+1 + db_app.updateuserid = userid, appversion = models.AppVersion( domainurl = db_app.domainurl, appid=db_app.appid, @@ -139,6 +140,7 @@ class dbapp(crudbase): if not db_appversion: return None db_app.version = version + db_app.versionname = db_appversion.versionname db_app.updateuserid = userid db.add(db_app) diff --git a/backend/app/db/models.py b/backend/app/db/models.py index 3dbe8fc..6f0ef3c 100644 --- a/backend/app/db/models.py +++ b/backend/app/db/models.py @@ -67,6 +67,7 @@ class App(Base): appname = mapped_column(String(200), nullable=False) appid = mapped_column(String(100), index=True, nullable=False) version = mapped_column(Integer) + versionname = mapped_column(String(200), nullable=False) createuserid = mapped_column(Integer,ForeignKey("user.id")) updateuserid = mapped_column(Integer,ForeignKey("user.id")) createuser = relationship('User',foreign_keys=[createuserid]) @@ -87,6 +88,7 @@ class AppVersion(Base): updateuser = relationship('User',foreign_keys=[updateuserid]) + class AppSetting(Base): __tablename__ = "appsetting" @@ -225,11 +227,12 @@ class OperationLog(Base): __tablename__ = "operationlog" tenantid = mapped_column(String(100)) - domainurl = mapped_column(String(200)) + clientip = mapped_column(String(200)) + useragent = mapped_column(String(200)) userid = mapped_column(Integer,ForeignKey("user.id")) operation = mapped_column(String(200)) function = mapped_column(String(200)) - detail = mapped_column(String(200)) + response = mapped_column(String(200)) user = relationship('User') class KintoneFormat(Base): diff --git a/backend/app/db/schemas.py b/backend/app/db/schemas.py index 7e187ff..6753bc2 100644 --- a/backend/app/db/schemas.py +++ b/backend/app/db/schemas.py @@ -83,8 +83,11 @@ class AppList(Base): domainurl: str appname: str appid:str - updateuser: UserOut version:int + versionname: t.Optional[str] = None + updateuser: UserOut + createuser: UserOut + class AppVersion(BaseModel): domainurl: str diff --git a/backend/app/tests/conftest.py b/backend/app/tests/conftest.py index d7bb3d4..6f1fe95 100644 --- a/backend/app/tests/conftest.py +++ b/backend/app/tests/conftest.py @@ -70,7 +70,7 @@ def password(): return "password" @pytest.fixture(scope="session") -def user(password,test_tenant_id): +def user(test_db,password,test_tenant_id): user = models.User( email = "user@test.com", first_name = "user", @@ -80,10 +80,13 @@ def user(password,test_tenant_id): is_superuser = False, tenantid = test_tenant_id ) - return user + test_db.add(user) + test_db.commit() + test_db.refresh(user) + return user.__dict__ @pytest.fixture(scope="session") -def admin(password,test_tenant_id): +def admin(test_db,password,test_tenant_id): user = models.User( email = "admin@test.com", first_name = "admin", @@ -93,22 +96,25 @@ def admin(password,test_tenant_id): is_superuser = True, tenantid =test_tenant_id ) - return user - -@pytest.fixture(scope="session") -def login_user(test_db,test_client,user,password): test_db.add(user) test_db.commit() test_db.refresh(user) - response = test_client.post("/api/token", data={"username": user.email, "password":password }) + return user.__dict__ + +@pytest.fixture(scope="session") +def login_user(test_db,test_client,user,password): + # test_db.add(user) + # test_db.commit() + #test_db.refresh(user) + response = test_client.post("/api/token", data={"username": user["email"], "password":password }) return response.json()["access_token"] @pytest.fixture(scope="session") def login_admin(test_db,test_client,admin,password): - test_db.add(admin) - test_db.commit() - test_db.refresh(admin) - response = test_client.post("/api/token", data={"username": admin.email, "password":password }) + # test_db.add(admin) + # test_db.commit() + #test_db.refresh(admin) + response = test_client.post("/api/token", data={"username": admin["email"], "password":password }) return response.json()["access_token"] @pytest.fixture(scope="session") diff --git a/backend/app/tests/test_user.py b/backend/app/tests/test_user.py index 2b2c142..b8d5d7e 100644 --- a/backend/app/tests/test_user.py +++ b/backend/app/tests/test_user.py @@ -1,4 +1,5 @@ +import logging def test_users_list(test_client,login_user): response = test_client.get("/api/v1/users", headers={"Authorization": "Bearer " + login_user}) @@ -12,7 +13,7 @@ def test_users_list_for_admin(test_client,login_admin): assert response.status_code == 200 data = response.json() assert "data" in data - assert len(data["data"]) == 3 + assert len(data["data"]) == 3 def test_user_create(test_client,login_user): user_data = { @@ -24,8 +25,9 @@ def test_user_create(test_client,login_user): "is_superuser": False } response = test_client.post("/api/v1/users", json=user_data, headers={"Authorization": "Bearer " + login_user}) - assert response.status_code == 200 data = response.json() + logging.error(data) + assert response.status_code == 200 assert "data" in data assert data["data"] is not None assert data["data"]["id"] > 0 @@ -59,8 +61,9 @@ def test_admin_create_for_admin(test_client,login_admin): "is_superuser": True } response = test_client.post("/api/v1/users", json=user_data, headers={"Authorization": "Bearer " + login_admin}) - assert response.status_code == 200 data = response.json() + logging.error(data) + assert response.status_code == 200 assert "data" in data assert data["data"] is not None assert data["data"]["id"] > 0 @@ -70,22 +73,23 @@ def test_admin_create_for_admin(test_client,login_admin): assert data["data"]["is_active"] == user_data["is_active"] assert data["data"]["is_superuser"] == user_data["is_superuser"] -def test_user_details(test_client, login_user_id, login_user,user): +def test_user_details(test_client,login_user_id, login_user,user): id = login_user_id response = test_client.get("/api/v1/users/"+ str(id), headers={"Authorization": "Bearer " + login_user}) - assert response.status_code == 200 data = response.json() - assert data["data"]["email"] ==user.email - assert data["data"]["first_name"] == user.first_name - assert data["data"]["last_name"] == user.last_name - assert data["data"]["is_active"] == user.is_active - assert data["data"]["is_superuser"] == user.is_superuser + logging.error(data) + assert response.status_code == 200 + assert data["data"]["email"] == user["email"] + assert data["data"]["first_name"] == user["first_name"] + assert data["data"]["last_name"] == user["last_name"] + assert data["data"]["is_active"] == user["is_active"] + assert data["data"]["is_superuser"] == user["is_superuser"] assert data["data"]["id"] == id def test_user_edit(test_client, login_user_id,login_user,user): id = login_user_id user_data = { - "email": user.email, + "email": user["email"], "first_name": "Updated", "last_name": "test", "is_active": True, @@ -95,10 +99,10 @@ def test_user_edit(test_client, login_user_id,login_user,user): assert response.status_code == 200 data = response.json() - assert data["data"]["email"] == user.email + assert data["data"]["email"] == user["email"] assert data["data"]["first_name"] == user_data["first_name"] assert data["data"]["last_name"] == user_data["last_name"] - assert data["data"]["is_active"] == user.is_active + assert data["data"]["is_active"] == user["is_active"] assert data["data"]["id"] == id def test_user_delete(test_client, login_user): diff --git a/backend/app/tests/test_user_kintone.py b/backend/app/tests/test_user_kintone.py new file mode 100644 index 0000000..1e644bc --- /dev/null +++ b/backend/app/tests/test_user_kintone.py @@ -0,0 +1,9 @@ +import logging +def test_get_allapps(test_client,test_domain,login_user): + response = test_client.get("/api/v1/allapps", headers={"Authorization": "Bearer " + login_user}) + data = response.json() + logging.error(data) + assert response.status_code == 200 + assert "apps" in data + assert data["apps"] is not None + assert len(data["apps"]) > 0 \ No newline at end of file