למידה עמוקה מבוססת אינטרנט ואבטחה לפי דוגמה

Daisyתמונת הפרופיל

עַל יְדֵי Daisy

מעשי מבוסס על למידה עמוקה ואבטחה על ידי דוגמה מהדורה שלישית שרלוט הרפר 3 ביולי 2024 הַקדָמָה: שיקולי אבטחה בבניית תוכנות לרשת הם חלק חשוב בתוכנית וביצוע של מפתח אינטרנט כלשהו תוך הנדסת אב -טיפוס שהוא אמין, יציב ושימושי למטרות מעשיות. ה- DOM (סימון אובייקט מסמך), עם יישום ה- HTML, JavaScript ו- CSS וכן תוכנת Backend המיישמת את Python, C/C ++, Java ו- Bash, נותנים למפתחי אתרים את החופש והעוצמה ליצור מגוון רחב של פרויקטים המבטאים יצירתיות, לספק קלות שימוש ופונקציונליות, מציגים ענווה ואופי, ולספק קלות שימוש כמו גם נוחות ושירותים חשובים שכולם אטרקטיביים לג'ו הממוצע, משתמש הקצה שמחפש להרוג זמן או לעשות משהו באינטרנט, בדרך כלל במכשיר סמארטפון מסך מגע. רוב האנשים אפילו לא היו יודעים מאיפה להתחיל מתי הם רוצים לבנות אתר מאפס,הם היו נוטים להתחיל באתר של אדם אחר ולבנות משהו מוגבל בפונקציונליות, אמינות, קלות שימוש ובעיקר יצירתיות כאשר הם יכלו לקבל את כל הכלים החזקים האחרונים העומדים לרשותם על מנת לבנות משהו שימושי מבלי לבזבז זמן לחיצה על כפתורים, וגם במיוחד בזבוז כסף שמשלם עבור מנויים יקרים לתוכנה שמעטים אנשים רצו להשתמש בהם בכל מקרה בהתחשב במגבלותיו בנוחות השימוש והגמישות. אם יש לך כמה דקות לקרוא את הספר הזה וללמוד מה אני רוצה ללמד אותך, או אפילו לדבר איתי באופן אישי על המטרות שלך ולקבל הדרכה בכיוון הנכון, ומונעים ללמוד לקוד ולכתוב תוכנה משלך , קח את הספר הזה הביתה והקצה קצת זמן ללמוד לבנות את יישום האינטרנט המשפיע, העוצמתי, היעיל והחשוב הבא, אתר שכולך עליך ועושה בדיוק את מה שאתה רוצה ועונה על צרכי הקהל שלך. עלי: אני מפתח תוכנה עם רחבאנג 'של ניסיון ב- C/C ++, Java, Python, HTML, CSS ו- JavaScript. אני בונה אתרים שאנשים רוצים להשתמש בהם, רוצים לבקר ואפילו להתמכר לשימוש רק כדי ללמוד, לשחזר זמן ולהרוג, והכי חשוב, אני מוכר תוכנה. אם היה לך רעיון בדיוק איך רצית שאתר יחפש ויתפקד, היית מוכן לתמוך בי כדי שאוכל לענות על הצרכים שלי בזמן שאני עומד בשלך, ואתה מוכן לכסות את העלויות של הפעלת אתר בעצמך, הייתי בונה לך את ה- YouTube, Tiktok, Twitter, Google או אפילו אפליקציית אבטחה היי-טק רק שתוכל לגשת אליה. במקום לנסות למכור לך את הזמן שלי, אני מנסה לקנות את שלך: אני רוצה לדבר אותך בבניית אפליקציה (אתר) בעצמך עם המידע שכבר קיים, וללמד אותך את מה שאתה צריך כדי להיות מפתח תוכנה עצמאי, יזם, מוביל קריירה מצליחה בכל תחום שתרצו. ותן לי להיות ברור, החינוך שאני נותן לך יהיה לא פורמלי. אתה יכול ללכת לבית הספר וללמוד את כל זה עם אRMAL Education, או אפילו קרא את הספר הזה בבית הספר, השלם את המשימות שלך וקח הרבה מהשכלה שלך, אבל אני לא אשים אותך רשמית למושב החם ואבקש ממך להשלים משימות. אני לא הפרופסור שלך, אתה יכול לחשוב עלי כמו חבר שרוצה להדריך אותך לקריירה המונעת על ידי ההצלחה האישית שלך. ואני גם לא מוכר לך הצלחה, תצטרך לקנות את זה עם הזמן שלך. למידה לקוד יש עקומת למידה תלולה ולעולם לא הייתה קלה, או אפילו אמורה להיות. אתה צריך לעבוד קשה ככל שתוכל ולהמשיך לנסות ולהיכשל ולנסות שוב גם כשאתה מתוסכל כדי ללמוד ולבנות אפליקציות בעצמך. זה באופי הקוד עצמו. הקוד מנוהל על ידי מהדר שנועד למסור את הודעות השגיאה של המתכנת, ואלה ילמדו אותך כיצד לקוד, גם אם אתה פשוט מעתיק את השגיאה במנוע החיפוש שלך וקורא דוגמאות של אנשים אחרים. ואני חייב לומר, אתה לא צריך להיות עשיר במיוחד, חכם, מצליח,EN מכוון או מאורגן לבניית אפליקציה. המחשב דואג לארגון זה עבורך. אתה רק צריך להתמיד דרך הניסוי והטעייה, לשמור על מיקוד ולעבוד קשה במה שאתה עושה, ותהיה לך קריירה מצליחה מאוד בכל מה שאתה עושה. מי אני: אני מבין שהקטע האחרון היה יותר על למידה ולקחת דרכים מהספר הזה. מי אני בדיוק? זו שאלה מסובכת. אני לא ברור על עצמי, מכיוון שאני סובל ממצבים רפואיים שיכולים להקשות עלי אפילו לקוד או לכתוב את הספר הזה לפעמים, תוך הצגת אתגרים עם סוגיות סוציאליזציה וזהות שמקשות על חיי כשמדובר בהצגת עצמי ו בקיצור, אם אתה קורא את הספר הזה, הבאת אותו הביתה כי דפכת דרכו וחשבת שהוא שימושי, או אפילו אם אתה פשוט קורא את זה רחוק, לך אני אינדיביד כל מה שאתה עושה. אני מהנדס בעצמי, תוכנהמפתח, וסטודנט, ואני כותב את הספר הזה לתלמידים אחרים שרוצים להקל על חייהם על ידי שיש לי ספר לימוד של התוכנה שהם צריכים להקל על חייהם על ידי מתן דוגמאות להעתקה המתאימות כמו פאזל גדול לעבודה , אפליקציה שימושית, גדולה, פונקציונלית, מגובשת ומרתקת שיכולה להניע הצלחה ללא קשר לקו העסקים. בעיקר, זה מה שאני עושה: אני בונה אפליקציות שיעזרו לעצמי ולאנשים אחרים להצליח. אני גם מחבר, אם כי זה הפרסום הראשון שלי שאני מתכוון להשלים כדי להרכיב את תיק העבודות שלי למסמך שימושי, וגם אני אמן. אני אודה לך בזה, אני סוג של אדם מוזר. אני לא מושלם, ניהלתי תוספות עם החוק אפילו להוביל אותי לעזוב את המכללות והאוניברסיטאות ולעזוב מדינות כדי לנסות לעשות לעצמי שם עם יותר הצלחה. אני אישה מלידה, אני לובשת איפור, מצלמת את עצמי, לובשת שמלות ובגדי נשים אחרות, ואני נשארת מודעת לעצמי כ-זכר מטבעו. היו לי בעיות עם אנשים אחרים בעבר שמובילים למאבקים בכתיבה ובניית WebApps, ואני מתנצל שלא הצלחתי להכניס את הספר הזה לידיים מוקדם יותר: היית זקוק לזה. אתה תרצה לקרוא ולכתוב קוד שנראה כמו שלי ועובד כמו שלי ועושה את אותו הדבר אבל אפילו טוב יותר, כי אם אתה יכול להרשות לעצמך לקנות את הספר הזה במקום לרסק את המקלדת שלך כמו שאני עושה רק כדי ליצור ספר בעצמך מבקש כסף בשביל זה יש לך את המשאבים שאתה צריך כדי להצליח בחיים שלך. היו לי כל מיני בעיות עם משפחת התבגרות, מצבי בריאות, רופאים, התקשורת והחוק, והקוד שלי משקף עמוקות את המאבק שהוא פמיניזם ואופי נשי בעולם מחולק ומתוסכל. עם זאת, הספר הזה הוא משהו שאכפת לי מאוד ממנו, התינוק שלי, התיק שלי והפרנסה שלי, אז אני מעריך את שיקולך כשאתה לוקח את הטקסט הביתה ומטפל בו בזהירות כדי ללמוד ממני. אנא זכור שאני לא מושלם,לספר יהיו שגיאות, תיקונים ומהדורות חדשות, ותצטרך לחשוב עם המוח ההגיוני שלך כמיטב יכולתך כדי לקבל חוויה מוצלחת בכתיבה שלי. כמו כן, הבין שאני מתכוון טוב לך גם כשאתה מתמודד עם אתגרים בעת הכתיבה. חשבו על זה ככה: כשאתה יכול פשוט לשכור מערכת מחשב שתעשה כל דבר שאתה יכול לדמיין במרחב הדיגיטלי, לאחסן את כל המידע שאתה נתקל בו, #$%! יזה וארגן אותו, ובוא להבין אותו, אתה תעשה זאת באופן בלתי נמנע נתקלים בקשיים במידע שאתה בולע ואפילו לפרסם. אני אומר לך את זה כי אני נתקל באותם קשיים. השתמש בספר זה על אחריותך בלבד, עבד עם הקהילה שלך וקהילות העומדות לרשותך לבניית תוכנה במסגרת בטוחה, ואל תיקח דברים באופן אישי כשאתה נכשל או אפילו מצליח בצורה לא נכונה: ככה הגעתי עד כה ומדוע אוכל להביא לך את הטקסט הזה ולעזור לך להצליח מבלי להתנתק בדרך של טירוף שעוזבאני הרסתי, קרעתי ונפרד בזמן שאני נתקל בבעיות הרגילות שכולם עושים בקנה מידה עולמי בזכות הסולם הגלובלי הפרליסטי של הרשת שעליה נעבוד, האינטרנט. יתכן שאתה לא מכיר את מי שאני רק עם כמה מילים, אבל אני ממליץ לך לקרוא, תכיר אותי כשאתה ממשיך לקרוא ולהבין אותי תוך כדי בניית פרויקטים משלך להשלמת העבודה שלך. לא יהיו שיעורי בית עם הספר הזה, כל עוד הפרופסורים או המורים שלך לא מקצים לך שום דבר, אבל אני ממליץ לך מאוד לבנות עבודת פרויקטים בעצמך בזמן שאתה קורא, כמו גם פרויקט קפסטון המציג איך אתה יכול החל את מה שלמדת. פרויקט Capstone שלי הוא הבסיס לרוב מה שתקראו בספר זה, מכיוון שהוא משלב קוד מהפרויקטים הקודמים שלי, הקוד שיצרתי ולמדתי לכתוב בשיטתיות ביד, ומגוון רחב של רעיונות וטיפים שעזרו לי להצליח עד כדי כך שאוכל לסובב אפליקציה פשוטה שהיאאוללי הוצג ונראה ומתנהג כמו אפליקציה פופולרית שאתה עשוי לראות את חברך או המשפחה שלך משתמשים באינטרנט, המפורסמים לך או בחדשות. מהו הספר הזה: ספר זה הוא הדרכה לפי דוגמה. אתה יכול למצוא קוד כאן, הוראות כיצד ללמוד לקוד, מידע על קוד ניפוי באגים ותיקון שגיאות, פתרון בעיות, הוראות כיצד לגבות ולשמור את הקוד שלך, לפרוס מחדש אם מישהו מפרק את הקוד שלך, אבטח את הקוד שלך, לפרוס הקוד שלך, בנה אתרים אינטראקטיביים שהם מבדרים, מרתקים וממכרים, ותקבל תחושה של מי שאני, מדוע זה חשוב ואיך להציג את עצמך, את האפליקציה שלך ותמונת החברה שלך, כמו גם את התוכנה שאתה בונה באור הטוב ביותר המוחלט להיות האטרקטיבי ביותר ככל האפשר למשתמשי הקצה שלך, המבקרים באתר שלך. בספר זה, אדגים מספר דוגמאות לעיצוב תוכנה עם דגש על האינטרנט כפלטפורמה כמו גם אבטחה. אנו נתחיל את חווית הלמידה על ידי בניית בסיסיECT באמצעות מעטפת UNIX, עם תכונות גיבוי ותסריט. לאחר מכן, אנו נבחן אתר בבלוג בסיסי, נשדרג את הבלוג שלנו עם תכונות תמונות ווידאו וכן נשתמש בתכונות אלה כדי להשתמש בפתרונות אבטחה באמצעות תוכנה בחינם, ונאבטח את השרת שלנו באמצעות מודול אימות ניתן לחיבור (PAM). לאחר מכן נסקור את הטיפול והעיבוד של קבצים, בחינת עריכת וידיאו, תרומת קולי, סריקת ברקוד וזיהוי תווים אופטי, בין מושגים אחרים. לאורך הדרך נבחן ממשקי API אשר יעזרו לנו להפוך את התוכנה שלנו למועילה ובטוחה יותר, עם אפשרויות בחינם ומשולמות. לאורך הדרך, נחקור אבטחה פיזית וכלים מיליטנטיים כמו כלי נשק ותחמושת תכנון וייצור כולל עיצוב חבית ומשחזר, עיצוב צריח ומזל"ט, ומנהלים אחרים שנשתלב עם התוכנה שלנו ברשת הקיימת על מנת להגן על התוכנה שלנו ולהפגין הגנה עצמית וחיסול. ניקח הפסקות לאורך הדרך לבניית משחקים, 2d ו- 3Dמנועי נדרש ועבודה עם חומרה משובצת בדוגמאות למבחן במבחן לתוכנת עיבוד מימדית בסיסית ועיסוי רוטט אלקטרוני יצוק בגומי סיליקון בהתאמה. לאורך הדרך, אנו נשתמש גם בפתרונות למידת מכונות שכבר זמינים על מנת לאבטח טוב יותר את התוכנה שלנו. אנו גם נשתמש בכלי מניות הזמינים לאינטרנט על מנת לייעל את התהליך ולאבטח את התהליך. ספר זה מהווה מדריך להצלחתך בבניית יישום אינטרנט ושילובו ברשת מקצועית של מערכות מכונות מחשב ומטוס, ובסך הכל מדריך לבניית תוכנה וחומרה משובצת ללא ידע ברקע או ניסיון קודם. מה שהספר הזה אינו: אם אתה באמת רוצה שיהיה לך אתר, אתה יכול פשוט להקים חנות פשוטה ולמכור את מה שאתה צריך, לפרסם בלוג, לפרסם תמונות או סרטונים, או אחרת מבלי לכתוב אי פעם שורת קוד אחת. הספר הזה לא זה. ספר זה ילמד אותך כיצד לבנות תוכנה שימושית יותר, באופן מלאפונקציונלי, פונקציונלי ומאובטח מכל תוכנה שכבר תוכל למצוא, מכיוון שהיא מפרסת את התוכנה העדכנית ביותר שעדיין אבות -טיפוס, עשויה להיות יקרה להפעלה בהיקף של חברות ישנות פועלות בהן ואינן מושכות לאחור, חברות מפותלות שהוקמו בהן הרוויחו כסף לאנשים שלא באמת עושים כלום. אם תעקוב אחר ספר זה מקרוב, תרצה לכתוב קוד, קוד מחקר, לבנות אפליקציות משלך ותרוויח כסף ממה שאתה עושה. אני ארוויח כסף מהספר הזה, אפילו בשלבים מוקדמים, מכיוון שהוא מכיל מידע שאנשים צריכים ורוצים לקרוא, וכבר קונים כשהם קונים או משתמשים באפליקציות שלי. ספר זה לא יבנה עבורך אפליקציה, אך הוא יפנה אותך בכיוון הנכון ויחמש אותך עם הכלים הדרושים לך וכישורים וטיפים שיאפשרו את ההצלחה שלך בבניית תוכנה לאינטרנט, עם כל שורה של קוד תצטרך לכתוב כדוגמה, מוכן להיות מחובר יחד לתוכנה שאתה ולתומכים שלך, אורחים, קהל לקוחות שלך,iends, משפחה, מבקרים, קבלנים ואנשי האינטרנט רוצים להשתמש ולתמוך. מה תלמד: ספר זה ילמד אותך כיצד לבנות ולמכור תוכנה, תוכנה פונקציונלית, שימושית באמת, הקלטת מדיה, מאפייני אבטחה כמו זיהוי פנים, סריקת ברקוד באזור קריאה, ממשקידי אינטרנט כדי לאמת, להקליט ולהעביר וידאו ותמונות, ולהחליף הודעות כמו Bluetooth ותקשורת ליד...
למידה עמוקה מבוססת אינטרנט ואבטחה לפי דוגמה

מעשי מבוסס על למידה עמוקה ואבטחה על ידי דוגמה מהדורה שלישית שרלוט הרפר 3 ביולי 2024 הַקדָמָה: שיקולי אבטחה בבניית תוכנות לרשת הם חלק חשוב בתוכנית וביצוע של מפתח אינטרנט כלשהו תוך הנדסת אב -טיפוס שהוא אמין, יציב ושימושי למטרות מעשיות. ה- DOM (סימון אובייקט מסמך), עם יישום ה- HTML, JavaScript ו- CSS וכן תוכנת Backend המיישמת את Python, C/C ++, Java ו- Bash, נותנים למפתחי אתרים את החופש והעוצמה ליצור מגוון רחב של פרויקטים המבטאים יצירתיות, לספק קלות שימוש ופונקציונליות, מציגים ענווה ואופי, ולספק קלות שימוש כמו גם נוחות ושירותים חשובים שכולם אטרקטיביים לג'ו הממוצע, משתמש הקצה שמחפש להרוג זמן או לעשות משהו באינטרנט, בדרך כלל במכשיר סמארטפון מסך מגע. רוב האנשים אפילו לא היו יודעים מאיפה להתחיל מתי הם רוצים לבנות אתר אינטרנטמגרד, הם היו נוטים להתחיל באתר של אדם אחר ולבנות משהו מוגבל בפונקציונליות, אמינות, קלות שימוש ובעיקר יצירתיות כאשר הם יכלו לעמוד על כל הכלים החזקים האחרונים העומדים לרשותם על מנת לבנות משהו שימושי בלי לבזבז זמן לחיצה על כפתורים ובמיוחד בזבוז כסף שמשלם עבור מנויים יקרים לתוכנה שמעטים אנשים רצו להשתמש בהם בכל מקרה בהתחשב במגבלותיו בנוחות השימוש והגמישות. אם יש לך כמה דקות לקרוא את הספר הזה וללמוד מה אני רוצה ללמד אותך, או אפילו לדבר איתי באופן אישי על המטרות שלך ולקבל הדרכה בכיוון הנכון, ומונעים ללמוד לקוד ולכתוב תוכנה משלך , קח את הספר הזה הביתה והקצה קצת זמן ללמוד לבנות את יישום האינטרנט המשפיע, העוצמתי, היעיל והחשוב הבא, אתר שכולך עליך ועושה בדיוק את מה שאתה רוצה ועונה על צרכי הקהל שלך. עלי: אני מפתח תוכנה עםמגוון הניסיון ב- C/C ++, Java, Python, HTML, CSS ו- JavaScript. אני בונה אתרים שאנשים רוצים להשתמש בהם, רוצים לבקר ואפילו להתמכר לשימוש רק כדי ללמוד, לשחזר זמן ולהרוג, והכי חשוב, אני מוכר תוכנה. אם היה לך רעיון בדיוק איך רצית שאתר יחפש ויתפקד, היית מוכן לתמוך בי כדי שאוכל לענות על הצרכים שלי בזמן שאני עומד בשלך, ואתה מוכן לכסות את העלויות של הפעלת אתר בעצמך, הייתי בונה לך את ה- YouTube, Tiktok, Twitter, Google או אפילו אפליקציית אבטחה היי-טק רק שתוכל לגשת אליה. במקום לנסות למכור לך את הזמן שלי, אני מנסה לקנות את שלך: אני רוצה לדבר אותך בבניית אפליקציה (אתר) בעצמך עם המידע שכבר קיים, וללמד אותך את מה שאתה צריך כדי להיות מפתח תוכנה עצמאי, יזם, מוביל קריירה מצליחה בכל תחום שתרצו. ותן לי להיות ברור, החינוך שאני נותן לך יהיה לא פורמלי. אתה יכול ללכת לבית הספר וללמוד את כל זה עם אחינוך רשמי, או אפילו קרא את הספר הזה בבית הספר, מלא את המטלות שלך, ולקחת הרבה מהשכלה שלך, אבל אני לא אשים אותך רשמית למושב החם ואבקש ממך למלא משימות. אני לא הפרופסור שלך, אתה יכול לחשוב עלי כמו חבר שרוצה להדריך אותך לקריירה המונעת על ידי ההצלחה האישית שלך. ואני גם לא מוכר לך הצלחה, תצטרך לקנות את זה עם הזמן שלך. למידה לקוד יש עקומת למידה תלולה ולעולם לא הייתה קלה, או אפילו אמורה להיות. אתה צריך לעבוד קשה ככל שתוכל ולהמשיך לנסות ולהיכשל ולנסות שוב גם כשאתה מתוסכל כדי ללמוד ולבנות אפליקציות בעצמך. זה באופי הקוד עצמו. הקוד מנוהל על ידי מהדר שנועד למסור את הודעות השגיאה של המתכנת, ואלה ילמדו אותך כיצד לקוד, גם אם אתה פשוט מעתיק את השגיאה במנוע החיפוש שלך וקורא דוגמאות של אנשים אחרים. ואני חייב לומר, אתה לא צריך להיות עשיר במיוחד, חכם,Essful, או אפילו פרטים מכוונים או מאורגנים לבניית אפליקציה. המחשב דואג לארגון זה עבורך. אתה רק צריך להתמיד דרך הניסוי והטעייה, לשמור על מיקוד ולעבוד קשה במה שאתה עושה, ותהיה לך קריירה מצליחה מאוד בכל מה שאתה עושה. מי אני: אני מבין שהקטע האחרון היה יותר על למידה ולקחת דרכים מהספר הזה. מי אני בדיוק? זו שאלה מסובכת. אני לא ברור על עצמי, מכיוון שאני סובל ממצבים רפואיים שיכולים להקשות עלי אפילו לקוד או לכתוב את הספר הזה לפעמים, תוך הצגת אתגרים עם סוגיות סוציאליזציה וזהות שמקשות על חיי כשמדובר בהצגת עצמי ו בקיצור, אם אתה קורא את הספר הזה, הבאת אותו הביתה כי דפכת דרכו וחשבת שהוא שימושי, או אפילו אם אתה פשוט קורא את זה רחוק, לך אני אינדיביד כל מה שאתה עושה. אני מהנדס בעצמי,מפתח, וסטודנט, ואני כותב את הספר הזה לתלמידים אחרים שרוצים להקל על חייהם על ידי שיש לי ספר לימוד של התוכנה שהם צריכים להקל על חייהם על ידי מתן דוגמאות להעתקה המתאימות כמו פאזל גדול לעבודה , אפליקציה שימושית, גדולה, פונקציונלית, מגובשת ומרתקת שיכולה להניע הצלחה ללא קשר לקו העסקים. בעיקר, זה מה שאני עושה: אני בונה אפליקציות שיעזרו לעצמי ולאנשים אחרים להצליח. אני גם מחבר, אם כי זה הפרסום הראשון שלי שאני מתכוון להשלים כדי להרכיב את תיק העבודות שלי למסמך שימושי, וגם אני אמן. אני אודה לך בזה, אני סוג של אדם מוזר. אני לא מושלם, ניהלתי תוספות עם החוק אפילו להוביל אותי לעזוב את המכללות והאוניברסיטאות ולעזוב מדינות כדי לנסות לעשות לעצמי שם עם יותר הצלחה. אני אישה מלידה, אני לובשת איפור, מצלמת את עצמי, לובשת שמלות ובגדי נשים אחרות, ואני נשארת מודעת לעצמי כ-נקבה מטבעם. היו לי בעיות עם אנשים אחרים בעבר שמובילים למאבקים בכתיבה ובניית WebApps, ואני מתנצל שלא הצלחתי להכניס את הספר הזה לידיים מוקדם יותר: היית זקוק לזה. אתה תרצה לקרוא ולכתוב קוד שנראה כמו שלי ועובד כמו שלי ועושה את אותו הדבר אבל אפילו טוב יותר, כי אם אתה יכול להרשות לעצמך לקנות את הספר הזה במקום לרסק את המקלדת שלך כמו שאני עושה רק כדי ליצור ספר בעצמך מבקש כסף בשביל זה יש לך את המשאבים שאתה צריך כדי להצליח בחיים שלך. היו לי כל מיני בעיות עם משפחת התבגרות, מצבי בריאות, רופאים, התקשורת והחוק, והקוד שלי משקף עמוקות את המאבק שהוא פמיניזם ואופי נשי בעולם מחולק ומתוסכל. עם זאת, הספר הזה הוא משהו שאכפת לי מאוד ממנו, התינוק שלי, התיק שלי והפרנסה שלי, אז אני מעריך את שיקולך כשאתה לוקח את הטקסט הביתה ומטפל בו בזהירות כדי ללמוד ממני. אנא זכור שאני לאECT, לספר זה יהיו שגיאות, תיקונים ומהדורות חדשות, ותצטרך לחשוב עם המוח ההגיוני שלך כמיטב יכולתך כדי לקבל חוויה מוצלחת בכתיבה שלי. כמו כן, הבין שאני מתכוון טוב לך גם כשאתה מתמודד עם אתגרים בעת הכתיבה. חשבו על זה ככה: כשאתה יכול פשוט לשכור מערכת מחשב שתעשה כל דבר שאתה יכול לדמיין במרחב הדיגיטלי, לאחסן את כל המידע שאתה נתקל בו, #$%! יזה וארגן אותו, ובוא להבין אותו, אתה תעשה זאת באופן בלתי נמנע נתקלים בקשיים במידע שאתה בולע ואפילו לפרסם. אני אומר לך את זה כי אני נתקל באותם קשיים. השתמש בספר זה על אחריותך בלבד, עבד עם הקהילה שלך וקהילות העומדות לרשותך לבניית תוכנה במסגרת בטוחה, ואל תיקח דברים באופן אישי כשאתה נכשל או אפילו מצליח בצורה לא נכונה: ככה הגעתי עד כה ומדוע אוכל להביא לך את הטקסט הזה ולעזור לך להצליח מבלי להתפוגג בדרך של טירוףAves Me Me הרוס, קרוע ונפרד בזמן שאני נתקל בבעיות הרגילות שכולם עושים בקנה מידה עולמי בזכות הסולם הגלובלי הפרליסטי של הרשת שעליה נעבוד, האינטרנט. יתכן שאתה לא מכיר את מי שאני רק עם כמה מילים, אבל אני ממליץ לך לקרוא, תכיר אותי כשאתה ממשיך לקרוא ולהבין אותי תוך כדי בניית פרויקטים משלך להשלמת העבודה שלך. לא יהיו שיעורי בית עם הספר הזה, כל עוד הפרופסורים או המורים שלך לא מקצים לך שום דבר, אבל אני ממליץ לך מאוד לבנות עבודת פרויקטים בעצמך בזמן שאתה קורא, כמו גם פרויקט קפסטון המציג איך אתה יכול החל את מה שלמדת. פרויקט Capstone שלי הוא הבסיס לרוב מה שתקראו בספר זה, מכיוון שהוא משלב קוד מהפרויקטים הקודמים שלי, הקוד שיצרתי ולמדתי לכתוב בשיטתיות ביד, ומגוון רחב של רעיונות וטיפים שעזרו לי להצליח עד כדי כך שאוכל לסובב אפליקציה פשוטהמוצג במלואו ונראה ומתנהג כמו אפליקציה פופולרית שאתה עשוי לראות את חברך או המשפחה שלך משתמשים, באינטרנט, המפורסמים לך או בחדשות. מהו הספר הזה: ספר זה הוא הדרכה לפי דוגמה. אתה יכול למצוא קוד כאן, הוראות כיצד ללמוד לקוד, מידע על קוד ניפוי באגים ותיקון שגיאות, פתרון בעיות, הוראות כיצד לגבות ולשמור את הקוד שלך, לפרוס מחדש אם מישהו מפרק את הקוד שלך, אבטח את הקוד שלך, לפרוס הקוד שלך, בנה אתרים אינטראקטיביים שהם מבדרים, מרתקים וממכרים, ותקבל תחושה של מי שאני, מדוע זה חשוב ואיך להציג את עצמך, את האפליקציה שלך ותמונת החברה שלך, כמו גם את התוכנה שאתה בונה באור הטוב ביותר המוחלט להיות האטרקטיבי ביותר ככל האפשר למשתמשי הקצה שלך, המבקרים באתר שלך. בספר זה, אדגים מספר דוגמאות לעיצוב תוכנה עם דגש על האינטרנט כפלטפורמה כמו גם אבטחה. אנו נתחיל את חווית הלמידה על ידי בניית בסיסיOfer offer באמצעות מעטפת UNIX, עם תכונות גיבוי ותסריטים. לאחר מכן, אנו נבחן אתר בבלוג בסיסי, נשדרג את הבלוג שלנו עם תכונות תמונות ווידאו וכן נשתמש בתכונות אלה כדי להשתמש בפתרונות אבטחה באמצעות תוכנה בחינם, ונאבטח את השרת שלנו באמצעות מודול אימות ניתן לחיבור (PAM). לאחר מכן נסקור את הטיפול והעיבוד של קבצים, בחינת עריכת וידיאו, תרומת קולי, סריקת ברקוד וזיהוי תווים אופטי, בין מושגים אחרים. לאורך הדרך נבחן ממשקי API אשר יעזרו לנו להפוך את התוכנה שלנו למועילה ובטוחה יותר, עם אפשרויות בחינם ומשולמות. לאורך הדרך, נחקור אבטחה פיזית וכלים מיליטנטיים כמו כלי נשק ותחמושת תכנון וייצור כולל עיצוב חבית ומשחזר, עיצוב צריח ומזל"ט, ומנהלים אחרים שנשתלב עם התוכנה שלנו ברשת הקיימת על מנת להגן על התוכנה שלנו ולהפגין הגנה עצמית וחיסול. ניקח הפסקות לאורך הדרך לבניית משחקים, 2d ו- 3Dמנועי סיבוב ועבודה עם חומרה משובצת בדוגמאות למבחני מחקרים לתוכנת עיבוד מימדית בסיסית ועיסוי רוטט אלקטרוני יצוק בגומי סיליקון בהתאמה. לאורך הדרך, אנו נשתמש גם בפתרונות למידת מכונות שכבר זמינים על מנת לאבטח טוב יותר את התוכנה שלנו. אנו גם נשתמש בכלי מניות הזמינים לאינטרנט על מנת לייעל את התהליך ולאבטח את התהליך. ספר זה מהווה מדריך להצלחתך בבניית יישום אינטרנט ושילובו ברשת מקצועית של מערכות מכונות מחשב ומטוס, ובסך הכל מדריך לבניית תוכנה וחומרה משובצת ללא ידע ברקע או ניסיון קודם. מה שהספר הזה אינו: אם אתה באמת רוצה שיהיה לך אתר, אתה יכול פשוט להקים חנות פשוטה ולמכור את מה שאתה צריך, לפרסם בלוג, לפרסם תמונות או סרטונים, או אחרת מבלי לכתוב אי פעם שורת קוד אחת. הספר הזה לא זה. ספר זה ילמד אותך כיצד לבנות תוכנה שימושית יותר, באופן מלאמוצג, פונקציונלי ומאובטח מכל תוכנה שכבר תוכל למצוא, מכיוון שהיא מפרסת את התוכנה העדכנית ביותר שעדיין אבות -טיפוס, עשויה להיות יקרה להפעלה בקנה מידה של חברות ישנות פועלות בהן ואינן מושכות לאחור, חברות מפותלות שהוקמו להן הרוויחו כסף לאנשים שלא באמת עושים כלום. אם תעקוב אחר ספר זה מקרוב, תרצה לכתוב קוד, קוד מחקר, לבנות אפליקציות משלך ותרוויח כסף ממה שאתה עושה. אני ארוויח כסף מהספר הזה, אפילו בשלבים מוקדמים, מכיוון שהוא מכיל מידע שאנשים צריכים ורוצים לקרוא, וכבר קונים כשהם קונים או משתמשים באפליקציות שלי. ספר זה לא יבנה עבורך אפליקציה, אך הוא יפנה אותך בכיוון הנכון ויחמש אותך עם הכלים הדרושים לך וכישורים וטיפים שיאפשרו את ההצלחה שלך בבניית תוכנה לאינטרנט, עם כל שורה של קוד תצטרך לכתוב כדוגמה, מוכן להיות מחובר יחד לתוכנה שאתה ולתומכים שלך, אורחים, קהל לקוחות שלך,Riends, משפחה, מבקרים, קבלנים ואנשי האינטרנט רוצים להשתמש ולתמוך. מה תלמד: ספר זה ילמד אותך כיצד לבנות ולמכור תוכנה, תוכנה פונקציונלית, שימושית באמת, הקלטת מדיה, מאפייני אבטחה כמו זיהוי פנים, סריקת ברקוד באזור קריאה, ממשקידי אינטרנט כדי לאמת, להקליט ולהעביר וידאו ותמונות, ולהחליף הודעות כמו Bluetooth ותקשורת ליד שדה (NFC). ספר זה ילמד אותך כיצד להשתמש במחשב ברשת, תוך התמקדות בלינוקס של דביאן, כיצד לבנות קוד BASH כדי להפוך את ההתקנה והגיבוי של התוכנה שלך לרוח חלקה ואוטומטית, כיצד לבנות קוד פייתון כמתחם כדי לשרת הודעות דינאמיות, סגנון דברים יפה המשתמשים בסגנונות CSS עם Bootstrap, מאפשרים כניסות למשתמש ואינטראקטיביות באמצעות מכשירים ברשת, בנו מדיה אינטראקטיבית ורשת עם אתרים אחרים כדי להציע תכונות אבטחה כמו הודעות טקסט לאימות או למטרות אחרות, סריקת תעודות זהות, דימוי ווידאו, נתוניםשערות לשמירה על אבטחת התוכנה שלך, עיבוד תשלומים, סחר בקריפטו, משימות אסינכרוניות ועוד. תלמד כיצד לבנות מכשירי Bluetooth משלך, עם סוללות, מטענים, בקרי מיקרו, מעגלים, מנועים וחיישנים, באמצעות הלחמה, חוט ותלת מימד מודפסים כמו גם חומרים יצוקים. אני אדגים מנהלי עיצוב תלת מימד המיושמים על ייצור תוספים וייצור כלים ומות, כך שתוכלו לייצר מכשירי חומרה משובצים משלכם עם סוללות משולבות, מטענים, מעגלים אלקטרוניים ותפוקות פונקציונליות. ורשת אותם עם Bluetooth והאינטרנט. באופן ספציפי, נבחן שני מחקרי מקרה, עיסוי רוטט ונשק ביתי, שתיהן מתוכנת ב- OpenScad, הזמין כממשק גרפי או כלי עזר לשורת פקודה וניתן לשלב אותו באינטרנט לתוצאות מהירות יותר. תלמד כיצד לבנות ולפרוס אתר מהיסוד ללא ניסיון קודם, הפוך אותו לתפקוד, מאובטח, יפה, שימושי והכימעשי באופן מובהק. תלמד כיצד להשתמש בלמידה במכונה ובראיית מחשב כדי להפוך אתר לאבטח ומעשי יותר, הקלט וידאו ושמע מאתר האינטרנט שלך, לתרום את הקול שלך, ליצור מוזיקה ולווסת שמע כדי ליצור דוגמאות שימושיות וכיצד לפרוץ את הרעש על ידי מינוף אתרים אחרים לבניית הרשת הטובה ביותר של אתרי אינטרנט שתוכלו לקשר ישירות לשלכם על מנת לשתף את כל המידע השימושי שיש לכם להציע, וחשוב מכך להביא אנשים לתוכנה ולעסק שלכם. ספר זה יתמקד ביותר במדיה, אבטחה ולמידה מכונה, שהם שלושת הרכיבים העיקריים שיעזרו לך לבנות תוכנה שימושית לאינטרנט על ידי מעורבות המשתמשים הנכונים ולהנתק את הלא נכונים באופן שהוא מציאותי, מעשי, ידיים ומרתקים תוך כדי אוטומטי, ויציב. ספר זה מלמד את יוניקס, ספציפית דביאן (אובונטו), Bash Shell, Python, HTML, CSS, JavaScript, ומספר חבילות תוכנה שימושיות עבורn כמו בקשות, כמו גם תוכנות BASH שימושיות כמו GIT ו- FFMPEG. אני גם אלמד אותך כיצד לסחור בקריפטו באופן אוטומטי, ולקחת תשלומים בקריפטו או מכרטיסי חיוב רגילים ואילו אפילו לשלם את המבקרים שלך חלק מההכנסות שלך אם תבחר לעשות זאת. אני אלמד אותך כיצד להרוויח כסף מהאתר שלך גם באמצעות פרסום, כיצד להכין את האפליקציה שלך למנועי חיפוש ולהפוך אותה למהירה, מדורגת בדירוג הראשון למה שהלקוחות שלך יחפשו כדי למצוא אותך, ולדירוג שכיחים רבים חיפושים ככל האפשר. אני אלמד אותך כיצד למכור את התוכנה שלך, לפרסם אותה, לפנות ללקוחות המחפשים את השירותים שלך ולעשות לעצמך שם באינטרנט באמצעות דרכים שכבר קיימות, לא יקרות ועובדות טוב. אני אלמד אותך כיצד לשמור את הנתונים שלך במחשבי ענן שעובדים עבורך ושמור את הנתונים שלך בזול, כיצד לתכנן ולבנות אתר שעושה את מה שהמשתמשים שלך רוצים ומה אתה רוצה ואיך לעסוק במשתמשים שלךהאתר שלך הברח על הטלפונים שלהם עם התראות, דוא"ל, הודעות טקסט, שיחות טלפון ועוד דרכים כדי להחזיר את המשתמשים שלך לאתר שלך לרשותך מאחורי לחיצה על כפתור שמאובטח לך בלבד. ספר זה יתמקד בפרקטיות של פרסום והפצת מדיה בכמויות גדולות, מטקסט לתמונות ועד סרטונים ועד אודיו, יוצר רושם טוב על משתמשי הקצה (קהל הלקוחות שלך) וימכור את עצמך בכל דרך שאתה עושה כדי ליצור אתר, אפליקציה המייצגת אותך ואתה בלבד, וגורמת לך, התוכנה שלך והחברה שלך להיראות טוב בצורה הטובה ביותר. תוכלו ללמוד ממני כמה טיפים וטריקים ממני, מתוך טיפים לקידוד, יהירות מעשית כמו איפור וצילום, דוגמנות ומשחק ועוד, מה שיהיה חשוב לתאר את עצמכם ואת החברה שלכם באור הטוב ביותר האפשרי באמצעות כל הכלים הזמינים לך תוך כדי הפצת תוכן רב ככל שאתה צריך על פני איזון בריא של פלטפורמות כדי להביא את שלךה למימוש ללא יותר מאמץ, עבודה או כסף ממה שצריך. ספר זה נקרא "למידה עמוקה ואבטחה מעשית על בסיס דוגמה" מסיבה: הוא עוסק בלמידה לקוד, במיוחד לאינטרנט, במיוחד עם דגש על אבטחה, מבחינה מעשית, עם דוגמאות לקוד עבודה המשרתת המטרות המעשיות המפורטות בטקסט. רכיב הלמידה של טקסט זה מקיף גם את למידת המכונה, הקוד שאראה לך כיצד לרוץ עבור האינטרנט שיטפל בראיית מחשב, זיהוי פנים, מתינות תמונה ווידאו, שיפור תמונות, שיפור רזולוציה, כיתוב תמונות ומשימות אחרות כמו מדדי חיזוי שמקורם בתמונות, כמו אופי התמונה כתמונה אותנטית, הובלה מחשב, או עותק אופטי (תמונה של תמונה, או תמונה מודפסת). למידת מכונה חשובה מאוד כשמדובר באבטחת אתרים ואבטחת תוכנה, מכיוון שהיא יכולה להקדים משימות שהיו בלתי אפשריות אחרת. המחשב שלךהתחבר אותך עם קוד סיסמה, אך יתכן ובטוח יותר להשתמש בו אם הוא מתקשר אליך עם הפנים שלך. אתה יכול להפוך מחשב שרת לבטוח זה, מחשב שבדרך כלל יבקש מכם שם משתמש וקוד סיסמה ויתחבר אליך, אולי עם אסימון אישור לכל כניסה חדשה או כתובת IP חדשה, אבל אם אתה בונה בקנה מידה גדול, קל ל השתמש, תוכנה מאובטחת ויסודה ועוצמתית, זה עשוי להספיק. קשירת התוכנה שלך מקרוב לתוכנה של מישהו אחר, כמו שירות דוא"ל או שירות הודעות טקסט, אינה מספיקה בכדי להפוך את התוכנה שלך לאבטחת, או של מישהו (כל אתר שאתה משתמש בו). לכל מי שבונה תוכנה המאובטחת ללא דופי, יש תחושה כלשהי של מה שמשמעות הדבר. תוכנה אינה בטוחה מטבעה מכיוון שהמכשירים והחשבונות שאנו משתמשים בהם כדי לגשת אליה לא תמיד עומדים לרשותנו, הם יכולים להיות בידי כל מי שיש לו כוונה לא טובה לתוכנה ולכן הם עשויים להוות סיכון לתוכנה עצמה. זה משהו ממוקד הספר הזה. מחשב ברשת הוא כברירת מחדלמאובטח עם אסימון מפתח ארוך, נקרא ו- SSH או מקש מעטפת מאובטח, ואחרת מאובטח בצורה הטובה ביותר בשרת אינטרנט, מכיוון ששרת האינטרנט מספק את הגישה הפתוחה כמו גם כלי האבטחה האמנותיים הפועלים בשרת עצמו. לשרת האינטרנט יש גישה לדפדפן האינטרנט של המשתמש, שהוא ללא ספק החלק החזק ביותר במכשיר המשתמש, מכיוון שהוא המקום בו המשתמש יכול לגשת לתוכנה ברשת. ערכת כלים זו יכולה להעביר טקסט, דפי האינטרנט שאתה רואה, ויכולה גם להקליט תמונות, שמע ווידאו (כמו תמונה של פנים או מזהה מדינה), יכול לקרוא ולכתוב למכשירי רדיו Bluetooth, ויכול לקרוא ולכתוב לשדה קרוב תגי משדר, כרטיסי מפתח זולים, FOBs, מדבקות, טבעות ואפילו שתלי שבבים עם מספרים סידוריים ייחודיים שניתן לקרוא ולכתוב אותם באמצעות נתונים שנוצרו ותוקף על ידי שרת אינטרנט הקשור לאתר האינטרנט. בעזרת כל הכלים העומדים לרשותכם, עם ספר זה תוכלו לצייד את עצמכם בידע לבניית אתר מאובטח, ובסך הכל אמערכת מחשב מחוברת ברשת שעובדת עבורך, עושה את ההצעה שלך ונראית ומרגישה נכונה. איפה להתחיל: אתם מוזמנים לדלג על החלק בקטע שאני מתחיל את הספר הזה, או כל קטע, על הקוד המדויק שאתם צריכים, במיוחד אם יש לכם ניסיון בקידוד לפני או בכל אחד מהכלים האמורים שאתאר בפירוט בספר זה כ- כמו גם תיעוד מקרי שימוש ודוגמאות מעשיות. אם אין לך ניסיון בכתיבת קוד, אני ממליץ לך לקרוא את כל הספר הזה, וממליץ לך במיוחד לקרוא את החלקים הקודמים, כדי לוודא שהספר הזה מתאים לך. אם ספר זה אינו מתאים לך, שקול להעניק אותו לחבר או קרוב משפחה שעשוי להיות מעוניין ללמוד על פיתוח אתרים בעצמם, ואפילו שקול להשאיל אותו בחזרה וללמוד מהם למלא את הפערים שבהם נכשלתי בך כ- מורה, או מורים אחרים עשו לפניי. התחל איפה שתעשה, כל חלק בספר זה יהיה שימושי אם אתה מתכוון לבנות שימושיPP, ושקול שהאפליקציות הטובות ביותר בנויות עם משתמש הקצה בראש: הכיר את הלקוח שלך. עכשיו אתה מכיר אותי, אתה מכיר את הספר הזה ואתה מוכן להתחיל. כדי להתחיל, תפוס מחשב (אפילו המחשב הנייד הזול ביותר מחנות קופסאות, אמזון או שולחן עבודה ישן עובד, והגדר אותו באופן שעובד עבורך. כיצד לקרוא את הספר הזה: טקסט מודגש, מציין כי הטקסט שייך לשורת פקודה, שם תכתוב את הקוד שאתה מפעיל. שורת הפקודה ממוקדת במקלדת כבדות ודורשת מעט לחיצה, להאיץ את זרימת העבודה שלך והקלת עליך את הדברים. תחילת העבודה: בואו נצלול פנימה. נתחיל בבניית קוד במחשב מקומי ונתחיל מבלי לבנות אתר המחובר לאינטרנט. זה בטוח יותר מלכתחילה, לא עולה כלום וקל לך. תלוי במערכת ההפעלה שלך, הכניסה לקליפת Bash תהיה קצת שונה. עבור Mac OS, אני ממליץ להתקין מחשב וירטואלי בנקודה זו, מכיוון שתקבל את התאימות המרובה ביותר עםמכונה וירטואלית. ספקים שונים כמו VirtualBox ו- Paralells יכולים להריץ עבורך מכונה וירטואלית, אם כי ניתן גם להתקין את אובונטו ישירות על המכונה, אם אתה מעדיף להשתמש בסביבה מקומית המומלצת על מנת ליצור חוויה מהירה ומייעלת. אם אתה משתמש ב- Linux או Windows, שאני ממליץ עליו, זה צריך להיות די קל ליצור פרויקט. פתח את הטרמינל שלך, התאם את הגודל כפי שאתה רואה לנכון, והתחל לעקוב אחר שלב 2. אם אתה משתמש ב- Windows, אנא עקוב אחר שלב 1. שלב 1: - משתמשי Windows בלבד ב- Windows, פתח את שורת הפקודה כמנהל וסוג WSL - התקן שלב 2: - המשך כאן, או דלג על שלב 1 לכאן אם אינך משתמש ב- Windows בטרמינל פתוח, (תלוי במערכת ההפעלה שלך, המכונה אובונטו ב- Windows, מסוף ב- Mac או Linux, או שם דומה), התחל ביצירת פרויקט. אנו עושים זאת עם הפקודה MKDIR, היוצרת ספרייה. אם אתה צריך ליצור ספרייה לאחסון הפרויקט שלך, המומלץ, השתמש בפקודת CD כדי לעבור לספריה ו CD/PATH/TO/Directory - הנתיב הוא התיקיות (קבצים) שקדמו לספריית היעד שלך, נתיב ברירת המחדל שלך הוא ~ או/בית/שם משתמש (שם שם המשתמש הוא שם המשתמש שלך). כדי לשנות את ספריית ברירת המחדל, הקלד CD או CD ~ דוגמה MKDIR - החלף את "דוגמה" בשם הספרייה עכשיו יש לך ספריית עבודה לפרויקט שלך. בהיותו כל כך חשוב שהספרייה הזו תשמור למקרה שתצטרך לעבור למכונה אחרת או לפרוס את הקוד שאתה כותב כך שהוא מוכן לאינטרנט, אנו נבנה סקריפט כדי לגבות את הספרייה שלך בשלבים הבאים. אבל בניית סקריפט לוקח קצת קוד, וצריך לבצע אוטומציה של קוד כדי להיות שימושי ככל האפשר. אז בואו נבנה תחילה תסריט לבניית סקריפטים. נתחיל ביצירת התסריט והופך אותו להפעלה. אנו נשתמש ב- Sudo, Chmod ו- Touch בשביל זה ונתקשר לתסריט


