PERF: Criado script para manter a porta 3001 sempre disponível
All checks were successful
Deploy Dev / deploy (push) Successful in 45s

This commit is contained in:
Rafael Alves Lopes 2026-05-19 09:28:26 -03:00
parent 21a81282d5
commit 8790ce70d0
4 changed files with 127 additions and 8 deletions

View File

@ -5,6 +5,7 @@
"scripts": {
"build": "nest build",
"start": "cross-env NODE_ENV=production node dist/main.js",
"predev": "node scripts/ensure-port-free.js",
"dev": "cross-env NODE_ENV=development nest start --watch",
"start:dev": "npm run dev"
},

View File

@ -0,0 +1,89 @@
const { execSync } = require('child_process');
const fs = require('fs');
const path = require('path');
function loadEnvFile(filePath) {
if (!fs.existsSync(filePath)) return {};
return fs
.readFileSync(filePath, 'utf8')
.split(/\r?\n/)
.reduce((acc, line) => {
const trimmed = line.trim();
if (!trimmed || trimmed.startsWith('#')) return acc;
const separatorIndex = trimmed.indexOf('=');
if (separatorIndex === -1) return acc;
const key = trimmed.slice(0, separatorIndex).trim();
const value = trimmed.slice(separatorIndex + 1).trim();
acc[key] = value;
return acc;
}, {});
}
function getConfiguredPort() {
const env = {
...loadEnvFile(path.resolve(process.cwd(), '.env.development')),
...process.env,
};
return Number(env.PORT || env.BACKEND_PORT || 3001);
}
function getWindowsPids(port) {
const output = execSync(`netstat -ano -p tcp | findstr :${port}`, {
encoding: 'utf8',
stdio: ['ignore', 'pipe', 'ignore'],
});
return Array.from(
new Set(
output
.split(/\r?\n/)
.filter((line) => line.includes('LISTENING'))
.map((line) => line.trim().split(/\s+/).at(-1))
.filter(Boolean)
.filter((pid) => pid !== String(process.pid)),
),
);
}
function getUnixPids(port) {
const output = execSync(`lsof -ti tcp:${port} -sTCP:LISTEN`, {
encoding: 'utf8',
stdio: ['ignore', 'pipe', 'ignore'],
});
return Array.from(
new Set(
output
.split(/\r?\n/)
.map((pid) => pid.trim())
.filter(Boolean)
.filter((pid) => pid !== String(process.pid)),
),
);
}
function killPid(pid) {
if (process.platform === 'win32') {
execSync(`taskkill /PID ${pid} /F /T`, { stdio: 'ignore' });
return;
}
execSync(`kill -TERM ${pid}`, { stdio: 'ignore' });
}
const port = getConfiguredPort();
try {
const pids = process.platform === 'win32' ? getWindowsPids(port) : getUnixPids(port);
if (!pids.length) {
process.exit(0);
}
pids.forEach(killPid);
console.log(`Porta ${port} liberada. Processo(s) encerrado(s): ${pids.join(', ')}`);
} catch {
process.exit(0);
}

View File

@ -9,10 +9,24 @@ async function bootstrap() {
const app = await NestFactory.create(AppModule);
const frontendUrl = process.env.FRONTEND_URL || 'http://localhost:3000';
const allowedOrigins = new Set(
frontendUrl
.split(',')
.map((origin) => origin.trim())
.filter(Boolean),
);
allowedOrigins.add('http://localhost:3000');
allowedOrigins.add('http://localhost:5173');
const port = Number(process.env.PORT || process.env.BACKEND_PORT || 3001);
app.enableCors({
origin: frontendUrl,
origin(origin, callback) {
if (!origin || allowedOrigins.has(origin)) {
callback(null, true);
return;
}
callback(new Error(`Origem CORS nao permitida: ${origin}`));
},
credentials: true,
});

View File

@ -15,6 +15,7 @@ export class LdapAuthProvider {
async authenticate({ username, password }: LoginData): Promise<AuthResult> {
const config = this.authConfig.getConfig();
const normalizedUsername = this.normalizeUsername(username);
if (!config.ldap.enabled) {
throw new ForbiddenException('Login AD/LDAP desabilitado');
@ -24,7 +25,7 @@ export class LdapAuthProvider {
throw new Error('LDAP_URL nao configurado');
}
if (!username || !password) {
if (!normalizedUsername || !password) {
throw new UnauthorizedException('Usuario e senha sao obrigatorios');
}
@ -39,17 +40,19 @@ export class LdapAuthProvider {
await client.bind(config.ldap.bindDn, config.ldap.bindPassword);
}
const userPrincipal = this.buildUserPrincipal(username);
const userPrincipal = this.buildUserPrincipal(normalizedUsername);
await client.bind(userPrincipal, password);
const directoryUser = await this.searchUser(client, username);
const directoryUser = await this.searchUser(client, this.getSearchUsername(normalizedUsername));
const providerUser = {
id: directoryUser?.email || userPrincipal,
name: directoryUser?.name || username,
name: directoryUser?.name || normalizedUsername,
email:
directoryUser?.email ||
(config.ldap.domain ? `${username}@${config.ldap.domain}` : null),
username: directoryUser?.username || username,
(config.ldap.domain && !normalizedUsername.includes('@')
? `${normalizedUsername}@${config.ldap.domain}`
: normalizedUsername),
username: directoryUser?.username || normalizedUsername,
provider: 'ldap' as const,
};
const user = await this.userAccess.syncAuthenticatedUser(providerUser);
@ -59,7 +62,7 @@ export class LdapAuthProvider {
user,
};
} catch (_error) {
throw new UnauthorizedException('Autenticacao AD/LDAP falhou');
throw new UnauthorizedException('Autenticação falhou');
} finally {
await client.unbind().catch(() => undefined);
}
@ -72,6 +75,10 @@ export class LdapAuthProvider {
return config.ldap.userDnTemplate.replaceAll('{{username}}', username);
}
if (username.includes('@')) {
return username;
}
if (config.ldap.domain) {
return `${username}@${config.ldap.domain}`;
}
@ -79,6 +86,14 @@ export class LdapAuthProvider {
return username;
}
private normalizeUsername(username: string) {
return String(username || '').trim();
}
private getSearchUsername(username: string) {
return username.includes('@') ? username.split('@')[0] : username;
}
private async searchUser(client: Client, username: string) {
const config = this.authConfig.getConfig();