@@ -4,15 +4,14 @@ import multer from 'multer';
44import path from 'path' ;
55
66import { UPLOAD_DIRECTORY } from '../constants/constants' ;
7+ import { authenticateToken } from '../middleware/auth.middleware' ;
78
89export const appShortcutRoute = Router ( ) ;
910
1011const sanitizeFileName = ( fileName : string ) : string => {
11- const nameWithoutExt = fileName . replace ( / \. [ ^ / . ] + $ / , '' ) ;
12-
13- // Replace special characters and normalize spaces
14- return nameWithoutExt
15- . replace ( / [ ^ \w \s - ] / g, '' )
12+ // Replace special characters and normalize spaces, but keep the extension
13+ return fileName
14+ . replace ( / [ ^ \w \s . - ] / g, '' )
1615 . replace ( / [ \s _ - ] + / g, ' ' )
1716 . trim ( ) ;
1817} ;
@@ -42,14 +41,14 @@ const storage = multer.diskStorage({
4241
4342const upload = multer ( { storage } ) ;
4443
45- // Upload app icon
44+ // Upload app icon (single file)
4645appShortcutRoute . post ( '/upload' , upload . single ( 'file' ) , ( req : Request , res : Response ) => {
4746 if ( ! req . file ) {
4847 res . status ( 400 ) . json ( { message : 'No file uploaded' } ) ;
4948 return ;
5049 }
5150
52- // Sanitize the file name for display
51+ // Sanitize the file name for display (keeping extension)
5352 const sanitizedName = sanitizeFileName ( req . file . originalname ) ;
5453
5554 console . log ( 'File uploaded successfully:' , {
@@ -67,6 +66,38 @@ appShortcutRoute.post('/upload', upload.single('file'), (req: Request, res: Resp
6766 } ) ;
6867} ) ;
6968
69+ // Upload multiple app icons (batch upload)
70+ appShortcutRoute . post ( '/upload-batch' , authenticateToken , upload . array ( 'files' , 20 ) , ( req : Request , res : Response ) => {
71+ const files = req . files as Express . Multer . File [ ] ;
72+
73+ if ( ! files || files . length === 0 ) {
74+ res . status ( 400 ) . json ( { message : 'No files uploaded' } ) ;
75+ return ;
76+ }
77+
78+ const uploadedIcons = files . map ( file => {
79+ const sanitizedName = sanitizeFileName ( file . originalname ) ;
80+
81+ console . log ( 'File uploaded successfully:' , {
82+ originalName : file . originalname ,
83+ sanitizedName,
84+ filename : file . filename ,
85+ path : file . path
86+ } ) ;
87+
88+ return {
89+ name : sanitizedName ,
90+ filePath : `/uploads/app-icons/${ file . filename } ` ,
91+ source : 'custom'
92+ } ;
93+ } ) ;
94+
95+ res . status ( 200 ) . json ( {
96+ message : `${ uploadedIcons . length } app icon(s) uploaded successfully` ,
97+ icons : uploadedIcons
98+ } ) ;
99+ } ) ;
100+
70101// Get list of custom app icons
71102appShortcutRoute . get ( '/custom-icons' , ( req : Request , res : Response ) => {
72103 try {
@@ -84,7 +115,8 @@ appShortcutRoute.get('/custom-icons', (req: Request, res: Response) => {
84115
85116 // Map files to icon objects
86117 const icons = files . map ( file => {
87- // Get the file name without extension
118+ // Get the file name without extension and the extension separately
119+ const fileExtension = path . extname ( file ) ;
88120 const filenameWithoutExt = path . parse ( file ) . name ;
89121
90122 // Extract the original name part (everything before the timestamp)
@@ -93,16 +125,16 @@ appShortcutRoute.get('/custom-icons', (req: Request, res: Response) => {
93125
94126 // If the filename has our expected format with a timestamp suffix,
95127 // remove the timestamp; otherwise keep the full name
96- let displayName = filenameWithoutExt ;
128+ let displayNameWithoutExt = filenameWithoutExt ;
97129
98130 // Check if the last part is a timestamp (all digits)
99131 if ( nameParts . length > 1 && / ^ \d + $ / . test ( nameParts [ nameParts . length - 1 ] ) ) {
100132 // Remove the timestamp part and join the rest
101- displayName = nameParts . slice ( 0 , - 1 ) . join ( '-' ) ;
133+ displayNameWithoutExt = nameParts . slice ( 0 , - 1 ) . join ( '-' ) ;
102134 }
103135
104- // Ensure the display name is sanitized
105- displayName = sanitizeFileName ( displayName ) ;
136+ // Ensure the display name is sanitized and add back the extension
137+ const displayName = sanitizeFileName ( displayNameWithoutExt + fileExtension ) ;
106138
107139 // Create the icon object
108140 return {
0 commit comments