@ycniuqton/devlens 0.1.15 → 0.1.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ export declare const authRouter: import("express-serve-static-core").Router;
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.authRouter = void 0;
4
+ const express_1 = require("express");
5
+ exports.authRouter = (0, express_1.Router)();
6
+ exports.authRouter.get('/status', (req, res) => {
7
+ res.json({ authenticated: !!req.session.authenticated });
8
+ });
9
+ exports.authRouter.post('/login', async (req, res) => {
10
+ const { username, password } = req.body;
11
+ const authService = req.app.locals.authService;
12
+ try {
13
+ const valid = await authService.validateCredentials(username, password);
14
+ if (valid) {
15
+ req.session.authenticated = true;
16
+ res.json({ ok: true });
17
+ }
18
+ else {
19
+ res.status(401).json({ error: 'Invalid username or password' });
20
+ }
21
+ }
22
+ catch (err) {
23
+ res.status(500).json({ error: err.message });
24
+ }
25
+ });
26
+ exports.authRouter.post('/logout', (req, res) => {
27
+ req.session.destroy(() => { });
28
+ res.json({ ok: true });
29
+ });
30
+ exports.authRouter.post('/change-password', async (req, res) => {
31
+ if (!req.session.authenticated)
32
+ return res.status(401).json({ error: 'Not authenticated' });
33
+ const { currentPassword, newPassword } = req.body;
34
+ if (!newPassword || newPassword.length < 4)
35
+ return res.status(400).json({ error: 'Password must be at least 4 characters' });
36
+ const authService = req.app.locals.authService;
37
+ try {
38
+ const valid = await authService.validateCredentials(authService.getUsername(), currentPassword);
39
+ if (!valid)
40
+ return res.status(401).json({ error: 'Current password is incorrect' });
41
+ await authService.changePassword(newPassword);
42
+ res.json({ ok: true });
43
+ }
44
+ catch (err) {
45
+ res.status(500).json({ error: err.message });
46
+ }
47
+ });
48
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/routes/auth.ts"],"names":[],"mappings":"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"}
package/dist/server.js CHANGED
@@ -7,6 +7,7 @@ exports.createServer = createServer;
7
7
  const express_1 = __importDefault(require("express"));
8
8
  const http_1 = __importDefault(require("http"));
9
9
  const path_1 = __importDefault(require("path"));
10
+ const express_session_1 = __importDefault(require("express-session"));
10
11
  const ws_1 = require("ws");
11
12
  const chokidar_1 = __importDefault(require("chokidar"));
12
13
  const git_1 = require("./services/git");
@@ -14,23 +15,32 @@ const watcher_1 = require("./services/watcher");
14
15
  const taskStore_1 = require("./services/taskStore");
15
16
  const rules_1 = require("./services/rules");
16
17
  const settings_1 = require("./services/settings");
18
+ const auth_1 = require("./services/auth");
17
19
  const diff_1 = require("./routes/diff");
18
20
  const tasks_1 = require("./routes/tasks");
19
21
  const integrations_1 = require("./routes/integrations");
20
22
  const rules_2 = require("./routes/rules");
21
23
  const browser_1 = require("./routes/browser");
22
24
  const settings_2 = require("./routes/settings");
25
+ const auth_2 = require("./routes/auth");
23
26
  function createServer(options) {
24
27
  const app = (0, express_1.default)();
25
28
  const httpServer = http_1.default.createServer(app);
26
29
  const wss = new ws_1.WebSocketServer({ server: httpServer, path: '/ws' });
27
30
  // Middleware
28
31
  app.use(express_1.default.json());
32
+ app.use((0, express_session_1.default)({
33
+ secret: require('crypto').randomBytes(32).toString('hex'),
34
+ resave: false,
35
+ saveUninitialized: false,
36
+ cookie: { httpOnly: true, maxAge: 7 * 24 * 60 * 60 * 1000 },
37
+ }));
29
38
  // Services
30
39
  const gitService = (0, git_1.createGitService)(options.projectDir);
31
40
  const taskStore = (0, taskStore_1.createTaskStore)(options.projectDir);
32
41
  const rulesService = (0, rules_1.createRulesService)(options.projectDir);
33
42
  const settingsService = (0, settings_1.createSettingsService)(options.projectDir);
43
+ const authService = (0, auth_1.createAuthService)(options.projectDir);
34
44
  rulesService.ensureDefault();
35
45
  // Always re-enable direct IP on startup so users are never permanently locked out
36
46
  settingsService.updateSettings({ directIpAccess: true });
@@ -39,6 +49,7 @@ function createServer(options) {
39
49
  app.locals.taskStore = taskStore;
40
50
  app.locals.rulesService = rulesService;
41
51
  app.locals.settingsService = settingsService;
52
+ app.locals.authService = authService;
42
53
  app.locals.projectDir = options.projectDir;
43
54
  app.locals.port = options.port;
44
55
  // Block non-localhost requests when directIpAccess is disabled
@@ -50,6 +61,19 @@ function createServer(options) {
50
61
  }
51
62
  next();
52
63
  });
