הבלוג של ינון פרק

טיפים קצרים וחדשות למתכנתים

היום למדתי: הסיפור עם bcrypt ו 72 בתים

04/02/2025

לא ברור איך העברתי חיים שלמים בלי לשים לב למגבלה הזאת, אבל עכשיו שגיליתי אותה אני משתף כאן כדי שאתם לא תיפלו באותו בור. לכוכב בסיפור של היום קוראים bcrypt והוא אלגוריתם Password Hashing ישן שעדיין נמצא בשימוש נרחב בהרבה מערכות. בין השאר ספריית Devise שהיא הספריה הפופולרית לניהול משתמשים ב Rails משתמשת בו.

בגדול bcrypt נחשב עדיין מאובטח למרות גילו המבוגר, אבל יש לו מגבלה מתועדת (ומסתבר שמאוד מפורסמת) - הוא לא יודע לערבל סיסמאות ארוכות יותר מ 72 בתים. מה זה אומר? הנה בקוד ריילס:

> u = User.new(name: 'a', email: 'ynon@gmail.com', password: '0' * 80 + '1')
> u.valid_password?('0' * 80 + '2')
 => true

כלומר בדיקת הסיסמאות היא רק על 72 הבתים הראשונים של הסיסמה. כשיש סיסמה ארוכה מ 72 בתים אנחנו מתעלמים מכל מה שבא אחרי.

לקחים? אם אפשר השתמשו ב Aragon2 במקום ב bcrypt, ובכל מקרה כשאתם בוחרים סיסמה לאתר שימו לב לא להתחיל את הסיסמה ב 72 תווים שקל לנחש ורק אז לשים את הסיסמה האמיתית. מי יודע אם הם לא משתמשים בטעות ב bcrypt וחותכים את מה שבא אחרי ה 72.

נ.ב. הפוסט הזה בנושא סופר מעניין וסוקר את המימוש של bcrypt בעוד שפות תכנות בדגש על איזה ספריות יסרבו לעבוד עם קלט שאורכו מעל 72 בתים לעומת איזה ספריות פשוט ימחקו את החלק שאחרי 72 כדי שבכל זאת דברים יעבדו: https://n0rdy.foo/posts/20250121/okta-bcrypt-lessons-for-better-apis/

טיפ פייתון: מחיקת שורה ראשונה מטקסט עם StringIO

03/02/2025

הפונקציה הבאה מקבלת מחרוזת טקסט ומחזירה את הטקסט בלי השורה הראשונה:

def remove_first_line(text):
    return '\n'.join(text.splitlines()[1:])

אבל אם בטעות נעביר ל None היא תזרוק Exception. בנוסף חיבור המחרוזות עם \n עלול להיות מבלבל כי מי שיגיע לקרוא את הקוד עלול לתהות מה קורה ב Windows שם תו סוף השורה הוא שונה. לפעמים זה רעיון טוב לעצור את התוכנית אם מקבלים None, אבל לפעמים אנחנו רוצים לבנות משהו יותר גמיש. דרך נוספת למחוק את השורה הראשונה היא הקלאס StringIO מתוך המחלקה io. הנה גירסה שנייה של אותה פונקציה:

def remove_first_line(text: str):
    f = StringIO(text)
    f.readline()
    return f.read()

הפונקציה יוצרת אוביקט שנראה כמו קובץ, אבל בעצם קורא מחרוזת מרובת שורות. בעזרת המתודות readline ו read אנחנו יכולים לקבל את כל השורות שאחרי הראשונה, בלי לפרק ולהדביק מחדש את השורות וגם אם מעבירים לפונקציה None הכל עדיין מסתדר והיא תחזיר מחרוזת ריקה.

לספר מה בנינו או לדעת מה נבנה?

02/02/2025

מה עדיף, לכתוב את הבדיקות לפני או אחרי הקוד? ואת דף הפרויקט בגיאהב? ואת התיעוד? והמדריך למשתמש?

בעבודה על פרויקט צד, רבים מעדיפים קודם לבנות את הפרויקט ואז לספר לעולם מה בנינו: אחרי כתיבת הקוד מנסחים הודעת קומיט, אחרי שהקוד מוכן כותבים בדיקות ותיעוד, אחרי שהפרויקט באוויר כותבים דף פרוקיט בגיטהאב ואחרי שהוצאנו גירסה חדשה כותבים ב Release Notes מה עשינו. התוצאה הרבה פעמים היא פרויקט "שצריך עוד תשומת לב" ומפתח שכבר התעייף מתחזוקה שלו. חלק גדול מהגישה הזאת נובע מפחד, פחד להתחייב.

