update
This commit is contained in:
195
ts/classes/registries.ts
Normal file
195
ts/classes/registries.ts
Normal file
@@ -0,0 +1,195 @@
|
||||
/**
|
||||
* Registry Manager for Onebox
|
||||
*
|
||||
* Manages Docker registry credentials and authentication
|
||||
*/
|
||||
|
||||
import * as plugins from '../plugins.ts';
|
||||
import type { IRegistry } from '../types.ts';
|
||||
import { logger } from '../logging.ts';
|
||||
import { OneboxDatabase } from './database.ts';
|
||||
|
||||
export class OneboxRegistriesManager {
|
||||
private oneboxRef: any; // Will be Onebox instance
|
||||
private database: OneboxDatabase;
|
||||
|
||||
constructor(oneboxRef: any) {
|
||||
this.oneboxRef = oneboxRef;
|
||||
this.database = oneboxRef.database;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypt a password (simple base64 for now, should use proper encryption)
|
||||
*/
|
||||
private encryptPassword(password: string): string {
|
||||
// TODO: Use proper encryption with a secret key
|
||||
// For now, using base64 encoding (NOT SECURE, just for structure)
|
||||
return plugins.encoding.encodeBase64(new TextEncoder().encode(password));
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt a password
|
||||
*/
|
||||
private decryptPassword(encrypted: string): string {
|
||||
// TODO: Use proper decryption
|
||||
return new TextDecoder().decode(plugins.encoding.decodeBase64(encrypted));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a registry
|
||||
*/
|
||||
async addRegistry(url: string, username: string, password: string): Promise<IRegistry> {
|
||||
try {
|
||||
// Check if registry already exists
|
||||
const existing = this.database.getRegistryByURL(url);
|
||||
if (existing) {
|
||||
throw new Error(`Registry already exists: ${url}`);
|
||||
}
|
||||
|
||||
// Encrypt password
|
||||
const passwordEncrypted = this.encryptPassword(password);
|
||||
|
||||
// Create registry in database
|
||||
const registry = await this.database.createRegistry({
|
||||
url,
|
||||
username,
|
||||
passwordEncrypted,
|
||||
createdAt: Date.now(),
|
||||
});
|
||||
|
||||
logger.success(`Registry added: ${url}`);
|
||||
|
||||
// Perform Docker login
|
||||
await this.loginToRegistry(registry);
|
||||
|
||||
return registry;
|
||||
} catch (error) {
|
||||
logger.error(`Failed to add registry ${url}: ${error.message}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a registry
|
||||
*/
|
||||
async removeRegistry(url: string): Promise<void> {
|
||||
try {
|
||||
const registry = this.database.getRegistryByURL(url);
|
||||
if (!registry) {
|
||||
throw new Error(`Registry not found: ${url}`);
|
||||
}
|
||||
|
||||
this.database.deleteRegistry(url);
|
||||
logger.success(`Registry removed: ${url}`);
|
||||
|
||||
// Note: We don't perform docker logout as it might affect other users
|
||||
} catch (error) {
|
||||
logger.error(`Failed to remove registry ${url}: ${error.message}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* List all registries
|
||||
*/
|
||||
listRegistries(): IRegistry[] {
|
||||
return this.database.getAllRegistries();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get registry by URL
|
||||
*/
|
||||
getRegistry(url: string): IRegistry | null {
|
||||
return this.database.getRegistryByURL(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform Docker login for a registry
|
||||
*/
|
||||
async loginToRegistry(registry: IRegistry): Promise<void> {
|
||||
try {
|
||||
logger.info(`Logging into registry: ${registry.url}`);
|
||||
|
||||
const password = this.decryptPassword(registry.passwordEncrypted);
|
||||
|
||||
// Use docker login command
|
||||
const command = [
|
||||
'docker',
|
||||
'login',
|
||||
registry.url,
|
||||
'--username',
|
||||
registry.username,
|
||||
'--password-stdin',
|
||||
];
|
||||
|
||||
const process = new Deno.Command('docker', {
|
||||
args: ['login', registry.url, '--username', registry.username, '--password-stdin'],
|
||||
stdin: 'piped',
|
||||
stdout: 'piped',
|
||||
stderr: 'piped',
|
||||
});
|
||||
|
||||
const child = process.spawn();
|
||||
|
||||
// Write password to stdin
|
||||
const writer = child.stdin.getWriter();
|
||||
await writer.write(new TextEncoder().encode(password));
|
||||
await writer.close();
|
||||
|
||||
const { code, stdout, stderr } = await child.output();
|
||||
|
||||
if (code !== 0) {
|
||||
const errorMsg = new TextDecoder().decode(stderr);
|
||||
throw new Error(`Docker login failed: ${errorMsg}`);
|
||||
}
|
||||
|
||||
logger.success(`Logged into registry: ${registry.url}`);
|
||||
} catch (error) {
|
||||
logger.error(`Failed to login to registry ${registry.url}: ${error.message}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Login to all registries (useful on daemon start)
|
||||
*/
|
||||
async loginToAllRegistries(): Promise<void> {
|
||||
const registries = this.listRegistries();
|
||||
|
||||
for (const registry of registries) {
|
||||
try {
|
||||
await this.loginToRegistry(registry);
|
||||
} catch (error) {
|
||||
logger.warn(`Failed to login to ${registry.url}: ${error.message}`);
|
||||
// Continue with other registries
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test registry connection
|
||||
*/
|
||||
async testRegistry(url: string, username: string, password: string): Promise<boolean> {
|
||||
try {
|
||||
const command = new Deno.Command('docker', {
|
||||
args: ['login', url, '--username', username, '--password-stdin'],
|
||||
stdin: 'piped',
|
||||
stdout: 'piped',
|
||||
stderr: 'piped',
|
||||
});
|
||||
|
||||
const child = command.spawn();
|
||||
|
||||
const writer = child.stdin.getWriter();
|
||||
await writer.write(new TextEncoder().encode(password));
|
||||
await writer.close();
|
||||
|
||||
const { code } = await child.output();
|
||||
|
||||
return code === 0;
|
||||
} catch (error) {
|
||||
logger.error(`Failed to test registry ${url}: ${error.message}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user