feat: change default listen port from 3000 to 1852 (#756)

Updates the backend listen port, Vite dev proxy target, Docker EXPOSE,
compose port mapping, .env.example default, GitHub Actions smoke-test
default, healthcheck URLs, and every doc/example reference. Test fixtures
that include example URLs were updated for consistency, though their
assertions are port-agnostic.

The rate-limit value of 3000 in middleware/rateLimiters.ts and the
3000 entry in WEB_UI_PORTS (which detects user containers like Grafana)
are intentionally untouched.
This commit is contained in:
Anso
2026-04-24 22:23:31 -04:00
committed by GitHub
parent d6b744e8e6
commit ed553f1f19
27 changed files with 72 additions and 72 deletions
+1 -1
View File
@@ -63,7 +63,7 @@ describe('POST /api/nodes - api_url SSRF validation (C2 fix)', () => {
.send({
name: 'lan-node',
type: 'remote',
api_url: 'http://192.168.1.50:3000',
api_url: 'http://192.168.1.50:1852',
api_token: 'sometoken',
});
// Should succeed (201 or 200) - not a validation error
@@ -1185,7 +1185,7 @@ describe('SchedulerService - executeUpdateRemote', () => {
it('proxies update execution to remote node', async () => {
mockGetNode.mockReturnValue({ id: 2, name: 'remote', type: 'remote', status: 'online' });
mockGetProxyTarget.mockReturnValue({
apiUrl: 'http://remote:3000',
apiUrl: 'http://remote:1852',
apiToken: 'test-token',
});
@@ -1211,7 +1211,7 @@ describe('SchedulerService - executeUpdateRemote', () => {
await svc.triggerTask(88);
expect(mockFetch).toHaveBeenCalledWith(
'http://remote:3000/api/auto-update/execute',
'http://remote:1852/api/auto-update/execute',
expect.objectContaining({
method: 'POST',
body: JSON.stringify({ target: 'web-app' }),
@@ -1226,7 +1226,7 @@ describe('SchedulerService - executeUpdateRemote', () => {
it('records failure when remote node returns error', async () => {
mockGetNode.mockReturnValue({ id: 2, name: 'remote', type: 'remote', status: 'online' });
mockGetProxyTarget.mockReturnValue({
apiUrl: 'http://remote:3000',
apiUrl: 'http://remote:1852',
apiToken: 'test-token',
});
@@ -210,11 +210,11 @@ describe('TemplateService', () => {
it('handles values with special characters', () => {
const result = service.generateEnvString({
PASSWORD: 'p@ss=word!',
URL: 'http://localhost:3000',
URL: 'http://localhost:1852',
});
expect(result).toContain('PASSWORD=p@ss=word!');
expect(result).toContain('URL=http://localhost:3000');
expect(result).toContain('URL=http://localhost:1852');
});
});
+1 -1
View File
@@ -568,7 +568,7 @@ describe('Orphaned role assignment cleanup', () => {
it('deleting a node removes its role assignments', async () => {
const db = DatabaseService.getInstance();
// Create a test node
const nodeId = db.addNode({ name: 'test-cleanup-node', type: 'remote', api_url: 'http://test:3000', api_token: '', compose_dir: '/tmp', is_default: false });
const nodeId = db.addNode({ name: 'test-cleanup-node', type: 'remote', api_url: 'http://test:1852', api_token: '', compose_dir: '/tmp', is_default: false });
// Create a role assignment for this node
const hash = await bcrypt.hash('password123', 1);
const userId = db.addUser({ username: 'nodeorphan', password_hash: hash, role: 'viewer' });
+8 -8
View File
@@ -63,7 +63,7 @@ describe('isValidStackName', () => {
describe('isValidRemoteUrl', () => {
it('accepts valid http URLs', () => {
const result = isValidRemoteUrl('http://192.168.1.10:3000');
const result = isValidRemoteUrl('http://192.168.1.10:1852');
expect(result.valid).toBe(true);
});
@@ -84,25 +84,25 @@ describe('isValidRemoteUrl', () => {
});
it('rejects localhost', () => {
expect(isValidRemoteUrl('http://localhost:3000').valid).toBe(false);
expect(isValidRemoteUrl('http://LOCALHOST:3000').valid).toBe(false);
expect(isValidRemoteUrl('http://localhost:1852').valid).toBe(false);
expect(isValidRemoteUrl('http://LOCALHOST:1852').valid).toBe(false);
});
it('rejects loopback IPs', () => {
expect(isValidRemoteUrl('http://127.0.0.1:3000').valid).toBe(false);
expect(isValidRemoteUrl('http://127.0.0.1:1852').valid).toBe(false);
expect(isValidRemoteUrl('http://127.1.2.3').valid).toBe(false);
// Node.js URL.hostname preserves brackets: new URL('http://[::1]').hostname === '[::1]'
expect(isValidRemoteUrl('http://[::1]:3000').valid).toBe(false);
expect(isValidRemoteUrl('http://[::1]:1852').valid).toBe(false);
});
it('rejects 0.0.0.0', () => {
expect(isValidRemoteUrl('http://0.0.0.0:3000').valid).toBe(false);
expect(isValidRemoteUrl('http://0.0.0.0:1852').valid).toBe(false);
});
it('allows LAN/private IPs (users need these for local network nodes)', () => {
// Users legitimately run Sencho nodes on their LAN
expect(isValidRemoteUrl('http://192.168.1.100:3000').valid).toBe(true);
expect(isValidRemoteUrl('http://10.0.0.5:3000').valid).toBe(true);
expect(isValidRemoteUrl('http://192.168.1.100:1852').valid).toBe(true);
expect(isValidRemoteUrl('http://10.0.0.5:1852').valid).toBe(true);
});
});
+1 -1
View File
@@ -3,7 +3,7 @@
// monolith.
// Server
export const PORT = 3000;
export const PORT = 1852;
// Password policy
export const MIN_PASSWORD_LENGTH = 8;
+1 -1
View File
@@ -37,7 +37,7 @@ function mintPilotEnrollment(nodeId: number, req: Request): { token: string; exp
const forwardedProto = req.headers['x-forwarded-proto'];
const protoHeader = Array.isArray(forwardedProto) ? forwardedProto[0] : forwardedProto;
const protocol = protoHeader || req.protocol || 'http';
const host = req.get('host') || 'localhost:3000';
const host = req.get('host') || 'localhost:1852';
const primaryUrl = `${protocol}://${host}`;
const dockerRun =
+1 -1
View File
@@ -22,7 +22,7 @@ export function isValidRemoteUrl(
console.warn('[Validation] URL parse failure:', (e as Error).message, '— input:', raw);
return {
valid: false,
reason: 'API URL must be a valid URL (e.g. https://my-server.example.com:3000)',
reason: 'API URL must be a valid URL (e.g. https://my-server.example.com:1852)',
};
}
if (!['http:', 'https:'].includes(url.protocol)) {