דילוג לתוכן

רשתות

3.7k נושאים 37.7k פוסטים

קטגוריות משנה


  • 219 נושאים
    2k פוסטים
    י
    @פלמנמוני ממש תודה רבה
  • 635 נושאים
    5k פוסטים
    ע
    טוב, נהיה לי בלאגן שלם בראש מהכללים האלו. יש לי המון (9 והיד עוד נטויה) פונקציות עזר למאגר מידע, ויש לי פונקציה שמקבלת רשימת פרמטרים + פונקציות לבדיקה שמתאימות להן ועוד פרמטר שאומר האם לבדוק שכל הפרמטרים ההכרחיים נמצאים או לא. אני ממש לא יודע מה צריך לבדוק בפונקציות למאגר מידע ומה לא (נראה לי לא אחראי ליצור ככה פונקציות, מצד שני אם בודקים תקינות בפונקציה עצמה, ישר מזדעקים שזה "סקריפט שברירי") או איך לחלק אותן, אשמח לעזרה. זה קובץ של הולידציה // סכימה לאימות פרמטרים של מודעה חדשה const adValidationSchema = { phone: {validate: isValidIsraeliPhone, required: true}, type: {validate: checkType, required: true}, profession: {validate: checkProfession, required: true}, min_max_price: {validate: checkNumber, required: false}, ringMode: {validate: checkRingMode, required: false}, area: {validate: checkArea, required: false}, city: {validate: checkCity, required: false}, path: {validate: (path) => typeof path === 'string' && path.trim() !== '', required: false} }; /** * פונקציות DB עם פורמט תשובה אחיד: { success, data, message?, error? } */ function successResponse(data, message = null) { return { success: true, data, message, error: null }; } function errorResponse(error) { return { success: false, data: null, message: null, error }; } // פונקציה לאימות פרמטרים של מודעה חדשה והכנת אובייקט נקי להוספה למסד הנתונים export function validateAdParams(params, checkNecessary = true) { // הגנה מפני קלט ריק if (!params || typeof params !== 'object') { return errorResponse("Invalid parameters input"); } let cleanParams = {}; // בדיקה שכל הפרמטרים שהועברו קיימים בסכימה ותקינים for (const [key, value] of Object.entries(params)) { if (!(key in adValidationSchema) ) {return errorResponse(`Unknown parameter: ${key}`);} const validateFunc = adValidationSchema[key].validate; if (!validateFunc(value)) { return errorResponse(`Invalid value for ${key}`); } cleanParams[key] = value; } if (checkNecessary) { // בדיקה שכל הפרמטרים החיוניים קיימים for (const [key, field] of Object.entries(adValidationSchema)) { if (field.required && !(key in params)) { return errorResponse(`Missing required parameter: ${key}`); } } } return successResponse(cleanParams); } אלו הפונקציות של מסד הנתונים: import argon from "argon2"; /** * פונקציות DB עם פורמט תשובה אחיד: { success, data, message?, error? } */ function successResponse(data, message = null) { return { success: true, data, message, error: null }; } function errorResponse(error) { return { success: false, data: null, message: null, error }; } /** * ולידציה בסיסית עבור שדות מודעות */ function validateAdData(tableName, data) { const allowedTables = ['ads', 'ad_cities']; if (!allowedTables.includes(tableName)) { throw new Error("שם טבלה לא חוקי"); } const records = Array.isArray(data) ? data : [data]; if (records.length === 0) return true; const validColumns = { ads: ['phone', 'type', 'profession', 'recording_path', 'min_max_price', 'ring_mode'], ad_cities: ['ad_id', 'city'] }; const columns = validColumns[tableName]; for (const record of records) { const invalidKeys = Object.keys(record).filter(k => !columns.includes(k)); if (invalidKeys.length > 0) { throw new Error(`עמודה לא חוקית בטבלה ${tableName}: ${invalidKeys.join(', ')}`); } } return true; } /** * הוספת רשומה אחת או מרובות לטבלה */ export async function insertRecord(trx, tableName, data) { try { validateAdData(tableName, data); const records = Array.isArray(data) ? data : [data]; if (records.length === 0) { return successResponse([], "אין רשומות להוספה"); } const result = await trx(tableName).insert(records); return successResponse( { insertedId: result[0], affectedRows: records.length }, "רשומות נוספו בהצלחה" ); } catch (err) { console.error("שגיאה בהכנסת נתונים:", err); return errorResponse(err.message || "שגיאה בהכנסת נתונים"); } } /** * פונקציה אוניברסלית לעדכון מודעה ו/או הערים שלה */ export async function updateAdWithCities(knex, adId, adData = {}, cities) { try { return await knex.transaction(async (trx) => { // 1️⃣ עדכון המודעה (אם נשלחו נתונים) if (adData && Object.keys(adData).length > 0) { validateAdData('ads', adData); const updatedRows = await trx('ads') .where({ id: adId }) .update(adData); if (updatedRows === 0) { // חובה לזרוק שגיאה כדי לבטל את הטרנזקציה ולא לשמור בטעות שינויים throw new Error("AD_NOT_FOUND"); } } // 2️⃣ טיפול בערים (רק אם נשלח מערך - גם אם הוא ריק) if (Array.isArray(cities)) { // מחיקת כל הערים הקיימות await trx('ad_cities').where({ ad_id: adId }).del(); const validCities = [...new Set(cities)].filter(c => typeof c === 'string' && c.trim()); // הכנסת הערים החדשות if (validCities.length > 0) { const records = validCities.map(city => ({ ad_id: adId, city: city.trim() })); validateAdData('ad_cities', records); await trx('ad_cities').insert(records); } } return successResponse({ adId }, "העדכון בוצע בהצלחה"); }); } catch (err) { console.error("שגיאה בעדכון מודעה:", err); // תפיסת השגיאה היזומה שלנו if (err.message === "AD_NOT_FOUND") { return errorResponse(`מודעה עם ID ${adId} לא נמצאה`); } return errorResponse(err.message || "שגיאה בעדכון מודעה או הערים"); } } /** * מחזיר את כל המודעות עם הערים הקשורות */ export async function getAllAds(knex) { try { const rows = await knex('ads as a') .leftJoin('ad_cities as c', 'a.id', 'c.ad_id') .select( 'a.*', knex.raw('COALESCE(JSON_ARRAYAGG(c.city), JSON_ARRAY()) as cities') ) .groupBy('a.id'); const formatted = rows.map(row => ({ ...row, cities: (typeof row.cities === 'string' ? JSON.parse(row.cities) : row.cities).filter(Boolean) })); return successResponse(formatted); } catch (err) { console.error("שגיאה בשליפת מודעות:", err); return errorResponse("שגיאה בשליפת מודעות"); } } /** * מחזיר מודעה לפי מזהה */ export async function getAdById(knex, adId) { try { const ad = await knex('ads as a') .leftJoin('ad_cities as c', 'a.id', 'c.ad_id') .select( 'a.*', knex.raw('COALESCE(JSON_ARRAYAGG(c.city), JSON_ARRAY()) as cities') ) .where('a.id', adId) .groupBy('a.id') .first(); if (!ad) return errorResponse(`מודעה עם ID ${adId} לא נמצאה`); const citiesArray = typeof ad.cities === 'string' ? JSON.parse(ad.cities) : ad.cities; return successResponse({ ...ad, cities: citiesArray.filter(Boolean) }); } catch (err) { console.error("שגיאה בשליפת מודעה:", err); return errorResponse("שגיאה בשליפת מודעה"); } } /** * הגדרת שדות מותרים לסינון והאופרטור שלהם */ const FILTER_CONFIG = { phone: '=', type: '=', profession: '=', ringMode: '=', city: '=' }; // פונקציה למציאת מודעות עבור משתמש עם אפשרות לסינון וסטטוס קריאה export async function findAdsForUser( knex, { phone, filters = {}, status = "all"/*, limit = 50, offset = 0 */} ) { if (!phone) return errorResponse("מספר טלפון חסר"); /* const cleanLimit = Math.min(Math.max(parseInt(limit) || 50, 1), 100); const cleanOffset = Math.max(parseInt(offset) || 0, 0);*/ const allowedStatus = ["all", "read", "unread"]; const cleanStatus = allowedStatus.includes(status) ? status : "all"; try { // 1️⃣ הפילטרים מגיעים ישירות מהבקשה const activeFilters = { ...filters }; // 2️⃣ שאילתת בסיס let baseQuery = knex("ads as M"); // 3️⃣ יישום פילטרים קבועים מהקוד Object.entries(FILTER_CONFIG).forEach(([field, operator]) => { const value = activeFilters[field]; if (value !== undefined && value !== null && value !== "") { baseQuery.where(`M.${field}`, operator, value); } }); // 4️⃣ סטטוס קריאה const readSubquery = knex("adsReads") .whereRaw("adsReads.ModhaId = M.Id") .andWhere("adsReads.Phone", phone); if (cleanStatus === "read") baseQuery.whereExists(readSubquery); else if (cleanStatus === "unread") baseQuery.whereNotExists(readSubquery); // 5️⃣ ספירה const countQuery = baseQuery.clone().countDistinct({ total: "M.Id" }).first(); // 6️⃣ שליפת נתונים עם is_read const dataQuery = baseQuery.clone() .select("M.*") .select( knex.raw( `EXISTS ( SELECT 1 FROM adsReads WHERE ModhaId = M.Id AND Phone = ? ) as is_read`, [phone] ) ) .orderBy("M.Id", "desc") /*.limit(cleanLimit) .offset(cleanOffset)*/; // 7️⃣ הרצה מקבילית const [totalResult, rows] = await Promise.all([countQuery, dataQuery]); const totalCount = parseInt(totalResult?.total || 0); return successResponse({ ads: rows.map(r => ({ ...r, is_read: !!r.is_read }))/*, pagination: { total: totalCount, limit: cleanLimit, offset: cleanOffset, hasMore: cleanOffset + rows.length < totalCount }*/ }); } catch (err) { console.error("[findAdsForUser] Error:", err); return errorResponse("שגיאה בשליפת המודעות"); } } /** * מחיקת מודעה (הערים יימחקו אוטומטית בזכות ON DELETE CASCADE ב-DB) * */ export async function deleteAd(knex, adId) { try { // מחיקת המודעה (שים לב: העמודה בטבלת ads נקראת id) const deletedRows = await knex('ads').where({ id: adId }).del(); // אם 0 שורות נמחקו, סימן שהמודעה לא הייתה קיימת if (deletedRows === 0) { return errorResponse(`מודעה עם ID ${adId} לא נמצאה`); } // החזרת תשובת הצלחה בפורמט האחיד return successResponse({ adId }, "המודעה נמחקה בהצלחה"); } catch (err) { console.error("שגיאה במחיקת מודעה:", err); return errorResponse("שגיאה פנימית במחיקת מודעה"); } } // מסמן מודעה כנקראה עבור משתמש מסוים export async function markAdAsRead(knex, phone, adId) { if (!phone || !adId) { return errorResponse("מספר טלפון או מזהה מודעה חסרים"); } try { await knex("adsReads").insert({ ModhaId: adId, Phone: phone }).onconflict(['ModhaId', 'Phone']).ignore(); return successResponse(null, "המודעה סומנה כנקראה"); } catch (err) { console.error("[markAdAsRead] Error:", err); return errorResponse("שגיאה בסימון המודעה כנקראה"); } } export async function findAdByFilter(knex, filters = {}) { if ( !filters || typeof filters !== 'object' || Array.isArray(filters) || Object.keys(filters).length === 0) { return errorResponse("לא סופקו פילטרים חוקיים"); } try { let { city, ...activeFilters } = filters; // 2️⃣ שאילתת בסיס let query = knex("ads as M"); // 3️⃣ יישום פילטרים קבועים מהקוד if (activeFilters.phone) { delete activeFilters.phone; } // טיפול בסינון לפי עיר (תמיכה גם במערך של ערים) if (city) { query.join("ad_cities as C", "C.adId", "M.id"); if (Array.isArray(city)) { query.whereIn("C.city", city); } else { query.where("C.city", city); } } Object.entries(FILTER_CONFIG).forEach(([field, operator]) => { const value = activeFilters[field]; if (value !== undefined && value !== null && value !== "") { // אם הערך הוא מערך, נשתמש ב-IN כדי לאפשר בחירה מרובה if (Array.isArray(value)) { query.whereIn(`M.${field}`, value); } else { query.where(`M.${field}`, operator, value); } } }); // ביצוע השאילתה - שימוש ב-distinct כדי למנוע כפילויות במקרה של JOIN const result = await query .select("M.*") .distinct(); return successResponse({ ads: result }); } catch (error) { console.error("[findAdByFilter] Error:", error); return errorResponse("שגיאה בשליפת המודעה לפי פילטרים"); } } export async function rate(knex, phone, phoneTorating, rating) { if (!phone || !phoneTorating || typeof rating !== 'number' || rating < 1 || rating > 5) { return errorResponse("מספר טלפון או דירוג לא חוקיים"); } try { const hashedPhone = await argon.hash(phone); await knex("rating").insert({ phone: hashedPhone, phoneTorating, rating }).onconflict(['phone', 'phoneTorating']).merge(); return successResponse(null, "הדירוג נוסף/עודכן בהצלחה"); } catch (err) { console.error("[rate] Error:", err); return errorResponse("שגיאה בהוספת הדירוג"); } }
  • מה הסינון המועדף עליך

    נעוץ נעול
    64
    24 הצבעות
    64 פוסטים
    8k צפיות
    ה
    @bbn אמר במה הסינון המועדף עליך: @148 לא צריך לשלם אתה מקבל כל חודש נקודות במתנה ובכל סינון אין כזאת אפשרות לשלוח דברים לבדיקה בלי הגבלה הרי זה עבודה שאתה מבקש מהם יותר וזה הגיוני שזה ככה זה עם הגבלה... יש רק מספר מצומצם של פניות לחודש....
  • שיתוף | פניה לגוגל בשביל אימות קולי ללא SMS

    103
    1
    15 הצבעות
    103 פוסטים
    3k צפיות
    נ
    @בינוני @אליהו-ה. יהא הרבה יותר תועלת אם כל אחד ישלח משוב לגוגל כמו שההוא שם כתב לכם. משוב לגוגל מגיע 100 % לגוגל, הפניות האלו הם בפורום משתמשים, ויתכן מאוד שאף גורם רישמי לא יראה או ידע מזה.
  • בקשת מידע | סטיק G5 מומלץ בעלי אקספרס

    לא נפתר
    10
    0 הצבעות
    10 פוסטים
    56 צפיות
    א
    @ההא-quot-ה תנסה MIFI0211AB אגב זה עלה כ470 לאחרונה ליי עלה כ300
  • 14 הצבעות
    37 פוסטים
    2k צפיות
    EBAE
    @צמצם כתב בלהורדה | להורדה - תוסף חדש לכרום להורדת כל הקבצים המצורפים בבת אחת: האם יש אפשרות שזה יוריד את הכל לתוך תיקייה אחת לא מכווצת (ולא כפי שעכשיו שמוריד את הכל כקבצים נפרדים) בוצע, ובוצעו עוד המון פיצ'רים חדשים, תמונות בספויילר. [חלק מהשיפורים ממתינים לבדיקה של גוגל ויעלו בקרוב כעדכון גרסה לתוסף בחנות]. Spoiler [image: 1772046070459-%D7%A6%D7%99%D7%9C%D7%95%D7%9D-%D7%9E%D7%A1%D7%9A1.png] [image: 1772046070568-%D7%A6%D7%99%D7%9C%D7%95%D7%9D-%D7%9E%D7%A1%D7%9A-2.png] לגבי שני השיפורים הנוספים שהצעת, זה בכוונה לא ככה. לבקשת הנהלת המעניינים שבשבילם יצרתי את זה. כי זה יבטל את הצפייה בפרסומות שלהם.
  • בירור | ספק הכי זול - סיבים נטפרי בבני ברק

    10
    0 הצבעות
    10 פוסטים
    61 צפיות
    ר
    יש ליוסי תקשורת סיבים בבני ברק
  • בירור | מעוניין לקנות סטיק מעלי אקספרס אשמח לעזרה

    לא נפתר
    15
    0 הצבעות
    15 פוסטים
    153 צפיות
    אהרן שובקסא
    @יאיר-י. זה שהמליץ לך, שלח לך את הקישור הזה? או שהוא רק אמר לך דגם ואתה שלחת קישור שמצאת?
  • בקשת מידע | איך אני שואב מנדרים פלוס נתוני קמפיין

    4
    0 הצבעות
    4 פוסטים
    63 צפיות
    צבי דורש ציוןצ
    @kasnik זה התיעוד https://matara.pro/nedarimplus/ApiDocumentation.html?v=1 זכור לי שנתוני קמפיין אין להם ממש, אולי אפשר לקמבן שם עם התרומות וכו'
  • בירור | מסך פתיחה של הנטסטיק

    9
    1
    0 הצבעות
    9 פוסטים
    57 צפיות
    א
    זה פותח בדפדפן ? בכל מקרה נסה לחפור בהגדרות הסטיק או לאפס הגדרות
  • בעיה | בעיה בAPI בחיבור לגוגל AI סטודיו

    22
    1 הצבעות
    22 פוסטים
    84 צפיות
    H
    ג'מיני באופן קבוע לא מעודכן במודלים המעודכנים אני תמיד צריך להראות לו בפירוש מה מעודכן אצלי ואז הוא כותב שהשגתי גישה למודלים עתידניים שאין לאף אחד גישה אליהם...
  • 40 הצבעות
    370 פוסטים
    15k צפיות
    א
    @פלמנמוני באמת הציעו פה רעיון נכון לכאורה המצב היום שיש כאלו שהגדירו מפתח לא מספיק מוצלח [כמוני כנראה..] ואזז בהודעות ארוכות יוצאים 'קרח מכאן ומכאן' כיון שהמפתח לא מספיק להמרת טקסט לדיבור ארוכה מאידך אין את הפתרון שעשית בימות [והפיצול לשתי הודעות] כיון שהמשתמש הכניס מפתח כך שבהודעות ארוכות שומעים "שגיאה" וכדומה אולי אפשר באמת לעשות שגם מי שהכניס מפתח מ"מ הודעות ארוכות יהיו דרך ימות?
  • בעיה | בעיה בהתקנת תוספים בגוגל

    לא נפתר
    16
    2
    0 הצבעות
    16 פוסטים
    139 צפיות
    א
    @אלדד-הדני-0 אני לא מבין בזה עשיתי מה שכתבת בכל אופן תודה רבה על העזרה
  • שיתוף | מייל לפלאפון הכשר

    1k
    25
    132 הצבעות
    1k פוסטים
    170k צפיות
    א
    @תחשוב-טוב כתב בשיתוף | מייל לפלאפון הכשר: לעשות את זה בימות המשיח ולא הבנתי מה צריך לעשות לנייד את המספר הרגיל שלי או את המספר של המערכת שפתחתי? פשוט המערכת חסומה נראה לי שזה עולה כסף, או שאתה פותח קו חדש לא כשר בחברה אחרת ועושה ניוד אליהם.
  • בירור | קיצור דרך למייל

    6
    0 הצבעות
    6 פוסטים
    21 צפיות
    א
    (הרעיון עם הצילומי מסך)
  • המלצה | המענה של גוגל על פניה בשביל אימות קולי ללא SMS

    4
    8 הצבעות
    4 פוסטים
    219 צפיות
    ב
    אני פשוט רוצה להכין נוסח מסודר (לשליחה למיילים) שאנשים יצטרפו לפנייה דרך "שליחת משוב לגוגל", אשמח לעזרה: האם עדיף דרך דיווח על בעיה או דרך הצעת רעיון? מישהו יודע לנסח את זה טוב? להכין טקסט שאנשים פשוט ידביקו שם בלי להתאמץ, למי שיש לו רעיון איך זה יהיה הכי קל ויעיל שישתף תודה רבה
  • בירור | על איזה אתר לבנית 'אפלקציות' הייתם ממליצים?

    28
    0 הצבעות
    28 פוסטים
    483 צפיות
    ב
    רפליט. יכול לבנות כל מה שצריך, כולל הכל, כולל כל דבר, מערכות, לא משנה מה. רק יש בקושי חינמי.... הכי טוב בפער לבניית פלטפורמות
  • 5 הצבעות
    6 פוסטים
    129 צפיות
    ה
    אפשר לעשות AI שיענה רק מתוך אתר פרוג כמו שיש באתר כל זכות?
  • בקשת מידע | שיבושים רבים בקו האינטרנט

    8
    2 הצבעות
    8 פוסטים
    94 צפיות
    א.מ.א
    @גמזו לא. לא נראה לי שיש ראוטר עם סים שספקיות האינטרנט בארץ תומכות בו...
  • 25 הצבעות
    5 פוסטים
    453 צפיות
    צדיק 0צ
    @למה-זה-תשאל-לשמי אתה רציני? להקפיץ נושא בין 5 שנים תקרא את כללי הפורום
  • 0 הצבעות
    6 פוסטים
    66 צפיות
    מתכנת חובבמ
    @דאנציג כתב בבקשת מידע | שימוש במכשיר נדרים פלוס כראוטר?!? האם יהיה מסונן?: לדעתי לשאול את זה כאן, זה כמו לשאול כאן כמה עולה חלב בחנות ליד הבית שלך 7.28 שקל לליטר רוצה לומר אין בזה סטנדרט? כלומר הסים שהם מביאים הוא לא זהה בכל המקרים ורק שבשנים האחרונות יש שינוי לגבי עניין הנקודה החמה?
  • עזרה | נטסטיק מומלץ אלי אקספרס

    43
    1 הצבעות
    43 פוסטים
    3k צפיות
    ע
    @אמיר כתב בעזרה | נטסטיק מומלץ אלי אקספרס: יצא עוד דגם רק שימו לב: ● סוללה: ללא סוללה