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:
@@ -4,7 +4,7 @@ import { FormsModule } from '@angular/forms';
|
||||
import { ApiService } from '../../core/services/api.service';
|
||||
import { ToastService } from '../../core/services/toast.service';
|
||||
import { LogStreamService } from '../../core/services/log-stream.service';
|
||||
import { IService, IServiceUpdate } from '../../core/types/api.types';
|
||||
import { IService, IServiceUpdate, IPlatformResource } from '../../core/types/api.types';
|
||||
import {
|
||||
CardComponent,
|
||||
CardHeaderComponent,
|
||||
@@ -209,6 +209,61 @@ import {
|
||||
</ui-card>
|
||||
}
|
||||
|
||||
<!-- Platform Resources -->
|
||||
@if (service()!.platformRequirements || platformResources().length > 0) {
|
||||
<ui-card>
|
||||
<ui-card-header class="flex flex-col space-y-1.5">
|
||||
<ui-card-title>Platform Resources</ui-card-title>
|
||||
<ui-card-description>Managed infrastructure provisioned for this service</ui-card-description>
|
||||
</ui-card-header>
|
||||
<ui-card-content class="space-y-4">
|
||||
@if (platformResources().length > 0) {
|
||||
@for (resource of platformResources(); track resource.id) {
|
||||
<div class="border rounded-lg p-4 space-y-2">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-2">
|
||||
@if (resource.resourceType === 'database') {
|
||||
<svg class="h-5 w-5 text-green-500" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7M4 7c0 2.21 3.582 4 8 4s8-1.79 8-4M4 7c0-2.21 3.582-4 8-4s8 1.79 8 4m0 5c0 2.21-3.582 4-8 4s-8-1.79-8-4" />
|
||||
</svg>
|
||||
} @else if (resource.resourceType === 'bucket') {
|
||||
<svg class="h-5 w-5 text-blue-500" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M5 8h14M5 8a2 2 0 110-4h14a2 2 0 110 4M5 8v10a2 2 0 002 2h10a2 2 0 002-2V8m-9 4h4" />
|
||||
</svg>
|
||||
}
|
||||
<span class="font-medium">{{ resource.resourceName }}</span>
|
||||
</div>
|
||||
<ui-badge [variant]="resource.platformService.status === 'running' ? 'success' : 'secondary'">
|
||||
{{ resource.platformService.status }}
|
||||
</ui-badge>
|
||||
</div>
|
||||
<div class="text-sm text-muted-foreground">
|
||||
{{ resource.platformService.type === 'mongodb' ? 'MongoDB Database' : 'S3 Bucket (MinIO)' }}
|
||||
</div>
|
||||
<div class="mt-2 pt-2 border-t">
|
||||
<p class="text-xs font-medium text-muted-foreground mb-1">Injected Environment Variables</p>
|
||||
<div class="flex flex-wrap gap-1">
|
||||
@for (key of getEnvKeys(resource.envVars); track key) {
|
||||
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-mono bg-muted">{{ key }}</span>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
} @else if (service()!.platformRequirements) {
|
||||
<div class="text-sm text-muted-foreground">
|
||||
@if (service()!.platformRequirements!.mongodb) {
|
||||
<p>MongoDB database pending provisioning...</p>
|
||||
}
|
||||
@if (service()!.platformRequirements!.s3) {
|
||||
<p>S3 bucket pending provisioning...</p>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</ui-card-content>
|
||||
</ui-card>
|
||||
}
|
||||
|
||||
<!-- Onebox Registry Info -->
|
||||
@if (service()!.useOneboxRegistry) {
|
||||
<ui-card>
|
||||
@@ -225,21 +280,11 @@ import {
|
||||
<dt class="text-sm font-medium text-muted-foreground">Tag</dt>
|
||||
<dd class="text-sm">{{ service()!.registryImageTag || 'latest' }}</dd>
|
||||
</div>
|
||||
@if (service()!.registryToken) {
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-muted-foreground">Push Token</dt>
|
||||
<dd class="flex items-center gap-2">
|
||||
<input
|
||||
uiInput
|
||||
type="password"
|
||||
[value]="service()!.registryToken"
|
||||
readonly
|
||||
class="font-mono text-xs"
|
||||
/>
|
||||
<button uiButton variant="outline" size="sm" (click)="copyToken()">Copy</button>
|
||||
</dd>
|
||||
</div>
|
||||
}
|
||||
<div class="pt-2 border-t">
|
||||
<a routerLink="/tokens" class="text-sm text-primary hover:underline">
|
||||
Manage registry tokens for CI/CD pipelines →
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="text-sm font-medium text-muted-foreground">Auto-update on push</dt>
|
||||
<dd class="text-sm">{{ service()!.autoUpdateOnPush ? 'Enabled' : 'Disabled' }}</dd>
|
||||
@@ -346,6 +391,7 @@ export class ServiceDetailComponent implements OnInit, OnDestroy {
|
||||
@ViewChild('logContainer') logContainer!: ElementRef<HTMLDivElement>;
|
||||
|
||||
service = signal<IService | null>(null);
|
||||
platformResources = signal<IPlatformResource[]>([]);
|
||||
loading = signal(false);
|
||||
actionLoading = signal(false);
|
||||
editMode = signal(false);
|
||||
@@ -389,6 +435,11 @@ export class ServiceDetailComponent implements OnInit, OnDestroy {
|
||||
port: response.data.port,
|
||||
domain: response.data.domain,
|
||||
};
|
||||
|
||||
// Load platform resources if service has platform requirements
|
||||
if (response.data.platformRequirements) {
|
||||
this.loadPlatformResources(name);
|
||||
}
|
||||
} else {
|
||||
this.toast.error(response.error || 'Service not found');
|
||||
this.router.navigate(['/services']);
|
||||
@@ -400,6 +451,17 @@ export class ServiceDetailComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
async loadPlatformResources(name: string): Promise<void> {
|
||||
try {
|
||||
const response = await this.api.getServicePlatformResources(name);
|
||||
if (response.success && response.data) {
|
||||
this.platformResources.set(response.data);
|
||||
}
|
||||
} catch {
|
||||
// Silent fail - platform resources are optional
|
||||
}
|
||||
}
|
||||
|
||||
startLogStream(): void {
|
||||
const name = this.service()?.name;
|
||||
if (name) {
|
||||
@@ -546,12 +608,4 @@ export class ServiceDetailComponent implements OnInit, OnDestroy {
|
||||
this.deleteDialogOpen.set(false);
|
||||
}
|
||||
}
|
||||
|
||||
copyToken(): void {
|
||||
const token = this.service()?.registryToken;
|
||||
if (token) {
|
||||
navigator.clipboard.writeText(token);
|
||||
this.toast.success('Token copied to clipboard');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user