Coverage for .venv/lib/python3.13/site-packages/litellm/proxy/common_utils/html_forms/ui_login.py: 67%

13 statements  

« prev     ^ index     » next       coverage.py v7.15.2, created at 2026-10-10 12:01 +0000

1import os 

2from typing import Final 

3 

4from litellm.proxy.utils import get_custom_url 

5 

6url_to_redirect_to = os.getenv("PROXY_BASE_URL", "") 

7server_root_path: Final = os.getenv("SERVER_ROOT_PATH", "") 

8if server_root_path != "": 8 ↛ 9line 8 didn't jump to line 9 because the condition on line 8 was never true

9 url_to_redirect_to += server_root_path 

10url_to_redirect_to += "/login" 

11new_ui_login_url: Final = get_custom_url("", "ui/login") 

12 

13 

14def build_ui_login_form( 

15 show_deprecation_banner: bool = False, 

16 hide_default_credentials_hint: bool = False, 

17) -> str: 

18 banner_html: Final = ( 

19 f""" 

20 <div class="deprecation-banner"> 

21 <strong>Deprecated:</strong> Logging in with username and password on this page is deprecated. 

22 Please use the <a href="{new_ui_login_url}">new login page</a> instead. 

23 This page will be dedicated to signing in via SSO in the future. 

24 </div> 

25 """ 

26 if show_deprecation_banner 

27 else "" 

28 ) 

29 

30 info_box_html: Final = ( 

31 "" 

32 if hide_default_credentials_hint 

33 else """ 

34 <div class="info-box"> 

35 <div class="info-header"> 

36 <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> 

37 <circle cx="12" cy="12" r="10"></circle> 

38 <line x1="12" y1="16" x2="12" y2="12"></line> 

39 <line x1="12" y1="8" x2="12.01" y2="8"></line> 

40 </svg> 

41 Default Credentials 

42 </div> 

43 <p>By default, Username is <code>admin</code> and Password is your set LiteLLM Proxy <code>MASTER_KEY</code>.</p> 

44 <p>Need to set UI credentials or SSO? <a href="https://docs.litellm.ai/docs/proxy/ui" target="_blank">Check the documentation</a>.</p> 

45 </div> 

46 """ 

47 ) 

48 

