Angular + Pilvio API autentimine
Selles juhendis loome Angular rakenduse, mis suhtleb Pilvio API-ga HTTP interceptori abil, et hallata infrastruktuuri turvaliselt.
Mida ehitame
- Angular 17+ standalone komponentidega
- HTTP interceptor, mis lisab autentimise päised
- Pilvio VM-ide ja StorageVault bucketi haldamise teenus
Eeldused
- Pilvio konto ja API token (vaata ülevaadet)
- Node.js 20+ ja Angular CLI (
npm install -g @angular/cli)
1. samm: Projekti loomine
ng new pilvio-angular-app --standalone --style=css --routing
cd pilvio-angular-app
2. samm: Keskkonna seadistamine
Loo fail src/environments/environment.ts:
export const environment = {
production: false,
// Backend proxy URL — mitte kunagi otse Pilvio API URL frontendis!
apiUrl: 'http://localhost:3001/pilvio',
sessionSecret: 'sinu-arendus-saladus',
};
Loo fail src/environments/environment.prod.ts:
export const environment = {
production: true,
apiUrl: '/pilvio',
sessionSecret: '', // Tootmises hallatakse serveripoolselt
};
3. samm: Auth interceptor
Loo fail src/app/interceptors/auth.interceptor.ts:
import { HttpInterceptorFn } from '@angular/common/http';
import { environment } from '../../environments/environment';
export const authInterceptor: HttpInterceptorFn = (req, next) => {
// Lisa autentimise päis ainult Pilvio API päringutele
if (req.url.startsWith(environment.apiUrl)) {
const cloned = req.clone({
setHeaders: {
'Authorization': `Bearer ${environment.sessionSecret}`,
},
});
return next(cloned);
}
return next(req);
};
Registeeri interceptor src/app/app.config.ts failis:
import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
import { provideRouter } from '@angular/router';
import { provideHttpClient, withInterceptors } from '@angular/common/http';
import { routes } from './app.routes';
import { authInterceptor } from './interceptors/auth.interceptor';
export const appConfig: ApplicationConfig = {
providers: [
provideZoneChangeDetection({ eventCoalescing: true }),
provideRouter(routes),
provideHttpClient(withInterceptors([authInterceptor])),
],
};
4. samm: Pilvio teenus
Loo fail src/app/services/pilvio.service.ts:
import { Injectable } from '@angular/core';
import { HttpClient, HttpParams } from '@angular/common/http';
import { Observable } from 'rxjs';
import { environment } from '../../environments/environment';
export interface PilvioVM {
uuid: string;
name: string;
status: string;
os_name: string;
os_version: string;
vcpu: number;
memory: number;
private_ipv4: string;
created_at: string;
}
export interface PilvioBucket {
name: string;
size_bytes: number;
num_objects: number;
billing_account_id: number;
}
export interface PilvioLocation {
slug: string;
display_name: string;
is_default: boolean;
country_code: string;
}
@Injectable({ providedIn: 'root' })
export class PilvioService {
private baseUrl = environment.apiUrl;
constructor(private http: HttpClient) {}
// VM-id
listVMs(): Observable<PilvioVM[]> {
return this.http.get<PilvioVM[]>(`${this.baseUrl}/user-resource/vm/list`);
}
getVM(uuid: string): Observable<PilvioVM> {
return this.http.get<PilvioVM>(`${this.baseUrl}/user-resource/vm`, {
params: new HttpParams().set('uuid', uuid),
});
}
startVM(uuid: string): Observable<any> {
return this.http.post(`${this.baseUrl}/user-resource/vm/start`, `uuid=${uuid}`, {
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
});
}
stopVM(uuid: string): Observable<any> {
return this.http.post(`${this.baseUrl}/user-resource/vm/stop`, `uuid=${uuid}`, {
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
});
}
// StorageVault (S3 bucketid)
listBuckets(): Observable<PilvioBucket[]> {
return this.http.get<PilvioBucket[]>(`${this.baseUrl}/storage/bucket/list`);
}
// Asukohad
listLocations(): Observable<PilvioLocation[]> {
return this.http.get<PilvioLocation[]>(`${this.baseUrl}/config/locations`);
}
}
5. samm: Infrastruktuuri dashboard
Loo fail src/app/components/infra-dashboard.component.ts:
import { Component, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { PilvioService, PilvioVM, PilvioBucket } from '../services/pilvio.service';
@Component({
selector: 'app-infra-dashboard',
standalone: true,
imports: [CommonModule],
template: `
<div class="dashboard">
<h1>Pilvio infrastruktuuri haldus</h1>
<div *ngIf="error" class="error">{{ error }}</div>
<!-- VM-id -->
<section>
<h2>Virtuaalmasinad ({{ vms.length }})</h2>
<button (click)="loadVMs()" [disabled]="loadingVMs">Värskenda</button>
<table *ngIf="vms.length > 0">
<thead>
<tr>
<th>Nimi</th>
<th>Staatus</th>
<th>OS</th>
<th>Ressursid</th>
<th>IP</th>
<th>Tegevused</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let vm of vms">
<td>{{ vm.name }}</td>
<td [class]="'status-' + vm.status">{{ vm.status }}</td>
<td>{{ vm.os_name }} {{ vm.os_version }}</td>
<td>{{ vm.vcpu }} vCPU / {{ formatMemory(vm.memory) }}</td>
<td><code>{{ vm.private_ipv4 }}</code></td>
<td>
<button *ngIf="vm.status === 'stopped'" (click)="startVM(vm.uuid)" class="btn-start">
Käivita
</button>
<button *ngIf="vm.status === 'running'" (click)="stopVM(vm.uuid)" class="btn-stop">
Peata
</button>
</td>
</tr>
</tbody>
</table>
</section>
<!-- StorageVault bucketid -->
<section>
<h2>StorageVault bucketid ({{ buckets.length }})</h2>
<table *ngIf="buckets.length > 0">
<thead>
<tr>
<th>Nimi</th>
<th>Objektide arv</th>
<th>Maht</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let b of buckets">
<td>{{ b.name }}</td>
<td>{{ b.num_objects }}</td>
<td>{{ formatBytes(b.size_bytes) }}</td>
</tr>
</tbody>
</table>
</section>
</div>
`,
styles: [`
.dashboard { max-width: 1000px; margin: 0 auto; padding: 20px; }
.error { color: #ef4444; padding: 10px; background: #fef2f2; border-radius: 4px; margin-bottom: 16px; }
table { width: 100%; border-collapse: collapse; margin-top: 12px; }
th, td { padding: 10px; text-align: left; border-bottom: 1px solid #e5e7eb; }
th { font-weight: 600; background: #f9fafb; }
code { background: #f3f4f6; padding: 2px 6px; border-radius: 3px; font-size: 0.9em; }
section { margin-bottom: 32px; }
.status-running { color: #16a34a; font-weight: 600; }
.status-stopped { color: #dc2626; font-weight: 600; }
.btn-start { background: #22c55e; color: white; border: none; padding: 4px 12px; border-radius: 4px; cursor: pointer; }
.btn-stop { background: #ef4444; color: white; border: none; padding: 4px 12px; border-radius: 4px; cursor: pointer; }
`],
})
export class InfraDashboardComponent implements OnInit {
vms: PilvioVM[] = [];
buckets: PilvioBucket[] = [];
loadingVMs = false;
error: string | null = null;
constructor(private pilvio: PilvioService) {}
ngOnInit() {
this.loadVMs();
this.loadBuckets();
}
loadVMs() {
this.loadingVMs = true;
this.pilvio.listVMs().subscribe({
next: (data) => { this.vms = data; this.loadingVMs = false; },
error: () => { this.error = 'VM-ide laadimine ebaõnnestus'; this.loadingVMs = false; },
});
}
loadBuckets() {
this.pilvio.listBuckets().subscribe({
next: (data) => this.buckets = data,
error: () => this.error = 'Bucketi laadimine ebaõnnestus',
});
}
startVM(uuid: string) {
this.pilvio.startVM(uuid).subscribe({ next: () => this.loadVMs() });
}
stopVM(uuid: string) {
this.pilvio.stopVM(uuid).subscribe({ next: () => this.loadVMs() });
}
formatMemory(mb: number): string {
return mb >= 1024 ? `${(mb / 1024).toFixed(1)} GB` : `${mb} MB`;
}
formatBytes(bytes: number): string {
if (bytes < 1024) return `${bytes} B`;
if (bytes < 1048576) return `${(bytes / 1024).toFixed(1)} KB`;
if (bytes < 1073741824) return `${(bytes / 1048576).toFixed(1)} MB`;
return `${(bytes / 1073741824).toFixed(1)} GB`;
}
}
6. samm: Marsruutimine
Uuenda fail src/app/app.routes.ts:
import { Routes } from '@angular/router';
import { InfraDashboardComponent } from './components/infra-dashboard.component';
export const routes: Routes = [
{ path: '', component: InfraDashboardComponent },
];
7. samm: Deploy
Backend proxy seadistus on identne Vue.js juhendiga.
ng build --configuration production
scp -r dist/pilvio-angular-app/browser/* deploy@SINU_FLOATING_IP:/var/www/angular-app/
Järgmised sammud: Kasuta StorageVault't failide haldamiseks või lisa andmebaas VM-ile.