Coverage for opt/mealie/lib/python3.12/site-packages/mealie/app.py: 91%

70 statements  

« prev     ^ index     » next       coverage.py v7.10.6, created at 2025-11-25 15:48 +0000

1import re 1a

2import warnings 1a

3 

4# pyrdfa3 is no longer being updated and has docstrings that emit syntax warnings 

5warnings.filterwarnings( 1a

6 "ignore", module=".*pyRdfa", category=SyntaxWarning, message=re.escape("invalid escape sequence '\\-'") 

7) 

8 

9# ruff: noqa: E402 

10from collections.abc import AsyncGenerator 1a

11from contextlib import asynccontextmanager 1a

12 

13import uvicorn 1a

14from fastapi import FastAPI 1a

15from fastapi.middleware.cors import CORSMiddleware 1a

16from fastapi.middleware.gzip import GZipMiddleware 1a

17from fastapi.routing import APIRoute 1a

18from starlette.middleware.sessions import SessionMiddleware 1a

19 

20from mealie.core.config import get_app_settings 1a

21from mealie.core.root_logger import get_logger 1a

22from mealie.core.settings.static import APP_VERSION 1a

23from mealie.routes import router, spa, utility_routes 1a

24from mealie.routes.handlers import register_debug_handler 1a

25from mealie.routes.media import media_router 1a

26from mealie.services.scheduler import SchedulerRegistry, SchedulerService, tasks 1a

27 

28settings = get_app_settings() 1a

29 

30description = """ 1a

31Mealie is a web application for managing your recipes, meal plans, and shopping lists. This is the Restful 

32API interactive documentation that can be used to explore the API. If you're justing getting started with 

33the API and want to get started quickly, you can use the 

34[API Usage | Mealie Docs](https://docs.mealie.io/documentation/getting-started/api-usage/) 

35as a reference for how to get started. 

36 

37 

38If you have any questions or comments about mealie, please use the discord server to talk to the developers or other 

39community members. If you'd like to file an issue, please use the 

40[GitHub Issue Tracker | Mealie](https://github.com/mealie-recipes/mealie/issues/new/choose) 

41 

42 

43## Helpful Links 

44- [Home Page](https://mealie.io) 

45- [Documentation](https://docs.mealie.io) 

46- [Discord](https://discord.gg/QuStdQGSGK) 

47- [Demo](https://demo.mealie.io) 

48""" 

49 

50logger = get_logger() 1a

51 

52 

53@asynccontextmanager 1a

54async def lifespan_fn(_: FastAPI) -> AsyncGenerator[None, None]: 1a

55 """ 

56 lifespan_fn controls the startup and shutdown of the FastAPI Application. 

57 This function is called when the FastAPI application starts and stops. 

58 

59 See FastAPI documentation for more information: 

60 - https://fastapi.tiangolo.com/advanced/events/ 

61 """ 

62 logger.info("start: database initialization") 1a

63 import mealie.db.init_db as init_db 1a

64 

65 init_db.main() 1a

66 logger.info("end: database initialization") 1a

67 

68 await start_scheduler() 1a

69 

70 logger.info("-----SYSTEM STARTUP-----") 1a

71 logger.info("------APP SETTINGS------") 1a

72 logger.info( 1a

73 settings.model_dump_json( 

74 indent=4, 

75 exclude={ 

76 "SECRET", 

77 "SESSION_SECRET", 

78 "DB_URL", # replace by DB_URL_PUBLIC for logs 

79 "DB_PROVIDER", 

80 }, 

81 ) 

82 ) 

83 logger.info("------APP FEATURES------") 1a

84 logger.info("--------==SMTP==--------") 1a

85 logger.info(settings.SMTP_FEATURE) 1a

86 logger.info("--------==LDAP==--------") 1a

87 logger.info(settings.LDAP_FEATURE) 1a

88 logger.info("--------==OIDC==--------") 1a

89 logger.info(settings.OIDC_FEATURE) 1a

90 logger.info("-------==OPENAI==-------") 1a

91 logger.info(settings.OPENAI_FEATURE) 1a

92 logger.info("------------------------") 1a

93 

94 yield 1ab

95 

96 logger.info("-----SYSTEM SHUTDOWN----- \n") 1b

97 

98 

99app = FastAPI( 1a

100 title="Mealie", 

101 description=description, 

102 version=APP_VERSION, 

103 docs_url=settings.DOCS_URL, 

104 redoc_url=settings.REDOC_URL, 

105 lifespan=lifespan_fn, 

106) 

107 

108app.add_middleware(GZipMiddleware, minimum_size=1000) 1a

109app.add_middleware(SessionMiddleware, secret_key=settings.SESSION_SECRET) 1a

110 

111if not settings.PRODUCTION: 111 ↛ 112line 111 didn't jump to line 112 because the condition on line 111 was never true1a

112 allowed_origins = ["http://localhost:3000"] 

113 

114 app.add_middleware( 

115 CORSMiddleware, 

116 allow_origins=allowed_origins, 

117 allow_credentials=True, 

118 allow_methods=["*"], 

119 allow_headers=["*"], 

120 ) 

121 

122register_debug_handler(app) 1a

123 

124 

125async def start_scheduler(): 1a

126 SchedulerRegistry.register_daily( 1a

127 tasks.purge_expired_tokens, 

128 tasks.purge_group_registration, 

129 tasks.purge_password_reset_tokens, 

130 tasks.purge_group_data_exports, 

131 tasks.create_mealplan_timeline_events, 

132 tasks.delete_old_checked_list_items, 

133 ) 

134 

135 SchedulerRegistry.register_minutely( 1a

136 tasks.post_group_webhooks, 

137 ) 

138 

139 SchedulerRegistry.register_hourly( 1a

140 tasks.locked_user_reset, 

141 ) 

142 

143 SchedulerRegistry.print_jobs() 1a

144 

145 await SchedulerService.start() 1a

146 

147 

148def api_routers(): 1a

149 app.include_router(router) 1a

150 app.include_router(media_router) 1a

151 app.include_router(utility_routes.router) 1a

152 

153 if settings.PRODUCTION and not settings.TESTING: 153 ↛ exitline 153 didn't return from function 'api_routers' because the condition on line 153 was always true1a

154 spa.mount_spa(app) 1a

155 

156 

157api_routers() 1a

158 

159# fix routes that would get their tags duplicated by use of @controller, 

160# leading to duplicate definitions in the openapi spec 

161for route in app.routes: 1a

162 if isinstance(route, APIRoute): 1a

163 route.tags = list(set(route.tags)) 1a

164 

165 

166def main(): 1a

167 uvicorn.run( 

168 "app:app", 

169 host="0.0.0.0", 

170 port=settings.API_PORT, 

171 reload=True, 

172 reload_dirs=["mealie"], 

173 reload_delay=2, 

174 log_level="info", 

175 use_colors=True, 

176 log_config=None, 

177 workers=1, 

178 forwarded_allow_ips="*", 

179 ) 

180 

181 

182if __name__ == "__main__": 182 ↛ 183line 182 didn't jump to line 183 because the condition on line 182 was never true1a

183 main()