Files
idea.llm.gitea.repo.docker.…/app/main/project-scanner.js
2026-02-27 08:55:41 -06:00

124 lines
3.6 KiB
JavaScript

const fs = require('fs');
const path = require('path');
class ProjectScanner {
constructor(rootPath) {
this.rootPath = rootPath;
}
async scan() {
const projects = [];
try {
const entries = fs.readdirSync(this.rootPath, { withFileTypes: true });
for (const entry of entries) {
if (!entry.isDirectory()) continue;
const projectPath = path.join(this.rootPath, entry.name);
const projectInfo = this.analyzeProject(projectPath, entry.name);
if (projectInfo) {
projects.push(projectInfo);
}
}
} catch (err) {
console.error('Failed to scan projects:', err);
}
return projects;
}
analyzeProject(projectPath, name) {
const hasDockerfile = fs.existsSync(path.join(projectPath, 'Dockerfile'));
const hasDockerCompose = fs.existsSync(path.join(projectPath, 'docker-compose.yml'));
const hasBuildScript = fs.existsSync(path.join(projectPath, 'build-image-tar.ps1'));
const hasDeployScript = fs.existsSync(path.join(projectPath, 'deploy-docker-auto.ps1'));
const hasDeploymentConfig = fs.existsSync(path.join(projectPath, 'docker-deployment.json'));
const hasDockerIgnore = fs.existsSync(path.join(projectPath, '.dockerignore'));
// Find tar files
let tarFile = null;
try {
const files = fs.readdirSync(projectPath);
tarFile = files.find(f => f.endsWith('.tar'));
} catch (err) {
// ignore
}
// Read deployment config if exists
let deploymentConfig = null;
if (hasDeploymentConfig) {
try {
deploymentConfig = JSON.parse(fs.readFileSync(path.join(projectPath, 'docker-deployment.json'), 'utf-8'));
} catch (err) {
// ignore
}
}
// Determine docker status
let dockerStatus = 'none';
if (hasDockerfile && hasDockerCompose) {
dockerStatus = 'configured';
} else if (hasDockerfile || hasDockerCompose) {
dockerStatus = 'partial';
}
return {
name,
path: projectPath,
hasDockerfile,
hasDockerCompose,
hasBuildScript,
hasDeployScript,
hasDeploymentConfig,
hasDockerIgnore,
tarFile,
deploymentConfig,
dockerStatus,
serverId: deploymentConfig?.deployment?.serverId || null,
remotePath: deploymentConfig?.deployment?.targetPath || `~/containers/${name}`
};
}
// Scan subdirectories too (for monorepos like AudioSphere)
async scanDeep(maxDepth = 2) {
const projects = [];
await this._scanRecursive(this.rootPath, projects, 0, maxDepth);
return projects;
}
async _scanRecursive(currentPath, projects, depth, maxDepth) {
if (depth > maxDepth) return;
try {
const entries = fs.readdirSync(currentPath, { withFileTypes: true });
// Check if this directory has a Dockerfile
const hasDockerfile = entries.some(e => e.name === 'Dockerfile');
if (hasDockerfile) {
const name = path.relative(this.rootPath, currentPath).replace(/\\/g, '/');
const projectInfo = this.analyzeProject(currentPath, name);
if (projectInfo) {
projects.push(projectInfo);
}
}
// Recurse into subdirectories
for (const entry of entries) {
if (!entry.isDirectory()) continue;
if (entry.name.startsWith('.')) continue;
if (entry.name === 'node_modules') continue;
if (entry.name === 'dist') continue;
if (entry.name === 'build') continue;
await this._scanRecursive(path.join(currentPath, entry.name), projects, depth + 1, maxDepth);
}
} catch (err) {
// ignore permission errors etc
}
}
}
module.exports = { ProjectScanner };