Skip to content

Commit 0dfee45

Browse files
authored
v1.1.8
v1.1.8
2 parents 5de7aeb + 92cde99 commit 0dfee45

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

70 files changed

+8383
-1208
lines changed

backend/src/middleware/auth.middleware.ts

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,14 @@ import jwt from 'jsonwebtoken';
44
const JWT_SECRET = process.env.SECRET || '@jZCgtn^qg8So*^^6A2M'; // Same secret used in auth.route.ts
55

66
// Define custom Request interface with user property
7-
declare global {
8-
namespace Express {
9-
interface Request {
10-
user?: {
11-
username: string;
12-
role: string;
13-
[key: string]: any;
14-
};
15-
}
7+
8+
declare module 'express-serve-static-core' {
9+
interface Request {
10+
user?: {
11+
username: string;
12+
role: string;
13+
[key: string]: any;
14+
};
1615
}
1716
}
1817

backend/src/routes/app-shortcut.route.ts

Lines changed: 44 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,14 @@ import multer from 'multer';
44
import path from 'path';
55

66
import { UPLOAD_DIRECTORY } from '../constants/constants';
7+
import { authenticateToken } from '../middleware/auth.middleware';
78

89
export const appShortcutRoute = Router();
910

1011
const 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

4342
const upload = multer({ storage });
4443

45-
// Upload app icon
44+
// Upload app icon (single file)
4645
appShortcutRoute.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
71102
appShortcutRoute.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

Comments
 (0)