@inetafrica/open-claudia 1.8.0 → 1.9.0

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/web.js +53 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inetafrica/open-claudia",
3
- "version": "1.8.0",
3
+ "version": "1.9.0",
4
4
  "description": "Your always-on AI coding assistant — Claude Code via Telegram",
5
5
  "main": "bot.js",
6
6
  "bin": {
package/web.js CHANGED
@@ -30,8 +30,24 @@ function getPassword() {
30
30
  return initial;
31
31
  }
32
32
 
33
+ const PASSWORD_CHANGED_FILE = path.join(CONFIG_DIR, ".password-changed");
34
+
35
+ function isPasswordChanged() {
36
+ return fs.existsSync(PASSWORD_CHANGED_FILE);
37
+ }
38
+
39
+ function validatePasswordComplexity(pw) {
40
+ if (pw.length < 12) return "Password must be at least 12 characters";
41
+ if (!/[A-Z]/.test(pw)) return "Must contain at least one uppercase letter";
42
+ if (!/[a-z]/.test(pw)) return "Must contain at least one lowercase letter";
43
+ if (!/[0-9]/.test(pw)) return "Must contain at least one number";
44
+ if (!/[^A-Za-z0-9]/.test(pw)) return "Must contain at least one symbol (!@#$%^&*)";
45
+ return null;
46
+ }
47
+
33
48
  function setPassword(newPassword) {
34
49
  fs.writeFileSync(WEB_PASSWORD_FILE, newPassword);
50
+ fs.writeFileSync(PASSWORD_CHANGED_FILE, new Date().toISOString());
35
51
  }
36
52
 
37
53
  function checkAuth(req) {
@@ -122,7 +138,7 @@ async function handleAPI(req, res, body) {
122
138
  "Content-Type": "application/json",
123
139
  "Set-Cookie": `oc_session=${authToken()}; Path=/; HttpOnly; SameSite=Strict; Max-Age=86400`,
124
140
  });
125
- return res.end(JSON.stringify({ ok: true }));
141
+ return res.end(JSON.stringify({ ok: true, mustChangePassword: !isPasswordChanged() }));
126
142
  }
127
143
  res.writeHead(401, { "Content-Type": "application/json" });
128
144
  return res.end(JSON.stringify({ ok: false, error: "Wrong password" }));
@@ -309,6 +325,11 @@ async function handleAPI(req, res, body) {
309
325
  res.writeHead(400, { "Content-Type": "application/json" });
310
326
  return res.end(JSON.stringify({ error: "Wrong current password" }));
311
327
  }
328
+ const complexityError = validatePasswordComplexity(newPassword);
329
+ if (complexityError) {
330
+ res.writeHead(400, { "Content-Type": "application/json" });
331
+ return res.end(JSON.stringify({ error: complexityError }));
332
+ }
312
333
  setPassword(newPassword);
313
334
  res.writeHead(200, {
314
335
  "Content-Type": "application/json",
@@ -409,10 +430,40 @@ function showLogin() {
409
430
  async function doLogin() {
410
431
  const pw = $("#pw").value;
411
432
  const r = await api("/api/login", { method: "POST", body: { password: pw } });
412
- if (r?.ok) init();
433
+ if (r?.ok && r.mustChangePassword) showForceChangePassword(pw);
434
+ else if (r?.ok) init();
413
435
  else $("#login-msg").innerHTML = '<div class="msg err">Wrong password</div>';
414
436
  }
415
437
 
438
+ function showForceChangePassword(currentPw) {
439
+ $("#app").innerHTML = \`
440
+ <div class="login-page"><div class="login-box">
441
+ <h1>Change Password</h1>
442
+ <p class="subtitle">You must set a secure password before continuing</p>
443
+ <div id="change-msg"></div>
444
+ <div class="card">
445
+ <div class="form-group">
446
+ <input type="password" id="new-pw-1" placeholder="New password">
447
+ </div>
448
+ <div class="form-group">
449
+ <input type="password" id="new-pw-2" placeholder="Confirm password" onkeydown="if(event.key==='Enter')doForceChange()">
450
+ </div>
451
+ <p style="font-size:11px;color:#888;margin:8px 0">Min 12 chars, uppercase, lowercase, number, and symbol</p>
452
+ <button onclick="doForceChange()" style="width:100%">Set Password</button>
453
+ </div>
454
+ </div></div>\`;
455
+ window._forceChangePw = currentPw;
456
+ setTimeout(() => $("#new-pw-1")?.focus(), 100);
457
+ }
458
+
459
+ async function doForceChange() {
460
+ const p1 = $("#new-pw-1").value, p2 = $("#new-pw-2").value;
461
+ if (p1 !== p2) { $("#change-msg").innerHTML = '<div class="msg err">Passwords do not match</div>'; return; }
462
+ const r = await api("/api/password", { method: "POST", body: { current: window._forceChangePw, newPassword: p1 } });
463
+ if (r?.ok) { delete window._forceChangePw; init(); }
464
+ else $("#change-msg").innerHTML = '<div class="msg err">' + (r?.error || "Failed") + '</div>';
465
+ }
466
+
416
467
  function render() {
417
468
  const tabs = [
418
469
  { id: "dashboard", label: "Dashboard" },