64
+ // Auth routes (public — no auth required)
65
+ app.use('/api/auth', auth_2.authRouter);
66
+ // Auth guard — protect all other API routes and the SPA
67
+ app.use((req, res, next) => {
68
+ const isAuthenticated = !!req.session.authenticated;
69
+ const isPublic = req.path === '/login' || req.path.startsWith('/css/') || req.path.startsWith('/js/') || req.path.startsWith('/fonts/');
70
+ if (!isAuthenticated && !isPublic) {
71
+ if (req.path.startsWith('/api/'))
72
+ return res.status(401).json({ error: 'Not authenticated' });
73
+ return res.redirect('/login');
74
+ }
75
+ next();
76
+ });
53
77
  // API routes
54
78
  app.get('/api/info', (_req, res) => {
55
79
  const os = require('os');
@@ -77,6 +101,10 @@ function createServer(options) {
77
101
  // Static files
78
102
  const publicDir = path_1.default.resolve(__dirname, '../public');
79
103
  app.use(express_1.default.static(publicDir));
104
+ // Login page (public)
105
+ app.get('/login', (_req, res) => {
106
+ res.sendFile(path_1.default.join(publicDir, 'login.html'));
107
+ });
80
108
  // SPA fallback
81
109
  app.get('*', (_req, res) => {
82
110
  res.sendFile(path_1.default.join(publicDir, 'index.html'));
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export interface AuthService {
2
+ validateCredentials(username: string, password: string): Promise<boolean>;
3
+ changePassword(newPassword: string): Promise<void>;
4
+ getUsername(): string;
5
+ }
6
+ export declare function createAuthService(projectDir: string): AuthService;
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.createAuthService = createAuthService;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const bcryptjs_1 = __importDefault(require("bcryptjs"));
10
+ const DEFAULT_PASSWORD = 'devlens';
11
+ const DEFAULT_USERNAME = 'admin';
12
+ function createAuthService(projectDir) {
13
+ const authFile = path_1.default.join(projectDir, '.devlens', 'auth.json');
14
+ function ensureAuthFile() {
15
+ const devlensDir = path_1.default.join(projectDir, '.devlens');
16
+ if (!fs_1.default.existsSync(devlensDir))
17
+ fs_1.default.mkdirSync(devlensDir, { recursive: true });
18
+ if (!fs_1.default.existsSync(authFile)) {
19
+ const hash = bcryptjs_1.default.hashSync(DEFAULT_PASSWORD, 10);
20
+ const data = { username: DEFAULT_USERNAME, passwordHash: hash };
21
+ fs_1.default.writeFileSync(authFile, JSON.stringify(data, null, 2), { mode: 0o600 });
22
+ }
23
+ }
24
+ function load() {
25
+ ensureAuthFile();
26
+ return JSON.parse(fs_1.default.readFileSync(authFile, 'utf-8'));
27
+ }
28
+ return {
29
+ async validateCredentials(username, password) {
30
+ const data = load();
31
+ if (username !== data.username)
32
+ return false;
33
+ return bcryptjs_1.default.compare(password, data.passwordHash);
34
+ },
35
+ async changePassword(newPassword) {
36
+ const data = load();
37
+ data.passwordHash = bcryptjs_1.default.hashSync(newPassword, 10);
38
+ fs_1.default.writeFileSync(authFile, JSON.stringify(data, null, 2), { mode: 0o600 });
39
+ },
40
+ getUsername() {
41
+ return load().username;
42
+ },
43
+ };
44
+ }
45
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/services/auth.ts"],"names":[],"mappings":";;;;;AAkBA,8CAmCC;AArDD,4CAAoB;AACpB,gDAAwB;AACxB,wDAA8B;AAE9B,MAAM,gBAAgB,GAAG,SAAS,CAAC;AACnC,MAAM,gBAAgB,GAAG,OAAO,CAAC;AAajC,SAAgB,iBAAiB,CAAC,UAAkB;IAClD,MAAM,QAAQ,GAAG,cAAI,CAAC,IAAI,CAAC,UAAU,EAAE,UAAU,EAAE,WAAW,CAAC,CAAC;IAEhE,SAAS,cAAc;QACrB,MAAM,UAAU,GAAG,cAAI,CAAC,IAAI,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;QACrD,IAAI,CAAC,YAAE,CAAC,UAAU,CAAC,UAAU,CAAC;YAAE,YAAE,CAAC,SAAS,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC9E,IAAI,CAAC,YAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC7B,MAAM,IAAI,GAAG,kBAAM,CAAC,QAAQ,CAAC,gBAAgB,EAAE,EAAE,CAAC,CAAC;YACnD,MAAM,IAAI,GAAa,EAAE,QAAQ,EAAE,gBAAgB,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC;YAC1E,YAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QAC7E,CAAC;IACH,CAAC;IAED,SAAS,IAAI;QACX,cAAc,EAAE,CAAC;QACjB,OAAO,IAAI,CAAC,KAAK,CAAC,YAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC;IACxD,CAAC;IAED,OAAO;QACL,KAAK,CAAC,mBAAmB,CAAC,QAAgB,EAAE,QAAgB;YAC1D,MAAM,IAAI,GAAG,IAAI,EAAE,CAAC;YACpB,IAAI,QAAQ,KAAK,IAAI,CAAC,QAAQ;gBAAE,OAAO,KAAK,CAAC;YAC7C,OAAO,kBAAM,CAAC,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;QACrD,CAAC;QAED,KAAK,CAAC,cAAc,CAAC,WAAmB;YACtC,MAAM,IAAI,GAAG,IAAI,EAAE,CAAC;YACpB,IAAI,CAAC,YAAY,GAAG,kBAAM,CAAC,QAAQ,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;YACrD,YAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QAC7E,CAAC;QAED,WAAW;YACT,OAAO,IAAI,EAAE,CAAC,QAAQ,CAAC;QACzB,CAAC;KACF,CAAC;AACJ,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ycniuqton/devlens",
3
- "version": "0.1.15",
3
+ "version": "0.1.16",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -30,20 +30,26 @@
30
30
  "homepage": "https://github.com/ycniuqton/Devlens#readme",
31
31
  "dependencies": {
32
32
  "@ycniuqton/devlens": "^0.1.13",
33
+ "bcryptjs": "^3.0.3",
33
34
  "better-sqlite3": "^12.8.0",
34
35
  "chokidar": "^3.6.0",
35
36
  "cloudflared": "^0.7.1",
36
37
  "commander": "^12.0.0",
38
+ "cookie-parser": "^1.4.7",
37
39
  "diff2html": "^3.4.47",
38
40
  "express": "^4.18.2",
41
+ "express-session": "^1.19.0",
39
42
  "ngrok": "^4.3.3",
40
43
  "open": "^10.1.0",
41
44
  "simple-git": "~3.33.0",
42
45
  "ws": "^8.16.0"
43
46
  },
44
47
  "devDependencies": {
48
+ "@types/bcryptjs": "^2.4.6",
45
49
  "@types/better-sqlite3": "^7.6.13",
50
+ "@types/cookie-parser": "^1.4.10",
46
51
  "@types/express": "^4.17.21",
52
+ "@types/express-session": "^1.19.0",
47
53
  "@types/node": "^20.11.0",
48
54
  "@types/ws": "^8.5.10",
49
55
  "typescript": "^5.3.3"
@@ -236,6 +236,52 @@ body {
236
236
  border-top: 1px solid var(--color-border);
237
237
  }
238
238
 
239
+ .auth-footer {
240
+ display: flex;
241
+ align-items: center;
242
+ gap: var(--sp-1);
243
+ margin-top: var(--sp-2);
244
+ }
245
+ .btn-auth {
246
+ display: flex; align-items: center; gap: 6px;
247
+ background: none; border: none; cursor: pointer;
248
+ color: var(--color-text-muted); font-size: var(--text-xs);
249
+ font-family: inherit; padding: var(--sp-1) var(--sp-2);
250
+ border-radius: var(--radius-sm);
251
+ transition: color 0.15s, background 0.15s;
252
+ flex: 1;
253
+ }
254
+ .btn-auth:hover { color: var(--color-text); background: var(--color-surface-2); }
255
+ .btn-logout { flex: 0; }
256
+
257
+ .modal-overlay {
258
+ position: fixed; inset: 0; z-index: 1000;
259
+ background: rgba(0,0,0,0.6);
260
+ display: flex; align-items: center; justify-content: center;
261
+ }
262
+ .modal-box {
263
+ background: var(--color-surface);
264
+ border: 1px solid var(--color-border);
265
+ border-radius: var(--radius-md);
266
+ width: 100%; max-width: 380px;
267
+ padding: 24px;
268
+ }
269
+ .modal-header {
270
+ display: flex; align-items: center; justify-content: space-between;
271
+ margin-bottom: 20px;
272
+ }
273
+ .modal-header h3 { font-size: var(--text-base); font-weight: 600; }
274
+ .modal-close {
275
+ background: none; border: none; cursor: pointer;
276
+ color: var(--color-text-muted); font-size: 16px; padding: 4px;
277
+ }
278
+ .modal-close:hover { color: var(--color-text); }
279
+ .modal-body label {
280
+ display: block; font-size: var(--text-xs);
281
+ color: var(--color-text-secondary); margin-bottom: 4px;
282
+ font-weight: 500;
283
+ }
284
+
239
285
  .connection-status {
240
286
  display: flex;
241
287
  align-items: center;
package/public/index.html CHANGED
@@ -76,9 +76,40 @@
76
76
  <span class="status-indicator"></span>
77
77
  <span class="status-label">Disconnected</span>
78
78
  </div>
79
+ <div class="auth-footer">
80
+ <button class="btn-auth" onclick="showChangePassword()" title="Change password">
81
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="8" r="4"/><path d="M6 20v-2a6 6 0 0 1 12 0v2"/></svg>
82
+ <span id="auth-username">admin</span>
83
+ </button>
84
+ <button class="btn-auth btn-logout" onclick="logoutAction()" title="Sign out">
85
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/></svg>
86
+ </button>
87
+ </div>
79
88
  </div>
80
89
  </aside>
81
90
 
91
+ <!-- Change Password Modal -->
92
+ <div id="change-password-modal" class="modal-overlay" style="display:none">
93
+ <div class="modal-box">
94
+ <div class="modal-header">
95
+ <h3>Change Password</h3>
96
+ <button class="modal-close" onclick="hideChangePassword()">✕</button>
97
+ </div>
98
+ <div class="modal-body">
99
+ <label>Current Password</label>
100
+ <input type="password" id="cp-current" placeholder="Current password" style="width:100%;margin-bottom:12px;padding:8px 12px;background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-sm);color:var(--color-text);font-size:var(--text-sm)">
101
+ <label>New Password</label>
102
+ <input type="password" id="cp-new" placeholder="New password (min 4 chars)" style="width:100%;margin-bottom:12px;padding:8px 12px;background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-sm);color:var(--color-text);font-size:var(--text-sm)">
103
+ <label>Confirm New Password</label>
104
+ <input type="password" id="cp-confirm" placeholder="Confirm new password" style="width:100%;margin-bottom:16px;padding:8px 12px;background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-sm);color:var(--color-text);font-size:var(--text-sm)">
105
+ <div style="display:flex;gap:8px;justify-content:flex-end">
106
+ <button class="btn btn-ghost btn-sm" onclick="hideChangePassword()">Cancel</button>
107
+ <button class="btn btn-primary btn-sm" onclick="submitChangePassword()">Update Password</button>
108
+ </div>
109
+ </div>
110
+ </div>
111
+ </div>
112
+
82
113
  <!-- Main Content -->
83
114
  <main id="main-content">
84
115
 
package/public/js/app.js CHANGED
@@ -1,3 +1,55 @@
1
+ // Auth
2
+ fetch('/api/auth/status').then(r => r.json()).then(data => {
3
+ if (!data.authenticated) location.href = '/login';
4
+ }).catch(() => {});
5
+
6
+ async function logoutAction() {
7
+ await fetch('/api/auth/logout', { method: 'POST' });
8
+ location.href = '/login';
9
+ }
10
+
11
+ function showChangePassword() {
12
+ document.getElementById('cp-current').value = '';
13
+ document.getElementById('cp-new').value = '';
14
+ document.getElementById('cp-confirm').value = '';
15
+ document.getElementById('change-password-modal').style.display = 'flex';
16
+ document.getElementById('cp-current').focus();
17
+ }
18
+
19
+ function hideChangePassword() {
20
+ document.getElementById('change-password-modal').style.display = 'none';
21
+ }
22
+
23
+ async function submitChangePassword() {
24
+ const current = document.getElementById('cp-current').value;
25
+ const newPw = document.getElementById('cp-new').value;
26
+ const confirm = document.getElementById('cp-confirm').value;
27
+ if (!current || !newPw) { showToast('Please fill in all fields', 'error'); return; }
28
+ if (newPw !== confirm) { showToast('Passwords do not match', 'error'); return; }
29
+ if (newPw.length < 4) { showToast('Password must be at least 4 characters', 'error'); return; }
30
+ try {
31
+ const res = await fetch('/api/auth/change-password', {
32
+ method: 'POST',
33
+ headers: { 'Content-Type': 'application/json' },
34
+ body: JSON.stringify({ currentPassword: current, newPassword: newPw }),
35
+ });
36
+ const data = await res.json();
37
+ if (data.ok) {
38
+ hideChangePassword();
39
+ showToast('Password updated', 'success');
40
+ } else {
41
+ showToast(data.error || 'Failed to update password', 'error');
42
+ }
43
+ } catch {
44
+ showToast('Connection error', 'error');
45
+ }
46
+ }
47
+
48
+ // Close modal on backdrop click
49
+ document.getElementById('change-password-modal')?.addEventListener('click', (e) => {
50
+ if (e.target === e.currentTarget) hideChangePassword();
51
+ });
52
+
1
53
  // Load project info into sidebar + page title
2
54
  fetch('/api/info').then(r => r.json()).then(info => {
3
55
  if (info.projectName) {
@@ -0,0 +1,181 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Devlens — Login</title>
7
+ <link rel="preconnect" href="https://fonts.googleapis.com">
8
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
9
+ <style>
10
+ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
11
+ :root {
12
+ --color-bg: #0d0d0f;
13
+ --color-surface: #16181c;
14
+ --color-surface-2: #1e2026;
15
+ --color-border: #2a2d35;
16
+ --color-text: #e8eaed;
17
+ --color-text-secondary: #9aa0ad;
18
+ --color-primary: #6366f1;
19
+ --color-primary-hover: #818cf8;
20
+ --color-danger: #f87171;
21
+ --radius-md: 8px;
22
+ }
23
+ body {
24
+ font-family: 'Inter', sans-serif;
25
+ background: var(--color-bg);
26
+ color: var(--color-text);
27
+ min-height: 100vh;
28
+ display: flex;
29
+ align-items: center;
30
+ justify-content: center;
31
+ }
32
+ .login-card {
33
+ background: var(--color-surface);
34
+ border: 1px solid var(--color-border);
35
+ border-radius: 12px;
36
+ padding: 40px;
37
+ width: 100%;
38
+ max-width: 380px;
39
+ }
40
+ .brand {
41
+ display: flex;
42
+ align-items: center;
43
+ gap: 12px;
44
+ margin-bottom: 32px;
45
+ }
46
+ .brand-icon {
47
+ width: 40px; height: 40px;
48
+ background: linear-gradient(135deg, #6366f1, #818cf8);
49
+ border-radius: 10px;
50
+ display: flex; align-items: center; justify-content: center;
51
+ }
52
+ .brand-name { font-size: 20px; font-weight: 700; letter-spacing: -0.3px; }
53
+ .brand-sub { font-size: 12px; color: var(--color-text-secondary); }
54
+ label {
55
+ display: block;
56
+ font-size: 13px;
57
+ font-weight: 500;
58
+ color: var(--color-text-secondary);
59
+ margin-bottom: 6px;
60
+ }
61
+ input {
62
+ width: 100%;
63
+ padding: 10px 14px;
64
+ background: var(--color-surface-2);
65
+ border: 1px solid var(--color-border);
66
+ border-radius: var(--radius-md);
67
+ color: var(--color-text);
68
+ font-size: 14px;
69
+ font-family: inherit;
70
+ outline: none;
71
+ transition: border-color 0.15s;
72
+ margin-bottom: 16px;
73
+ }
74
+ input:focus { border-color: var(--color-primary); }
75
+ button {
76
+ width: 100%;
77
+ padding: 11px;
78
+ background: var(--color-primary);
79
+ color: white;
80
+ border: none;
81
+ border-radius: var(--radius-md);
82
+ font-size: 14px;
83
+ font-weight: 600;
84
+ font-family: inherit;
85
+ cursor: pointer;
86
+ transition: background 0.15s;
87
+ margin-top: 4px;
88
+ }
89
+ button:hover { background: var(--color-primary-hover); }
90
+ button:disabled { opacity: 0.6; cursor: not-allowed; }
91
+ .error {
92
+ color: var(--color-danger);
93
+ font-size: 13px;
94
+ margin-top: 12px;
95
+ text-align: center;
96
+ min-height: 20px;
97
+ }
98
+ .hint {
99
+ font-size: 12px;
100
+ color: var(--color-text-secondary);
101
+ text-align: center;
102
+ margin-top: 20px;
103
+ }
104
+ code {
105
+ font-family: 'JetBrains Mono', monospace;
106
+ background: var(--color-surface-2);
107
+ padding: 2px 6px;
108
+ border-radius: 4px;
109
+ font-size: 11px;
110
+ }
111
+ </style>
112
+ </head>
113
+ <body>
114
+ <div class="login-card">
115
+ <div class="brand">
116
+ <div class="brand-icon">
117
+ <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
118
+ <circle cx="12" cy="12" r="3"/><path d="M12 1v4M12 19v4M4.22 4.22l2.83 2.83M16.95 16.95l2.83 2.83M1 12h4M19 12h4M4.22 19.78l2.83-2.83M16.95 7.05l2.83-2.83"/>
119
+ </svg>
120
+ </div>
121
+ <div>
122
+ <div class="brand-name">Devlens</div>
123
+ <div class="brand-sub">Claude Code Dashboard</div>
124
+ </div>
125
+ </div>
126
+
127
+ <form id="login-form">
128
+ <label for="username">Username</label>
129
+ <input type="text" id="username" name="username" value="admin" autocomplete="username" required>
130
+
131
+ <label for="password">Password</label>
132
+ <input type="password" id="password" name="password" placeholder="Enter password" autocomplete="current-password" required>
133
+
134
+ <button type="submit" id="login-btn">Sign in</button>
135
+ <div class="error" id="error-msg"></div>
136
+ </form>
137
+
138
+ <p class="hint">Default: <code>admin</code> / <code>devlens</code></p>
139
+ </div>
140
+
141
+ <script>
142
+ document.getElementById('login-form').addEventListener('submit', async (e) => {
143
+ e.preventDefault();
144
+ const btn = document.getElementById('login-btn');
145
+ const errorEl = document.getElementById('error-msg');
146
+ btn.disabled = true;
147
+ btn.textContent = 'Signing in...';
148
+ errorEl.textContent = '';
149
+
150
+ try {
151
+ const res = await fetch('/api/auth/login', {
152
+ method: 'POST',
153
+ headers: { 'Content-Type': 'application/json' },
154
+ body: JSON.stringify({
155
+ username: document.getElementById('username').value,
156
+ password: document.getElementById('password').value,
157
+ }),
158
+ });
159
+ const data = await res.json();
160
+ if (data.ok) {
161
+ const redirect = new URLSearchParams(location.search).get('redirect') || '/';
162
+ location.href = redirect;
163
+ } else {
164
+ errorEl.textContent = data.error || 'Login failed';
165
+ btn.disabled = false;
166
+ btn.textContent = 'Sign in';
167
+ }
168
+ } catch {
169
+ errorEl.textContent = 'Connection error';
170
+ btn.disabled = false;
171
+ btn.textContent = 'Sign in';
172
+ }
173
+ });
174
+
175
+ // Focus password if username is already filled
176
+ if (document.getElementById('username').value) {
177
+ document.getElementById('password').focus();
178
+ }
179
+ </script>
180
+ </body>
181
+ </html>