add infrastructure resource YAML viewer and fix PVC visibility
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/push/woodpecker Pipeline was successful
This commit is contained in:
@@ -195,6 +195,15 @@ export class AppsController {
|
||||
return this.appsService.listRuntimeResources(namespace);
|
||||
}
|
||||
|
||||
@Get('runtime-namespaces/:namespace/resources/:kind/:name/yaml')
|
||||
getRuntimeResourceYaml(
|
||||
@Param('namespace') namespace: string,
|
||||
@Param('kind') kind: string,
|
||||
@Param('name') name: string,
|
||||
) {
|
||||
return this.appsService.getRuntimeResourceYaml(namespace, kind, name);
|
||||
}
|
||||
|
||||
@Post('runtime-namespaces')
|
||||
ensureRuntimeNamespace(@Body() body: EnsureRuntimeNamespaceDto) {
|
||||
return this.appsService.ensureRuntimeNamespace(body);
|
||||
|
||||
@@ -1506,7 +1506,7 @@ export class AppsService {
|
||||
{ kind: 'StatefulSet', command: 'statefulsets' },
|
||||
{ kind: 'Service', command: 'services' },
|
||||
{ kind: 'Ingress', command: 'ingress' },
|
||||
{ kind: 'PVC', command: 'pvc' },
|
||||
{ kind: 'PersistentVolumeClaim', command: 'pvc' },
|
||||
];
|
||||
const resources = [];
|
||||
|
||||
@@ -1530,12 +1530,32 @@ export class AppsService {
|
||||
deployments: resources.filter((resource) => resource.kind === 'Deployment').length,
|
||||
statefulSets: resources.filter((resource) => resource.kind === 'StatefulSet').length,
|
||||
services: resources.filter((resource) => resource.kind === 'Service').length,
|
||||
persistentVolumeClaims: resources.filter((resource) => resource.kind === 'PVC').length,
|
||||
persistentVolumeClaims: resources.filter((resource) => resource.kind === 'PersistentVolumeClaim').length,
|
||||
},
|
||||
resources,
|
||||
};
|
||||
}
|
||||
|
||||
async getRuntimeResourceYaml(namespace: string, kind: string, name: string) {
|
||||
const normalizedNamespace = normalizeNamespace(namespace);
|
||||
const resourceType = getKubectlResourceType(kind);
|
||||
if (!resourceType) {
|
||||
throw new BadRequestException('YAML 조회를 지원하지 않는 리소스 유형입니다.');
|
||||
}
|
||||
|
||||
const yaml = await this.kubectl([
|
||||
'get',
|
||||
resourceType,
|
||||
name,
|
||||
'-n',
|
||||
normalizedNamespace,
|
||||
'-o',
|
||||
'yaml',
|
||||
]);
|
||||
|
||||
return { namespace: normalizedNamespace, kind, name, yaml };
|
||||
}
|
||||
|
||||
async ensureRuntimeNamespace(body: EnsureRuntimeNamespaceDto) {
|
||||
const namespace = normalizeNamespace(body.namespace);
|
||||
const logs: string[] = [];
|
||||
@@ -2754,6 +2774,26 @@ function getNumber(value: unknown, fallback = 0) {
|
||||
return typeof value === 'number' ? value : fallback;
|
||||
}
|
||||
|
||||
function getKubectlResourceType(kind: string) {
|
||||
const resourceTypes: Record<string, string> = {
|
||||
Deployment: 'deployment',
|
||||
StatefulSet: 'statefulset',
|
||||
DaemonSet: 'daemonset',
|
||||
Pod: 'pod',
|
||||
Job: 'job',
|
||||
CronJob: 'cronjob',
|
||||
ReplicaSet: 'replicaset',
|
||||
Service: 'service',
|
||||
Ingress: 'ingress',
|
||||
PersistentVolumeClaim: 'pvc',
|
||||
PVC: 'pvc',
|
||||
Endpoint: 'endpoints',
|
||||
Endpoints: 'endpoints',
|
||||
};
|
||||
|
||||
return resourceTypes[kind];
|
||||
}
|
||||
|
||||
function getAge(createdAt: string) {
|
||||
const created = new Date(createdAt).getTime();
|
||||
if (!created || Number.isNaN(created)) return '-';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Body, Controller, Delete, Get, Param, Patch, Post, Put, Query } from '@nestjs/common';
|
||||
import { Body, Controller, Delete, Get, Param, Patch, Post, Put, Query, Req } from '@nestjs/common';
|
||||
|
||||
import { AssignLicenseDto, ListUserLicensesQuery, UpdateLicenseAppPermissionsDto, UpdateLicensePlanDto } from './dto';
|
||||
import { LicensesService } from './licenses.service';
|
||||
@@ -38,13 +38,21 @@ export class LicensesController {
|
||||
}
|
||||
|
||||
@Post('users/:userId')
|
||||
assign(@Param('userId') userId: string, @Body() body: AssignLicenseDto) {
|
||||
return this.licensesService.assign(userId, body);
|
||||
assign(
|
||||
@Param('userId') userId: string,
|
||||
@Body() body: AssignLicenseDto,
|
||||
@Req() request: { admin?: { id: string } },
|
||||
) {
|
||||
return this.licensesService.assign(userId, body, request.admin?.id);
|
||||
}
|
||||
|
||||
@Patch('user-licenses/:id')
|
||||
update(@Param('id') id: string, @Body() body: AssignLicenseDto) {
|
||||
return this.licensesService.update(id, body);
|
||||
update(
|
||||
@Param('id') id: string,
|
||||
@Body() body: AssignLicenseDto,
|
||||
@Req() request: { admin?: { id: string } },
|
||||
) {
|
||||
return this.licensesService.update(id, body, request.admin?.id);
|
||||
}
|
||||
|
||||
@Delete('user-licenses/:id')
|
||||
|
||||
@@ -2,11 +2,12 @@ import { Module } from '@nestjs/common';
|
||||
|
||||
import { AuthPrismaModule } from '../auth-prisma/auth-prisma.module';
|
||||
import { LauncherPrismaModule } from '../launcher-prisma/launcher-prisma.module';
|
||||
import { PrismaModule } from '../prisma/prisma.module';
|
||||
import { LicensesController } from './licenses.controller';
|
||||
import { LicensesService } from './licenses.service';
|
||||
|
||||
@Module({
|
||||
imports: [AuthPrismaModule, LauncherPrismaModule],
|
||||
imports: [AuthPrismaModule, LauncherPrismaModule, PrismaModule],
|
||||
controllers: [LicensesController],
|
||||
providers: [LicensesService],
|
||||
})
|
||||
|
||||
@@ -2,6 +2,7 @@ import { BadRequestException, ConflictException, Injectable, NotFoundException }
|
||||
|
||||
import { AuthPrismaService } from '../auth-prisma/auth-prisma.service';
|
||||
import { LauncherPrismaService } from '../launcher-prisma/launcher-prisma.service';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import { AssignLicenseDto, ListUserLicensesQuery, UpdateLicenseAppPermissionsDto, UpdateLicensePlanDto } from './dto';
|
||||
|
||||
type LauncherAppPermissionRow = {
|
||||
@@ -18,6 +19,7 @@ export class LicensesService {
|
||||
constructor(
|
||||
private readonly authPrisma: AuthPrismaService,
|
||||
private readonly launcherPrisma: LauncherPrismaService,
|
||||
private readonly prisma: PrismaService,
|
||||
) {}
|
||||
|
||||
async listPlans() {
|
||||
@@ -141,6 +143,15 @@ export class LicensesService {
|
||||
this.authPrisma.userLicense.count({ where }),
|
||||
]);
|
||||
|
||||
const assignedByIds = [...new Set(items.flatMap((item) => item.assignedById ? [item.assignedById] : []))];
|
||||
const admins = assignedByIds.length
|
||||
? await this.prisma.admin.findMany({
|
||||
where: { id: { in: assignedByIds } },
|
||||
select: { id: true, name: true },
|
||||
})
|
||||
: [];
|
||||
const assignedByName = new Map(admins.map((admin) => [admin.id, admin.name]));
|
||||
|
||||
return {
|
||||
assignments: items.map((userLicense, index) => ({
|
||||
id: userLicense.id,
|
||||
@@ -153,7 +164,9 @@ export class LicensesService {
|
||||
licenseName: userLicense.license.name,
|
||||
startsAt: userLicense.startsAt,
|
||||
expiresAt: userLicense.expiresAt,
|
||||
assignedBy: userLicense.assignedById,
|
||||
assignedBy: userLicense.assignedById
|
||||
? assignedByName.get(userLicense.assignedById) ?? '-'
|
||||
: '-',
|
||||
status: isActiveLicense(userLicense.startsAt, userLicense.expiresAt, now) ? '활성' : '만료',
|
||||
})),
|
||||
total,
|
||||
@@ -279,7 +292,7 @@ export class LicensesService {
|
||||
return userIds.length;
|
||||
}
|
||||
|
||||
async assign(userId: string, body: AssignLicenseDto) {
|
||||
async assign(userId: string, body: AssignLicenseDto, assignedById?: string) {
|
||||
const user = await this.authPrisma.user.findUnique({
|
||||
where: { id: userId },
|
||||
select: { id: true },
|
||||
@@ -321,7 +334,7 @@ export class LicensesService {
|
||||
licenseId: license.id,
|
||||
startsAt,
|
||||
expiresAt,
|
||||
assignedById: body.assignedById,
|
||||
assignedById,
|
||||
},
|
||||
include: { license: true, user: true },
|
||||
});
|
||||
@@ -331,7 +344,7 @@ export class LicensesService {
|
||||
return { userLicense };
|
||||
}
|
||||
|
||||
async update(id: string, body: AssignLicenseDto) {
|
||||
async update(id: string, body: AssignLicenseDto, assignedById?: string) {
|
||||
const current = await this.authPrisma.userLicense.findUnique({
|
||||
where: { id },
|
||||
select: { id: true },
|
||||
@@ -360,7 +373,7 @@ export class LicensesService {
|
||||
licenseId: license.id,
|
||||
startsAt,
|
||||
expiresAt,
|
||||
assignedById: body.assignedById,
|
||||
assignedById,
|
||||
},
|
||||
include: { license: true, user: true },
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user