feat(mod_services): Add global service registry and global commands for managing project containers
This commit is contained in:
190
ts/mod_services/classes.globalregistry.ts
Normal file
190
ts/mod_services/classes.globalregistry.ts
Normal file
@@ -0,0 +1,190 @@
|
||||
import * as plugins from '../plugins.js';
|
||||
import { DockerContainer } from './classes.dockercontainer.js';
|
||||
import { logger } from '../gitzone.logging.js';
|
||||
|
||||
export interface IRegisteredProject {
|
||||
projectPath: string;
|
||||
projectName: string;
|
||||
containers: {
|
||||
mongo?: string;
|
||||
minio?: string;
|
||||
elasticsearch?: string;
|
||||
};
|
||||
ports: {
|
||||
mongo?: number;
|
||||
s3?: number;
|
||||
s3Console?: number;
|
||||
elasticsearch?: number;
|
||||
};
|
||||
enabledServices: string[];
|
||||
lastActive: number;
|
||||
}
|
||||
|
||||
export interface IGlobalRegistryData {
|
||||
projects: { [projectPath: string]: IRegisteredProject };
|
||||
}
|
||||
|
||||
export class GlobalRegistry {
|
||||
private static instance: GlobalRegistry | null = null;
|
||||
private kvStore: plugins.npmextra.KeyValueStore<IGlobalRegistryData>;
|
||||
private docker: DockerContainer;
|
||||
|
||||
private constructor() {
|
||||
this.kvStore = new plugins.npmextra.KeyValueStore({
|
||||
typeArg: 'userHomeDir',
|
||||
identityArg: 'gitzone-services',
|
||||
});
|
||||
this.docker = new DockerContainer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the singleton instance
|
||||
*/
|
||||
public static getInstance(): GlobalRegistry {
|
||||
if (!GlobalRegistry.instance) {
|
||||
GlobalRegistry.instance = new GlobalRegistry();
|
||||
}
|
||||
return GlobalRegistry.instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register or update a project in the global registry
|
||||
*/
|
||||
public async registerProject(data: Omit<IRegisteredProject, 'lastActive'>): Promise<void> {
|
||||
const allData = await this.kvStore.readAll();
|
||||
const projects = allData.projects || {};
|
||||
|
||||
projects[data.projectPath] = {
|
||||
...data,
|
||||
lastActive: Date.now(),
|
||||
};
|
||||
|
||||
await this.kvStore.writeKey('projects', projects);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a project from the registry
|
||||
*/
|
||||
public async unregisterProject(projectPath: string): Promise<void> {
|
||||
const allData = await this.kvStore.readAll();
|
||||
const projects = allData.projects || {};
|
||||
|
||||
if (projects[projectPath]) {
|
||||
delete projects[projectPath];
|
||||
await this.kvStore.writeKey('projects', projects);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the lastActive timestamp for a project
|
||||
*/
|
||||
public async touchProject(projectPath: string): Promise<void> {
|
||||
const allData = await this.kvStore.readAll();
|
||||
const projects = allData.projects || {};
|
||||
|
||||
if (projects[projectPath]) {
|
||||
projects[projectPath].lastActive = Date.now();
|
||||
await this.kvStore.writeKey('projects', projects);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all registered projects
|
||||
*/
|
||||
public async getAllProjects(): Promise<{ [path: string]: IRegisteredProject }> {
|
||||
const allData = await this.kvStore.readAll();
|
||||
return allData.projects || {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a project is registered
|
||||
*/
|
||||
public async isRegistered(projectPath: string): Promise<boolean> {
|
||||
const projects = await this.getAllProjects();
|
||||
return !!projects[projectPath];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get status of all containers across all registered projects
|
||||
*/
|
||||
public async getGlobalStatus(): Promise<
|
||||
Array<{
|
||||
projectPath: string;
|
||||
projectName: string;
|
||||
containers: Array<{ name: string; status: string }>;
|
||||
lastActive: number;
|
||||
}>
|
||||
> {
|
||||
const projects = await this.getAllProjects();
|
||||
const result: Array<{
|
||||
projectPath: string;
|
||||
projectName: string;
|
||||
containers: Array<{ name: string; status: string }>;
|
||||
lastActive: number;
|
||||
}> = [];
|
||||
|
||||
for (const [path, project] of Object.entries(projects)) {
|
||||
const containerStatuses: Array<{ name: string; status: string }> = [];
|
||||
|
||||
for (const containerName of Object.values(project.containers)) {
|
||||
if (containerName) {
|
||||
const status = await this.docker.getStatus(containerName);
|
||||
containerStatuses.push({ name: containerName, status });
|
||||
}
|
||||
}
|
||||
|
||||
result.push({
|
||||
projectPath: path,
|
||||
projectName: project.projectName,
|
||||
containers: containerStatuses,
|
||||
lastActive: project.lastActive,
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop all containers across all registered projects
|
||||
*/
|
||||
public async stopAll(): Promise<{ stopped: string[]; failed: string[] }> {
|
||||
const projects = await this.getAllProjects();
|
||||
const stopped: string[] = [];
|
||||
const failed: string[] = [];
|
||||
|
||||
for (const project of Object.values(projects)) {
|
||||
for (const containerName of Object.values(project.containers)) {
|
||||
if (containerName) {
|
||||
const status = await this.docker.getStatus(containerName);
|
||||
if (status === 'running') {
|
||||
if (await this.docker.stop(containerName)) {
|
||||
stopped.push(containerName);
|
||||
} else {
|
||||
failed.push(containerName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { stopped, failed };
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove stale registry entries (projects that no longer exist on disk)
|
||||
*/
|
||||
public async cleanup(): Promise<string[]> {
|
||||
const projects = await this.getAllProjects();
|
||||
const removed: string[] = [];
|
||||
|
||||
for (const projectPath of Object.keys(projects)) {
|
||||
const exists = await plugins.smartfs.directory(projectPath).exists();
|
||||
if (!exists) {
|
||||
await this.unregisterProject(projectPath);
|
||||
removed.push(projectPath);
|
||||
}
|
||||
}
|
||||
|
||||
return removed;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user