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
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-10 12:01 +0000
1import os
2from typing import Final
4from litellm.proxy.utils import get_custom_url
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")
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 )
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 )
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 }}
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 }}
78 .logo-container {{
79 text-align: center;
80 margin-bottom: 30px;
81 }}
83 .logo {{
84 font-size: 24px;
85 font-weight: 600;
86 color: #1e293b;
87 }}
89 h2 {{
90 margin: 0 0 10px;
91 color: #1e293b;
92 font-size: 28px;
93 font-weight: 600;
94 text-align: center;
95 }}
97 .subtitle {{
98 color: #64748b;
99 margin: 0 0 20px;
100 font-size: 16px;
101 text-align: center;
102 }}
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 }}
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 }}
121 .info-header svg {{
122 margin-right: 8px;
123 }}
125 .info-box p {{
126 color: #475569;
127 margin: 8px 0;
128 line-height: 1.5;
129 font-size: 14px;
130 }}
132 label {{
133 display: block;
134 margin-bottom: 8px;
135 font-weight: 500;
136 color: #334155;
137 font-size: 14px;
138 }}
140 .required {{
141 color: #dc2626;
142 margin-left: 2px;
143 }}
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 }}
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 }}
166 .toggle-password {{
167 display: flex;
168 align-items: center;
169 margin-top: -15px;
170 margin-bottom: 20px;
171 }}
173 .toggle-password input[type="checkbox"] {{
174 margin-right: 8px;
175 vertical-align: middle;
176 width: 16px;
177 height: 16px;
178 }}
180 .toggle-password label {{
181 margin-bottom: 0;
182 font-size: 14px;
183 cursor: pointer;
184 line-height: 1;
185 }}
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 }}
201 input[type="submit"]:hover {{
202 background-color: #4138C2;
203 }}
205 a {{
206 color: #3b82f6;
207 text-decoration: none;
208 }}
210 a:hover {{
211 text-decoration: underline;
212 }}
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 }}
223 .help-text {{
224 color: #64748b;
225 font-size: 14px;
226 margin-top: -12px;
227 margin-bottom: 20px;
228 }}
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 }}
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">
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"""