add : icons module
This commit is contained in:
@@ -28,6 +28,7 @@ import { DanakServicesModule } from "./modules/danak-services/danak-services.mod
|
||||
import { DashboardModule } from "./modules/dashboards/dashboard.module";
|
||||
import { DiscountsModule } from "./modules/discounts/discounts.module";
|
||||
import { InvoicesModule } from "./modules/invoices/invoices.module";
|
||||
import { IconsModule } from "./modules/icons/icons.module";
|
||||
import { LandingModule } from "./modules/landing/landing.module";
|
||||
import { LearningModule } from "./modules/learnings/learning.module";
|
||||
import { LoggerModule } from "./modules/logger/logger.module";
|
||||
@@ -85,6 +86,7 @@ import { WalletsModule } from "./modules/wallets/wallets.module";
|
||||
DiscountsModule,
|
||||
SupportPlansModule,
|
||||
AccessLogsModule,
|
||||
IconsModule,
|
||||
],
|
||||
controllers: [],
|
||||
})
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import { ConfigService } from "@nestjs/config";
|
||||
|
||||
export function iconsConfig() {
|
||||
return {
|
||||
inject: [ConfigService],
|
||||
useFactory: async (configService: ConfigService) => {
|
||||
return {
|
||||
baseUrl: configService.getOrThrow<string>("DMENU_BACKEND_URL"),
|
||||
username: configService.getOrThrow<string>("DMENU_USERNAME"),
|
||||
password: configService.getOrThrow<string>("DMENU_PASSWORD"),
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export interface IIconsConfig {
|
||||
baseUrl: string;
|
||||
username: string;
|
||||
password: string;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { ApiProperty } from "@nestjs/swagger";
|
||||
import { IsNotEmpty, IsOptional, IsString } from "class-validator";
|
||||
|
||||
export class CreateGroupDto {
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
@ApiProperty({ description: "Group name", example: "Navigation Icons" })
|
||||
name: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@ApiProperty({ description: "Group description", example: "Icons used for navigation", required: false })
|
||||
description?: string;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import { ApiProperty } from "@nestjs/swagger";
|
||||
import { IsNotEmpty, IsOptional, IsString, IsUrl } from "class-validator";
|
||||
|
||||
export class CreateIconDto {
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
@ApiProperty({ description: "Icon name", example: "home-icon" })
|
||||
name: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@ApiProperty({ description: "Icon description", example: "Home icon", required: false })
|
||||
description?: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsUrl({ protocols: ["http", "https"], require_protocol: true })
|
||||
@ApiProperty({ description: "Icon URL", example: "https://example.com/icons/home.svg" })
|
||||
url: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@ApiProperty({ description: "Icon group ID", example: "group-123", required: false })
|
||||
groupId?: string;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
import { PartialType } from "@nestjs/swagger";
|
||||
|
||||
import { CreateGroupDto } from "./create-group.dto";
|
||||
|
||||
export class UpdateGroupDto extends PartialType(CreateGroupDto) {}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
import { PartialType } from "@nestjs/swagger";
|
||||
|
||||
import { CreateIconDto } from "./create-icon.dto";
|
||||
|
||||
export class UpdateIconDto extends PartialType(CreateIconDto) {}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
export const ICONS_CONFIG = "ICONS_CONFIG";
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
import { Body, Controller, Delete, Get, Param, Patch, Post } from "@nestjs/common";
|
||||
import { ApiBearerAuth, ApiBody, ApiOperation, ApiParam, ApiTags } from "@nestjs/swagger";
|
||||
|
||||
import { CreateGroupDto } from "./DTO/create-group.dto";
|
||||
import { CreateIconDto } from "./DTO/create-icon.dto";
|
||||
import { UpdateGroupDto } from "./DTO/update-group.dto";
|
||||
import { UpdateIconDto } from "./DTO/update-icon.dto";
|
||||
import { IconsService } from "./providers/icons.service";
|
||||
import { AuthGuards } from "../../common/decorators/auth-guard.decorator";
|
||||
import { PermissionsDec } from "../../common/decorators/permission.decorator";
|
||||
import { PermissionEnum } from "../users/enums/permission.enum";
|
||||
|
||||
@ApiTags("icons")
|
||||
@Controller("admin/icons")
|
||||
@AuthGuards()
|
||||
@PermissionsDec(PermissionEnum.ICONS)
|
||||
@ApiBearerAuth()
|
||||
export class IconsController {
|
||||
constructor(private readonly iconsService: IconsService) {}
|
||||
|
||||
// Icon endpoints
|
||||
@Post()
|
||||
@ApiOperation({ summary: "Create icon" })
|
||||
@ApiBody({ type: CreateIconDto })
|
||||
createIcon(@Body() dto: CreateIconDto) {
|
||||
return this.iconsService.createIcon(dto);
|
||||
}
|
||||
|
||||
@Get()
|
||||
@ApiOperation({ summary: "Get all icons" })
|
||||
findAllIcons() {
|
||||
return this.iconsService.findAllIcons();
|
||||
}
|
||||
|
||||
@Get(":id")
|
||||
@ApiOperation({ summary: "Get icon by id" })
|
||||
@ApiParam({ name: "id", required: true, type: String })
|
||||
findOneIcon(@Param("id") id: string) {
|
||||
return this.iconsService.findOneIcon(id);
|
||||
}
|
||||
|
||||
@Patch(":id")
|
||||
@ApiOperation({ summary: "Update icon" })
|
||||
@ApiParam({ name: "id" })
|
||||
@ApiBody({ type: UpdateIconDto })
|
||||
updateIcon(@Param("id") id: string, @Body() dto: UpdateIconDto) {
|
||||
return this.iconsService.updateIcon(id, dto);
|
||||
}
|
||||
|
||||
@Delete(":id")
|
||||
@ApiOperation({ summary: "Delete icon" })
|
||||
@ApiParam({ name: "id" })
|
||||
removeIcon(@Param("id") id: string) {
|
||||
return this.iconsService.removeIcon(id);
|
||||
}
|
||||
|
||||
// Group endpoints (must come before :id routes to avoid route conflicts)
|
||||
@Post("groups")
|
||||
@ApiOperation({ summary: "Create icon group" })
|
||||
@ApiBody({ type: CreateGroupDto })
|
||||
createGroup(@Body() dto: CreateGroupDto) {
|
||||
return this.iconsService.createGroup(dto);
|
||||
}
|
||||
|
||||
@Get("groups")
|
||||
@ApiOperation({ summary: "Get all icon groups" })
|
||||
findAllGroups() {
|
||||
return this.iconsService.findAllGroups();
|
||||
}
|
||||
|
||||
@Get("groups/:id")
|
||||
@ApiOperation({ summary: "Get icon group by id" })
|
||||
@ApiParam({ name: "id", required: true, type: String })
|
||||
findOneGroup(@Param("id") id: string) {
|
||||
return this.iconsService.findOneGroup(id);
|
||||
}
|
||||
|
||||
@Patch("groups/:id")
|
||||
@ApiOperation({ summary: "Update icon group" })
|
||||
@ApiParam({ name: "id" })
|
||||
@ApiBody({ type: UpdateGroupDto })
|
||||
updateGroup(@Param("id") id: string, @Body() dto: UpdateGroupDto) {
|
||||
return this.iconsService.updateGroup(id, dto);
|
||||
}
|
||||
|
||||
@Delete("groups/:id")
|
||||
@ApiOperation({ summary: "Delete icon group" })
|
||||
@ApiParam({ name: "id" })
|
||||
removeGroup(@Param("id") id: string) {
|
||||
return this.iconsService.removeGroup(id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import { Module } from "@nestjs/common";
|
||||
|
||||
import { iconsConfig, IIconsConfig } from "../../configs/icons.config";
|
||||
import { IconsController } from "./icons.controller";
|
||||
import { ICONS_CONFIG } from "./constants";
|
||||
import { IconsService } from "./providers/icons.service";
|
||||
|
||||
@Module({
|
||||
imports: [],
|
||||
controllers: [IconsController],
|
||||
providers: [
|
||||
IconsService,
|
||||
{
|
||||
provide: ICONS_CONFIG,
|
||||
useFactory: iconsConfig().useFactory,
|
||||
inject: iconsConfig().inject,
|
||||
},
|
||||
],
|
||||
exports: [IconsService],
|
||||
})
|
||||
export class IconsModule {}
|
||||
|
||||
@@ -0,0 +1,201 @@
|
||||
import { HttpService } from "@nestjs/axios";
|
||||
import { Inject, Injectable, Logger } from "@nestjs/common";
|
||||
import { AxiosError } from "axios";
|
||||
import { catchError, firstValueFrom, throwError } from "rxjs";
|
||||
|
||||
import { IIconsConfig } from "../../../configs/icons.config";
|
||||
import { ICONS_CONFIG } from "../constants";
|
||||
import { CreateGroupDto } from "../DTO/create-group.dto";
|
||||
import { CreateIconDto } from "../DTO/create-icon.dto";
|
||||
import { UpdateGroupDto } from "../DTO/update-group.dto";
|
||||
import { UpdateIconDto } from "../DTO/update-icon.dto";
|
||||
|
||||
@Injectable()
|
||||
export class IconsService {
|
||||
private readonly logger = new Logger(IconsService.name);
|
||||
|
||||
constructor(
|
||||
@Inject(ICONS_CONFIG) private readonly config: IIconsConfig,
|
||||
private readonly httpService: HttpService,
|
||||
) {}
|
||||
|
||||
private getHeaders(): Record<string, string> {
|
||||
const credentials = Buffer.from(`${this.config.username}:${this.config.password}`).toString("base64");
|
||||
return {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Basic ${credentials}`,
|
||||
};
|
||||
}
|
||||
|
||||
// Icon methods
|
||||
async createIcon(dto: CreateIconDto) {
|
||||
try {
|
||||
const { data } = await firstValueFrom(
|
||||
this.httpService.post(`${this.config.baseUrl}/admin/icons`, dto, { headers: this.getHeaders() }).pipe(
|
||||
catchError((err: AxiosError) => {
|
||||
this.logger.error(`Failed to create icon: ${err.message}`, err.stack);
|
||||
return throwError(() => err);
|
||||
}),
|
||||
),
|
||||
);
|
||||
return data;
|
||||
} catch (error: unknown) {
|
||||
this.logger.error(`Error creating icon: ${error instanceof Error ? error.message : "Unknown error"}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async findAllIcons() {
|
||||
try {
|
||||
const { data } = await firstValueFrom(
|
||||
this.httpService.get(`${this.config.baseUrl}/admin/icons`, { headers: this.getHeaders() }).pipe(
|
||||
catchError((err: AxiosError) => {
|
||||
this.logger.error(`Failed to fetch icons: ${err.message}`, err.stack);
|
||||
return throwError(() => err);
|
||||
}),
|
||||
),
|
||||
);
|
||||
return data;
|
||||
} catch (error: unknown) {
|
||||
this.logger.error(`Error fetching icons: ${error instanceof Error ? error.message : "Unknown error"}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async findOneIcon(id: string) {
|
||||
try {
|
||||
const { data } = await firstValueFrom(
|
||||
this.httpService.get(`${this.config.baseUrl}/admin/icons/${id}`, { headers: this.getHeaders() }).pipe(
|
||||
catchError((err: AxiosError) => {
|
||||
this.logger.error(`Failed to fetch icon: ${err.message}`, err.stack);
|
||||
return throwError(() => err);
|
||||
}),
|
||||
),
|
||||
);
|
||||
return data;
|
||||
} catch (error: unknown) {
|
||||
this.logger.error(`Error fetching icon: ${error instanceof Error ? error.message : "Unknown error"}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async updateIcon(id: string, dto: UpdateIconDto) {
|
||||
try {
|
||||
const { data } = await firstValueFrom(
|
||||
this.httpService.patch(`${this.config.baseUrl}/admin/icons/${id}`, dto, { headers: this.getHeaders() }).pipe(
|
||||
catchError((err: AxiosError) => {
|
||||
this.logger.error(`Failed to update icon: ${err.message}`, err.stack);
|
||||
return throwError(() => err);
|
||||
}),
|
||||
),
|
||||
);
|
||||
return data;
|
||||
} catch (error: unknown) {
|
||||
this.logger.error(`Error updating icon: ${error instanceof Error ? error.message : "Unknown error"}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async removeIcon(id: string) {
|
||||
try {
|
||||
const { data } = await firstValueFrom(
|
||||
this.httpService.delete(`${this.config.baseUrl}/admin/icons/${id}`, { headers: this.getHeaders() }).pipe(
|
||||
catchError((err: AxiosError) => {
|
||||
this.logger.error(`Failed to delete icon: ${err.message}`, err.stack);
|
||||
return throwError(() => err);
|
||||
}),
|
||||
),
|
||||
);
|
||||
return data;
|
||||
} catch (error: unknown) {
|
||||
this.logger.error(`Error deleting icon: ${error instanceof Error ? error.message : "Unknown error"}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Group methods
|
||||
async createGroup(dto: CreateGroupDto) {
|
||||
try {
|
||||
const { data } = await firstValueFrom(
|
||||
this.httpService.post(`${this.config.baseUrl}/admin/icons/groups`, dto, { headers: this.getHeaders() }).pipe(
|
||||
catchError((err: AxiosError) => {
|
||||
this.logger.error(`Failed to create group: ${err.message}`, err.stack);
|
||||
return throwError(() => err);
|
||||
}),
|
||||
),
|
||||
);
|
||||
return data;
|
||||
} catch (error: unknown) {
|
||||
this.logger.error(`Error creating group: ${error instanceof Error ? error.message : "Unknown error"}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async findAllGroups() {
|
||||
try {
|
||||
const { data } = await firstValueFrom(
|
||||
this.httpService.get(`${this.config.baseUrl}/admin/icons/groups`, { headers: this.getHeaders() }).pipe(
|
||||
catchError((err: AxiosError) => {
|
||||
this.logger.error(`Failed to fetch groups: ${err.message}`, err.stack);
|
||||
return throwError(() => err);
|
||||
}),
|
||||
),
|
||||
);
|
||||
return data;
|
||||
} catch (error: unknown) {
|
||||
this.logger.error(`Error fetching groups: ${error instanceof Error ? error.message : "Unknown error"}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async findOneGroup(id: string) {
|
||||
try {
|
||||
const { data } = await firstValueFrom(
|
||||
this.httpService.get(`${this.config.baseUrl}/admin/icons/groups/${id}`, { headers: this.getHeaders() }).pipe(
|
||||
catchError((err: AxiosError) => {
|
||||
this.logger.error(`Failed to fetch group: ${err.message}`, err.stack);
|
||||
return throwError(() => err);
|
||||
}),
|
||||
),
|
||||
);
|
||||
return data;
|
||||
} catch (error: unknown) {
|
||||
this.logger.error(`Error fetching group: ${error instanceof Error ? error.message : "Unknown error"}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async updateGroup(id: string, dto: UpdateGroupDto) {
|
||||
try {
|
||||
const { data } = await firstValueFrom(
|
||||
this.httpService.patch(`${this.config.baseUrl}/admin/icons/groups/${id}`, dto, { headers: this.getHeaders() }).pipe(
|
||||
catchError((err: AxiosError) => {
|
||||
this.logger.error(`Failed to update group: ${err.message}`, err.stack);
|
||||
return throwError(() => err);
|
||||
}),
|
||||
),
|
||||
);
|
||||
return data;
|
||||
} catch (error: unknown) {
|
||||
this.logger.error(`Error updating group: ${error instanceof Error ? error.message : "Unknown error"}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async removeGroup(id: string) {
|
||||
try {
|
||||
const { data } = await firstValueFrom(
|
||||
this.httpService.delete(`${this.config.baseUrl}/admin/icons/groups/${id}`, { headers: this.getHeaders() }).pipe(
|
||||
catchError((err: AxiosError) => {
|
||||
this.logger.error(`Failed to delete group: ${err.message}`, err.stack);
|
||||
return throwError(() => err);
|
||||
}),
|
||||
),
|
||||
);
|
||||
return data;
|
||||
} catch (error: unknown) {
|
||||
this.logger.error(`Error deleting group: ${error instanceof Error ? error.message : "Unknown error"}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,4 +20,5 @@ export enum PermissionEnum {
|
||||
PAYMENTS = "payments",
|
||||
MANAGE_SSO_CLIENTS = "manage_sso_clients",
|
||||
SUPPORT_PLAN = "support_plan",
|
||||
ICONS = "icons",
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user