CFTunnels/frontend/src/app/features/dashboard/dashboard.component.ts
hitanshu310 523c9d941e Add Angular frontend with Authentik OIDC authentication
- Angular 17 with standalone components
- Angular Material + Tailwind CSS
- OIDC authorization code flow with Authentik
- Role-based access control (USER, DEVELOPER, APPROVER, ADMIN)
- Dashboard with pending requests, tunnel list, and create mapping
- Nginx reverse proxy to backend API
- Multi-container Docker Compose setup (frontend, backend, postgres)
- Environment-based configuration (local, test, prod)
2026-02-16 01:47:04 +05:30

168 lines
4.2 KiB
TypeScript

import { Component, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { MatToolbarModule } from '@angular/material/toolbar';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import { MatSidenavModule } from '@angular/material/sidenav';
import { MatListModule } from '@angular/material/list';
import { MatCardModule } from '@angular/material/card';
import { AuthService } from '../../core/auth/auth.service';
import { ApiService } from '../../core/services/api.service';
import { User, Request, Tunnel } from '../../shared/models';
import { PendingRequestsComponent } from './pending-requests/pending-requests.component';
import { TunnelListComponent } from './tunnel-list/tunnel-list.component';
import { CreateMappingComponent } from './create-mapping/create-mapping.component';
@Component({
selector: 'app-dashboard',
standalone: true,
imports: [
CommonModule,
MatToolbarModule,
MatButtonModule,
MatIconModule,
MatSidenavModule,
MatListModule,
MatCardModule,
PendingRequestsComponent,
TunnelListComponent,
CreateMappingComponent,
],
template: `
<mat-toolbar color="primary" class="toolbar">
<span class="title">
<mat-icon>hub</mat-icon>
CFTunnels
</span>
<span class="spacer"></span>
<span class="user-info" *ngIf="user">
{{ user.username }}
<span class="roles">({{ user.roles.join(', ') }})</span>
</span>
<button mat-icon-button (click)="logout()" title="Logout">
<mat-icon>logout</mat-icon>
</button>
</mat-toolbar>
<div class="dashboard-container">
<div class="dashboard-content">
<!-- Pending Requests Section - APPROVER/ADMIN only -->
@if (canApprove()) {
<app-pending-requests
[requests]="pendingRequests"
(requestUpdated)="loadRequests()"
></app-pending-requests>
}
<!-- Tunnel List Section - DEVELOPER+ -->
@if (canViewTunnels()) {
<app-tunnel-list [tunnels]="tunnels"></app-tunnel-list>
}
<!-- Create Mapping Section - USER+ -->
<app-create-mapping></app-create-mapping>
</div>
</div>
`,
styles: [
`
.toolbar {
position: sticky;
top: 0;
z-index: 1000;
}
.title {
display: flex;
align-items: center;
gap: 0.5rem;
font-weight: 500;
}
.spacer {
flex: 1 1 auto;
}
.user-info {
display: flex;
align-items: center;
gap: 0.5rem;
margin-right: 1rem;
font-size: 0.9rem;
}
.roles {
color: rgba(255, 255, 255, 0.7);
font-size: 0.8rem;
}
.dashboard-container {
display: flex;
justify-content: center;
padding: 1.5rem;
}
.dashboard-content {
width: 100%;
max-width: 900px;
}
`,
],
})
export class DashboardComponent implements OnInit {
user: User | null = null;
pendingRequests: Request[] = [];
tunnels: Tunnel[] = [];
constructor(
private authService: AuthService,
private apiService: ApiService
) {}
ngOnInit(): void {
this.authService.user$.subscribe((user) => {
this.user = user;
if (user) {
this.loadData();
}
});
}
loadData(): void {
this.loadRequests();
this.loadTunnels();
}
loadRequests(): void {
this.apiService.getRequests().subscribe({
next: (response) => {
this.pendingRequests = response.data.filter(
(r) => r.status === 'PENDING'
);
},
error: (err) => console.error('Error loading requests:', err),
});
}
loadTunnels(): void {
this.apiService.getConfiguredTunnels().subscribe({
next: (response) => {
this.tunnels = response.data;
},
error: (err) => console.error('Error loading tunnels:', err),
});
}
canApprove(): boolean {
return this.authService.hasAnyRole(['APPROVER', 'ADMIN']);
}
canViewTunnels(): boolean {
return this.authService.hasAnyRole(['DEVELOPER', 'APPROVER', 'ADMIN']);
}
logout(): void {
this.authService.logout();
}
}