49 return f""" 

50<!DOCTYPE html> 

51<html lang="en"> 

52<head> 

53 <meta charset="UTF-8"> 

54 <title>LiteLLM Login</title> 

55 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 

56 <style> 

57 body {{ 

58 font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif; 

59 background-color: #f8fafc; 

60 margin: 0; 

61 padding: 20px; 

62 display: flex; 

63 justify-content: center; 

64 align-items: center; 

65 min-height: 100vh; 

66 color: #333; 

67 }} 

68 

69 form {{ 

70 background-color: #fff; 

71 padding: 40px; 

72 border-radius: 8px; 

73 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); 

74 width: 450px; 

75 max-width: 100%; 

76 }} 

77  

78 .logo-container {{ 

79 text-align: center; 

80 margin-bottom: 30px; 

81 }} 

82  

83 .logo {{ 

84 font-size: 24px; 

85 font-weight: 600; 

86 color: #1e293b; 

87 }} 

88  

89 h2 {{ 

90 margin: 0 0 10px; 

91 color: #1e293b; 

92 font-size: 28px; 

93 font-weight: 600; 

94 text-align: center; 

95 }} 

96  

97 .subtitle {{ 

98 color: #64748b; 

99 margin: 0 0 20px; 

100 font-size: 16px; 

101 text-align: center; 

102 }} 

103 

104 .info-box {{ 

105 background-color: #f1f5f9; 

106 border-radius: 6px; 

107 padding: 20px; 

108 margin-bottom: 30px; 

109 border-left: 4px solid #2563eb; 

110 }} 

111  

112 .info-header {{ 

113 display: flex; 

114 align-items: center; 

115 margin-bottom: 12px; 

116 color: #1e40af; 

117 font-weight: 600; 

118 font-size: 16px; 

119 }} 

120  

121 .info-header svg {{ 

122 margin-right: 8px; 

123 }} 

124  

125 .info-box p {{ 

126 color: #475569; 

127 margin: 8px 0; 

128 line-height: 1.5; 

129 font-size: 14px; 

130 }} 

131 

132 label {{ 

133 display: block; 

134 margin-bottom: 8px; 

135 font-weight: 500; 

136 color: #334155; 

137 font-size: 14px; 

138 }} 

139  

140 .required {{ 

141 color: #dc2626; 

142 margin-left: 2px; 

143 }} 

144 

145 input[type="text"], 

146 input[type="password"] {{ 

147 width: 100%; 

148 padding: 10px 14px; 

149 margin-bottom: 20px; 

150 box-sizing: border-box; 

151 border: 1px solid #e2e8f0; 

152 border-radius: 6px; 

153 font-size: 15px; 

154 color: #1e293b; 

155 background-color: #fff; 

156 transition: border-color 0.2s, box-shadow 0.2s; 

157 }} 

158  

159 input[type="text"]:focus, 

160 input[type="password"]:focus {{ 

161 outline: none; 

162 border-color: #3b82f6; 

163 box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.2); 

164 }} 

165 

166 .toggle-password {{ 

167 display: flex; 

168 align-items: center; 

169 margin-top: -15px; 

170 margin-bottom: 20px; 

171 }} 

172  

173 .toggle-password input[type="checkbox"] {{ 

174 margin-right: 8px; 

175 vertical-align: middle; 

176 width: 16px; 

177 height: 16px; 

178 }} 

179  

180 .toggle-password label {{ 

181 margin-bottom: 0; 

182 font-size: 14px; 

183 cursor: pointer; 

184 line-height: 1; 

185 }} 

186 

187 input[type="submit"] {{ 

188 background-color: #6466E9; 

189 color: #fff; 

190 cursor: pointer; 

191 font-weight: 500; 

192 border: none; 

193 padding: 10px 16px; 

194 transition: background-color 0.2s; 

195 border-radius: 6px; 

196 margin-top: 10px; 

197 font-size: 14px; 

198 width: 100%; 

199 }} 

200 

201 input[type="submit"]:hover {{ 

202 background-color: #4138C2; 

203 }} 

204  

205 a {{ 

206 color: #3b82f6; 

207 text-decoration: none; 

208 }} 

209 

210 a:hover {{ 

211 text-decoration: underline; 

212 }} 

213  

214 code {{ 

215 background-color: #f1f5f9; 

216 padding: 2px 4px; 

217 border-radius: 4px; 

218 font-family: monospace; 

219 font-size: 13px; 

220 color: #334155; 

221 }} 

222  

223 .help-text {{ 

224 color: #64748b; 

225 font-size: 14px; 

226 margin-top: -12px; 

227 margin-bottom: 20px; 

228 }} 

229 

230 .deprecation-banner {{ 

231 background-color: #fee2e2; 

232 border: 1px solid #ef4444; 

233 color: #991b1b; 

234 padding: 14px 16px; 

235 border-radius: 6px; 

236 margin-bottom: 20px; 

237 font-size: 14px; 

238 line-height: 1.5; 

239 }} 

240 

241 .deprecation-banner a {{ 

242 color: #991b1b; 

243 font-weight: 600; 

244 text-decoration: underline; 

245 }} 

246 </style> 

247</head> 

248<body> 

249 <form action="{url_to_redirect_to}" method="post"> 

250 {banner_html} 

251 <div class="logo-container"> 

252 <div class="logo"> 

253 🚅 LiteLLM 

254 </div> 

255 </div> 

256 <h2>Login</h2> 

257 <p class="subtitle">Access your LiteLLM Admin UI.</p> 

258 {info_box_html} 

259 <label for="username">Username<span class="required">*</span></label> 

260 <input type="text" id="username" name="username" required placeholder="Enter your username" autocomplete="username"> 

261  

262 <label for="password">Password<span class="required">*</span></label> 

263 <input type="password" id="password" name="password" required placeholder="Enter your password" autocomplete="current-password"> 

264 <div class="toggle-password"> 

265 <input type="checkbox" id="show-password" onclick="togglePasswordVisibility()"> 

266 <label for="show-password">Show password</label> 

267 </div> 

268 <input type="submit" value="Login"> 

269 </form> 

270 <script> 

271 function togglePasswordVisibility() {{ 

272 var passwordField = document.getElementById("password"); 

273 passwordField.type = passwordField.type === "password" ? "text" : "password"; 

274 }} 

275 </script> 

276</body> 

277</html> 

278"""