אם נשים את הפחד בצד רגע נוכל לנסות גישה שונה:

  1. בפרויקט קוד פתוח - מתחילים מבניית דף גיטהאב לפרויקט ומסבירים מה יהיה בו, מה הארכיטקטורה, מה התהליכים המרכזיים במערכת, באיזה טכנולוגיות נשתמש וכן גם איך אפשר לתרום קוד לפרויקט. כבר בקומיטים הראשונים מייצרים את ה Action שמעביר את הפרויקט לפרודקשן.

  2. בפרויקט בתשלום - מתחילים מבניית דף נחיתה לפרויקט שמסביר מה אנחנו רוצים לבנות, כולל "צילומי מסך" ו Use Cases למוצר, והכי חשוב תיבה לאיסוף מיילים של אנשים שמתעניינים בפרויקט.

נכון, לבנות מוצר לפי Spec זה פחות מלהיב מ"לגלות תוך כדי תנועה", אבל מגדיל את הסיכויים שהפרויקט יראה אור יום בתור פרויקט שנהיה גאים בו.

צעדים ראשונים עם localstack

01/02/2025

לוקאל סטאק https://github.com/localstack/localstack הוא פרויקט שמטרתו הפעלת גירסה מקומית של AWS אצלכם על המחשב בתוך קונטיינר. הם מכסים עשרות סרביסים של AWS בחשבון החינמי, ויש גם חשבון בתשלום שכולל יותר יכולות לחלק מהסרביסים ויותר סרביסים. מה שאהבתי במיוחד בעבודה עם local stack זה שהקוד לא צריך להשתנות - הממשק שלהם זהה לממשק של AWS מלבד פקודת קונפיגורציה אחת שמגדירה את ה endpoint שלהם במקום של AWS.

בואו נראה איך להתקין ולעבוד עם סרביס S3 של לוקאל סטאק דרך שלושה סקריפטים.

המשך קריאה

ואל טאון הוא הפרויקט הכי מדליק שאתם לא מכירים

31/01/2025

אתמול פרסמתי פה סקריפט שכתבתי שמחפש מידע על פודקסטים ב val.town. כן הכרתי את val.town בעיקר בזכות הסלוגן עם החרוז שלהם וקצת זכרתי שזה אתר שיתוף קוד כמו קודפן רק לצד שרת, אבל המשחקים איתו בימים האחרונים השאירו אותי בפה פעור. ואל טאון הוא הפרויקט הכי מדליק שאתם לא מכירים, ואנסה להסביר למה:

המשך קריאה

היום למדתי: חיפוש פודקסטים ב podcast index

30/01/2025

ל val town יש את אחד ה AI-ים הכי חמודים שפגשתי - לא רק שהוא כותב את הקוד בשבילי, אפשר גם להריץ ולעשות Deploy בלחיצת כפתור, וכל זה בחינם. וכן אני יודע שגם רפליט מספקים שירות דומה ואפילו עם תמיכה בהרבה יותר שפות, אבל רפליט לאחרונה מרגיש מסורבל מדי.

בחזרה ל val town בשביל לשחק עם הבוט ביקשתי שיכתוב פונקציה לחיפוש פודקסטים ב podcast index. למרבה ההפתעה התוצאה עבדה בניסיון הראשון, כולל הטריק מה API שלהם עם ה sha1 למפתח. זה הקוד:

import crypto from "https://esm.sh/crypto-js";

export default async function searchPodcasts(query: string) {
  const apiKey = Deno.env.get("PODCAST_INDEX_API_KEY");
  const apiSecret = Deno.env.get("PODCAST_INDEX_API_SECRET");

  // Generate authentication headers as per Podcast Index API requirements
  const currentTime = Math.floor(Date.now() / 1000);
  const hashString = apiKey + apiSecret + currentTime;
  const hash = crypto.SHA1(hashString).toString();

  const headers = {
    "X-Auth-Key": apiKey,
    "X-Auth-Date": currentTime.toString(),
    "Authorization": hash,
    "User-Agent": "ValTownPodcastSearch/1.0",
  };

  try {
    const response = await fetch(`https://api.podcastindex.org/api/1.0/search/byterm?q=${encodeURIComponent(query)}`, {
      method: "GET",
      headers: headers,
    });

    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }

    const data = await response.json();

    return data.feeds.map(podcast => ({
      title: podcast.title,
      description: podcast.description,
      url: podcast.url,
      artwork: podcast.artwork,
    }));
  } catch (error) {
    console.error("Podcast search error:", error);
    return [];
  }
}

