דילוג לתוכן

עזרה הדדית - JavaScript

95 נושאים 796 פוסטים
  • בירור | nodejs תקינות פונקציה

    45
    0 הצבעות
    45 פוסטים
    259 צפיות
    ע
    טוב, נהיה לי בלאגן שלם בראש מהכללים האלו. יש לי המון (9 והיד עוד נטויה) פונקציות עזר למאגר מידע, ויש לי פונקציה שמקבלת רשימת פרמטרים + פונקציות לבדיקה שמתאימות להן ועוד פרמטר שאומר האם לבדוק שכל הפרמטרים ההכרחיים נמצאים או לא. אני ממש לא יודע מה צריך לבדוק בפונקציות למאגר מידע ומה לא (נראה לי לא אחראי ליצור ככה פונקציות, מצד שני אם בודקים תקינות בפונקציה עצמה, ישר מזדעקים שזה "סקריפט שברירי") או איך לחלק אותן, אשמח לעזרה. זה קובץ של הולידציה // סכימה לאימות פרמטרים של מודעה חדשה const adValidationSchema = { phone: {validate: isValidIsraeliPhone, required: true}, type: {validate: checkType, 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', '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: '=', 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("שגיאה בהוספת הדירוג"); } } תראו את הסקריפט הבא: <?php include 'db_config.php'; include 'validate_func.php'; $fields = []; $params = []; $types = ""; if (!isset($_REQUEST['phone']) || validate_phone($_REQUEST['phone']) === false) { logRequest('/tmp/request_log.txt', "error - ערך טלפון לא תקין"); respondError("שגיאה במספר טלפון"); } if (isset($_REQUEST['city'])) { if (!validate_city($_REQUEST['city'])) { respondError("city לא תקין"); } $fields[] = "city"; $params[] = $_REQUEST['city']; $types .= "s"; } // --- 2. betShemesh --- if (isset($_REQUEST['betShemesh']) && trim($_REQUEST['betShemesh']) !== "") { // חייב להופיע city קודם if (!isset($_REQUEST['city']) || $_REQUEST['city'] !== "בית שמש") { respondError("betShemesh חייב להגיע עם city"); } if (!validate_betShemesh($_REQUEST['betShemesh'])) { respondError("betShemesh לא תקין"); } $fields[] = "betShemesh"; $params[] = $_REQUEST['betShemesh']; $types .= "s"; } // --- 3. region --- if (isset($_REQUEST['region']) && trim($_REQUEST['region']) !== "") { // חייבים להיות city + betShemesh if (!isset($_REQUEST['city'], $_REQUEST['betShemesh'])) { respondError("region חייב להגיע עם city ו-betShemesh"); } if (!validate_region($_REQUEST['region'], $_REQUEST['city'], $_REQUEST['betShemesh'])) { respondError("region לא תקין"); } $fields[] = "region"; $params[] = $_REQUEST['region']; $types .= "s"; } if (isset($_REQUEST['buyorrent']) && validate_buyorrent($_REQUEST['buyorrent'])) { $fields[] = "buyorrent"; $params[] = $_REQUEST['buyorrent']; $types .= "s"; } if (isset($_REQUEST['room']) && validate_room($_REQUEST['room']) !== false) { $fields[] = "room"; $params[] = $_REQUEST['room']; $types .= "d"; } if (!empty($fields) && count($fields) === count($params)) { $fields[] = "phone"; $params[] = $_REQUEST['phone']; $types .= "s"; $placeholders = implode(", ", array_fill(0, count($fields), "?")); $sql = "INSERT INTO subcrib (" . implode(", ", $fields) . ") VALUES ($placeholders)"; } else { respondError("בקשה לא תקינה"); } // --- 3. התחברות למסד נתונים --- mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT); // הפעלת זריקת חריגות אוטומטית לשגיאות try { $conn = new mysqli($host, $username, $password, $dbname); $conn->set_charset("utf8mb4"); $stmt = $conn->prepare($sql); $stmt->bind_param($types, ...$params); if ($stmt->execute()) { logRequest('/tmp/request_log.txt', "ok - הרישום נוסף בהצלחה"); header('Content-Type: application/json; charset=utf-8'); echo json_encode([ "status" => "success", "message" => "הרישום נוסף בהצלחה" ], JSON_UNESCAPED_UNICODE); } else { logRequest('/tmp/request_log.txt', "error - שגיאה"); respondError("שגיאה בהוספת רישום"); } } catch (mysqli_sql_exception $e) { logRequest('/tmp/request_log.txt', "Database Connection Failed: " . $e->getMessage()); // למערכת חיצונית נחזיר הודעה גנרית respondError("שגיאה"); } finally { // חשוב לסגור את החיבורים בסוף if (isset($stmt)) { $stmt->close(); } $conn->close(); } קוד יפיפה, מובן, מוגן, אף מתכנת לא יכול להשתמש בפונקציה לא לפי הכללים וא"א לעקוף את ההגנות על המסד נתונים. מה יותר טוב מזה?
  • בירור | קובץ הוראות לצורת עבודה נכונה בjs

    11
    2 הצבעות
    11 פוסטים
    101 צפיות
    ע
    תסלחו לי שאני שואל אתכם ולא את הAI, אני פשוט לא סומך עליו... על אף אחד מהם...
  • 4 הצבעות
    1 פוסטים
    40 צפיות
    אין תגובות
  • 7 הצבעות
    1 פוסטים
    51 צפיות
    אין תגובות
  • עזרה | תגובות ל- [מדריך | המדריך למתכנת המתחיל | javascript |

    6
    0 הצבעות
    6 פוסטים
    115 צפיות
    מייבין במקצתמ
    @ישיביש מתזכר שוב: פתחתי כבר נושאים בעניין - אין מה להספים את הפורום סתם
  • 3 הצבעות
    1 פוסטים
    46 צפיות
    אין תגובות
  • 7 הצבעות
    1 פוסטים
    113 צפיות
    אין תגובות
  • בקשה | מחפש ממשק API על ימי חג ומועד בישראל

    4
    0 הצבעות
    4 פוסטים
    88 צפיות
    א
    @יום_שמח https://tchumim.com/topic/14325
  • באג | באג שקורה לי מידי פעם בטרמינל

    2
    0 הצבעות
    2 פוסטים
    36 צפיות
    H
    @טופטופיסט כתב בבאג | באג שקורה לי מידי פעם בטרמינל: יש למישהו מושג למה זה קורה שכשאני מקליד: npm המחשב מחליף את זה אוטומטי ל: npnpm ואז כשאני מתקן את זה חזרה מחזיר לי שגיאה שלא הקלדתי נכון את הפקודה pm : The term 'pm' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again. At line:1 char:1 + pm run dev + ~~ + CategoryInfo : ObjectNotFound: (pm:String) [], CommandNotFoundException + FullyQualifiedErrorId : CommandNotFoundException זה באג מוכר אתה לא צריך לתקן רק להתעלם מהאותיות שנוספו בהתחלה אני חושב שזה קורה רק כשבנתיב שבו פתוח הטרנימל יש תווים בעברית אבל אני לא בטוח שזו הסיבה
  • 0 הצבעות
    5 פוסטים
    81 צפיות
    צדיק תמיםצ
    @איש-אמת אני מנסה להבין באיזה נקודה הוא מסתבך בכל מקרה הפתרון הוא לעשות שלוחה לכל מספר טלפון וניתוב לשלוחה האישית יש המון הסברים בפורום ימות איך לעשות ניתוב לשלוחה אישית
  • בירור | הפעלת סקריפט

    8
    0 הצבעות
    8 פוסטים
    202 צפיות
    צ
    @פלמנמוני כתב בבירור | הפעלת סקריפט: @ציון-הלוא-תשאלי כתב בבירור | הפעלת סקריפט: לא הבנתי איפה לכתוב את זה? תכתוב פונקציה בקוד function doGet(e) { if (e.parameter && e.parameter.doPost) { var functionName = e.parameter.doPost; if (typeof this[functionName] === 'function') { try { var result = this[functionName](); return ContentService.createTextOutput(result); } catch (error) { return ContentService.createTextOutput("Error: " + error.toString()); } } } return ContentService.createTextOutput("Function not found or parameter missing."); } תודה
  • המלצה | קומפוננטת תאריך עברי חדשה | React Hebrew DatePicker

    9
    13 הצבעות
    9 פוסטים
    429 צפיות
    25802
    @oryadaniely התכוונת לתייג את @Es-Mes
  • 2 הצבעות
    7 פוסטים
    219 צפיות
    cfopuserC
    @hartkhartk תתחיל לפתח שאיפות Spoiler [image: 1761225261909-813a7aa5-a4eb-4bff-a2f1-463523270d45-image.png]
  • באג | תעלומה בכתיבת תוסף לכרום

    5
    1
    0 הצבעות
    5 פוסטים
    202 צפיות
    צדיק תמיםצ
    @פרה-אדומה כתב בבאג | תעלומה בכתיבת תוסף לכרום: גוגל סוגרים את חנות התוספים לכרום... מה?!
  • בעיה | 😪 apps script, מה אני מפספס⁉️

    35
    1 הצבעות
    35 פוסטים
    642 צפיות
    מ
    @ע-ה-דכו-ע כתב בבעיה | apps script, מה אני מפספס️: @מתעניין-בהכל כתב בבעיה | apps script, מה אני מפספס️: @ע-ה-דכו-ע כמובן, את זה עשיתי... Spoiler חשבתי שכתבת שעדיין לא פרסת את הסקריפט, עכשיו אני מבין שרק את הHTML הבעיה בדף שהוא לא שולח שום טוקן, למרות שבסקריפט מוגדר בדיקה של טוקן יייחודי. גם הסקריפטים שלא היה בהם בקשה של טוקן לא עבדו.
  • בקשה | למי יש חשבון מפתחים בתוספים של גוגל

    3
    0 הצבעות
    3 פוסטים
    88 צפיות
    פ
    שלח לי הודעה למייל
  • שיתוף | תוסף פשוט ליצירת תמונות אוטומטית בנייט קפה

    5
    2
    3 הצבעות
    5 פוסטים
    187 צפיות
    ר
    @MindBlaze עודכן בפוסט
  • שיתוף | תוסף פשוט לכרום לליוק בנייט קפה

    9
    1
    4 הצבעות
    9 פוסטים
    207 צפיות
    ר
    @MindBlaze יצרתי כזה דבר כבר
  • נושא זה נמחק!

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

    1
    0 הצבעות
    1 פוסטים
    88 צפיות
    אין תגובות