PERF: Criado script para manter a porta 3001 sempre disponível
All checks were successful
Deploy Dev / deploy (push) Successful in 45s
All checks were successful
Deploy Dev / deploy (push) Successful in 45s
This commit is contained in:
parent
21a81282d5
commit
8790ce70d0
@ -5,6 +5,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "nest build",
|
"build": "nest build",
|
||||||
"start": "cross-env NODE_ENV=production node dist/main.js",
|
"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",
|
"dev": "cross-env NODE_ENV=development nest start --watch",
|
||||||
"start:dev": "npm run dev"
|
"start:dev": "npm run dev"
|
||||||
},
|
},
|
||||||
|
|||||||
89
scripts/ensure-port-free.js
Normal file
89
scripts/ensure-port-free.js
Normal 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);
|
||||||
|
}
|
||||||
16
src/main.ts
16
src/main.ts
@ -9,10 +9,24 @@ async function bootstrap() {
|
|||||||
|
|
||||||
const app = await NestFactory.create(AppModule);
|
const app = await NestFactory.create(AppModule);
|
||||||
const frontendUrl = process.env.FRONTEND_URL || 'http://localhost:3000';
|
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);
|
const port = Number(process.env.PORT || process.env.BACKEND_PORT || 3001);
|
||||||
|
|
||||||
app.enableCors({
|
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,
|
credentials: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -15,6 +15,7 @@ export class LdapAuthProvider {
|
|||||||
|
|
||||||
async authenticate({ username, password }: LoginData): Promise<AuthResult> {
|
async authenticate({ username, password }: LoginData): Promise<AuthResult> {
|
||||||
const config = this.authConfig.getConfig();
|
const config = this.authConfig.getConfig();
|
||||||
|
const normalizedUsername = this.normalizeUsername(username);
|
||||||
|
|
||||||
if (!config.ldap.enabled) {
|
if (!config.ldap.enabled) {
|
||||||
throw new ForbiddenException('Login AD/LDAP desabilitado');
|
throw new ForbiddenException('Login AD/LDAP desabilitado');
|
||||||
@ -24,7 +25,7 @@ export class LdapAuthProvider {
|
|||||||
throw new Error('LDAP_URL nao configurado');
|
throw new Error('LDAP_URL nao configurado');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!username || !password) {
|
if (!normalizedUsername || !password) {
|
||||||
throw new UnauthorizedException('Usuario e senha sao obrigatorios');
|
throw new UnauthorizedException('Usuario e senha sao obrigatorios');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,17 +40,19 @@ export class LdapAuthProvider {
|
|||||||
await client.bind(config.ldap.bindDn, config.ldap.bindPassword);
|
await client.bind(config.ldap.bindDn, config.ldap.bindPassword);
|
||||||
}
|
}
|
||||||
|
|
||||||
const userPrincipal = this.buildUserPrincipal(username);
|
const userPrincipal = this.buildUserPrincipal(normalizedUsername);
|
||||||
await client.bind(userPrincipal, password);
|
await client.bind(userPrincipal, password);
|
||||||
|
|
||||||
const directoryUser = await this.searchUser(client, username);
|
const directoryUser = await this.searchUser(client, this.getSearchUsername(normalizedUsername));
|
||||||
const providerUser = {
|
const providerUser = {
|
||||||
id: directoryUser?.email || userPrincipal,
|
id: directoryUser?.email || userPrincipal,
|
||||||
name: directoryUser?.name || username,
|
name: directoryUser?.name || normalizedUsername,
|
||||||
email:
|
email:
|
||||||
directoryUser?.email ||
|
directoryUser?.email ||
|
||||||
(config.ldap.domain ? `${username}@${config.ldap.domain}` : null),
|
(config.ldap.domain && !normalizedUsername.includes('@')
|
||||||
username: directoryUser?.username || username,
|
? `${normalizedUsername}@${config.ldap.domain}`
|
||||||
|
: normalizedUsername),
|
||||||
|
username: directoryUser?.username || normalizedUsername,
|
||||||
provider: 'ldap' as const,
|
provider: 'ldap' as const,
|
||||||
};
|
};
|
||||||
const user = await this.userAccess.syncAuthenticatedUser(providerUser);
|
const user = await this.userAccess.syncAuthenticatedUser(providerUser);
|
||||||
@ -59,7 +62,7 @@ export class LdapAuthProvider {
|
|||||||
user,
|
user,
|
||||||
};
|
};
|
||||||
} catch (_error) {
|
} catch (_error) {
|
||||||
throw new UnauthorizedException('Autenticacao AD/LDAP falhou');
|
throw new UnauthorizedException('Autenticação falhou');
|
||||||
} finally {
|
} finally {
|
||||||
await client.unbind().catch(() => undefined);
|
await client.unbind().catch(() => undefined);
|
||||||
}
|
}
|
||||||
@ -72,6 +75,10 @@ export class LdapAuthProvider {
|
|||||||
return config.ldap.userDnTemplate.replaceAll('{{username}}', username);
|
return config.ldap.userDnTemplate.replaceAll('{{username}}', username);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (username.includes('@')) {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
if (config.ldap.domain) {
|
if (config.ldap.domain) {
|
||||||
return `${username}@${config.ldap.domain}`;
|
return `${username}@${config.ldap.domain}`;
|
||||||
}
|
}
|
||||||
@ -79,6 +86,14 @@ export class LdapAuthProvider {
|
|||||||
return username;
|
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) {
|
private async searchUser(client: Client, username: string) {
|
||||||
const config = this.authConfig.getConfig();
|
const config = this.authConfig.getConfig();
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user