@pixelbyte-software/pixcode 1.54.10 → 1.54.11
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.
- package/CODE_OF_CONDUCT.md +41 -41
- package/CONTRIBUTING.md +156 -156
- package/LICENSE +21 -718
- package/README.de.md +169 -169
- package/README.ja.md +167 -167
- package/README.ko.md +167 -167
- package/README.md +435 -418
- package/README.ru.md +169 -169
- package/README.tr.md +298 -298
- package/README.zh-CN.md +167 -167
- package/SECURITY.md +46 -46
- package/dist/api-automation.html +110 -110
- package/dist/api-docs.html +548 -548
- package/dist/assets/{index-DWOW_Jn0.js → index-Bcqs_1vc.js} +114 -114
- package/dist/clear-cache.html +85 -85
- package/dist/convert-icons.md +52 -52
- package/dist/docs.html +308 -308
- package/dist/features.html +133 -133
- package/dist/generate-icons.js +48 -48
- package/dist/humans.txt +15 -15
- package/dist/icons/codex-white.svg +3 -3
- package/dist/icons/codex.svg +3 -3
- package/dist/icons/cursor-white.svg +11 -11
- package/dist/icons/qwen-logo.svg +14 -14
- package/dist/index.html +58 -58
- package/dist/landing.html +268 -268
- package/dist/llms-full.txt +119 -119
- package/dist/llms.txt +53 -53
- package/dist/manifest.json +60 -60
- package/dist/openapi.yaml +1696 -1696
- package/dist/orchestration.html +125 -125
- package/dist/robots.txt +4 -4
- package/dist/site.css +692 -692
- package/dist/sitemap.xml +51 -51
- package/dist/sw.js +132 -132
- package/dist-server/server/cli.js +100 -100
- package/dist-server/server/daemon/manager.js +33 -33
- package/dist-server/server/daemon-manager.js +64 -64
- package/dist-server/server/routes/commands.js +25 -25
- package/dist-server/server/routes/git.js +17 -17
- package/dist-server/server/routes/live-view.js +46 -46
- package/dist-server/server/services/public-api-manifest.js +51 -51
- package/package.json +224 -224
- package/scripts/fix-node-pty.js +67 -67
- package/scripts/github/create-v1.38-issues.mjs +351 -351
- package/scripts/github/create-vscode-workbench-issues.mjs +121 -121
- package/scripts/smoke/backend-resource-bounds.mjs +152 -152
- package/scripts/smoke/changes-panel-layout.mjs +48 -48
- package/scripts/smoke/chat-composer-fixed-layout.mjs +55 -55
- package/scripts/smoke/chat-message-timeline-order.mjs +41 -41
- package/scripts/smoke/chat-realtime-hydration.mjs +44 -44
- package/scripts/smoke/chat-session-provider-pools.mjs +35 -35
- package/scripts/smoke/chat-session-state.mjs +19 -19
- package/scripts/smoke/code-editor-theme.mjs +55 -55
- package/scripts/smoke/code-editor-vscode-engine.mjs +91 -91
- package/scripts/smoke/command-center-agent-writes.mjs +79 -79
- package/scripts/smoke/command-center-non-git.mjs +46 -46
- package/scripts/smoke/context-packet.mjs +43 -43
- package/scripts/smoke/control-room-ux-redesign.mjs +91 -91
- package/scripts/smoke/daemon-entrypoint.mjs +20 -20
- package/scripts/smoke/default-landing-routing.mjs +33 -33
- package/scripts/smoke/desktop-native-notifications.mjs +30 -30
- package/scripts/smoke/desktop-tray-icon.mjs +33 -33
- package/scripts/smoke/discord-release-workflow.mjs +24 -24
- package/scripts/smoke/git-install-update.mjs +255 -255
- package/scripts/smoke/handoff-artifact-protocol.mjs +50 -50
- package/scripts/smoke/live-view-diagnostics.mjs +53 -53
- package/scripts/smoke/live-view-environment.mjs +92 -92
- package/scripts/smoke/live-view-integration.mjs +450 -450
- package/scripts/smoke/mac-desktop-runtime.mjs +37 -37
- package/scripts/smoke/mobile-tunnel-guidance.mjs +29 -29
- package/scripts/smoke/mobile-ux.mjs +76 -76
- package/scripts/smoke/model-registry.mjs +36 -36
- package/scripts/smoke/multi-project-ui.mjs +45 -45
- package/scripts/smoke/multi-worker-slots.mjs +42 -42
- package/scripts/smoke/notification-center.mjs +87 -87
- package/scripts/smoke/notification-inapp-preference.mjs +23 -23
- package/scripts/smoke/notification-taxonomy.mjs +58 -58
- package/scripts/smoke/orchestration-api.mjs +172 -172
- package/scripts/smoke/orchestration-execution-dashboard.mjs +33 -33
- package/scripts/smoke/orchestration-live-run.mjs +176 -176
- package/scripts/smoke/orchestration-mobile-scroll.mjs +29 -29
- package/scripts/smoke/orchestration-model-sync.mjs +30 -30
- package/scripts/smoke/orchestration-permission-fallback.mjs +34 -34
- package/scripts/smoke/orchestration-runtime-guards.mjs +48 -48
- package/scripts/smoke/orchestration-user-facing-output.mjs +25 -25
- package/scripts/smoke/permission-policy.mjs +50 -50
- package/scripts/smoke/pixcode-workbench-1-48.mjs +167 -167
- package/scripts/smoke/provider-models-opencode-live.mjs +66 -66
- package/scripts/smoke/provider-rest-api.mjs +124 -124
- package/scripts/smoke/provider-selection-status.mjs +52 -52
- package/scripts/smoke/run-state-refresh.mjs +52 -52
- package/scripts/smoke/runtime-manager.mjs +99 -99
- package/scripts/smoke/shell-manual-disconnect.mjs +30 -30
- package/scripts/smoke/side-panel-editor-layout.mjs +34 -34
- package/scripts/smoke/static-root-routing.mjs +21 -21
- package/scripts/smoke/strict-handoff-compact.mjs +60 -60
- package/scripts/smoke/taskmaster-config.mjs +24 -24
- package/scripts/smoke/taskmaster-execution-telegram.mjs +3 -3
- package/scripts/smoke/taskmaster-onboarding.mjs +3 -3
- package/scripts/smoke/taskmaster-run-graph.mjs +3 -3
- package/scripts/smoke/telegram-control.mjs +331 -331
- package/scripts/smoke/tunnel-persistence.mjs +56 -56
- package/scripts/smoke/update-issue-progress.mjs +69 -69
- package/scripts/smoke/update-ux.mjs +55 -55
- package/scripts/smoke/v138-completion.mjs +132 -132
- package/scripts/smoke/v138-desktop-release-hardening.mjs +69 -69
- package/scripts/smoke/v138-diagnostics.mjs +63 -63
- package/scripts/smoke/v138-issue-planner.mjs +33 -33
- package/scripts/smoke/v143-remote-control.mjs +76 -76
- package/scripts/smoke/v144-production-loop.mjs +47 -47
- package/scripts/smoke/v145-platformization.mjs +46 -46
- package/scripts/smoke/v146-control-room-ui.mjs +150 -150
- package/scripts/smoke/version-modal-autoshow.mjs +29 -29
- package/scripts/smoke/vscode-workbench-layout.mjs +63 -63
- package/scripts/smoke/vscode-workbench-polish.mjs +461 -461
- package/scripts/smoke/workflow-fallback-replay.mjs +56 -56
- package/scripts/smoke/workflow-templates.mjs +43 -43
- package/scripts/smoke/workflow-trace-timeline.mjs +46 -46
- package/scripts/update-git-install.mjs +298 -298
- package/server/claude-sdk.js +927 -927
- package/server/cli.js +1106 -1106
- package/server/constants/config.js +4 -4
- package/server/cursor-cli.js +344 -344
- package/server/daemon/manager.js +563 -563
- package/server/daemon-manager.js +964 -964
- package/server/database/db.js +988 -988
- package/server/database/json-store.js +197 -197
- package/server/gemini-cli.js +550 -550
- package/server/gemini-response-handler.js +79 -79
- package/server/index.js +5671 -5671
- package/server/load-env.js +35 -35
- package/server/middleware/account-lockout.js +112 -112
- package/server/middleware/auth.js +277 -277
- package/server/middleware/rate-limiter.js +87 -87
- package/server/modules/orchestration/a2a/adapter-registry.ts +108 -108
- package/server/modules/orchestration/a2a/adapters/abstract-a2a.adapter.ts +63 -63
- package/server/modules/orchestration/a2a/adapters/claude-code.adapter.ts +286 -286
- package/server/modules/orchestration/a2a/adapters/codex.adapter.ts +244 -244
- package/server/modules/orchestration/a2a/adapters/cursor.adapter.ts +249 -249
- package/server/modules/orchestration/a2a/adapters/gemini.adapter.ts +248 -248
- package/server/modules/orchestration/a2a/adapters/json-event.adapter.test.ts +60 -60
- package/server/modules/orchestration/a2a/adapters/json-event.adapter.ts +101 -101
- package/server/modules/orchestration/a2a/adapters/opencode.adapter.ts +248 -248
- package/server/modules/orchestration/a2a/adapters/qwen.adapter.ts +248 -248
- package/server/modules/orchestration/a2a/agent-card.ts +55 -55
- package/server/modules/orchestration/a2a/auth.middleware.ts +29 -29
- package/server/modules/orchestration/a2a/bus.ts +46 -46
- package/server/modules/orchestration/a2a/routes.ts +586 -586
- package/server/modules/orchestration/a2a/task-dispatcher.ts +345 -345
- package/server/modules/orchestration/a2a/task-store.ts +178 -178
- package/server/modules/orchestration/a2a/types.ts +126 -126
- package/server/modules/orchestration/a2a/validator.ts +113 -113
- package/server/modules/orchestration/index.ts +99 -99
- package/server/modules/orchestration/preview/port-watcher.ts +112 -112
- package/server/modules/orchestration/preview/preview-proxy.ts +60 -60
- package/server/modules/orchestration/preview/types.ts +19 -19
- package/server/modules/orchestration/security/permission-policy.ts +401 -401
- package/server/modules/orchestration/tasks/orchestration-task-store.ts +41 -41
- package/server/modules/orchestration/tasks/orchestration-task.routes.ts +81 -81
- package/server/modules/orchestration/tasks/orchestration-task.service.ts +201 -201
- package/server/modules/orchestration/tasks/orchestration-task.types.ts +40 -40
- package/server/modules/orchestration/tasks/task-run-graph.ts +155 -155
- package/server/modules/orchestration/workflows/approval-queue.ts +106 -106
- package/server/modules/orchestration/workflows/built-in-workflows.ts +127 -127
- package/server/modules/orchestration/workflows/context-packet.ts +186 -186
- package/server/modules/orchestration/workflows/handoff-artifact.ts +175 -175
- package/server/modules/orchestration/workflows/workflow-fallback-policy.ts +161 -161
- package/server/modules/orchestration/workflows/workflow-replay.ts +254 -254
- package/server/modules/orchestration/workflows/workflow-runner.ts +2062 -2062
- package/server/modules/orchestration/workflows/workflow-store.ts +97 -97
- package/server/modules/orchestration/workflows/workflow-templates.ts +272 -272
- package/server/modules/orchestration/workflows/workflow-trace.ts +424 -424
- package/server/modules/orchestration/workflows/workflow.routes.ts +586 -586
- package/server/modules/orchestration/workflows/workflow.types.ts +111 -111
- package/server/modules/orchestration/workflows/workspace-target.ts +122 -122
- package/server/modules/orchestration/workspace/docker-workspace.ts +136 -136
- package/server/modules/orchestration/workspace/path-safety.ts +55 -55
- package/server/modules/orchestration/workspace/types.ts +52 -52
- package/server/modules/orchestration/workspace/workspace-manager.ts +102 -102
- package/server/modules/orchestration/workspace/worktree-workspace.ts +126 -126
- package/server/modules/providers/index.ts +2 -2
- package/server/modules/providers/list/claude/claude-auth.provider.ts +146 -146
- package/server/modules/providers/list/claude/claude-mcp.provider.ts +135 -135
- package/server/modules/providers/list/claude/claude-sessions.provider.ts +306 -306
- package/server/modules/providers/list/claude/claude.provider.ts +15 -15
- package/server/modules/providers/list/codex/codex-auth.provider.ts +117 -117
- package/server/modules/providers/list/codex/codex-mcp.provider.ts +135 -135
- package/server/modules/providers/list/codex/codex-sessions.provider.ts +319 -319
- package/server/modules/providers/list/codex/codex.provider.ts +15 -15
- package/server/modules/providers/list/cursor/cursor-auth.provider.ts +147 -147
- package/server/modules/providers/list/cursor/cursor-mcp.provider.ts +108 -108
- package/server/modules/providers/list/cursor/cursor-sessions.provider.ts +421 -421
- package/server/modules/providers/list/cursor/cursor.provider.ts +15 -15
- package/server/modules/providers/list/gemini/gemini-auth.provider.ts +173 -173
- package/server/modules/providers/list/gemini/gemini-mcp.provider.ts +110 -110
- package/server/modules/providers/list/gemini/gemini-sessions.provider.ts +227 -227
- package/server/modules/providers/list/gemini/gemini.provider.ts +15 -15
- package/server/modules/providers/list/opencode/opencode-auth.provider.ts +131 -131
- package/server/modules/providers/list/opencode/opencode-sessions.provider.ts +286 -286
- package/server/modules/providers/list/qwen/qwen-auth.provider.ts +146 -146
- package/server/modules/providers/list/qwen/qwen-sessions.provider.ts +265 -265
- package/server/modules/providers/provider.registry.ts +40 -40
- package/server/modules/providers/provider.routes.ts +982 -982
- package/server/modules/providers/services/mcp.service.ts +86 -86
- package/server/modules/providers/services/provider-auth.service.ts +26 -26
- package/server/modules/providers/services/sessions.service.ts +45 -45
- package/server/modules/providers/shared/base/abstract.provider.ts +20 -20
- package/server/modules/providers/shared/mcp/mcp.provider.ts +151 -151
- package/server/modules/providers/tests/mcp.test.ts +293 -293
- package/server/openai-codex.js +468 -468
- package/server/opencode-cli.js +491 -491
- package/server/opencode-response-handler.js +111 -111
- package/server/projects.js +3135 -3135
- package/server/qwen-code-cli.js +410 -410
- package/server/routes/agent.js +1462 -1462
- package/server/routes/auth.js +298 -298
- package/server/routes/codex.js +20 -20
- package/server/routes/commands.js +581 -581
- package/server/routes/cursor.js +61 -61
- package/server/routes/diagnostics.js +44 -44
- package/server/routes/gemini.js +25 -25
- package/server/routes/git.js +1822 -1822
- package/server/routes/live-view.js +434 -434
- package/server/routes/mcp-utils.js +13 -13
- package/server/routes/messages.js +97 -97
- package/server/routes/network.js +143 -143
- package/server/routes/platformization.js +231 -231
- package/server/routes/plugins.js +690 -690
- package/server/routes/production-agent-loop.js +90 -90
- package/server/routes/projects.js +918 -918
- package/server/routes/public-api.js +34 -34
- package/server/routes/qwen.js +27 -27
- package/server/routes/remote.js +56 -56
- package/server/routes/settings.js +321 -321
- package/server/routes/telegram.js +163 -163
- package/server/routes/user.js +125 -125
- package/server/routes/webhooks.js +63 -63
- package/server/services/control-room.js +102 -102
- package/server/services/diagnostics.js +165 -165
- package/server/services/external-access.js +403 -403
- package/server/services/install-jobs.js +715 -715
- package/server/services/live-view.js +956 -956
- package/server/services/managed-runtimes.js +493 -493
- package/server/services/model-registry.js +144 -144
- package/server/services/notification-orchestrator.js +365 -365
- package/server/services/notification-taxonomy.js +204 -204
- package/server/services/platformization.js +1052 -1052
- package/server/services/production-agent-loop.js +248 -248
- package/server/services/provider-cli-versions.js +149 -149
- package/server/services/provider-credentials.js +189 -189
- package/server/services/provider-models.js +396 -396
- package/server/services/public-api-manifest.js +181 -181
- package/server/services/qr-login.js +126 -126
- package/server/services/remote-connection.js +127 -127
- package/server/services/runtime-manager.js +323 -323
- package/server/services/startup-update.js +259 -259
- package/server/services/telegram/bot.js +393 -393
- package/server/services/telegram/control-center.js +2453 -2453
- package/server/services/telegram/telegram-gateway.js +314 -314
- package/server/services/telegram/telegram-http-client.js +201 -201
- package/server/services/telegram/translations.js +470 -470
- package/server/services/webhooks.js +216 -216
- package/server/sessionManager.js +225 -225
- package/server/setup-wizard.js +283 -283
- package/server/shared/interfaces.ts +54 -54
- package/server/shared/types.ts +172 -172
- package/server/shared/utils.ts +193 -193
- package/server/tsconfig.json +36 -36
- package/server/utils/colors.js +21 -21
- package/server/utils/commandParser.js +305 -305
- package/server/utils/frontmatter.js +18 -18
- package/server/utils/gitConfig.js +34 -34
- package/server/utils/plugin-loader.js +461 -461
- package/server/utils/plugin-process-manager.js +185 -185
- package/server/utils/port-access.js +227 -227
- package/server/utils/runtime-paths.js +37 -37
- package/server/utils/security-log.js +84 -84
- package/server/utils/url-detection.js +71 -71
- package/server/vite-daemon.js +79 -79
- package/shared/modelConstants.js +161 -161
- package/shared/networkHosts.js +22 -22
package/server/load-env.js
CHANGED
|
@@ -1,35 +1,35 @@
|
|
|
1
|
-
// Load environment variables from .env before other imports execute.
|
|
2
|
-
import fs from 'fs';
|
|
3
|
-
import os from 'os';
|
|
4
|
-
import path from 'path';
|
|
5
|
-
|
|
6
|
-
import { findAppRoot, getModuleDir } from './utils/runtime-paths.js';
|
|
7
|
-
|
|
8
|
-
const __dirname = getModuleDir(import.meta.url);
|
|
9
|
-
// Resolve the repo/app root via the nearest /server folder so this file keeps finding the
|
|
10
|
-
// same top-level .env file from both /server/load-env.js and /dist-server/server/load-env.js.
|
|
11
|
-
const APP_ROOT = findAppRoot(__dirname);
|
|
12
|
-
|
|
13
|
-
try {
|
|
14
|
-
const envPath = path.join(APP_ROOT, '.env');
|
|
15
|
-
const envFile = fs.readFileSync(envPath, 'utf8');
|
|
16
|
-
envFile.split('\n').forEach(line => {
|
|
17
|
-
const trimmedLine = line.trim();
|
|
18
|
-
if (trimmedLine && !trimmedLine.startsWith('#')) {
|
|
19
|
-
const [key, ...valueParts] = trimmedLine.split('=');
|
|
20
|
-
if (key && valueParts.length > 0 && !process.env[key]) {
|
|
21
|
-
process.env[key] = valueParts.join('=').trim();
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
});
|
|
25
|
-
} catch (e) {
|
|
26
|
-
console.log('No .env file found or error reading it:', e.message);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// Keep the default database in a stable user-level location so rebuilding dist-server
|
|
30
|
-
// never changes where the backend stores auth.db when DATABASE_PATH is not set explicitly.
|
|
31
|
-
const DEFAULT_DATABASE_PATH = path.join(os.homedir(), '.pixcode', 'auth.db');
|
|
32
|
-
|
|
33
|
-
if (!process.env.DATABASE_PATH) {
|
|
34
|
-
process.env.DATABASE_PATH = DEFAULT_DATABASE_PATH;
|
|
35
|
-
}
|
|
1
|
+
// Load environment variables from .env before other imports execute.
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import os from 'os';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
|
|
6
|
+
import { findAppRoot, getModuleDir } from './utils/runtime-paths.js';
|
|
7
|
+
|
|
8
|
+
const __dirname = getModuleDir(import.meta.url);
|
|
9
|
+
// Resolve the repo/app root via the nearest /server folder so this file keeps finding the
|
|
10
|
+
// same top-level .env file from both /server/load-env.js and /dist-server/server/load-env.js.
|
|
11
|
+
const APP_ROOT = findAppRoot(__dirname);
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
const envPath = path.join(APP_ROOT, '.env');
|
|
15
|
+
const envFile = fs.readFileSync(envPath, 'utf8');
|
|
16
|
+
envFile.split('\n').forEach(line => {
|
|
17
|
+
const trimmedLine = line.trim();
|
|
18
|
+
if (trimmedLine && !trimmedLine.startsWith('#')) {
|
|
19
|
+
const [key, ...valueParts] = trimmedLine.split('=');
|
|
20
|
+
if (key && valueParts.length > 0 && !process.env[key]) {
|
|
21
|
+
process.env[key] = valueParts.join('=').trim();
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
} catch (e) {
|
|
26
|
+
console.log('No .env file found or error reading it:', e.message);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Keep the default database in a stable user-level location so rebuilding dist-server
|
|
30
|
+
// never changes where the backend stores auth.db when DATABASE_PATH is not set explicitly.
|
|
31
|
+
const DEFAULT_DATABASE_PATH = path.join(os.homedir(), '.pixcode', 'auth.db');
|
|
32
|
+
|
|
33
|
+
if (!process.env.DATABASE_PATH) {
|
|
34
|
+
process.env.DATABASE_PATH = DEFAULT_DATABASE_PATH;
|
|
35
|
+
}
|
|
@@ -1,112 +1,112 @@
|
|
|
1
|
-
import { securityLog } from '../utils/security-log.js';
|
|
2
|
-
|
|
3
|
-
const MAX_FAILED_ATTEMPTS = 5;
|
|
4
|
-
const LOCKOUT_DURATION_MS = 15 * 60 * 1000;
|
|
5
|
-
const failedAttempts = new Map();
|
|
6
|
-
|
|
7
|
-
function cleanupExpired() {
|
|
8
|
-
const now = Date.now();
|
|
9
|
-
for (const [key, entry] of failedAttempts.entries()) {
|
|
10
|
-
if (entry.lockedUntil && entry.lockedUntil <= now) {
|
|
11
|
-
failedAttempts.delete(key);
|
|
12
|
-
} else if (!entry.lockedUntil && entry.lastAttempt && now - entry.lastAttempt > LOCKOUT_DURATION_MS) {
|
|
13
|
-
failedAttempts.delete(key);
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const cleanupInterval = setInterval(cleanupExpired, 5 * 60 * 1000);
|
|
19
|
-
cleanupInterval.unref();
|
|
20
|
-
|
|
21
|
-
export function checkAccountLockout(username, ip) {
|
|
22
|
-
const key = `${username}:${ip}`;
|
|
23
|
-
const entry = failedAttempts.get(key);
|
|
24
|
-
if (!entry) return { locked: false };
|
|
25
|
-
|
|
26
|
-
if (entry.lockedUntil && entry.lockedUntil > Date.now()) {
|
|
27
|
-
const remainingMs = entry.lockedUntil - Date.now();
|
|
28
|
-
const remainingMin = Math.ceil(remainingMs / 60000);
|
|
29
|
-
return {
|
|
30
|
-
locked: true,
|
|
31
|
-
remainingMs,
|
|
32
|
-
message: `Account locked due to too many failed attempts. Try again in ${remainingMin} minute(s).`,
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
if (entry.lockedUntil && entry.lockedUntil <= Date.now()) {
|
|
37
|
-
failedAttempts.delete(key);
|
|
38
|
-
return { locked: false };
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
return { locked: false };
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export function recordFailedLogin(username, ip) {
|
|
45
|
-
const key = `${username}:${ip}`;
|
|
46
|
-
let entry = failedAttempts.get(key);
|
|
47
|
-
if (!entry) {
|
|
48
|
-
entry = { count: 0, lockedUntil: null, lastAttempt: Date.now() };
|
|
49
|
-
failedAttempts.set(key, entry);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
entry.count += 1;
|
|
53
|
-
entry.lastAttempt = Date.now();
|
|
54
|
-
|
|
55
|
-
if (entry.count >= MAX_FAILED_ATTEMPTS) {
|
|
56
|
-
entry.lockedUntil = Date.now() + LOCKOUT_DURATION_MS;
|
|
57
|
-
securityLog('account_locked', {
|
|
58
|
-
username,
|
|
59
|
-
ip,
|
|
60
|
-
reason: `Exceeded ${MAX_FAILED_ATTEMPTS} failed login attempts`,
|
|
61
|
-
});
|
|
62
|
-
return {
|
|
63
|
-
locked: true,
|
|
64
|
-
message: `Account locked due to too many failed attempts. Try again in 15 minutes.`,
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
return {
|
|
69
|
-
locked: false,
|
|
70
|
-
remaining: MAX_FAILED_ATTEMPTS - entry.count,
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
export function recordSuccessfulLogin(username, ip) {
|
|
75
|
-
const key = `${username}:${ip}`;
|
|
76
|
-
failedAttempts.delete(key);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
export function validatePasswordPolicy(password) {
|
|
80
|
-
if (typeof password !== 'string' || password.length < 8) {
|
|
81
|
-
return { valid: false, error: 'Password must be at least 8 characters long' };
|
|
82
|
-
}
|
|
83
|
-
if (password.length > 128) {
|
|
84
|
-
return { valid: false, error: 'Password must not exceed 128 characters' };
|
|
85
|
-
}
|
|
86
|
-
if (!/[a-z]/.test(password)) {
|
|
87
|
-
return { valid: false, error: 'Password must contain at least one lowercase letter' };
|
|
88
|
-
}
|
|
89
|
-
if (!/[A-Z]/.test(password)) {
|
|
90
|
-
return { valid: false, error: 'Password must contain at least one uppercase letter' };
|
|
91
|
-
}
|
|
92
|
-
if (!/[0-9]/.test(password)) {
|
|
93
|
-
return { valid: false, error: 'Password must contain at least one number' };
|
|
94
|
-
}
|
|
95
|
-
if (!/[^a-zA-Z0-9]/.test(password)) {
|
|
96
|
-
return { valid: false, error: 'Password must contain at least one special character' };
|
|
97
|
-
}
|
|
98
|
-
return { valid: true };
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
export function validateUsername(username) {
|
|
102
|
-
if (typeof username !== 'string' || username.length < 3) {
|
|
103
|
-
return { valid: false, error: 'Username must be at least 3 characters' };
|
|
104
|
-
}
|
|
105
|
-
if (username.length > 64) {
|
|
106
|
-
return { valid: false, error: 'Username must not exceed 64 characters' };
|
|
107
|
-
}
|
|
108
|
-
if (!/^[a-zA-Z0-9_.-]+$/.test(username)) {
|
|
109
|
-
return { valid: false, error: 'Username may only contain letters, numbers, dots, hyphens, and underscores' };
|
|
110
|
-
}
|
|
111
|
-
return { valid: true };
|
|
112
|
-
}
|
|
1
|
+
import { securityLog } from '../utils/security-log.js';
|
|
2
|
+
|
|
3
|
+
const MAX_FAILED_ATTEMPTS = 5;
|
|
4
|
+
const LOCKOUT_DURATION_MS = 15 * 60 * 1000;
|
|
5
|
+
const failedAttempts = new Map();
|
|
6
|
+
|
|
7
|
+
function cleanupExpired() {
|
|
8
|
+
const now = Date.now();
|
|
9
|
+
for (const [key, entry] of failedAttempts.entries()) {
|
|
10
|
+
if (entry.lockedUntil && entry.lockedUntil <= now) {
|
|
11
|
+
failedAttempts.delete(key);
|
|
12
|
+
} else if (!entry.lockedUntil && entry.lastAttempt && now - entry.lastAttempt > LOCKOUT_DURATION_MS) {
|
|
13
|
+
failedAttempts.delete(key);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const cleanupInterval = setInterval(cleanupExpired, 5 * 60 * 1000);
|
|
19
|
+
cleanupInterval.unref();
|
|
20
|
+
|
|
21
|
+
export function checkAccountLockout(username, ip) {
|
|
22
|
+
const key = `${username}:${ip}`;
|
|
23
|
+
const entry = failedAttempts.get(key);
|
|
24
|
+
if (!entry) return { locked: false };
|
|
25
|
+
|
|
26
|
+
if (entry.lockedUntil && entry.lockedUntil > Date.now()) {
|
|
27
|
+
const remainingMs = entry.lockedUntil - Date.now();
|
|
28
|
+
const remainingMin = Math.ceil(remainingMs / 60000);
|
|
29
|
+
return {
|
|
30
|
+
locked: true,
|
|
31
|
+
remainingMs,
|
|
32
|
+
message: `Account locked due to too many failed attempts. Try again in ${remainingMin} minute(s).`,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (entry.lockedUntil && entry.lockedUntil <= Date.now()) {
|
|
37
|
+
failedAttempts.delete(key);
|
|
38
|
+
return { locked: false };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return { locked: false };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function recordFailedLogin(username, ip) {
|
|
45
|
+
const key = `${username}:${ip}`;
|
|
46
|
+
let entry = failedAttempts.get(key);
|
|
47
|
+
if (!entry) {
|
|
48
|
+
entry = { count: 0, lockedUntil: null, lastAttempt: Date.now() };
|
|
49
|
+
failedAttempts.set(key, entry);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
entry.count += 1;
|
|
53
|
+
entry.lastAttempt = Date.now();
|
|
54
|
+
|
|
55
|
+
if (entry.count >= MAX_FAILED_ATTEMPTS) {
|
|
56
|
+
entry.lockedUntil = Date.now() + LOCKOUT_DURATION_MS;
|
|
57
|
+
securityLog('account_locked', {
|
|
58
|
+
username,
|
|
59
|
+
ip,
|
|
60
|
+
reason: `Exceeded ${MAX_FAILED_ATTEMPTS} failed login attempts`,
|
|
61
|
+
});
|
|
62
|
+
return {
|
|
63
|
+
locked: true,
|
|
64
|
+
message: `Account locked due to too many failed attempts. Try again in 15 minutes.`,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
locked: false,
|
|
70
|
+
remaining: MAX_FAILED_ATTEMPTS - entry.count,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function recordSuccessfulLogin(username, ip) {
|
|
75
|
+
const key = `${username}:${ip}`;
|
|
76
|
+
failedAttempts.delete(key);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function validatePasswordPolicy(password) {
|
|
80
|
+
if (typeof password !== 'string' || password.length < 8) {
|
|
81
|
+
return { valid: false, error: 'Password must be at least 8 characters long' };
|
|
82
|
+
}
|
|
83
|
+
if (password.length > 128) {
|
|
84
|
+
return { valid: false, error: 'Password must not exceed 128 characters' };
|
|
85
|
+
}
|
|
86
|
+
if (!/[a-z]/.test(password)) {
|
|
87
|
+
return { valid: false, error: 'Password must contain at least one lowercase letter' };
|
|
88
|
+
}
|
|
89
|
+
if (!/[A-Z]/.test(password)) {
|
|
90
|
+
return { valid: false, error: 'Password must contain at least one uppercase letter' };
|
|
91
|
+
}
|
|
92
|
+
if (!/[0-9]/.test(password)) {
|
|
93
|
+
return { valid: false, error: 'Password must contain at least one number' };
|
|
94
|
+
}
|
|
95
|
+
if (!/[^a-zA-Z0-9]/.test(password)) {
|
|
96
|
+
return { valid: false, error: 'Password must contain at least one special character' };
|
|
97
|
+
}
|
|
98
|
+
return { valid: true };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function validateUsername(username) {
|
|
102
|
+
if (typeof username !== 'string' || username.length < 3) {
|
|
103
|
+
return { valid: false, error: 'Username must be at least 3 characters' };
|
|
104
|
+
}
|
|
105
|
+
if (username.length > 64) {
|
|
106
|
+
return { valid: false, error: 'Username must not exceed 64 characters' };
|
|
107
|
+
}
|
|
108
|
+
if (!/^[a-zA-Z0-9_.-]+$/.test(username)) {
|
|
109
|
+
return { valid: false, error: 'Username may only contain letters, numbers, dots, hyphens, and underscores' };
|
|
110
|
+
}
|
|
111
|
+
return { valid: true };
|
|
112
|
+
}
|