Compare commits
2 Commits
d76f8e1e29
...
bbe82ec9a3
| Author | SHA1 | Date | |
|---|---|---|---|
| bbe82ec9a3 | |||
| b4845dd435 |
@ -58,11 +58,14 @@ export async function PUT(req:Request)
|
|||||||
|
|
||||||
// Validate logo if provided
|
// Validate logo if provided
|
||||||
if (logo && typeof logo === 'string') {
|
if (logo && typeof logo === 'string') {
|
||||||
// Check if it's a valid SVG
|
const isSvg = logo.includes('<svg') && logo.includes('</svg>');
|
||||||
if (!logo.includes('<svg') || !logo.includes('</svg>')) {
|
const isPng = logo.startsWith('data:image/png;base64,');
|
||||||
|
|
||||||
|
// Check if it's a valid SVG or PNG data URL
|
||||||
|
if (!isSvg && !isPng) {
|
||||||
return NextResponse.json({
|
return NextResponse.json({
|
||||||
success: false,
|
success: false,
|
||||||
message: "invalidSVGFile",
|
message: "invalidLogoFile",
|
||||||
}, {
|
}, {
|
||||||
status: 400,
|
status: 400,
|
||||||
headers: {
|
headers: {
|
||||||
@ -71,11 +74,13 @@ export async function PUT(req:Request)
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove any script tags for security
|
// For SVG files, remove any script tags for security
|
||||||
|
if (isSvg) {
|
||||||
const cleanLogo = logo.replace(/<script[^>]*>.*?<\/script>/gi, '');
|
const cleanLogo = logo.replace(/<script[^>]*>.*?<\/script>/gi, '');
|
||||||
// Update the logo variable with cleaned content
|
|
||||||
logo = cleanLogo;
|
logo = cleanLogo;
|
||||||
}
|
}
|
||||||
|
// PNG data URLs are already safe as they're base64 encoded
|
||||||
|
}
|
||||||
// update the doc
|
// update the doc
|
||||||
let updated_doc = await userModel.updateMany({} , {
|
let updated_doc = await userModel.updateMany({} , {
|
||||||
$set: {
|
$set: {
|
||||||
|
|||||||
@ -35,8 +35,8 @@ export default function GeneralSettings()
|
|||||||
{ value: true, label: t('show') },
|
{ value: true, label: t('show') },
|
||||||
{ value: false, label: t('dontShow') },
|
{ value: false, label: t('dontShow') },
|
||||||
]
|
]
|
||||||
// extract svg from svg file
|
// extract content from svg or png file
|
||||||
const readFile = (file : Blob) => {
|
const readFile = (file : Blob, fileType: string) => {
|
||||||
// this function return a promise
|
// this function return a promise
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
// read the file
|
// read the file
|
||||||
@ -50,25 +50,34 @@ export default function GeneralSettings()
|
|||||||
reader.onerror = (error) => {
|
reader.onerror = (error) => {
|
||||||
reject(error);
|
reject(error);
|
||||||
};
|
};
|
||||||
// read the file as a text thene fire onload listener
|
// read the file based on type
|
||||||
|
if (fileType === 'svg') {
|
||||||
reader.readAsText(file);
|
reader.readAsText(file);
|
||||||
|
} else {
|
||||||
|
reader.readAsDataURL(file);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
// handle the changes in the file input
|
// handle the changes in the file input
|
||||||
const handleFileChange = async (event : any , setFieldValue : any) => {
|
const handleFileChange = async (event : any , setFieldValue : any) => {
|
||||||
const file = event.target.files[0];
|
const file = event.target.files[0];
|
||||||
if (file) {
|
if (file) {
|
||||||
// Validate file type
|
// Validate file type - allow both SVG and PNG
|
||||||
if (!file.type.includes('svg') && !file.name.toLowerCase().endsWith('.svg')) {
|
const isSvg = file.type.includes('svg') || file.name.toLowerCase().endsWith('.svg');
|
||||||
alert(t('onlySVGFilesAllowed') || 'Only SVG files are allowed');
|
const isPng = file.type.includes('png') || file.name.toLowerCase().endsWith('.png');
|
||||||
|
|
||||||
|
if (!isSvg && !isPng) {
|
||||||
|
alert(t('onlySVGAndPNGFilesAllowed') || 'Only SVG and PNG files are allowed');
|
||||||
event.target.value = ''; // Clear the input
|
event.target.value = ''; // Clear the input
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const fileType = isSvg ? 'svg' : 'png';
|
||||||
// get the file content
|
// get the file content
|
||||||
const content = await readFile(file) as string;
|
const content = await readFile(file, fileType) as string;
|
||||||
|
|
||||||
|
if (fileType === 'svg') {
|
||||||
// Validate SVG content
|
// Validate SVG content
|
||||||
if (!content.includes('<svg') || !content.includes('</svg>')) {
|
if (!content.includes('<svg') || !content.includes('</svg>')) {
|
||||||
alert(t('invalidSVGFile') || 'Invalid SVG file format');
|
alert(t('invalidSVGFile') || 'Invalid SVG file format');
|
||||||
@ -78,9 +87,11 @@ export default function GeneralSettings()
|
|||||||
|
|
||||||
// Additional security check - remove any script tags
|
// Additional security check - remove any script tags
|
||||||
const cleanContent = content.replace(/<script[^>]*>.*?<\/script>/gi, '');
|
const cleanContent = content.replace(/<script[^>]*>.*?<\/script>/gi, '');
|
||||||
|
setFieldValue('logo' , cleanContent);
|
||||||
// set the field value with with new file content
|
} else {
|
||||||
setFieldValue('logo' , cleanContent)
|
// For PNG files, store the data URL directly
|
||||||
|
setFieldValue('logo' , content);
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error reading file:', error);
|
console.error('Error reading file:', error);
|
||||||
alert(t('errorReadingFile') || 'Error reading file');
|
alert(t('errorReadingFile') || 'Error reading file');
|
||||||
@ -343,13 +354,21 @@ export default function GeneralSettings()
|
|||||||
dark:border-primary-dark rounded-md
|
dark:border-primary-dark rounded-md
|
||||||
overflow-hidden flex items-center justify-center
|
overflow-hidden flex items-center justify-center
|
||||||
`}>
|
`}>
|
||||||
|
{values.logo && values.logo.startsWith('data:image/png') ? (
|
||||||
|
<img
|
||||||
|
src={values.logo}
|
||||||
|
alt="Logo"
|
||||||
|
className="max-w-[100px] max-h-[100px] object-contain"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
<div className="overflow-hidden [&_svg]:max-w-[100px] [&_svg]:max-h-[100px]" dangerouslySetInnerHTML={{ __html: values.logo }} />
|
<div className="overflow-hidden [&_svg]:max-w-[100px] [&_svg]:max-h-[100px]" dangerouslySetInnerHTML={{ __html: values.logo }} />
|
||||||
|
)}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
onChange={((e) => {
|
onChange={((e) => {
|
||||||
handleFileChange(e , setFieldValue)
|
handleFileChange(e , setFieldValue)
|
||||||
})}
|
})}
|
||||||
accept=".svg"
|
accept=".svg,.png"
|
||||||
disabled={!edite}
|
disabled={!edite}
|
||||||
className="invisible w-0 h-0 hidden"
|
className="invisible w-0 h-0 hidden"
|
||||||
type="file"
|
type="file"
|
||||||
|
|||||||
@ -46,7 +46,17 @@ export default function Sidebar () {
|
|||||||
<nav className={`fixed top-0 ltr:left-0 rtl:right-0 lg:relative shadow-[5px_0_25px_0_rgba(94,92,154,0.1)] z-[99] h-screen bg-primary-dark dark:bg-primary-dark duration-300 ${sidebarState ? 'min-w-[260px] max-w-[260px] ' : 'ltr:translate-x-[-260px] rtl:translate-x-[260px] w-0'}`}>
|
<nav className={`fixed top-0 ltr:left-0 rtl:right-0 lg:relative shadow-[5px_0_25px_0_rgba(94,92,154,0.1)] z-[99] h-screen bg-primary-dark dark:bg-primary-dark duration-300 ${sidebarState ? 'min-w-[260px] max-w-[260px] ' : 'ltr:translate-x-[-260px] rtl:translate-x-[260px] w-0'}`}>
|
||||||
<header className="w-full max-h-[64px] flex justify-between items-center px-[18px] py-[14px] [&_*]:text-text-light bg-secondary-dark">
|
<header className="w-full max-h-[64px] flex justify-between items-center px-[18px] py-[14px] [&_*]:text-text-light bg-secondary-dark">
|
||||||
<div className="flex justify-center items-center flex gap-[15px]">
|
<div className="flex justify-center items-center flex gap-[15px]">
|
||||||
<div className={`w-12 h-16 flex items-center justify-center relative ${appGeneralSettings.showLogo ? '' : 'hidden'}`} dangerouslySetInnerHTML={{ __html: appGeneralSettings.logo || '' }} />
|
<div className={`w-12 h-16 flex items-center justify-center relative ${appGeneralSettings.showLogo ? '' : 'hidden'}`}>
|
||||||
|
{appGeneralSettings.logo && appGeneralSettings.logo.startsWith('data:image/png') ? (
|
||||||
|
<img
|
||||||
|
src={appGeneralSettings.logo}
|
||||||
|
alt="Logo"
|
||||||
|
className="max-w-full max-h-full object-contain"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div dangerouslySetInnerHTML={{ __html: appGeneralSettings.logo || '' }} />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
<h1 className="text-[25px] font-light">{local == 'ar' ? appGeneralSettings.appName : appGeneralSettings.appNameEN}</h1>
|
<h1 className="text-[25px] font-light">{local == 'ar' ? appGeneralSettings.appName : appGeneralSettings.appNameEN}</h1>
|
||||||
</div>
|
</div>
|
||||||
<button onClick={() => {dispatch(toggleSidebar())}} className="rounded-full rtl:rotate-180 hover:bg-primary/5 p-[6px]">
|
<button onClick={() => {dispatch(toggleSidebar())}} className="rounded-full rtl:rotate-180 hover:bg-primary/5 p-[6px]">
|
||||||
|
|||||||
@ -365,8 +365,9 @@
|
|||||||
"save": "الحفظ",
|
"save": "الحفظ",
|
||||||
"ignore": "تجاهل",
|
"ignore": "تجاهل",
|
||||||
"appNameEN": "اسم التطبيق ( en )",
|
"appNameEN": "اسم التطبيق ( en )",
|
||||||
"onlySVGFilesAllowed": "يُسمح فقط بملفات SVG",
|
"onlySVGAndPNGFilesAllowed": "يُسمح فقط بملفات SVG و PNG",
|
||||||
"invalidSVGFile": "تنسيق ملف SVG غير صالح",
|
"invalidSVGFile": "تنسيق ملف SVG غير صالح",
|
||||||
|
"invalidLogoFile": "تنسيق ملف الشعار غير صالح. يُسمح فقط بملفات SVG و PNG",
|
||||||
"errorReadingFile": "خطأ في قراءة الملف"
|
"errorReadingFile": "خطأ في قراءة الملف"
|
||||||
},
|
},
|
||||||
"statistics": {
|
"statistics": {
|
||||||
|
|||||||
@ -369,8 +369,9 @@
|
|||||||
"edite": "Edite",
|
"edite": "Edite",
|
||||||
"save": "Save",
|
"save": "Save",
|
||||||
"ignore": "Ignore",
|
"ignore": "Ignore",
|
||||||
"onlySVGFilesAllowed": "Only SVG files are allowed",
|
"onlySVGAndPNGFilesAllowed": "Only SVG and PNG files are allowed",
|
||||||
"invalidSVGFile": "Invalid SVG file format",
|
"invalidSVGFile": "Invalid SVG file format",
|
||||||
|
"invalidLogoFile": "Invalid logo file format. Only SVG and PNG files are allowed",
|
||||||
"errorReadingFile": "Error reading file"
|
"errorReadingFile": "Error reading file"
|
||||||
},
|
},
|
||||||
"statistics": {
|
"statistics": {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user