-
المساهمات
12 -
تاريخ الانضمام
-
تاريخ آخر زيارة
إنجازات Kamel Mecheri

عضو مساهم (2/3)
0
السمعة بالموقع
-
مرحباً! تشفير الفيديو وحمايته عبر العناوين الموقعة أو التشفير بالمفتاح يمكن أن يكون موضوعًا معقدًا بعض الشيء، لكن سأبسط الأمور لك بشكل يناسب فهمك. سأستعرض كيفية تطبيق الحماية باستخدام كل من تشفير الفيديو بـ AES-128 و العناوين الموقعة. 1. تشفير الفيديو باستخدام AES-128 لتشفير فيديوهات HLS (HTTP Live Streaming) باستخدام خوارزمية AES-128، يمكنك اتباع الخطوات التالية: الخطوة 1: توليد مفتاح التشفير أولاً، تحتاج إلى توليد مفتاح تشفير عشوائي. يمكنك استخدام OpenSSL لتوليد مفتاح: openssl rand -hex 16 سيقوم هذا الأمر بإنشاء مفتاح عشوائي بطول 128 بت، وهو ما تحتاجه لتشفير الفيديو. الخطوة 2: إنشاء ملف مفتاح التشفير احفظ المفتاح في ملف نصي. على سبيل المثال، encryption.key. الخطوة 3: استخدام FFmpeg لتشفير الفيديو افترض أن لديك ملف فيديو باسم input.mp4 وترغب في تحويله إلى HLS مع تشفير AES-128. يمكنك استخدام FFmpeg لتنفيذ ذلك: ffmpeg -i input.mp4 -c:a aac -c:v h264 -hls_key_info_file key_info.txt -hls_segment_filename 'segment_%03d.ts' -hls_playlist_type vod output.m3u8 في هذا المثال، key_info.txt هو ملف يحتوي على معلومات حول مفتاح التشفير. يمكنك إنشاء هذا الملف بالشكل التالي: key.bin https://example.com/keys/key.bin حيث: key.bin هو اسم الملف الذي يحتوي على مفتاح التشفير. الرابط هو المكان الذي ستقوم بتحميل المفتاح منه (عادة ما يكون خادمًا آمنًا). الخطوة 4: توفير المفتاح عبر HTTP يجب أن يكون ملف المفتاح (key.bin) متاحًا عبر HTTP على الخادم الخاص بك. تأكد من توفير هذا الملف في موقع آمن وسرّي. 2. حماية عبر العناوين الموقعة (Token-based Protection) لحماية الفيديو باستخدام عناوين موقعة، تحتاج إلى اتباع الخطوات التالية: الخطوة 1: توليد توقيع URL ستحتاج إلى توليد توقيع URL يكون صالحًا لفترة زمنية محددة. يُستخدم هذا التوقيع لتحديد من يمكنه الوصول إلى الفيديو. إليك مثالًا باستخدام Node.js: const crypto = require('crypto'); const base64url = require('base64url'); // Generate a secure token function generateSignedUrl(url, secretKey, expiresInSeconds) { const expires = Math.floor(Date.now() / 1000) + expiresInSeconds; const signature = crypto.createHmac('sha256', secretKey) .update(`${url}?expires=${expires}`) .digest('hex'); const signedUrl = `${url}?expires=${expires}&signature=${signature}`; return signedUrl; } // Usage example const url = 'https://example.com/stream/video.m3u8'; const secretKey = 'your_secret_key'; const expiresInSeconds = 3600; // 1 hour const signedUrl = generateSignedUrl(url, secretKey, expiresInSeconds); console.log(signedUrl); الخطوة 2: التحقق من التوقيع عند الطلب عند طلب الفيديو، تحقق من التوقيع والتأكد من صلاحية التوقيع قبل تقديم المحتوى. يمكنك استخدام نفس الطريقة للتحقق من التوقيع. function verifySignedUrl(url, signature, secretKey) { const parsedUrl = new URL(url); const expires = parsedUrl.searchParams.get('expires'); const validSignature = crypto.createHmac('sha256', secretKey) .update(`${url}?expires=${expires}`) .digest('hex'); return validSignature === signature && Date.now() / 1000 < expires; } // Usage example const requestUrl = 'https://example.com/stream/video.m3u8?expires=1627074600&signature=valid_signature'; const signature = new URL(requestUrl).searchParams.get('signature'); if (verifySignedUrl(requestUrl, signature, secretKey)) { console.log('URL is valid'); } else { console.log('URL is invalid'); } النصائح: تأمين مفتاح التشفير: تأكد من حماية مفتاح التشفير جيدًا ولا تضعه في مكان يمكن الوصول إليه بسهولة. استخدام عناوين موقعة بشكل آمن: تأكد من استخدام خوارزميات التشفير القوية وتحديث السر بشكل دوري. التحقق من صلاحية التوقيع: تحقق من التوقيع والتأكد من أنه لم يتجاوز وقت الصلاحية. تحديث المفاتيح بانتظام: استخدم مفاتيح جديدة بانتظام لضمان الأمان.
-
مرحباً! تواجه مشكلة بسبب استخدام async/await في مكونات العميل في Next.js، وهذا غير مدعوم حالياً في هذه المكونات. الخطأ ناتج عن محاولة استخدام async داخل مكون العميل (Client Component) بينما async/await مدعوم فقط في مكونات الخادم (Server Components). شرح المشكلة عندما تضيف 'use client' إلى مكون، فإنه يصبح مكوناً للعميل ويجب ألا يحتوي على وظائف غير مدعومة مثل async/await. في حالتك، المشكلة تحدث لأنك تستخدم async في دالة handleClick داخل مكون عميل. الحل المقترح لتجاوز هذا الخطأ، يجب عليك تحويل الدالة handleClick إلى دالة غير متزامنة (async) أو استخدام طرق أخرى للتعامل مع حالة غير المتزامنة. يمكنك إما التعامل مع async/await خارج مكون العميل، أو استخدام طرق غير متزامنة أخرى. تصحيح الكود يمكنك تعديل الكود بحيث يكون التعامل مع الحالة غير المتزامنة منفصلاً عن مكون العميل: 'use client' import styles from '../../styles/leftMenu.module.css' import BigLink from './BigLinks' import { GoHomeFill, GoSearch, GoBellFill, GoChecklist } from 'react-icons/go' import { useContext } from 'react' import { UserContext } from '~/contexts/UserContext' import Image from 'next/image' import { FaUser } from 'react-icons/fa' import { useRouter } from 'next/navigation' export default function LeftMenu() { const { user, setUser } = useContext(UserContext) const router = useRouter() const handleClick = () => { // استخدم دالة غير متزامنة داخل handleClick ولكن لا تجعلها async const handleAsyncClick = async () => { if (user.id) { setUser({ id: '', username: '', image: '' }) } else { router.push('authentication/login') } } handleAsyncClick() } return ( <main className={styles.left_menu_container}> <svg fill="rgba(231,233,234,1.00)" className={styles.left_menu_logo} viewBox="0 0 24 24" aria-hidden="true" > <g> <path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"></path> </g> </svg> <ul> <li> <BigLink title="Home" page=""> <GoHomeFill size={30} fill="white" /> </BigLink> </li> <li> <BigLink title="Search" page="search"> <GoSearch size={30} fill="white" /> </BigLink> </li> <li> <BigLink title="Notifications" page="notifications"> <GoBellFill size={30} fill="white" /> </BigLink> </li> <li> <BigLink title="Lists" page="lists"> <GoChecklist size={30} fill="white" /> </BigLink> </li> </ul> <div className={styles.profile_section_wrapper}> <p>{user.username}</p> {user.image ? ( <Image src="" alt="" /> ) : ( <FaUser size={30} onClick={handleClick} /> )} </div> </main> ) } في هذا الكود، قمت بفصل التعامل مع الحالة غير المتزامنة إلى دالة handleAsyncClick التي تقوم باستدعاء async/await، بينما handleClick نفسها تبقى غير متزامنة. أتمنى أن يساعدك هذا في حل المشكلة!
-
مرحباً! سأشرح لك كل من المفاهيم التالية في جافاسكريبت بشكل مفصل مع الأمثلة. 1. IIFE (Immediately Invoked Function Expression) التعريف: IIFE هو تعبير دالة يتم تنفيذه مباشرة بعد تعريفه. يستخدم عادة لتجنب التلوث العالمي للمساحة الأسمية (namespace) وحماية المتغيرات داخل نطاق الدالة من التداخل مع المتغيرات الأخرى. مثال: (function() { var message = "Hello, world!"; console.log(message); // "Hello, world!" })(); في هذا المثال، يتم تعريف دالة وإدارتها مباشرة. المتغير message هنا محمي داخل نطاق هذه الدالة فقط. 2. this التعريف: في جافاسكريبت، الكلمة الرئيسية this تشير إلى الكائن الذي يتم تنفيذ الدالة عليه. معنى this يمكن أن يتغير بناءً على كيفية استدعاء الدالة. مثال: function show() { console.log(this); } const obj = { name: 'Alice', show: show }; obj.show(); // { name: 'Alice', show: [Function: show] } هنا، this يشير إلى الكائن obj لأن الدالة show تم استدعاؤها كجزء من الكائن obj. 3. call() و apply() و bind() التعريف: هذه الطرق تستخدم لتحديد قيمة this في دالة معينة. call() التعريف: تتيح لك طريقة call() استدعاء دالة مع تحديد قيمة this والوسائط التي سيتم تمريرها إلى الدالة. مثال: function greet(greeting, punctuation) { console.log(`${greeting}, ${this.name}${punctuation}`); } const person = { name: 'John' }; greet.call(person, 'Hello', '!'); // "Hello, John!" هنا، greet.call(person, 'Hello', '!') يستدعي الدالة greet ويحدد this ليشير إلى الكائن person. apply() التعريف: تعمل طريقة apply() مثل call()، ولكنها تأخذ الوسائط كصفيف بدلاً من قائمة من القيم. مثال: function greet(greeting, punctuation) { console.log(`${greeting}, ${this.name}${punctuation}`); } const person = { name: 'Jane' }; greet.apply(person, ['Hi', '?']); // "Hi, Jane?" هنا، greet.apply(person, ['Hi', '?']) يستدعي الدالة greet ويحدد this ليشير إلى الكائن person، ويمرر الوسائط كصفيف. bind() التعريف: تقوم طريقة bind() بإنشاء دالة جديدة ذات قيمة this محددة والوسائط المبدئية، ولكنها لا تقوم بتنفيذ الدالة على الفور. مثال: function greet(greeting, punctuation) { console.log(`${greeting}, ${this.name}${punctuation}`); } const person = { name: 'Emma' }; const greetEmma = greet.bind(person, 'Hey'); greetEmma('!!'); // "Hey, Emma!!" هنا، greet.bind(person, 'Hey') ينشئ دالة جديدة حيث this يشير إلى الكائن person، وتكون الوسائط المبدئية هي 'Hey'. وعندما نستخدم greetEmma('!!')، يتم استدعاء الدالة مع الوسائط المضافة. التلخيص: IIFE: يستخدم لتنفيذ دالة فوراً وعزل المتغيرات. this: يشير إلى الكائن الذي يتم تنفيذ الدالة عليه. call() و apply(): تستخدم لتحديد قيمة this في الدالة، مع اختلاف في كيفية تمرير الوسائط. bind(): تستخدم لإنشاء دالة جديدة مع تحديد قيمة this ووسائط مبدئية، دون تنفيذها فوراً. أتمنى أن يكون هذا قد ساعدك في فهم هذه المفاهيم!
-
يبدو أنك تواجه مشكلة في تحميل جميع المنتجات عبر التمرير عند استخدام getAllProducts في عملية الترقيم (pagination). في الوقت الحالي، الكود الخاص بك يسترجع البيانات من آخر صفحة فقط، بينما تحتاج إلى جمع البيانات من كل الصفحات. للقيام بذلك، تحتاج إلى تعديل الكود الخاص بك بحيث يواصل تحميل البيانات من الصفحات التالية حتى يتم الحصول على كل المنتجات. سأقوم بتقديم مثال لكيفية فعل ذلك: useEffect(() => { const getProducts = async () => { const { data: { items = [], itemsCount, pageNumber, pageSize } = {} } = await getAllProducts({ body: {} }); let noOfPages = Math.ceil(itemsCount / pageSize); let currentPage = pageNumber; let productOptions = [...items]; while (currentPage < noOfPages) { currentPage += 1; const { data: { items: additionalItems = [] } = {} } = await getAllProducts({ body: { pageNumber: currentPage }, }); productOptions = [...productOptions, ...additionalItems]; } console.log("All products:", productOptions); }; getProducts(); }, []); في هذا التعديل: تحويل المتغير productOptions إلى مصفوفة جديدة: بدأنا بتخزين العناصر من الصفحة الأولى في productOptions واستخدمنا التمديد عبر ... لتجميع البيانات من الصفحات اللاحقة. استخدام حلقة while لتحميل الصفحات التالية: قمنا بتكرار استرجاع البيانات من الصفحات التالية حتى نغطي جميع الصفحات المتاحة. جمع البيانات من جميع الصفحات في مصفوفة واحدة: بعد كل استرجاع بيانات من صفحة جديدة، ندمجها مع البيانات السابقة في productOptions. هذا سيضمن أنك تحصل على جميع المنتجات من جميع الصفحات وليس فقط من آخر صفحة.
-
مرحبًا! إذا كنت تواجه مشكلة في استيراد الصور في Next.js وتظهر الصور تالفة، فإليك بعض الخطوات التي قد تساعدك في حل المشكلة: الحلول المقترحة تأكد من استيراد الصور بشكل صحيح: تأكد من أنك تستورد الصور بشكل صحيح. يجب أن يكون المسار إلى الصورة صحيحًا وتأكد من وجود الصورة في ذلك المسار. بالنسبة للملفات SVG، تأكد من أن لديك امتداد .svg في نهاية المسار. import Logo from '../assets/Logo.svg'; import hamburgerBtn from '../assets/hamburgerBtn.svg'; استخدم مكون Image من next/image: من الأفضل استخدام مكون Image من Next.js لتحميل الصور، حيث يوفر تحسينات في الأداء وتوافق أفضل. import Image from 'next/image'; import Logo from '../assets/Logo.svg'; import hamburgerBtn from '../assets/hamburgerBtn.svg'; function Navbar() { const [toggle, setToggle] = useState(false); const handleClick = () => setToggle(!toggle); return ( <div className='w-full h-[80px] bg-white'> <div className="2xl:max-w-[1400px] 2xl:px-[0] xl:max-w-[1180px] lg:max-w-[924px] md:max-w-[668px] sm:max-w-[540px] max-w-[460px] md:px-[0px] sm:px-[40px] px-[40px] m-auto w-full h-full flex justify-between items-center"> <Image src={Logo} alt="Logo" className='sm:h-[40px] h-[30px]' /> <div className="hidden md:flex items-center"> <ul className='flex gap-6'> <li>Home</li> <li>About</li> <li>Contact</li> </ul> </div> <div className='md:hidden' onClick={handleClick}> <Image src={hamburgerBtn} alt="Hamburger Button" className="sm:h-[25px] h-[20px]" /> </div> </div> </div> ); } export default Navbar; تحقق من إعدادات Webpack في next.config.js: تأكد من إعدادات Webpack لتحميل الصور. يمكنك إضافة إعدادات لتحميل الصور إذا لزم الأمر. // next.config.js module.exports = { images: { domains: ['example.com'], // أضف النطاقات التي يمكن تحميل الصور منها }, }; تحقق من وجود الصورة: تأكد من أن الصورة موجودة في المسار الصحيح الذي تستورده منه. تحقق من المسارات ونوع الملفات. إذا كنت لا تزال تواجه مشاكل بعد هذه الخطوات، حاول التحقق من سجل الأخطاء في وحدة التحكم أو أدوات المطورين في المتصفح، أو تحقق من الوثائق الرسمية لـ Next.js.
-
نعم، يمكنك العثور على دورات عربية في لغة البرمجة MQL5 (MetaQuotes Language 5) الخاصة ببرنامج MetaTrader 5. هناك عدة طرق للعثور على هذه الدورات: المنصات التعليمية العربية: تحقق من المنصات مثل "رواق"، "إدراك"، و"دورات" التي قد تقدم دورات في هذا المجال. يوتيوب: هناك العديد من القنوات التعليمية العربية التي تقدم شروحات ودورات تعليمية في MQL5. ابحث عن "MQL5" أو "برمجة MetaTrader 5" على يوتيوب. المنتديات التقنية: انضم إلى منتديات عربية متخصصة في الفوركس وتداول الأسهم، مثل منتديات "عرب فوركس" أو "منتدى الأسهم" حيث يمكنك العثور على دروس ومقالات حول MQL5. مواقع التدوين: ابحث عن مواقع تكتب مقالات ودروس حول MQL5، حيث قد تجد أيضًا دروسًا باللغة العربية. كيف أتعلم لغة MQL5 بالضبط؟ ابدأ بالأساسيات: اقرأ الوثائق الرسمية لـ MQL5 حيث توفر MetaQuotes معلومات تفصيلية عن اللغة. الدورات التعليمية: الالتحاق بدورات على الإنترنت التي تشرح كيفية كتابة وتطبيق السكربتات والبرامج باستخدام MQL5. التطبيق العملي: قم بإنشاء سكربتات بسيطة، مثل مؤشرات أو استراتيجيات تداول، لتطبيق ما تعلمته بشكل عملي. قراءة الأكواد: اقرأ الأكواد التي كتبها مبرمجون آخرون وحاول فهم كيفية عملها. يمكنك العثور على هذه الأكواد في Market وCodeBase. المنتديات والمجتمعات: انضم إلى منتديات ومجموعات مخصصة لـ MQL5. يمكنك الاستفادة من خبرات الآخرين وطرح الأسئلة عند الحاجة.
-
في حالتك، يظهر أن هناك مشكلة في تطابق نوع الإدخال (Input Type) بين ما ترسله من جهة العميل (client) وما يتوقعه الخادم (server) في StatusInput. دعنا نحلل المشكلة خطوة بخطوة. المشكلة: تلقيت رمز حالة 400 مع خطأ يشير إلى أن StatusInput يجب أن يكون كائنًا (object)، ولكن يبدو أنك ترسل مصفوفة (array) من الكائنات. السبب: من تعريف نوع الإدخال في الخادم، يتضح أن StatusInput يحتاج إلى خصائص معينة مثل start, planning, ux, إلخ، وأيضًا يجب أن يكون لكل حالة project معرف مشروع (project: ID!). الحل: تأكد من شكل البيانات: يجب أن يكون شكل البيانات المرسلة في الاستعلام مطابقًا لما يتوقعه الخادم. بناءً على تعريف نوع الإدخال في الخادم، يجب أن تكون الحالة (status) عبارة عن كائن واحد يتبع بنية StatusInput. تحديث البيانات المرسلة: بدلاً من إرسال مصفوفة من الكائنات، أرسل كائنًا واحدًا من نوع StatusInput. الكود المعدل: تأكد من أن formState.status هو كائن واحد، وليس مصفوفة. على سبيل المثال: const handleSubmit = async (event) => { event.preventDefault(); try { const mutationRes = await createProject({ variables: { name: formState.name, description: formState.description, status: { start: formState.status.start, planning: formState.status.planning, ux: formState.status.ux, content: formState.status.content, code: formState.status.code, qa: formState.status.qa, launch: formState.status.launch, project: formState.status.projectId // تأكد من إرسال معرف المشروع إذا كان مطلوباً }, }, }); console.log(mutationRes); } catch (err) { console.log(err); } }; التعديلات الأخرى: تحديث الاستعلام: إذا كانت البيانات المرسلة عبارة عن كائن واحد، فتأكد من تحديث الاستعلام في mutation ليتناسب مع هذا التغيير. تأكد أيضًا من التحقق من StatusInput في الخادم وتطابقه مع ما ترسله من جهة العميل. إذا كان هناك تغيير في هيكل البيانات، تأكد من تحديث كل من العميل والخادم لينسجموا معًا. بهذا التحديث، يجب أن يكون لديك تواصل سلس بين العميل والخادم ويتوافق مع متطلبات GraphQL.
-
في React Router v6، تم تعديل الكثير من الأمور التي كانت موجودة في الإصدارات السابقة، بما في ذلك كيفية التعامل مع المعلمات في المسارات وتوجيه المكونات. إحدى المشكلات الشائعة هي أنه لا يتم إعادة تصيير المكونات بشكل صحيح عند تغيير عنوان URL. في الكود الذي قدمته، هناك بعض التعديلات التي يجب إجراؤها لتتناسب مع React Router v6 وتحديث مكوناتك. التعديلات المطلوبة تحديث دالة StaffWithId: في React Router v6، لم يعد هناك match مثلما كان في الإصدارات السابقة. بدلاً من ذلك، يمكنك استخدام useParams للحصول على المعلمات من URL. تحديث Redirect: Redirect لم يعد موجودًا في React Router v6. يمكنك استخدام مكون Navigate بدلاً منه. الكود المعدل import React, { Component } from 'react'; import Header from './HeaderComponent'; import Footer from './FooterComponent'; import { Routes, Route, Navigate, useParams } from 'react-router-dom'; import Stafflist from './StaffComponent'; import { STAFFS } from '../shared/staffs'; import StaffDetail from './StaffDetailComponent'; // تعريف مكون دالة للعرض عند وجود معلمة في URL const StaffWithId = ({ staff }) => { const { staffId } = useParams(); const staffDetails = staff.find((s) => s.id === parseInt(staffId, 10)); return staffDetails ? <StaffDetail staff={staffDetails} /> : <Navigate to="/staff" />; }; class Main extends Component { constructor(props) { super(props); this.state = { staffs: STAFFS }; } render() { return ( <div> <Header /> <Routes> <Route path='/staff' element={<Stafflist staffs={this.state.staffs} />} /> <Route path='/staff/:staffId' element={<StaffWithId staff={this.state.staffs} />} /> </Routes> <Footer /> </div> ); } } export default Main; الشرح : StaffWithId: تم تحويله إلى مكون دالة خارج Main وتحديثه لاستخدام useParams للحصول على staffId من URL. كما يتم استخدام Navigate لإعادة التوجيه في حالة عدم وجود تفاصيل الموظف. Routes: استخدم element مع المكونات بدلاً من component، حيث element يجب أن يكون عنصر JSX. بهذه الطريقة، سيتم إعادة تصيير المكون StaffDetail بشكل صحيح عند تغيير عنوان URL، وستعمل إعادة التوجيه بشكل صحيح. أتمنى أن يكون هذا قد ساعدك.
-
في أحدث إصدارات Electron (12 وما بعدها)، تم إيقاف دعم nodeIntegration في المتصفح بشكل افتراضي لأسباب تتعلق بالأمان. بدلاً من ذلك، يمكنك استخدام خاصية contextBridge وpreload لتمكين الاتصال بين واجهة المستخدم و Node.js بطريقة آمنة. إليك كيفية تحديث الكود الخاص بك: 1. تحديث ملف index.js لا تحتاج إلى استخدام nodeIntegration في webPreferences بعد الآن. بدلاً من ذلك، قم بإعداد preload وcontextBridge في ملف preload.js لتمكين الاتصال بين المتصفح و Node.js. const { app, BrowserWindow, ipcMain } = require('electron'); const path = require('path'); const CmdExec = require('child_process'); function createWindow() { mainWindow = new BrowserWindow({ width: 800, height: 600, webPreferences: { preload: path.join(__dirname, 'preload.js'), contextIsolation: true, enableRemoteModule: false } }); mainWindow.loadFile('index.html'); } app.on('ready', createWindow); ipcMain.on('video:submit', (event, path) => { CmdExec.exec("echo hello", (error, stdout, stderr) => { if (error) { console.error(`exec error: ${error}`); return; } console.log(`stdout: ${stdout}`); console.error(`stderr: ${stderr}`); }); }); 2. إنشاء ملف preload.js هذا الملف يتم تحميله قبل تحميل واجهة المستخدم، ويمكنك استخدامه لعرض وظائف Node.js للواجهة الأمامية بشكل آمن. const { contextBridge, ipcRenderer } = require('electron'); contextBridge.exposeInMainWorld('electron', { submitVideo: (path) => ipcRenderer.send('video:submit', path), }); 3. تحديث ملف index.html يمكنك الآن استخدام window.electron للوصول إلى واجهات Node.js من جافا سكريبت في المتصفح. <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Electron App</title> </head> <body> <h1>Hello Electron</h1> <button id="submitBtn">Submit Video</button> <script> document.getElementById('submitBtn').addEventListener('click', () => { window.electron.submitVideo('path/to/video'); }); </script> </body> </html>
-
الفرق بين mongod و mongosh mongod: هو خادم MongoDB، المسؤول عن إدارة قاعدة البيانات والتعامل مع الطلبات. mongosh: هو واجهة سطر الأوامر التفاعلية التي تستخدم للتفاعل مع قاعدة البيانات، مثل تنفيذ الاستعلامات وإدارة البيانات. مشكلة تنفيذ mongoexport الخطأ الذي تواجهه قد يكون بسبب محاولة تنفيذ أمر mongoexport داخل واجهة mongosh. يجب تنفيذ هذا الأمر في سطر الأوامر العادي (Terminal أو Command Prompt) وليس في واجهة mongosh. خطوات الحل افتح سطر الأوامر: على macOS/Linux: افتح Terminal. على Windows: افتح Command Prompt أو PowerShell. نفذ الأمر: mongoexport --uri "mongodb+srv://uname:password@cluster0.oyvrw.mongodb.net/dbname" --collection colname --type json --out cats.json تأكد من استبدال uname, password, dbname, و colname بالقيم الصحيحة.
-
عند استخدام Sequelize، هناك طريقتان أساسيتان لإنشاء وإدارة قاعدة البيانات والجداول: استخدام عمليات الترحيل (Migrations) والنماذج (Models) مع المزامنة (Sync). 1. عمليات الترحيل (Migrations): توفر عمليات الترحيل طريقة لإدارة تغييرات بنية قاعدة البيانات بمرور الوقت بطريقة يمكن تتبعها. يمكنك استخدام عمليات الترحيل لإنشاء الجداول وتعديلها وحذفها، بالإضافة إلى إضافة الفهارس والقيود. 2. النماذج (Models) مع المزامنة (Sync): النماذج تعبر عن الجداول في قاعدة البيانات. يمكن استخدام المزامنة (Sync) لإنشاء الجداول بناءً على تعريف النماذج. يمكن استخدام sequelize.sync() لإنشاء الجداول إذا لم تكن موجودة، أو تعديلها بناءً على تغييرات النماذج. مثال على استخدام Sequelize مع Migrations و Models 1. نموذج المستخدم (User model): 'use strict'; module.exports = (sequelize, DataTypes) => { const User = sequelize.define('User', { username: { type: DataTypes.STRING, allowNull: false, unique: true }, password: { type: DataTypes.STRING, allowNull: false }, email: { type: DataTypes.STRING, allowNull: false, unique: true } }, {}); User.associate = function(models) { User.hasMany(models.Post); }; return User; }; 2. نموذج المنشور (Post model): 'use strict'; module.exports = (sequelize, DataTypes) => { const Post = sequelize.define('Post', { title: { type: DataTypes.STRING, allowNull: false }, content: { type: DataTypes.TEXT, allowNull: false }, userId: { type: DataTypes.INTEGER, allowNull: false, references: { model: 'Users', key: 'id' } } }, {}); Post.associate = function(models) { Post.belongsTo(models.User); }; return Post; }; 3. عملية الترحيل لإنشاء جدول المستخدمين (User Migration): 'use strict'; module.exports = { up: (queryInterface, Sequelize) => { return queryInterface.createTable('Users', { id: { allowNull: false, autoIncrement: true, primaryKey: true, type: Sequelize.INTEGER }, username: { allowNull: false, type: Sequelize.STRING, unique: true }, password: { allowNull: false, type: Sequelize.STRING }, email: { allowNull: false, type: Sequelize.STRING, unique: true }, createdAt: { allowNull: false, type: Sequelize.DATE }, updatedAt: { allowNull: false, type: Sequelize.DATE } }); }, down: (queryInterface, Sequelize) => { return queryInterface.dropTable('Users'); } }; 4. عملية الترحيل لإنشاء جدول المنشورات (Post Migration): 'use strict'; module.exports = { up: (queryInterface, Sequelize) => { return queryInterface.createTable('Posts', { id: { allowNull: false, autoIncrement: true, primaryKey: true, type: Sequelize.INTEGER }, title: { allowNull: false, type: Sequelize.STRING }, content: { allowNull: false, type: Sequelize.TEXT }, userId: { allowNull: false, type: Sequelize.INTEGER, references: { model: 'Users', key: 'id' }, onUpdate: 'CASCADE', onDelete: 'SET NULL' }, createdAt: { allowNull: false, type: Sequelize.DATE }, updatedAt: { allowNull: false, type: Sequelize.DATE } }); }, down: (queryInterface, Sequelize) => { return queryInterface.dropTable('Posts'); } }; من الجيد تحديد القيود مثل عدم السماح بالقيم الفارغة (not null) والفريدة (unique) في النماذج (Models) بالإضافة إلى عمليات الترحيل (Migrations) لضمان التكامل بين قاعدة البيانات والنموذج. سأوضح كيفية القيام بذلك وكيفية إضافة المفتاح الخارجي يدويًا في الترحيلات. تحديد القيود في النموذج (Model) لتحديد أن اسم المستخدم والبريد الإلكتروني لا يمكن أن يكونا فارغين ويجب أن يكونا فريدين، يمكنك القيام بذلك في تعريف النموذج كما يلي: 'use strict'; module.exports = (sequelize, DataTypes) => { const User = sequelize.define('User', { username: { type: DataTypes.STRING, allowNull: false, unique: true }, password: { type: DataTypes.STRING, allowNull: false }, email: { type: DataTypes.STRING, allowNull: false, unique: true } }, {}); User.associate = function(models) { User.hasMany(models.Post); }; return User; }; إضافة المفتاح الخارجي يدويًا في الترحيل (Migration) يمكنك إضافة المفتاح الخارجي يدويًا عند إنشاء جدول المنشورات كما يلي: 'use strict'; module.exports = { up: (queryInterface, Sequelize) => { return queryInterface.createTable('Posts', { id: { allowNull: false, autoIncrement: true, primaryKey: true, type: Sequelize.INTEGER }, title: { allowNull: false, type: Sequelize.STRING }, content: { allowNull: false, type: Sequelize.TEXT }, userId: { allowNull: false, type: Sequelize.INTEGER, references: { model: 'Users', key: 'id' }, onUpdate: 'CASCADE', onDelete: 'SET NULL' }, createdAt: { allowNull: false, type: Sequelize.DATE }, updatedAt: { allowNull: false, type: Sequelize.DATE } }); }, down: (queryInterface, Sequelize) => { return queryInterface.dropTable('Posts'); } }; نموذج المنشور (Post Model) يمكنك أيضاً تحديد المفتاح الخارجي في نموذج المنشور: 'use strict'; module.exports = (sequelize, DataTypes) => { const Post = sequelize.define('Post', { title: { type: DataTypes.STRING, allowNull: false }, content: { type: DataTypes.TEXT, allowNull: false }, userId: { type: DataTypes.INTEGER, allowNull: false } }, {}); Post.associate = function(models) { Post.belongsTo(models.User); }; return Post; }; تشغيل الترحيلات لتنفيذ الترحيلات وإنشاء الجداول مع القيود والمفاتيح الخارجية، يمكنك استخدام الأوامر التالية: npx sequelize-cli db:migrate
-
لتعديل الأيقونة لتفتح في منتصف الصفحة أو لتحويلها إلى زر يظهر أسفل المقال، يمكن تعديل الأكواد التالية: لتظهر الأيقونة في منتصف الصفحة: يمكن تعديل CSS للأيقونة بحيث تظهر في منتصف الشاشة بدلاً من أسفلها. <style> .fb-livechat,.fb-widget{display:none} .ctrlq.fb-button,.ctrlq.fb-close{position:fixed;cursor:pointer} .ctrlq.fb-button{ z-index:1; background:url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjwhRE9DVFlQRSBzdmcgIFBVQkxJQyAnLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4nICAnaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkJz48c3ZnIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDEyOCAxMjgiIGhlaWdodD0iMTI4cHgiIGlkPSJMYXllcl8xIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCAxMjggMTI4IiB3aWR0aD0iMTI4cHgiIHhtbDpzcGFjZT0icHJlc2VydmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxnPjxyZWN0IGZpbGw9IiMwMDg0RkYiIGhlaWdodD0iMTI4IiB3aWR0aD0iMTI4Ii8+PC9nPjxwYXRoIGQ9Ik02NCwxNy41MzFjLTI1LjQwNSwwLTQ2LDE5LjI1OS00Niw0My4wMTVjMCwxMy41MTUsNi42NjUsMjUuNTc0LDE3LjA4OSwzMy40NnYxNi40NjIgIGwxNS42OTgtOC43MDdjNC4xODYsMS4xNzEsOC42MjEsMS44LDEzLjIxMywxLjhjMjUuNDA1LDAsNDYtMTkuMjU4LDQ2LTQzLjAxNUMxMTAsMzYuNzksODkuNDA1LDE3LjUzMSw2NCwxNy41MzF6IE02OC44NDUsNzUuMjE0ICBMNTYuOTQ3LDYyLjg1NUwzNC4wMzUsNzUuNTI0bDI1LjEyLTI2LjY1N2wxMS44OTgsMTIuMzU5bDIyLjkxLTEyLjY3TDY4Ljg0NSw3NS4yMTR6IiBmaWxsPSIjRkZGRkZGIiBpZD0iQnViYmxlX1NoYXBlIi8+PC9zdmc+) center no-repeat #0084ff; width:60px; height:60px; text-align:center; border:0; outline:0; border-radius:60px; box-shadow:0 1px 6px rgba(0,0,0,.06),0 2px 32px rgba(0,0,0,.16); transition:all .2s ease-in-out; top:50%; left:50%; transform:translate(-50%, -50%); } .ctrlq.fb-button:focus,.ctrlq.fb-button:hover{ transform:translate(-50%, -50%) scale(1.1); box-shadow:0 2px 8px rgba(0,0,0,.09),0 4px 40px rgba(0,0,0,.24) } .fb-widget{ background:#fff; z-index:2; position:fixed; width:360px; height:435px; overflow:hidden; opacity:0; top:50%; left:50%; transform:translate(-50%, -50%); border-radius:6px; box-shadow:0 5px 40px rgba(0,0,0,.16); } .fb-credit{ text-align:center; margin-top:8px } .fb-credit a{ transition:none; color:#bec2c9; font-family:Helvetica,Arial,sans-serif; font-size:12px; text-decoration:none; border:0; font-weight:400 } .ctrlq.fb-overlay{ z-index:0; position:fixed; height:100vh; width:100vw; transition:opacity .4s,visibility .4s; top:0; left:0; background:rgba(0,0,0,.05); display:none } .ctrlq.fb-close{ z-index:4; padding:0 6px; background:#365899; font-weight:700; font-size:11px; color:#fff; margin:8px; border-radius:3px } .ctrlq.fb-close::after{ content:'x'; font-family:sans-serif } </style> <div class="fb-livechat"> <div class="ctrlq fb-overlay"></div> <div class="fb-widget"> <div class="ctrlq fb-close"></div> <div class="fb-page" data-href="https://www.facebook.com/frienditech" data-tabs="messages" data-width="360" data-height="400" data-small-header="true" data-hide-cover="true" data-show-facepile="false"> <blockquote cite="https://www.facebook.com/frienditech" class="fb-xfbml-parse-ignore"></blockquote> </div> <div class="fb-credit"> <a href="https://www.facebook.com/frienditech" target="_blank">تحتاج الي مساعدة ؟! راسلنا علي فيسبوك</a> </div> <div id="fb-root"></div> </div> <a href="https://m.me/digital.inspiration" title="Send us a message on Facebook" class="ctrlq fb-button"></a> </div> <script src="https://connect.facebook.net/en_US/sdk.js#xfbml=1&version=v2.9"></script> <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script> <script> $(document).ready(function(){ var t={delay:125,overlay:$(".fb-overlay"),widget:$(".fb-widget"),button:$(".fb-button")}; setTimeout(function(){$("div.fb-livechat").fadeIn()},8*t.delay), $(".ctrlq").on("click",function(e){ e.preventDefault(), t.overlay.is(":visible")?(t.overlay.fadeOut(t.delay),t.widget.stop().animate({opacity:0},2*t.delay,function(){$(this).hide("slow"),t.button.show()})) :t.button.fadeOut("medium",function(){ t.widget.stop().show().animate({opacity:1},2*t.delay), t.overlay.fadeIn(t.delay) }) }) }); </script> لتحويل الأيقونة إلى زر يظهر أسفل المقال: إذا كنت ترغب في تحويل الأيقونة إلى زر يظهر أسفل المقال، يمكنك إضافة زر HTML وتعديل CSS وجافا سكريبت بحيث يظهر الزر أسفل المقال. <style> .fb-livechat,.fb-widget{display:none} .ctrlq.fb-button,.ctrlq.fb-close{position:fixed;cursor:pointer} .ctrlq.fb-button{ z-index:1; background:#0084ff; color:#fff; width:200px; height:60px; text-align:center; bottom:24px; left:50%; transform:translateX(-50%); border:0; outline:0; border-radius:30px; box-shadow:0 1px 6px rgba(0,0,0,.06),0 2px 32px rgba(0,0,0,.16); transition:all .2s ease-in-out; font-size:16px; line-height:60px; text-decoration:none; } .ctrlq.fb-button:focus,.ctrlq.fb-button:hover{ transform:translateX(-50%) scale(1.1); box-shadow:0 2px 8px rgba(0,0,0,.09),0 4px 40px rgba(0,0,0,.24) } .fb-widget{ background:#fff; z-index:2; position:fixed; width:360px; height:435px; overflow:hidden; opacity:0; bottom:80px; left:50%; transform:translateX(-50%); border-radius:6px; box-shadow:0 5px 40px rgba(0,0,0,.16); } .fb-credit{ text-align:center; margin-top:8px } .fb-credit a{ transition:none; color:#bec2c9; font-family:Helvetica,Arial,sans-serif; font-size:12px; text-decoration:none; border:0; font-weight:400 } .ctrlq.fb-overlay{ z-index:0; position:fixed; height:100vh; width:100vw; transition:opacity .4s,visibility .4s; top:0; left:0; background:rgba(0,0,0,.05); display:none } .ctrlq.fb-close{ z-index:4; padding:0 6px; background:#365899; font-weight:700; font-size:11px; color:#fff; margin:8px; border-radius:3px } .ctrlq.fb-close::after{ content:'x'; font-family:sans-serif } </style> <div class="fb-livechat"> <div class="ctrlq fb-overlay"></div> <div class="fb-widget"> <div class="ctrlq fb-close"></div> <div class="fb-page" data-href="https://www.facebook.com/frienditech" data-tabs="messages" data-width="360" data-height="400" data-small-header="true" data-hide-cover="true" data-show-facepile="false"> <blockquote cite="https://www.facebook.com/frienditech" class="fb-xfbml-parse-ignore"></blockquote> </div> <div class="fb-credit"> <a href="https://www.facebook.com/frienditech" target="_blank">تحتاج الي مساعدة ؟! راسلنا علي فيسبوك</a> </div> <div id="fb-root"></div> </div> <a href="https://m.me/digital.inspiration" title="Send us a message on Facebook" class="ctrlq fb-button">تواصل معنا</a> </div> <script src="https://connect.facebook.net/en_US/sdk.js#xfbml=1&version=v2.9"></script> <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script> <script> $(document).ready(function(){ var t={delay:125,overlay:$(".fb-overlay"),widget:$(".fb-widget"),button:$(".fb-button")}; setTimeout(function(){$("div.fb-livechat").fadeIn()},8*t.delay), $(".ctrlq").on("click",function(e){ e.preventDefault(), t.overlay.is(":visible")?(t.overlay.fadeOut(t.delay),t.widget.stop().animate({opacity:0},2*t.delay,function(){$(this).hide("slow"),t.button.show()})) :t.button.fadeOut("medium",function(){ t.widget.stop().show().animate({opacity:1},2*t.delay), t.overlay.fadeIn(t.delay) }) }) }); </script> بهذا الشكل، الأيقونة ستظهر في منتصف الصفحة، أو ستتحول إلى زر يظهر أسفل المقال.