Skip to content

Commit

Permalink
feat(app):
Browse files Browse the repository at this point in the history
- replace on_startup and on_shutdown to lifespan method
- fix /docs and /openapi.json problem
- notice done
- scheduler if id the same, then raise error
  • Loading branch information
MorvanZhou committed May 18, 2024
1 parent 2a5d4d8 commit b11f340
Show file tree
Hide file tree
Showing 36 changed files with 608 additions and 180 deletions.
82 changes: 51 additions & 31 deletions src/retk/application.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import os
from contextlib import asynccontextmanager

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

from retk import const, config, safety, utils
from retk.controllers.oauth import init_oauth_provider_map
from retk.controllers.self_hosted import notice_new_pkg_version
from retk.core import scheduler
from retk.core.files.importing import async_tasks
from retk.logger import logger, add_rotating_file_handler
from retk.plugins.register import register_official_plugins
from .models.client import client
from .routes import (
user,
Expand All @@ -22,9 +27,55 @@
statistic,
)


@asynccontextmanager
async def lifespan(app: FastAPI):
# on startup
if not config.is_local_db():
add_rotating_file_handler(
log_dir=const.settings.RETHINK_DIR.parent.parent / "logs",
max_bytes=10 * 1024 * 1024,
backup_count=10,
)
logger.debug(f'startup_event RETHINK_LOCAL_STORAGE_PATH: {os.environ.get("RETHINK_LOCAL_STORAGE_PATH")}')
logger.debug(f'startup_event VUE_APP_MODE: {os.environ.get("VUE_APP_MODE")}')
logger.debug(f'startup_event VUE_APP_API_PORT: {os.environ.get("VUE_APP_API_PORT")}')
logger.debug(f'startup_event RETHINK_DEFAULT_LANGUAGE: {os.environ.get("RETHINK_DEFAULT_LANGUAGE")}')
await client.init()
logger.debug("db initialized")

# schedule job
scheduler.start()
scheduler.init_tasks()

# init oauth provider map
init_oauth_provider_map()

# notice new pkg version
if config.is_local_db():
await notice_new_pkg_version()

# register official plugins
register_official_plugins()

# local finish up
utils.local_finish_up()

yield

# on shutdown
scheduler.stop()
await client.close()
logger.debug("fastapi shutdown event: db closed")

async_tasks.stop()
logger.debug("fastapi shutdown event: async_tasks stopped")


app = FastAPI(
docs_url="/docs",
openapi_url="/openapi.json",
lifespan=lifespan,
)