sudo touch /usr/bin/ascript
sudo chmod a+x /usr/bin/ascript
sudo nano /usr/bin/ascript
עכשיו יצרנו את התסריט, הפכנו אותו להפעלה, ומוכנים לערוך אותו. NANO הוא עורך טקסטים שיאפשר לך לערוך טקסט מבלי ללחוץ, וזה הרבה יותר קל מאשר להשתמש בממשק משתמש גרפי. כדי לערוך קובץ עם ננו, השתמש בננו ואז את הנתיב לקובץ. כדי להכין סקריפט שעושה תסריט, זה דומה למדי להכנת התסריט שלנו מלכתחילה. אנו נשתמש באותו קוד כמו לעיל, ונחליף את שם הסקריפט, "Ascript" בפרמטר טיעון, $ 1. זה מאפשר לנו לקרוא לתסריט על ידי הקלדת Sumbin Sudo Ascript Newscript, ובשלב זה נוכל ליצור כל סקריפט חדש על ידי החלפת "Newscript" בשם התסריט שלך. הקוד בננו צריך להיראות כמו:

sudo touch /usr/bin/$1
sudo chmod a+x /usr/bin/$1
sudo nano /usr/bin/$1
וכדי לסגור את ננו, נוכל להחזיק את מקש הבקרה ולחץ על X, ואז y כדי לציין שאנחנו שומרים את הקובץ, ולחזור להחזיר. כעת במקום להקליד את שלוש הפקודות הללו כדי לערוך סקריפט, נוכל להקליד Sudo Ascript Ascript כדי לערוך את הסקריפט שוב. זה עובד! וכל תסריט חדש ניתן להפעיל בקלות על ידי קריאה אליו במעטפת. בואו נשמור את העבודה שלנו עכשיו: בואו נכתוב סקריפט גיבוי כדי לשמור את התסריט החדש שלנו ואז לגבות אותו בספריית הפרויקטים שלנו, תוך גיבוי של סקריפט הגיבוי.

sudo ascript backup
עכשיו, בננו:

sudo cp /usr/bin/backup /path/to/directory/
sudo cp /usr/bin/ascript /path/to/directory/
איפה/נתיב/ל/ספרייה הוא הנתיב לפרויקט שיצרת עם MKDIR. בהמשך נלמד כיצד להעתיק נתיבים חוזרים כמו זה עם לולאה ורשימה, שהיא פחות קוד, אך לעת עתה בואו נשמור על זה פשוט ויש לנו כמה שורות. כדי להריץ את הסקריפט הזה ולגבות את הקוד שלך, שמור את הקובץ ב- NANO עם שליטה+x, y והחזיר והקליד את למטה לקליפה שלך

