feat: Implement platform service providers for MinIO and MongoDB

- Added base interface and abstract class for platform service providers.
- Created MinIOProvider class for S3-compatible storage with deployment, provisioning, and deprovisioning functionalities.
- Implemented MongoDBProvider class for MongoDB service with similar capabilities.
- Introduced error handling utilities for better error management.
- Developed TokensComponent for managing registry tokens in the UI, including creation, deletion, and display of tokens.
This commit is contained in:
2025-11-25 04:20:19 +00:00
parent 9aa6906ca5
commit 8ebd677478
28 changed files with 3462 additions and 490 deletions

View File

@@ -1,5 +1,6 @@
import { Component, inject, signal, OnInit } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { RouterLink } from '@angular/router';
import { ApiService } from '../../core/services/api.service';
import { ToastService } from '../../core/services/toast.service';
import { IRegistry, IRegistryCreate } from '../../core/types/api.types';
@@ -13,6 +14,7 @@ import {
import { ButtonComponent } from '../../ui/button/button.component';
import { InputComponent } from '../../ui/input/input.component';
import { LabelComponent } from '../../ui/label/label.component';
import { BadgeComponent } from '../../ui/badge/badge.component';
import {
TableComponent,
TableHeaderComponent,
@@ -35,6 +37,7 @@ import {
standalone: true,
imports: [
FormsModule,
RouterLink,
CardComponent,
CardHeaderComponent,
CardTitleComponent,
@@ -43,6 +46,7 @@ import {
ButtonComponent,
InputComponent,
LabelComponent,
BadgeComponent,
TableComponent,
TableHeaderComponent,
TableBodyComponent,
@@ -59,42 +63,75 @@ import {
template: `
<div class="space-y-6">
<div>
<h1 class="text-3xl font-bold tracking-tight">Docker Registries</h1>
<p class="text-muted-foreground">Manage Docker registry credentials</p>
<h1 class="text-3xl font-bold tracking-tight">Registries</h1>
<p class="text-muted-foreground">Manage container image registries</p>
</div>
<!-- Add Registry Form -->
<ui-card>
<ui-card-header class="flex flex-col space-y-1.5">
<ui-card-title>Add Registry</ui-card-title>
<ui-card-description>Add credentials for a private Docker registry</ui-card-description>
<!-- Onebox Registry Card -->
<ui-card class="border-primary/50">
<ui-card-header class="flex flex-row items-start justify-between space-y-0">
<div class="space-y-1">
<div class="flex items-center gap-2">
<svg class="h-5 w-5 text-primary" viewBox="0 0 24 24" fill="currentColor">
<path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/>
</svg>
<ui-card-title>Onebox Registry (Built-in)</ui-card-title>
<ui-badge>Default</ui-badge>
</div>
<ui-card-description>Built-in container registry for your services</ui-card-description>
</div>
</ui-card-header>
<ui-card-content>
<form (ngSubmit)="addRegistry()" class="grid gap-4 md:grid-cols-4">
<div class="space-y-2">
<label uiLabel>Registry URL</label>
<input uiInput [(ngModel)]="form.url" name="url" placeholder="registry.example.com" required />
<div class="grid gap-6 md:grid-cols-3">
<div>
<div class="text-sm font-medium text-muted-foreground">Status</div>
<div class="flex items-center gap-2 mt-1">
<span class="h-2 w-2 rounded-full bg-success animate-pulse"></span>
<span class="font-medium text-success">Running</span>
</div>
</div>
<div class="space-y-2">
<label uiLabel>Username</label>
<input uiInput [(ngModel)]="form.username" name="username" required />
<div>
<div class="text-sm font-medium text-muted-foreground">Registry URL</div>
<div class="font-mono text-sm mt-1">localhost:3000/v2</div>
</div>
<div class="space-y-2">
<label uiLabel>Password</label>
<input uiInput type="password" [(ngModel)]="form.password" name="password" required />
<div>
<div class="text-sm font-medium text-muted-foreground">Authentication</div>
<div class="mt-1">
<a routerLink="/tokens" class="text-primary hover:underline text-sm">
Manage Tokens
</a>
</div>
</div>
<div class="flex items-end">
<button uiButton type="submit" [disabled]="loading()">Add Registry</button>
</div>
<div class="mt-6 p-4 bg-muted rounded-lg">
<h4 class="font-medium mb-2">Quick Start</h4>
<p class="text-sm text-muted-foreground mb-3">
To push images to the Onebox registry, use a CI or Global token:
</p>
<div class="font-mono text-xs bg-background p-3 rounded border overflow-x-auto">
<div class="text-muted-foreground"># Login to the registry</div>
<div>docker login localhost:3000 -u onebox -p YOUR_TOKEN</div>
<div class="mt-2 text-muted-foreground"># Tag and push your image</div>
<div>docker tag myapp localhost:3000/myservice:latest</div>
<div>docker push localhost:3000/myservice:latest</div>
</div>
</form>
</div>
</ui-card-content>
</ui-card>
<!-- Registries List -->
<!-- External Registries Section -->
<div class="flex items-center justify-between">
<div>
<h2 class="text-xl font-semibold">External Registries</h2>
<p class="text-sm text-muted-foreground">Add credentials for private Docker registries</p>
</div>
<button uiButton variant="outline" (click)="addDialogOpen.set(true)">
Add Registry
</button>
</div>
<ui-card>
<ui-card-header class="flex flex-col space-y-1.5">
<ui-card-title>Registered Registries</ui-card-title>
</ui-card-header>
<ui-card-content class="p-0">
@if (loading() && registries().length === 0) {
<div class="p-6 space-y-4">
@@ -104,15 +141,24 @@ import {
</div>
} @else if (registries().length === 0) {
<div class="p-12 text-center">
<p class="text-muted-foreground">No registries configured</p>
<svg class="h-12 w-12 mx-auto text-muted-foreground/50 mb-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1.5">
<path stroke-linecap="round" stroke-linejoin="round" d="M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4" />
</svg>
<h3 class="text-lg font-medium">No external registries</h3>
<p class="text-muted-foreground mt-1">
Add credentials for Docker Hub, GitHub Container Registry, or other private registries.
</p>
<button uiButton variant="outline" class="mt-4" (click)="addDialogOpen.set(true)">
Add External Registry
</button>
</div>
} @else {
<ui-table>
<ui-table-header>
<ui-table-row>
<ui-table-head>URL</ui-table-head>
<ui-table-head>Registry URL</ui-table-head>
<ui-table-head>Username</ui-table-head>
<ui-table-head>Created</ui-table-head>
<ui-table-head>Added</ui-table-head>
<ui-table-head class="text-right">Actions</ui-table-head>
</ui-table-row>
</ui-table-header>
@@ -136,6 +182,35 @@ import {
</ui-card>
</div>
<!-- Add Registry Dialog -->
<ui-dialog [open]="addDialogOpen()" (openChange)="addDialogOpen.set($event)">
<ui-dialog-header>
<ui-dialog-title>Add External Registry</ui-dialog-title>
<ui-dialog-description>
Add credentials for a private Docker registry
</ui-dialog-description>
</ui-dialog-header>
<div class="grid gap-4 py-4">
<div class="space-y-2">
<label uiLabel>Registry URL</label>
<input uiInput [(ngModel)]="form.url" placeholder="registry.example.com" />
</div>
<div class="space-y-2">
<label uiLabel>Username</label>
<input uiInput [(ngModel)]="form.username" />
</div>
<div class="space-y-2">
<label uiLabel>Password</label>
<input uiInput type="password" [(ngModel)]="form.password" />
</div>
</div>
<ui-dialog-footer>
<button uiButton variant="outline" (click)="addDialogOpen.set(false)">Cancel</button>
<button uiButton (click)="addRegistry()" [disabled]="loading()">Add Registry</button>
</ui-dialog-footer>
</ui-dialog>
<!-- Delete Confirmation Dialog -->
<ui-dialog [open]="deleteDialogOpen()" (openChange)="deleteDialogOpen.set($event)">
<ui-dialog-header>
<ui-dialog-title>Delete Registry</ui-dialog-title>
@@ -156,6 +231,7 @@ export class RegistriesComponent implements OnInit {
registries = signal<IRegistry[]>([]);
loading = signal(false);
addDialogOpen = signal(false);
deleteDialogOpen = signal(false);
registryToDelete = signal<IRegistry | null>(null);
@@ -191,6 +267,7 @@ export class RegistriesComponent implements OnInit {
if (response.success) {
this.toast.success('Registry added');
this.form = { url: '', username: '', password: '' };
this.addDialogOpen.set(false);
this.loadRegistries();
} else {
this.toast.error(response.error || 'Failed to add registry');