coolify-deploy-logs-cli
Version:
CLI tool for Coolify deployments
307 lines (250 loc) ⢠12.7 kB
JavaScript
/**
* Coolify Tools - Working Version with proper login handling
*/
const https = require('https');
const { URL } = require('url');
class CoolifyDeployWorking {
constructor(baseURL = null, githubRepo = null, domain = null) {
// Support command line arguments or environment variables
this.baseURL = baseURL ||
process.env.COOLIFY_URL ||
process.argv[2] ||
'https://coolify.247420.xyz';
// Ensure URL has proper protocol
if (this.baseURL && !this.baseURL.startsWith('http://') && !this.baseURL.startsWith('https://')) {
this.baseURL = 'https://' + this.baseURL;
}
this.githubRepo = githubRepo ||
process.env.GITHUB_REPO ||
process.argv[3] ||
'https://github.com/AnEntrypoint/nixpacks-test-app.git';
this.domain = domain ||
process.env.DOMAIN ||
process.argv[4] ||
'schwepe.247420.xyz';
this.cookies = '';
this.csrfToken = null;
this.projectId = null;
this.environmentId = null;
this.applicationId = null;
console.log('š Coolify Deploy Tool');
console.log('š” Target URL: ' + this.baseURL);
console.log('š¦ Repository: ' + this.githubRepo);
console.log('š Domain: ' + this.domain);
}
async request(url, options = {}, followRedirects = true) {
return new Promise((resolve, reject) => {
const makeRequest = (requestUrl, redirectCount = 0) => {
if (redirectCount > 5) {
reject(new Error('Too many redirects'));
return;
}
const urlObj = new URL(requestUrl);
const requestOptions = {
hostname: urlObj.hostname,
port: urlObj.port || (urlObj.protocol === 'https:' ? 443 : 80),
path: urlObj.pathname + urlObj.search,
method: options.method || 'GET',
headers: {
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'Accept-Language': 'en-US,en;q=0.5',
'Accept-Encoding': 'gzip, deflate, br',
'Connection': 'keep-alive',
'Upgrade-Insecure-Requests': '1',
...options.headers
}
};
if (this.cookies) {
requestOptions.headers['Cookie'] = this.cookies;
}
if (options.data) {
const data = typeof options.data === 'string' ? options.data : JSON.stringify(options.data);
requestOptions.headers['Content-Type'] = requestOptions.headers['Content-Type'] || 'application/json';
requestOptions.headers['Content-Length'] = Buffer.byteLength(data);
}
const req = https.request(requestOptions, (res) => {
let rawData = '';
res.on('data', (chunk) => {
rawData += chunk;
});
res.on('end', () => {
// Handle cookies
if (res.headers['set-cookie']) {
this.cookies = res.headers['set-cookie'].map(cookie => cookie.split(';')[0]).join('; ');
}
// Extract CSRF token if present
if (rawData.includes('name="_token"')) {
const csrfMatch = rawData.match(/name="_token"[^>]+value="([^"]+)"/);
if (csrfMatch) {
this.csrfToken = csrfMatch[1];
}
}
// Handle redirects
if (followRedirects && (res.statusCode === 302 || res.statusCode === 301 || res.statusCode === 303)) {
if (res.headers.location) {
const redirectUrl = res.headers.location.startsWith('http')
? res.headers.location
: this.baseURL + res.headers.location;
console.log('š Following redirect to:', redirectUrl);
return makeRequest(redirectUrl, redirectCount + 1);
}
}
resolve({
status: res.statusCode,
headers: res.headers,
raw: rawData
});
});
});
req.on('error', (err) => {
reject(err);
});
if (options.data) {
const data = typeof options.data === 'string' ? options.data : JSON.stringify(options.data);
req.write(data);
}
req.end();
};
makeRequest(url);
});
}
async login() {
console.log('\nš Logging in...');
try {
// Get login page first
console.log('š Getting login page...');
const loginPage = await this.request(this.baseURL + '/login');
if (!this.csrfToken) {
throw new Error('Could not find CSRF token on login page');
}
console.log('š Found CSRF token:', this.csrfToken.substring(0, 20) + '...');
// Submit login
console.log('š Submitting login form...');
const loginResponse = await this.request(this.baseURL + '/login', {
method: 'POST',
data: 'email=admin%40247420.xyz&password=123%2Cslam123%2Cslam&_token=' + this.csrfToken,
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Referer': this.baseURL + '/login'
}
});
console.log('š Login response status:', loginResponse.status);
if (loginResponse.status === 302 || loginResponse.status === 200) {
console.log('ā
Login successful');
// After successful login, try to access the main page
const mainResponse = await this.request(this.baseURL);
console.log('š Main page status:', mainResponse.status);
return true;
} else {
throw new Error('Login failed with status: ' + loginResponse.status);
}
} catch (error) {
console.error('ā Login error:', error.message);
throw error;
}
}
async findResources() {
console.log('\nš Looking for existing resources...');
try {
// Try different endpoints to find projects
const endpoints = [
this.baseURL,
this.baseURL + '/dashboard',
this.baseURL + '/projects',
this.baseURL + '/resources'
];
for (const endpoint of endpoints) {
console.log('š Checking endpoint:', endpoint.replace(this.baseURL, ''));
try {
const response = await this.request(endpoint);
// Look for the specific domain in the response
if (response.raw.includes(this.domain)) {
console.log('ā
Found existing resource for domain:', this.domain);
// Extract project/environment info if possible
const projectMatch = response.raw.match(/project\/([a-z0-9]{24})/);
if (projectMatch) {
this.projectId = projectMatch[1];
console.log('š Project ID:', this.projectId);
}
const envMatch = response.raw.match(/environment\/([a-z0-9]{24})/);
if (envMatch) {
this.environmentId = envMatch[1];
console.log('š Environment ID:', this.environmentId);
}
return true;
}
// Look for any project patterns
const projectMatches = response.raw.match(/project\/([a-z0-9]{24})/g);
if (projectMatches && projectMatches.length > 0) {
console.log('š Found projects:', projectMatches.length);
this.projectId = projectMatches[0].replace('project/', '');
console.log('š Using first project ID:', this.projectId);
// Now try to get environment from the project page
const projectResponse = await this.request(this.baseURL + '/project/' + this.projectId);
const envMatches = projectResponse.raw.match(/environment\/([a-z0-9]{24})/g);
if (envMatches && envMatches.length > 0) {
this.environmentId = envMatches[0].replace('environment/', '');
console.log('š Found environment ID:', this.environmentId);
}
return false; // Found project but not the specific domain
}
} catch (error) {
console.log(' ā Error accessing endpoint:', error.message);
}
}
console.log('ā¹ļø No existing resources found');
return false;
} catch (error) {
console.error('ā Resource search error:', error.message);
return false;
}
}
async deploy() {
try {
console.log('\nš Starting deployment process...');
// Step 1: Login
await this.login();
// Step 2: Look for existing resources
const resourceExists = await this.findResources();
if (resourceExists) {
console.log('\nā
Resource already exists for ' + this.domain);
if (this.projectId) {
console.log('š Manage at: ' + this.baseURL + '/project/' + this.projectId);
}
return;
}
console.log('\nš Deployment configuration:');
console.log(' Coolify URL: ' + this.baseURL);
if (this.projectId) {
console.log(' Project ID: ' + this.projectId);
}
if (this.environmentId) {
console.log(' Environment ID: ' + this.environmentId);
}
console.log(' Repository: ' + this.githubRepo);
console.log(' Domain: ' + this.domain);
if (this.projectId) {
console.log('\nš Manage at: ' + this.baseURL + '/project/' + this.projectId);
} else {
console.log('\nā ļø Could not find project - may need manual setup');
}
} catch (error) {
console.error('\nā Error:', error.message);
throw error;
}
}
}
// Main execution
if (require.main === module) {
const baseURL = process.argv[2] || null;
const githubRepo = process.argv[3] || null;
const domain = process.argv[4] || null;
const cli = new CoolifyDeployWorking(baseURL, githubRepo, domain);
cli.deploy().catch(err => {
console.error('Fatal:', err);
process.exit(1);
});
}
module.exports = CoolifyDeployWorking;