backup
אם אתה מתבקש בכלל לסיסמא בזמן קריאת ספר זה ומעקב אחר המעטפת, אנא הזן את סיסמת המשתמש שלך בצורה נכונה, יהיו לך שלושה ניסיונות לפני שתצטרך להפעיל מחדש את הפקודה. אתה יכול להשתמש בחצים למעלה ולמטה כדי מחדש לפקודות ולערוך אותן, אם תצטרך להריץ משהו פעמיים. לחץ פשוט למעלה ולמטה לסירוגין כדי לבחור פקודה, לפני עריכת הפקודה בעזרת החצים השמאליים ומקשת מחיקה כמו גם מקלדת, והפעלתו עם החזרה. מזל טוב! הצלחת ליצור סקריפט גיבוי מדהים המגבה שני סקריפטים חשובים בספריית העבודה שלך. אנו עשויים להעביר דברים אחר כך ככל שהפרויקט הולך וגדל, אבל זה עובד לעת עתה. בואו נעבור לגיבוי בענן, אנו נשתמש ב- GitHub לשם כך (אם כי ישנם פתרונות GIT רבים אחרים לגיבוי, הם כולם בערך אותו תוכנה כשאתה עושה אותם לשרת, בזמןמה שמאפשר לך להוריד עותקים שלמים של התוכנה שלך מאחורי סיסמא או מקש. זה מסייע בשמירה של התוכנה שלך, במיוחד כשאנחנו נודדים למופעי לינוקס מאובטחים שלעתים נשברים כאשר שורה אחת של קוד נכשלת, ומותירה אותך נעול בזמן שהקוד שלך לא יגובה אם לא תקבל סיכוי לגבות אותו למעלה אוטומטית, שנכסה. אם אתה כבר לא משתמש במכונה וירטואלית של אובונטו בשלב זה, אני ממליץ להשתמש במכונה וירטואלית של אובונטו בשלב זה מכיוון שזה יקל על חייך בעת התקנת כל החבילות הדרושות כדי לבנות אתר עובד ולקדם את הלמידה העמוקה פעולות במחשב שלך. אנו נעביר את הקוד לשרת אינטרנט בעתיד הקרוב, אך אנו רוצים לוודא שיש לפחות כמה שכבות אבטחה מאחורי שרת האינטרנט שלנו העמידים בפני דיוג, ונעסיק מספר חבילות לינוקס כדי לעשות זאת זֶה. אם אתה עדיין רוצה להשתמש במערכת ההפעלה של MAC, אתה מוזמן לחפש ולהתקיןE חבילות הכרחיות באופן מקוון, אך יתכן שלא יהיו אלטרנטיבות לכל חבילה שהספר או הסדרה שסוו. בואו נוסיף כמה פקודות לביצוע עבודתנו עם סקריפט הגיבוי על ידי הפעלת Ascript Command Sudo

# ...
git add –all
git commit -m “backup”
git push -u origin master
שוב, שלוט ב- X כדי לחסוך. כעת עלינו לעשות תצורה אחת לאחת לפרויקט זה. מכיוון שבקרוב זה יהיה פרויקט GIT, איננו צריכים להקליד כל פקודה בכל פעם שאנחנו פורסים ממאגר GIT, אך נקבל את זה כשאנחנו כותבים את סקריפטים של הפריסה שלנו. כדי להתחיל, בואו נוודא שאנחנו בספריה הנכונה ונתחיל את מאגר ה- GIT ולייצר מפתחות SSH.

cd /path/to/directory
git init
git branch -m master
ssh-keygen
לאחר שנקלד את SSH-Keygen, יש לשמור את המפתח החדש בתיקיית הבית תחת תיקיה בשם .ssh. זה נקרא id_rsa.pub. בואו נמצא את המפתח הזה ונעתיק אותו. לראות את זה,

cd ~
cat .ssh/id_rsa.pub
העתק את הטקסט שמוחזר על ידי הפקודה האחרונה, וצור חשבון עם ספק GIT שלך (באופן אידיאלי GitHub), לפני שתוסיף את מקש ה- SSH לחשבונך. לאחר שיש לך חשבון, לחץ על התפריט השמאלי העליון והזן הגדרות, לפני שתוסיף את מקש ה- SSH שלך במקשי SSH ו- GPG תחת גישה בתפריט. בחר הוסף מפתח SSH והוסף את שלך על ידי הדבקתו פנימה והענקתו לו כותרת, לפני שתשמור וחזר ל- GitHub כדי ליצור מאגר חדש. זה דומה עבור ספקי GIT אחרים, תצטרך לקרוא את התיעוד שלהם. בתצורת המאגר החדש, תן למאגר שלך שם תיאורי והחליט אם ברצונך לפרסם אותו, וקפד להגדיר עדיין שום קבצים להכללה. לאחר יצירת המאגר, העתק את השיבוט בכתובת SSH והדבק אותו לפקודה הבאה.

git remote add git://… (your remote URL)
עכשיו אתה יכול לחזור למאגר שלך עם CD, אתה תכיר את זה. נסה את סקריפט הגיבוי שלך עכשיו עם גיבוי גָדוֹל! עכשיו אנחנו באמת יכולים לקבל קידוד. בואו להתקין את Django עכשיו כשיש לנו תפיסה טובה על Bash ו- Git. Django יאפשר לנו לגבות אוטומטית את התוכנה שלנו, Bash יכול לעשות זאת גם אבל ל- Django צריך להיות יישום בטוח יותר יותר (ניתן להשבית אותה ולהגדיר אותו ביתר קלות). כדי להתקין תוכנה באובונטו, אנו נשתמש בפקודה SUDO APT-GET. ראשית, בואו נעדכן ולשדרג את התוכנה שכבר הייתה לנו. ניתן לעשות זאת באמצעות עדכון Sudo Apt-Get ושדרוג Sudo Apt-Get-Get -y. בשלב הבא, בואו להתקין את פייתון והסביבה הווירטואלית שלנו, בית הקוד שלנו, עם הפקודה הבאה: sudo apt-get התקנה python-is-python3 python3-venv זה כל מה שאתה צריך כדי להתחיל עם Django מבחינת התקנות תוכנה במופע אובונטו. עבור Windows ו- Linux זה אמור להיות די פשוט, אבל עבור Mac אולי תרצה להתקין מחשב וירטואלי ולינוקס עליו באמצעות סביבה וירטואלית בחינם או בתשלום כמו Virtualbox או Paralells Desktop ולשחזר את השלבים שלמעלה על מנת להגדיר סביבת אובונטו. אובונטו הוא קריטי במקרה זה מכיוון שזו התוכנה שהאתרים מפעילים והיא מאפשרת להם לארח אתרים עם כל התוכנה הנ"ל. בואו נחפור בדג'נגו. בספריה שלנו שוב, עם

python -m venv venv # יוצר את הסביבה הווירטואלית בה מאוחסן הקוד
source venv/bin/activate # מפעיל את הסביבה הווירטואלית
pip install Django
django-admin startproject mysite . # איפה MySite הוא הפרויקט שאני מתחיל בספריה הנוכחית שלי.
Django רק מתחיל אותנו, מכיוון שג'אנגו מארח את שרת האינטרנט ועושה את כל מה שאנחנו צריכים כדי להפעיל אתר מקומי בסיסי. כעת, לאחר שהתקין את Django, בואו נערוך קצת את ההגדרות כדי לגרום לזה לעבוד איך שאנחנו צריכים. ראשית, בואו ניצור אפליקציה חדשה

python manage.py startapp feed
תבחין שהאפליקציה הראשונה נקראת Feed. יש לקרוא לאפליקציה ככל שתרצה, ואנחנו ניצור אפליקציות חדשות, אך שמו של כל אפליקציה חייב להיות עקבי בכל פעם שהאפליקציה מופנית בקוד. כדי להוסיף אפליקציה חדשה, אנו תמיד נערוך את ההגדרות. Py בספריה האחרת שהאפליקציה יצרה, בשם ב- StartProject, להלן אפליקציה. באמצעות ננו,

nano app/settings.py
בהגדרות, מצא התקנה_אפס והפריד בין [] לשלושה שורות. בעזרת ארבעה חללים בשורת המרכז הריקה, הוסף 'הזנה' או שם האפליקציה שלך. החלק הזה של ההגדרות. PY צריך להיראות כמו:

INSTALLED_APPS = [
    'feed',
]
לפני שנשכח, בואו נבחן שג'אנגו עובד. בעזרת הפקודה Python Manage.py Runserver 0.0.0.0:8000, אנו יכולים להריץ את השרת ואז לנווט בדפדפן אינטרנט במחשב שמריץ את הקוד ל- http: // localhost: 8000 ולראות דוגמה של דף אינטרנט (זה עובד!) צא מהשרת עם Control C, זהה לכל פקודה אחרת. עכשיו, בואו נחפור בכתיבת קוד פייתון. לדג'נגו שלושה רכיבים עיקריים, כולם מנוהלים על ידי קוד לחלוטין. הרכיבים נקראים מודל, תצוגה ותבנית, וכל אחד מהם נמצא ברמה גבוהה ונמוכה בהתאמה לפני שדף האינטרנט מועבר למשתמש. המודל הוא הקוד המאחסן מידע במסד הנתונים לאחזור, מיון ועיבוד. התצוגה מחליטה כיצד המודל ניתן, מניפולציה ושינוי, כמעט כל תצוגה תשתמש במודל ישירות. התבנית היא קוד HTML עם כמה פעמונים ושריקות נוספות הנקראות שפת תבנית. התבנית ניתנת על ידי הנוף בו היא מלאה בקוד פייתון והקשר כמו מודלים ומידע (מיתרי Usuall ומספרים שלמים) מהנוף. לדג'נגו יש גם רכיבים אחרים, כולל אך לא מוגבלים ל: הגדרות, המגדירות את האפליקציה כפי שדיברנו. כתובות אתרים, שהם דפוסים שהמשתמש עוקב אחריו כדי לקבל גישה לחלקים ספציפיים ביישום האינטרנט. טפסים, המגדירים כיצד מידע שנשלח לשרת מטופל ומועברים למסד הנתונים כמו גם למשתמש. אלה הם הבסיס למידע על עיבוד בצד השרת, ויכולים לקבל כל סוג של מידע שמחסן המחשבים, בעיקר מיתרי טקסט, מספרים ובוליאנים אמיתיים/שווא (בדרך כלל תיבות סימון). תבניות, שהן קוד HTML ושפת תבנית ומגשרות על הפער בין פייתון ל- HTML, כלומר מידע על פייתון יכול לשרת כקוד HTML שכל אחד יכול לגשת אליו ויכול לאבטח אתר עם גישה מוגבלת, תוך הפיכת קוד פיתון לנגיש לרשת ולשימוש למגוון מטרות במכשיר מרוחק שלאלהיות קרוב לשרת. קבצים סטטיים, שהם בדרך כלל javaScript ומדובר בספריות שהשרת משרת ומקושר לתבנית. קבצי מדיה, שהשרת משרת או מתארחים חיצונית, או סתם נכתבים לשרת לפני שהוא מעובד ומוצב לשרת אחר (דלי) לאירוח. תוכנת Middleware, שהיא חתיכות קוד המופעלות במקביל לכל תצוגה ונחשבות "כלולות" בתצוגה. מעבדי הקשר, המעבדים את ההקשר של כל תצוגה ומשמשים להוספת הקשר נוסף. בדיקות, המאמתות כי המשתמש או הבקשה מעבירים דרישות מסוימות לפני שניתן לתצוגה. צרכנים, המכתיבים כיצד WebSockets מתמודדים ומגיבים לתקשורת. מנהל, המשמש לרישום דגמים כך שניתן יהיה לתפעל אותם בפירוט בתוך דף הניהול של Django, שם ניתן לתת את בסיס הנתונים דרך ממשק גרפי. סלרי, המגדירה משימות אסינכרוניות חלקים מקוד הג'נגו יכולים להתחילning לפני שתמשיך מייד למשימה או לשורת הקוד הבאה. לדג'נגו יכול להיות רכיבים רבים אחרים, עליהם נדון בפירוט כאן. יש המון דרכים להפוך את דג'נגו לפונקציונליות יותר, להוסיף את רשתות האינטרנט, המהירות ערוצי תקשורת מהירים, סלרי, המבצעים משימות אסינכרוניות, ושלל חתיכות תוכנה אחרות להרחבת Django, במיוחד בפונקציות הנוף, בהן רובם של רובם הקוד מבוצע. פונקציות תצוגה הן המפתח מכיוון שהן בדרך כלל מכריזות על כל פיסת קוד הספציפית לתבנית URL ספציפית, או על קטע מהשרת. ראשית, בואו לחקור פונקציות תצוגה. פונקציות תצוגה מתחילות ביבוא המציין קוד שישמש בתצוגה, ומוגדרים באמצעות הגדרות או מחלקות פונקציה רגילות. התצוגות הפשוטות ביותר מוגדרות על ידי הגדרת הפונקציה def, ומחזירו HTTPresponse עם תבנית בסיסית. נתחיל בהגדרת תצוגה בסיסית להחזרת הטקסט "שלום עולם". זכור שבכל פעם שאתה מוסיףלהצהיר כמו DEF, אם, אם, עבור וכו ', תצטרך להוסיף 4 רווחים לכל אחת מההגדרות המקדמות שתרצה להחיל על הפונקציה שלך. ניכנס למה שאמצעים כל אחד מאלה בקרוב. מהספרייה של האתר שלנו, ערוך את קובץ ה- feed/views.py באמצעות NANO והוסף את השורות הבאות לסוף ה-

from django.http import HttpResponse

def hello(request):
    return HttpResponse('hello world')
HTTPresponse של Django מגיב במחרוזת טקסט, המכונה עם הפתיחה והסגירה '. בכל פעם שאתה מעביר מידע לפונקציה או לשיעור, כמו בקשה או מחרוזת, תצטרך להשתמש בסוגריים (פתיחה וסגירה). זה עדיין לא כל מה שאנחנו צריכים לראות את השקפתנו. כמובן, לא אמרנו לשרת היכן התצוגה בדיוק, אנו עדיין צריכים להגדיר נתיב שבאמצעותו התצוגה צריכה להעביר. נתחיל בהגדרת נתיב בסיסי באפליקציה/urls.py, ונכנס לקבוצות נתיבים בהמשך. ב- App/urls.py, הוסף שורה לאחר הצהרות הייבוא ​​לאחר תחילת ייבוא ​​התצוגה שיצרנו זה עתה.

from feed import views as feed_views
עכשיו, בואו נגדיר את דפוס התצוגה. לדפוסי התצוגה יש שלושה רכיבים, רכיב הנתיב, המספר לשרת היכן התצוגה קיימת בתוך השרת (נתיב ה- URL שהמשתמש מקליד לסרגל הניווט כדי להיכנס לדף האינטרנט), רכיב התצוגה בו מצוין התצוגה, ו- שם ידידותי לתצוגה כך שקל לאחזר את התבנית שלו בעת עבודה עם תבנית, במיוחד כך שניתן לשנות את השם ולעדכן במידת הצורך כדי לפנות מקום לתצוגה אחרת או לקחת שם הגיוני יותר. הגיוני לעשות דברים בדרך זו ולהיות גמישים, מכיוון שבסיס הקוד שלך יהיה סביבה משתנה אי פעם שצריכה גמישות ואלתור על מנת להיות בעלת ערך וקל לעבוד איתה. הנה איך תיראה התצוגה שלך, אתה יכול להוסיף את זה ל- urlPatterns = [קטע של App/urls.py. דפוס התצוגה מוגדר עם שלושת הרכיבים שתוארו לעיל, ופונקציה הנקראת נתיב. דפוסי כתובת האתר שלך הם רשימה, לכן הקפד לסיים תמיד כל פריט בהםעם פסיק, מכיוון שזה מפריד בין כל אחד מהם. כל פריט צריך גם לעלות על שורה חדשה, שוב עם ארבעה חללים לפניו, ממש כמו האפליקציה ב- Settings.py. נגדיר את הרכיב הראשון בתצוגה עם פונקציית מחרוזת ריקה, על מנת ליצור תצוגה הפועלת בספריית השורש של שרת האינטרנט. עכשיו URLs.py שלך צריך להיראות כמו

from feed import views as feed_views

urlpatterns = [
    path('', feed_views.hello, name='hello'),
]
זה הבסיס ליצירת אתר עם Django שהוא סטטי לחלוטין. על מנת ליצור אתר דינמי יותר בו נוכל להתחיל במטמון מידע, כמו תמונות, סרטונים, שמע ועוד, נצטרך להשתמש בדגמים, אותם נחקור בהמשך. לעת עתה, בואו נבדוק את הקוד שלנו ונפעל את השרת. כדי לבדוק את הקוד שגיאות, הפעל:

python manage.py check
אם יש הודעות שגיאה, עליך לבדוק בזהירות את השינויים שביצעת באפליקציה שלך ולראות אם יש משהו שצריך לתקן, כמו חלל זרים או חסר, תו נוסף, מחרוזת לא סגורה, כל שגיאת הקלדה, בכל טעות אופי שנמחק, או כל דבר אחר. קריאת הודעת השגיאה (אם יש לך אחת), אתה אמור להיות מסוגל לראות את הנתיב לקובץ שיצרת או ערכת יחד עם מספר שורה, אז בדוק את הקובץ והקו הזה ובדוק אם אתה יכול לתקן משהו שיש שם ו אם תיקנת את הבעיה, הפעל את הפקודה לעיל שוב. כאשר התוכנה שלך מוכנה להפעלה ועובדת, תראה את הפלט "בדיקת מערכת לא זיהתה שום בעיות." עכשיו אתה מוכן ללכת. הפעל את השרת עם:

python manage.py runserver 0.0.0.0:8000
כעת פתחו דפדפן אינטרנט ונווט אל http: // localhost: 8000. אתה אמור לראות את הטקסט המוחזר בסוגריים וציטוטים של פונקציית HTTPRESPONSE בתצוגה שלך. זו רק דוגמא בסיסית, אבל אם היית עושה את זה עד כה, אתה מבין את היסודות של עבודת לינוקס, באש, פייתון ודג'נגו. בואו נחפור עמוק יותר במודלים של מסדי נתונים, ונחקור את הכוח של כיתת פייתון באחסון מידע. לאחר מכן, נתחיל לאחוז ב- HTML ו- CSS לפני שנהפוך את האתר שלנו למציג מלא, גמיש ומאובטח באמצעות JavaScript ולמידת מכונה. השיעורים מאוחסנים בדגמים. Py של האפליקציה שלך. באמצעות NANO, ערוך אפליקציה/models.py והוסף כיתה חדשה. מחלקה מוגדרת עם הגדרת הכיתה ומעבירה סכום -על ממנו הוא יורש, במקרה זה מודלים. מודל. שם השיעור מגיע לאחר הגדרת הכיתה, ואחרי הגדרת הכיתה א ': (קולון) משמש, לפני שתכונות ההגדרות של התכונות והגדרות הפונקציה הקשורות לכיתה. הכיתה שלנוזקוק למזהה בו אנו יכולים להשתמש כדי לאחזר אותו ולשמור עליו ייחודי, והוא גם זקוק לשדה טקסט כדי לאחסן מידע כלשהו. בהמשך אנו יכולים להוסיף חותמת זמן, קבצים, בוליאנים (הגדרות אמיתיות או שווא שיכולות לעזור לקוד שלנו לקבל החלטות מה לעשות עם הדגם, וניתן להשתמש בהן כדי למיין אותו), מופע כדי לקשור את הדגם למשתמש שנרשם לשרת ועוד. בואו נפרק את הקוד

from django.db import models # היבוא המשמש להגדרת הכיתה שלנו ותכונותיו

class Post(models.Model): # ההגדרה של הכיתה שלנו עצמה
    id = models.AutoField(primary_key=True) # המזהה של המודל שלנו, מפתח שנוצר אוטומטית שיאפשר לנו לשאול את הדגם, לשמור עליו ייחודי, והוא שימושי כשאנחנו צריכים ליצור אינטראקציה עם הדגם לאחר שנוצר.
    text = models.TextField(default='') # התכונה חנויות הכיתה שלנו, במקרה זה, טקסט כלשהו, ​​מחליפה למחרוזת ריקה.
סגור ושמור את הקובץ כפי שעשינו קודם כדי לסיים. ישנם שדות ואפשרויות רבות אחרות שנחקור כאשר אנו מעדכנים כיתה זו ככל שהתפתחה האפליקציה שלנו, אך זהו הצרכים הבסיסיים של יצירת אפליקציה לפרסום טקסט כלשהו. עם זאת, מודל זה לא יעבוד לבד. כמתואר קודם, נצטרך תצוגה מותאמת אישית ותבנית כתובת אתר מותאמת אישית כדי לגרום למודל זה לעבוד, ואנחנו נצטרך גם טופס יחד עם תבנית. בואו נחקור את הטופס קודם. כדי להגדיר טופס, ערוך אפליקציה/forms.py עם ננו והוסף את השורות הבאות. נצטרך שני יבוא, מחלקת הטפסים שלנו, כמו גם את המודל שיצרנו (Feed.Models.Post), הגדרת מחלקה הדומה למודל, ושדה יחד עם תת -סוג שנקרא Meta שיגדיר את המודל שהצורה מתקשרת עִם. לטופס יכול להיות גם פונקציית אתחול שמגדירה אותה על סמך מידע בבקשה, מודל או אחרת, נחקור זאת בהמשך. צורות מודל כל כך שימושיות מכיוון שהן יכולות ליצור מודל או גם לערוך מודל,אז אנו נשתמש בהם לשניהם. בואו נגדיר אחד בצורות.

from django import forms
from feed.models import Post

class PostForm(forms.ModelForm):
    text = forms.CharField(widget=forms.Textarea)
    class Meta:
        model = Post
        fields = ('text',)
זה היסודות של איך נראים צורה ודגם. ניתן להשתמש בטופס מודל זה כדי להפעיל או לערוך פוסט, ולשנות את הטקסט שהוא מכיל. נסתכל על שילוב טופס זה בתצוגה הבאה. ראשית, בואו נעשה את ההגירה ונעביר את מסד הנתונים כך שהקוד שלנו יוכל לקיים אינטראקציה עם הדגם כאשר הוא פועל. לשם כך, הפעל את הפקודות הבאות:

python manage.py makemigrations
python manage.py migrate
זה ייקח דקה לביצוע, אך ברגע שזה יקרה, זה יאפשר לך לגשת לדגם בתצוגות, בתווך או בכל מקום אחר בתוכנה. בואו נמשיך על ידי צפייה בה אנו יכולים לראות את המודל שלנו. ערוך FEED/Views.PY והוסף את הקוד הבא, כאמור. לא תצטרך להוסיף שום דבר לאחר סימן #, קוד זה הערות המשמשות לציון מידע על הקוד. נתחיל בייבוא ​​המודל שלנו בתצוגות, ונוסיף אותו להקשר בו אנו יכולים להעביר אותו בתבנית כרשימה לתצוגה. בשלב הבא נוסיף תבנית בה אנו יכולים להעניק את הטופס ואת הדגם עם כפתור ליצירת אובייקט חדש המבוסס על הדגם ולפרסם אותו לשרת. זה נשמע מסובך, אז בואו פשוט ניקח את זה צעד אחר צעד. לפני שנסיים את התצוגה, בואו ניצור תבנית שפשוט הופכת את הדגם וודא שנוכל לראות אותו על ידי יצירת פוסט חדש במעטפת. כך נראה התצוגה הזו:

from feed.models import Post
from django.shortcuts import render, redirect
from django.urls import reverse

def feed(request):
    posts = Post.objects.all() # שאילתה את כל הפוסטים במאגר עד כה
    return render(request, 'feed/feed.html', {
        'posts': posts,
    })
כל זה נראה די פשוט עד שנגיע לתחתית. העבר, הערך שהוחזר על ידי הפונקציה במקום בתגובה HTTP כמו הדוגמה הקודמת, תמיד לוקח בקשה כקלט הראשון שלה, מקבל הקשר (במקרה זה הפוסטים בבסיס הנתונים), שכעת ניתן להעביר בתבנית , ומחזיר את התבנית המוגדרת בפונקציה. התבנית הולכת להיות מסמך HTML עם מעט שפה בשם Jinja2, מה שהופך את המידע של פייתון ל- HTML. כדי להתחיל ליצור תבניות, ערוך שני ספריות בעדכון.

mkdir feed/templates
mkdir feed/templates/feed
בשלב הבא, ערוך תבנית בספריה שלמעלה, הזנה/תבניות/הזנה והוסיפו את הקוד לדוגמה זו. בואו נסתכל על התבנית לדוגמא זו.
 
<!doctype HTML>
<html>
<body>
<legend>Feed</legend>
<hr>
{% for post in posts %}
<p>{{ post.text }}</p>
{% endfor %}
</body>
</html>
 
זוהי תבנית פשוטה מאוד. זה מגדיר פתיחה וסגירת תגי HTML, תג סוג מסמך, תג גוף עם כותרת אגדה, תג הפסקה שמוסיף שורה קטנה על המסך, ולולאה שהופכת כל פוסט ברשימת הפוסטים כפסקה ב התבנית. זה כל מה שנדרש כדי לפנות פוסטים, אך עדיין אין במאגר. בואו ניצור כמה עם הקליפה. אנחנו יכולים להריץ את הקליפה עם Manage.py

python manage.py shell
עכשיו, בואו נביט את דגם הפוסט שלנו

from feed.models import Post
בשלב הבא ניצור פוסט פשוט עם מחרוזת ונצא מהקליפה. המחרוזת יכולה להיות כל דבר, כל עוד הטקסט התקף.

Post.objects.create(text='hello world')
exit()
לבסוף, נצטרך להוסיף דפוס כתובת אתר לעדכון שלנו. מכיוון שאפליקציית ההזנה שלנו תשתמש בכתובות אתרים מרובות ואנחנו רוצים לשמור על גדלי קבצים קטנים, בואו ניצור כתובות URL מקומיות באפליקציית ההזנה שלנו שנראית כך:

from django.urls import path
from . import views

urlpatterns = [
    path('', views.feed, name='feed'),
]
נצטרך גם לערוך את ה- URLs.PY באפליקציית הבסיס, כל מה שהחלטנו לקרוא לזה, זה היה הספרייה הראשונה שיצרנו. ערוך אפליקציה/app.py והוסף את הדברים הבאים לדפוסי כתובת האתר

from django.urls import include # בחלקו העליון

urlpatterns = [
    # ... קוד קודם כאן
    path('feed/', include(('feed.urls'), namespace='feed')),
]
כעת, כאשר אנו מפעילים את השרת עם Python Manage.py Runserver, נראה את הדף שיצרנו מכיוון שיש לנו את הדגם, התצוגה והתבנית כמו גם את דפוס ה- URL, יחד עם פריטים במאגר. בשלב הבא, בואו ליישם את הטופס שיצרנו ונתחיל ליצור פוסטים משלנו. אבל לפני שנכתוב יותר מדי קוד, בואו נעשה גיבוי באמצעות הסקריפט שכתבתי קודם, גיבוי. הפעל את התסריט הזה במעטפת, המתן כמה רגעים, וכל הקוד יגובה במאגר GIT שלנו.

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

from feed.forms import PostForm

def feed(request):
    posts = Post.objects.all() # שאילתה את כל הפוסטים במאגר עד כה
    if request.method == 'POST': # טפל בבקשת ההודעה
        form = PostForm(request.POST) # צור מופע של הטופס ושמור אליו את הנתונים
        if form.is_valid(): # לאמת את הטופס
            form.save() # שמור את האובייקט החדש
        return redirect(reverse('feed:feed')) # להפנות לאותה כתובת אתר עם בקשת GET
    return render(request, 'feed/feed.html', {
        'form': PostForm(), # הקפד להעביר את הצורה להקשר כדי שנוכל לספק אותה.
        'posts': posts,
    })
כעת, נצטרך לעדכן את התבנית כדי להסביר את הטופס החדש. אנו יכולים לעשות זאת באמצעות
תייג ב- HTML והעניק את הטופס בתבנית HTML עם כפתור הגשה. אנו נצטרך גם אסימון CSRF, אסימון המונע את האתרים החיצוניים לפרסם לטופס מבלי לטעון תחילה דף.
 
<!doctype HTML>
<html>
<body>
<legend>Feed</legend>
<form method=”POST”>
{% csrf_token %}
{{ form }}
<button type=”submit”>New Post</button>
</form>
<hr>
{% for post in posts %}
<p>{{ post.text }}</p>
{% endfor %}
</body>
</html>
 
בואו נשבר את זה. יש מחלקת טופס חדש, אסימון, הטופס עצמו וכפתור הגשה. די פשוט, אבל כשאנחנו מסתכלים על זה, אולי נרצה לגרום לזה להיראות טוב יותר. זה עובד, אנחנו יכולים לפרסם פוסטים חדשים עם הטופס והם נשמרים כעת במסד הנתונים. יש כאן כמה דברים שקורים. אנו משתמשים בתגי HTML כדי להכריז כי המסמך הוא מסמך HTML, אנו משתמשים בתג תבנית ({ %… %}) כדי להפוך את האסימון לטופס, ועוד, {{…}} כדי להעניק את הטופס. יש לנו גם לולאה להעניק את הטקסט באמצעות תגי חסימות ותג תבנית. תגי חסימות חשובים באמת מכיוון שאנו יכולים להגדיר כיצד ניתן להבחין בקטעי התבנית איתם, ותגי תבנית הם הבסיס לאופן בו אנו מכניסים משתנים לקוד שלנו. עכשיו עלינו לגרום לאפליקציה שלנו להיראות טוב יותר, כי בינתיים היא נראית ממש בסיסית. אנו יכולים לעשות זאת על ידי שימוש ב- CSS, או בשורה או בשיעורים הקשורים לכל אובייקט במסמך. CSS זה ממש נחמד כי זה אומר הכל בדף איך הוא צריך להיראות,ויכול לגרום לזה להיראות ממש טוב. יש כמה ספריות שיכולות לעשות זאת, אבל ההליכה האישית שלי היא Bootstrap. ניתן להוריד את Bootstrap מאתר האינטרנט שלהם,GetBootstrap.com/ו לאחר שם, לחץ על הכפתור כדי לקרוא את מסמכי ההתקנה, והעתק את הקוד מהסעיף כולל באמצעות CDN. תזדקק לקוד זה בראש מסמך HTML שלך, בתג שנקרא Head. כמו כן, בואו נמשיך ליצור תבנית בסיס כך שלא נצטרך ליצור מחדש את הקישורים האלה בכל תבנית. ערוך ספרייה חדשה שנקראת תבניות עם תבניות MKDIR, ואז ערוך תבניות/base.html. זה אמור להיראות כך:
 
<!doctype HTML>
<html>
<head>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js" crossorigin="anonymous"></script>
</head>
<body>
{% block body %}
{% endblock %}
</body>
</html>
 
הקפד להעתיק את ה- CSS ו- JavaScript, את קבצי ה- .css ו- .js, מכיוון שנצטרך ל- JavaScript כדי להפוך את האתר שלנו לפונקציונלי יותר בעתיד. עכשיו, בואו נחזור למעטפת הבסיס ונרץ פקודה מהירה. זכור, אם אי פעם תצטרך לגשת לסביבה הווירטואלית, הקלד מקור venv/bin/הפעלה. זה יאפשר לך להתקין חבילות פייתון באופן מקומי באופן המאפשר ל- Django לגשת אליהם. כדי לתת את הטפסים שלנו שנוצרו על ידי שיעורי Django Bootstrap, אנו נשתמש בחבילת פייתון בשם צורות פריכות. אנו יכולים להוריד זאת עם הפקודה הבאה

pip install django-crispy-forms
לאחר התקנת זה, הוסף אותו ל- Settings.py

INSTALLED_APPS = [
    # ... קוד קודם כאן
    'crispy_forms',
]
כעת, חזרה בתבנית ההזנה שלנו, אנו יכולים להסיר כמה דברים. בואו נסיר את תחילתו וסופו של המסמך ונחליף אותו בירושה מתבנית הבסיס שלנו, תוך שימוש בהרחבות והגדרת החסימה. כמו כן, נוסיף יבוא מסנן תבנית עם עומס ומסנן תבנית לטופס. לבסוף, בואו נוסיף לכפתור כיתת Bootstrap כדי לגרום לו להיראות יותר כמו כפתור. זה אמור להיראות כך:
 
{% extends 'base.html' %}
{% block body %}
{% load crispy_forms_tags %}
<form method=”POST”>
{% csrf_token %}
{{ form|crispy }}
<button type=”submit” class=”btn btn-outline-primary”>New Post</button>
</form>
<hr>
{% for post in posts %}
<p>{{ post.text }}</p>
{% endfor %}
{% endblock %}
 
יָפֶה! זה כבר לא מעט קוד. בשלב הבא עלינו לבדוק את זה ולוודא שנוכל לראות שהכל נראה נחמד, וגם להיות בטוח שהכל עובד כמו שצריך. הפעל את השרת לפי הוראות קודמות וודא שהאתר נראה ועובד בסדר. עבודה נהדרת! אתה מוכן לעבור לשלב הבא, בו נוסיף פונקציונליות כניסה למשתמש באמצעות כתובות אתרים, צורות, תצוגות ותבניות דומות. תבנית הבסיס חשובה, ואנחנו נמשיך לשנות אותה ולבצע שינויים לפי הצורך, אך לעת עתה בואו נתמקד בהפיכת האתר שלנו לבטוח יותר, על ידי כך שאפשר למשתמשים להתחבר עם שם משתמש ובקוד סיסמה, ובסופו של דבר מידע חשוב עוד יותר יעזור לשמור על האפליקציה שלך מאובטחת וחשבון משלך נגיש רק על ידך. לשם כך, עלינו להשתמש במודל המשתמש המובנה ב- Django. מודל המשתמש הוא מודל מסד נתונים, כמו הפוסט שלנו, שניתן להעביר כדי לרשום משתמש לאתר. בעתיד, לפני שנפרוס את האתר לאינטרנט, אנו נעשה זאתהרחב מודל זה עם דגמים אחרים המיוחסים אליו, ובנה אמצעי אבטחה נוספים עבור הכניסה העמידים בפני דיוג. נתחיל להשתמש בכמה צורות כניסה מובנות ש- Django מספק. ראשית, בואו ניצור אפליקציה חדשה בה אנו נשתמש בכדי להציג את התבניות והתצוגות לדף הכניסה הבסיסי. אנו ניצור גם אפליקציות אחרות לייצוג את אתגרי ההתחברות המתמשכים על מנת לאבטח את האפליקציה, כולל PINCODE, זיהוי פנים, תקשורת שדה קרוב, מכשירים חיצוניים, אימות רב גורם וגורם וזיהוי טביעות אצבע. כבר דיברנו על הקמת אפליקציה. מהספרייה שלנו, בתוך הסביבה הווירטואלית, עברו את ניהול.

python manage.py startapp users
כעת, עלינו לקבל ספרייה לאפליקציה החדשה. נתחיל ביצירת תצוגה בספריה ההיא המתאימה להתחברות המשתמש. Django בנה תצוגות לכניסות משתמשים, אך אלה לא יתאימו לנו מכיוון שאנו זקוקים לתצוגה מותאמת אישית, שעדיף לעשות עם הגדרה. בתצוגה זו נתחיל בבדיקת בקשת פוסט, תעבור בקשה. הפוסט לצורת כניסה המיובאת מ- Django, לאמת את חשבון המשתמש ולהיכנס למשתמש לפני שנפנה אותם לאפליקציית Feed שלנו. במשתמשים/views.py, הוסף את הקוד הבא

from django.shortcuts import render, redirect
from django.urls import reverse
from django.contrib.auth.forms import AuthenticationForm, SetPasswordForm
from django.contrib.auth import authenticate, logout
from django.contrib.auth import login as auth_login
from django.contrib import messages

def login(request):
    if request.method == “POST”:
        username = request.POST['username'] # קבל את שם המשתמש והסיסמה מבקשת ההודעה
        password = request.POST['password'] # לאמת את המשתמש
        user = authenticate(username=username, password=password)
        if user:
            auth_login(request, user, backend='django.contrib.auth.backends.ModelBackend')
            messages.success(request, 'Your password was accepted. Please continue')
            return redirect(reverse('feed:feed'))
        else: messages.warning(request, 'Username or password incorrect. Please try again')
    return render(request, 'users/login.html', {'form': AuthenticationForm()})
זה כל מה שאתה צריך לתצוגת כניסה בסיסית. כעת, בואו ניצור טופס לתצוגה על ידי הרחבת תבנית הבסיס. נתחיל ביצירת ספרייה חדשה לתבניות בתיקיית המשתמשים.

mkdir users/templates
mkdir users/templates/users
כעת, עלינו להיות מסוגלים לערוך משתמשים/תבניות/משתמשים/login.html. בזמן שאנחנו בעניין, ניצור תבנית שתאפשר למשתמש להירשם גם כן.

nano users/templates/users/login.html
עכשיו, בתבנית,
 
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block content %}
<form method="POST">
    {% csrf_token %}
    <fieldset class="form-group">
        <legend class="border-bottom mb-4 break">Log In</legend>
        {{ form|crispy }}
    </fieldset>
    <div class="form-group">
        <button class="btn btn-outline-info" type="submit">Login</button>
    </div>
</form>
{% endblock %}
 
זהו היסודות של תבנית כניסה. זה באמת ממש כמו התבנית האחרת במבנה, אבל היא נראית קצת שונה כשהיא ניתנת. אנו יכולים להעתיק קוד זה כדי לבנות תבנית דומה מאוד דומה בשם Register.html, שם נשנה את הנוסח ונשתמש בטופס חדש שאנו בונים. בואו נעשה את התבנית קודם. ערוך משתמשים/תבניות/משתמשים/register.html והוסף את הקוד הבא:
 
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block content %}
<form method="POST">
    {% csrf_token %}
    <fieldset class="form-group">
        <legend class="border-bottom mb-4 break">Create an account</legend>
        {{ form|crispy }}
    </fieldset>
    <div class="form-group">
        <button class="btn btn-outline-info" type="submit">Register</button>
    </div>
</form>
{% endblock %}
 
כעת, בואו נבנה טופס לרישום המשתמש שלנו ונחזור לתצוגות לפני שנשדרג את כניסה למשתמש שלנו באמצעות מודל. אנו נעשות טופס זה בסיסית מלכתחילה, אך נוכל לשלב פרטים נוספים ותכונות אבטחה כמו הסכמים וקפטצ'ה בעתיד. ערוך את הטפסים עם משתמשי NANO/Forms.PY, והוסף את הקוד הבא.

from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm

class UserRegisterForm(UserCreationForm):
    email = forms.EmailField()

    class Meta:
        model = User
        fields = ['username', 'email', 'password1', 'password2']
אז יש לנו כאן צורה אחרת, שעובדת די בפשטות. זהו טופס רישום משתמשים עם שם משתמש, דוא"ל וסיסמא, כמו גם שדה אישור סיסמא. שימו לב כי טופס זה אינו מרחיב את הטפסים הרגילים. מחלקת צורה, זהו צורת דגם שמשמעותה שיש לה מטא. שדה אחד מוגדר בדיוק אותו דבר, ומטא הכיתה מגדיר את המודל שהטופס תואם את שאר המידע שייכתב לטופס. רוב זה כבר קיים בצורת המשתמש המובנית של Django, ולכן נשתמש בזה כבסיס לכיתה (שהועבר בסוגריים). בשלב הבא נבחן את התצוגה לרשום משתמש, כעת כשיש לנו טופס ותבנית. זהו modelform, ממש כמו זה שנמצא בתצוגת הפוסט החדשה. ערוך משתמשים/views.py והוסף את הקוד הבא:

# ... סכומים
from .forms import UserRegisterForm

def register(request):
    if request.method == “POST”:
        form = UserRegisterForm(request.POST)
        if form.is_valid():
            user = form.save()
            messages.success(request, 'Welcome to the app, {}.'.format(user.username))
    return render(request, 'users/register.html', {'form': UserRegisterForm})
זה כל מה שאנחנו צריכים כדי לרשום משתמש, אך עלינו לקבל מידע נוסף. אנו רוצים לדעת את הזמן בו נרשם המשתמש, באיזו שעה היה האחרון באתר, קצת מידע עליהם, כמו ביוגרפיה, אזור זמן וכו '. כמו כן, נצטרך לעדכן את מודל ההזנה שלנו, לפרסם, כדי להסביר את המשתמש דגם ומייחס פוסטים לכל משתמש. כדי לעשות זאת, אנו נעדכן את הדגמים. PY בשני האפליקציות. נתחיל בעריכת דגם ההזנה. זה אמור להיראות כך עכשיו:

from django.db import models # ... סכומים
from django.contrib.auth.models import User

class Post(models.Model):
    id = models.AutoField(primary_key=True)
    author = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True, related_name='posts') # הוסף בשורה זו
    text = models.TextField(default='')
שימו לב לשורה השנייה שנוספה לקובץ. זהו מפתח זר, שייחסר כל פוסט למשתמש יחיד לכל פוסט, כך שנוכל לוודא שנשמור את הפוסטים על בסיס משתמש למשתמש ולא ניתן להעלות פוסט מבלי לייחס אותו למשתמש. אנו מגדירים את המפתח הזר הזה עם הכיתה שהוא מייצג, טיעון מחק כדי להבטיח שהפוסטים יימחקו עם משתמשים, בנות וריקות כדי לוודא שנוכל להסיר את המשתמש במידת הצורך, ולהתאים לחוסר משתמש בפוסטים שאנחנו כבר נוצר ושם קשור, בו אנו יכולים להשתמש כדי להתייחס לאובייקטים של הפוסט שהמשתמש יוצר. שם קשור זה, בניגוד ל- Post.author, מחבר הפוסט, נותן לנו למשתמש שפרסם את הפוסט עצמו. כעת אנו יכולים לקבל את הפוסטים שמשתמש שנעשה על ידי הפעלת user.posts.all (), או מחבר. Posts.all (). עכשיו, בואו נהפוך את ההתחברות שלנו לגמישות יותר. אנחנו כבר יכולים להפוך את האתר שלנו להרבה פחות פגיע לדיוג על ידי פשוט להגביל את מספר הפעמים שנאפשר התחברות לאתר, זה די קל. בואו נתחיל לאחסן קצת מידע על כל משתמש לפני שאנחנו ממשיכים לפתח את האפליקציה שלנו. עריכת משתמשים/models.py, הוסף את הדברים הבאים

from django.db import models
from django.contrib.auth.models import User
from django.utils import timezone

class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE, null=True, blank=True, related_name='profile')
    account_created = models.DateTimeField(default=timezone.now)
    last_seen = models.DateTimeField(default=timezone.now)
    can_login = models.DateTimeField(default=timezone.now)
    preferred_name = models.CharField(max_length=20,default='', null=True, blank=True)
    bio = models.TextField(blank=True, default='')
שים לב שמודל זה דומה למדי לדגם הפוסט. יש לנו ייבוא ​​נוסף, אזור זמן, שיאפשר לנו להגדיר ברירות מחדל בשדות DateTime, ויש לנו גם תווים ושדה טקסט כמו הפוסט. השימוש בכל חותמות הזמן הללו עוזר לנו לאבטח את האתר ולהבין את השימוש בו, ושדות הטקסט מאפשרים לנו להעניק מידע על כל משתמש, או מחבר, באתר. שדה Onetoonefield צריך להיות השיקול הקלה היחיד, הוא מתנהג בדיוק כמו מראש אך עם רק אחד לכל מודל שלאחר מכן. בדרך זו, למשתמש יש רק פרופיל אחד, בעוד שיש לו פוסטים רבים. כעת, בואו נשפר את הכניסה שלנו ונרשם לתצוגות כדי להסביר את הפרופיל. ראשית, ערוך משתמשים/views.py והתמקד בתצוגת הפנקס:

# ... סכומים
from .forms import UserRegisterForm

def register(request):
    if request.method == “POST”:
        form = UserRegisterForm(request.POST)
        if form.is_valid():
            user = form.save()
            Profile.objects.create(user=user) # הקפד להוסיף שורה זו, כדי ליצור פרופיל למשתמש
            messages.success(request, 'Welcome to the app, {}.'.format(user.username))
    return render(request, 'users/register.html', {'form': UserRegisterForm})
זה פשוט יוצר פרופיל למשתמש, מבלי למלא אף אחד מהמידע. כעת, אנו רוצים לוודא שלא ניתן לחבר את חשבון המשתמש לעיתים קרובות מדי, או לפחות לא ניתן לנסות סיסמאות לעתים קרובות מדי, אז בואו נעדכן את תצוגת ההתחברות.

# ... סכומים
from .models import Profile
from django.utils import timezone
import datetime

def login(request):
    if request.method == “POST”:
        username = request.POST['username']
        password = request.POST['password']
        user = authenticate(username=username, password=password)
        if user and user.profile.can_login < timezone.now(): # שים לב שעכשיו אנו בודקים אם המשתמש יכול להתחבר
            auth_login(request, user, backend='django.contrib.auth.backends.ModelBackend')
            messages.success(request, 'Your password was accepted. Please continue.')
            return redirect(reverse('feed:feed'))
        else: # אם הכניסה לא הייתה מוצלחת,
            messages.warning(request, 'Username or password incorrect. Please try again.')
            user = User.objects.filter(username=username).first() # זה החלק בו אנו מעדכנים את פרופיל המשתמשים
            if user: 
                profile = user.profile
                profile.can_login = timezone.now() + datetime.timedelta(seconds=15) # אז הם לא יכולים להתחבר שוב לכמה שניות
                profile.save()
    return render(request, 'users/login.html', {'form': AuthenticationForm()})
זהו הבסיס הבסיסי של האבטחה. וודא שהאתר אינו חשוף למישהו פשוט מנסה כל שילוב אפשרי של סיסמאות, או אפילו מעטים מהם בו זמנית. זה לא יהיה מתסכל עבור המשתמש הרגיל שמכיר את קוד הסיסמה שלו ופשוט נכנס לכמה מכשירים, אבל זה ישמור על מספר רב של רובוטים דיוג מהאפליקציה. שים לב שהוספנו הצהרת IF עם משתנה, Can_Login, זה אמור להיות זמן בעבר, ולעדכן אותה עם כל כניסה לא מוצלחת באמצעות אותו שם משתמש. בדרך זו, משתמש זדוני לא יוכל לנחש סיסמה בשום מקום קרוב באותה מהירות. ניתן לעדכן גם את מספר השניות ב- DateTime.Timedelta (), והאתר יהיה גמיש יותר ועם זאת מעט פחות שמיש עם יותר שניות. אני ממליץ 15 מלכתחילה. זכור, בנינו תסריט גיבוי כדי להציל את העבודה שלנו, אז בואו נמשיך לגבות את מה שיש לנו עד כה כדי לוודא שהכל שנשמר. הפעל את הפקודה:

sudo backup
שוב, זה יחסוך את העבודה שלך עד כה. אני ממליץ להריץ גיבויים תכופים כדי לחסוך את העבודה שלך, ואולי אפילו תרצה להריץ עבודת גיבוי באופן אוטומטי. אתה יכול לעשות זאת באמצעות כלי עזר של UNIX בשם CRON. כדי להפעיל כלי עזר זה, הפעל את הפקודה הבאה והזן את הסיסמה שלך:

sudo crontab -e
אם עדיין לא בחרת באפשרות 1 עבור NANO, עורך הטקסט שאתה כבר צריך להכיר ולגלול לתחתית הקובץ באמצעות מקשי החצים. הוסף את השורה הבאה:

0 * * * * sudo backup
CRON משתמשת בדקה הפורמט, שעה, יום בחודש, חודש, יום שבוע, שם A * או מספר מייצגים מתי להפעיל את הפקודה. בעזרת 0 לרגע ו * לשאר האפשרויות, נוכל להריץ פקודה בדקה הראשונה בכל שעה בתחילת הדקה. זה מאפשר לנו לגבות את הקוד באופן אוטומטי. כל העבודות של CRON כאשר מבוצעות עם SUDO RUCE כשורש, כך שלא נצטרך להקליד סיסמה בכל שעה. כדי להקל על גיבוי הקוד שלנו מבלי להשתמש בסיסמה, בואו נשבית את הסיסמה לפקודת הגיבוי שלנו. אנו נעשה זאת על ידי ביצוע הפקודה הבאה והזנת סיסמה:

sudo visudo
עכשיו, בואו לגלול לתחתית הקובץ ונוסיף שורה נוספת:

ALL ALL=NOPASSWD: /bin/backup
זה מאפשר לנו להריץ את הפקודה "גיבוי" כמשתמש, ללא סיסמה. הפורמט לכך קל, פשוט קידום הקו עם "הכל = nopasswd:/bin/" וסוף עם הפקודה, למשל/bin/גיבוי, שקיים ב-/usr/bin/. עכשיו, נתחיל לעבוד עם דוא"ל. דוא"ל חשוב באמת לאתרים, מכיוון שזו דרך לשמור על אתר מאובטח יותר, לאמת שמשתמשים הם אנשים אמיתיים ואפילו מוצרים או שירותים לשוק ללקוחות. אנשים רבים שתפקידים באינטרנט בודקים את הדוא"ל שלהם מדי יום, ומקבלים כל מיני דוא"ל שיווקי אודות מוצרים ושירותים שהם מעוניינים בהם. יש כמה אפשרויות בכל הקשור לאפשר דוא"ל באתר Django, ואתה מוזמן לבחור מה שמתאים לך ביותר עבורך. ראשית, אתה יכול לשלם עבור שירות דוא"ל שיאפשר לך לשלוח דוא"ל מהתחום שלך ודורש קוד מינימלי. ישנם שירותים רבים המציעים זאת, כמו סביבת העבודה של גוגל, SendInblue, MailGun ועוד. אחרת, אתה טוב לבנותשירות דוא"ל משלך בשרת שלך מאפס. אני ממליץ על אפשרות זו, למרות שזו קוד יותר ועשוי לדרוש אירוח מיוחד. לא תוכל להפעיל שרת דואר מהמחשב הביתי שלך ככל הנראה, אז בואו נמשיך לבחון את התצורה והקוד כדי לשלוח דוא"ל לפני שנתחיל שרת בענן ונוצר שרת דואר משלנו בתוכו. ראשית, ערוך הגדרות. Py עם הדברים הבאים