app.add_middleware(
Expand Down Expand Up @@ -58,34 +109,3 @@
app.include_router(self_hosted.node_file_router)
app.include_router(self_hosted.home_router)
self_hosted.mount_static(app=app)


@app.on_event("startup")
async def startup_event():
if not config.is_local_db():
add_rotating_file_handler(
log_dir=const.settings.RETHINK_DIR.parent.parent / "logs",
max_bytes=10 * 1024 * 1024,
backup_count=10,
)
logger.debug(f'startup_event RETHINK_LOCAL_STORAGE_PATH: {os.environ.get("RETHINK_LOCAL_STORAGE_PATH")}')
logger.debug(f'startup_event VUE_APP_MODE: {os.environ.get("VUE_APP_MODE")}')
logger.debug(f'startup_event VUE_APP_API_PORT: {os.environ.get("VUE_APP_API_PORT")}')
logger.debug(f'startup_event RETHINK_DEFAULT_LANGUAGE: {os.environ.get("RETHINK_DEFAULT_LANGUAGE")}')
await client.init()
logger.debug("db initialized")

# local finish up
utils.local_finish_up()

# schedule job
scheduler.start()
scheduler.init_tasks()


@app.on_event("shutdown")
async def shutdown_event():
scheduler.stop()
await client.close()
logger.debug("db closed")
logger.debug("shutdown_event")
1 change: 1 addition & 0 deletions src/retk/const/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

DOMAIN = "rethink.run"
RETHINK_DIR = Path(__file__).parent.parent
DOT_DATA = ".data"
FRONTEND_DIR = RETHINK_DIR / "dist-local"
MD_MAX_LENGTH = 100_000
REQUEST_ID_MAX_LENGTH = 50
Expand Down
2 changes: 1 addition & 1 deletion src/retk/controllers/files/upload_files.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
from retk.controllers import schemas
from retk.controllers.utils import (
AuthedUser,
datetime2str,
is_allowed_mime_type,
maybe_raise_json_exception,
)
from retk.utils import datetime2str


async def upload_obsidian_files(
Expand Down
35 changes: 33 additions & 2 deletions src/retk/controllers/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from retk.controllers.utils import maybe_raise_json_exception, json_exception
from retk.core import account, user, notice
from retk.models.tps import AuthedUser
from retk.utils import datetime2str


def __check_use_uid(au: AuthedUser, req: schemas.manager.GetUserRequest) -> bool:
Expand All @@ -18,13 +19,43 @@ def __check_use_uid(au: AuthedUser, req: schemas.manager.GetUserRequest) -> bool
async def get_user_info(
au: AuthedUser,
req: schemas.manager.GetUserRequest,
) -> schemas.user.UserInfoResponse:
) -> schemas.manager.GetUserResponse:
if __check_use_uid(au=au, req=req):
u, code = await user.get(uid=req.uid, disabled=None, exclude_manager=True)
else:
u, code = await user.get_by_email(email=req.email, disabled=None, exclude_manager=True)
maybe_raise_json_exception(au=au, code=code)
return schemas.user.get_user_info_response_from_u_dict(u=u, request_id=au.request_id)
return schemas.manager.GetUserResponse(
requestId=au.request_id,
user=schemas.manager.GetUserResponse.User(
id=u["id"],
source=u["source"],
account=u["account"],
nickname=u["nickname"],
email=u["email"],
avatar=u["avatar"],
disabled=u["disabled"],
createdAt=datetime2str(u["_id"].generation_time),
modifiedAt=datetime2str(u["modifiedAt"]),
usedSpace=u["usedSpace"],
type=u["type"],
lastState=schemas.manager.GetUserResponse.User.LastState(
nodeDisplayMethod=u["lastState"]["nodeDisplayMethod"],
nodeDisplaySortKey=u["lastState"]["nodeDisplaySortKey"],
recentSearch=u["lastState"]["recentSearch"],
recentCursorSearchSelectedNIds=u["lastState"]["recentCursorSearchSelectedNIds"],
),
settings=schemas.manager.GetUserResponse.User.Settings(
language=u["settings"]["language"],
theme=u["settings"]["theme"],
editorMode=u["settings"]["editorMode"],
editorFontSize=u["settings"]["editorFontSize"],
editorCodeTheme=u["settings"]["editorCodeTheme"],
editorSepRightWidth=u["settings"]["editorSepRightWidth"],
editorSideCurrentToolId=u["settings"]["editorSideCurrentToolId"],
),
),
)


async def disable_account(
Expand Down
4 changes: 2 additions & 2 deletions src/retk/controllers/node/node_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

from retk import const, core
from retk.controllers import schemas
from retk.controllers.utils import datetime2str, maybe_raise_json_exception
from retk.controllers.utils import maybe_raise_json_exception
from retk.models.tps import AuthedUser, Node
from retk.utils import contain_only_http_link, get_title_description_from_link
from retk.utils import contain_only_http_link, get_title_description_from_link, datetime2str


def __get_node_data(n: Node) -> schemas.node.NodeData:
Expand Down
2 changes: 1 addition & 1 deletion src/retk/controllers/oauth.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
user_source_map: Dict[str, int] = {}


def init_provider_map():
def init_oauth_provider_map():
sso_map.update({
"github": GithubSSO(
client_id=config.get_settings().OAUTH_CLIENT_ID_GITHUB,
Expand Down
2 changes: 1 addition & 1 deletion src/retk/controllers/schemas/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class LoginRequest(BaseModel):

class EmailVerificationRequest(BaseModel):
email: str = Field(max_length=settings.EMAIL_MAX_LENGTH)
userExistOk: bool = Field(type=bool, default=False)
userExistOk: bool = Field(default=False)
captchaToken: str = Field(max_length=2000)
captchaCode: str = Field(max_length=10)
language: LanguageEnum = LanguageEnum.EN
Expand Down
35 changes: 35 additions & 0 deletions src/retk/controllers/schemas/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,41 @@ class GetUserRequest(BaseModel):
uid: Optional[str] = Field(max_length=settings.UID_MAX_LENGTH, default=None)


class GetUserResponse(BaseModel):
class User(BaseModel):
class LastState(BaseModel):
nodeDisplayMethod: int
nodeDisplaySortKey: str
recentSearch: List[str]
recentCursorSearchSelectedNIds: List[str]

class Settings(BaseModel):
language: str
theme: str
editorMode: str
editorFontSize: int
editorCodeTheme: str
editorSepRightWidth: int
editorSideCurrentToolId: str

id: str
source: int
account: str
nickname: str
email: str
avatar: str
disabled: bool
createdAt: str
modifiedAt: str
usedSpace: int
type: int
lastState: LastState
settings: Settings

requestId: str = Field(max_length=settings.REQUEST_ID_MAX_LENGTH, description="request ID")
user: User = Field(description="user info")


class ManagerNoticeDeliveryRequest(BaseModel):
title: str = Field(
max_length=settings.MAX_SYSTEM_NOTICE_TITLE_LENGTH,
Expand Down
13 changes: 7 additions & 6 deletions src/retk/controllers/schemas/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@

from retk import config
from retk.const import settings, LanguageEnum, app, NodeDisplaySortKeyEnum, USER_TYPE
from retk.controllers.utils import datetime2str
from retk.models import tps
from retk.utils import datetime2str


class UserInfoResponse(BaseModel):
Expand Down Expand Up @@ -103,16 +103,17 @@ def get_user_info_response_from_u_dict(


class NotificationResponse(BaseModel):
class Data(BaseModel):
class System(BaseModel):
class System(BaseModel):
class Notice(BaseModel):
id: str
title: str
content: str
publishAt: datetime
publishAt: str
read: bool
readTime: Optional[datetime]

system: List[System]
total: int
notices: List[Notice]

requestId: str
data: Data
system: System
38 changes: 31 additions & 7 deletions src/retk/controllers/user.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
from retk import const, core, config
from retk.controllers import schemas
from retk.controllers.utils import datetime2str, maybe_raise_json_exception
from retk.controllers.utils import maybe_raise_json_exception
from retk.core import account, notice
from retk.core.user import reset_password
from retk.models.tps import AuthedUser
from retk.utils import mask_email, regex
from retk.utils import mask_email, regex, datetime2str


async def get_user(
Expand Down Expand Up @@ -88,15 +88,39 @@ async def get_user_notices(

return schemas.user.NotificationResponse(
requestId=au.request_id,
data=schemas.user.NotificationResponse.Data(
system=[
schemas.user.NotificationResponse.Data.System(
id=str(n["noticeId"]),
system=schemas.user.NotificationResponse.System(
total=nt["system"]["total"],
notices=[
schemas.user.NotificationResponse.System.Notice(
id=n["id"],
title=n["title"],
content=n["content"],
publishAt=n["publishAt"],
read=n["read"],
readTime=n["readTime"],
) for n in nt["system"]],
) for n in nt["system"]["notices"]],
),
)


async def mark_system_notice_read(
au: AuthedUser,
notice_id: str,
) -> schemas.RequestIdResponse:
code = await notice.mark_system_notice_read(au=au, notice_id=notice_id)
maybe_raise_json_exception(au=au, code=code)

return schemas.RequestIdResponse(
requestId=au.request_id,
)


async def mark_all_system_notice_read(
au: AuthedUser,
) -> schemas.RequestIdResponse:
code = await notice.mark_all_system_notice_read(au=au)
maybe_raise_json_exception(au=au, code=code)

return schemas.RequestIdResponse(
requestId=au.request_id,
)
5 changes: 0 additions & 5 deletions src/retk/controllers/utils.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import datetime
import inspect
from typing import Sequence
from urllib.parse import urlparse
Expand All @@ -10,10 +9,6 @@
from retk.models.tps import AuthedUser


def datetime2str(dt: datetime.datetime) -> str:
return dt.strftime('%Y-%m-%dT%H:%M:%SZ')


def is_allowed_mime_type(data_url, allowed_mime_types: Sequence[str]):
try:
result = urlparse(data_url)
Expand Down
Loading

0 comments on commit b11f340

Please sign in to comment.