console.log(await searchPodcasts("syntax"));

ואם יש לכם 5 דקות תוכלו גם ליצור חשבון ב val town ולהריץ אותו ישר מהממשק שלהם בקישור:

https://www.val.town/v/ynonp/righteousLimeGalliform

רק שימו לב שצריך קודם ליצור את שני משתני הסביבה PODCAST_INDEX_API_KEY ו PODCAST_INDEX_API_SECRET דרך הממשק של val town לחשבון שלכם בקישור:

https://www.val.town/settings/environment-variables

ובשביל לקבל את המפתחות יש ליצור חשבון ב podcast index api:

https://api.podcastindex.org/

נ.ב. הממשק של podcast index די מדליק אפשר למצוא את כל המידע על פודקסט, רשימות הפרקים וגם לינקים לקבצי הקול של כל פרק.

חידת Vue - איך עובד Scoped Style

29/01/2025

נכתוב את הקוד הבא בקומפוננטת App.vue:

<script setup>
import Child from './Child.vue';
</script>

<template>
  <p class="red">Hello World from App</p>
  <Child />
</template>

<style scoped>
.red {
  color: red;
}
</style>

וזה Child.vue:

<script setup></script>

<template>
  <button class="red">
    Hello World from Child
  </button>
</template>

אפשר לראות את הדוגמה לייב בקישור הזה: https://tinyurl.com/4nff62kp

עכשיו לשאלות - למה הטקסט בכפתור מופיע באדום? ואיך אפשר "לנתק" אותו מהעיצוב של App כדי שלא יקבל את הגדרת האדום משם?

שימו לב שהגדרות העיצוב הן בבלוק styled scoped ושזו לא הבעיה שמתוארת כאן: https://www.tocode.co.il/blog/2021-10-watch-out-inherited-css, למרות שגם הבעיה בפוסט ההוא מעניינת.

ואם בכל זאת אני רוצה להעביר משתנה ריאקטיבי ב Vue לילדים?

28/01/2025

הקוד הבא ב Vue לא עובד, או לפחות לא עושה את מה שהתכוונתי. קומפוננטה עליונה:

<script setup>
import { ref } from 'vue'
import Child from './Child.vue';
const x = ref(10)
</script>

<template>
  <Child :x="x" />
</template>

וקומפוננטת Child:

<script setup lang="ts">
const {x} = defineProps<{x: any}>();

function inc() {
  x.value++;
}
</script>

<template>
  <p>x = {{x}}</p>
  <button @click="inc">+1</button>
</template>

נראה כאילו Child מקבל אוביקט ריאקטיבי x ומעלה את ערכו ב-1. בפועל העברת הפרמטר דרך props מעבירה את ה value של האוביקט הריאקטיבי, כלומר בתוך Child המשתנה x הוא מספר. למספר אין שדה .value ולכן הקוד נכשל.

המשך קריאה

תבנית פרויקט: nuxt, drizzle, auth0

27/01/2025

אתמול פירסמתי פה תבנית לפרויקט React שמשתמש ב Next.js ומכיל קומפוננטות צד שרת, משיכת מידע מבסיס נתונים וניהול משתמשים עם auth0. היום נראה את החלק השני של הפוסט ונבנה את אותה תבנית עבור nuxt ליישומי vue. הקוד כאן:

https://github.com/ynonp/nuxt-drizzle-auth0-demo

בואו נראה מה יש בריפו.

המשך קריאה

תבנית פרויקט: next, drizzle, auth0

26/01/2025

רוצים להתחיל פרויקט React חדש שיתחבר לבסיס נתונים ויכיל מנגנון ניהול משתמשים מובנה? ברור, מי לא ירצה. במקום לשבור את השיניים על חיבור כל הכלים תנו לי לחסוך לכם עבודה ולשתף תבנית פשוטה שכתבתי שפשוט עובדת, מבוססת על דוגמה של auth0. זה הגיטהאב:

https://github.com/ynonp/next-drizzle-demo

בואו נראה מה יש בפנים.

המשך קריאה