nano app/settings.py
כאשר האפליקציה היא שם האפליקציה שיצרת עם StartApp. הוסף את השורות הבאות:

SITE_NAME = 'Django App'

EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'localhost'
EMAIL_PORT = 587
EMAIL_USE_TLS = True
EMAIL_ADDRESS = username@server.com'
EMAIL_HOST_USER = 'username'
EMAIL_HOST_PASSWORD = config['EMAIL_HOST_PASSWORD']
DEFAULT_FROM_EMAIL = '{} <{}>'.format(SITE_NAME, EMAIL_HOST_USER)
הקפד לשנות את אלה כשאתה מוכן לפרוס את האפליקציה שלך, אנו נתקן זאת בהמשך. הגדרת הדוא"ל_דברס צריכה להיות הדוא"ל שתרצה לשלוח ממנה, והסיסמה (דוא"ל_HOST_PASPWORD) צריכה להיות מוגדרת לסיסמה שאתה מייצר עבור השרת. אני טוען את הסיסמה מקובץ config כדי להרחיק אותו מהקוד באמצעות ההיגיון הבא, מעל שורות אלה ב- Settings.py:

import os
import json
with open('/etc/config.json') as config_file:
    config = json.load(config_file)
לאחר מכן, הקמתי קובץ JSON עם התצורה ב /etc/config.json באמצעות ננו כדלקמן. לעריכת הקובץ:

sudo nano /etc/config.json
הוסף את השורות הבאות:

{
	“EMAIL_HOST_PASSWORD”: “<some password here>”
}
אנו נמשיך לערוך את קובץ התצורה ונוסיף את כל הסיסמאות והמפתחות בה אנו נשתמש באפליקציה. לעת עתה, בואו נבחן במהירות כיצד לשלוח דוא"ל באמצעות פייתון. ראשית, בואו ניצור תבנית להודעת אימות שנוכל לשלוח למשתמשים שלנו, ונכניס אותה לספריית תבניות המשתמש. תבנית זו תיכתב ב- HTML.

nano users/templates/users/verification_email.html
 
<h1>Django App - Verify Your Email</h1>
<p>Dear {{ user.username }},</p>
<p>To verify your email, please <a href="{{ base_url }}{% url 'users:activate' uidb64=uid token=token %}">click here</a>.</p>

<p>Alternatively, you can paste the following link in your browser's address bar:</p>
<p>{{ base_url }}{% url 'users:activate' uidb64=uid token=token %}</p>

<p>The link will expire in 30 minutes.</p>
<p>If you have not requested a verification email you can simply ignore this email.</p>
<p>See you there,</p>
<p>Daisy</p>
 
דוא"ל זה די פשוט. זה לוקח הקשר של משתמש, כתובת האתר הבסיסית לאתר, ומזהה משתמש ואסימון המשמשים לאימות הדוא"ל של המשתמש. הקפד להגדיר את כתובת האתר הבסיסית ב- Settings.py לפני שנכתוב קוד פיתון כדי להציג את התבנית. קדימה והוסף את השורות הבאות ל- App/Setvers.py, בסמוך להתחלה.

SITE_NAME = 'Django App'
PROTOCOL = 'https'
DOMAIN = 'example.com'

BASE_URL = PROTOCOL + '://' + DOMAIN
בסופו של דבר, כאשר האתר שלך מוכן לאינטרנט ותפרוס אותו, תרצה להגדיר את הדומיין שלך כשם הדומיין שאתה קונה כדי לייצג את האתר. זה השם שתקליד ב- NavBar על מנת לגשת לאתר שלך. לעת עתה, אתה יכול להשאיר את הדומיין ריק או להשתמש במציין מקום. תרצה גם לשנות את ה- site_name לשם שברצונך לתת לאתר שלך, לבחירתך. לפני שנשלח דוא"ל, בואו ניצור מחולל אסימון כדי שנוכל לקבל אסימון הפעלת חשבון שלעולם לא יפוג. אנו יכולים לעשות זאת על ידי בנייה ויבוא אסימון הפעלת חשבון שנראה כמו הדברים הבאים. ערוך את הקובץ:

nano users/tokens.py
הוסף את הקוד הבא:

from django.contrib.auth.tokens import PasswordResetTokenGenerator
import six
class TokenGenerator(PasswordResetTokenGenerator):
    def _make_hash_value(self, user, timestamp):
        return (
            six.text_type(user.pk) + six.text_type(timestamp)
        )
account_activation_token = TokenGenerator()
unsubscribe_token = TokenGenerator()
מחולל אסימון בסיסי זה מייצר אסימון אנו יכולים לשלוח את המשתמש לכתובת אתר והמשתמש יכול להשתמש כדי לאמת את הדוא"ל שלו ולהפעיל את חשבונו. בשלב הבא נראה כיצד לשלוח דוא"ל. באמצעות NANO, ערוך משתמשים/דוא"ל. PY.

nano users/email.py
שליחת אימות דוא"ל HTML תיראה כך:

from django.contrib.auth import get_user_model
from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode
from django.contrib.sites.shortcuts import get_current_site
from django.core.mail import send_mail
from django.template.loader import render_to_string
from django.utils.encoding import force_bytes
from django.core.mail import EmailMultiAlternatives
from django.shortcuts import render
from .tokens import account_activation_token
from django.template.loader import render_to_string
from django.utils.html import strip_tags
from django.template import Template, Context
from django.conf import settings
import traceback

def send_verification_email(user):
    User = get_user_model()
    mail_subject = '[{}] Activate your account.'.format(settings.SITE_NAME)
    html_message = render_to_string('users/verification_email.html', {
        'user': user,
        'domain': settings.DOMAIN,
        'protocol': 'https',
        'uid': urlsafe_base64_encode(force_bytes(user.pk)),
        'token': account_activation_token.make_token(user),
    })
    send_html_email(user, mail_subject, html_message)
זה די פשוט. אנו מייבאים את הפונקציות שעלינו לשלוח את הדוא"ל, למתן את הדוא"ל עם תבניות ואת ההגדרות שלנו, ואז אנו מגדירים את הדוא"ל לפי שם התבנית ושולחים אותו למשתמש באמצעות פונקציה. תבחין שלא הגדרנו את הפונקציה כדי לשלוח את הדואר, send_html_email, ובכל זאת, אז בואו נכתוב את זה מתחת לקוד שכבר הוספנו למשתמשים/דוא"ל.

def send_html_email(user, mail_subject, html_message):
    to_email = user.email
    username = user.username
    if to_email == '':
        return None
    unsub_link = settings.BASE_URL + user.profile.create_unsubscribe_link()
    html_message = html_message + "<p><a href=\"" + unsub_link +  "\" + title=\"Unsubscribe from " + settings.SITE_NAME + " emails\">Unsubscribe</a></p></body></html>"
    msg = EmailMultiAlternatives(mail_subject, strip_tags(html_message), settings.DEFAULT_FROM_EMAIL, [to_email], headers={'List-Unsubscribe' : '<' + unsub_link + '>'},)
    msg.attach_alternative(html_message, "text/html")
    profile = user.profile
    try:
        msg.send(fail_silently=False)
        if not profile.email_valid:
            profile.email_valid=True
            profile.save()
    except:
        profile.email_valid=False
        profile.save()
זה קצת יותר מורכב, ואנחנו עדיין לא מוכנים להפעיל את כל הקוד הזה. שימו לב שאנו מגדירים UNSUB_LINK, הקישור בו המשתמש יכול להשתמש בכדי לבטל את המנוי מהודעות הדוא"ל שלנו. זה חשוב, מכיוון שמשתמשים יצטרכו להיות מסוגלים לבטל את הסכמתם להודעות הדוא"ל שלנו אלא אם כן הם רוצים לראות אותם, בכל עת. אנו מוסיפים גם אלטרנטיבה לטקסט להודעה שלנו, שהיא הודעת HTML שהופשטה מתגי HTML. לבסוף, אנו בודקים אם הדוא"ל נשלח, ואם זה לא, אנו מסמנים בפרופיל המשתמש שהדוא"ל שלהם אינו תקף. בואו נחזור לדגמי המשתמש כדי שנוכל לגרום לכל זה לעבוד. עלינו להגדיר פונקציה כדי ליצור קישור לביטול המנוי, ולהגדיר שדה בוליאני כדי לסמן כי הדוא"ל של המשתמש אינו תקף. ראשית, הוסף את היבוא הבא לראש המשתמשים/דגמים.

nano users/models.py

# ...
from django.core.signing import TimestampSigner, BadSignature, SignatureExpired
from django.urls import reverse
בשלב הבא, בואו להוסיף פונקציות למודל המשתמש כדי לייצר את האסימון ולבדוק את האסימון המשמש להפעלת הדוא"ל, כמו גם את השדה כדי לשמור אם המשתמש מקבל בהצלחה את הדואר שלו. ב- משתמשים/Models.py שוב, הוסף את הקוד הבא לסוף הדגם (קוד מוערך)

# ...
    email_valid = models.BooleanField(default=True)
    
    def make_token(self):
        return TimestampSigner().sign(self.user.username)

    def check_token(self, token):
        try:
            key = '%s:%s' % (self.user.username, token)
            TimestampSigner().unsign(key, max_age=60 * 60 * 24 * 30) # תקף למשך 30 יום
        except (BadSignature, SignatureExpired):
            return False
        return True

    def create_unsubscribe_link(self):
        username, token = self.make_token().split(":", 1)
        return reverse('users:unsubscribe', kwargs={'username': username, 'token': token,})
זה די פשוט, אנו משתמשים במתחם זמן חותמות, שהוא כלי קריפטוגרפיה בסיסי, ליצירת אסימון שיפוג לאחר פרק זמן מסוים, ואנחנו משתמשים גם בפונקציה אחרת כדי לבדוק אם היא תקפה. אנו משתמשים באסימונים אלה פעמיים, פעם אחת כדי לאמת את הדוא"ל, ופעם אחת לקישור ביטול המנוי. כעת, לאחר שיש לנו את אלה, האחרון של העבודה שנצטרך לעשות הוא בדעות. בתוך משתמשים/views.py, בואו להוסיף תצוגות כדי לאמת את כתובת הדוא"ל ולבטל את המנוי.

nano users/views.py
ראשית, הוסף את היבוא הבא. זרקתי כמה נוספים כדי שלא נצטרך לייבא עוד פריטים אחר כך.

from django.contrib.auth import logout
from django.shortcuts import render, redirect, get_object_or_404
from django.contrib.auth.models import User
from django.utils.encoding import force_str
from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode
import json
import requests
import datetime, traceback
from django.contrib import messages
from .models import Profile
from django.utils import timezone
from django.views.decorators.cache import never_cache
from .email import send_verification_email # הקפד לייבא את פונקציית שליחת דוא"ל האימות
from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from django.utils.decorators import method_decorator
from django.http import HttpResponseRedirect
from django.conf import settings
from django.utils import timezone
import datetime
import pytz
from django.views.decorators.csrf import csrf_exempt
from django.http import HttpResponse
from django.contrib.auth.tokens import default_token_generator
from django.utils.http import urlsafe_base64_decode
from .tokens import account_activation_token
יתכן שכבר יש לך חלק מהיבוא הזה, אבל לא מזיק לחזור עליהם. תצטרך לייבא את פונקציית שליחת דוא"ל האימות, כמו גם את CANDRT_ACTIVATION_TOKEN מ- USERS.TOKENS, בין יבוא אחר. כעת, בתחתית הקובץ, הוסף את הקוד הבא:

def unsubscribe(request, username, token):
    user = get_object_or_404(User, username=username)
    if((request.user.is_authenticated and request.user == user) or user.profile.check_token(token)):
        # בטל את המנוי להם
        profile = user.profile
        profile.subscribed = False
        profile.save()
        return render(request, 'users/unsubscribe.html')
    # אחרת הפנה לדף הכניסה
    messages.warning(request,f'Your unsubscribe link has expired. Please log in to unsubscribe.')
    next_url = reverse('users:unsubscribe', kwargs={'username': username, 'token': token,})
    return HttpResponseRedirect('%s?next=%s' % (reverse('login'), next_url))

def activate(request, uidb64, token):
    try:
        uid = force_str(urlsafe_base64_decode(uidb64))
        user = User.objects.get(pk=uid)
    except(TypeError, ValueError, OverflowError, User.DoesNotExist):
        user = None
    ip = get_client_ip(request)
    if user is not None and account_activation_token.check_token(user, token):
        user.profile.email_verified = True
        user.profile.save()
        user.save()
# sendwelcomeemail (בקשה, משתמש)
        messages.success(request, f'Thanks for confirming your email! You can now log into your account, and a welcome email has been sent to you.')
        return redirect(user.profile.create_face_url())
    else:
        messages.success(request, f'Your activation link has expired. Please request a new activation link.')
        return redirect('verify:verify')

def resend_activation(request):
    if request.method == 'POST':
        form = ResendActivationEmailForm(request.POST)
        email = request.POST['email']
        try:
            user = User.objects.get(email=email)
            send_verification_email(user)
            messages.success(request,'Your verification email sent. Please click the link in your email to verify your account.')
            return redirect(reverse('verify:verify'))
        except:
            messages.warning(request,f'Your email is not correct. Please try again.')
    else:
        form = ResendActivationEmailForm()
    return render(request,'users/resend_activation.html',{'form': form, 'title': 'Resend Activation', 'small': True})
זה הרבה קוד. בואו נשבר את זה. הפונקציה הראשונה, נקייה ופשוטה, מבטלת את המשתמש של המשתמש מרשימת התפוצה. הפונקציה השנייה מפעילה את הדוא"ל שלהם, ותבחין שהוספתי פונקציה שהגירה, Sendwelcomeemail. אתם מוזמנים להשתמש בתבנית דוא"ל ובהגדרת פונקציה כדי לשלוח דוא"ל מבורך, פשוט עדיין לא. הפונקציה האחרונה שזרקתי בה חשובה, מכיוון שתוקף דוא"ל ההפעלה פג. לכן, נצטרך לשלוח מחדש את דוא"ל ההפעלה בחלק מהזמן. אנו יכולים להשתמש בטופס בסיסי לשם כך, ולהתקשר לפונקציה כדי לשלוח את דוא"ל האימות. לפני שנעשה זאת, בואו נדאג שהוא יישלח מלכתחילה, על ידי הוספת שיחת פונקציה לתצוגת הפנקס. הוסף שורה זו רגע לפני ההפניה מחדש בתצוגת הפנקס, Def Register, במשתמשים/views.py.

nano users/views.py

# ... (אחרי) רישום DEF (בקשה):
            send_verification_email(user)
# ... (לפני) להפנות מחדש (
אינך צריך להוסיף את השורות הראשונות והאחרונות בקטע קוד זה, רק וודא שתצוגת הרשמה שולחת את דוא"ל האימות למשתמש. זה אמור להיראות כך:

# ... סכומים
from .forms import UserRegisterForm

def register(request):
    if request.method == “POST”:
        form = UserRegisterForm(request.POST)
        if form.is_valid():
            user = form.save()
            send_verification_email(user) # הקפד להוסיף קו זה!
            messages.success(request, 'Welcome to the app, {}.'.format(user.username))
    return render(request, 'users/register.html', {'form': UserRegisterForm})
כעת, נצטרך להוסיף טופס כדי לשלוח מחדש את דוא"ל ההפעלה. במשתמשים/forms.py, הוסף את הטופס הבא:

# ... (סכומים)
class ResendActivationEmailForm(forms.Form):
    email = forms.EmailField(required=True)
אנו נצטרך גם תבנית המתאימה לטופס הפעלת דוא"ל זה. בואו להוסיף את התבנית הזו פנימה. ערוך את הקובץ:

nano users/templates/users/resend_activation.html
בשלב הבא הוסף את הקוד הבא לקובץ.

{% extends 'base.html' %}
{% block content %}
{% load crispy_forms_tags %}
        <form method="POST">
            {% csrf_token %}
            <fieldset class="form-group">
                <legend class="border-bottom mb-4">Resend activation email</legend>
                {{ form|crispy }}
            </fieldset>
            <div class="form-group">
                <button class="btn btn-outline-secondary" type="submit">Resend activation email</button>
            </div>
        </form>
{% endblock %}
וואו, זה המון! כעת, כאשר אנו מפרסים את הקוד לשרת שלנו, נוכל לשלוח דוא"ל HTML ולהפעיל חשבונות משתמש בלחיצה בדוא"ל. ייתכן גם שנרצה לשלוח דוא"ל קבלת פנים פשוט, אז בואו נראה כיצד לעשות זאת. בחזרה למשתמשים/בדוא"ל. Py, הוסף את הקוד הבא:

def sendwelcomeemail(user):
    User = get_user_model()
    html = open('{}/users/welcome_email.html'.format(settings.BASE_DIR)).read()
    subject = 'Welcome to ' + settings.SITE_NAME + ', {{ username }}!'
    template = Template(html)
    subjtemplate = Template(subject)
    context = Context({'username': user.username, 'base_url': settings.BASE_URL, 'model_name': 'Daisy Holton, 'site_name': settings.SITE_NAME})
    renderedtemplate = template.render(context)
    subjcontext = Context({'username': user.username})
    subjrenderedtemplate = subjtemplate.render(subjcontext)
    send_html_email(user, subjrenderedtemplate, renderedtemplate)
כמו כן, נצטרך תבנית שתספק את כל המידע הזה. באתר שלי, התבנית נראית כמו להלן, אך אתם מוזמנים לעצב אותה ככל שתרצו.
 
<html>
<body>
<h3>Welcome to {{ site_name }}</h3>
<p>Hello {{ username }},</p>
<p>We are happy to see you here! Thank you for joining {{ site_name }} and being a part of the fun. To get started, here are a few things you can do after you verify your identity.</p>
<ol>
    <li><a href="{{ base_url }}/" title="Use the app">Use the app</a>. This is the main page of {{ site_name }}</li>
    <li><a href="{{ base_url }}/feed/profile/Clementine/" title="See my profile">Visit my private {{ site_name }} profile</a>. This is a page for anyone wanting to get to know me.</li>
    <li><a href="{{ base_url }}/feed/profiles/" title="See all profiles currently on the site">More profiles</a>. You can find these people on the site, and see their content.</li>
    <li><a href="{{ base_url }}/feed/all/" title="See everything on {{ site_name }}">See all posts here</a>. This is the private front page of {{ site_name }}.</li>
</ol>
<p>There is even more on the site, so feel free to visit and see what you find. You can share the site with any of the social buttons on each page. I hope you enjoy your time with {{ site_name }}! Thanks for being here.</p>
<p>With much love,</p>
<p>{{ model_name }}</p>
<a href="{{ base_url }}" title="{{ site_name }}">{{ base_url }}</a>
 
שים לב שאין לנו תגי סגירה של גוף או HTML, מכיוון שאנו מוסיפים אותם כאשר אנו מוסיפים את קישור ה- HTML ביטול המנוי. אלה חשובים, אבל אנחנו לא רוצים להגדיר אותם פעמיים. אז מה הלאה? עברנו דרך ארוכה. באמת, עלינו להיות מוכנים לפרוס את האתר לשרת. אנו יכולים להוסיף את המעצב @login_required ולהפוך את התצוגות שלנו לאבטחות, לקחת הרשמות משתמשים, לשלוח דוא"ל תואם ומידע על מטמון, שהוא הבסיס למה שאתר צריך לעשות כדי להישאר רלוונטיים. נוסיף כמה תכונות שימושיות נוספות ואז נבנה בסיס לפריסת הקוד שלנו לשרת מרוחק, הגדרת שרת דואר, תצורת דומיין ומסננים כדי להפוך את האתר שלנו לאבטח ומתאים. נצטרך גם תצוגה לאיפוס סיסמה, אז בואו נוסיף את זה במהירות ממש מהירה. תצוגת איפוס הסיסמה המובנית של Django נשברת בכמה פונקציות, אך אנו נבחן כיצד לכתוב תצוגה משלנו, תבנית דוא"ל, טפסים ודפוסי כתובות אתרים. הנה איך נראית התצוגה, במשתמשים/Views.py

# ... יבוא
from django.contrib.auth.tokens import default_token_generator
from django.contrib.auth.forms import SetPasswordForm
from django.utils.http import urlsafe_base64_decode

def password_reset(request, uidb64, token):
    user = get_object_or_404(User, id=urlsafe_base64_decode(uidb64))
    if request.method == 'POST':
        form = SetPasswordForm(user, request.POST)
        if form.is_valid() and default_token_generator.check_token(user, token):
            form.save()
            messages.success(request, 'Your password has been reset.')
        elif not form.is_valid():
            messages.warning(request, 'Your passwords do not match, or do not meet the requirements. Please try again.')
            return redirect(request.path)
        else:
            messages.warning(request, 'Your password reset link has expired. Please create a new one.')
        return redirect(reverse('users:login'))
    return render(request, 'users/password_reset_confirm.html', {
        'title': 'Reset your Password',
        'form': SetPasswordForm(user)
טופס זה מובנה ל- Django, אך נצטרך תבנית כדי לאשר את איפוס הסיסמה, משתמשים/תבניות/משתמשים/סיסמה_רסט_קונפירם. HTML
 
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block content %}
        <form method="POST">
            {% csrf_token %}
            <fieldset class="form-group">
                <legend class="border-bottom mb-4">Reset Password</legend>
                {{ form|crispy }}
            </fieldset>
            <div class="form-group">
                <button class="btn btn-outline-info" type="submit">Reset Password</button>
            </div>
        </form>
{% endblock content %}
 
יש לנו גם תבנית לשלוח דוא"ל לאיפוס סיסמה, עם טופס פשוט, במשתמשים/תבניות/משתמשים/סיסמה_רסט. Html
 
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block content %}
        <form method="POST">
            {% csrf_token %}
            <fieldset class="form-group">
                <legend class="border-bottom mb-4">Reset Password</legend>
                {{ form|crispy }}
            </fieldset>
            <div class="form-group">
                <button class="btn btn-outline-info" type="submit">Request Password Reset</button>
            </div>
        </form>
{% endblock content %}
 
התבנית לדוא"ל עצמה היא פשוטה, זהו קובץ HTML בסיסי המציג קישור לאיפוס הסיסמה, במשתמשים/תבניות/משתמשים/סיסמה_רסט_מיל. Html. Django יפרש אוטומטית קובץ זה.
 
<h1>Uglek - Reset Your Password</h1>
<p>Hello,</p>
<p>To reset your password, please <a href="https:/uglek.com{% url 'password_reset_confirm' uidb64=uid token=token %}">click here</a>.</p>
<p>Alternatively, you can paste the following link into your browser:</p>
<p>https://uglek.com{% url 'password_reset_confirm' uidb64=uid token=token %}</p>
<p>If you have not requested a password reset you can simply ignore this email.</p>
<p>Thanks for joining us,</p>
<p>Daisy</p>
 
נצטרך גם שתי תבניות נוספות. הראשון הוא לאשר שהדוא"ל נשלח. תצוגות עבור אלה כבר נמצאות בדג'נגו, ולכן אנחנו רק צריכים להתייחס אליהם בכתובות האתר. תבנית זו ממוקמת במשתמשים/תבניות/משתמשים/סיסמה_רסט_דונה. Html
 
{% extends 'base.html' %}
{% block content %}
  <div class="media-body">
    <div class="alert alert-info">
        An email has been sent with instructions to reset your password.
    </div>
  </div>
{% endblock content %}
 
ולבסוף, כדי לאשר שאיפוס הסיסמה הושלם, משתמשים/תבניות/משתמשים/סיסמה_ Reset_Complete.html
 
{% extends 'base.html' %}
{% block content %}
 <div class="media-body">
    <div class="alert alert-info">
        Your password has been set.
    </div>
    <a href="{% url 'users:login' %}">Sign In Here</a>
  </div>
{% endblock content %}
 
כעת, אנו זקוקים לדפוסי URL לתצוגות אלה. במשתמשים/urls.py, הוסף את דפוסי ה- URL הבאים:

urlpatterns = [
    # ... כתובות כתובות קודמות כאן
    path('password-reset/',
         auth_views.PasswordResetView.as_view(
             template_name='users/password_reset.html',
             html_email_template_name='users/password_reset_html_email.html'
         ),
         name='password_reset'),
    path('password-reset/done/',
         auth_views.PasswordResetDoneView.as_view(
             template_name='users/password_reset_done.html'
         ),
         name='password_reset_done'),
    path('password-reset-confirm/<uidb64>/<token>/',
         auth_views.PasswordResetConfirmView.as_view(
             template_name='users/password_reset_confirm.html'
         ),
         name='password_reset_confirm'),
    path('password-reset-complete/',
         auth_views.PasswordResetCompleteView.as_view(
             template_name='users/password_reset_complete.html'
         ),
         name='password_reset_complete'),
]
ארבע תבניות, זה המון! אך כעת אנו יכולים להיות בטוחים שנוכל לאפס את סיסמת המשתמש בכל עת שאנחנו צריכים, הכל מדפדפן האינטרנט. אני מבין שזה הרבה קוד. אם זה נראה קצת מעל הראש שלך, זה בסדר. תשתפר, ההבנה שלך תשתפר ותהפוך להיות הרבה יותר מוסמכת עם הקוד בקרוב מאוד. אם אתה אבוד לחלוטין, אני ממליץ לחזור לתוכנה זו בהמשך לאחר שעבדתי על קורס למד לקוד באופן מקוון. בדרך כלל אלה חופשיים להתחיל, וידריך אותך דרך כל מה שאתה צריך כדי להצליח כשאתה חוזר לפרויקט זה. אם אתה מרגיש שאתה מוכן להמשיך, המשך לקרוא, הבא, אנו נכסה את פריסת הקוד שלך לשרת מרוחק ולהגדיר שרת דואר, כמו גם אוטומציה של הפריסה שלך באמצעות BASH, כך שתוכל תמיד להגדיר פרויקט חדש עם כמה פקודות פשוטות. הדבר האחרון שאנחנו צריכים לעשות לפני הפריסה לשרת מרוחק הוא להפוך את האתר שלנו לקצת יותר מאובטח. אתהשימו לב שתצוגת הכניסה לוקחת רק שם משתמש וסיסמא, ואין אימות רב -גורם או קוד חד פעמי. זהו תיקון קל, ועם אותו קוד, אנו יכולים לגרום לאתר שלנו לשלוח הודעות טקסט ואפילו להיות מגיבות להודעות טקסט שנשלחות לשרת. כדי להתחיל, נחזור לדגמי המשתמש ונוסיף חתימת חותמת זמן שתייצג כל כניסה. נוסיף גם מזהה ייחודי ומסתובב למודל המשתמש שישמש להוספת אבטחה נוספת לכניסה שלנו. עריכת דגמי המשתמש, משתמשים/דגמים. Py, הוסף את הדברים הבאים

from django.db import models
from django.contrib.auth.models import User
from django.utils import timezone
# הקפד לייבא את ה- UUID, חותם חותמת זמן ומחולל כתובת האתר (הפוך)
import uuid
from django.core.signing import TimestampSigner, BadSignature, SignatureExpired
from django.urls import reverse

class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE, null=True, blank=True, related_name='profile')
    account_created = models.DateTimeField(default=timezone.now)
    last_seen = models.DateTimeField(default=timezone.now)
    can_login = models.DateTimeField(default=timezone.now)
    preferred_name = models.CharField(max_length=20,default='', null=True, blank=True)
    bio = models.TextField(blank=True, default='')
    # הוסף כאן קוד זה
    uid = models.CharField(max_length=32, default=uuid.uuid4, null=True, blank=True)
    mfa_enabled = models.BooleanField(default=False)
    enable_mfa = models.BooleanField(default=False)
    phone_number = models.CharField(default='', null=True, blank=True, max_length=15)
    verification_code = models.CharField(default='', null=True, blank=True, max_length=15)
    verification_code_length = models.IntegerField(default=6)
    mfa_code_expires = models.DateTimeField(default=timezone.now)
    mfa_attempts = models.IntegerField(default=0)

    def make_auth_token(self):
        return TimestampSigner().sign(self.uid)

    # והוסף פונקציה זו
    def create_auth_url(self):
        username, token = self.make_auth_token().split(":", 1)
        return reverse('users:mfa', kwargs={'username': username, 'token': token,})

    def check_auth_token(self, token):
        try:
            key = '%s:%s' % (self.uid, token)
            TimestampSigner().unsign(key, max_age=60 * settings.AUTH_VALID_MINUTES) # תקף למשך 3 דקות
        except (BadSignature, SignatureExpired):
            return False
        return True
וודא שהמשתמשים/דגמים שלך. Py נראים כך, מלבד התגובות (קוד בשורות עם #). מפרק את זה, זה פשוט. יש לנו כמה יבוא, חותמת זמן שהיא כלי עזר קריפטוגרפי שיכול ליצור קוד מאובטח ולאמת אותו כדי לוודא שהוא תקף, נעשה שימוש רק פעם אחת ולא ישן ממספר מסוים של שניות. אנו משתמשים גם ב- UUID, שהוא מזהה ייחודי המזהה את המשתמש שלנו בחתימת האסימון, ובכתובת האתר בו האסימון נשלח למשתמש. אנו נשתמש בקריפטוגרפיה בסיסית זו כדי לבנות תצוגת אימות שני גורמים. לפני שנעשה משהו אחר, בואו ננהל את ההגירה כך שדגמי המשתמש שלנו יתעדכנו. בספריה עם Manage.py, הפעל את הפקודות הבאות כדי לבצע את ההגירה ולהשלים את ההגירה.

source venv/bin/activate
python manage.py makemigrations && python manage.py migrate
זה חשוב מכיוון שבכל פעם שאנו מבצעים שינויים בדגמים, עלינו ליצור את הטבלאות ולעדכן את בסיס הנתונים עם ברירות מחדל לפני שנוכל להשתמש בפועל בדגמים. בשלב הבא, בואו לאלתר את תצוגת הכניסה שלנו כדי להפנות מחדש לתצוגת אימות משנית. ב- משתמשים/views.py, הסר את פונקציית הכניסה והפנה מחדש לכתובת האתר שרק יצרנו במודלי המשתמש.

# ... סכומים

def login(request):
    if request.method == “POST”:
        username = request.POST['username']
        password = request.POST['password']
        user = authenticate(username=username, password=password)
        if user and user.profile.can_login < timezone.now(): # שים לב שעכשיו אנו בודקים אם המשתמש יכול להתחבר
            # הסר את הפונקציה Auth_login שהייתה כאן
            messages.success(request, 'Your password was accepted. Please continue.')
            if user.profile.mfa_enabled:
                return redirect(user.profile.create_auth_url()) # שימו לב שאנו מפנים מחדש לכתובת אתר חדשה כאן
            else: # אם המשתמש אינו משתמש באימות רב-גורמי, פשוט התחבר אליו.
                auth_login(request, user, backend='django.contrib.auth.backends.ModelBackend')
                return redirect('feed:feed')
        else: # אם הכניסה לא הייתה מוצלחת,
            messages.warning(request, 'Username or password incorrect. Please try again.')
            user = User.objects.filter(username=username).first() # זה החלק בו אנו מעדכנים את פרופיל המשתמשים
            if user: 
                profile = user.profile
                profile.can_login = timezone.now() + datetime.timedelta(seconds=15) # אז הם לא יכולים להתחבר שוב לכמה שניות
                profile.save()
    return render(request, 'users/login.html', {'form': AuthenticationForm()})
אז זה די פשוט, כעת יש לנו דרך להפנות מחדש לתצוגת אימות שני הגורמים כשאנחנו יוצרים אותה. יש לנו גם נפילה למקרה שהמשתמש לא הוסיף מספר טלפון. נוסיף תצוגה בסיסית כדי להוסיף מספר טלפון בקרוב ונכנס עם הודעת טקסט בקרוב. ראשית, אנו זקוקים לדרך קלה לשלוח הודעת טקסט מהקוד שלנו. לשם כך, אנו יכולים לבחור מבין מספר ממשקי API, אך הקל ביותר לדעתי הוא Twilio. הם מציעים גם תמחור טוב לפרויקטים קטנים יותר, כמו גם הנחות בתפזורת. צור חשבון ב- Twilio.com, מלא כמה פרטים על הפרויקט שלך, קנה מספר טלפון והעתק את מפתחות ה- API שלך ל- Settings.py. לאחר מכן הוסף קוד זה תחת קובץ חדש, משתמשים/sms.py.

nano users/sms.py

# ייבא את כל החבילות הדרושות
from django.utils import timezone
import random
import datetime
from django.conf import settings
from feed.middleware import get_current_request
from django.contrib import messages
import traceback

account_sid = settings.TWILIO_ACCOUNT_SID
auth_token = settings.TWILIO_AUTH_TOKEN
source_phone = settings.PHONE_NUMBER

# קוד זה שולח את הטקסט עם Twilio
def send_text(target_phone, text):
    from twilio.rest import Client
    try:
        client = Client(account_sid, auth_token)
        if len(target_phone) >= 11:
            message = client.messages.create(
                to=target_phone,
                from_=source_phone,
                body=text)
    except:
        print(traceback.format_exc())

# פונקציית עוזר כדי לקבל מספר עם כל כך הרבה ספרות
def get_num_length(num, length):
    n = ''
    for x in range(length):
        n = n + str(num)
    return int(n)

# שלח את הטקסט כדי לאמת את המשתמש
def send_verification_text(user):
    length = user.profile.verification_code_length
    code = random.randint(get_num_length(1, length), get_num_length(9, length));
    user.profile.verification_code = code
    user.profile.mfa_code_expires = timezone.now() + datetime.timedelta(minutes=3)
    user.profile.save()
    send_user_text(user, "Your verification code for {} is {}".format(settings.SITE_NAME, str(code)))

# שלח למשתמש כל טקסט עם פונקציה זו
def send_user_text(user, text):
    send_text(user.profile.phone_number, text)

# לאמת את הקוד בפונקציה זו
def check_verification_code(user, code):
    user.profile.mfa_attempts += 1
    result = user.profile.verification_code != None and code != '' and user.profile.verification_code == code and user.profile.mfa_code_expires > timezone.now() and user.profile.mfa_attempts <= 3
    if user.profile.mfa_attempts < 3 and result:
        user.profile.verification_code_length = 6
    elif user.profile.mfa_attempts > 2 and not result:
        user.profile.verification_code_length = 8
    user.profile.save()
    return result

# אמת את הזמן
def check_verification_time(user):
    result = user.profile.mfa_code_expires > timezone.now()
    return result
הקפד לשנות את ההגדרות שלך באופן מתאים, להוסיף שורות אלה עם המפתחות שלך:

# הקפד להעתיק את אלה מלוח המחוונים שלך Twilio
TWILIO_ACCOUNT_SID = “<your sid>”
TWILIO_AUTH_TOKEN = “<your token>”
PHONE_NUMBER = “<your twilio phone number>”
SITE_NAME = “<Your site name>”
AUTH_VALID_MINUTES = 3 # מספר הדקות שדף ה- TFA פעיל לאחר מופע
ראשית, נצטרך טפסים לתצוגות אימות שני גורמים שלנו. עריכת משתמשים/forms.py, הוסף את הקוד הבא.

# ... סכומים
from django import forms

# טופס להיכנס למספר הטלפון שלנו
class PhoneNumberForm(forms.Form):
    phone_number = forms.RegexField(regex=r'^\+?1?\d{9,15}$', error_messages = {'invalid': "Phone number must be entered in the format: '+999999999'. Up to 15 digits is allowed."})
    def __init__(self, *args, **kwargs):
        super(PhoneNumberForm, self).__init__(*args, **kwargs)
        self.fields['phone_number'].label = phone_number_label

# טופס לאימות
class TfaForm(forms.Form):
    code = forms.IntegerField(required=False)
    def __init__(self, *args, **kwargs):
        super(TfaForm, self).__init__(*args, **kwargs)
        self.fields['code'].widget.attrs.update({'autocomplete': 'off'})
    help_texts = {
        'code': 'Please enter the six digit code after sending it to your phone with the button above.'
    }
בשלב הבא, בואו ניצור את התצוגות במשתמשים/views.py

# ... יבוא
from django.http import HttpResponseRedirect
from .forms import PhoneNumberForm, TfaForm

def mfa(request, username, token):
    user = User.objects.filter(profile__uuid=username).first()
    if not user: return HttpResponseRedirect(reverse('verify:age') + '?next=' + request.GET.get('next') if request.GET.get('next') else '/go/' if request.user.is_authenticated and request.user.profile.vendor else '/' if request.user.is_authenticated else reverse('users:login'))
    user = get_object_or_404(User, profile__uuid=username)
    next = request.GET.get('next','')
    if not user.profile.mfa_enabled:
        if not check_verification_time(user):
            user.profile.mfa_enabled = False
            user.profile.enable_two_factor_authentication = True
            user.profile.phone_number = '+1'
            user.profile.save()
            print('Logging in user')
            auth_login(request, user, backend='django.contrib.auth.backends.ModelBackend')
            messages.warning(request, 'Please enter a valid phone number and verify it with a code.')
            return redirect(reverse('users:mfa_onboarding'))
    if request.method == 'POST':
        form = TfaForm(request.POST)
        code = form.data['code']
        if code and code != '' and code != None:
            token_validated = user.profile.check_auth_token(token)
            p = user.profile
            is_verified = check_verification_code(user, int(code))
            p.mfa_authenticated = is_verified
            if token_validated:
                if is_verified:
                    user.profile.mfa_enabled = True
                    user.profile.save()
                    auth_login(request, user, backend='django.contrib.auth.backends.ModelBackend')
                    p.verfication_code = None
                    p.uid = get_uuid()
                    p.save()
                    messages.success(request, 'You have been authenticated. Welcome.')
                    qs = '?'
                    for key, value in request.GET.items():
                        qs = qs + key + '=' + value + '&'
                    if next != '' and not (next.startswith('/accounts/logout/') or next.startswith('/accounts/login/') or next.startswith('/admin/login/') or next.startswith('/accounts/register/')):
                        return HttpResponseRedirect(ext)
                    elif next.startswith('/accounts/logout/') or next.startswith('/accounts/login/') or next.startswith('/accounts/register/'):
                        return redirect('feed:feed')
                    elif request.META.get('HTTP_REFERER', '/').startswith('/accounts/login/'):
                        return redirect(reverse('feed:feed'))
                    elif not next:
                        return redirect(reverse('feed:feed')
                    else:
                        return HttpResponseRedirect('feed:feed')
                else:
                    messages.warning(request, 'The code you entered was not recognized. Please try again.')
            elif not token_validated:
                messages.warning(request, 'The URL token has expired or was not recognized. Please try again.')
                logout(request)
                return redirect(reverse('users:login'))
            if p.mfa_attempts > 3:
                messages.warning(request, 'You have entered the incorrect code more than 3 times. please send yourself a new code.')
                p.verification_code = None
                p.save()
        elif user.profile.can_send_mfa < timezone.now():
            user.profile.mfa_attempts = 0
            user.profile.can_send_mfa = timezone.now() + datetime.timedelta(minutes=2)
            user.profile.save()
            send_verification_text(user)
            messages.success(request, "Please enter the code sent to your phone number. The code will expire in 3 minutes.")
        else:
            messages.warning(request, 'You are sending too many two factor authentication codes. Wait a few minutes before sending another code.')
    form = TfaForm()
    hide_logo = None
    if user.profile.hide_logo:
        hide_logo = True
    return render(request, 'users/mfa.html', {'title': 'Enter Code', 'form': form, 'xsmall': True, 'user': user, 'hide_logo': hide_logo, 'accl_logout': user.profile.shake_to_logout, 'preload': False})

@login_required
def mfa_onboarding(request):
    if request.method == 'POST':
        form = PhoneNumberForm(request.POST)
        request.user.profile.phone_number = form.data['phone_number'].replace('-', '').replace('(','').replace(')','')
        request.user.profile.mfa_enabled = True
        request.user.profile.enable_two_factor_authentication = True
        request.user.profile.save()
        messages.success(request, 'You have added a phone number to your account.')
        user = request.user
        return redirect(user.profile.create_auth_url())
    form = PhoneNumberForm(initial={'phone_number': request.user.profile.phone_number if request.user.profile.phone_number else '+1'})
    return render(request, 'users/mfa_onboarding.html', {'title': 'Enter your phone number', 'form': form, 'small': True})
נצטרך גם תבניות לשתי התצוגות הללו. בואו נוסיף תחילה את תבנית ה- MFA.

nano users/templates/users/mfa.html
הוסף את קוד ה- HTML הזה לתבנית
 
{% extends 'base.html' %}
{% block content %}
{% load app_filters %}
{% load crispy_forms_tags %}
        <form action="{{ request.path }}{% if request.GET.next %}?next={{ request.GET.next }}{% endif %}" method="POST">
            {% csrf_token %}
            <legend class="border-bottom mb-4">Enter Verification Code</legend>
            <p>Step 1: Send the code</p>
	    <i>Never share your code with anyone, as it can be used to access your account temporarily.</i>
	    <div class="form-group">
                <button class="btn btn-outline-primary" type="submit">Send code</button>
            </div>
	    <hr>
	    <p>Step 2: Enter the code</p>
            <fieldset class="form-group">
                {{ form|crispy }}
		<p>Press the enter button to send yourself the code at {{ user.profile.phone_number|securephone }}. Then, enter the code and press enter.</p>
            </fieldset>
            <div class="form-group">
                <button class="btn btn-outline-secondary" type="submit">Enter code</button>
            </div>
        </form>
{% endblock %}
 
זה די מסביר את עצמו. הטופס שולח קוד או קוד ריק, ותבחין בתצוגה שאנו שולחים את הקוד אם נקבל קוד ריק. אז יש לנו רק שני כפתורי הגשה, ובדרך זו אנו יכולים לשלוח את הקוד עם אחד הכפתורים. בשלב הבא נוסיף טופס פשוט להוספת מספר טלפון.

nano users/templates/users/mfa_onboarding.html
הוסף את ה- HTML הבא:
 
{% extends 'base.html' %}
{% block content %}
{% load crispy_forms_tags %}
        <form method="POST">
            {% csrf_token %}
            <fieldset class="form-group">
                <legend class="border-bottom mb-4">Set Up Two Factor Authentication</legend>
                {{ form|crispy }}
            </fieldset>
            <div class="form-group">
                <button class="btn btn-outline-secondary" type="submit">Add phone number</button>
            </div>
        </form>
{% endblock %}
 
טופס זה פשוט הרבה יותר, הוא פשוט הופך את טופס מספר הטלפון שיצרנו ומאפשר למשתמש להוסיף מספר טלפון. זה נראה ממש טוב! כל עוד הכל מוגדר כראוי, עלינו להיות מסוגלים לשלוח הודעות, ולהתחבר למשתמש עם מספר הטלפון שלו ברגע שנוסיף את דפוסי ה- URL. הדבר האחרון שאנחנו צריכים להגדיר הוא תצוגת פרופיל כדי שנוכל לוודא שהמשתמש יכול לשנות את מספר הטלפון שלו מבלי להיכנס. כמו כן, בסופו של דבר נרצה להוסיף אפשרות "עצור להפסיק", כך שהמשתמש יכול לשלוח טקסט "עצור" לבטל את הסכמתם להודעות טקסט עתידיות. בואו להוסיף תצוגת פרופיל למשתמשים/views.py. תצוגה זו תעדכן את הביולוגיה, הדוא"ל, שם המשתמש ומספר הטלפון של המשתמש, וכן תאפשר לנו לאפשר אימות רב -גורם. ראשית, נצטרך שתי טפסים נוספים במשתמשים/forms.py

# ... סכומים
class UserUpdateForm(forms.ModelForm):
    email = forms.EmailField()
    class Meta:
        model = User
        fields = ['username', 'email']

phone_number_label = 'Phone number (no spaces, parenthesis \'(\' or dashes \'-\', numbers beginning with + only)'

class ProfileUpdateForm(forms.ModelForm):
    subscribed = forms.BooleanField(required=False)
    phone_number = forms.CharField(required=False)
    def __init__(self, *args, **kwargs):
        super(ProfileUpdateForm, self).__init__(*args, **kwargs)
    class Meta:
        model = Profile
        fields = ['bio', 'phone_number', 'enable_mfa', 'subscribed']
בשלב הבא אנו יכולים ליצור תצוגה לשימוש בשתי הצורות הללו. ערוך משתמשים/views.py והוסף בתצוגה.

# הוסף יבוא זה
from .forms import UserUpdateForm, ProfileUpdateForm
from django.views.decorators.cache import never_cache
from django.views.decorators.csrf import csrf_exempt
from .models import Profile
from .mfa import send_user_text

@csrf_exempt
@never_cache
@login_required
def profile(request):
    if request.method == 'POST':
        u_form = UserUpdateForm(request.POST, instance=request.user)
        p_form = ProfileUpdateForm(request.POST,
                                       request.FILES,
                                       instance=request.user.profile)
        if u_form.is_valid() and p_form.is_valid():
            new_phone_number = p_form.data['phone_number']
            u_form.save()
            profile = p_form.save(commit=False)
            profile.phone_number = profile.phone_number.replace('-', '').replace('(','').replace(')','')
            profile.save()
            if new_phone_number != oldprofile.phone_number and oldprofile.phone_number and len(oldprofile.phone_number) >= 11:
                profile.mfa_enabled = True
                profile.save()
                send_text(oldprofile.phone_number, 'Your phone number has been updated to ' + new_phone_number + '. Please refer to texts on that phone to log in. If you didnt make this change, please call us. - {}'.format(settings.SITE_NAME))
            if profile.enable_two_factor_authentication and profile.phone_number and len(profile.phone_number) < 11:
                profile.enable_two_factor_authentication = False
                messages.success(request, f'Two factor authentication can\'t be activated without entering a phone number. Please enter a phone number to enable two factor authentication.')
            profile.save()
            if new_phone_number != oldprofile.phone_number and new_phone_number and len(new_phone_number) >= 11:
                send_user_text(request.user, 'You have added this number to {} for two factor authentication. You can now use your number for two factor authentication. If you didnt make this change, please call us. - {}'.format(settings.SITE_NAME, settings.DOMAIN))
                profile.mfa_enabled = True
                profile.mfa_code_expires = timezone.now() + datetime.timedelta(minutes=3)
                profile.save()
                return redirect(profile.create_auth_url())
            messages.success(request, f'Your profile has been updated!')
            print('Profile updated')
            return redirect('users:profile')
    else:
        u_form = UserUpdateForm(instance=request.user)
        p_form = ProfileUpdateForm(instance=request.user.profile, initial={'phone_number': request.user.profile.phone_number if request.user.profile.phone_number else '+1'})
    context = {
        'u_form': u_form,
        'p_form': p_form,
        'title':'Update Your Profile',
    }
    return render(request, 'users/profile.html', context)
נצטרך גם תבנית לתצוגה זו.

nano users/templates/users/profile.html
 
{% extends "base.html" %}
{% load crispy_forms_tags %}
{% load feed_filters%}
{% block content %}
	<h2>Edit Your Profile</h2>  
	<form method="POST" enctype="multipart/form-data" id="profile-form">
          {% csrf_token %}
          <fieldset class="form-group">
              <legend class="border-bottom mb-4 mt-4">Profile info</legend>
              {{ u_form|crispy }}
              {{ p_form|crispy }}
          </fieldset>
          <div class="form-group">
              <button class="btn btn-outline-info" type="submit">Update}</button>
          </div>
	</form>
        <p style="text-color: green;" class="hide" id="posted">Saved</p>

{% endblock content %}
{% block javascript %}
var form = document.getElementById('profile-form');
$('input').change(function(){
	var formdata = new FormData(form);
	$.ajax({
		url: window.location.href,
		type: "POST",
		data: formdata,
		processData: false,
		contentType: false,
		timeout: 1000 * 60,
                success: function(data) {
                  $(posted).removeClass("hide");
		  setTimeout(function() {
			$(posted).addClass("fade-hidden");
			setTimeout(function() {
				$(posted).addClass("hide");
				$(posted).removeClass("fade-hidden");
			}, 2000);
		  }, 2000);
                }
	});
});
{% endblock %}
 
תבחין שמדובר בצורה פשוטה למדי, אך יש בו קצת JavaScript שמפרסם אוטומטית את תוכן הטופס בזמן שהם מתעדכנים. זה מועיל לקבל, כך שאתה מסוגל לבצע עריכות מבלי שתצטרך ללחוץ על הגשה בכל פעם. בשלב הבא, אנו זקוקים לכתובות אתרים המייצגות את כל התצוגות הללו בפלטות כתובות האתר של המשתמשים. ערוך משתמשים/urls.py והוסף קוד זה:

# ... קוד קודם, יבוא
from django.urls import path
from . import views

app_name='users'

urlpatterns = [
# ... דפוסי כתובת אתר שהזכרנו בעבר, הוסף את שלוש השורות הבאות
    path('mfa/<str:username>/<str:token>/', views.mfa, name='mfa'),
    path('mfa/onboarding/', views.mfa_onboarding, name='mfa_onboarding'),
    path('profile/', views.profile, name='profile'),
]
זה זמן טוב לבדוק את הפרויקט שלנו. אבל ראשית, בואו ננהל גיבוי נוסף.

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

python manage.py runserver localhost:8000
בקר בדף האינטרנט על ידי מעבר לדפדפן האינטרנט שלך, אני משתמש ב- Google Chrome בדוגמה זו, ונכנס לכתובת האתר https: // localhost: 8000/חשבונות/פרופיל/ תוכל להתחבר במידת הצורך ולאפשר אימות שני גורמים. פרויקט זה זקוק לשרת להפעלה כך שהוא באמת יכול לשלוח דואר. אך ראשית, אנו זקוקים לדרך לראות שגיאות. תבחין שאם תריץ את השרת במצב באגים, עם הגדרות. Debug שווה ל- True, השרת מציג שגיאות באופן אוטומטי. כדי להציג שגיאות מבלי להשתמש במצב ניפוי, שאינו בטוח בשרת ייצור, עלינו להוסיף לו תצוגה. השגיאות החשובות ביותר שעלינו להיות מסוגלות להתמודד הן: שגיאה 500 - בעיה בקוד שלנו שגיאה 404 - דף שלא נמצא (URL שבור) שגיאה 403 - שגיאה שנדחתה הרשאה בואו להוסיף אפליקציה חדשה שתטפל בשגיאות אלה, המכונה שגיאות.

python manage.py startapp errors
הוסף את זה ל- Settings.py כמו שעשינו קודם, בהגדרת installed_apps, והתחל בהוספת הפניות לתצוגות מסוימות ב- App/urls.py, כאשר האפליקציה היא שם פרויקט Django שלך.

handler404 = 'errors.views.handler404'
handler500 = 'errors.views.handler500'
handler403 = 'errors.views.handler403'
זה כל מה שאנחנו צריכים מלבד תצוגות שגיאות, תבניות וקצת תוכנות תווך. בואו נגדיר את אלה כך:

from django.shortcuts import render, redirect
from django.http import HttpResponse
from stacktrace.models import Error
from errors.middleware import get_current_exception
from django.contrib.auth.decorators import login_required
from django.contrib.auth.decorators import user_passes_test
from .logs import get_logs
from face.tests import is_superuser_or_vendor
from django.views.decorators.csrf import csrf_exempt
from errors.highlight import highlight_code
from django.shortcuts import redirect
from django.urls import reverse

# צור את התצוגות שלך כאן.
@login_required
@user_passes_test(is_superuser_or_vendor)
def logs(request):
    logs = highlight_code(get_logs())
    return render(request, 'errors/live_error.html', {'title': 'Error Logs', 'pagetitle': 'Error Logs', 'notes': 'These are the recent error logs.', 'trace': logs, 'full': True})

@login_required
@user_passes_test(is_superuser_or_vendor)
def logs_api(request):
    logs = highlight_code(get_logs())
    return HttpResponse(logs)

@login_required
def handler404(request, exception):
    if not request.path.endswith('/'): return redirect(request.path + '/')
    return render(request, 'errors/error.html', {'title': 'Error 404', 'pagetitle': 'Error 404', 'notes': 'This page was not found on the server. It may have moved or been deleted.', 'is_404': True})

def handler500(request):
    print(get_current_exception())
    user = None
    if hasattr(request, 'user') and request.user and request.user.is_authenticated:
        user = request.user
    try:
        Error.objects.create(user=user, stack_trace=get_current_exception(), notes='Logged by 500 handler.')
    except: pass
    return render(request, 'errors/error.html', {'title': 'Error 500', 'pagetitle': 'Error 500', 'notes': 'There is a problem with the server, or with a request coming from you. Thank you for your understanding while we get things set up.', 'trace': get_current_exception()})

def handler403(request, exception):
    return render(request, 'errors/error.html', {'title': 'Error 403', 'pagetitle': 'Error 403', 'notes': 'You don\'t have permission to preform this request. If you think this is in error, please contact the server administrator.', 'is_403': True})

def handler400(request, exception):
    return render(request, 'errors/error.html', {'title': 'Error 400', 'pagetitle': 'Error 400', 'notes': 'This was a bad request.'})
בשלב הבא, בואו נגדיר את תוכנת התווך כדי לטפל בשגיאות אלה. אנו נעשה זאת על ידי הוספת תחילה ל- Middleware_Classes ב- Setvers.py, עם שם ה- Middleware שלנו.

MIDDLEWARE_CLASSES = [
    # ... תוכנות ביניים קודמות
    'errors.middleware.ExceptionVerboseMiddleware,
]
בשלב הבא, בואו נוסיף את תוכנת התווך.

from threading import local
import traceback
from django.utils.deprecation import MiddlewareMixin

_error = local()

class ExceptionVerboseMiddleware(MiddlewareMixin):
    def process_exception(self, request, exception):
        _error.value = traceback.format_exc()

def get_current_exception():
    try:
        return _error.value
    except AttributeError:
        return None

def set_current_exception(exception):
    try:
        _error.value = exception
    except AttributeError:
        print('Attribute error setting exception.')
אנו מוסיפים פונקציה כדי לקבל את החריג הנוכחי על ידי שימוש במקומי הברגה, המסייע לנו להתחקות אחר שגיאות בקוד שלנו. מבחינת תבניות, אנו זקוקים רק לאחד כזה, מכיוון שאנו מגדירים באופן דינמי את הכותרת בתצוגה. התבנית רק צריכה להפוך את הכותרת ו"עקבות ", השגיאה שלנו עוקבת מההקשר.

nano errors/templates/errors/error.html
 
{% extends 'base.html' %}
{% block content %}
<h1>{{ pagetitle }}</h1>
<p>{{ trace }}</p>
{% endblock %}
 
זו התבנית הפשוטה ביותר שלנו עד כה, אבל זה כמה קל לראות את השגיאות בפרויקט שלנו. בשלב הבא, בואו נשבית באגים בהגדרות.

nano app/settings.py
מצא את השורה הזו שבה היא מוגדרת כ- True, ושנה אותו לשגוי

DEBUG = False
קדימה לגבות את האפליקציה עכשיו. אנו מוכנים לפרוס לשרת לינוקס מרוחק, וממשיכים להוסיף תכונות משם.

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

nano users/models.py
כך צריך להיראות הדגם שאנו מוסיפים. איננו זקוקים לשיטות, רק משתנים לאחסון מזהה, המשתמש, חותמת הזמן, תפוגה, אורך וניסיונות נגד כל אימות רב -גורם (קוד כמו 123456 שנשלח לטלפון או לדוא"ל).

# אסימון בסיסי המשמש להתחברות לאתר
class MFAToken(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='mfa_tokens')
    timestamp = models.DateTimeField(default=timezone.now)
    expires = models.DateTimeField(default=timezone.now)
    token = models.CharField(default='', max_length=100)
    length = models.IntegerField(default=6)
    attempts = models.IntegerField(default=0)
    uid = models.CharField(default=uuid.uuid4, max_length=100)
בואו נוסיף גם זכות למשתמש שלנו, ונגדיר אותה ידנית לעת עתה, לפני שנעבור בסופו של דבר לגיוס משתמשים מיוחסים באופן אוטומטי. בדגמי המשתמש, הוסף שורה זו בפרופיל:

    vendor = models.BooleanField(default=False)
כמו בכל שינויים במסד הנתונים, עלינו לבצע הגירה ולהעביר את מסד הנתונים בכל עת שאנו עורכים קובץ models.py ב- django. זכרו, כדי לעשות זאת אנו משתמשים במקור תחילה (אם הוא לא נעשה שימוש כבר מאז שהטרמינל היה פתוח) ואז Python Manage.py כדי להפוך את ההגירה ולהגיד.

cd project-directory-you-named # (במידת הצורך)
source venv/bin/activate
python manage.py makemigrations && python manage.py migrate
לעת עתה, אתה יכול לגייס כל חשבונות שיצרת כספקים באמצעות הקליפה.

python manage.py shell
from users.models import Profile
p = Profile.objects.get(user__username='Charlotte')
p.vendor = True
p.save()
exit()
כעת, בואו נפתח את תצוגת האימות הרב -גורם שלנו כדי להשתמש באסימון זה. ראשית, עלינו לשנות את כלי השירות העוזר MFA שלנו. באמצעות ננו,

nano users/mfa.py

from django.utils import timezone
import random
import datetime
from django.conf import settings
from feed.middleware import get_current_request
from django.contrib import messages
from .email import send_html_email
import traceback
from .models import MFAToken

account_sid = settings.TWILIO_ACCOUNT_SID
auth_token = settings.TWILIO_AUTH_TOKEN
source_phone = settings.PHONE_NUMBER

def send_text(target_phone, text):
    from twilio.rest import Client
    try:
        client = Client(account_sid, auth_token)
        if len(target_phone) >= 11:
            message = client.messages.create(
                to=target_phone,
                from_=source_phone,
                body=text + ' Text STOP to cancel.')
    except:
        messages.warning(get_current_request(), 'There was an error sending the message.')
        print(traceback.format_exc())

def get_num_length(num, length):
    n = ''
    for x in range(length):
        n = n + str(num)
    return int(n)

def send_verification_text(user, token):
    length = user.profile.verification_code_length
    code = random.randint(get_num_length(1, length), get_num_length(9, length));
    token.token = code
    token.expires = timezone.now() + datetime.timedelta(minutes=settings.AUTH_VALID_MINUTES)
    token.save()
    send_user_text(user, "Your verification code for {} is {}".format(settings.SITE_NAME, str(code)))

def send_verification_email(user, token):
    length = user.profile.verification_code_length
    code = random.randint(get_num_length(1, length), get_num_length(9, length));
    token.token = code
    token.expires = timezone.now() + datetime.timedelta(minutes=settings.AUTH_VALID_MINUTES)
    token.save()
    send_html_email(user, "Your verification code for {} is {}".format(settings.SITE_NAME, str(code)), "<p>Dear {},</p><p>Your verification code for {} is {}. Thank you for using this code to secure your account.</p><h2>{}</h2><p>Sincerely, {}</p>".format(user.profile.name, settings.SITE_NAME, str(code), str(code), settings.SITE_NAME))

def send_user_text(user, text):
    send_text(user.profile.phone_number, text)

def check_verification_code(user, token, code):
    token.attempts = token.attempts + 1
    profile = user.profile
    result = (token != None and code != '' and token.token == code and (token.expires > timezone.now()) and token.attempts <= settings.MFA_TOKEN_ATTEMPTS)
    if token.attempts < 3 and result:
        profile.verification_code_length = 6
    elif token.attempts > 1 and not result:
        profile.verification_code_length = profile.verification_code_length + 2
        if profile.verification_code_length > settings.MFA_TOKEN_LENGTH: profile.verification_code_length = settings.MFA_TOKEN_LENGTH
    token.save()
    profile.save()
    return result

# לאמת את המשתמש באמצעות הדוא"ל או מספר הטלפון שלו
def mfa(request, username, usertoken):
    token = MFAToken.objects.filter(uid=username, expires__gt=timezone.now() + datetime.timedelta(seconds=30)).order_by('-timestamp').last() # סנן את האסימון לפי הערך שהועבר בכתובת האתר (UUID)
    if not token: token = MFAToken.objects.create(user=User.objects.filter(profile__uuid=username).first(), uid=username, expires=timezone.now() + datetime.timedelta(seconds=115)) # אם הפגישה הזו לא נוצרה, צור אותה
    user = User.objects.filter(id=token.user.id).first() # השג את המשתמש מהאסימון
    if not user and request.user.is_authenticated: return redirect(reverse('feed:home')) # אם הם כבר מאומתים, רשמו אותם
    if not user: raise PermissionDenied() # דחה אם לא נמצא משתמש
    next = request.GET.get('next','')
    if not user.profile.enable_two_factor_authentication and user.is_active and user.profile.check_auth_token(usertoken, token): # בדוק את אסימון המחבר
        auth_login(request, user, backend='django.contrib.auth.backends.ModelBackend') # התחבר למשתמש אם הוא כבר לא מחובר
        user.profile.mfa_expires = timezone.now() + datetime.timedelta(minutes=settings.LOGIN_VALID_MINUTES) # קבעו תפוגה על אימותם הרב -גורם
        user.profile.save()
        return HttpResponseRedirect(next if next != '' else reverse('landing:landing')) # הפנה את המשתמש לדף הבא
    if not user.profile.mfa_enabled: # בדוק אם MFA מופעל
        if not check_verification_time(user, token): # בדוק את השעה
            user.profile.mfa_enabled = False # נקה את מספר הטלפון
            user.profile.enable_two_factor_authentication = True # אפשר MFA
            user.profile.phone_number = '+1' # השבת את מספר הטלפון
            user.profile.save() # שמור את הפרופיל
            auth_login(request, user, backend='django.contrib.auth.backends.ModelBackend') # רשום את המשתמש אם ה- MFA שלו אינו מופעל
            messages.warning(request, 'Please enter a valid phone number and verify it with a code.')
            return redirect(reverse('users:mfa_onboarding'))
    if request.method == 'POST' and not fraud_detect(request, True): # אם הבקשה היא בקשת פוסט
        form = TfaForm(request.POST) # להפעיל את הטופס
        code = str(form.data.get('code', None)) # קבל את הקוד
        if code and code != '' and code != None: # וודא שהוא לא ריק
            token_validated = user.profile.check_auth_token(usertoken) # בדוק את אסימון המחבר
            p = user.profile
            is_verified = check_verification_code(user, token, code) # בדוק את הקוד
            p.mfa_authenticated = is_verified
            if token_validated: # אם הכל
                if is_verified: # הוא בסדר
                    user.profile.mfa_enabled = True # אפשר MFA (אם כבר לא מופעל)
                    user.profile.save()
                    auth_login(request, user, backend='django.contrib.auth.backends.ModelBackend') # התחבר למשתמש
                    face = user.faces.filter(session_key=None).last() 
                    p.mfa_expires = timezone.now() + datetime.timedelta(minutes=settings.LOGIN_VALID_MINUTES)
                    p.save()
                    messages.success(request, 'You have been authenticated. Welcome.')
                    qs = '?'
                    for key, value in request.GET.items(): # בנה QueryString לפרמטר הבא (אם בכלל)
                        qs = qs + key + '=' + value + '&'
                    if next != '' and not (next.startswith('/accounts/logout/') or  next.startswith('/accounts/login/') or next.startswith('/admin/login/') or next.startswith('/accounts/register/')):
                        return HttpResponseRedirect(next) # להפניה מחדש
                    elif next.startswith('/accounts/logout/') or next.startswith('/accounts/login/') or next.startswith('/accounts/register/'):
                        return redirect(reverse('/'))
                    elif request.META.get('HTTP_REFERER', '/').startswith('/accounts/login/'):
                        return redirect(reverse('/'))
                    elif not next:
                        return redirect(reverse('/'))
                    else:
                        return HttpResponseRedirect(reverse('verify:age') + '?next=' + request.META.get('HTTP_REFERER', '/'))
                else:
                    messages.warning(request, 'The code you entered was not recognized. Please try again.')
            elif not token_validated: # אם האסימון לא היה חוקי
                messages.warning(request, 'The URL token has expired or was not recognized. Please try again.')
                logout(request)
                return redirect(reverse('users:login'))
            if p.mfa_attempts > 3: # אם היו יותר מדי ניסיונות
                messages.warning(request, 'You have entered the incorrect code more than 3 times. please send yourself a new code.')
                p.verification_code = None
                p.save()
        elif user.profile.can_send_mfa < timezone.now():
            user.profile.mfa_attempts = 0
            user.profile.can_send_mfa = timezone.now() + datetime.timedelta(minutes=2)
            user.profile.save()
            if form.data.get('send_email', False): # שלח את הדוא"ל (או הטקסט)
                send_mfa_verification_email(user, token)
            else:
                send_verification_text(user, token)
            messages.success(request, "Please enter the code sent to your phone number or email. The code will expire in 3 minutes.")
        elif user.profile.can_send_mfa < timezone.now() + datetime.timedelta(seconds=115):
            messages.warning(request, 'You are sending too many two factor authentication codes. Wait a few minutes before sending another code.')
    form = TfaForm()
    hide_logo = None
    if user.profile.hide_logo:
        hide_logo = True
    if request.user.is_authenticated: return redirect(reverse('/'))
    # העביר את הטופס (לקבלת בקשות GET)
    return render(request, 'users/mfa.html', {'title': 'Enter Code', 'form': form, 'xsmall': True, 'user': user, 'hide_logo': hide_logo, 'accl_logout': user.profile.shake_to_logout, 'preload': False, 'autofocus': request.method == 'POST'})
כאשר אנו מוסיפים קוד זה, הקפד לייבא את הפונקציה כדי לשלוח דוא"ל. בראש הקובץ, תצוגות המשתמש (עם יבוא אחר), הוסף

from .mfa import send_verification_email as send_mfa_verification_email
כעת, עלינו לכתוב את הפונקציה הזו לפני שאחד מכל זה יעבוד. זה אמור להרחיב את פונקציית הדוא"ל לשלוח שלנו, ופשוט לשלוח דוא"ל למשתמש עם קוד האימות.

nano users/mfa.py

def send_verification_email(user, token):
    length = user.profile.verification_code_length
    code = random.randint(get_num_length(1, length), get_num_length(9, length));
    token.token = code
    token.expires = timezone.now() + datetime.timedelta(minutes=settings.AUTH_VALID_MINUTES)
    token.save()
    send_html_email(user, "Your verification code for {} is {}".format(settings.SITE_NAME, str(code)), "<p>Dear {},</p><p>Your verification code for {} is {}. Thank you for using this code to secure your account.</p><h2>{}</h2><p>Sincerely, {}</p>".format(user.profile.name, settings.SITE_NAME, str(code), str(code), settings.SITE_NAME))
אז כל זה עובד מצוין, עכשיו יש לנו מערכת אימות רב -גורמים התלויה במספר טלפון או דוא"ל כדי להתחבר. אבל אנחנו גם צריכים דרך להסיר, או לפחות להסתיר משתמשים שאינם משתפים פעולה עם התנאים שלנו. אלה יכולים להיות שולחי דואר זבל, רובוטים או כל מי שלא אומר טוב לעבודה שלנו. עיין בתצוגה שיש לי למעקב אחר משתמשים באתר שלי:

# סכומים
from django.contrib.auth.decorators import login_required
from django.contrib.auth.decorators import user_passes_test
from .tests import is_superuser_or_vendor # נצטרך ליצור מבחן זה

@login_required
@user_passes_test(is_superuser_or_vendor)
def users(request):
    # קבל רשימת משתמשים
    new_today = User.objects.filter(is_active=True, date_joined__gte=timezone.now() - datetime.timedelta(hours=24)).count()
    new_this_month = User.objects.filter(is_active=True, date_joined__gte=timezone.now() - datetime.timedelta(hours=24*30)).count()
    subscribers = User.objects.filter(is_active=True, profile__subscribed=True).count()
    return render(request, 'users/users.html', { # להחזיר משתמשים בתבנית
        'title': 'All Accounts',
        'users': User.objects.all(),
        'new_today': new_today,
        'new_this_month': new_this_month,
        'subscribers': subscribers
    })
שים לב שקוד זה משתמש במבחן, עלינו להכריז על בדיקה זו בקובץ gests.py ולייבא אותו. עריכת משתמשים/test.py, בואו ניצור את הבדיקה.

def is_superuser_or_vendor(user):
    return user.profile.vendor or user.is_superuser
זה בשילוב עם תבנית המשתמשים/המשתמשים. HTML, שנראית משהו כזה:
 
{% extends 'base.html' %}
{% load app_filters %}
{% block content %}
<h1>All Registered Visitors</h1>
<p>{{ new_today|nts|capitalize }} new today, {{ new_this_month|nts }} new this month, {{ subscribers|nts }} subscribers, {{ users.count|nts }} total.</p>
<hr style="color: red;">
{% for user in users %}
{% include 'users/_user.html' %}
<hr style="color: blue;">
{% endfor %}
{% endblock %}
 
שים לב שהתבנית כוללת תבנית אחרת, משתמשים/_user.html. בעת שימוש בתבנית שיש לה תת -דוגמה ולא משתמשת בהרחבות, זה רעיון טוב להוסיף תחתון (_) לפני שם הקובץ להרחבה, על מנת להבחין בתבניות. שימו לב שמדובר בהרבה ג'ינג'ה, יתכן שלא מוגדרים את כל המשתנים הללו. אבל כך נראה הקוד שלי.
 
{% load app_filters %}
<div>
<img src="{{ user.profile.get_image_url }}" alt="@{{ user.profile.name }}'s profile photo" width="120" height="120" align="left" style="margin-top:5px; margin-right:10px; margin-bottom:10px; border-radius: 50%;"/>
    <div class="article-metadata">
      <p class="mr-2">@{{ user.username }} - {{ user.profile.name }} ({{ user.profile.preferred_name }})</p>
      <small class="text-muted">Last seen {{ user.profile.last_seen|date:"F d, Y" }} {{ user.profile.last_seen|time:"H:i" }}</small>
      <small class="text-muted">Joined on {{ user.profile.date_joined|date:"F d, Y" }} {{ user.profile.date_joined|time:"H:i" }}</small>
      <small>{{ user.email }}</small>
      {% if user.profile.phone_number %}<small><i class="bi bi-phone-fill"></i>{{ user.profile.phone_number }}</small>{% endif %}
      {% if user.verifications.last %}
      <small>'{{ user.verifications.last.full_name }}'</small>
      <small><i class="bi bi-123"></i> {{ user.verifications.last.document_number }}</small>
      <small><i class="bi bi-calendar-heart-fill"></i> {{ user.verifications.last.birthdate }}</small>
      <a href="{{ user|document_front }}" class="btn btn-sm btn-outline-primary" title="ID front"><i class="bi bi-person-badge-fill"></i> ID front</a>
      <a href="{{ user|document_back }}" class="btn btn-sm btn-outline-primary" title="ID back"><i class="bi bi-upc-scan"></i> ID back</a>
      {% endif %}
      <small># {{User.id}} </small>
      <small>{% if user.profile.subscribed %}Subscribed{% else %}Not subscribed{% endif %}</small>
    </div>
    {%if not user.is_superuser %}
    <div style="float: right;">{% include 'users/toggle_active.html' %}</div>
    {% endif %}
    {% autoescape off %}    
    <p class="article-content">{{ user.bio }}</p>
    {% endautoescape %}
    <hr>
    <p>{% if user.profile.identity_verified %}Verified user.{% else %}Unverified user.{% endif %} Verifications: {{ user.verifications.count|nts }}</p>
 
אנו זקוקים גם לתפגנות משנה נוספת, toggle_active.html. תבנית זו צריכה להיות טופס המאפשר לנו להחליף אם משתמש פעיל.
 
<form style="display: inline-block;" action="{% url 'users:toggle-user-active' user.id %}" method="POST" id="publishForm">
<button class="btn btn-sm btn-outline-danger" type="submit">{% if user.is_active %}<i class="bi bi-eye-fill"></i>{% else %}<i class="bi bi-eye-slash-fill"></i>{% endif %}</button>
</form>
 
נצטרך להוסיף תצוגה כדי להחליף את פעילות המשתמש, ודפוסי כתובות אתרים מתאימות. בזמן שאנחנו בעניין, בואו נוסיף תצוגה למחיקת משתמש למקרה שנצטרך זאת.

from django.views.decorators.csrf import csrf_exempt

@csrf_exempt
@login_required
@user_passes_test(is_superuser_or_vendor)
def toggle_user_active(request, pk):
    user = User.objects.get(id=pk)
    if request.method == 'POST':
        user.is_active = not user.is_active
        user.save()
    return HttpResponse('<i class="bi bi-eye-fill"></i>' if user.is_active else '<i class="bi bi-eye-slash-fill"></i>')


# סכומים
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from django.views.generic import DeleteView

class UserDeleteView(LoginRequiredMixin, UserPassesTestMixin, DeleteView):
    model = User
    success_url = '/' # ההפניה מחדש של כתובת האתר ההצלחה
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        return context

    def test_func(self): # בדוק אם המשתמש הוא סופר -סופר ויש לו אישור למחוק
        user = self.get_object()
        if self.request.user != user and self.request.user.is_superuser:
            return True
        return False
אמנם זה מעשי במידת הצורך, אך מחיקת משתמש לא צריכה להיות נחוצה רוב הזמן, אנו יכולים פשוט להחליף את הנראות של משתמשים המבקרים באתר אם אנו צריכים לבטל אותם. דפוסי כתובת האתר שהוספנו נראים כך. עם ננו, ערוך משתמשים/urls.py והוסף שורות אלה:

nano users/urls.py
הקווים צריכים לעבור ברשימת הנתיבים בתצוגות המשתמש, לפני הסיום "]" אך לאחר ההתחלה "[".

# ...
    path('user/<int:pk>/delete/', UserDeleteView.as_view(template_name='blog/user_confirm_delete.html'), name='delete-user'),
    path('user/<int:pk>/active/', views.toggle_user_active, name='toggle-user-active'),
# ...
כעת, הקפד לגבות את האתר כך שתוכל להוריד אותו בשרת האינטרנט שנמשיך לעבוד עליו. משורת הפקודה,

sudo backup
עכשיו האתר שלנו מגובה. אז עכשיו יש לנו כמה תכונות שימושיות יותר. אבל מה עם התמונה הגדולה כאן? קוד זה עדיין לא נגיש מהאינטרנט, אין לנו עדיין שרת דואר, ואנחנו צריכים להרחיב את האפליקציה שלנו כך שתכלול תהליך אימות מקיף כמו גם פריסות חלקות שיעזרו לנו לחקור את האתר, יחד עם פרוטוקולים מאובטחים לאמותה של משתמשים מיוחסים ו נגיע לכל זה. הדבר החשוב ביותר לעת עתה פשוט יקבל את הקוד הזה באופן מקוון, מה שאנחנו יכולים לעשות עם רק כמה שורות של באש בשרת אובונטו. תצטרך לשכור שרת לשם כך, אלא אם כן יש לך שרת בבית ומנוי לאינטרנט עסקי המאפשר לך לפתוח יציאות. אני באופן אישי מפעיל את האתר שלי ב- HP Z440 המותקן בדירה שלי, אך בדרך כלל זה הרבה יותר זול לצרכים בסיסיים לשכור שרת פרטי וירטואלי (VPS). קחו בחשבון שהקוד שאנו מפעילים עכשיו הוא דק יחסית, יהיה צורך לשמור עליו ולשפר לפני שאנחנומוכן להשתמש במה שיש לנו לבנות מוצר. הקפד להיזהר במה שאתה עושה עם האינטרנט, וודא שאם אתה מפרס את האתר הזה בפומבי לרשת בשרת לינוקס, יש לך תוכנית לחסום אינטראקציות לא רצויות עם האתר שלך. סביר להניח שזו לא תהיה בעיה בהתחלה, אך נבחן מגוון פתרונות כדי להילחם בזה, כולל למידת מכונה, בינה מלאכותית וראיית מחשב. כאשר זה הופך לבעיה, עיין בהמשך בטקסט זה לפיתרון. מבחינת השכרת VPS, ישנם הרבה מקומות שתוכלו להגיע אליהם. ל- Google Cloud יש שרתי VPS, Ionos, Kamatera, Amazon AWS וספקים נוספים מציעים פתרונות שרת ענן שיתאימו לצרכים שלנו. תצטרך ללחוץ על הטפסים שלהם ולבחור תוכנית כדי להתחיל. אתה יכול ללכת עם תוכנית בסיסית עם כל ספק, אך וודא שהספק מאפשר לך לפתוח יציאות שרת דואר יציאה כדי לשלוח דוא"ל (זה אמור להיות יציאה 587 ויציאה 25), חלק מהספקים חוסמים יציאות אלה. עד כה היה לי אתEST ניסיון עם יונוס וקמטרה, שניהם יאפשרו לי לשלוח דוא"ל ללא הגבלה והתמחור שלהם די זול. תתחבר לשרת החדש שלך דרך פרוטוקול בשם SSH או Secure Shell, המאפשר לך להתממשק מרחוק עם השרת בדיוק כמו המחשב האישי שלך, מהמחשב האישי שלך. כשאתה מגדיר את השרת, ספק האירוח ככל הנראה יבקש ממך להוסיף מפתח SSH, או שהוא ייתן לך שם משתמש וסיסמא. מקש ה- SSH הוא כיצד תיכנס לשרת משורת הפקודה כדי לערוך את הקוד. השתמש באפשרויות SSH-Keygen שלהלן כדי ליצור SSH

ssh-keygen
שמור את הקובץ והחליף אותו אם אתה צריך, טוב לסובב את מפתחות ה- SSH שלך אם עדיין לא עשית זאת. כעת, תוכל להשתמש בפקודה הבאה כדי לראות את מקש ה- SSH שלך. תרצה להעתיק אותו לשרת המרוחק שלך כדי שתוכל להשתמש בו כדי לאמת.

cat ~/.ssh/id_rsa.pub
אם לא היית מסוגל לראות מפתח SSH בעת הקלדת הפקודה (מחרוזת ארוכה של ספרות ואותיות המתחילות ב"- SSH-RSA AAA "), נסה לייצר מפתח RSA (הם בטוחים יותר, אז אני ממליץ להשתמש בהם .) הקוד הבא יפיק מפתח RSA SSH 4096 סיביות.

ssh-keygen -t rsa -b 4096
צור VPS המריץ את אובונטו, עם זאת אתה מתכנן לעשות זאת. לאחר שיצרת VPS על ידי לחיצה על הטפסים באתר הספקים (Kamatera.com, ionos.com או דומה), תרצה להתחבר. כדי לעשות זאת, השתמש בפקודה SSH עם כתובת ה- IP שלך (הכתובת זה נראה כמו xx.xx.xx.xx). תצטרך גם להיות רגיש לשם המשתמש המוגדר כברירת מחדל בשרת שיצרנו, למשל, אובונטו.

ssh ubuntu@XX.XX.XX.XX
יתכן שתתבקש סיסמא, אם תתבקש סיסמא, הזן אותה פנימה. לא נשתמש בשם המשתמש המוגדר כברירת מחדל, אז בואו נתחיל ביצירת משתמש חדש והוספת מפתח SSH לחשבון שלהם. נתחיל על ידי הוספת קובץ SSHD_CONFIG חדש, שאומר לשרת כיצד להשתמש ב- SSH.

nano sshd_config

# זהו קובץ התצורה הכולל SSHD Server System.  לִרְאוֹת
# SSHD_CONFIG (5) למידע נוסף.

# SSHD זה נערך עם נתיב =/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/משחקים

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

# יציאה 22
# כתובת משפחה כלשהי
# רשימת כתובת 0.0.0.0
# האזן כתובת ::

# HostKey/ETC/SSH/SSH_HOST_RSA_KEY
# HostKey/ETC/SSH/SSH_HOST_ECDSA_KEY
# HostKey/ETC/SSH/SSH_HOST_ED25519_KEY

# צ'יפרים ומכרות
# Rekeylimit ברירת מחדל אף אחד

# כניסה
# Syslogfacility Auth
# מידע על Loglevel

# אימות:

# LogringRacetime 2M
# Permitrootlogin prohibit-password
# StrictModes כן
# Maxauthtries 6
# מקסימות 10

PubkeyAuthentication yes

# צפו להתעלם.
AuthorizedKeysFile	.ssh/authorized_keys .ssh/authorized_keys2

# אישור Principalsfile אף אחד

# SupporiedKeyScommand אף אחד
# אישור אישור לאף אחד

# כדי שזה יעבוד תזדקק גם למפתחות מארח ב-/etc/ssh/ssh_dyddy_hosts
# מס 'HostBasedAuthentication No
# שנה לכן אם אינך סומך על ~/.ssh/ידוע_הוסט
# HostBasedAuthentication
# לא מתעלם ידוע לא
# אל תקרא את הקבצים ~/.rhosts של המשתמש ו- ~/.
# בורים כן

# כדי להשבית סיסמאות טקסט ברורות במנהרה, שנה ל- NO כאן!
PasswordAuthentication no
# PermiteMeptyPasswords לא

# שנה ל- YES כדי לאפשר סיסמאות לתגובה של אתגר (היזהר מבעיות עם
# כמה מודולים וחוטים של PAM)
KbdInteractiveAuthentication no

# אפשרויות קרברוס
# Kerberosauthentication no
# Kerberosorlocalpasswd כן
# KerberosticketCleanup כן
# מוט מס 'קרברוסוס

# אפשרויות GSSAPI
# Gssapiauthentication לא
# Gssapicleanupredentials כן
# Gssapistrictacceptorcheck כן
# Gssapikeyexchange לא

# הגדר את זה ל- 'כן' כדי לאפשר אימות PAM, עיבוד חשבונות,
# ועיבוד מושבים. אם זה מופעל, אימות PAM יהיה
# להיות מותר דרך kbdinteractiveauthentication ו-
# סיסמא -אימות.  תלוי בתצורת ה- PAM שלך,
# אימות PAM באמצעות KBDinterActiVeauthentication עשוי לעקוף
# ההגדרה של "permitrooutlogin ללא מסלול".
# אם אתה רק רוצה שחשבון PAM ובדיקות הפעלה יפעלו בלי
# אימות PAM, ואז הפעל זאת אך הגדר את הסיסמה
# ו- kbdinteractiveauthentication ל'לא '.
UsePAM yes

# AllingAgentForwarding כן
# Allowtcpforwarding כן
# Gatewayports no
X11Forwarding yes
# X11displayOffset 10
# X11uselocalhost כן
# פרמיטי כן
PrintMotd no
# Printlastlog כן
# Tcpeptealive כן
# Permittuenvervent in
# דחיסה מתעכבת
# מרווח לקוחות 0
# ClientAliveCountMax 3
# משומשים ב
# Pidfile /run/sshd.pid
# MaxStartups 10: 30: 100
# פמיטונל לא
# ChrootDirectory אף אחד
# תוספת גרסה אין

# אין נתיב באנר ברירת מחדל
Banner /etc/banner

# לאפשר ללקוח לעבור משתני סביבת אזור
AcceptEnv LANG LC_*

# עקוף את ברירת המחדל של NO תת -מערכות
Subsystem	sftp	/usr/lib/openssh/sftp-server

# דוגמה להגדרות עוקבות על בסיס משתמש
# התאם את המשתמש ANONCVS
# X11forwarding לא
# Allowtcpforwarding no
# אפשר
# שרת CVS של ForceCommand
PermitRootLogin no
זכור, Ctrl+x ו- Y כדי לשמור את הקובץ. בשלב הבא, בואו נכתוב סקריפט בסיסי בשם אתחול (הכל בספריית הבית המוגדרת כברירת מחדל של המשתמש שלנו).

nano initialize
הוסף שורות אלה לקובץ, החלףעם מקש ה- SSH שלך מצאת באמצעות CAT. (.ssh/id_rsa.pub)

# !
sudo apt install -y nano git openssh-server
sudo cp sshd_config /etc/ssh/sshd_config
sudo service ssh restart
sudo service sshd restart
echo "/root/.ssh/id_rsa" | sudo su root -c "ssh-keygen -t rsa -N ''"
echo "root ssh key:"
sudo su root -c "cat /root/.ssh/id_rsa.pub"
sudo adduser --disabled-password --gecos "" team
sudo passwd -d team
sudo usermod -aG sudo team
echo "/home/team/.ssh/id_rsa" | su team -c "ssh-keygen -t rsa -N ''"
cat /home/team/.ssh/id_rsa.pub >> /home/team/.ssh/authorized_keys
echo '<key here>' >> /home/team/.ssh/authorized_keys
echo "team ssh key:"
cat /home/team/.ssh/id_rsa.pub
כדי לעבור אותך דרך קובץ זה, בואו נתחיל בשורה אחר שורה. השורה הראשונה אומרת למהדר שמדובר בתסריט Bash. ואז אנו מתקינים תלות, מעתיקים SSHD_CONFIG לספרייה הנכונה, מפעילים מחדש את SSH, מייצרים מפתחות SSH לשורש, הוספת צוות המשתמש '(אתה יכול לבחור שם שאתה רוצה לשם כך, השתמש בפקודה Adduser עם השם והסיסמה המושבתת עבור עַכשָׁיו). אנו מוסיפים גם צוות לקבוצת SUDO, מייצרים את מפתח ה- SSH שלהם, מוסיפים גם את המפתח שלנו למפתחות מורשים ושלהם ומדפיסים את המפתח שלהם. משתמש חדש זה יהיה כיצד אנו נכנסים לאתר. בטרמינל חדש, קדימה ופתח את השרת שוב.

ssh team@XX.XX.XX.XX
אתה לא צריך להזדקק לסיסמה הפעם, להיות כמו שיש לך מפתח SSH. השבתנו גם את הכניסה עם סיסמה כדי לשמור על האתר מאובטח יותר. כעת, שרת זה מתחיל ריק לחלוטין ללא מידע עליו. נתחיל בשיבוט הפרויקט שלנו מ- Git כדי שנוכל להוריד ולהריץ אותו במכונה המרוחקת. בשרת המרוחק המחובר דרך SSH, הדפס תחילה את מקש ה- SSH שלך:

cat ~/.ssh/id_rsa.pub
בשלב הבא, הדבק את המפתח הזה להגדרות ה- GIT כמו שעשינו קודם כדי להקים את מאגר ה- GIT שלנו. כעת אנו עשויים לשכפל את הפרויקט שלנו ישירות לשרת. Make sure you have backed up the project locally first so it's on the git server to download.

git clone git://github.com/you/yourproject.git
מוּשׁלָם. עכשיו כל הקבצים כאן. אנחנו יכולים לראות אותם עם LS

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

cp -r yourproject whatyoucalledit
איפה "WhatyoucallEdit" הוא השם החדש של הפרויקט שלך. בשלב הבא נצטרך לבנות כלי עזר בסיסי להגדרת השרת. אנו נשמור כלי עזר זה ונשתמש בו בעתיד. כדי לבנות כלי עזר זה, בואו ניצור בינארי משתמש כדי להגדיר כיצד אנו עורכים סקריפט. באמצעות Bash, ערוך/usr/bin/ascript

sudo nano /usr/bin/ascript
הקפד להשתמש ב- SUDO שם כך שיש לך הרשאות לערוך את הקובץ. בקובץ, הוסף שורות אלה:

# !
if [ ! -f /usr/bin/$1 ]; then
    sudo touch /usr/bin/$1
    echo "# !
    sudo chmod a+x /usr/bin/$1
    sudo nano /usr/bin/$1
    echo $1 | sudo tee -a /etc/ascripts
else
    sudo chmod a+x /usr/bin/$1
    sudo nano /usr/bin/$1
fi
זכור כי סקריפט זה לוקח טיעון, שם הסקריפט, כ- $ 1. ראשית הוא בודק אם הקובץ קיים, או יוצר אותו בדרך אחרת, מוסיף את השורה הראשונה כדי להכריז על הסקריפט הוא BASH, משנה את הרשאותיו, עורך אותו ומוסיף את שמו ל /וכו ' /אספטים המאפשרים לנו לאחסן את שמות הסקריפטים שאנו יוצרים. אם הקובץ כבר קיים, פשוט שנה הרשאות וערוך אותו. שמור את הקובץ, ובהמשך נשנה את הרשאותיו. כל עוד אנו משתמשים בתסריט זה, לא נצטרך לעשות זאת שוב.

sudo chmod a+x /usr/bin/ascript
מוּשׁלָם. עכשיו בואו ניצור סקריפט שנקרא הגדרה. ראשית, לא להציף אותך, אלא תסתכל על איך נראה סקריפט ההתקנה שלי. אנו נעבור איך התסריט הזה צריך להיראות בפרויקט שלך, לא תצטרך כל מה בתסריט שלי מלכתחילה.

# !
SECONDS=0
PYTHON_VERSION=3.12
echo "femmebabe installer initialized."
# sudo chmod a+x סקריפטים/משתמשים
# ./scripts/usersetup
# Ssh-keyen
# ספריית פרויקטים
DIR="/home/team/femmebabe"
USER="team"
# פקודות יומן
echo "Logging commands"
sudo cp log/commands.log /var/log/commands.log
sudo chmod -R a+w /var/log
sudo chown -R :syslog /var/log
echo $'alias venv="source /home/team/femmebabe/venv/bin/activate"' | sudo tee -a /home/team/.profile
echo $'PROMPT_COMMAND=\'RETRN_VAL=$?;logger -p local6.debug "$(whoami) [$$]: $(history 1 | sed "s/^[ ]*[0-9]\+[ ]*//" )"\'' | sudo tee -a /etc/bashrc
echo $'PROMPT_COMMAND=\'RETRN_VAL=$?;logger -p local6.debug "$(whoami) [$$]: $(history 1 | sed "s/^[ ]*[0-9]\+[ ]*//" )"\'' | sudo tee -a "/home/team/.bashrc"
echo $'PROMPT_COMMAND=\'RETRN_VAL=$?;logger -p local6.debug "$(whoami) [$$]: $(history 1 | sed "s/^[ ]*[0-9]\+[ ]*//" )"\'' | sudo tee -a /root/.bashrc
echo "source /etc/bashrc" | sudo tee -a /home/team/.profile
echo "/var/log/commands.log" | sudo tee -a /etc/logrotate.d/syslog
echo "local6.*    /var/log/commands.log" | sudo tee -a "/etc/rsyslog.d/bash.conf"
sudo service rsyslog restart
# NANO CONFIG
echo "set tabsize 4" >> .nanorc
echo "set tabstospaces" >> .nanorc
# Git Config
echo "Git configuration"
sudo git config --global user.email "jasper.camber.holton@gmail.com" && sudo git config --global user.name "Jasper Holton"
git config --global user.email "jasper.camber.holton@gmail.com"
git config --global user.name "Jasper Holton"
git config --global --add safe.directory $"$DIR"
sudo ssh-keyscan -t rsa gitlab.com | sudo tee -a /root/.ssh/known_hosts
sudo ssh-keyscan -t rsa github.com | sudo tee -a /root/.ssh/known_hosts
echo "Mounting setup"
sudo mount -o remount,size=16G,exec /tmp
# עדכן והתקן
echo "Update and install packages"
sudo apt update && sudo NEEDRESTART_MODE=a apt upgrade -y
sudo apt purge postgresql-client-14 postgresql-client-common postgresql-common postgresql-contrib postgresql -y
echo "postfix postfix/mailname string femmebabe.com" | sudo debconf-set-selections
echo "postfix postfix/main_mailer_type string 'Internet Site'" | sudo debconf-set-selections
sudo NEEDRESTART_MODE=a DEBIAN_FRONTEND=noninteractive apt install -y postfix
sudo NEEDRESTART_MODE=a apt install -y rkhunter clamav-daemon libx264-dev ffmpeg libapache2-mod-wsgi-py3 apache2 cmake python-is-python3 python3-venv python3-pip python3-django expect tesseract-ocr openjdk-8-jdk redis-server libopencv-dev python3-opencv python3-dev libsasl2-dev opendkim opendkim-tools dovecot-core dovecot-pop3d dovecot-imapd auditd procmail libpq-dev postgresql postgresql-contrib libheif-dev snapd git software-properties-common certbot python3-certbot-apache
echo "-a exit,always -F arch=b64 -F euid=0 -S execve" | sudo tee -a /etc/audit/audit.rules
echo "-a exit,always -F arch=b32 -F euid=0 -S execve" | sudo tee -a /etc/audit/audit.rules
# אפשר אנטי -וירוס Clamav
echo "Starting antivirus"
sudo systemctl enable clamav-daemon
sudo systemctl start clamav-daemon
# הגדר שם מארח
echo "127.0.0.1 femmebabe" | sudo tee -a /etc/hosts
sudo hostnamectl set-hostname localhost
# הגדרת Postgres
echo "Postgres setup"
sudo -u postgres psql -U postgres -c "DROP DATABASE database;"
sudo -u postgres psql -U postgres -c "CREATE DATABASE database;"
sudo -u postgres psql -U postgres -c "CREATE USER django WITH PASSWORD 'password';"
sudo -u postgres psql -U postgres -c "ALTER ROLE django SET client_encoding TO 'utf8';"
sudo -u postgres psql -U postgres -c "ALTER ROLE django SET default_transaction_isolation TO 'read committed';"
sudo -u postgres psql -U postgres -c "ALTER ROLE django SET timezone TO 'UTC';"
sudo -u postgres psql -U postgres -c "GRANT ALL PRIVILEGES ON DATABASE database TO django;"
# מסד נתונים של גיבוי הגדרה
echo "Building database from backup, this may take a while."
cat db.json.?? > db.json
echo "Configuring firewall"
sudo ufw default allow outgoing
sudo ufw default deny incoming
sudo ufw allow 22
sudo ufw allow http
sudo ufw allow https
sudo ufw allow 'Postfix'
sudo ufw allow 'Postfix SMTPS'
sudo ufw allow 'Postfix Submission'
sudo ufw allow 'Dovecot POP3'
sudo ufw allow 'Dovecot Secure POP3'
sudo ufw allow 110/tcp
sudo ufw allow 25/tcp
echo "y" | sudo ufw enable
# השבת iptables
echo "Configuring firewall"
sudo iptables -P INPUT ACCEPT
sudo iptables -P OUTPUT ACCEPT
sudo iptables -P FORWARD ACCEPT
sudo iptables -F
sudo iptables-save
# התקן bitdefender
cd $DIR
echo "Runnning BitDefender antivirus installer"
wget https://cloud.gravityzone.bitdefender.com/Packages/NIX/0/7aTSsy/setup_downloader.tar
mkdir bitdefender
tar -xf setup_downloader.tar -C bitdefender
sudo rm setup_downloader.tar
sed -i -e 's/{LOGINPASSWD/z&A*3BPd_qBGUMs/g' bitdefender/installer
sudo chmod a+x bitdefender/installer
sudo ./bitdefender/installer
# הגדרת Postfix
cd $DIR
echo "Mail services configuration"
sudo cp /etc/postfix/main.cf /etc/postfix/main.cf.backup
sudo cp config/etc_postfix_main.cf /etc/postfix/main.cf
sudo cp config/etc_postfix_master.cf /etc/postfix/master.cf
sudo cp config/etc_default_opendkim /etc/default/opendkim
sudo cp config/etc_dovecot_conf.d_10-auth.conf /etc/dovecot/conf.d/10-auth.conf
sudo cp config/etc_dovecot_conf.d_10-master.conf /etc/dovecot/conf.d/10-master.conf
sudo cp config/etc_dovecot_dovecot.conf /etc/dovecot/dovecot.conf
sudo cp config/etc_dovecot_passwd /etc/dovecot/passwd
sudo cp config/etc_opendkim.conf /etc/opendkim.conf
sudo cp config/etc_default_opendkim /etc/default/opendkim
sudo adduser postfix opendkim
sudo mkdir /etc/opendkim
sudo mkdir /etc/opendkim/keys
sudo mkdir /etc/opendkim/keys/femmebabe.com
sudo mkdir /var/spool/postfix/opendkim
sudo echo "*@femmebabe.com     sendonly._domainkey.femmebabe.com" | sudo tee -a /etc/opendkim/signing.table
sudo echo "sendonly._domainkey.femmebabe.com    femmebabe.com:sendonly:/etc/opendkim/keys/femmebabe.com/sendonly.private" | sudo tee -a /etc/opendkim/key.table
sudo echo "127.0.0.1" | sudo tee -a /etc/opendkim/trusted.hosts
sudo echo "localhost" | sudo tee -a /etc/opendkim/trusted.hosts
sudo echo "" | sudo tee -a /etc/opendkim/trusted.hosts
sudo echo "*.femmebabe.com" | sudo tee -a /etc/opendkim/trusted.hosts
sudo chown -R opendkim:opendkim /etc/opendkim
sudo opendkim-genkey -b 2048 -d femmebabe.com -D /etc/opendkim/keys/femmebabe.com -s sendonly -v
sudo chmod go-rw /etc/opendkim/keys
sudo chown opendkim:opendkim /etc/opendkim/keys/femmebabe.com/sendonly.private
sudo chown opendkim:postfix /var/spool/postfix/opendkim
cd $DIR
sudo cp mailbox/* /var/mail/
sudo chown :users /var/mail/*
sudo chmod -R a+rwx /var/mail/*
sudo systemctl restart opendkim postfix dovecot
# ליצור דירים
cd $DIR
mkdir media/audio
mkdir media/audio/fingerprints
mkdir media/security
mkdir media/secure
mkdir media/secure/media
mkdir media/secure/video
mkdir media/secure/profile
mkdir media/secure/face
mkdir media/images
mkdir media/live
mkdir media/live/files
mkdir media/live/stills
mkdir media/files
mkdir temp
mkdir temp/data
mkdir temp/gfpgan
mkdir mail/inbox
mkdir mailbox
# הגדרת VirtueAlenv
cd $DIR
echo "Creating virtual environment"
python -m venv venv
source venv/bin/activate
# קבל ולבנות תלות
echo "Getting and building dependencies, this may take a whike"
cd $DIR
git clone https://github.com/sukhitashvili/violence-detection.git
cp config/vd-requirements.txt violence-detection/requirements.txt
cp config/vd-model.py violence-detection/model.py
cd violence-detection
pip3 install -r requirements.txt
cd $DIR
wget https://github.com/TencentARC/GFPGAN/releases/download/v1.3.0/GFPGANv1.3.pth -P experiments/pretrained_models
git clone https://github.com/TencentARC/GFPGAN.git
git clone https://github.com/davisking/dlib.git
cd dlib
mkdir build; cd build; cmake ..; cmake --build .
cd ..
source venv/bin/activate
python setup.py install
cd $DIR
source venv/bin/activate
cd $DIR/GFPGAN/
echo "Installing python dependencies"
pip install basicsr
pip install facexlib
pip install -r requirements.txt
python setup.py develop
pip install realesrgan
cd $DIR
sudo chown -R team:users gfpgan
echo "Installing ta-lib"
wget https://prdownloads.sourceforge.net/ta-lib/ta-lib-0.4.0-src.tar.gz
tar xvzf ta-lib-0.4.0-src.tar.gz
sudo rm ta-lib-*
cd ta-lib
sudo ./configure
sudo make
sudo make install
# קבעו כללי חומת אש
cd $DIR
# התקן תלות PYPI
echo "Installing remaining python dependencies (this may take a while)"
sudo systemctl mask tmp.mount
cd $DIR
source venv/bin/activate
pip3 install -U "celery[redis]"
pip3 install -r requirements.txt --use-deprecated=legacy-resolver --use-pep517
pip3 install --upgrade opencv-python # == 4.5.4.60
pip3 install --upgrade opencv-contrib-python # == 4.5.4.60
# PIP התקנה OpenCV-python == 4.5.5.64
# PIP התקן OpenCV-Contrib-python == 4.5.5.64
pip3 install --upgrade opencv-python-headless
pip3 uninstall channels
pip3 uninstall daphne
pip3 install channels["daphne"]
pip3 install Pillow==9.5.0
pip3 install librosa
pip3 install -U 'Twisted[tls,http2]'
pip3 install --upgrade certifi requests urllib3 numpy oauthlib twisted pyjwt sqlparse cryptography astral webauthn docbarcodes pdf417 deepface --no-cache-dir
pip3 install tensorflow==2.15.1
# התקן את CertBot
echo "Installing certificates"
sudo snap install core; sudo snap refresh core
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot
sudo snap install redis
sudo systemctl enable apache2
sudo systemctl start apache2
# הפעל את CertBot
sudo certbot --apache --non-interactive --agree-tos --domains femmebabe.com --email jasper.camber.holton@gmail.com
# טען מחדש שרת דואר
sudo systemctl restart opendkim postfix dovecot
# העתק אישורים
# sudo cp /etc/letsencrypt/live/femmebabe.com/privkey.pem privkey.pem
# sudo cp /etc/lettesencrypt/live/femmebabe.com/cert.pem cert.pem
# לְהַטלִיא
cp scripts/content.py $"/home/team/femmebabe/venv/lib/python${PYTHON_VERSION}/site-packages/pyxb/binding/content.py"
cp scripts/pwa_webpush_forms.py $"/home/team/femmebabe/venv/lib/python${PYTHON_VERSION}/site-packages/pwa_webpush/forms.py"
cp scripts/webauth_views.py $"/home/team/femmebabe/venv/lib/python${PYTHON_VERSION}/site-packages/webauth/views.py"
cp scripts/json.py $"venv/lib/python${PYTHON_VERSION}/site-packages/django/core/serializers/json.py"
# הגדר הגדרות משתמש
sudo gpasswd -a www-data users
# הגדר הרשאות
echo "Setting permissions"
sudo chown -R team:users cache/
sudo chmod a+rwx -R cache/
# SUDO CHOWN -R TEAM: משתמשים/var/run/
# שורש Sudo Chown: שורש/ריצה/sudo/ts -r
sudo chown -R redis:redis /var/lib/redis
sudo chown -R redis:redis /var/log/redis
sudo chmod -R u+rwX,g+rwX,u+rx /var/log/redis
sudo chmod +r /etc/redis/redis.conf
sudo chown -R team:users /var/log/
sudo chown -R :users .././
sudo chmod -R g+rwX ./
sudo chmod -R g+rX .././
sudo chmod -R g-rwX ../.ssh
sudo chmod 774 ./
# sudo chmod 664 db.sqlite3
# sudo chown www-data: משתמשים db.sqlite3
sudo chown -R www-data:www-data media/
sudo chown www-data:users ./
sudo chown -R team:users media/
sudo chown -R team:users ./
sudo chown -R team:users ./gfpgan/
sudo chown -R team:users ./temp/
sudo chmod a+r team /var/mail/$USER
# העתק תצורה והגדר הרשאות
echo "Configuring remaining services"
sudo cp config/apis.json /etc/apis.json
sudo cp config/config.json /etc/config.json
sudo cp config/femmebabe-le-ssl.conf /etc/apache2/sites-available/femmebabe-le-ssl.conf
sudo cp config/etc_dovecot_passwd /etc/dovecot/passwd
sudo cp config/etc_init.d_celery /etc/init.d/celery
sudo cp config/etc_init.d_celerybeat /etc/init.d/celerybeat
sudo cp config/etc_default_celerybeat /etc/default/celerybeat
sudo cp config/etc_default_celery /etc/default/celery
sudo cp config/etc_systemd_system_daphne.service /etc/systemd/system/daphne.service
sudo cp config/etc_systemd_system_celery.service /etc/systemd/system/celery.service
sudo cp config/etc_systemd_system_celerybeat.service /etc/systemd/system/celerybeat.service
sudo chmod a+x /etc/init.d/celery
sudo chmod a+x /etc/init.d/celerybeat
# הגדרת מסד נתונים
echo "Running migrations, this should be quick"
python manage.py makemigrations
python manage.py migrate --run-syncdb
echo "Loading data, this may take a while"
python manage.py loaddata db.json
echo "Setup crontab/sudoers configuration"
sudo crontab -l -u root | cat - config/crontab | sudo crontab -u root -
sudo sh -c "cat config/sudoers >> /etc/sudoers"
# הזרקו את תצורת PAM והסירו את תצורת SSH פגומה
# sudo sed -i '' -e '$ d' /tc/pam.d/sshd
# Sudo sed -i '' -ו- $ d ' /etc /פרופיל
echo "session required pam_exec.so seteuid /home/team/femmebabe/pam.sh" | sudo tee -a /etc/pam.d/sshd
echo "session required pam_exec.so seteuid /home/team/femmebabe/logout.sh" | sudo tee -a /etc/pam.d/sshd
sudo chmod a+x pam.sh
sudo rm /etc/ssh/sshd_config.d/50-cloud-init.conf
# העתק סקריפטים של סל והגדר הרשאות
echo "Copying scripts"
sudo cp scripts/reload /usr/bin/
sudo cp scripts/check /usr/bin/
sudo cp scripts/enagpu /usr/bin/
sudo cp scripts/disgpu /usr/bin/
sudo cp scripts/activate /usr/bin/
sudo cp scripts/backup /usr/bin/
sudo cp scripts/ascript /usr/bin/
sudo cp scripts/setup /usr/bin/
sudo cp scripts/addsetup /usr/bin/
sudo cp scripts/watchlogs /usr/bin/
sudo cp scripts/logs /usr/bin/
sudo cp scripts/cmds /usr/bin/
sudo cp scripts/setup /usr/bin/
sudo cp scripts/pushweb /usr/bin/
sudo cp scripts/purgecache /usr/bin/
sudo cp config/banner /etc/banner
cd /usr/bin/
sudo chmod a+x activate
sudo chmod a+x backup
sudo chmod a+x ascript
# טען מחדש ומאפשר שירותים
echo "Enabling services"
sudo systemctl daemon-reload
sudo systemctl enable daphne.service
sudo systemctl enable celery.service
sudo systemctl enable celerybeat.service
sudo systemctl enable clamav-daemon
sudo systemctl start daphne.service
sudo systemctl start celery.service
sudo systemctl start celerybeat.service
sudo systemctl start clamav-daemon
# אפשר מודולי אפאצ'י
echo "Enabling apache2"
sudo a2enmod rewrite
sudo a2enmod wsgi
sudo a2enmod headers
sudo a2enmod ssl
sudo a2enmod proxy
sudo a2enmod proxy_balancer
sudo a2enmod proxy_http
sudo a2enmod proxy_wstunnel
# sudo a2dismod mpm_event
# sudo a2dismod mpm_worker
# sudo a2enmod mpm_prefork
# השבת אתר ברירת מחדל
sudo a2dissite 000-default
sudo a2dissite 000-default-le-ssl
# אפשר לאתר
sudo a2ensite femmebabe-le-ssl
# טען מחדש את הדמון והפעיל מחדש את אפאצ'י, Postfix ו- Opendkim
sudo systemctl daemon-reload
sudo systemctl restart apache2
sudo systemctl restart opendkim postfix
sudo systemctl start daphne
# הגדר הרשאות
sudo chown -R :www-data /var/www/
sudo chown -R :www-data /var/www/.deepface
# החלפת תצורה
echo "Allocating swap, this may take a while"
sudo swapoff /swapfile
sudo rm /swapfile
sudo fallocate -l 8G /swapfile
sudo dd if=/dev/zero of=/swapfile bs=1024 count=8388608
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo "/swapfile swap swap defaults 0 0" | sudo tee -a /etc/fstab
sudo swapon --show
# מנוע הכיתוב של init
echo "Initializing routine caption"
/home/team/femmebabe/venv/bin/python /home/team/femmebabe/routine_caption.py
/home/team/femmebabe/venv/bin/python /home/team/femmebabe/setup_mail.py
# הגדרת git
echo "Setting up git"
cd $DIR
sudo rm -r .git
git init --initial-branch=main
echo "Setting user password"
sudo usermod --password $(echo team | openssl passwd -1 -stdin) team
# הצג את IPv6 ו- OpenDkim לתצורת דומיין
echo "COPY the below information to domain configuration."
hostname -I
ip a | grep inet
ip -6 addr | grep "scope link"
sudo cat /etc/opendkim/keys/femmebabe.com/sendonly.txt | tr -d '\n' | sed 's/\s//g' | sed 's/""//g' | awk -F'[)(]' '{print $2}'
# ההתקנה הושלמה
echo "Setup completed in"
wc -l scripts/setup
echo "lines of code."
echo "Total time:"
duration=$SECONDS
echo "$((duration / 60)) minutes and $((duration % 60)) seconds elapsed."
echo "TODO:"
echo "- COPY above IPv6 address to domain DNS configuration"
echo "- COPY domain key to domain DNS configuration"
echo "- ADD new git repository with git remote add originlab <repo>."
echo "- OPEN port 25"
echo "- INSTALL antivirus as per reccomendations"
echo "- TEST"
echo "If neccesary,"
echo "- DEBUG"
echo "- FIX setup and backup scripts"
echo "- Fix server"
echo ""
echo "Thank you for using the femmebabe installer. Have a great day!"
echo "Goodbye."
זה הרבה התקנה! בקיצור, קוד זה רושם פקודות, מגדיר את Nano ו- Git, מעתיק מעל קבצים, מוריד ומתקין חבילות Apt של Ubuntu, תלות פייתון, מגדיר את התצורה של Postfix, מגדיר את התצורה של PostgreSQL (שרת מסד הנתונים) ומטען את מסד הנתונים, מגדיר את UFW (חובה לא מסובך), מבטל את ה- iPtables, מוריד אנטי -וירוס, מייצר ספריות, תלות בשיבוטים, מתקין תעודות ומגדיר את השרת, מתקין תצורה, מתחיל ומאפשר את Sect, הקצה החלפת, מגדיר הרשאות ומדפיס את ה- IP, כתובת ה- IPv6 ומפתח OpenDkim. די פשוט, אבל זה נראה כמו הרבה קוד. לא נצטרך הרבה מזה מכיוון שאין לנו את התלות, אנחנו לא משתמשים בסלרי, סלריביט או דפנה, אבל אנו מתקינים חלק מהם בכל מקרה כדי להתחיל. שימו לב שלקוד זה יש תחום המוכרז מספר פעמים. נצטרך גם לרכוש שם דומיין (שהוא תשלום שנתי קטן). אני ממליץ על Squarespace לרכישת דומיין, הפריסה שלהם היאאינטואיטיבי וקל לשימוש. אתה יכול לקנות כל תחום שתבחר, אבל אני משתמש ב- Domain FemMebabe.com בדוגמה זו. לאחר שקנית דומיין, פנה ללוח התצורה של Squarespace DNS והוסף רשומה מכוונת את הדומיין שלך לשרת לפי כתובת IP. זה אמור להיראות כך: @ A xx.xx.xx.xx עם מפעיל @ כמארח, כלומר כל תת -הדומיינים תחת תחום זה ותחום השורש יפנה מחדש לשרת. יש עוד רשומות להצהיר, אך אנו יכולים לעבור לאלה ברגע שאנחנו מוכנים לשלוח דואר. זכור, ייתכן שייקח מספר ימים עד שתוכל לשלוח בהצלחה דואר מהשרת. רשומות ה- DNS שאנו מציבים ייקחו זמן להפיץ. בכל אופן, התקליט היחיד שאנחנו צריכים להתחיל הוא תקליט. אז עכשיו נוכל למלא את התסריט שלהלן בהתאם לפרויקט שלנו ולהפעיל אותו. נתחיל עם סקריפט הגדרה קטן יותר כדי להתקין רק את מה שאנחנו צריכים להתקדמות בסיסית. עדיין לא נשתמש בכל כך הרבה תלות או PostgreSQLהעלה שרת HTTP בסיסי ודואג לאישורו בסיום זה. זכור, כדי לקבל תעודת HTTPS ולהפעיל את השרת בצורה מאובטחת, נצטרך לקנות דומיין יחד עם שרת שרת. לעת עתה, החלף את "צוות" בקובץ זה בשם המשתמש שלך, "DIR" עם ספריית הפרויקט שלך, וספק את הדוא"ל והתחום שלך בתגי <>. בנוסף, לפני שנפעל קוד זה, עלינו לשנות את ההגדרות לחומת האש שספק האירוח תומך בו, אם בכלל. בדרך כלל זה בכרטיסייה 'רשתות' של ספק האירוח שלך, או אם אתה מארח עצמי, זה בקטע 'העברת יציאה' בנתב שלך. תרצה גם להגדיר IP סטטי דרך הנתב שלך עם הכתובת של מכונת השרת שלך, אם אתה משתמש באירוח עצמי. יהיה עליכם לפתוח את היציאות הבאות לגישה לקריאה/כתיבה. 22 (SSH) 25 (דואר) 587 (דואר) 110 (לקוח דואר) 80 (http) 443

# !
SECONDS=0
PYTHON_VERSION=3.12
echo "femmebabe installer initialized."
DIR="/home/team/<yourproject>"
USER="team"
# פקודות יומן
echo "Logging commands"
sudo cp log/commands.log /var/log/commands.log
sudo chmod -R a+w /var/log
sudo chown -R :syslog /var/log
echo $'alias venv="source /home/team/femmebabe/venv/bin/activate"' | sudo tee -a /home/team/.profile
echo $'PROMPT_COMMAND=\'RETRN_VAL=$?;logger -p local6.debug "$(whoami) [$$]: $(history 1 | sed "s/^[ ]*[0-9]\+[ ]*//" )"\'' | sudo tee -a /etc/bashrc
echo $'PROMPT_COMMAND=\'RETRN_VAL=$?;logger -p local6.debug "$(whoami) [$$]: $(history 1 | sed "s/^[ ]*[0-9]\+[ ]*//" )"\'' | sudo tee -a "/home/team/.bashrc"
echo $'PROMPT_COMMAND=\'RETRN_VAL=$?;logger -p local6.debug "$(whoami) [$$]: $(history 1 | sed "s/^[ ]*[0-9]\+[ ]*//" )"\'' | sudo tee -a /root/.bashrc
echo "source /etc/bashrc" | sudo tee -a /home/team/.profile
echo "/var/log/commands.log" | sudo tee -a /etc/logrotate.d/syslog
echo "local6.*    /var/log/commands.log" | sudo tee -a "/etc/rsyslog.d/bash.conf"
sudo service rsyslog restart
# NANO CONFIG
echo "set tabsize 4" >> .nanorc
echo "set tabstospaces" >> .nanorc
# Git Config
echo "Git configuration"
sudo git config --global user.email "<youremail>@gmail.com" && sudo git config --global user.name "<yourname>"
git config --global --add safe.directory $"$DIR"
sudo ssh-keyscan -t rsa gitlab.com | sudo tee -a /root/.ssh/known_hosts
sudo ssh-keyscan -t rsa github.com | sudo tee -a /root/.ssh/known_hosts
# עדכן והתקן
echo "Update and install packages"
sudo apt update && sudo NEEDRESTART_MODE=a apt upgrade -y
sudo apt purge postgresql-client-14 postgresql-client-common postgresql-common postgresql-contrib postgresql -y
echo "postfix postfix/mailname string femmebabe.com" | sudo debconf-set-selections
echo "postfix postfix/main_mailer_type string 'Internet Site'" | sudo debconf-set-selections
sudo NEEDRESTART_MODE=a DEBIAN_FRONTEND=noninteractive apt install -y postfix
sudo NEEDRESTART_MODE=a apt install -y rkhunter clamav-daemon libx264-dev ffmpeg libapache2-mod-wsgi-py3 apache2 cmake python-is-python3 python3-venv python3-pip python3-django expect tesseract-ocr openjdk-8-jdk redis-server libopencv-dev python3-opencv python3-dev libsasl2-dev opendkim opendkim-tools dovecot-core dovecot-pop3d dovecot-imapd auditd procmail libpq-dev postgresql postgresql-contrib libheif-dev snapd git software-properties-common certbot python3-certbot-apache
# אפשר אנטי -וירוס Clamav
echo "Starting antivirus"
sudo systemctl enable clamav-daemon
sudo systemctl start clamav-daemon
# הגדר שם מארח
echo "127.0.0.1 femmebabe" | sudo tee -a /etc/hosts
sudo hostnamectl set-hostname femmebabe
# מסד נתונים של גיבוי הגדרה
echo "Building database from backup, this may take a while."
cat db.json.?? > db.json
echo "Configuring firewall"
sudo ufw default allow outgoing
sudo ufw default deny incoming
sudo ufw allow 22
sudo ufw allow http
sudo ufw allow https
sudo ufw allow 'Postfix'
sudo ufw allow 'Postfix SMTPS'
sudo ufw allow 'Postfix Submission'
sudo ufw allow 'Dovecot POP3'
sudo ufw allow 'Dovecot Secure POP3'
sudo ufw allow 110/tcp
sudo ufw allow 25/tcp
echo "y" | sudo ufw enable
# מושבתת איטוביות מושבתות
echo "Configuring firewall"
sudo iptables -P INPUT ACCEPT
sudo iptables -P OUTPUT ACCEPT
sudo iptables -P FORWARD ACCEPT
sudo iptables -F
sudo iptables-save
# הגדרת VirtueAlenv
cd $DIR
echo "Creating virtual environment"
python -m venv venv
source venv/bin/activate
pip3 install -r requirements.txt
# התקן את CertBot
echo "Installing certificates"
sudo snap install core; sudo snap refresh core
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot
sudo snap install redis
sudo systemctl enable apache2
sudo systemctl start apache2
# הפעל את CertBot
sudo certbot --apache --non-interactive --agree-tos --domains femmebabe.com --email <youremail>@gmail.com
# הגדר הגדרות משתמש
sudo gpasswd -a www-data users
# הגדר הרשאות
echo "Setting permissions"
sudo chown -R team:users cache/
sudo chmod a+rwx -R cache/
# SUDO CHOWN -R TEAM: משתמשים/var/run/
# שורש Sudo Chown: שורש/ריצה/sudo/ts -r
sudo chown -R redis:redis /var/lib/redis
sudo chown -R redis:redis /var/log/redis
sudo chmod -R u+rwX,g+rwX,u+rx /var/log/redis
sudo chmod +r /etc/redis/redis.conf
sudo chown -R team:users /var/log/
sudo chown -R :users .././
sudo chmod -R g+rwX ./
sudo chmod -R g+rX .././
sudo chmod -R g-rwX ../.ssh
sudo chmod 774 ./
sudo chown -R www-data:www-data media/
sudo chown www-data:users ./
sudo chown -R team:users media/
sudo chown -R team:users ./
# טען מחדש ומאפשר שירותים
echo "Enabling services"
sudo systemctl daemon-reload
sudo systemctl enable clamav-daemon
sudo systemctl start clamav-daemon
# אפשר מודולי אפאצ'י
echo "Enabling apache2"
sudo a2enmod rewrite
sudo a2enmod wsgi
sudo a2enmod headers
sudo a2enmod ssl
sudo a2enmod proxy
sudo a2enmod proxy_balancer
sudo a2enmod proxy_http
sudo a2enmod proxy_wstunnel
# טען מחדש את הדמון והפעיל מחדש את אפאצ'י, Postfix ו- Opendkim
sudo systemctl daemon-reload
sudo systemctl restart apache2
sudo systemctl restart opendkim postfix
# הצג את IPv6 ו- OpenDkim לתצורת דומיין
echo "COPY the below information to domain configuration."
hostname -I
ip a | grep inet
ip -6 addr | grep "scope link"
לפני הפעלת קוד זה, וודא שהתחום שרכשת מחובר לשרת. לשם כך, פתח מסוף במחשב המקומי שלך והפעל את הפקודה עם הדומיין שלך:

ping femmebabe.com # הכנס את הדומיין שלך כאן, אחרי פינג
אם הכל נראה טוב והשרת שולח תגובות, אנו מוכנים להפעיל את הסקריפט ולהתקין חבילות כמו גם להתחיל, לאפשר ולאשר את שרת Apache שלנו. זה לא כל ההתקנה הדרושה להגדרת התצורה של Postfix, אנו נסתכל על ההתקנה הזו בהמשך. לעת עתה, הפעל את קוד ההתקנה הזה ועליו לקחת מספר דקות להתקנה ולאשר את השרת שלך. שוב, הקפד להחליף שם, דוא"ל ושם דומיין בסקריפט לפי השם שרכשת. כעת, לאחר שהשרת מועבר, אתה יכול ללכת לכתובת האתר בכל דפדפן אינטרנט ולבדוק כדי לוודא שהשרת מפעיל HTTPS. אם זה לא, נסה לחכות קצת זמן לרשומות ה- DNS להתעדכן ואז הפעל את הפקודה הבאה כדי לנסות מחדש את אישור CertBot:

sudo certbot --apache --non-interactive --agree-tos --domains <domain>.com --email <youremail>@gmail.com
כל עוד הגדרת את הכל נכון, אתה אמור להיות מסוגל לגשת לדף ברירת המחדל של Apache רק כדי לדעת שהקוד שלך עובד ולהציג דף אינטרנט חי. בשלב הבא, בואו נערוך את ההגדרות. PY כדי לשנות את מצב ברירת המחדל של ברירת המחדל שלנו לייצור. נקבע גם את התצורה של הדומיין בהגדרות, כמו גם IPs פנימיים.

nano yourproject/settings.py
בהגדרות, שנה/הוסף שורות אלה.

DEBUG = False

# תצורת אתר
SITE_NAME = 'Femme Babe'
PROTOCOL = 'https'
DOMAIN = 'femmebabe.com'
SITE_ID = 1
BASE_URL = PROTOCOL + '://' + DOMAIN
ALLOWED_HOSTS = [DOMAIN]

INTERNAL_IPS = [
    'XX.XX.XX.XX',
]
כעת, נצטרך להגדיר את התצורה של Apache2. בואו נערוך את קובץ התצורה שנפרוס בשורה זו:

sudo nano /etc/apache2/sites-available/femmebabe-le-ssl.conf
קובץ config זה צריך להיות שם הדומיין שלנו, ושם המשתמש והפרויקט. אני משתמש בשם הדומיין FemMebabe.com, בצוות שם המשתמש ובשם הפרויקט FemMebabe.

ServerSignature Off
ServerTokens Prod
<IfModule mod_ssl.c>
<VirtualHost *:80> 
	Redirect permanent / https://femmebabe.com/
</VirtualHost>
<VirtualHost *:443>
	ServerName femmebabe.com
	ServerAdmin team@femmebabe.com
	DocumentRoot /var/www/html

	ErrorLog ${APACHE_LOG_DIR}/error.log
	CustomLog ${APACHE_LOG_DIR}/access.log combined
	
	Alias /static /home/team/femmebabe/static
	<Directory /home/team/femmebabe/static>
		Require all granted
	</Directory>

Alias /media/icons /home/team/femmebabe/media/
<Directory /home/team/femmebabe/media>
Require all granted
</Directory>

	<Directory /home/team/femmebabe/femmebabe>
		<Files wsgi.py>
			Require all granted
		</Files>
	</Directory>

	WSGIScriptAlias / /home/team/femmebabe/femmebabe/wsgi.py
	WSGIDaemonProcess femmebabe python-path=/home/team/femmebabe/ python-home=/home/team/femmebabe/venv header-buffer-size=100000000000 user=team
	WSGIProcessGroup femmebabe
	WSGIApplicationGroup %{GLOBAL}
	
	<Directory /home/team/femmebabe/static>
                Options Indexes FollowSymLinks
                AllowOverride All
	</Directory>

	<IfModule mod_rewrite.c>
		RewriteEngine on
		RewriteCond %{REQUEST_URI} \.(css|webp|webm|gif|png|mp3|wav|jpeg|jpg|svg|webp)$ [NC]
		RewriteCond %{HTTP_REFERER} !^https://femmebabe.com/media/.*$ [NC]
		RewriteRule ^(.+?)/$ /media/$1 [F,L]
	</IfModule>

	Include /etc/letsencrypt/options-ssl-apache.conf
	SSLCertificateFile /etc/letsencrypt/live/femmebabe.com/fullchain.pem
	SSLCertificateKeyFile /etc/letsencrypt/live/femmebabe.com/privkey.pem

	Header set X-Frame-Options: "SAMEORIGIN"
	Header set Access-Control-Allow-Origin "https://femmebabe.com"

	TimeOut 60000
	LimitRequestBody 0

	<FilesMatch ".(ico|pdf|flv|jpg|jpeg|png|gif|webp|JPG|JPEG|wav|mp3|mp4|public|js|css|swf|webp|svg)$">
		Header set Cache-Control "max-age=30, public"
	</FilesMatch>
</VirtualHost>
</IfModule>
<IfModule mod_ssl.c>
<VirtualHost *:80>
	ServerName femmebabe.com
	ServerAdmin team@femmebabe.com
	DocumentRoot /var/www/html

	ErrorLog ${APACHE_LOG_DIR}/error.log
	CustomLog ${APACHE_LOG_DIR}/access.log combined

	RewriteEngine on
	RewriteCond %{SERVER_NAME} =femmebabe.com
	RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]
</VirtualHost>
</IfModule>
הקפד להחליף את שם הפרויקט, הספריות והתחום בקוד דוגמה זה בעת קביעת התצורה של השרת שלך. כעת, עלינו להשבית את אתר ברירת המחדל. ניתן לעשות זאת באמצעות Bash.

sudo a2dissite 000-default-le-ssl
sudo a2dissite 000-default
sudo a2dissite default-ssl
בשלב הבא נוכל לאפשר את אתר ברירת המחדל ולטען מחדש את Apache2, גם באמצעות Bash. זכור להחליף את FemMebabe בשם הקובץ שהצהרת בעת עריכת/etc/apache2/אתרים- Anbail/.

sudo a2ensite femmebabe-le-ssl
sudo systemctl reload apache2
חזור לדומיין שלך ב- NAVBAR. אתה אמור לראות את האתר שהגדרת בדפדפן האינטרנט שלך. מזל טוב! אם אינך רואה את זה, ייתכן שתצטרך לבצע שינויים מסוימים. עיין בזהירות את ההגדרות בפרויקט שלך, תצורת Apache, וודא שאין לך שגיאות, והפעל את הפקודות הבאות כדי לבדוק שגיאות בפרויקט.

cd projectname
source venv/bin/activate
python manage.py check
אם יש לך שגיאות בפרויקט Python שלך, עקוב אחריהן למקום בו הם נמצאים ולתקן אותן. יתכן שלא תוכל לראות את כל השגיאות שלך, תלוי היכן הן, כך שאם יש לך שגיאה שפשוט אומרת "אוכלוסייה אינה נוכחת מחדש", ערוך את הקובץ הבא בסביבה הווירטואלית, Registing.Py, כדי לחשוף את שְׁגִיאָה.

nano venv/lib/python3.12/site-packages/django/apps/registry.py
גלול לשורה 83, שם מוגבה שגיאת זמן ריצה זו (העלה RuntimeError ("אוכלוסייה () אינה מרוכזת מחדש")), והוסיפו תגובה לפני שורה זו, ואז הוספה, עם אותה כניסה, self.app_configs = {}. זה נראה כך:

            if self.loading:
                # מנע שיחות מחדש כדי להימנע מהפעלת AppConfig.ready ()
                # שיטות פעמיים.
# העלאת RuntimeError ("אוכלוסייה () אינה נוכחת מחדש")
                self.app_configs = {}
            self.loading = True
לאחר מכן תוכל לבדוק שוב את הפרויקט ולחשוף את השגיאה.

python manage.py check
אז אתה יכול לראות את השגיאה ולתקן אותה. כאשר יש לך אותו קבוע והקוד אוסף ללא שגיאות, הקפד לשנות את הקובץ בחזרה כך שהוא ייראה כך:

            if self.loading:
                # מנע שיחות מחדש כדי להימנע מהפעלת AppConfig.ready ()
                # שיטות פעמיים.
                raise RuntimeError("populate() isn't reentrant")
# self.app_configs = {}
            self.loading = True
בתנאי שהשרת מקוון, כאשר אנו מבצעים בו שינויים נוספים, עלינו להשתמש בפקודה הבאה כדי לטעון מחדש את השרת:

sudo systemctl reload apache2
מדהים! אבל מה לגבי שליחת דואר? כדי להתחיל לשלוח דוא"ל, נצטרך תחילה לעדכן את תצורת הדומיין. זה אמור להיות בפאנל ה- DNS שלך ב- Squarespace, או בכל רשם שם הדומיין שבחרת. נצטרך להתקין ולהוסיף תצורה ולהפעל כמה פקודות. ראשית, בואו נקבל את כתובת ה- IPv6 של השרת. לאחר מכן נפתח את ה- DNS שלך ונוסיף את הרשומות. כדי לקבל את כתובת ה- IPv6 של השרת, השתמש בפקודה זו:

ip -6 addr
כעת, אנו יכולים להוסיף את הרשומות הבאות להגדרות ה- DNS. הרשומות שלי נראות כך. עם זאת, עבור הרשומות שלך, עליך להחליף את כתובת ה- IP ב- IP שלך (לא 75.147.182.214, זה שלי). הוסף גם את הדומיין שלך במקום FemMebabe.com, כמו גם את כתובת ה- IPv6 שלך שנמצאה עם הפקודה הקודמת (אינך יכול להשתמש בשלי, FE80 :: 725A: FFF: FE49: 3E02). אל תדאג לגבי DomainKey לעת עתה, זה נוצר כאשר אנו מגדירים את Postfix, שרת הדואר, עם OpenDkim, ומדפיס את המפתח. אנו נקבע את התצורה האחרונה. @ א N/a 75.147.182.214 @ Mx 10 femmebabe.com @ Ptr N/a femmebabe.com @ Txt N/a Txt @ v = spf1 mx ip75.147.182.214ip6: fe80 :: 725a: fff: fe49: 3e02 ~ הכל Default._bimi Txt N/a v = bimi1; l = https: //femmebabe.com/media/static/femmebabe.svg _dmarc Txt N/a v = dmarc1; p = אין sendonly._domainkey Txt N/aכעת, נצטרך להוסיף תצורה מתמשכת עבור Postfix. כל שעלינו לעשות הוא לוודא שאנו מחליפים את שם הדומיין, femmebabe.com, עם שם הדומיין בו אתה משתמש. בואו נסתכל על כל קבצי התצורה בזה אחר זה, ונתקין אותם בספריה שנקראת Config בפרויקט שלנו, להתקנה למערכת ההפעלה.

nano config/etc_postfix_main.cf
הוסף טקסט זה לקובץ

# ראה /usr/share/postfix/main.cf.dist להגיב, גרסה שלמה יותר


# Debian ספציפי: ציון שם קובץ יגרום לראשונה
# שורה של אותו קובץ שישמש כשם.  ברירת המחדל של דביאן
# IS /etc /שם דואר.
# Myorigine = /etc /mailname

smtpd_banner = $myhostname ESMTP $mail_name (Ubuntu)
biff = no

# הוספת. דומיין הוא תפקידו של ה- MUA.
append_dot_mydomain = no

# בטל את השורה הבאה כדי ליצור אזהרות "דואר מעוכב"
# DECUREN_WARNING_TIME = 4H

readme_directory = no

# ראו http://www.postfix.org/compatibility_readme.html - ברירת מחדל ל -3.6
# התקנות טריות.
compatibility_level = 3.6



# פרמטרים של TLS
smtpd_tls_cert_file=/etc/letsencrypt/live/femmebabe.com/fullchain.pem
smtpd_tls_key_file=/etc/letsencrypt/live/femmebabe.com/privkey.pem
smtpd_tls_security_level=may

smtp_tls_CApath=/etc/ssl/certs
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache

smtpd_relay_restrictions = permit_sasl_authenticated, defer_unauth_destination
myhostname = femmebabe.com
alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases
myorigin = /etc/mailname
mydestination = femmebabe.com, localhost, $myhostname
smtp_helo_name = femmebabe.com
mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128
mailbox_size_limit = 0
recipient_delimiter = +
inet_interfaces = all
inet_protocols = all

# תצורת מילטר
milter_default_action = accept
milter_protocol = 6
smtpd_milters = local:/opendkim/opendkim.sock
non_smtpd_milters = $smtpd_milters

smtp_tls_security_level = encrypt
smtp_tls_loglevel = 1

virtual_transport=lmtp:unix:private/dovecot-lmtp

smtpd_sasl_path = private/auth
התצורה הבאה!

nano config/etc_postfix_master.cf
הוסף את השורות האלה:

# 
# קובץ תצורת תהליך Master Postfix.  לפרטים על הפורמט
# של הקובץ, עיין בדף הידני של המאסטר (5) (פקודה: "Man 5 Master" או
# אונליין: http://www.postfix.org/master.5.html).
# 
# אל תשכח לבצע "טעינה מחדש של Postfix" לאחר עריכת קובץ זה.
# 
# ================================================= ========================
# סוג שירות פרטי unpriv chruot chroot Wakeup פקודה maxproc + args
# (כן) (כן) (לא) (לעולם) (100)
# ================================================= ========================
smtp      inet  n       -       y       -       -       smtpd
# smtp inet n - y - 1 מסך לאחר
# מעבר SMTPD - - Y - - SMTPD
# Dnsblog unix - - y - 0 dnsblog
# Tlsproxy unix - - y - 0 tlsproxy
# בחר אחת: הפעל הגשה ללקוחות Loopback בלבד, או עבור כל לקוח.
# 127.0.0.1:SUBSISSISSISSISS
submission inet n       -       y       -       -       smtpd
  -o smtpd_relay_restrictions=permit_sasl_authenticated,reject
  -o smtpd_recipient_restrictions=permit_sasl_authenticated,reject
  -o syslog_name=postfix/submission
  -o smtpd_tls_security_level=encrypt
  -o smtpd_tls_wrappermode=no
  -o smtpd_sasl_auth_enable=yes
  -o smtpd_sasl_type=dovecot
  -o smtpd_sasl_path=private/auth
# -o syslog_name = postfix/הגשה
# -o smtpd_tls_security_level = incrypt
# -o smtpd_sasl_auth_enable = כן
# -o smtpd_tls_auth_only = כן
# -o smtpd_reject_unlisted_recipient = לא
# -O smtpd_client_restrictions = $ mua_client_restrictions
# -o smtpd_helo_restrictions = $ mua_helo_restrictions
# -O SMTPD_SENDER_RESTRICTIONS = $ MUA_SENDER_RESTRICTIONS
# -O smtpd_recipient_trastrictions =
# -o smtpd_relay_restrictions = permit_sasl_authenticated, דחה
# -O milter_macro_daemon_name = מקור
# בחר אחד: אפשר SMTPs ללקוחות Loopback בלבד, או לכל לקוח.
# 127.0.0.1:smtps inet n - y - - smtpd
# Smtps inet n - y - - smtpd
# -O syslog_name = postfix/smtps
# -o smtpd_tls_wrappermode = כן
# -o smtpd_sasl_auth_enable = כן
# -o smtpd_reject_unlisted_recipient = לא
# -O smtpd_client_restrictions = $ mua_client_restrictions
# -o smtpd_helo_restrictions = $ mua_helo_restrictions
# -O SMTPD_SENDER_RESTRICTIONS = $ MUA_SENDER_RESTRICTIONS
# -O smtpd_recipient_trastrictions =
# -o smtpd_relay_restrictions = permit_sasl_authenticated, דחה
# -O milter_macro_daemon_name = מקור
# 628 INET N - Y - - QMQPD
pickup    unix  n       -       y       60      1       pickup
cleanup   unix  n       -       y       -       0       cleanup
qmgr      unix  n       -       n       300     1       qmgr
# QMGR UNIX N - N 300 1 OQMG
tlsmgr    unix  -       -       y       1000?   1       tlsmgr
rewrite   unix  -       -       y       -       -       trivial-rewrite
bounce    unix  -       -       y       -       0       bounce
defer     unix  -       -       y       -       0       bounce
trace     unix  -       -       y       -       0       bounce
verify    unix  -       -       y       -       1       verify
flush     unix  n       -       y       1000?   0       flush
proxymap  unix  -       -       n       -       -       proxymap
proxywrite unix -       -       n       -       1       proxymap
smtp      unix  -       -       y       -       -       smtp
relay     unix  -       -       y       -       -       smtp
        -o syslog_name=postfix/$service_name
# -o smtp_helo_timeout = 5 -o smtp_connect_timeout = 5
showq     unix  n       -       y       -       -       showq
error     unix  -       -       y       -       -       error
retry     unix  -       -       y       -       -       error
discard   unix  -       -       y       -       -       discard
local     unix  -       n       n       -       -       local
virtual   unix  -       n       n       -       -       virtual
lmtp      unix  -       -       y       -       -       lmtp
anvil     unix  -       -       y       -       1       anvil
scache    unix  -       -       y       -       1       scache
postlog   unix-dgram n  -       n       -       1       postlogd
# 
# ================================================= ===================
# ממשקים לתוכנה שאינה postfix. הקפד לבחון את המדריך
# דפים של תוכנת Non-Postfix כדי לגלות אילו אפשרויות היא רוצה.
# 
# רבים מהשירותים הבאים משתמשים במסירה של צינור Postfix (8)
# סוֹכֵן.  עיין בדף האישה של הצינור (8) למידע על $ {נמען}
# ואפשרויות מעטפת הודעות אחרות.
# ================================================= ===================
# 
# MailDrop. עיין בקובץ Postfix MailDrop_ReadMe לפרטים.
# ציין גם ב- main.cf: maildrop_destination_recipient_limit = 1
# 
maildrop  unix  -       n       n       -       -       pipe
  flags=DRXhu user=vmail argv=/usr/bin/maildrop -d ${recipient}
# 
# ================================================= ===================
# 
# גרסאות סיירוס אחרונות יכולות להשתמש בכניסה לקיימת "LMTP" הקיים.
# 
# ציין ב- cyrus.conf:
# lmtp cmd = "lmtpd -a" lixe = "localhost: lmtp" proto = tcp4
# 
# ציין ב- Main.cf אחד או יותר מהדברים הבאים:
# mailbox_transport = lmtp: inet: localhost
# Virtual_transport = lmtp: inet: localhost
# 
# ================================================= ===================
# 
# סיירוס 2.1.5 (עמוס גווו)
# ציין גם ב- main.cf: cyrus_destination_recipient_limit = 1
# 
# Cyrus Unix - n n - - צינור
# דגלים = drx user = cyrus arg =/cyrus/bin/ceaver -e -r $ {שולח} -m $ {הרחבה} $ {user}
# 
# ================================================= ===================
# דוגמה ישנה למסירה באמצעות סיירוס.
# 
# Old -Cyrus unix - n n - - צינור
# דגלים = r user = cyrus argv =/cyrus/bin/celipty -e -m $ {הרחבה} $ {user}
# 
# ================================================= ===================
# 
# עיין בקובץ PostFix UUCP_README לקבלת פרטי תצורה.
# 
uucp      unix  -       n       n       -       -       pipe
  flags=Fqhu user=uucp argv=uux -r -n -z -a$sender - $nexthop!rmail ($recipient)
# 
# שיטות משלוח חיצוניות אחרות.
# 
ifmail    unix  -       n       n       -       -       pipe
  flags=F user=ftn argv=/usr/lib/ifmail/ifmail -r $nexthop ($recipient)
bsmtp     unix  -       n       n       -       -       pipe
  flags=Fq. user=bsmtp argv=/usr/lib/bsmtp/bsmtp -t$nexthop -f$sender $recipient
scalemail-backend unix -       n       n       -       2       pipe
  flags=R user=scalemail argv=/usr/lib/scalemail/bin/scalemail-store ${nexthop} ${user} ${extension}
mailman   unix  -       n       n       -       -       pipe
  flags=FRX user=list argv=/usr/lib/mailman/bin/postfix-to-mailman.py ${nexthop} ${user}
ותצורת OpenDkim. OpenDkim מזהה שרתי דוא"ל עם מפתחות דומיין כדי להפוך אותם לבטוחים יותר. בלעדיו, הדואר אינו חתום ואולי לא יגיע לתיבת הדואר הנכנס.

nano config/etc_default_opendkim
הוסף את השורות האלה:

# הערה: זהו קובץ תצורה מדור קודם. זה לא משמש על ידי ה- Opendkim
# שירות Systemd. אנא השתמש בפרמטרי התצורה המתאימים ב
# /etc/opendkim.conf במקום.
# 
# בעבר, אפשר היה לערוך את הגדרות ברירת המחדל כאן ואז לבצע
# /lib/opendkim/opendkim.service.generate כדי ליצור קבצי עקיפה של Systemd ל
# /etc/systemd/system/opendkim.service.d/override.conf ו-
# /etc/tmpfiles.d/opendkim.conf. אמנם זה עדיין אפשרי, אבל עכשיו זה
# מומלץ להתאים את ההגדרות ישירות ב- /etc/opendkim.conf.
# 
# Daemon_opts = ""
# שנה ל-/var/spool/postfix/run/opendkim כדי להשתמש בשקע יוניקס עם
# Postfix ב Chroot:
# Rundir =/var/spool/postfix/run/opendkim
RUNDIR=/run/opendkim
# 
# ביטול סימון כדי לציין שקע חלופי
# שים לב שהגדרת זה יעקוף כל ערך שקע ב- OpenDkim.conf
# בְּרִירַת מֶחדָל:
SOCKET="local:/var/spool/postfix/opendkim/opendkim.sock"
# האזן בכל הממשקים ביציאה 54321:
# שקע = inet: 54321
# האזן ב- Loopback ביציאה 12345:
# שקע = אינט: 12345@localhost
# האזנה היא 192.0.2.1 היא יציאה 12345:
# שקע = inet: 12345@192.0.2.1
USER=opendkim
GROUP=opendkim
PIDFILE=$RUNDIR/$NAME.pid
EXTRAAFTER=

nano config/etc_dovecot_conf.d_10-master.conf
הוסף את השורות האלה:

0-master.conf 
# default_process_limit = 100
# default_client_limit = 1000

# מגבלת ברירת מחדל VSZ (גודל זיכרון וירטואלי) לתהליכי שירות. זה בעיקר
# נועד לתפוס ולהרוג תהליכים שמדליפים זיכרון לפני שהם אוכלים
# הַכֹּל.
# default_vsz_limit = 256m

# משתמש הכניסה משמש באופן פנימי על ידי תהליכי כניסה. זה הכי לא אמון
# משתמש במערכת Dovecot. זה לא אמור להיות גישה לשום דבר בכלל.
# default_login_user = dovenull

# המשתמש הפנימי משמש על ידי תהליכים ללא חיבור. זה צריך להיות נפרד מ
# משתמש כניסה, כך שתהליכי כניסה לא יכולים להפריע לתהליכים אחרים.
# default_internal_user = dovecot

service imap-login {
  inet_listener imap {
    # יציאה = 143
  }
  inet_listener imaps {
    # יציאה = 993
    # SSL = כן
  }

  # מספר החיבורים לטיפול לפני שמתחילים תהליך חדש. בדרך כלל
  # הערכים השימושיים היחידים הם 0 (ללא הגבלה) או 1. 1 הוא מאובטח יותר, אך 0
  # מהיר יותר. <doc/wiki/loginprocess.txt>
  # Service_Count = 1

  # מספר התהליכים שימשיכו תמיד לחכות לחיבורים נוספים.
  # Process_min_avail = 0

  # אם אתה מגדיר Service_Count = 0, סביר להניח שאתה צריך לגדל את זה.
  # vsz_limit = $ default_vsz_limit
}

service pop3-login {
  inet_listener pop3 {
    # יציאה = 110
  }
  inet_listener pop3s {
    # יציאה = 995
    # SSL = כן
  }
}

service submission-login {
  inet_listener submission {
    # יציאה = 587
  }
}

service lmtp {
  unix_listener /var/spool/postfix/private/dovecot-lmtp {
    group = postfix
    mode = 0666
    user = postfix
  }

  # צור מאזין INET רק אם אינך יכול להשתמש בשקע UNIX לעיל
  # inet_lister lmtp {
    # הימנע מהפכת LMTP לגלוי לכל האינטרנט
    # כתובת =
    # יציאה =
  # }
}

service imap {
  # רוב הזיכרון עובר לקבצי MMAP (). יתכן שתצטרך להגדיל את זה
  # הגבל אם יש לך תיבות דואר ענקיות.
  # vsz_limit = $ default_vsz_limit

  # מקס. מספר תהליכי IMAP (חיבורים)
  # Process_Limit = 1024
}

service pop3 {
  # מקס. מספר תהליכי POP3 (חיבורים)
  # Process_Limit = 1024
}

service submission {
  # מקס. מספר תהליכי הגשת SMTP (חיבורים)
  # Process_Limit = 1024
}

service auth {
  # Auth_socket_path מצביע על שקע userdb זה כברירת מחדל. זה בדרך כלל
  # משמש על ידי Dovecot-lda, Doveadm, אולי תהליך IMAP וכו '. משתמשים שיש
  # הרשאות מלאות לשקע זה מסוגלות להשיג רשימה של כל שמות המשתמש ו
  # קבל את התוצאות של בדיקות המשתמש של כולם.
  # 
  # מצב ברירת המחדל 0666 מאפשר לכל אחד להתחבר לשקע, אך
  # בדיקות userdb יצליחו רק אם userdb יחזיר שדה "UID"
  # תואם את ה- UID של תהליך המתקשר. גם אם ה- UID או ה- GID של המתקשר תואם את
  # ה- UID של Socket או Gid The Lookup מצליח. כל דבר אחר גורם לכישלון.
  # 
  # כדי לתת למתקשר הרשאות מלאות לחפש את כל המשתמשים, הגדר את המצב ל
  # משהו אחר מ- 0666 ודובקוט מאפשר לגרעין לאכוף את
  # הרשאות (למשל 0777 מאפשרות לכולם הרשאות מלאות).
  unix_listener /var/spool/postfix/private/auth {
    mode = 0660
    user = postfix
    group = postfix
  }
}

service auth-worker {
  # תהליך העובד של Auth מופעל כשורש כברירת מחדל, כך שהוא יכול לגשת אליו
  # /וכו '/צל. אם זה לא הכרחי, יש לשנות את המשתמש ל
  # $ default_interal_user.
  # משתמש = שורש
}

service dict {
  # אם משתמשים ב- DICT Proxy, לתהליכי הדואר צריכים להיות גישה לשקע שלו.
  # לדוגמה: מצב = 0660, קבוצה = vmail ו- Global mail_access_groups = vmail
  unix_listener dict {
    # מצב = 0600
    # משתמש =
    # קבוצה =
  }
}
שוב, הקפד להחליף את הדומיין בכל הקבצים הללו, FemMebabe.com, עם התחום שבחרת. ערוך את הקובץ הבא, config של dovecot,

nano config/etc_dovecot_dovecot
ולהוסיף את השורות האלה

## קובץ תצורת OVECOT

# אם אתה ממהר, ראה http://wiki2.dovecot.org/quickconfiguration

# הפקודה "doveconf -n" נותנת פלט נקי של ההגדרות שהשתנו. השתמש בזה
# במקום להעתיק ולהדביק קבצים בעת פרסום לרשימת התפוצה של Dovecot.

# '# 'דמות והכל אחרי זה מתייחסים אליו כאל הערות. חללים נוספים
# ומתעלמים משרביטים. אם אתה רוצה להשתמש באחד מאותם מפורשות, שים את
# value inside quotes, eg.: key = "# Char and Lawshing Bhartespace "

# ניתן לעקוף את רוב הגדרות (אך לא את כל) על ידי פרוטוקולים שונים ו/או
# IPs מקור/יעד על ידי הצבת ההגדרות בתוך קטעים, למשל:
# פרוטוקול IMAP {}, מקומי 127.0.0.1 {}, שלט 10.0.0.0/8 {}

# ערכי ברירת מחדל מוצגים עבור כל הגדרה, זה לא נדרש לביטול
# הָהֵן. אלה הם יוצאים מן הכלל לכך: אין קטעים (למשל מרחב שמות {})
# או הגדרות התוסף מתווספות כברירת מחדל, הן רשומות רק כדוגמאות.
# נתיבים הם גם דוגמאות רק עם ברירות המחדל האמיתיות מבוססות על תצורה
# אפשרויות. הנתיבים המופיעים כאן מיועדים לתצורה -prefix =/usr
# -sysconfdir =/etc-localstatedir =/var

# הפעל פרוטוקולים מותקנים
!include_try /usr/share/dovecot/protocols.d/*.protocol

# רשימה מופרדת בפסיקים של IP או מארחים היכן להאזין לחיבורים.
# "*" מקשיב בכל ממשקי ה- IPv4, "::" מאזין בכל ממשקי ה- IPv6.
# אם ברצונך לציין יציאות שאינן מפתקות או משהו מורכב יותר,
# ערוך conf.d/master.conf.
# האזנה = *, ::

# ספריית בסיס היכן לאחסן נתוני זמן ריצה.
# base_dir =/var/run/dovecot/

# שם מופע זה. בהגדרה רב-מפגשים DoVeadm ופקודות אחרות
# יכול להשתמש ב- -i <semance_name> כדי לבחור באיזה מופע משתמשים (אלטרנטיבה
# ל- -c <config_path>). שם המופע מתווסף גם לתהליכי dovecot
# בפלט PS.
# despant_name = dovecot

# הודעת ברכה ללקוחות.
# login_greeting = dovecot מוכן.

# רשימה מופרדת בשטח של טווחי רשת מהימנים. חיבורים מאלו
# IPs רשאים לעקוף את כתובות ה- IP והיציאות שלהם (לרישום ו
# לבדיקות אימות). מתעלם גם הוא מתעלם
# רשתות אלה. בדרך כלל היית מציין את שרתי ה- Proxy של ה- IMAP שלך כאן.
# login_trusted_networks =

# רשימה מופרדת בשטח של שטח בדיקת גישה כניסה (למשל TCPWRAP)
# login_access_sockets =

# עם proxy_maybe = כן אם יעד ה- proxy תואם לאף אחד מה- IPs האלה, אל תעשה
# פרוקסיזציה. זה לא הכרחי כרגיל, אך עשוי להיות שימושי אם היעד
# ה- IP הוא למשל IP של איזון עומס.
# Auth_proxy_self =

# הראה יותר כותרות תהליכים מילוליות (ב- PS). כרגע מציג שם משתמש ו
# כתובת IP. שימושי לראות מי משתמש בפועל בתהליכי IMAP
# (למשל תיבות דואר משותפות או אם UID משמשת לחשבונות מרובים).
# Verbose_proctitle = לא

# אם כל התהליכים ייהרגו כאשר תהליך האב של Dovecot נכבה.
# קביעת זה ל- "לא" פירושו שניתן לשדרג את Dovecot בלי
# אילוץ חיבורי לקוחות קיימים לסגירה (אם כי זה יכול להיות גם
# בעיה אם השדרוג הוא למשל בגלל תיקון אבטחה).
# STOWNOWN_CLIENTS = כן

# אם לא אפס, הפעל פקודות דואר באמצעות שרת חיבורים רבים אלה ל- DoVeadm,
# במקום להריץ אותם ישירות באותו תהליך.
# doveadm_worker_count = 0
# שקע או מארח יוניקס: יציאה המשמשת לחיבור לשרת DoVeadm
# doveadm_socket_path = doveadm-server

# רשימה מופרדת בחלל של משתני סביבה שנשמרים ב- Dovecot
# הפעלה ועברה לכל תהליכי הילד שלה. אתה יכול גם לתת
# KEY = זוגות ערך כדי להגדיר תמיד הגדרות ספציפיות.
# Import_environment = tz

## 
## הגדרות שרת מילון
## 

# ניתן להשתמש במילון לאחסון רשימות מפתח = ערך. זה משמש כמה
# תוספים. ניתן לגשת למילון באופן ישיר או אם
# שרת מילון. ה- DICT הבא בלוק את שמות מילון
# כאשר משתמשים בשרת. לאחר מכן ניתן להפנות אליהם באמצעות URIS בפורמט
# "פרוקסי :: <שם>".

dict {
  # מכסה = mysql: /andc/dovecot/dovecot-dic-sql.conf.ext
}

# מרבית התצורה בפועל נכללת להלן. שמות הקובץ הם
# לראשונה ממוין לפי ערך ה- ASCII שלהם ומנותח בסדר הזה. ה- 00-PREFIXES
# בשמות קבצים נועדו להקל על הבנת ההזמנה.
!include conf.d/*.conf

# קובץ config יכול גם לנסות להיכלל מבלי לתת שגיאה אם
# זה לא נמצא:
!include_try local.conf

passdb {
  driver = passwd-file
  args = /etc/dovecot/passwd
}
userdb {
  driver = passwd
}

protocols = imap pop3

# מאפשר ל- Dovecot להאזין לכל חיבורי הקלט (IPv4 / IPv6)

listen = *, ::
הוסף סיסמה למשתמש Dovecot:

nano config/etc_dovecot_passwd
החלק הראשון של הקובץ, לפני המעי הגס, הוא שם המשתמש. החלק האחרון, "ה- Passionsword שלך", מציין את הסיסמה שתרצה לתת לשרת הדואר שלך.

team:{plain}yourpassword
בשלב הבא, תצורת OpenDkim

nano config/etc_opendkim.conf
ולהוסיף את השורות האלה:

# זוהי תצורה בסיסית לחתימה ואימות. זה יכול להיות בקלות
# מותאם להתאים להתקנה בסיסית. ראה OpenDkim.conf (5) ו-
# /usr/share/doc/opendkim/examples/opendkim.conf.sample להשלמה
# תיעוד של פרמטרי תצורה זמינים.

Syslog			yes
SyslogSuccess		yes
# Logwhy לא

# פרמטרי חתימה ואימות נפוצים. ב- Debian, הכותרת "מ"ה" היא
# הוגן יתר על המידה, מכיוון שלעתים קרובות הוא מפתח הזהות המשמש את מערכות המוניטין
# ובכך מעט רגיש לאבטחה.
Canonicalization	relaxed/simple
Mode			s
SubDomains		no
OversignHeaders		From

# חתימה על תחום, בורר ומפתח (חובה). לדוגמה, בצע חתימה
# לדומיין "דוגמה.com" עם בורר "2020" (2020._domainkey.example.com),
# באמצעות המפתח הפרטי המאוחסן ב /etc/dkimkeys/example.private. גרגירי יותר
# אפשרויות הגדרה ניתן למצוא ב/usr/share/doc/opendkim/readme.opendkim.
# DOMAIN דוגמה.com
# בורר 2020
# Keyfile /etc/dkimkeys/example.private

# ב- Debian, Opendkim פועל כמשתמש "Opendkim". נדרש umask של 007 כאשר
# באמצעות שקע מקומי עם MTAs ניגשים לשקע כל
# משתמש (לדוגמא, Postfix). יתכן שתצטרך להוסיף "PostFix" של המשתמש לקבוצה
# "אופנדקים" במקרה זה.
UserID			opendkim
UMask			007

# שקע לחיבור MTA (חובה). אם ה- MTA נמצא בתוך כלא Chroot,
# יש להבטיח כי השקע נגיש. בביאן, Postfix פועל פנימה
# chroot in/var/spool/postfix, ולכן שקע יוניקס יצטרך להיות
# מוגדר כמוצג בשורה האחרונה למטה.
# שקע מקומי: /run/opendkim/opendkim.sock
# Socket Inet: 8891@localhost
# Socket Inet: 8891
Socket			local:/var/spool/postfix/opendkim/opendkim.sock

PidFile			/run/opendkim/opendkim.pid

# מארחים שעבורם לחתום ולא לאמת, ברירת המחדל היא 127.0.0.1. ראה את
# פרק פעולה של OpenDkim (8) למידע נוסף.
# Internalhosts 192.168.0.0/16, 10.0.0.0/8, 172.16.0.0/12

# עוגן האמון מאפשר DNSSEC. ב- Debian, קובץ עוגן הנאמנות מסופק
# לפי החבילה DNS-ROOT-DATA.
TrustAnchorFile		/usr/share/dns/root.key
# שרתים שמות 127.0.0.1

# מפה דומיינים מכתובות למפתחות המשמשים לחתימת הודעות
KeyTable           refile:/etc/opendkim/key.table
SigningTable       refile:/etc/opendkim/signing.table

# קבוצה של מארחים פנימיים שצריך לחתום על הדואר שלהם
InternalHosts       /etc/opendkim/trusted.hosts

nano config/etc_default_opendkim
ולהוסיף את השורות האלה

# הערה: זהו קובץ תצורה מדור קודם. זה לא משמש על ידי ה- Opendkim
# שירות Systemd. אנא השתמש בפרמטרי התצורה המתאימים ב
# /etc/opendkim.conf במקום.
# 
# בעבר, אפשר היה לערוך את הגדרות ברירת המחדל כאן ואז לבצע
# /lib/opendkim/opendkim.service.generate כדי ליצור קבצי עקיפה של Systemd ל
# /etc/systemd/system/opendkim.service.d/override.conf ו-
# /etc/tmpfiles.d/opendkim.conf. אמנם זה עדיין אפשרי, אבל עכשיו זה
# מומלץ להתאים את ההגדרות ישירות ב- /etc/opendkim.conf.
# 
# Daemon_opts = ""
# שנה ל-/var/spool/postfix/run/opendkim כדי להשתמש בשקע יוניקס עם
# Postfix ב Chroot:
# Rundir =/היה/Spool/Postfix/Run/OpenDkim
RUNDIR=/run/opendkim
# 
# ביטול סימון כדי לציין שקע חלופי
# שים לב שהגדרת זה יעקוף כל ערך שקע ב- OpenDkim.conf
# בְּרִירַת מֶחדָל:
SOCKET="local:/var/spool/postfix/opendkim/opendkim.sock"
# האזן בכל הממשקים ביציאה 54321:
# שקע = inet: 54321
# האזן ב- Loopback ביציאה 12345:
# שקע = אינט: 12345@localhost
# האזנה היא 192.0.2.1 היא יציאה 12345:
# שקע = inet: 12345@192.0.2.1
USER=opendkim
GROUP=opendkim
PIDFILE=$RUNDIR/$NAME.pid
EXTRAAFTER=
כאשר אנו מוכנים להגדיר את שרת ה- Postfix שלנו, אנו נפעיל את הקוד שלהלן, כאשר שם הדומיין המתאים משובץ. התחל ביצירת סקריפט

touch scripts/postfixsetup
sudo chmod a+x scripts/postfixsetup
nano scripts/postfixsetup
כעת, ב- Nano, עורך הטקסטים, ערוך את הקובץ הזה כך שהוא כולל את שם הדומיין שלך במקום femmebabe.com.

# !
# הגדרת Postfix
cd $DIR
echo "Mail services configuration"
sudo cp /etc/postfix/main.cf /etc/postfix/main.cf.backup
sudo cp config/etc_postfix_main.cf /etc/postfix/main.cf
sudo cp config/etc_postfix_master.cf /etc/postfix/master.cf
sudo cp config/etc_default_opendkim /etc/default/opendkim
sudo cp config/etc_dovecot_conf.d_10-auth.conf /etc/dovecot/conf.d/10-auth.conf
sudo cp config/etc_dovecot_conf.d_10-master.conf /etc/dovecot/conf.d/10-master.conf
sudo cp config/etc_dovecot_dovecot.conf /etc/dovecot/dovecot.conf
sudo cp config/etc_dovecot_passwd /etc/dovecot/passwd
sudo cp config/etc_opendkim.conf /etc/opendkim.conf
sudo cp config/etc_default_opendkim /etc/default/opendkim
sudo adduser postfix opendkim
sudo mkdir /etc/opendkim
sudo mkdir /etc/opendkim/keys
sudo mkdir /etc/opendkim/keys/femmebabe.com
sudo mkdir /var/spool/postfix/opendkim
sudo echo "*@femmebabe.com     sendonly._domainkey.femmebabe.com" | sudo tee -a /etc/opendkim/signing.table
sudo echo "sendonly._domainkey.femmebabe.com    femmebabe.com:sendonly:/etc/opendkim/keys/femmebabe.com/sendonly.private" | sudo tee -a /etc/opendkim/key.table
sudo echo "127.0.0.1" | sudo tee -a /etc/opendkim/trusted.hosts
sudo echo "localhost" | sudo tee -a /etc/opendkim/trusted.hosts
sudo echo "" | sudo tee -a /etc/opendkim/trusted.hosts
sudo echo "*.femmebabe.com" | sudo tee -a /etc/opendkim/trusted.hosts
sudo chown -R opendkim:opendkim /etc/opendkim
sudo opendkim-genkey -b 2048 -d femmebabe.com -D /etc/opendkim/keys/femmebabe.com -s sendonly -v
sudo chmod go-rw /etc/opendkim/keys
sudo chown opendkim:opendkim /etc/opendkim/keys/femmebabe.com/sendonly.private
sudo chown opendkim:postfix /var/spool/postfix/opendkim
cd $DIR
sudo cp mailbox/* /var/mail/
sudo chown :users /var/mail/*
sudo chmod -R a+rwx /var/mail/*
sudo systemctl restart opendkim postfix dovecot
sudo cat /etc/opendkim/keys/femmebabe.com/sendonly.txt | tr -d '\n' | sed 's/\s//g' | sed 's/""//g' | awk -F'[)(]' '{print $2}'
כעת, הפעל את הסקריפט שהושלם כדי להגדיר את התצורה של Postfix, Opendkim ו- Dovecot.

./scripts/postfixsetup
לאחר הפעלת סקריפט זה, העתק את השורה האחרונה שהוא מדפיס והדבק אותו בתצורת ה- DNS שלך כערך עבור sendonly._domainkey. זהו מקש OpenDkim המשמש לזיהוי הדומיין שלך בעת שליחת דואר מאובטח. מדהים! תוך מספר ימים, אתה אמור להיות מסוגל לשלוח דואר מהשרת בתנאי שהכל מוגדר נכון. אם רק הגדרת את ה- DNS עבור שרת הדואר שלך, זה אמור לקחת פחות מ- 72 שעות עד שהרשומות יעודדו. זה בדרך כלל הרבה יותר מהיר. אתה יכול לבדוק אם השרת שלך עובד באמצעות פקודה זו, סיפק את הדוא"ל שלך:

echo “test” | mail -s “Test Email” youremail@gmail.com
אם נראה שהכל עובד נכון, אתה אמור להיות מסוגל לשלוח דוא"ל עם השרת שלך. אם זה לא עובד, נסה להסתכל על היומנים כדי לראות מה השגיאה.

tail –lines 150 /var/log/mail.log
זה יציע מידע מילולי על דואר שנשלח על ידי השרת והאם הוא פועל כראוי. אתה אמור להיות מסוגל לראות את הדוא"ל גם בתיבת הדואר הנכנס שלך, אם הוא לא שם, בדוק את תיקיית הספאם שלך. תצטרך גם להגדיר את ההגדרות שלך ב- Settings.py כדי ששרת הדוא"ל שלך יוכל לדבר עם אפליקציית Django שלך, הפרויקט. הוסף או החלף שורות אלה בהגדרות שלך

EMAIL_HOST = DOMAIN
EMAIL_PORT = 587
EMAIL_USE_TLS = True
EMAIL_ADDRESS = 'team@femmebabe.com'
EMAIL_HOST_USER = 'team' # 'Love@mamasheen.com'
EMAIL_HOST_PASSWORD = config['EMAIL_HOST_PASSWORD']
DEFAULT_FROM_EMAIL = '{} <{}>'.format(SITE_NAME, EMAIL_HOST_USER)
שימו לב שאנו משתמשים בקובץ config כדי לקבל את הסיסמה. בואו נטען את הקובץ הזה בהגדרות כמו כן, ממש בתחילת הקובץ:

import os
import json

# פתח וטוען את התצורה
with open('/etc/config.json') as config_file:
    config = json.load(config_file)
בואו ניצור קובץ זה ונוסיף לו מפתח סודי, כמו גם את סיסמת הדואר. כדי ליצור מפתח סודי, השתמש בפקודה זו, בכל אורך שתרצה בסוף:

openssl rand -base64 64
כעת, העתק את הטקסט שיצר OpenSSL וערוך /etc/config.json

sudo nano /etc/config.json
הוסף את השורות הבאות לקובץ שלך, עם המפתח שנוצר OpenSSL כמפתח הסודי.

{
	"SECRET_KEY": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX-generated-using-openssl)",
	"EMAIL_HOST_PASSWORD": "yourpassword"
}
פורמט JSON הוא פשוט וקל לשימוש, אנו יכולים להכריז על מפתחות אחרים בהם אנו רוצים להשתמש בפרויקט שלנו בדרך זו, ולשמור עליהם נפרדים מספריית הפרויקט שלנו כך שמשתמשים אחרים לא יוכלו לכתוב להם ולכן לא ניתן לקרוא אותם מספריית הפרויקטים שלנו בלבד. זה מומלץ לתרגול למפתחות API, שנשתמש בהם יותר מכמה מעטים כאן. תרצה גם לגבות את הפרויקט שלך כדי לוודא שהכל נשמר ותוכל לשחזר את העבודה שלך בהמשך גם אם אתה כבר לא רוצה לשכור שרת.

sudo backup
כעת, נסה לשלוח דוא"ל HTML משרת האינטרנט, בתנאי שליחת אחד משורת הפקודה עובדת. שאילתה את מופע המשתמש שלך במעטפת ושלח דוא"ל HTML למשתמש זה דרך Django. שנה את שמי בקוד, שרלוט, לשם המשתמש שלך.

python manage.py shell
from django.contrib.auth.models import User
u = User.objects.get(username='Charlotte')
from users.email import send_welcome_email
send_welcome_email(u)
exit()
אם הפקודה הראשונה לא עובדת, הקפד להשתמש

source venv/bin/activate
בתנאי שהכל מוגדר נכון, כעת תקבל דוא"ל מבורך בתיבת הדואר שלך שנשלחה על ידי אפליקציית האינטרנט שלך. עבודה טובה! עשית דרך ארוכה. רציתי להוסיף, אם אתה בכלל נאבק עם שגיאות בכלל בזמן שאתה עובד על פרויקט כזה, אל תהסס לחפש תשובות ולבקש עזרה. גוגל, בין מנועי חיפוש, הם משאבים מעולים לחיפוש אחר עזרה בתכנות. כל שעליך לעשות הוא לחפש את השגיאה שאתה מקבל, ותוכל לראות כיצד אנשים אחרים פותרים את הבעיה. כמו כן, אתם מוזמנים ליצור איתי קשר, המחנכים שלכם (מורים, פרופסורים, מורים), כל עמיתים באינטרנט הזמינים לתכנות עזרה, או להתייעץ שוב עם ספר זה או משאבים אחרים כדי למצוא פתרונות לנושאים שאתה חווה. אני מבין שזה לא קל, אבל גם אם קראת עד כה ואינך כותב שום קוד, אתה לומד הרבה על בניית אפליקציית אינטרנט מאפס. טפח על עצמך בגב, אתה עושה נהדרמִשׂרָה. תודה שקיבלת את הזמן לקרוא מדריך לפיתוח אתרים מהדורה שלישית. במהדורות עתידיות אכלול יותר מהדוגמאות החשובות שנדונו בתחילת המסמך ואנחנו נצלול הרבה יותר עמוק בעולם של פיתוח תוכנה ופיתוח חומרה. הישאר מעודכן לעתיד לבוא, ואני מצפה ללמד אותך כיצד לבנות תוכנה מדהימה. נתראה הבא






לִסְגוֹר
עַמוּד 1
קְפִיצָה
ראה מאמר מלא
המשך לקרוא

לִקְנוֹת | קנה עם קריפטו



https://glamgirlx.com/he/practical-web-based-deep -


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

השאירו לי טיפ בביטקוין באמצעות הכתובת הזו: 3KhDWoSve2N627RiW8grj6XrsoPT7d6qyE

© Glam Girl X 2025

תנאים והגבלות