Apprentissage approfondi et sécurité pratiques basés sur le Web par l'exemple

DaisyPhoto de profil

Par Daisy

Apprentissage et sécurité en profondeur basée sur le Web par exemple Troisième édition Charlotte Harper 3 juillet 2024 Avant-propos: Les considérations de sécurité dans la création de logiciels pour le Web sont une partie importante du plan et de l'exécution de tout développeur Web tout en gérant un prototype fiable, stable et utile à des fins pratiques. Le DOM (document d'objet de document), avec son implémentation de HTML, JavaScript et CSS ainsi que des logiciels backend implémentant Python, C / C ++, Java et Bash, donnent aux développeurs Web la liberté et le pouvoir de créer une grande variété de projets qui expriment Créativité, offrez une facilité d'utilisation et des fonctionnalités, représenter l'humilité et le caractère, et assurez-vous une facilité d'utilisation ainsi que la commodité et les services importants qui sont tous attrayants pour le Joe moyen, l'utilisateur final qui cherche à tuer Il est temps ou faire quelque chose sur Internet, généralement sur un périphérique pour smartphone à écran tactile. La plupart des gens ne savaient même pas par où commencer quand ils veulent construire un site Web à partir de zéro,Ils auraient tendance à commencer sur le site Web d'une autre personne et à construire quelque chose de limité dans les fonctionnalités, la fiabilité, la facilité d'utilisation et surtout la créativité alors qu'ils auraient pu avoir tous les derniers outils puissants à leur disposition afin de créer quelque chose d'utile sans perdre de temps à appuyer sur les boutons, et Surtout gaspiller de l'argent en payant pour des abonnements coûteux aux logiciels que peu de gens voulaient utiliser de toute façon étant donné ses limites de facilité d'utilisation et de flexibilité. Si vous avez quelques minutes pour lire ce livre et apprendre ce que je veux vous apprendre, ou même me parler personnellement de vos objectifs et obtenir des conseils dans la bonne direction, et que vous êtes motivé à apprendre à coder et à écrire votre propre logiciel , ramenez ce livre à la maison et réservez un peu de temps pour apprendre à créer l'application Web influente, puissante, rationalisée et importante, un site Web qui est sur vous et qui fait exactement ce que vous voulez et répond aux besoins de votre public. Sur moi: Je suis un développeur de logiciels avec un largeAnge d'expérience en C / C ++, Java, Python, HTML, CSS et JavaScript. Je construis des sites Web que les gens souhaitent utiliser, veulent visiter, et même devenir accro à utiliser juste pour apprendre, recréer et tuer du temps, et surtout, je vends un logiciel. Si vous aviez une idée exactement de la façon dont vous vouliez qu'un site Web ressemble et fonctionne, vous étiez prêt à me soutenir afin que je puisse répondre à mes propres besoins pendant que je rencontre le vôtre, et vous êtes prêt à couvrir les coûts de la gestion d'un site Web vous-même, Je vous créerais le prochain YouTube, Tiktok, Twitter, Google ou même une application de sécurité de haute technologie à laquelle vous pouvez accéder. Au lieu d'essayer de vous vendre mon temps, j'essaie d'acheter le vôtre: je veux vous demander de créer vous-même une application (site Web) avec les informations qui existent déjà et vous apprendre ce dont vous avez besoin pour être un développeur de logiciels indépendants, Entrepreneur, menant une carrière réussie dans le domaine que vous désirez. Et laissez-moi être clair, l'éducation que je vous donne sera informelle. Vous pourriez aller à l'école et apprendre tout cela avec unRMAL Education, ou même lire ce livre à l'école, terminer vos tâches et retirer beaucoup de votre éducation, mais je ne vous mettrai pas officiellement sur la sellette et vous demanderai de compléter les missions. Je ne suis pas votre professeur, vous pouvez penser à moi comme un ami qui veut vous guider vers une carrière axée sur votre propre succès personnel. Et je ne vends pas non plus de succès, vous devrez l'acheter avec votre temps. L'apprentissage du code a une courbe d'apprentissage abrupte et n'a jamais été facile, ni même censé l'être. Vous devez travailler aussi dur que possible et continuer à essayer d'échouer et de réessayer même lorsque vous êtes frustré pour apprendre et créer vous-même des applications. C'est dans la nature du code lui-même. Le code est exécuté par un compilateur conçu pour donner des messages d'erreur du programmeur, et ceux-ci vous apprendront comment coder, même si vous copiez simplement l'erreur dans votre moteur de recherche et lisez les exemples des autres. Et je dois dire que vous n'avez pas besoin d'être extrêmement riche, intelligent, réussi,en détail orienté ou organisé pour créer une application. L'ordinateur s'occupe de cette organisation pour vous. Il vous suffit de persévérer à travers les essais et les erreurs, de maintenir la concentration et de travailler dur dans ce que vous faites, et vous aurez une carrière très réussie dans l'intégralité de ce que vous faites. Qui suis-je: Je me rends compte que la dernière section concernait davantage l'apprentissage et que vous prenez un chemin de ce livre. Qui suis-je exactement? C'est une question compliquée. Je ne suis pas clair à ce sujet moi-même, car je souffre de conditions médicales qui peuvent me rendre difficile de coder ou d'écrire ce livre parfois, tout en présentant des défis avec des problèmes de socialisation et d'identité qui me rendent la vie plus difficile lorsqu'il s'agit de me présenter . En bref, si vous lisez ce livre, vous l'avez ramené à la maison parce que vous l'avez traversé et que vous pensiez que c'était utile, ou même si vous lisez aussi loin, à vous tout ce que vous faites. Je suis moi-même ingénieur, un logicieldéveloppeur et étudiant, et j'écris ce livre pour d'autres étudiants qui veulent leur faciliter la vie en ayant un manuel du logiciel dont ils ont besoin pour faciliter leur vie en donnant des exemples pour copier qui s'adapte comme un grand puzzle dans un travail , Utile, grand, fonctionnel, cohérent et engageant l'application qui peut stimuler le succès, peu importe le secteur des affaires. En grande partie, c'est ce que je fais: je crée des applications pour aider moi-même et les autres à réussir. Je suis aussi un auteur, bien que ce soit ma première publication que j'ai l'intention de terminer afin de réunir mon portefeuille dans un document utile, et je suis également un artiste. Je vais vous admettre, je suis en quelque sorte une personne étrange. Je ne suis pas parfait, j'avais dirigé des Ins avec la loi me conduisant même à quitter les collèges et les universités et quitter les États afin d'essayer de me faire un nom avec plus de succès. Je suis une femme de naissance, je porte du maquillage, je prends des photos de moi-même, je porte des robes et d'autres vêtements pour femmes, et je reste conscient de moi en tant quemasculin par nature. J'ai eu des problèmes avec d'autres personnes dans le passé qui ont conduit à des difficultés avec l'écriture et la création de Webapps, et je m'excuse de ne pas avoir pu obtenir ce livre entre vos mains plus tôt: vous en aviez besoin. Vous voudrez lire et écrire du code qui ressemble à la mienne et fonctionne comme le mien et fait la même chose mais encore mieux, car si vous pouvez vous permettre d'acheter ce livre au lieu d'écraser votre clavier comme je le fais juste pour créer un livre vous-même en demandant de l'argent Pour cela, vous avez les ressources dont vous avez besoin pour réussir dans votre vie. J'ai eu toutes sortes de problèmes avec la croissance de la famille, les problèmes de santé, les médecins, les médias et la loi, et mon code reflète profondément la lutte qui est le féminisme et la nature féminine dans un monde divisé et frustré. Cependant, ce livre est quelque chose qui me tient profondément à cœur, mon bébé, mon portefeuille et mes moyens de subsistance, donc j'apprécie votre considération lorsque vous ramenez le texte à la maison et que vous y porez soigneusement pour apprendre de moi. Veuillez garder à l'esprit que je ne suis pas parfait,Le livre aura des erreurs, des révisions et de nouvelles éditions, et vous devrez penser avec votre cerveau logique du mieux que vous le pouvez afin d'avoir une expérience réussie avec mon écriture. Comprenez également que je veux dire bien pour vous même lorsque vous faites face à des défis lors de l'écriture. Pensez-y comme ceci: lorsque vous pouvez simplement louer un système informatique pour faire tout ce que vous pouvez imaginer dans l'espace numérique, stocker toutes les informations que vous rencontrez, # $%! Rencontrez inévitablement des difficultés avec les informations que vous ingérez et même publier. Je vous le dis parce que je rencontre les mêmes difficultés. Utilisez ce livre à vos risques et périls, travaillez avec votre communauté et vos communautés à votre disposition pour construire des logiciels dans un cadre sûr, et n'apportez pas les choses personnellement lorsque vous échouez ou même réussissez dans le mauvais sens: c'est comme ça que je suis allé aussi loin , et pourquoi je peux vous apporter ce texte et vous aider à réussir sans se détacher sur un chemin de folie qui partMoi, j'ai ruiné, déchiré et effiloché pendant que je rencontre les problèmes ordinaires que tout le monde fait à l'échelle mondiale grâce à l'échelle mondiale paralellistique du réseau sur lequel nous travaillerons, Internet. Vous ne connaissez peut-être pas très bien qui je ne suis que quelques mots, mais je vous encourage à lire, vous pourrez me connaître alors que vous continuez à me lire et à me comprendre tout en construisant vos propres projets pour terminer votre travail. Il n'y aura pas de devoirs avec ce livre, tant que vos professeurs ou enseignants ne vous en affectent pas, mais je vous encourage fortement à construire un portefeuille de projets vous-même pendant que vous lisez, ainsi qu'un projet Capstone mettant en valeur la façon dont vous pouvez Appliquez ce que vous avez appris. Mon projet Capstone est la base de la...
Apprentissage approfondi et sécurité pratiques basés sur le Web par l'exemple

Apprentissage et sécurité en profondeur basée sur le Web par exemple Troisième édition Charlotte Harper 3 juillet 2024 Avant-propos: Les considérations de sécurité dans la création de logiciels pour le Web sont une partie importante du plan et de l'exécution de tout développeur Web tout en gérant un prototype fiable, stable et utile à des fins pratiques. Le DOM (document d'objet de document), avec son implémentation de HTML, JavaScript et CSS ainsi que des logiciels backend implémentant Python, C / C ++, Java et Bash, donnent aux développeurs Web la liberté et le pouvoir de créer une grande variété de projets qui expriment Créativité, offrez une facilité d'utilisation et des fonctionnalités, représenter l'humilité et le caractère, et assurez-vous une facilité d'utilisation ainsi que la commodité et les services importants qui sont tous attrayants pour le Joe moyen, l'utilisateur final qui cherche à tuer Il est temps ou faire quelque chose sur Internet, généralement sur un périphérique pour smartphone à écran tactile. La plupart des gens ne savaient même pas par où commencer lorsqu'ils veulent créer un site Web à partir deScratch, ils auraient tendance à commencer sur le site Web d'une autre personne et à construire quelque chose de limité dans les fonctionnalités, la fiabilité, la facilité d'utilisation et surtout la créativité alors qu'ils auraient pu avoir tous les derniers outils puissants à leur disposition afin de créer quelque chose d'utile sans perdre de temps à appuyer sur les boutons , et surtout gaspiller de l'argent en payant des abonnements coûteux aux logiciels que peu de personnes voulaient utiliser de toute façon étant donné ses limites de facilité d'utilisation et de flexibilité. Si vous avez quelques minutes pour lire ce livre et apprendre ce que je veux vous apprendre, ou même me parler personnellement de vos objectifs et obtenir des conseils dans la bonne direction, et que vous êtes motivé à apprendre à coder et à écrire votre propre logiciel , ramenez ce livre à la maison et réservez un peu de temps pour apprendre à créer l'application Web influente, puissante, rationalisée et importante, un site Web qui est sur vous et qui fait exactement ce que vous voulez et répond aux besoins de votre public. Sur moi: Je suis un développeur de logiciels avecgamme d'expérience en C / C ++, Java, Python, HTML, CSS et JavaScript. Je construis des sites Web que les gens souhaitent utiliser, veulent visiter, et même devenir accro à utiliser juste pour apprendre, recréer et tuer du temps, et surtout, je vends un logiciel. Si vous aviez une idée exactement de la façon dont vous vouliez qu'un site Web ressemble et fonctionne, vous étiez prêt à me soutenir afin que je puisse répondre à mes propres besoins pendant que je rencontre le vôtre, et vous êtes prêt à couvrir les coûts de la gestion d'un site Web vous-même, Je vous créerais le prochain YouTube, Tiktok, Twitter, Google ou même une application de sécurité de haute technologie à laquelle vous pouvez accéder. Au lieu d'essayer de vous vendre mon temps, j'essaie d'acheter le vôtre: je veux vous parler de créer une application (site Web) vous-même avec les informations qui existent déjà et vous apprendre ce dont vous avez besoin pour être un développeur de logiciels indépendants, Entrepreneur, menant une carrière réussie dans le domaine que vous désirez. Et laissez-moi être clair, l'éducation que je vous donne sera informelle. Vous pourriez aller à l'école et apprendre tout cela avec unL'éducation formelle, ou même lire ce livre à l'école, terminer vos tâches et retirer beaucoup de votre éducation, mais je ne vous mettrai pas officiellement sur la sellette et vous demanderai de terminer les missions. Je ne suis pas votre professeur, vous pouvez penser à moi comme un ami qui veut vous guider vers une carrière axée sur votre propre succès personnel. Et je ne vends pas non plus de succès, vous devrez l'acheter avec votre temps. L'apprentissage du code a une courbe d'apprentissage abrupte et n'a jamais été facile, ni même censé l'être. Vous devez travailler aussi dur que possible et continuer à essayer d'échouer et de réessayer même lorsque vous êtes frustré pour apprendre et créer vous-même des applications. C'est dans la nature du code lui-même. Le code est exécuté par un compilateur conçu pour donner des messages d'erreur du programmeur, et ceux-ci vous apprendront comment coder, même si vous copiez simplement l'erreur dans votre moteur de recherche et lisez les exemples des autres. Et je dois dire que vous n'avez pas besoin d'être extrêmement riche, intelligent,Essentif, ou même orienté vers le détail ou organisé pour créer une application. L'ordinateur s'occupe de cette organisation pour vous. Il vous suffit de persévérer à travers les essais et les erreurs, de maintenir la concentration et de travailler dur dans ce que vous faites, et vous aurez une carrière très réussie dans l'intégralité de ce que vous faites. Qui suis-je: Je me rends compte que la dernière section concernait davantage l'apprentissage et que vous prenez un chemin de ce livre. Qui suis-je exactement? C'est une question compliquée. Je ne suis pas clair à ce sujet moi-même, car je souffre de conditions médicales qui peuvent me rendre difficile de coder ou d'écrire ce livre parfois, tout en présentant des défis avec des problèmes de socialisation et d'identité qui me rendent la vie plus difficile lorsqu'il s'agit de me présenter . En bref, si vous lisez ce livre, vous l'avez ramené à la maison parce que vous l'avez traversé et que vous pensiez que c'était utile, ou même si vous lisez aussi loin, je suis une personne partante qui veut vous voir réussir dans tout ce que vous faites. Je suis moi-même ingénieur,développeur et étudiant, et j'écris ce livre pour d'autres étudiants qui veulent leur faciliter la vie en ayant un manuel du logiciel dont ils ont besoin pour faciliter leur vie en donnant des exemples pour copier qui s'adapte comme un grand puzzle dans un travail , Utile, grand, fonctionnel, cohérent et engageant l'application qui peut stimuler le succès, peu importe le secteur des affaires. En grande partie, c'est ce que je fais: je crée des applications pour aider moi-même et les autres à réussir. Je suis aussi un auteur, bien que ce soit ma première publication que j'ai l'intention de terminer afin de réunir mon portefeuille dans un document utile, et je suis également un artiste. Je vais vous admettre, je suis en quelque sorte une personne étrange. Je ne suis pas parfait, j'avais dirigé des Ins avec la loi me conduisant même à quitter les collèges et les universités et quitter les États afin d'essayer de me faire un nom avec plus de succès. Je suis une femme de naissance, je porte du maquillage, je prends des photos de moi-même, je porte des robes et d'autres vêtements pour femmes, et je reste conscient de moi en tant queFemme par nature. J'ai eu des problèmes avec d'autres personnes dans le passé qui ont conduit à des difficultés avec l'écriture et la création de Webapps, et je m'excuse de ne pas avoir pu obtenir ce livre entre vos mains plus tôt: vous en aviez besoin. Vous voudrez lire et écrire du code qui ressemble à la mienne et fonctionne comme le mien et fait la même chose mais encore mieux, car si vous pouvez vous permettre d'acheter ce livre au lieu d'écraser votre clavier comme je le fais juste pour créer un livre vous-même en demandant de l'argent Pour cela, vous avez les ressources dont vous avez besoin pour réussir dans votre vie. J'ai eu toutes sortes de problèmes avec la croissance de la famille, les problèmes de santé, les médecins, les médias et la loi, et mon code reflète profondément la lutte qui est le féminisme et la nature féminine dans un monde divisé et frustré. Cependant, ce livre est quelque chose qui me tient profondément à cœur, mon bébé, mon portefeuille et mes moyens de subsistance, donc j'apprécie votre considération lorsque vous ramenez le texte à la maison et que vous y porez soigneusement pour apprendre de moi. Veuillez garder à l'esprit que je ne suis pasect, ce livre aura des erreurs, des révisions et de nouvelles éditions, et vous devrez penser avec votre cerveau logique du mieux que vous le pouvez afin d'avoir une expérience réussie avec mon écriture. Comprenez également que je veux dire bien pour vous même lorsque vous faites face à des défis lors de l'écriture. Pensez-y comme ceci: lorsque vous pouvez simplement louer un système informatique pour faire tout ce que vous pouvez imaginer dans l'espace numérique, stocker toutes les informations que vous rencontrez, # $%! Rencontrez inévitablement des difficultés avec les informations que vous ingérez et même publier. Je vous le dis parce que je rencontre les mêmes difficultés. Utilisez ce livre à vos risques et périls, travaillez avec votre communauté et vos communautés à votre disposition pour construire des logiciels dans un cadre sûr, et n'apportez pas les choses personnellement lorsque vous échouez ou même réussissez dans le mauvais sens: c'est comme ça que je suis allé aussi loin , et pourquoi je peux vous apporter ce texte et vous aider à réussir sans détourner sur un chemin de folieM'a me ruine, déchiré et effiloché pendant que je rencontre les problèmes ordinaires que tout le monde fait à l'échelle mondiale grâce à l'échelle mondiale paralellistique du réseau sur lequel nous travaillerons, Internet. Vous ne connaissez peut-être pas très bien qui je ne suis que quelques mots, mais je vous encourage à lire, vous pourrez me connaître alors que vous continuez à me lire et à me comprendre tout en construisant vos propres projets pour terminer votre travail. Il n'y aura pas de devoirs avec ce livre, tant que vos professeurs ou enseignants ne vous en affectent pas, mais je vous encourage fortement à construire un portefeuille de projets vous-même pendant que vous lisez, ainsi qu'un projet Capstone mettant en valeur la façon dont vous pouvez Appliquez ce que vous avez appris. Mon projet Capstone est la base de la majeure partie de ce que vous lirez dans ce livre, car il intègre le code de mes projets précédents, le code que j'ai créé et appris à écrire méthodiquement à la main, et un large éventail d'idées et de conseils qui m'ont aidé réussir au point où je peux tourner une application simple quiEntièrement en vedette et ressemble et se comporte comme une application populaire que vous pourriez voir votre ami ou votre famille utiliser, sur Internet, annoncé pour vous ou dans les nouvelles. Ce qu'est ce livre: Ce livre est un tutoriel par l'exemple. Vous pouvez trouver du code ici, des instructions pour savoir comment apprendre à coder, des informations sur le code de débogage et la correction des erreurs, le dépannage des étapes, les instructions sur la façon de sauvegarder et d'enregistrer votre code, de redéployer si quelqu'un déferle votre code, sécurisez votre code, déploie Votre code, créez des sites Web interactifs divertissants, engageants et addictifs, et vous aurez une idée de qui je suis, pourquoi c'est important et comment vous représenter, votre application et votre image d'entreprise, ainsi que les Le logiciel que vous créez dans la meilleure lumière absolue pour être le plus attrayant que possible pour vos utilisateurs finaux, les visiteurs de votre site Web. Dans ce livre, je vais démontrer un certain nombre d'exemples de conception de logiciels en mettant l'accent sur le Web en tant que plate-forme ainsi que la sécurité. Nous initierons l'expérience d'apprentissage en construisant une baseOject à l'aide du shell Unix, avec des fonctionnalités de sauvegarde et de script. Ensuite, nous examinerons un site Web de blog de base, mettrons à niveau notre blog avec des fonctionnalités photo et vidéo ainsi que pour utiliser ces fonctionnalités pour utiliser des solutions de sécurité à l'aide d'un logiciel gratuit et sécuriser notre serveur à l'aide d'un module d'authentification en permanence (PAM). Nous examinerons ensuite la gestion et le traitement des fichiers, explorant l'édition vidéo, le don vocal, la numérisation des codes à barres et la reconnaissance des caractères optiques, entre autres concepts. En cours de route, nous examinerons les API qui nous aiderons à rendre notre logiciel plus utile et sécurisé, avec des options gratuites et payantes. En cours de route, nous explorerons la sécurité physique et les outils militants tels que la conception et la fabrication des armes à feu et des munitions, y compris la conception de barils et de répétiteurs, la conception de tourelles et de drones, et d'autres directeurs que nous intégrerons avec notre logiciel sur le réseau existant afin de protéger notre logiciel et démontrer l'autodéfense et la résilience. Nous allons faire des pauses en cours de route pour construire des jeux, 2D et 3DEnfiler les moteurs et travailler avec du matériel intégré dans des exemples d'étude de cas de logiciel de rendu dimensionnel de base et un masseur vibrant électronique coulé respectivement dans du caoutchouc de silicone. En cours de route, nous utiliserons également des solutions d'apprentissage automatique déjà disponibles afin de mieux sécuriser notre logiciel. Nous utiliserons également des outils de stock disponibles pour le Web afin de rationaliser et de sécuriser le processus. Ce livre est un guide de votre succès dans la création d'une application Web et l'intégrer à un réseau professionnel de systèmes mécaniques informatiques et intégrés, et dans l'ensemble un guide pour créer des logiciels et du matériel intégré sans connaissance de fond ou expérience précédente. Ce que ce livre n'est pas: Si vous voulez vraiment avoir un site Web, vous pouvez simplement configurer un magasin simple et vendre ce dont vous avez besoin, publier un blog, publier des photos ou des vidéos, ou autrement sans jamais écrire une seule ligne de code. Ce livre n'est pas ça. Ce livre vous apprendra à créer des logiciels plus utiles, entièrementen vedette, fonctionnelle et sécurisée que n'importe quel logiciel que vous pouvez déjà trouver, car il déploie les derniers logiciels qui sont toujours des prototypes, peuvent être coûteux à exécuter à une échelle des entreprises plus anciennes et ne font pas appel à des sociétés alambiquées en arrière et conçues Gagnez de l'argent pour les gens qui ne font rien. Si vous suivez ce livre de près, vous voudrez écrire du code, rechercher du code, créer vos propres applications et vous gagnerez de l'argent à partir de ce que vous faites. Je ferai de l'argent à partir de ce livre, même à des premiers stades, car il contient des informations dont les gens ont besoin et veulent lire, et achètent déjà lorsqu'ils achètent ou utilisent mes applications. Ce livre ne créera pas une application pour vous, mais il vous indiquera dans la bonne direction et vous armera avec les outils dont vous avez besoin et les compétences et les conseils qui faciliteront votre propre succès dans la création de logiciels pour le Web, avec chaque ligne de Code que vous devrez écrire à titre d'exemple, prêt à être reconstitué dans le logiciel que vous et vos supporters, invités, clientèle,Les Riends, la famille, les visiteurs, les entrepreneurs et les habitants d'Internet souhaitent utiliser et soutenir. Ce que vous apprendrez: Ce livre vous apprendra à créer et vendre des logiciels, des logiciels vraiment fonctionnels et utiles, l'enregistrement des médias, des fonctionnalités de sécurité telles que la reconnaissance faciale, la numérisation des codes à barres lisible par la machine, les API Web pour authentifier, enregistrer et rendre des photos et des photos et échanger des messages comme Bluetooth et la communication proche du champ (NFC). Ce livre vous apprendra à utiliser un ordinateur en réseau, en se concentrant sur Debian Linux, comment créer du code bash pour faire de l'installation et de la sauvegarde de votre logiciel une brise transparente et automatisée, comment créer du code Python comme backend pour servir des messages dynamiques, style Les choses à l'aide de styles CSS avec bootstrap, permettent aux connexions des utilisateurs et à l'interactivité via des appareils en réseau, créez des médias interactifs et réseau Objectifs, numérisation d'identification, modération d'image et vidéo, donnéesRansactions pour garder votre logiciel en sécurité, le traitement des paiements, le trading des crypto-monnaies, les tâches asynchrones, et plus encore. Vous apprendrez à construire vos propres appareils Bluetooth, avec des batteries, des chargeurs, des microcontrôleurs, des circuits, des moteurs et des capteurs, en utilisant la soudure, le fil et la 3D imprimés ainsi que les matériaux coulés. Je vais démontrer les directeurs de conception 3D appliqués à la fabrication additive et à la fabrication d'outils et de matrices, vous pouvez donc fabriquer vos propres dispositifs matériels intégrés avec des batteries, des chargeurs, des circuits électroniques et des sorties fonctionnelles intégrées. et les réseauter avec Bluetooth et le Web. Plus précisément, nous examinerons deux études de cas, un masseur vibrant et une arme à feu faits maison, tous deux programmés dans OpenSCAD, qui est disponible sous forme d'interface graphique ou d'utilité de ligne de commande et peut être intégré dans un Web pour des résultats plus rapides. Vous apprendrez à créer et à déployer un site Web à partir de zéro sans aucune expérience préalable, le rendre fonctionnel, sécurisé, beau, utile et le plusMorporellement pratique. Vous apprendrez à utiliser l'apprentissage automatique et la vision par ordinateur pour rendre un site sécurisé et plus pratique, enregistrer la vidéo et l'audio à partir de votre site Web, faire don de votre voix, faire de la musique et moduler l'audio pour créer des échantillons utiles, et comment percer le bruit par Tirer parti d'autres sites Web pour créer le meilleur réseau possible de sites Web que vous pouvez lier directement avec le vôtre afin de partager toutes les informations utiles que vous avez à offrir, et plus important encore, amenez les gens à votre logiciel et à votre entreprise. Ce livre sera le plus axé sur les médias, la sécurité et l'apprentissage automatique, qui sont les trois principaux composants qui vous aideront à créer un logiciel utile pour le Web en engageant les bons utilisateurs et en désengageant les mauvaises d'une manière réaliste, pratique, pratique et engageant tout en étant automatique, et robuste. Ce livre enseigne Unix, spécifiquement Debian (Ubuntu), Bash Shell, Python, HTML, CSS, JavaScript et un certain nombre de packages de logiciels utiles pourn Aime les demandes, ainsi que des logiciels de bash utiles comme Git et FFMPEG. Je vais également vous apprendre à échanger automatiquement la crypto-monnaie et à prendre des paiements en crypto-monnaie ou à des cartes de débit régulières tout en payant à vos visiteurs une part de vos revenus si vous choisissez de le faire. Je vais vous apprendre à gagner de l'argent à partir de votre site Web via la publicité, comment préparer votre application pour les moteurs de recherche et le faire rapidement, classé dans le premier classe recherche comme possible. Je vais vous apprendre à vendre votre logiciel, à l'annoncer, à faire appel aux clients à la recherche de vos services et à vous faire un nom sur Internet via des avenues qui existent déjà, sont peu coûteuses et fonctionnent bien. Je vais vous apprendre à enregistrer vos données sur des ordinateurs cloud qui fonctionnent pour vous et à enregistrer vos données à moindre coût, à planifier et à créer un site Web qui fait ce que vos utilisateurs veulent et ce que vous voulez, et comment garder vos utilisateurs engagés parEn insistant votre site, appuyez sur leurs téléphones avec des notifications, des e-mails, des messages texte, des appels téléphoniques et plus de voies pour ramener vos utilisateurs à votre site Web à votre disposition derrière le clic d'un bouton qui vous est sécurisé uniquement. Ce livre se concentrera sur l'aspect pratique de la publication et de la distribution des médias en grande quantité, du texte aux photos en passant Un site Web, une application qui est représentative de vous et de vous uniquement, et vous fait, votre logiciel et votre entreprise sont beaux de la meilleure façon possible. Vous apprendrez également quelques conseils et astuces de moi, des conseils de codage, de la vanité pratique comme le maquillage et la photographie, la modélisation et le jeu, et plus encore, qui seront importants pour représenter vous-même et votre entreprise dans la meilleure lumière possible en utilisant tous les outils disponibles disponibles à vous tout en distribuant autant de contenu que vous avez besoin sur un équilibre sain de plateformes pour apporter votreE pour se concrétiser sans plus d'effort, de travail ou d'argent que nécessaire. Ce livre est appelé «Exemple d'apprentissage et de sécurité en profondeur basé sur le Web» pour une raison: il traite de l'apprentissage du code, en particulier pour le Web, en particulier en mettant l'accent sur la sécurité, d'un point de vue pratique, avec des exemples de code de travail qui sert Les objectifs pratiques décrits dans le texte. Le composant d'apprentissage de ce texte englobe également l'apprentissage automatique, le code que je vais vous montrer comment exécuter pour le Web qui s'occupera de la vision de l'ordinateur, de la reconnaissance faciale, de la modération de l'image et de la vidéo, de l'amélioration de l'image, de l'amélioration de la résolution, du sous-titrage de l'image et d'autres tâches comme Les mesures de prédiction provenant d'images, telles que la nature de l'image en tant qu'image authentique et transférée par ordinateur, ou une copie optique (photo d'une image ou photo imprimée). L'apprentissage automatique est très important en ce qui concerne la sécurité Web et la sécurité des logiciels, car il peut effectuer des tâches qui étaient autrement impossibles. Votre ordinateurEnregistrez-vous avec un code d'accès, mais il peut être plus sûr de l'utiliser s'il vous enregistre avec votre visage. Vous pouvez faire un ordinateur de serveur en toute sécurité, un ordinateur qui vous demanderait normalement un nom d'utilisateur et un mot de passe et vous enregistrer, peut-être avec un jeton de confirmation pour chaque nouvelle connexion ou nouvelle adresse IP, mais si vous construisez à grande échelle, facile à Utilisez, des logiciels fondamentalement sécurisés et puissants, cela peut suffire. Lier trop étroitement votre logiciel avec le logiciel de quelqu'un d'autre, comme un service de messagerie ou un service de SMS, ne suffit pas à rendre votre logiciel sécurisé, ou à quiconque (tout site que vous utilisez). Quiconque construit des logiciels impeccablement sécurisés a une idée de ce que cela implique. Le logiciel est intrinsèquement sans sécurité car les appareils et les comptes que nous utilisons pour y accéder ne sont pas toujours à notre disposition, ils pourraient être entre les mains de toute personne ayant une mauvaise intention pour le logiciel et peuvent donc poser un risque pour le logiciel lui-même. C'est quelque chose de l'objectif de ce livre. Un ordinateur en réseau est par défautsécurisé avec un jeton à clé long, appelé et ssh ou sécurisé shell, et est par ailleurs mieux sécurisé avec un serveur Web, car le serveur Web fournit l'accès ouvert ainsi que les outils de sécurité de pointe exécutés sur le serveur lui-même. Le serveur Web a accès au navigateur Web de l'utilisateur, qui est sans doute la partie la plus puissante de l'appareil de l'utilisateur, car c'est l'endroit où l'utilisateur peut accéder à un logiciel en réseau. Cette boîte à outils peut rendre du texte, les pages Web que vous voyez, et peut également enregistrer des images, l'audio et la vidéo (comme une photo d'un visage ou d'un identifiant d'état), peut lire et écrire sur des appareils radio Bluetooth, et peut lire et écrire sur un champ proche Les balises de transpondeur, les cartes clés bon marché, les FOB, les autocollants, les anneaux et même les implants de puce avec des numéros de série uniques qui peuvent être lus et écrits avec des données générées et validées par un serveur Web lié au site Web. En utilisant tous les outils à votre disposition, avec ce livre, vous vous équiperez des connaissances pour créer un site Web sécurisé, et dans l'ensemble unSystème informatique en réseau URE qui fonctionne pour vous, fait vos enchères et semble bien. Par où commencer: Vous êtes invités à passer la section avec laquelle je commence ce livre, ou n'importe quelle section, au code exact dont vous avez besoin, surtout si vous avez de l'expérience avec le codage avant ou l'un des outils susmentionnés que je décrirai en détail dans ce livre comme ainsi que de documenter les cas d'utilisation et leurs exemples pratiques. Si vous n'avez pas d'expérience dans la rédaction de code, je vous recommande fortement de lire tout ce livre, et surtout vous recommande de lire les sections précédentes, pour vous assurer que ce livre vous convient. Si ce livre ne vous convient pas, envisagez de le offrir à un ami ou à un parent qui pourrait être intéressé à apprendre le développement Web lui-même, et même envisager de l'emprunter et d'apprendre d'eux pour combler les lacunes où je vous ai échoué en tant que L'enseignant ou d'autres professeurs l'ont fait avant moi. Commencez où vous allez, chaque partie de ce livre sera utile si vous avez l'intention de créer un utilePP, et considérez que les meilleures applications sont construites en pensant à l'utilisateur final: connaissez votre client. Maintenant, vous me connaissez, vous connaissez ce livre et vous êtes prêt à commencer. Pour commencer, prenez un ordinateur (même l'ordinateur portable le moins cher à partir d'un magasin de boîtes, d'Amazon ou d'un vieux bureau, et de le configurer d'une manière qui fonctionne pour vous. Comment lire ce livre: Texte en surbrillance, indique que le texte appartient à une invite de commande, où vous rédigerez le code que vous exécutez. L'invite de commande est fortement axée sur le clavier et nécessite peu ou pas de clic, accélérant votre flux de travail et vous facilite les choses. Commencer: Plongeons. Nous commencerons par créer du code sur une machine locale et commençons sans créer un site Web connecté à Internet. C'est plus sûr de commencer, ne coûte rien et est facile pour vous. Selon votre système d'exploitation, entrer dans un coquille de bash sera un peu différent. Pour Mac OS, je recommande d'installer une machine virtuelle à ce stade, car vous obtiendrez le plus de compatibilité avecmachine virtuelle. Divers fournisseurs tels que VirtualBox et Paralells peuvent exécuter une machine virtuelle pour vous, bien qu'il soit également possible d'installer Ubuntu directement sur la machine, si vous préférez utiliser un environnement natif qui est recommandé afin de créer une expérience rapide et rationalisée. Si vous utilisez Linux ou Windows, ce que je recommande, il devrait être assez facile de créer un projet. Ouvrez votre terminal, ajustez le dimensionnement comme bon vous semble et commencez à suivre l'étape 2. Si vous utilisez Windows, veuillez suivre l'étape 1. Étape 1: - Utilisateurs de Windows uniquement Dans Windows, ouvrez l'invite de commande en tant qu'administrateur et tapez wsl - installer Étape 2: - Continuez ici, ou sautez l'étape 1 ici si vous n'utilisez pas Windows Dans un terminal ouvert, (selon votre système d'exploitation, appelé Ubuntu dans Windows, Terminal dans Mac ou Linux, ou un nom similaire), commencez par créer un projet. Nous le faisons avec la commande MKDIR, qui crée un répertoire. Si vous devez créer un répertoire pour stocker votre projet, ce qui est recommandé, utilisez leCommande CD pour passer au répertoire et et et CD / Path / To / Directory - Le chemin est les dossiers (fichiers) qui précèdent votre répertoire de destination, votre chemin par défaut est ~ ou / home / username (où le nom d'utilisateur est votre nom d'utilisateur). Pour passer au répertoire par défaut, tapez CD ou CD ~ Exemple MKDIR - Remplacez «Exemple» par le nom du répertoire Vous avez maintenant un répertoire de travail pour votre projet. Étant comme il est si important de faire enregistrer ce répertoire au cas où vous auriez besoin de passer à une autre machine ou de déployer le code que vous écrivez afin qu'il soit prêt pour le Web, nous créerons un script pour sauvegarder votre répertoire dans les prochaines étapes. Mais la construction d'un script prend un peu de code, et le code doit être automatisé pour être aussi utile que possible. Créons donc un script pour créer d'abord des scripts. Commençons par créer le script et en le faisant exécutable. Nous utiliserons sudo, chmod et touch pour cela, et appelons le script


sudo touch /usr/bin/ascript
sudo chmod a+x /usr/bin/ascript
sudo nano /usr/bin/ascript
Maintenant, nous avons créé le script, l'a fait exécutable et sommes prêts à le modifier. Nano est un éditeur de texte qui vous permettra de modifier le texte sans cliquer, ce qui est beaucoup plus facile que d'utiliser une interface utilisateur graphique. Pour modifier un fichier avec Nano, utilisez Nano puis le chemin d'accès au fichier. Pour faire un script qui fait un script, il est assez similaire à la fabrication de notre script en premier lieu. Nous utiliserons le même code que ci-dessus, en remplaçant le nom du script, «AScript» par un paramètre d'argument, 1 $. Cela nous permet d'appeler le script en tapant simplement sudo ascript newscript, à quel point nous pouvons créer tout nouveau script en remplaçant «Newscript» par le nom de votre script. Le code dans Nano devrait ressembler:

sudo touch /usr/bin/$1
sudo chmod a+x /usr/bin/$1
sudo nano /usr/bin/$1
Et pour fermer Nano, nous pouvons maintenir la touche de contrôle et appuyer sur X, puis Y pour indiquer que nous enregistrons le fichier et appuyez sur Retour. Maintenant, au lieu de taper ces trois commandes pour modifier un script, nous pourrons taper sudo ascript ascript pour modifier à nouveau le script. Cela fonctionne! Et tout nouveau script peut être exécuté facilement en l'appelant dans le shell. Enregistrons notre travail maintenant: écrivons un script de sauvegarde pour enregistrer notre nouveau script, puis le soutenons dans notre répertoire de projet, tout en sauvegardant le script de sauvegarde.

sudo ascript backup
Maintenant, dans Nano:

sudo cp /usr/bin/backup /path/to/directory/
sudo cp /usr/bin/ascript /path/to/directory/
WHERE / PATH / TO TO / DIRECTORY est le chemin du projet que vous avez créé avec MKDIR. Plus tard, nous apprendrons à copier des chemins de répétition comme celui-ci avec une boucle et une liste, ce qui est moins de code, mais pour l'instant, gardons les choses simples et ayez quelques lignes. Pour exécuter ce script et sauvegarder votre code, enregistrez le fichier dans Nano avec Control + X, Y et retourne, et tapez ce qui précède dans votre shell

backup
Si vous êtes invité à un mot de passe tout en lisant ce livre et en suivant le shell, veuillez saisir correctement votre mot de passe utilisateur, vous aurez trois essais avant de devoir relancer la commande. Vous pouvez utiliser les flèches de haut en bas pour réacheminer les commandes et de les modifier, si vous devez exécuter quelque chose deux fois. Appuyez sur Simple de haut en bas par intermittence pour sélectionner une commande, avant de modifier la commande avec les flèches à droite, les flèches gauche et la touche de supprimer ainsi que le clavier, et l'exécuter avec Return. Félicitations! Vous avez réussi à créer un script de sauvegarde génial qui soutient deux scripts shell importants dans votre répertoire de travail. Nous pourrions déplacer les choses plus tard à mesure que le projet s'agrandit, mais cela fonctionne pour l'instant. Passons à la sauvegarde dans le cloud, nous utiliserons GitHub pour cela (bien qu'il existe de nombreuses autres solutions GIT pour la sauvegarde, elles sont toutes à peu près les mêmes.) Git est un logiciel de contrôle Verision qui vous permet de sauvegarder les modifications à votre logiciel comme vous les faites sur un serveur, tandis queVous permettant également de télécharger des copies entières de votre logiciel derrière un mot de passe ou une clé. Il contribue à enregistrer votre logiciel, d'autant plus que nous migrons vers des instances Linux sécurisées qui se cassent parfois lorsqu'une seule ligne de code échoue, vous laissant verrouillé alors que votre code peut ne pas être sauvegardé si vous n'avez pas la chance de le soutenir Autant automatiquement, que nous couvrirons. Si vous n'utilisez pas déjà une machine virtuelle Ubuntu à ce stade, je recommande à l'aide d'une machine virtuelle Ubuntu à ce stade car elle vous facilitera la vie lors de l'installation de tous les packages nécessaires pour créer un site Web de travail et préformer l'apprentissage en profondeur opérations sur votre ordinateur. Nous déplacerons le code vers un serveur Web dans un avenir proche, mais nous voulons nous assurer qu'il y a au moins quelques couches de sécurité derrière notre serveur Web qui résistent au phishing et utilisent un certain nombre de packages Linux pour faire ce. Si vous souhaitez toujours utiliser Mac OS, vous êtes invités à rechercher et à installerE Packages nécessaires en ligne, mais il n'y a peut-être pas d'alternatives pour chaque package que ce livre ou série couvrira. Ajoutons quelques commandes pour commettre notre travail avec le script de sauvegarde en exécutant la commande sudo ascript

# …
git add –all
git commit -m “backup”
git push -u origin master
Encore une fois, contrôlez x pour enregistrer. Nous devons maintenant faire une configuration unique pour ce projet. Parce que ce sera bientôt un projet GIT, nous n'avons pas besoin de taper toutes les commandes à chaque fois que nous déploiez à partir d'un référentiel GIT, mais nous aurons le respect de cela lorsque nous écrivons nos scripts de déploiement. Pour commencer, assurez-vous que nous sommes dans le bon répertoire et initialisez le référentiel GIT et générons des clés SSH.

cd /path/to/directory
git init
git branch -m master
ssh-keygen
Après avoir saisi SSH-Keygen, la nouvelle clé doit être enregistrée dans le dossier à domicile dans un dossier appelé .ssh. Il s'appelle id_rsa.pub. Trouvez cette clé et copierons-la. Pour le voir,

cd ~
cat .ssh/id_rsa.pub
Copiez le texte qui est renvoyé par la dernière commande et créez un compte avec votre fournisseur GIT (idéalement github), avant d'ajouter la clé SSH à votre compte. Une fois que vous avez un compte, cliquez sur le menu supérieur droit et entrez les paramètres, avant d'ajouter votre touche SSH dans les touches SSH et GPG sous accès dans le menu. Sélectionnez Ajouter une touche SSH et ajoutez le vôtre en le collant et en lui donnant un titre, avant d'enregistrer et de revenir à GitHub pour créer un nouveau référentiel. Ceci est similaire pour d'autres fournisseurs GIT, vous devrez lire leur documentation. Dans la nouvelle configuration du référentiel, donnez à votre référentiel un nom descriptif et décidez si vous souhaitez le publier et assurez-vous de ne configurer aucun fichier pour l'inclusion. Une fois le référentiel créé, copiez le clone avec l'URL SSH et collez-le dans la commande suivante.

git remote add git://… (your remote URL)
Vous pouvez maintenant revenir à votre référentiel avec CD, vous le connaissez. Essayez votre script de sauvegarde maintenant avec une sauvegarde Super! Maintenant, nous pouvons vraiment obtenir du codage. Installons Django maintenant que nous avons une bonne compréhension de Bash et Git. Django nous permettra automatiquement de sauvegarder notre logiciel, Bash peut également le faire, mais Django devrait avoir une implémentation plus simple plus sûre (elle peut être désactivée et configurée plus facilement). Pour installer un logiciel dans Ubuntu, nous utiliserons la commande sudo apt-get. Tout d'abord, mettons à jour et améliorons le logiciel que nous avions déjà. Cela peut être fait avec la mise à jour Sudo Apt-Get et la mise à niveau Sudo apt-get -y. Ensuite, installons Python et notre environnement virtuel, la maison de notre code, avec la commande suivante: sudo apt-get installer python-is-python3 python3-vevv C'est tout ce dont vous avez besoin pour aller avec Django en termes d'installations logicielles dans l'instance Ubuntu. Pour Windows et Linux, cela devrait être assez simple, mais pour Mac, vous voudrez peut-être installer une machine virtuelle etLinux dessus à l'aide d'un environnement virtuel gratuit ou payé comme VirtualBox ou Paralells Desktop et recréer les étapes ci-dessus afin de configurer un environnement Ubuntu. Ubuntu est essentiel dans ce cas car c'est le logiciel que les sites Web exécutent et leur permet d'héberger des sites Web avec tous les logiciels susmentionnés. Discutons dans le Django. Dans notre annuaire encore, avec

python -m venv venv # Crée l'environnement virtuel où le code est stocké
source venv/bin/activate # Active l'environnement virtuel
pip install Django
django-admin startproject mysite . # Où MySite est le projet que je commence dans mon répertoire actuel.
Django ne fait que démarrer, car Django héberge le serveur Web et fait tout ce dont nous avons besoin pour obtenir un site Web local de base opérationnel. Maintenant que nous avons installé Django, modifions un peu les paramètres pour le faire fonctionner comme nous en avons besoin. Tout d'abord, créons une nouvelle application

python manage.py startapp feed
Vous remarquerez que la première application est appelée flux. L'application doit être appelée comme vous voulez, et nous créerons de nouvelles applications, mais le nom de chaque application doit être cohérent chaque fois que l'application est référencée dans le code. Pour ajouter une nouvelle application, nous modifierons toujours les paramètres.py dans l'autre répertoire créé par l'application, nommé dans StartProject, ci-après l'application. En utilisant nano,

nano app/settings.py
Dans les paramètres, trouvez installé_apps et séparez le [] en 3 lignes. À l'aide de quatre espaces sur la ligne centrale vide, ajoutez «Feed» ou le nom de votre application. Cette section des paramètres.py devrait ressembler:

INSTALLED_APPS = [
    'feed',
]
Avant d'oublier, testons que Django fonctionne. En utilisant la commande python manage.py runserver 0.0.0.0:8000, nous pouvons exécuter le serveur, puis naviguer dans un navigateur Web sur l'ordinateur exécutant le code vers http: // localhost: 8000 et voir un exemple de page Web (cela fonctionne!) Quittez le serveur avec Control C, comme n'importe quelle autre commande. Maintenant, foulons dans l'écriture de code python. Django a trois composants principaux, tous exécutés par code entièrement. Les composants sont appelés modèle, vue et modèle, et chacun est respectivement à un niveau supérieur et inférieur avant la livraison de la page Web à l'utilisateur. Le modèle est le code qui stocke les informations dans la base de données pour la récupération, la tri et le rendu. La vue décide comment le modèle est rendu, manipulé et modifié, presque toutes les vues utiliseront directement un modèle. Le modèle est le code HTML avec quelques cloches et sifflets supplémentaires appelés langue du modèle. Le modèle est rendu par la vue où il est rempli de code python etcontexte tel que les modèles et les informations (les chaînes et les entiers usuall) de la vue. Django a également d'autres composants, y compris, mais sans s'y limiter: Paramètres, qui configure l'application comme nous l'avons discuté. URL, qui sont des modèles que l'utilisateur suit pour accéder à des parties spécifiques de l'application Web. Formulaires, qui définissent comment les informations envoyées au serveur sont gérées et rendues à la base de données ainsi qu'à l'utilisateur. Ce sont les fondements du traitement des informations du côté serveur et peuvent accepter tout type d'informations dans les magasins informatiques, notamment les chaînes de texte, les nombres et les booléens vraies / faux (généralement des cases à cocher). Les modèles, qui sont du code HTML et du langage des modèles et comblent l'écart entre Python et HTML, ce qui signifie que les informations Python peuvent être servies de code HTML auquel tout le monde peut accéder et peut sécuriser un site Web avec un accès restreint, tout en rendant le code Python accessible et utile et utile à diverses fins sur un appareil distant qui neEed être près du serveur. Les fichiers statiques, qui sont généralement JavaScript et ses bibliothèques que le serveur sert et est lié au modèle. Fichiers multimédias, que le serveur sert ou est hébergé en externe, ou simplement écrit sur le serveur avant d'être traité et publié sur un autre serveur (un seau) pour l'hébergement. Middleware, qui est des éléments de code exécutés en même temps que chaque vue et qui sont considérés comme «inclus» dans la vue. Processeurs de contexte, qui traitent le contexte de chaque vue et sont utilisés pour ajouter un contexte supplémentaire. Tests, qui valident que l'utilisateur ou la demande transmet certaines exigences avant que la vue ne soit rendue. Les consommateurs, qui dictent la façon dont WebSockets gère et répond à la communication. Admin, qui est utilisé pour enregistrer des modèles afin qu'ils puissent être manipulés en détail dans la page d'administration Django, où la base de données peut être administrée via une interface graphique. Le céleri, qui définit les tâches asynchrones, les parties du code Django peuvent commencerNNING avant de passer immédiatement à la tâche ou à la ligne de code suivante. Django peut avoir de nombreux autres composants, dont nous discuterons en détail ici. Il existe de nombreuses façons de rendre Django plus fonctionnel, ajoutant des lignes Web, qui sont des canaux de communication rapides et rationalisés, du céleri, qui exécute des tâches asynchrones, et une multitude d'autres logiciels pour l'extension de Django, en particulier dans la vue, les fonctions, où la plupart de le code est exécuté. Les fonctions de vue sont essentielles car elles déclarent généralement chaque morceau de code spécifique à un modèle URL spécifique ou à une section du serveur. Tout d'abord, explorons les fonctions de vue. Les fonctions de vue commencent par des importations désignant le code qui sera utilisé dans la vue et est défini à l'aide de définitions ou de classes de fonctions régulières. Les vues les plus simples sont définies par la définition de fonction Def et renvoient une HTTPResponse avec un modèle de base. Commençons par définir une vue de base pour retourner le texte «Hello World». N'oubliez pas que chaque fois que vous ajoutezFter d'une déclaration comme Def, si, pour, pour, etc., vous devrez ajouter 4 espaces pour chacune des définitions précédentes que vous souhaitez appliquer à votre fonction. Nous entrerons dans ce que chacun signifie bientôt. Dans le répertoire de notre site, modifiez le fichier feed / views.py à l'aide de nano et ajoutez les lignes suivantes à la fin du

from django.http import HttpResponse

def hello(request):
    return HttpResponse('hello world')
La HTTPResponse de Django répond avec une chaîne de texte, indiquée avec l'ouverture et la clôture ». Chaque fois que vous transmettez des informations à une fonction ou à une classe, comme une demande ou une chaîne, vous devrez utiliser des parenthèses (, ouverture et fermeture). Ce n'est pas tout ce dont nous avons encore besoin pour voir notre point de vue. Bien sûr, nous n'avons pas dit au serveur où se trouvait la vue, nous devons toujours définir un chemin par lequel la vue devrait être rendu. Commençons par définir un chemin de base dans app / url.py, et nous entrerons dans des groupes de chemin plus tard. Dans APP / URLS.py, ajoutez une ligne après les instructions d'importation après l'importation de début de la vue que nous venons de créer.

from feed import views as feed_views
Maintenant, définissons le modèle de vue. Les modèles de vue ont trois composants, le composant de chemin, qui indique au serveur où la vue existe dans le serveur (le chemin d'accès URL que l'utilisateur tape dans la barre de navigation pour entrer la page Web), le composant de vue où la vue est spécifiée et un Nom convivial pour la vue, il est donc facile de récupérer son modèle lorsque vous travaillez avec un modèle, surtout pour que son nom puisse être modifié et mis à jour si nécessaire pour faire de la place pour une autre vue ou prendre un nom plus logique. Il est logique de faire les choses de cette façon et d'être flexible, car votre base de code sera un environnement en constante évolution qui nécessite une flexibilité et une improvisation afin d'être précieuses et faciles à travailler. Voici à quoi ressemblera votre point de vue, vous pouvez l'ajouter à la UrlPatterns = [Section de App / url.py. Le modèle de vue est défini avec les trois composants décrits ci-dessus, et une fonction appelée chemin. Vos modèles d'URL sont une liste, alors assurez-vous de toujours terminer chaque élément dedansavec une virgule, car cela sépare chacun. Chaque élément doit également aller sur une nouvelle ligne, encore une fois avec quatre espaces avant lui, tout comme l'application dans Settings.py. Nous définirons le premier composant de la vue avec une fonction de chaîne vide, afin de créer une vue qui s'exécute sur le répertoire racine du serveur Web. Votre url.py devrait maintenant ressembler à

from feed import views as feed_views

urlpatterns = [
    path('', feed_views.hello, name='hello'),
]
C'est la base de la création d'un site Web avec Django qui est complètement statique. Afin de créer un site Web plus dynamique où nous pouvons commencer à mettre en cache des informations, comme les images, les vidéos, l'audio et plus encore, nous devrons utiliser des modèles, que nous explorerons ensuite. Pour l'instant, vérifions notre code et exécutons le serveur. Pour vérifier les erreurs du code, exécutez:

python manage.py check
S'il y a des messages d'erreur, vous devez examiner attentivement les modifications que vous avez apportées à votre application et voir s'il y a quelque chose qui doit être corrigé, comme un espace étranger ou manquant, un caractère supplémentaire, une chaîne non clôturée, n'importe quelle faute Le caractère supprimé, ou autre chose. En lisant le message d'erreur (si vous en avez un), vous devriez être en mesure de voir le chemin d'accès à un fichier que vous avez créé ou édité avec un numéro de ligne, alors regardez ce fichier et cette ligne et voyez si vous pouvez corriger tout ce qui est là . Si vous avez résolu le problème, exécutez à nouveau la commande ci-dessus. Lorsque votre logiciel est prêt à s'exécuter et fonctionne, vous verrez la sortie «Le contrôle du système n'a identifié aucun problème». Maintenant, vous êtes prêt à partir. Exécutez le serveur avec:

python manage.py runserver 0.0.0.0:8000
Ouvrez maintenant un navigateur Web et accédez à http: // localhost: 8000. Vous devriez voir le texte renvoyé dans la parenthèse et les citations de la fonction HTTPResponse à votre avis. Ce n'est qu'un exemple de base, mais si vous avez fait jusqu'à présent, vous comprenez les bases du fonctionnement de Linux, Bash, Python et Django. Approfondissons une modélisation de la base de données et explorons la puissance d'une classe Python dans le stockage des informations. Ensuite, nous allons commencer à prendre une emprise sur HTML et CSS avant de créer notre site entièrement en vedette, flexible et sécurisé en utilisant JavaScript et l'apprentissage automatique. Les cours sont stockés dans les modèles.py de votre application. Utilisation de Nano, modifiez App / Models.py et ajoutez une nouvelle classe. Une classe est définie avec la définition de la classe et a passé une superclasse dont il hérite, dans ce cas Models.Model. Le nom de la classe vient après la définition de la classe, et après que la définition de la classe A: (Colon) est utilisée, avant que les attributs et les définitions de fonction liés à la classe ne soient désignés ci-dessous. Notre classeBesoin d'un ID que nous pouvons utiliser pour le récupérer et le garder unique, et il a également besoin d'un champ de texte pour stocker certaines informations. Plus tard, nous pouvons ajouter un horodatage, des fichiers, des booléens (des définitions vraies ou fausses qui peuvent aider notre code à prendre des décisions sur ce qu'il faut faire avec le modèle, et peut être utilisé pour le trier), une instance pour lier le modèle à un utilisateur enregistré dans le serveur, et plus encore. Décomposons le code

from django.db import models # L'importation utilisée pour définir notre classe et ses attributs

class Post(models.Model): # La définition de notre classe elle-même
    id = models.AutoField(primary_key=True) # L'ID de notre modèle, une clé générée automatiquement qui nous permettra d'interroger le modèle, de le garder unique et est utile lorsque nous devons interagir avec le modèle une fois qu'il a été créé.
    text = models.TextField(default='') # L'attribut que notre classe stocke, dans ce cas, du texte, défaut en une chaîne vide.
Fermez et enregistrez le fichier comme nous l'avons fait auparavant pour terminer. Il existe de nombreux autres champs et options que nous explorerons lorsque nous mettons à jour cette classe à mesure que notre application évolue, mais il s'agit des nécessités de base de la création d'une application pour publier du texte. Cependant, ce modèle ne fonctionnera pas seul. Comme décrit précédemment, nous aurons besoin d'une vue personnalisée et d'un modèle d'URL personnalisé pour faire fonctionner ce modèle, et nous aurons également besoin d'un formulaire avec un modèle. Explorons d'abord le formulaire. Pour définir un formulaire, modifiez l'application / forms.py avec nano et ajoutez les lignes suivantes. Nous aurons besoin de deux importations, de notre classe de formulaires, ainsi que du modèle que nous avons créé (feed.models.post), une définition de classe similaire au modèle et un champ ainsi qu'une sous-classe appelée méta qui définira le modèle. La forme interagit avec. Le formulaire peut également avoir une fonction d'initialisation qui les définit en fonction des informations dans la demande, le modèle ou autrement, nous l'explorerons plus tard. Les formulaires de modèle sont si utiles car ils peuvent créer un modèle ou modifier également un modèle,Nous les utiliserons donc pour les deux. Définissons-en un dans des formulaires.py

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',)
Ce sont les bases de ce à quoi ressemble une forme et un modèle. Ce formulaire de modèle peut être utilisé pour instancier ou modifier un post, modifiant le texte qu'il contient. Nous chercherons à intégrer ce formulaire dans une vue ensuite. Tout d'abord, faisons les migrations et migrons la base de données afin que notre code puisse interagir avec le modèle lorsqu'il s'exécute. Pour ce faire, exécutez les commandes suivantes:

python manage.py makemigrations
python manage.py migrate
Cela prendra une minute à exécuter, mais une fois que cela le fera, cela vous permettra d'accéder au modèle dans les vues, au middleware ou ailleurs dans le logiciel. Continuons en donnant une vue où nous pouvons voir notre modèle. Modifiez Feed / Views.py et ajoutez le code suivant, comme indiqué. Vous n'aurez pas besoin d'ajouter quoi que ce soit après le signe #, ce code est des commentaires utilisés pour désigner des informations sur le code. Nous allons commencer par importer notre modèle dans les vues et l'ajouter à un contexte où nous pouvons le rendre dans un modèle en tant que liste d'affichage. Ensuite, nous ajouterons un modèle où nous pouvons rendre le formulaire et le modèle avec un bouton pour créer un nouvel objet basé sur le modèle et le publier sur le serveur. Cela semble compliqué, alors faisons-le étape par étape. Avant de terminer la vue, créons un modèle qui ne fait que rendre le modèle et assurez-vous que nous pouvons le voir en créant un nouveau message dans le shell. Voici à quoi devrait ressembler cette vue:

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

def feed(request):
    posts = Post.objects.all() # Interrogez tous les messages de la base de données jusqu'à présent
    return render(request, 'feed/feed.html', {
        'posts': posts,
    })
Tout cela semble assez simple jusqu'à ce que nous arrivions au fond. Rendu, la valeur renvoyée par la fonction plutôt que dans une réponse HTTP comme l'exemple précédent, prend toujours une demande comme première entrée, accepte un contexte (dans ce cas, les messages de la base de données), qui peuvent maintenant être rendus dans le modèle et renvoie le modèle défini dans la fonction. Le modèle va être un document HTML avec un peu de langue appelée Jinja2, qui rend les informations Python dans le HTML. Pour commencer à créer des modèles, faites deux répertoires en flux.

mkdir feed/templates
mkdir feed/templates/feed
Ensuite, modifiez un modèle dans le répertoire ci-dessus, Feed / Modèles / Feed et ajoutez le code pour cet exemple. Regardons le modèle pour cet exemple.
 
<!doctype HTML>
<html>
<body>
<legend>Feed</legend>
<hr>
{% for post in posts %}
<p>{{ post.text }}</p>
{% endfor %}
</body>
</html>
 
Il s'agit d'un modèle très simple. Il définit l'ouverture et la fermeture des balises HTML, une balise de type de document, une balise corporelle avec un titre de légende, une balise de rupture qui ajoute une petite ligne à travers l'écran et une boucle pour la boucle qui rend chaque publication dans la liste des messages en tant que paragraphe dans le modèle. C'est tout ce qu'il faut pour rendre les publications, mais il n'y en a pas encore dans la base de données. Créons-nous avec la coquille. Nous pouvons exécuter le shell avec manage.py

python manage.py shell
Maintenant, importons notre modèle de poste

from feed.models import Post
Ensuite, nous créerons un article simple avec une chaîne et quitterons le shell. La chaîne peut être n'importe quoi, aussi longtemps que c'est un texte valide.

Post.objects.create(text='hello world')
exit()
Enfin, nous devrons ajouter un motif d'URL à notre flux. Parce que notre application Feed utilisera plusieurs URL et que nous voulons garder les tailles de fichiers petites, créons une URL.py locale dans notre application Feed qui ressemble à ceci:

from django.urls import path
from . import views

urlpatterns = [
    path('', views.feed, name='feed'),
]
Nous aurons également besoin de modifier les URL.py dans l'application de base, quoi que nous ayons décidé de l'appeler, c'était le premier répertoire que nous avons créé. Modifier app / app.py et ajouter ce qui suit aux modèles d'URL

from django.urls import include # en haut

urlpatterns = [
    # ... code précédent ici
    path('feed/', include(('feed.urls'), namespace='feed')),
]
Maintenant, lorsque nous exécutons le serveur avec Python Manage.py RunServer, nous verrons la page que nous avons créée car nous avons le modèle, la vue et le modèle ainsi que le modèle URL, ainsi que les éléments de la base de données. Ensuite, mettons en œuvre le formulaire que nous avons créé et commençons à créer nos propres articles. Mais avant d'écrire trop de code, faisons une sauvegarde en utilisant le script que nous avons écrit plus tôt, sauvegarde. Exécutez ce script dans le shell, attendez quelques instants et tout le code sera sauvegardé dans notre référentiel GIT.

backup
La mise en œuvre du formulaire est relativement simple. Nous importerons notre formulaire, ajouterons un gestionnaire de requête de post à la vue et enregistrerons le message dans la base de données avant de rediriger vers la même vue. Nous pouvons utiliser la fonction de redirection que nous avons déjà importée, et une autre fonction appelée Reverse pour obtenir l'URL du modèle de vue. Nous interrogerons cela avec la chaîne «Feed: Feed» parce que l'espace de noms du motif inclus est un flux, et la vue est également appelée alimentation.

from feed.forms import PostForm

def feed(request):
    posts = Post.objects.all() # Interrogez tous les messages de la base de données jusqu'à présent
    if request.method == 'POST': # Gérer la demande de poste
        form = PostForm(request.POST) # Créer une instance du formulaire et enregistrer les données
        if form.is_valid(): # Valider le formulaire
            form.save() # Enregistrer le nouvel objet
        return redirect(reverse('feed:feed')) # Rediriger vers la même URL avec une demande de GET
    return render(request, 'feed/feed.html', {
        'form': PostForm(), # Assurez-vous de passer le formulaire dans le contexte afin que nous puissions le rendre.
        'posts': posts,
    })
Maintenant, nous devons mettre à jour le modèle pour tenir compte du nouveau formulaire. Nous pouvons le faire en utilisant le
Tag dans HTML et rendant le formulaire dans le modèle HTML avec un bouton Soumettre. Nous aurons également besoin d'un jeton CSRF, un jeton qui empêche les sites externes de publier au formulaire sans charger d'abord une page.
 
<!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>
 
Décomposons cela. Il y a une nouvelle classe de formulaire, un jeton, le formulaire lui-même et un bouton de soumission. Assez simple, mais quand nous y regardons, nous voulons peut-être que cela soit meilleur. Cela fonctionne, nous pouvons publier de nouveaux articles avec le formulaire et ils sont maintenant enregistrés dans la base de données. Il se passe quelques choses ici. Nous utilisons des balises HTML pour déclarer que le document est un document HTML, nous utilisons une balise de modèle ({%…%}) pour rendre le jeton pour le formulaire, et un autre, {{…}} pour rendre le formulaire. Nous avons également une boucle pour rendre le texte à l'aide de balises de bloc et une balise de modèle. Les balises de bloc sont vraiment importantes car nous pouvons définir comment les sections du modèle sont rendues avec eux, et les balises de modèle sont la base de la façon dont nous mettons les variables dans notre code. Maintenant, nous devons améliorer notre application, car pour l'instant, il a l'air vraiment basique. Nous pouvons le faire en utilisant CSS, soit en ligne ou dans des classes liées à chaque objet dans le document. CSS est vraiment sympa car il dit tout sur la page à quoi il devrait ressembler,et peut le rendre vraiment beau. Il y a quelques bibliothèques qui peuvent le faire, mais mon retour personnel est bootstrap. Bootstrap peut être téléchargé à partir de leur site Web,Getbootstrap.com/. Une fois sur place, appuyez sur le bouton pour lire les documents d'installation et copiez le code de la section Inclure via CDN. Vous aurez besoin de ce code en haut de votre document HTML, dans une étiquette appelée Head. Allons également et créons un modèle de base afin que nous n'ayons pas besoin de recréer ces liens dans chaque modèle. Faites un nouveau répertoire appelé modèles avec des modèles MKDIR, puis modifiez des modèles / base.html. Cela devrait ressembler à ceci:
 
<!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>
 
Assurez-vous de copier les fichiers CSS et JavaScript, les fichiers .css et .js, car nous aurons besoin du javascript pour rendre notre site plus fonctionnel à l'avenir. Maintenant, revenons à la coquille de bash et exécutons une commande rapide. N'oubliez pas que si vous avez besoin d'accéder à l'environnement virtuel, tapez Source Venv / bin / Activate. Cela vous permettra d'installer localement des packages Python d'une manière qui permet à Django d'y accéder. Pour donner nos formulaires générés par les classes Bootstrap Django, nous utiliserons un package Python appelé Crispy Forms. Nous pouvons télécharger ceci avec la commande suivante

pip install django-crispy-forms
Une fois ceci installé, ajoutez-le aux paramètres.py

INSTALLED_APPS = [
    # … Code précédent ici
    'crispy_forms',
]
Maintenant, de retour dans notre modèle d'alimentation, nous pouvons supprimer certaines choses. Supprimons le début et la fin du document et le remplaçant par l'héritage de notre modèle de base, en utilisant les étendues et la définition du bloc. De plus, nous ajouterons une importation de filtre de modèle avec chargement et un filtre de modèle au formulaire. Enfin, ajoutons une classe bootstrap au bouton du formulaire pour le faire ressembler davantage à un bouton. Cela devrait ressembler à ceci:
 
{% 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 %}
 
Beau! C'est déjà un peu de code. Ensuite, nous devons le tester et nous assurer que nous pouvons voir que tout a l'air bien, et assurez-vous également que tout fonctionne correctement. Exécutez le serveur selon les instructions précédentes et assurez-vous que le site ressemble et fonctionne bien. Excellent travail! Vous êtes prêt à passer à l'étape suivante, dans laquelle nous ajouterons des fonctionnalités de connexion utilisateur en utilisant des URL, des formulaires, des vues et des modèles similaires. Le modèle de base est important, et nous continuerons à le modifier et à apporter des modifications au besoin, mais pour l'instant, concentrons-nous sur la création de notre site plus sécurisé, en permettant aux utilisateurs de se connecter avec un nom d'utilisateur et un code d'accès, et finalement des informations encore plus importantes qui Aidera à garder votre application en sécurité et votre propre compte accessible uniquement par vous. Pour ce faire, nous devrons utiliser le modèle utilisateur intégré à Django. Le modèle d'utilisateur est un modèle de base de données, comme notre message, qui peut être rendu pour enregistrer un utilisateur sur le site Web. À l'avenir, avant de déployer le site sur Internet, nousÉtendez ce modèle avec d'autres modèles qui y sont attribués et créez des mesures de sécurité supplémentaires pour la connexion qui résiste au phishing. Nous commencerons par utiliser des formulaires de connexion intégrés fournis par Django. Tout d'abord, créons une nouvelle application que nous utiliserons pour rendre les modèles et les vues pour la page de connexion de base. Nous créerons également d'autres applications pour représenter les défis de connexion continus afin de sécuriser l'application, y compris un Pincode, une reconnaissance faciale, une communication sur le terrain, des dispositifs externes, une authentification multi-facteurs et une reconnaissance d'empreinte digitale. Nous avons déjà parlé du démarrage d'une application. De notre répertoire, à l'intérieur de l'environnement virtuel, pass

python manage.py startapp users
Maintenant, nous devrions avoir un répertoire pour la nouvelle application. Commençons par créer une vue dans ce répertoire qui correspond à la connexion de l'utilisateur. Django a intégré des vues pour les connexions des utilisateurs, mais celles-ci ne vous conviendront pas car nous avons besoin d'une vue personnalisée, ce qui est de préférence effectué avec une définition. Dans cette vue, nous commencerons par vérifier une demande de poste, passer la demande.Post à une connexion importée de Django, authentifier le compte utilisateur et vous connecter à l'utilisateur avant de les rediriger vers notre application Feed. Dans les utilisateurs / vues.py, ajoutez le code suivant

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'] # Obtenez le nom d'utilisateur et le mot de passe de la demande de poste
        password = request.POST['password'] # Authentifier l'utilisateur
        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()})
C'est tout ce dont vous avez besoin pour une vue de connexion de base. Maintenant, créons un formulaire pour la vue en étendant le modèle de base. Nous commencerons par créer un nouveau répertoire pour les modèles dans le dossier des utilisateurs.

mkdir users/templates
mkdir users/templates/users
Maintenant, nous devrions être en mesure de modifier les utilisateurs / modèles / utilisateurs / login.html. Pendant que nous y sommes, nous créerons un modèle pour permettre à l'utilisateur de s'inscrire également.

nano users/templates/users/login.html
Maintenant, dans le modèle,
 
{% 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 %}
 
Ce sont les bases d'un modèle de connexion. C'est vraiment comme l'autre modèle de structure, mais il a l'air un peu différent lorsqu'il est rendu. Nous pouvons copier ce code pour créer un autre modèle très similaire appelé registre.html, où nous modifierons le libellé et utiliserons un nouveau formulaire que nous créons. Faisons d'abord le modèle. Modifier les utilisateurs / modèles / utilisateurs / registre.html et ajouter le code suivant:
 
{% 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 %}
 
Maintenant, créons un formulaire pour notre enregistrement d'utilisateur et revinons aux vues avant de mettre à niveau les connexions de nos utilisateurs avec un modèle. Nous allons faire ce formulaire de base pour commencer, mais incorporer plus de détails et de fonctionnalités de sécurité telles que les accords et le captcha à l'avenir. Modifiez les formulaires avec Nano Users / Forms.py et ajoutez le code suivant.

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']
Nous avons donc une autre forme ici, qui fonctionne assez simplement. Il s'agit d'un formulaire de registre d'utilisateurs avec un nom d'utilisateur, un e-mail et un mot de passe, ainsi qu'un champ de mot de passe de confirmation. Notez que ce formulaire n'étend pas la classe Forms. Un champ est défini de la même manière, et la méta de classe définit le modèle que le formulaire correspond au reste des informations qui seront écrites au formulaire. La majeure partie de cela existe déjà dans Django intégré à UserCreationForm de Django, nous allons donc l'utiliser comme base de la classe (passé dans la parenthèse). Ensuite, nous examinerons la vue pour enregistrer un utilisateur, maintenant que nous avons un formulaire et un modèle. Ceci est un model, tout comme celui de la nouvelle vue de publication. Modifier les utilisateurs / vues.py et ajouter le code suivant:

# … Des quantités
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})
C'est tout ce dont nous avons besoin pour enregistrer un utilisateur, mais nous devons avoir plus d'informations. Nous voulons connaître l'heure à laquelle l'utilisateur s'est inscrit, à quelle heure il était le dernier sur le site, quelques informations à leur sujet, comme une biographie, un fuseau horaire, etc. De plus, nous devrons mettre à jour notre modèle de flux, publier, pour tenir compte de l'utilisateur Modèle et attribut des publications à chaque utilisateur. Pour ce faire, nous mettrons à jour les modèles.py dans les deux applications. Commençons par modifier le modèle de flux. Cela devrait ressembler à ça maintenant:

from django.db import models # … Des quantités
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') # Ajouter cette ligne
    text = models.TextField(default='')
Faites attention à la deuxième ligne qui a été ajoutée au fichier. Il s'agit d'une clé étrangère, qui attribuera chaque message à un seul utilisateur par message, nous pouvons donc nous assurer que nous enregistrons les messages sur une base utilisateur par utilisateur et aucun message ne peut être fait sans l'attribuer à un utilisateur. Nous définissons cette clé étrangère avec la classe qu'il représente, un argument de suppression pour s'assurer que les publications sont supprimées avec les utilisateurs, les arguments nuls et vierges pour nous assurer que nous pouvons supprimer l'utilisateur si nécessaire, et pour s'adapter à l'absence d'un utilisateur sur les messages que nous créé, et un nom connexe, que nous pouvons utiliser pour référer aux objets de publication que l'utilisateur crée. Ce nom connexe, contrairement à Post.Author, l'auteur du Post, nous donne à l'utilisateur qui a publié le message lui-même. Nous pouvons maintenant obtenir les publications faites par un utilisateur en exécutant user.pos.all (), ou auteur.post.all (). Maintenant, rendons nos connexions plus résilientes. Nous pouvons déjà rendre notre site beaucoup moins vulnérable au phishing en limitant simplement le nombre de fois que nous autoriserons une connexion à laSite, c'est assez facile. Commençons également à stocker quelques informations sur chaque utilisateur auparavant alors que nous continuons à développer notre application. Édition d'utilisateurs / modèles.py, ajouter ce qui suit

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='')
Notez que ce modèle est assez similaire au modèle de post. Nous avons une importation supplémentaire, le fuseau horaire, qui nous permettra de définir les défauts par défaut sur les champs DateTime, et nous avons également un personnageFeild et TextField comme le post. L'utilisation de tous ces horodatages nous aide à sécuriser le site et à comprendre son utilisation, et les champs de texte nous permettent de rendre des informations sur chaque utilisateur ou auteur sur le site Web. Le OnetoOnefield devrait être la seule considération mineure, elle se comporte exactement de la même manière qu'une Foreginkey mais avec un seul par modèle suivant. De cette façon, l'utilisateur n'a qu'un seul profil, alors qu'il peut avoir de nombreux messages. Maintenant, améliorons nos vues de connexion et enregistrons pour tenir compte du profil. Tout d'abord, modifiez les utilisateurs / vues.py et concentrez-vous sur le registre View:

# … Des quantités
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) # Assurez-vous d'ajouter cette ligne, pour créer un profil pour l'utilisateur
            messages.success(request, 'Welcome to the app, {}.'.format(user.username))
    return render(request, 'users/register.html', {'form': UserRegisterForm})
Cela crée simplement un profil pour l'utilisateur, sans remplir aucune information. Maintenant, nous voulons nous assurer que le compte d'utilisateur ne peut pas être connecté trop souvent, ou au moins les mots de passe ne peuvent pas être essayés trop souvent, alors mettons à jour la vue de connexion.

# … Des quantités
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(): # Notez que nous vérifions maintenant si l'utilisateur peut se connecter
            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: # Si la connexion n'a pas réussi,
            messages.warning(request, 'Username or password incorrect. Please try again.')
            user = User.objects.filter(username=username).first() # C'est la partie où nous mettons à jour le profil des utilisateurs
            if user: 
                profile = user.profile
                profile.can_login = timezone.now() + datetime.timedelta(seconds=15) # Alors ils ne peuvent pas se connecter à nouveau pendant quelques secondes
                profile.save()
    return render(request, 'users/login.html', {'form': AuthenticationForm()})
C'est le fondamental de base de la sécurité. Assurez-vous que le site n'est pas vulnérable à quelqu'un qui essaie simplement toutes les combinaisons de mots de passe possible, ou même quelques-unes en même temps. Cela ne sera pas frustrant pour l'utilisateur ordinaire qui connaît son mot de passe et se connecte simplement sur quelques appareils, mais il gardera de nombreux robots de phishing hors de l'application. Notez que nous avons ajouté une instruction IF avec une variable, Can_Login, qui devrait être un moment dans le passé, et la mettez à jour à chaque connexion infructueuse en utilisant le même nom d'utilisateur. De cette façon, un utilisateur malveillant ne pourra pas deviner un mot de passe aussi rapidement. Le nombre de secondes dans le DateTime.Timelta () peut également être mis à jour, et le site Web sera plus résilient mais légèrement moins utilisable avec plus de secondes. Je recommande 15 pour commencer. N'oubliez pas que nous avons construit un script de sauvegarde pour sauver notre travail, alors allons-y et soutenons ce que nous avons jusqu'à présent pour nous assurer que tout a enregistré. Exécutez la commande:

sudo backup
Encore une fois, cela sauvera votre travail jusqu'à présent. Je recommande d'exécuter des sauvegardes fréquentes pour économiser votre travail, et vous pourriez même vouloir exécuter un travail de sauvegarde automatiquement. Vous pouvez le faire en utilisant un utilitaire UNIX appelé Cron. Pour activer cet utilitaire, exécutez la commande suivante et entrez votre mot de passe:

sudo crontab -e
Si vous n'avez pas déjà sélectionné l'option 1 pour Nano, l'éditeur de texte que vous devez déjà être familier et faites défiler vers le bas du fichier à l'aide des touches Arrow. Ajouter la ligne suivante:

0 * * * * sudo backup
Cron utilise le format minute, heure, jour de mois, mois, jour de semaine, où un * ou un nombre représente quand exécuter la commande. En utilisant un 0 pour la minute et * pour le reste des options, nous pouvons exécuter une commande à la première minute de chaque heure au début de la minute. Cela nous permet de sauvegarder automatiquement le code. Tous les travaux de Cron lorsqu'ils sont exécutés avec sudo s'exécutent en tant que racine, nous n'aurons donc pas besoin de saisir un mot de passe toutes les heures. Pour faciliter la sauvegarde de notre code sans utiliser de mot de passe, désactivons le mot de passe de notre commande de sauvegarde. Nous le ferons en exécutant la commande suivante et en entrant un mot de passe:

sudo visudo
Maintenant, faisons défiler vers le bas du fichier et ajoutons une autre ligne:

ALL ALL=NOPASSWD: /bin/backup
Cela nous permet d'exécuter la commande «Sauvegarde» comme n'importe quel utilisateur, sans mot de passe. Le format pour ceci est facile, préfixez simplement la ligne avec «all = nopasswd: / bin /» et finissez par la commande, par exemple / bin / sauvegarde, qui existe dans / usr / bin /. Maintenant, commençons à travailler avec un e-mail. Le courrier électronique est vraiment important pour les sites Web, car c'est un moyen de garder un site Web plus sécurisé, de vérifier que les utilisateurs sont de vraies personnes et même de commercialiser des produits ou des services aux clients. Beaucoup de gens qui fréquentent Internet consultent leurs e-mails quotidiennement et reçoivent toutes sortes d'e-mails marketing sur les produits et services qui les intéressent. Il y a quelques options lorsqu'il s'agit d'activer le courrier électronique sur un site Web de Django, et vous êtes invité à choisir Ce qui vous convient le mieux. Tout d'abord, vous pouvez payer un service de messagerie qui vous permettra d'envoyer un e-mail à partir de votre domaine et nécessite un code minimal. Il existe de nombreux services qui offrent cela, comme Google Workspace, SendinBlue, Mailgun, etc. Sinon, vous êtes bien en train de construireVotre propre service de messagerie dans votre serveur à partir de zéro. Je recommande cette option, même si elle est plus de code et peut nécessiter un hébergement spécial. Vous ne pourrez pas démarrer un serveur de messagerie à partir de votre ordinateur personnel, alors allons-y et examinons la configuration et le code pour envoyer un e-mail avant de démarrer un serveur dans le cloud et créer notre propre serveur de messagerie à l'intérieur. Tout d'abord, modifiez les paramètres avec les éléments suivants

nano app/settings.py
Où l'application est le nom de l'application que vous avez créée avec StartApp. Ajouter les lignes suivantes:

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)
Assurez-vous de les changer lorsque vous êtes prêt à déployer votre application, nous reviendrons cela plus tard. Le paramètre email_address devrait être l'e-mail que vous souhaitez envoyer, et le mot de passe (email_host_password) doit être défini sur le mot de passe que vous générez pour le serveur. Je charge le mot de passe à partir d'un fichier de configuration pour le garder hors du code en utilisant la logique suivante, au-dessus de ces lignes dans Settings.py:

import os
import json
with open('/etc/config.json') as config_file:
    config = json.load(config_file)
Ensuite, j'ai configuré un fichier JSON avec la configuration dans /etc/config.json en utilisant Nano comme suit. Pour modifier le fichier:

sudo nano /etc/config.json
Ajouter les lignes suivantes:

{
	“EMAIL_HOST_PASSWORD”: “<some password here>”
}
Nous continuerons à modifier le fichier de configuration et à ajouter tous les mots de passe et les clés que nous utiliserons dans l'application. Pour l'instant, examinons rapidement comment envoyer des e-mails à l'aide de Python. Tout d'abord, créons un modèle pour un e-mail de vérification que nous pouvons envoyer à nos utilisateurs et le mettre dans le répertoire des modèles d'utilisateur. Ce modèle sera écrit en 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>
 
Cet e-mail est assez simple. Il prend un contexte d'un utilisateur, de l'URL de base pour le site et d'un ID utilisateur et d'un jeton qui sont utilisés pour vérifier l'e-mail de l'utilisateur. Assurez-vous de définir l'URL de base dans Settings.py avant d'écrire un code Python pour rendre le modèle. Allez-y et ajoutez les lignes suivantes à App / Settings.py, vers le début.

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

BASE_URL = PROTOCOL + '://' + DOMAIN
Finalement, lorsque votre site est prêt pour Internet et que vous le déployez, vous voudrez définir votre domaine comme le nom de domaine que vous achetez pour représenter le site. C'est le nom que vous taperez dans la barre de navigation pour accéder à votre site. Pour l'instant, vous pouvez laisser le domaine vide ou utiliser un espace réservé. Vous voudrez également modifier le site_name en un nom que vous souhaitez donner à votre site, de votre choix. Avant d'envoyer un e-mail, créons un générateur de jeton afin que nous puissions avoir un jeton d'activation de compte qui ne s'expire jamais. Nous pouvons le faire en construisant et en important un jeton d'activation de compte qui ressemble à ce qui suit. Modifiez le fichier:

nano users/tokens.py
Ajouter le code suivant:

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()
Ce générateur de jeton de base génère un jeton Nous pouvons envoyer l'utilisateur dans une URL et l'utilisateur peut utiliser pour vérifier son e-mail et activer son compte. Ensuite, voyons comment envoyer un e-mail. En utilisant Nano, modifiez les utilisateurs / e-mail.py.

nano users/email.py
L'envoi de l'e-mail HTML de vérification ressemblera à ceci:

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)
C'est assez simple. Nous importons les fonctions dont nous avons besoin pour envoyer l'e-mail, rendre l'e-mail avec des modèles et nos paramètres, puis nous définissons l'e-mail par le nom du modèle et l'envoyons à l'utilisateur à l'aide d'une fonction. Vous remarquerez que nous n'avons pas encore défini la fonction pour envoyer le courrier, send_html_email, alors écrivons ceci sous le code que nous avons déjà ajouté aux utilisateurs / e-mail.py

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()
C'est un peu plus complexe, et nous ne sommes pas encore prêts à exécuter tout ce code. Remarquez que nous définissons un unsan_link, le lien que l'utilisateur peut utiliser pour se désabonner de nos e-mails. Ceci est important, car les utilisateurs devront être en mesure de se retirer de nos e-mails à moins qu'ils ne souhaitent les voir, à tout moment. Nous ajoutons également une alternative de texte à notre message, qui est le message HTML dépouillé des balises HTML. Enfin, nous vérifions si l'e-mail a envoyé, et si ce n'était pas le cas, nous marquons dans le profil de l'utilisateur que leur e-mail n'est pas valide. Revenons aux modèles d'utilisateurs afin que nous puissions que tout cela fonctionne. Nous devons définir une fonction pour générer un lien vers la désabonnement et définir un champ booléen pour marquer que l'e-mail de l'utilisateur n'est pas valide. Tout d'abord, ajoutez les importations suivantes en haut des utilisateurs / modèles.py

nano users/models.py

# …
from django.core.signing import TimestampSigner, BadSignature, SignatureExpired
from django.urls import reverse
Ensuite, ajoutons des fonctions au modèle utilisateur pour faire le jeton et vérifiez le jeton utilisé pour activer l'e-mail, ainsi que le champ pour enregistrer si l'utilisateur reçoit avec succès son courrier. Dans Users / Models.py à nouveau, ajoutez le code suivant à la fin du modèle (code en retrait)

# …
    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) # Valable pendant 30 jours
        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,})
Ceci est assez simple, nous utilisons un horodatsignor, qui est un outil de cryptographie de base, pour créer un jeton qui expirera après un certain temps, et nous utilisons également une autre fonction pour vérifier si elle est valide. Nous utilisons ces jetons deux fois, une fois pour vérifier l'e-mail et une fois pour un lien de désabonnement. Maintenant que nous en avons, le dernier travail que nous devrons faire est dans les vues. Dans les utilisateurs / vues.py, ajoutons des vues pour vérifier l'adresse e-mail et pour vous désabonner.

nano users/views.py
Tout d'abord, ajoutez les importations suivantes. J'ai lancé quelques extra afin que nous n'aurons plus à importer plus d'articles plus tard.

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 # Assurez-vous d'importer la fonction d'envoi d'e-mail de vérification
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
Vous avez peut-être déjà certaines de ces importations, mais cela ne fait pas de mal de les répéter. Vous devrez importer la fonction d'envoi d'e-mail de vérification, ainsi que account_activation_token de users.tokens, entre autres importations. Maintenant, au bas du fichier, ajoutez le code suivant:

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)):
        # les désinscrire
        profile = user.profile
        profile.subscribed = False
        profile.save()
        return render(request, 'users/unsubscribe.html')
    # Sinon rediriger vers la page de connexion
    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(request, user)
        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})
C'est beaucoup de code. Décomposons-le. La première fonction, propre et simple, désabonne l'utilisateur à partir de la liste de diffusion. La deuxième fonction active leur e-mail, et vous remarquerez que j'ai ajouté une fonction commentée, SendWelcomeMail. Vous êtes invités à utiliser un modèle de messagerie et une définition de fonction pour envoyer un e-mail de bienvenue, je ne l'ai pas encore. La dernière fonction que j'ai présentée est importante, car les e-mails d'activation expirent. Par conséquent, nous devrons renvoyer l'e-mail d'activation une partie du temps. Nous pouvons utiliser un formulaire de base pour cela et appeler la fonction pour envoyer l'e-mail de vérification. Avant de le faire, assurez-vous qu'il est envoyé en premier lieu, en ajoutant un appel de fonction à la vue du registre. Ajoutez cette ligne juste avant la redirection dans la vue du registre, DEF Register, dans les utilisateurs / vues.py.

nano users/views.py

# … (After) Def Register (demande):
            send_verification_email(user)
# … (Avant) Rediriger (
Vous n'avez pas besoin d'ajouter les premières et dernières lignes de cet extrait de code, assurez-vous simplement que la vue du registre envoie l'e-mail de vérification à l'utilisateur. Cela devrait ressembler à ceci:

# … Des quantités
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) # Assurez-vous d'ajouter cette ligne!
            messages.success(request, 'Welcome to the app, {}.'.format(user.username))
    return render(request, 'users/register.html', {'form': UserRegisterForm})
Maintenant, nous devrons ajouter un formulaire pour renvoyer l'e-mail d'activation. Dans les utilisateurs / forms.py, ajoutez le formulaire suivant:

# … (Montants)
class ResendActivationEmailForm(forms.Form):
    email = forms.EmailField(required=True)
Nous aurons également besoin d'un modèle correspondant à ce formulaire d'activation par e-mail de renvoi. Ajoutons ce modèle. Modifiez le fichier:

nano users/templates/users/resend_activation.html
Ensuite, ajoutez le code suivant au fichier.

{% 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 %}
Ouf, c'est beaucoup! Maintenant, lorsque nous déploierons le code sur notre serveur, nous pourrons envoyer un e-mail HTML et activer les comptes d'utilisateurs en cliquant dans l'e-mail. Nous pourrions également vouloir envoyer un e-mail de bienvenue simple, alors voyons comment le faire. Retour dans utilisateurs / e-mail.py, ajoutez le code suivant:

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)
De plus, nous aurons besoin d'un modèle pour rendre toutes ces informations. Sur mon site Web, le modèle ressemble à ce qui précède, mais vous êtes invités à le formater comme vous le souhaitez.
 
<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>
 
Notez que nous n'avons pas de balises de fermeture ou de HTML de fermeture, car nous les ajoutons lorsque nous ajoutons le lien HTML Désubuscribe. Ce sont importants, mais nous ne voulons pas les définir deux fois. Alors, quelle est la prochaine étape? Nous avons parcouru un long chemin. Vraiment, nous devrions être prêts à déployer le site sur un serveur. Nous pouvons ajouter le décorateur @login_requered et rendre nos vues sécurisées, prendre des inscriptions des utilisateurs, envoyer des e-mails conformes et le cache, ce qui est la base de ce qu'un site Web doit faire pour rester pertinent. Nous allons ajouter quelques fonctionnalités plus utiles, puis créer une base pour déployer notre code sur un serveur distant, configurer un serveur de messagerie, une configuration de domaine et des filtres pour rendre notre site sécurisé et approprié. Nous aurons également besoin d'une vue de réinitialisation de mot de passe, alors ajoutons cela très rapidement. La vue de réinitialisation du mot de passe intégrée de Django est rompue dans certaines fonctions, mais nous examinerons comment rédiger notre propre vue, modèle de messagerie, formulaires et modèles d'URL. Voici à quoi ressemble la vue, dans les utilisateurs / vues.py

# ... montants
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)
Ce formulaire est intégré à Django, mais nous aurons besoin d'un modèle pour confirmer la réinitialisation du mot de passe, utilisateurs / modèles / utilisateurs / mot de passe_reset_confirm.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 %}
 
Nous avons également un modèle pour envoyer un e-mail de réinitialisation de mot de passe, avec un formulaire simple, dans les utilisateurs / modèles / utilisateurs / mot de passe_reset.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 %}
 
Le modèle de l'e-mail lui-même est simple, il s'agit d'un fichier HTML de base rendant un lien pour réinitialiser le mot de passe, dans les utilisateurs / modèles / utilisateurs / mot de passe_reset_email.html. Django interprétera automatiquement ce fichier.
 
<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>
 
Nous aurons également besoin de deux modèles supplémentaires. La première consiste à confirmer que l'e-mail a été envoyé. Les vues pour celles-ci sont déjà à Django, nous avons donc juste besoin de les aborder dans les urls.py. Ce modèle est situé aux utilisateurs / modèles / utilisateurs / mot de passe_reset_done.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 %}
 
Et enfin, pour confirmer que la réinitialisation du mot de passe est terminée, les utilisateurs / modèles / utilisateurs / mot de passe_reset_compte.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 %}
 
Maintenant, nous avons besoin de modèles d'URL pour ces vues. Dans les utilisateurs / urls.py, ajoutez les modèles d'URL suivants:

urlpatterns = [
    # ... URL précédentes ici
    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'),
]
Quatre modèles, c'est beaucoup! Mais maintenant, nous pouvons être en mesure de réinitialiser le mot de passe de l'utilisateur à tout moment, le tout à partir du navigateur Web. Je comprends que c'est beaucoup de code. Si cela semble un peu au-dessus de votre tête, ça va. Vous vous améliorerez, votre compréhension s'améliorera et vous deviendrez beaucoup plus compétent avec le code très bientôt. Si vous êtes totalement perdu, je vous recommande de revenir sur ce logiciel plus tard après avoir travaillé sur un cours de codé à coder en ligne. Ceux-ci sont généralement gratuits pour commencer et vous guideront à travers tout ce dont vous avez besoin pour réussir à votre retour à ce projet. Si vous vous sentez prêt à continuer, lisez la suite, ensuite, nous couvrirons le déploiement de votre code sur un serveur distant et la configuration d'un serveur de messagerie, ainsi que l'automatisation de votre déploiement à l'aide de bash afin que vous puissiez toujours configurer un nouveau projet avec quelques commandes simples. La dernière chose que nous devons faire avant de se déployer sur un serveur distant est de rendre notre site un peu plus sécurisé. TuNotez que la vue de connexion ne prend qu'un nom d'utilisateur et un mot de passe, et il n'y a pas d'authentification multi-facteurs ou de code unique. Il s'agit d'une correction facile, et avec le même code, nous pouvons faire envoyer notre site des messages texte et même être réactif aux messages texte envoyés au serveur. Pour commencer, nous reviendrons dans les modèles utilisateur et ajouterons un signataire de l'horodatage qui représentera chaque connexion. Nous ajouterons également un identifiant unique et rotatif au modèle utilisateur qui sera utilisé pour ajouter une sécurité supplémentaire à notre connexion. Modification des modèles d'utilisateurs, utilisateurs / modèles.py, ajoutez ce qui suit

from django.db import models
from django.contrib.auth.models import User
from django.utils import timezone
# Assurez-vous d'importer l'UUID, le signataire de l'horodatage et le générateur d'URL (inverse)
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='')
    # Ajoutez ce code ici
    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)

    # Et ajouter cette fonction
    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) # Valable pendant 3 minutes
        except (BadSignature, SignatureExpired):
            return False
        return True
Assurez-vous que vos utilisateurs / modèles.py ressemble à ceci, en plus des commentaires (code sur les lignes avec #). Faire tomber cela, c'est simple. Nous avons quelques importations, un horodat-indigneur qui est un utilitaire cryptographique qui peut générer un code sécurisé et le vérifier afin de s'assurer qu'il est valide, n'a été utilisé qu'une seule fois, et pas plus ancien qu'un certain nombre de secondes. Nous utilisons également un UUID, qui est un identifiant unique qui identifie notre utilisateur dans la signature du jeton et dans l'URL où le jeton est envoyé à l'utilisateur. Nous utiliserons cette cryptographie de base pour créer une vue d'authentification à deux facteurs. Avant de faire quoi que ce soit d'autre, exécutons les migrations afin que nos modèles d'utilisateurs soient mis à jour. Dans le répertoire avec Manage.py, exécutez les commandes suivantes pour effectuer et terminer les migrations.

source venv/bin/activate
python manage.py makemigrations && python manage.py migrate
Ceci est important car chaque fois que nous apportons des modifications aux modèles, nous devrons créer les tables et mettre à jour la base de données par défaut avant de pouvoir réellement utiliser les modèles. Ensuite, improvirons notre vue de connexion pour rediriger vers une vue d'authentification secondaire. Dans les utilisateurs / vues.py, supprimez la fonction de connexion et redirigez vers l'URL que nous venons de générer dans les modèles d'utilisateurs.

# … Des quantités

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(): # Notez que nous vérifions maintenant si l'utilisateur peut se connecter
            # Supprimez la fonction Auth_Login qui était ici
            messages.success(request, 'Your password was accepted. Please continue.')
            if user.profile.mfa_enabled:
                return redirect(user.profile.create_auth_url()) # Notez que nous redirigeons vers une nouvelle URL ici
            else: # Si l'utilisateur n'utilise pas d'authentification multi-facteurs, connectez-le simplement.
                auth_login(request, user, backend='django.contrib.auth.backends.ModelBackend')
                return redirect('feed:feed')
        else: # Si la connexion n'a pas réussi,
            messages.warning(request, 'Username or password incorrect. Please try again.')
            user = User.objects.filter(username=username).first() # C'est la partie où nous mettons à jour le profil des utilisateurs
            if user: 
                profile = user.profile
                profile.can_login = timezone.now() + datetime.timedelta(seconds=15) # Alors ils ne peuvent pas se connecter à nouveau pendant quelques secondes
                profile.save()
    return render(request, 'users/login.html', {'form': AuthenticationForm()})
C'est donc assez simple, nous avons maintenant un moyen de rediriger vers la vue d'authentification à deux facteurs lorsque nous la créons. Nous avons également une repli au cas où l'utilisateur n'aurait pas ajouté de numéro de téléphone. Nous ajouterons une vue de base pour ajouter un numéro de téléphone bientôt et nous connecter avec un SMS bientôt. Tout d'abord, nous avons besoin d'un moyen facile d'envoyer un SMS à partir de notre code. Pour ce faire, nous pouvons choisir parmi un certain nombre d'API, mais le plus facile à mon avis est Twilio. Ils offrent également de bons prix pour les petits projets, ainsi que des remises en vrac. Créez un compte sur twilio.com, remplissez quelques détails sur votre projet, achetez un numéro de téléphone et copiez vos clés API à votre paramètres. Ensuite, ajoutez ce code dans un nouveau fichier, utilisateurs / sms.py.

nano users/sms.py

# Importer tous les packages nécessaires
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

# Ce code envoie le texte avec 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())

# Une fonction d'assistance pour obtenir un numéro avec autant de chiffres
def get_num_length(num, length):
    n = ''
    for x in range(length):
        n = n + str(num)
    return int(n)

# Envoyer le texte pour vérifier l'utilisateur
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)))

# Envoyez à un utilisateur n'importe quel texte avec cette fonction
def send_user_text(user, text):
    send_text(user.profile.phone_number, text)

# Valider le code avec cette fonction
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

# Valider l'heure
def check_verification_time(user):
    result = user.profile.mfa_code_expires > timezone.now()
    return result
Assurez-vous de modifier vos paramètres de manière appropriée, en ajoutant ces lignes avec vos clés:

# Assurez-vous de les copier à partir de votre tableau de bord 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 # Le nombre de minutes La page TFA est active une fois instanciée
Tout d'abord, nous aurons besoin de formulaires pour nos deux vues d'authentification. Édition d'utilisateurs / forms.py, ajoutez le code suivant.

# … Des quantités
from django import forms

# Un formulaire pour entrer notre numéro de téléphone
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

# Une forme pour l'authentification
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.'
    }
Ensuite, créons les vues dans les utilisateurs / vues.py

# … Des quantités
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})
Nous aurons également besoin de modèles pour ces deux vues. Ajoutons d'abord le modèle MFA.

nano users/templates/users/mfa.html
Ajoutez ce code HTML au modèle
 
{% 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 %}
 
C'est assez explicite. Le formulaire envoie un code ou un code vide, et vous remarquerez dans la vue que nous envoyons le code si nous recevons un code vide. Ensuite, nous n'avons que deux boutons de soumission, et de cette façon, nous pouvons envoyer le code avec l'un ou l'autre bouton. Ensuite, nous allons ajouter un formulaire simple pour ajouter un numéro de téléphone.

nano users/templates/users/mfa_onboarding.html
Ajouter le HTML suivant:
 
{% 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 %}
 
Ce formulaire est beaucoup plus simple, il rend simplement le formulaire de numéro de téléphone que nous avons créé et permet à l'utilisateur d'ajouter un numéro de téléphone. Cela a l'air vraiment bien! Tant que tout est correctement configuré, nous devrions pouvoir envoyer des messages et enregistrer l'utilisateur avec leur numéro de téléphone dès que nous ajoutons les modèles d'URL. La dernière chose que nous devons configurer est une vue de profil afin que nous puissions nous assurer que l'utilisateur peut modifier son numéro de téléphone sans être connecté. De plus, nous voulons finalement ajouter une option «Arrêtez-vous pour quitter», afin que l'utilisateur puisse envoyer un SMS «Arrêtez» pour se retirer des futurs messages texte. Ajoutons une vue de profil aux utilisateurs / vues.py. Cette vue mettra à jour la biographie, le courrier électronique, le nom d'utilisateur et le numéro de téléphone de l'utilisateur, ainsi que nous permettre d'activer l'authentification multi-facteurs. Tout d'abord, nous aurons besoin de deux autres formulaires dans les utilisateurs / forms.py

# ... montants
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']
Ensuite, nous pouvons créer une vue pour utiliser ces deux formulaires. Modifiez les utilisateurs / vues.py et ajoutez la vue.

# Ajouter ces importations
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)
Nous aurons également besoin d'un modèle pour cette vue.

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 %}
 
Vous remarquerez qu'il s'agit d'une forme assez simple, mais il y a un peu de javascript qui publie automatiquement le contenu du formulaire lorsqu'ils sont mis à jour. Ceci est utile à avoir, vous pouvez donc effectuer des modifications sans avoir à appuyer sur Soumettre à chaque fois. Ensuite, nous avons besoin d'URL représentant toutes ces vues dans les motifs URL des utilisateurs. Modifier les utilisateurs / urls.py et ajouter ce code:

#  … previous code, imports
from django.urls import path
from . import views

app_name='users'

urlpatterns = [
# … Modèles d'URL que nous avons précédemment entrés, ajouter les trois lignes suivantes
    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'),
]
C'est le bon moment pour tester notre projet. Mais d'abord, exécutons une autre sauvegarde.

backup
Et exécutez le serveur. Avant de se déployer sur un serveur Linux, c'est une bonne idée d'activer l'authentification à deux facteurs sur le compte. Nous allons faire cela en allant à notre URL de profil, / utilisateurs / profil /, et à la vérification de la case pour activer l'authentification après être entré dans notre numéro de téléphone, puis en soumettant le formulaire.

python manage.py runserver localhost:8000
Visitez la page Web en allant dans votre navigateur Web, j'utilise Google Chrome dans cet exemple et je saisis dans l'URL https: // localhost: 8000 / comptes / profil / Vous pourrez vous connecter si nécessaire et activer l'authentification à deux facteurs. Ce projet a besoin d'un serveur pour s'exécuter afin qu'il puisse vraiment envoyer du courrier. Mais d'abord, nous avons besoin d'un moyen de voir des erreurs. Vous remarquerez que si vous exécutez le serveur en mode débogage, avec des paramètres.debug égal à true, le serveur affiche automatiquement les erreurs. Pour afficher les erreurs sans utiliser le mode de débogage, qui est dangereuse sur un serveur de production, nous devons ajouter une vue pour cela. Les erreurs les plus importantes que nous devons gérer sont: Erreur 500 - Un problème avec notre code Erreur 404 - Une page qui n'a pas été trouvée (URL cassée) Erreur 403 - Une erreur d'autorisation Ajoutons une nouvelle application pour gérer ces erreurs, appelées erreurs.

python manage.py startapp errors
Ajoutez ceci à Settings.py comme nous l'avons fait auparavant, dans le paramètre installé_apps, et commencez par ajouter des références à certaines vues dans app / urls.py, où l'application est le nom de votre projet Django.

handler404 = 'errors.views.handler404'
handler500 = 'errors.views.handler500'
handler403 = 'errors.views.handler403'
C'est tout ce dont nous avons besoin en plus des vues d'erreur, des modèles et un peu de middleware. Définissons-les comme tels:

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

# Créez vos opinions ici.
@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.'})
Ensuite, définissons le middleware pour gérer ces erreurs. Nous le ferons en ajoutant d'abord à middleware_classes dans settings.py, avec le nom de notre middleware.

MIDDLEWARE_CLASSES = [
    # ... middleware précédent
    'errors.middleware.ExceptionVerboseMiddleware,
]
Ensuite, ajoutons le middleware.

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.')
Nous ajoutons une fonction pour obtenir l'exception actuelle en utilisant un local de filetage, ce qui nous aide à retracer les erreurs de notre code. En termes de modèles, nous n'en avons besoin qu'un, car nous définissons dynamiquement le titre dans la vue. Le modèle a juste besoin de rendre le titre et de «trace», notre erreur de trace du contexte.

nano errors/templates/errors/error.html
 
{% extends 'base.html' %}
{% block content %}
<h1>{{ pagetitle }}</h1>
<p>{{ trace }}</p>
{% endblock %}
 
C'est notre modèle le plus simple à ce jour, mais c'est à quel point il est facile de voir les erreurs de notre projet. Ensuite, désactivons le débogage dans les paramètres.

nano app/settings.py
Trouvez cette ligne où elle est définie sur true et changez-la en fausse

DEBUG = False
Allez-y et sauvegardez l'application maintenant. Nous sommes prêts à se déployer sur un serveur Linux distant et continuons d'ajouter des fonctionnalités à partir de là.

sudo backup
Avant de publier ce code sur un serveur, nous devons considérer qu'il peut y avoir des problèmes avec le code. Selon l'affaire, les sites qui acceptent les informations qui leur ont été publiés auront des problèmes avec le spam et la difficulté à retirer le spam. Cela ne devrait pas se produire immédiatement, mais si cela se produit, nous examinerons plus tard comment modérer automatiquement le spam sur le site et rendre les robots plus difficiles à accéder au site, ainsi que comment désactiver les comptes d'utilisateurs, et vérifier l'identité d'un utilisateur avec Une analyse de leur identifiant ou d'un scan biométrique, comme une empreinte digitale ou une reconnaissance faciale. En regardant l'exemple d'authentification multi-facteurs que nous avons examiné, en production, les choses peuvent être différentes. Remarquez comment nous limitons les connexions et l'expiration des jetons. Si les robots accèdent à un site, l'authentification à deux facteurs peut être plus difficile car ils peuvent entrer des codes en même temps que l'utilisateur est. Pour lutter contre cela, utilisons un modèle dans les modèles d'utilisateurs, déclarons comment nous interagissons avec le site lorsque nous sommesAuthentification à l'aide de l'authentification multi-facteurs avec un numéro de téléphone. Nous ajouterons également une option pour s'authentifier avec les e-mails. Commencez par modifier les modèles d'utilisateur avec

nano users/models.py
C'est à quoi devrait ressembler le modèle que nous ajoutons. Nous n'avons pas besoin de méthodes, de variables pour stocker un ID, l'utilisateur, l'horodatage, l'expiration, la longueur et les tentatives contre toute authentification multi-facteurs (un code comme 123456 envoyé à un téléphone ou un e-mail).

# Un jeton de base utilisé pour se connecter au site Web
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)
Ajoutons également un privilège à notre utilisateur, et nous le définirons manuellement pour l'instant, avant de migrer finalement vers les utilisateurs privilégiés automatiquement. Dans les modèles d'utilisateurs, ajoutez cette ligne dans le profil:

    vendor = models.BooleanField(default=False)
Comme pour toute modification de la base de données, nous devons faire des migrations et migrer la base de données chaque fois que nous modifions un fichier Models.py dans Django. N'oubliez pas, pour ce faire, nous utilisons d'abord Source (s'il n'a pas déjà été utilisé depuis l'ouverture du terminal), puis Python Manage.py pour faire les migrations et migrer.

cd project-directory-you-named # (si nécessaire)
source venv/bin/activate
python manage.py makemigrations && python manage.py migrate
Pour l'instant, vous pouvez enrôler les comptes que vous avez créés en tant que fournisseurs en utilisant le shell.

python manage.py shell
from users.models import Profile
p = Profile.objects.get(user__username='Charlotte')
p.vendor = True
p.save()
exit()
Maintenant, évoluons notre vue d'authentification multi-facteurs pour utiliser ce jeton. Tout d'abord, nous devons modifier nos utilitaires MFA Helper. En utilisant nano,

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

# Authentifier l'utilisateur à l'aide de son e-mail ou de son numéro de téléphone
def mfa(request, username, usertoken):
    token = MFAToken.objects.filter(uid=username, expires__gt=timezone.now() + datetime.timedelta(seconds=30)).order_by('-timestamp').last() # Filtrez le jeton par la valeur transmise dans l'URL (un 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)) # Si cette session n'a pas été créée, créez-le
    user = User.objects.filter(id=token.user.id).first() # Obtenez l'utilisateur du jeton
    if not user and request.user.is_authenticated: return redirect(reverse('feed:home')) # S'ils sont déjà authentifiés, connectez-les
    if not user: raise PermissionDenied() # Nier si aucun utilisateur n'a été trouvé
    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): # Vérifiez le jeton AUTH
        auth_login(request, user, backend='django.contrib.auth.backends.ModelBackend') # Connectez-vous à l'utilisateur s'il n'est pas déjà connecté
        user.profile.mfa_expires = timezone.now() + datetime.timedelta(minutes=settings.LOGIN_VALID_MINUTES) # Définissez une expiration sur leur authentification multi-facteurs
        user.profile.save()
        return HttpResponseRedirect(next if next != '' else reverse('landing:landing')) # Rediriger l'utilisateur vers la page suivante
    if not user.profile.mfa_enabled: # Vérifiez si le MFA est activé
        if not check_verification_time(user, token): # Vérifiez l'heure
            user.profile.mfa_enabled = False # Effacer le numéro de téléphone
            user.profile.enable_two_factor_authentication = True # Activer MFA
            user.profile.phone_number = '+1' # Désactiver le numéro de téléphone
            user.profile.save() # Enregistrer le profil
            auth_login(request, user, backend='django.contrib.auth.backends.ModelBackend') # Enregistrez l'utilisateur si son MFA n'est pas activé
            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): # Si la demande est une demande de poste
        form = TfaForm(request.POST) # Instancier le formulaire
        code = str(form.data.get('code', None)) # Obtenez le code
        if code and code != '' and code != None: # Assurez-vous que ce n'est pas vide
            token_validated = user.profile.check_auth_token(usertoken) # Vérifiez le jeton AUTH
            p = user.profile
            is_verified = check_verification_code(user, token, code) # Vérifiez le code
            p.mfa_authenticated = is_verified
            if token_validated: # Si tout
                if is_verified: # Est en ordre
                    user.profile.mfa_enabled = True # Activer MFA (s'il n'est pas déjà activé)
                    user.profile.save()
                    auth_login(request, user, backend='django.contrib.auth.backends.ModelBackend') # Connectez-vous à l'utilisateur
                    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(): # Créez une requête pour le paramètre suivant (le cas échéant)
                        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) # Réorienter
                    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: # Si le jeton n'était pas valide
                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: # S'il y avait trop de tentatives
                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): # Envoyer l'e-mail (ou le SMS)
                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('/'))
    # Rendez le formulaire (pour les demandes de 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'})
Lorsque nous ajoutons ce code, assurez-vous d'importer la fonction pour envoyer un e-mail. En haut du fichier, les vues de l'utilisateur (avec d'autres importations), ajouter

from .mfa import send_verification_email as send_mfa_verification_email
Maintenant, nous devons écrire cette fonction avant que tout cela ne fonctionne. Il devrait étendre notre fonction d'e-mail d'envoi et envoyer simplement un e-mail à l'utilisateur avec le code de vérification.

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))
Donc, tout cela fonctionne très bien, nous avons maintenant un système d'authentification multi-facteurs qui dépend d'un numéro de téléphone ou d'un e-mail pour se connecter. Mais nous avons également besoin d'un moyen de supprimer, ou au moins de masquer les utilisateurs qui ne coopèrent pas avec nos termes. Il pourrait s'agir de spammeurs, de robots ou de toute personne qui ne signifie pas bien pour notre travail. Jetez un œil à une vue que j'ai pour surveiller les utilisateurs sur mon site Web:

# montants
from django.contrib.auth.decorators import login_required
from django.contrib.auth.decorators import user_passes_test
from .tests import is_superuser_or_vendor # Nous aurons besoin de créer ce test

@login_required
@user_passes_test(is_superuser_or_vendor)
def users(request):
    # Obtenez la liste des utilisateurs
    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', { # Retourner les utilisateurs dans un modèle
        'title': 'All Accounts',
        'users': User.objects.all(),
        'new_today': new_today,
        'new_this_month': new_this_month,
        'subscribers': subscribers
    })
Notez que ce code utilise un test, nous devrons déclarer ce test dans un fichier Tests.py et l'importer. Édition d'utilisateurs / tests.py, créons le test.

def is_superuser_or_vendor(user):
    return user.profile.vendor or user.is_superuser
Ceci est en conjonction avec le modèle utilisateur / utilisateurs.html, qui ressemble à ceci:
 
{% 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 %}
 
Notez que le modèle comprend un autre modèle, Users / _user.html. Lorsque vous utilisez un modèle qui a un sous-modèle et n'utilise pas d'écran, c'est une bonne idée d'ajouter un soulignement (_) avant le nom du fichier à étendre, afin de distinguer les modèles. Notez que c'est beaucoup de Jinja, vous ne pouvez peut-être pas toutes ces variables définies. Mais c'est à quoi ressemble mon code.
 
{% 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>
 
Nous avons également besoin d'un autre sous-modèle, toggle_active.html. Ce modèle doit être un formulaire qui nous permet de basculer si un utilisateur est actif.
 
<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>
 
Nous devrons également ajouter une vue pour basculer l'activité utilisateur et les modèles d'URL appropriés. Pendant que nous y sommes, ajoutons une vue pour supprimer un utilisateur au cas où nous en aurons besoin.

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>')


# Montants
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from django.views.generic import DeleteView

class UserDeleteView(LoginRequiredMixin, UserPassesTestMixin, DeleteView):
    model = User
    success_url = '/' # La redirection de l'URL de succès
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        return context

    def test_func(self): # Tester si l'utilisateur est superutilisateur et a la permission de supprimer
        user = self.get_object()
        if self.request.user != user and self.request.user.is_superuser:
            return True
        return False
Bien que cela soit pratique lorsque cela est nécessaire, la suppression d'un utilisateur ne devrait pas être nécessaire la plupart du temps, nous pouvons simplement basculer la visibilité des utilisateurs qui visitent le site si nous devons les rejeter. Les modèles d'URL que nous avons ajoutés ressemblent à ceci. Avec Nano, modifiez les utilisateurs / urls.py et ajoutez ces lignes:

nano users/urls.py
Les lignes devraient aller dans la liste des chemins dans les vues de l'utilisateur, avant la fin «]» mais après le début «[».

# …
    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'),
# …
Maintenant, assurez-vous de sauvegarder le site afin que vous puissiez le télécharger sur le serveur Web sur lequel nous continuerons à travailler. De la ligne de commande,

sudo backup
Maintenant, notre site est sauvegardé. Alors maintenant, nous avons quelques fonctionnalités plus utiles. Mais qu'en est-il de la vue d'ensemble ici? Ce code n'est toujours pas accessible sur Internet, nous n'avons pas encore de serveur de message . Nous allons arriver à tout cela. La chose la plus importante pour l'instant sera simplement d'obtenir ce code en ligne, ce que nous pouvons faire avec seulement quelques lignes de bash sur un serveur Ubuntu. Vous devrez cependant louer un serveur pour cela, sauf si vous avez un serveur à la maison et un abonnement Internet professionnel qui vous permet d'ouvrir des ports. Personnellement, je gère mon site Web sur un HP Z440 qui est installé dans mon appartement, mais il est généralement beaucoup moins cher pour les besoins de base pour louer un serveur privé virtuel (VPS). Gardez à l'esprit que le code que nous exécutons maintenant est relativement mince, il devra être maintenu et amélioré avant que nous soyonsPrêt à utiliser ce que nous avons pour construire un produit. Assurez-vous de faire attention à ce que vous faites avec Internet, assurez-vous que si vous déployez ce site publiquement sur le Web sur un serveur Linux, vous avez un plan pour bloquer les interactions indésirables avec votre site Web. Ce ne sera probablement pas un problème au début, mais nous examinerons une variété de solutions pour lutter contre cela, y compris l'apprentissage automatique, l'intelligence artificielle et la vision par ordinateur. Lorsque cela devient un problème, regardez plus loin dans ce texte pour une solution. En termes de location d'un VPS, il y a beaucoup d'endroits où vous pouvez aller. Google Cloud possède des serveurs VPS, IonOS, Kamatera, Amazon AWS et plus de fournisseurs proposent des solutions de serveur cloud qui répondront à nos besoins. Vous devrez cliquer sur leurs formulaires et sélectionner un plan pour commencer. Vous pouvez suivre un plan de base avec n'importe quel fournisseur, mais assurez-vous que le fournisseur vous permet d'ouvrir des ports de serveur de messagerie portuaire pour envoyer un e-mail (ce devrait être le port 587 et le port 25), certains fournisseurs bloquent ces ports. Jusqu'à présent, j'ai eu leEST Expérience avec Ionos et Kamatera, tous deux me permettront d'envoyer des e-mails illimités et leur prix est assez bon marché. Vous vous connecteras à votre nouveau serveur via un protocole appelé SSH ou Secure Shell, qui vous permet d'interfacer à distance avec le serveur exactement comme votre ordinateur personnel, à partir de votre ordinateur personnel. Lorsque vous configurez le serveur, le fournisseur d'hébergement va probablement vous demander d'ajouter une clé SSH, ou il vous donnera un nom d'utilisateur et un mot de passe. La clé SSH est la façon dont vous vous connectez au serveur à partir de la ligne de commande pour modifier le code. Utilisez les options SSH-KeyGen ci-dessous pour générer un SSH

ssh-keygen
Enregistrez le fichier et écrasez-le si vous en avez besoin, il est bon de faire pivoter vos clés SSH si vous ne l'avez pas déjà fait. Maintenant, vous pouvez utiliser la commande suivante pour voir votre clé SSH. Vous voudrez le copier sur votre serveur distant afin que vous puissiez l'utiliser pour vous authentifier.

cat ~/.ssh/id_rsa.pub
Si vous n'êtes pas en mesure de voir une clé SSH lorsque vous tapez cette commande (une longue chaîne de chiffres et de lettres commençant par «SSH-RSA AAA»), essayez de générer une clé RSA (elles sont plus sécurisées, donc je conseille de les utiliser .) Le code suivant générera une clé RSA SSH 4096 bits.

ssh-keygen -t rsa -b 4096
Créez un VPS exécutant Ubuntu, mais vous prévoyez de le faire. Une fois que vous avez créé un VPS en cliquant sur les formulaires sur le site Web des fournisseurs (kamatera.com, ionos.com ou similaire), vous voudrez vous connecter. Pour ce faire, utilisez la commande SSH avec votre adresse IP (l'adresse Cela ressemble à xx.xx.xx.xx). Vous devrez également être sensible au nom d'utilisateur par défaut sur le serveur que nous avons créé, par exemple Ubuntu.

ssh ubuntu@XX.XX.XX.XX
On peut vous demander un mot de passe, si on vous demande un mot de passe, entrez-le. Nous n'utiliserons pas le nom d'utilisateur par défaut, alors commençons par créer un nouvel utilisateur et ajoutant une clé SSH à leur compte. Commençons par ajouter un nouveau fichier sshd_config, qui indique au serveur comment utiliser SSH.

nano sshd_config

# Il s'agit du fichier de configuration du système SSHD Server.  Voir
# sshd_config (5) pour plus d'informations.

# Ce SSHD a été compilé avec Path = / USR / LOCAL / SBIN: / USR / LOCAL / BIN: / USR / SBIN: / USR / BIN: / sbin: / bin: / usr / jeux

# La stratégie utilisée pour les options dans le SSHD_CONFIG par défaut
# OpenSSH est de spécifier des options avec leur valeur par défaut où
# possible, mais laissez-les commenter.  Des options non communiquées remplacent le
# Valeur par défaut.

# Port 22
# Addomfamily n'importe quel
# Adresse de liste 0.0.0.0
# Écoutez Adresse ::

# Hostkey / etc / ssh / ssh_host_rsa_key
# Hostkey / etc / ssh / ssh_host_ecdsa_key
# Hostkey / etc / ssh / ssh_host_ed25519_key

# Ciphers et Keying
# Rekeylimit Default Aucun

# Enregistrement
# Auth
# Infos Loglevel

# Authentification:

# Logringracetime 2m
# Permitrootlogin Prohibit-Password
# Strictsmodes oui
# Maxauthtries 6
# MaxSessions 10

PubkeyAuthentication yes

# Attendez-vous à ce que .SSH / AMORTINED_KEYS2 ne soit pas pris en compte par défaut à l'avenir.
AuthorizedKeysFile	.ssh/authorized_keys .ssh/authorized_keys2

# Principals permanents

# AutorizedKeysCommand Aucun
# AutorizedKeysCommandUser

# Pour que cela fonctionne, vous aurez également besoin de clés d'hôte dans / etc / ssh / ssh_known_hosts
# AUTHENTIFICATION BASE HOST
# Changer pour oui si vous ne faites pas confiance ~ / .ssh / connu_hosts pour
# Aauthentification à base d'hôte
# IgnoreUser Knowhosts non
# Ne lisez pas les fichiers ~ / .rhosts de l'utilisateur et ~ /.
# Ignorershosts oui

# Pour désactiver les mots de passe du texte effacé à tunnex, passez à non ici!
PasswordAuthentication no
# Pertinermptypasswords non

# Passer à oui pour activer les mots de passe de la réponse de défi (méfiez-vous des problèmes avec
# Quelques modules et fils PAM)
KbdInteractiveAuthentication no

# Options de Kerberos
# Kerberosauthentification non
# Kerberosorlocalpasswd oui
# Kerberosticketcleanup oui
# TIDE KERBEROSCOTTED NO

# Options GSSAPI
# GSSAPIATUTHENTICATION NON
# Gssapicleanupcredentials oui
# Gssapistrictacceptorcheck oui
# Gssapikeyexchange non

# Définissez ceci sur «Oui» pour activer l'authentification PAM, le traitement des comptes,
# et traitement de session. Si cela est activé, l'authentification PAM
# être autorisé via la kbdinteractiveautellication et
# PasswordAuthentication.  Selon votre configuration PAM,
# L'authentification PAM via KBDinterActiveAuthentification peut contourner
# Le réglage de "permutrootlogin sans mot de passe".
# Si vous voulez juste que le compte PAM et les chèques de session s'exécutent sans
# Authentification PAM, puis permettez-le mais définissez le mot de passe-authentification
# et kbdinteractiverauthentification à «non».
UsePAM yes

# Autorisant pour vous-même oui
# Pertetcp forforwing oui
# Gatewayports Non
X11Forwarding yes
# X11displayoffset 10
# X11useLocalhost Oui
# Permittty Oui
PrintMotd no
# Printlastlog oui
# Tcpkeepalive Oui
# Environnement permit
# Compression retardée
# Intervalle client 0
# ClientaliveCountMax 3
# Usages dans
# Pidfile /run/sshd.pid
# Maxstartups 10: 30: 100
# Pemittunl non
# Chrootdirectory
# Version addendum Aucun

# Pas de chemin de bannière par défaut
Banner /etc/banner

# Permettre au client de passer les variables d'environnement des paramètres régionaux
AcceptEnv LANG LC_*

# remplacer la valeur par défaut d'aucun sous-système
Subsystem	sftp	/usr/lib/openssh/sftp-server

# Exemple de paramètres de remplacement sur une base par utilisateur
# Faire correspondre les anoncvs de l'utilisateur
# X11 pour non
# Autorisation
# Perteetty dans
# ForceCommand CVS Server
PermitRootLogin no
N'oubliez pas, Ctrl + X et Y pour enregistrer le fichier. Ensuite, écrivons un script de base appelé initialize (le tout dans le répertoire domestique par défaut de notre utilisateur).

nano initialize
Ajouter ces lignes dans le fichier, en remplaçantAvec votre clé SSH, vous avez trouvé en utilisant CAT. (.ssh / id_rsa.pub)

# ! / bac / bash
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
Pour vous guider à travers ce fichier, commençons en ligne par ligne. La première ligne indique au compilateur qu'il s'agit d'un script bash. Ensuite, nous installons les dépendances, copiant SSHD_CONFIG dans le répertoire correct, redémarrant SSH, générant des clés SSH pour root, en ajoutant «l'équipe» de l'utilisateur (vous pouvez choisir un nom que vous aimez pour cela, utilisez la commande AddUser avec son nom et son mot de passe désactivé pour maintenant). Nous ajoutons également l'équipe au groupe sudo, générons leur clé SSH, ajoutons notre clé aux clés et les leurs autorisées et imprimations et impriment leur clé. Ce nouvel utilisateur sera la façon dont nous nous connectons au site. Dans un nouveau terminal, allez-y et ouvrez à nouveau le serveur.

ssh team@XX.XX.XX.XX
Vous ne devriez pas avoir besoin d'un mot de passe cette fois, être comme vous avez une clé SSH. Nous avons également désactivé la connexion avec le mot de passe pour garder le site plus sécurisé. Maintenant, ce serveur démarre complètement vide sans aucune information à ce sujet. Commençons par cloner notre projet depuis GIT afin que nous puissions le télécharger et l'exécuter sur la machine distante. Sur le serveur distant connecté sur SSH, imprimez d'abord votre clé SSH:

cat ~/.ssh/id_rsa.pub
Ensuite, collez cette clé dans les paramètres GIT comme nous l'avons fait auparavant pour configurer notre référentiel GIT. Nous pouvons maintenant cloner notre projet directement sur le serveur. Assurez-vous d'avoir soutenu le projet localement en premier, il est donc sur le serveur GIT de télécharger.

git clone git://github.com/you/yourproject.git
Parfait. Maintenant, tous les fichiers sont là. Nous pouvons les voir avec LS

ls
Maintenant, commençons à configurer le serveur. Tout d'abord, copiez votre répertoire de projet dans un nom simple et mémorable que nous utiliserons pour le projet.

cp -r yourproject whatyoucalledit
Où «WhatyouCalledit» est le nouveau nom de votre projet. Ensuite, nous devrons créer un utilitaire de base pour configurer le serveur. Nous allons enregistrer cet utilitaire et l'utiliser à l'avenir. Pour créer cet utilitaire, créons un binaire utilisateur pour définir la façon dont nous modifions un script. Utilisation de bash, edit / usr / bin / ascript

sudo nano /usr/bin/ascript
Assurez-vous d'utiliser Sudo là-bas afin que vous ayez des autorisations pour modifier le fichier. Dans le fichier, ajoutez ces lignes:

# ! / bac / bash
if [ ! -f /usr/bin/$1 ]; then
    sudo touch /usr/bin/$1
    echo "# ! / bin / bash ">> / usr / bin / 1 $
    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
N'oubliez pas que ce script prend un argument, le nom du script, à 1 $. Il vérifie d'abord si le fichier existe ou la crée autrement, ajoute la première ligne pour déclarer que le script est bash, modifie ses autorisations, le modifie et ajoute son nom à / etc / ascripts qui nous permet de stocker les noms des scripts que nous créent. Si le fichier existe déjà, modifiez simplement les autorisations et modifiez-la. Enregistrez le fichier, et ensuite nous modifierons ses autorisations. Tant que nous utilisons ce script, nous n'aurons plus à recommencer.

sudo chmod a+x /usr/bin/ascript
Parfait. Créons maintenant un script appelé Configuration. Tout d'abord, pas pour vous submerger, mais jetez un œil à quoi ressemble mon script de configuration. Nous allons parcourir ce à quoi ce script devrait ressembler dans votre projet, vous n'aurez pas besoin de tout dans mon script pour commencer.

# ! / bac / bash
SECONDS=0
PYTHON_VERSION=3.12
echo "femmebabe installer initialized."
# sudo chmod a + x scripts / useretup
# ./scripts/usersetup
# Ssh-keyen
# Répertoire de projet
DIR="/home/team/femmebabe"
USER="team"
# Commandes de journal
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
# Configuration git
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
# Mettre à jour et installer
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
# Activer Clamav Antivirus
echo "Starting antivirus"
sudo systemctl enable clamav-daemon
sudo systemctl start clamav-daemon
# Définir le nom d'hôte
echo "127.0.0.1 femmebabe" | sudo tee -a /etc/hosts
sudo hostnamectl set-hostname localhost
# Configuration de 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;"
# Configuration de la base de données de sauvegarde
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
# IPatables désactivés
echo "Configuring firewall"
sudo iptables -P INPUT ACCEPT
sudo iptables -P OUTPUT ACCEPT
sudo iptables -P FORWARD ACCEPT
sudo iptables -F
sudo iptables-save
# Installer 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
# Configuration du post-fixe
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
# Créer des rédacteurs
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
# Configuration de VirtuealV
cd $DIR
echo "Creating virtual environment"
python -m venv venv
source venv/bin/activate
# Obtenir et construire des dépendances
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
# Définir les règles du pare-feu
cd $DIR
# Installez les dépendances 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 install opencv-python == 4.5.5.64
# PIP installe 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
# Installer 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
# Exécuter un certificat
sudo certbot --apache --non-interactive --agree-tos --domains femmebabe.com --email jasper.camber.holton@gmail.com
# Recharger le serveur de messagerie
sudo systemctl restart opendkim postfix dovecot
# Copier les certificats
# sudo cp /etc/letsencrypt/live/femmebabe.com/privkey.pem privkey.pem
# sudo cp /etc/letsencrypt/live/femmebabe.com/cert.pem cert.pem
# Correctif
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"
# Définir les paramètres utilisateur
sudo gpasswd -a www-data users
#  Set permissions
echo "Setting permissions"
sudo chown -R team:users cache/
sudo chmod a+rwx -R cache/
# Sudo Chown -r Équipe: utilisateurs / var / run /
# sudo chown racine: root / run / 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: utilisateurs 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
# Copier la configuration et définir les autorisations
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
# Configuration de la base de données
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"
# Injecter la configuration PAM et supprimer la configuration SSH défectueuse
# Sudo sed -i '' -and $ d '/etc/pam.d/sshd
# Sudo sed -i '' -et $ d '/ etc / profil
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
# Copier les scripts de bac et définir les autorisations
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
# Recharger et activer les services
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
# Activer les modules Apache
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
#  Disable default site
sudo a2dissite 000-default
sudo a2dissite 000-default-le-ssl
#  Enable our site
sudo a2ensite femmebabe-le-ssl
# Recharger le démon et redémarrer Apache, Postfix et OpenDkim
sudo systemctl daemon-reload
sudo systemctl restart apache2
sudo systemctl restart opendkim postfix
sudo systemctl start daphne
#  Set permissions
sudo chown -R :www-data /var/www/
sudo chown -R :www-data /var/www/.deepface
# Configuration d'échange
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
# Moteur de légende 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
# Configuration 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
# Afficher IPv6 et OpenDkim pour la configuration du domaine
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}'
# Configuration terminée
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."
C'est beaucoup de configuration! En bref, ce code journalise les commandes, configure Nano et Git, copie sur les fichiers, télécharge et installe les packages Ubuntu APT, les dépendances Python, configure PostFix, configure PostgreSQL (le serveur de base de données) et charge la base de données, configure UFW (un pare-feu non compliqué), Désactive iptables, télécharge un antivirus, fait des répertoires, des dépendances clones, installe des certificats et des ensembles En haut du serveur, installe la configuration, démarre et active le Sever, alloue l'échange, définit les autorisations et imprime l'adresse IP, l'adresse IPv6 et la clé OpenDKIM. Assez simple, mais cela ressemble à beaucoup de code. Nous n'aurons pas besoin de beaucoup de cela parce que nous n'avons pas les dépendances, nous n'utilisons pas de céleri, de céleri ou de daphné, mais nous en installerons de toute façon pour commencer. Notez que ce code a un domaine déclaré plusieurs fois. Nous devrons également acheter un nom de domaine (qui est un petit frais annuel). Je recommande Squarespace pour acheter un domaine, leur disposition estintuitif et facile à utiliser. Vous pouvez acheter n'importe quel domaine de votre choix, mais j'utilise le domaine Femmebabe.com dans cet exemple. Une fois que vous avez acheté un domaine, rendez-vous au panneau de configuration Squarespace DNS et ajoutez un enregistrement A pointant votre domaine sur le serveur par adresse IP. Cela devrait ressembler à ceci: @ A xx.xx.xx.xx Avec l'opérateur @ comme l'hôte, ce qui signifie tous les sous-domaines sous ce domaine et le domaine racine redirigera tous vers le serveur. Il y a plus d'enregistrements à déclarer, mais nous pouvons passer à ceux-ci une fois que nous sommes prêts à envoyer du courrier. Gardez à l'esprit que cela peut prendre plusieurs jours avant de pouvoir envoyer avec succès le courrier du serveur. Les enregistrements DNS que nous définissons prendront du temps pour se propager. Quoi qu'il en soit, le seul enregistrement que nous devons démarrer est un enregistrement A. Alors maintenant, nous pouvons remplir le script ci-dessous en fonction de notre projet et l'exécuter. Commençons par un script de configuration plus petit pour installer simplement ce dont nous avons besoin pour une progression de base. Nous n'utiliserons pas encore autant de dépendances ou de postgresql, nous allons justeAugmentez un serveur HTTP de base et inquiétez-vous de le certifier lorsque cela est fait. N'oubliez pas que pour obtenir un certificat HTTPS et exécuter le serveur en toute sécurité, nous devrons acheter un domaine ainsi que la location d'un serveur. Pour l'instant, remplacez «Team» dans ce fichier par le nom de votre utilisateur, «DIR» par le répertoire de votre projet et fournissez votre e-mail et votre domaine dans les balises <>. De plus, avant d'exécuter ce code, nous devons modifier les paramètres vers le pare-feu prend en charge, le cas échéant. Habituellement, cela se trouve dans l'onglet «Networks» de votre fournisseur d'hébergement, ou si vous êtes auto-hébergé, c'est dans la section «transfert de port» de votre routeur. Vous voudrez également configurer une IP statique via votre routeur avec l'adresse de votre machine de serveur, si vous utilisez l'auto-hébergement. Vous devrez ouvrir les ports suivants pour un accès à lecture / écriture. 22 (ssh) 25 (courrier) 587 (courrier) 110 (Client de messagerie) 80 (http) 443

# ! / bac / bash
SECONDS=0
PYTHON_VERSION=3.12
echo "femmebabe installer initialized."
DIR="/home/team/<yourproject>"
USER="team"
# Commandes de journal
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
# Configuration git
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
# Mettre à jour et installer
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
#  Enable ClamAV antivirus
echo "Starting antivirus"
sudo systemctl enable clamav-daemon
sudo systemctl start clamav-daemon
# Définir le nom d'hôte
echo "127.0.0.1 femmebabe" | sudo tee -a /etc/hosts
sudo hostnamectl set-hostname femmebabe
# Configuration de la base de données de sauvegarde
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
# IPatables désactivés
echo "Configuring firewall"
sudo iptables -P INPUT ACCEPT
sudo iptables -P OUTPUT ACCEPT
sudo iptables -P FORWARD ACCEPT
sudo iptables -F
sudo iptables-save
# Configuration de VirtuealV
cd $DIR
echo "Creating virtual environment"
python -m venv venv
source venv/bin/activate
pip3 install -r requirements.txt
# Installer 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
# Exécuter un certificat
sudo certbot --apache --non-interactive --agree-tos --domains femmebabe.com --email <youremail>@gmail.com
# Définir les paramètres utilisateur
sudo gpasswd -a www-data users
#  Set permissions
echo "Setting permissions"
sudo chown -R team:users cache/
sudo chmod a+rwx -R cache/
# Sudo Chown -r Équipe: utilisateurs / var / run /
# sudo chown racine: root / run / 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 ./
# Recharger et activer les services
echo "Enabling services"
sudo systemctl daemon-reload
sudo systemctl enable clamav-daemon
sudo systemctl start clamav-daemon
# Activer les modules Apache
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
# Recharger le démon et redémarrer Apache, Postfix et OpenDkim
sudo systemctl daemon-reload
sudo systemctl restart apache2
sudo systemctl restart opendkim postfix
# Afficher IPv6 et OpenDkim pour la configuration du domaine
echo "COPY the below information to domain configuration."
hostname -I
ip a | grep inet
ip -6 addr | grep "scope link"
Avant d'exécuter ce code, assurez-vous que le domaine que vous avez acheté est connecté au serveur. Pour ce faire, ouvrez un terminal sur votre machine locale et exécutez cette commande avec votre domaine:

ping femmebabe.com # Insérez votre domaine ici, après ping
Si tout semble bien et que le serveur envoie des réponses, nous sommes prêts à exécuter le script et à installer des packages ainsi qu'à démarrer, activer et certifier notre serveur Apache. Ce n'est pas toute la configuration nécessaire pour configurer Postfix, nous examinerons cette configuration plus tard. Pour l'instant, exécutez ce code de configuration et il devrait prendre quelques minutes pour installer et certifier votre serveur. Encore une fois, assurez-vous de remplacer le nom, le courrier électronique et le nom de domaine dans le script en fonction du nom que vous avez acheté. Maintenant que le serveur est provisionné, vous pouvez accéder à l'URL dans n'importe quel navigateur Web et vérifier pour vous assurer que le serveur exécute HTTPS. Si ce n'est pas le cas, essayez d'attendre un peu de temps pour que les enregistrements DNS rattrapent et exécutez la commande suivante pour réessayer la certification CERTBOT:

sudo certbot --apache --non-interactive --agree-tos --domains <domain>.com --email <youremail>@gmail.com
Tant que vous avez tout configuré correctement, vous devriez pouvoir accéder à la page par défaut d'Apache juste pour savoir que votre code fonctionne et affiche une page Web en direct. Ensuite, modifions les paramètres.py pour modifier notre mode de débogage par défaut en production. Nous configurerons également le domaine dans les paramètres, ainsi que les IP internes.

nano yourproject/settings.py
Dans les paramètres, modifiez / ajoutez ces lignes.

DEBUG = False

# Configuration du site
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',
]
Maintenant, nous devrons configurer Apache2. Modifions le fichier de configuration que nous déploierons avec cette ligne:

sudo nano /etc/apache2/sites-available/femmebabe-le-ssl.conf
Ce fichier de configuration doit contenir notre nom de domaine et le nom de l'utilisateur et du projet. J'utilise le nom de domaine Femmebabe.com, l'équipe de nom d'utilisateur et le nom du projet 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>
Assurez-vous de remplacer le nom du projet, des répertoires et du domaine dans cet exemple de code lors de la configuration de votre serveur. Maintenant, nous devrons désactiver le site par défaut. Cela peut être fait en utilisant Bash.

sudo a2dissite 000-default-le-ssl
sudo a2dissite 000-default
sudo a2dissite default-ssl
Ensuite, nous pouvons activer le site par défaut et recharger Apache2, également en utilisant Bash. N'oubliez pas de remplacer Femmebabe par le nom du fichier que vous avez déclaré lors de l'édition dans / etc / apache2 / sites-disponible /.

sudo a2ensite femmebabe-le-ssl
sudo systemctl reload apache2
Retournez dans votre domaine dans la barre de navigation. Vous devriez voir le site que vous avez configuré dans votre navigateur Web. Félicitations! Si vous ne le voyez pas, vous devrez peut-être apporter quelques modifications. Passez soigneusement les paramètres de votre projet, la configuration Apache, et assurez-vous de ne pas avoir d'erreurs et exécutez les commandes suivantes pour vérifier le projet pour les erreurs.

cd projectname
source venv/bin/activate
python manage.py check
Si vous avez des erreurs dans votre projet Python, tracez-les là où ils se trouvent et réparez-les. Vous ne pourrez peut-être pas voir toutes vos erreurs en fonction de l'endroit où ils se trouvent, donc si vous avez une erreur qui dit simplement "Papulater n'est pas réentrant", modifiez le fichier suivant dans l'environnement virtuel, Registry.py, pour exposer le erreur.

nano venv/lib/python3.12/site-packages/django/apps/registry.py
Faites défiler la ligne 83, où cette erreur d'exécution est augmentée (augmenter RuntimeError ("Populate () n'est pas réentrant")), et ajoutez un commentaire avant cette ligne, puis ajoutant, avec la même indentation, self.app_configs = {}. Cela ressemble à ceci:

            if self.loading:
                # Empêcher les appels réentrants pour éviter d'exécuter AppConfig.ready ()
                # Méthodes deux fois.
# augmenter RunTimeError ("Populate () n'est pas réentrant")
                self.app_configs = {}
            self.loading = True
Vous pouvez ensuite vérifier à nouveau le projet et exposer l'erreur.

python manage.py check
Ensuite, vous pouvez voir l'erreur et le réparer. Lorsque vous l'avez corrigé et que le code se compile sans aucune erreur, assurez-vous de modifier le fichier pour que cela ressemble à ceci:

            if self.loading:
                # Empêcher les appels réentrants pour éviter d'exécuter AppConfig.ready ()
                # Méthodes deux fois.
                raise RuntimeError("populate() isn't reentrant")
# self.app_configs = {}
            self.loading = True
À condition que le serveur soit en ligne, lorsque nous apportons d'autres modifications, nous devons utiliser la commande suivante pour recharger le serveur:

sudo systemctl reload apache2
Génial! Mais qu'en est-il d'envoyer du courrier? Pour commencer à envoyer des e-mails, nous devrons d'abord mettre à jour la configuration du domaine. Cela devrait être dans votre panneau DNS dans Squarespace, ou n'importe quel registraire de nom de domaine que vous avez choisi. Nous devrons également installer et ajouter une configuration, et exécuter quelques commandes. Tout d'abord, obtenons l'adresse IPv6 du serveur. Nous ouvrirons ensuite votre DNS et ajouterons les enregistrements. Pour obtenir l'adresse IPv6 du serveur, utilisez cette commande:

ip -6 addr
Maintenant, nous pouvons ajouter les enregistrements suivants aux paramètres DNS. Mes enregistrements ressemblent à ceci. Cependant, pour vos enregistrements, vous devez remplacer l'adresse IP par votre IP (pas 75.147.182.214, c'est à moi). Ajoutez également votre domaine à la place de Femmebabe.com, ainsi que votre adresse IPv6 trouvée avec la commande précédente (vous ne pouvez pas utiliser le mien, Fe80 :: 725A: FFF: FE49: 3E02). Ne vous inquiétez pas de la DomainKey pour l'instant, ceci est créé lorsque nous configurons Postfix, le serveur de messagerie, avec OpenDKIM, et imprimez la clé. Nous allons configurer ce dernier. @ UN N / A 75.147.182.214 @ Mx 10 Femmebabe.com @ Ptr N / A Femmebabe.com @ SMS N / A TXT @ V = SPF1 MX IP75.147.182.214IP6: FE80 :: 725A: FFF: FE49: 3E02 ~ Tous default._Bimi SMS N / A v = bimi1; l = https: //femmebabe.com/media/static/femmebabe.svg _dmarc SMS N / A v = dMarc1; p = aucun sendonly._domainkey SMS N / AMaintenant, nous devons ajouter une configuration persistante pour Postfix. Tout ce que nous devons faire est de nous assurer de remplacer le nom de domaine, Femmebabe.com, par le nom de domaine que vous utilisez. Regardons tous les fichiers de configuration un par un et installons-les dans un répertoire appelé config dans notre projet, pour installer sur le système d'exploitation.

nano config/etc_postfix_main.cf
Ajoutez ce texte au fichier

# Voir /usr/share/postfix/main.cf.dist pour une version commentée et plus complète


# Debian Specific: Spécification d'un nom de fichier provoquera le premier
# Ligne de ce fichier à utiliser comme nom.  La défaut Debian
# est / etc / nom.
# Myorigin = / etc / nom.

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

# L'ajout .Domain est le travail du MUA.
append_dot_mydomain = no

# Décommente la ligne suivante pour générer des avertissements "Mail retardé"
# delay_warning_time = 4h

readme_directory = no

# Voir http://www.postfix.org/compatibilité_readme.html - par défaut à 3.6 sur
# Installations fraîches.
compatibility_level = 3.6



# Paramètres 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

# Configuration de miau
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
Configuration suivante!

nano config/etc_postfix_master.cf
Ajouter ces lignes:

# 
# Fichier de configuration du processus Master Postfix.  Pour plus de détails sur le format
# du fichier, voir la page manuelle maître (5) (commande: "Man 5 Master" ou
# En ligne: http://www.postfix.org/master.5.html).
# 
# N'oubliez pas d'exécuter "Reload PostFix" après avoir modifié ce fichier.
# 
# ==============================================. =======================.
# Type de service Private Upriv Chroot Wakeup MaxProc Commande + Args
# (oui) (oui) (non) (jamais) (100)
# ==============================================. =======================.
smtp      inet  n       -       y       -       -       smtpd
# SMTP INET N - Y - 1 PostScreen
# smtpd pass - - y - - smtpd
# Dnsblog unix - - y - 0 dnsblog
# Tlsproxy unix - - y - 0 tlSproxy
# Choisissez-en un: activez uniquement la soumission pour les clients de bouclage ou pour tout client.
# 127.0.0.1: SUBMISSION INET N - Y - - SMTPD
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 / soumission
# -o smtpd_tls_security_level = Encrypt
# -o smtpd_sasl_auth_enable = oui
# -o smtpd_tls_auth_only = oui
# -o smtpd_reject_unlistted_recipient = non
#   -o smtpd_client_restrictions=$mua_client_restrictions
# -o smtpd_helo_restrictions = $ mua_helo_restrictions
# -o SMTPD_SENDER_RESTRICTIONS = $ MUA_SENDER_RESTRICTIONS
# -o smtpd_recipent_trastracts =
# -o smtpd_relay_restrictions = permis_sasl_authenticated, rejeter
# -O milter_macro_daemon_name = originaire
# Choisissez-en un: activez uniquement SMTPS pour les clients de Loopback, ou pour tout client.
# 127.0.0.1:SMTPS INET N - Y - - SMTPD
# smtps inet n - y - - smtpd
# -O syslog_name = postfix / smtps
# -o smtpd_tls_wappermode = oui
# -o smtpd_sasl_auth_enable = oui
# -o smtpd_reject_unlistted_recipient = non
#   -o smtpd_client_restrictions=$mua_client_restrictions
# -o smtpd_helo_restrictions = $ mua_helo_restrictions
# -o SMTPD_SENDER_RESTRICTIONS = $ MUA_SENDER_RESTRICTIONS
# -o smtpd_recipent_restrictions =
# -o smtpd_relay_restrictions = permis_sasl_authenticated, rejeter
# -O milter_macro_daemon_name = originaire
# 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
# 
# ==============================================. ==================.
# Interfaces avec un logiciel non-Postfix. Assurez-vous d'examiner le manuel
# Pages du logiciel non-Postfix pour savoir quelles options il veut.
# 
# De nombreux services suivants utilisent la livraison de tuyaux postfix (8)
# agent.  Voir la page Pipe (8) Woman pour plus d'informations sur $ {destinataire}
# et d'autres options d'enveloppe de message.
# ==============================================. ==================.
# 
# mailDrop. Voir le fichier Postfix mailDrop_readme pour plus de détails.
# Spécifiez également dans main.cf: mailDrop_Destination_recipient_limit = 1
# 
maildrop  unix  -       n       n       -       -       pipe
  flags=DRXhu user=vmail argv=/usr/bin/maildrop -d ${recipient}
# 
# ==============================================. ==================.
# 
# Les versions Cyrus récentes peuvent utiliser l'entrée existante "LMTP" Master.CF.
# 
# Spécifiez dans Cyrus.conf:
# lmtp cmd = "lmtpd -a" écouter = "localhost: lmtp" proto = tcp4
# 
# Spécifiez dans main.cf un ou plusieurs des éléments suivants:
# Mailbox_Transport = LMTP: INET: LocalHost
# Virtual_transport = lmtp: inet: localhost
# 
# ==============================================. ==================.
# 
#  Cyrus 2.1.5 (Amos Gouaux)
# Spécifiez également dans main.cf: cyrus_destination_recipent_limit = 1
# 
# cyrus     unix  -       n       n       -       -       pipe
# Flags = drx user = cyrus argv = / cyrus / bin / délivre -e -r $ {expéditeur} -m $ {extension} $ {user}
# 
# ==============================================. ==================.
# Ancien exemple de livraison via Cyrus.
# 
# old-cyrus unix  -       n       n       -       -       pipe
# Flags = R User = Cyrus Argv = / Cyrus / bin / Deliver -e -M $ {Extension} $ {User}
# 
# ==============================================. ==================.
# 
# Voir le fichier postfix uucp_readme pour les détails de configuration.
# 
uucp      unix  -       n       n       -       -       pipe
  flags=Fqhu user=uucp argv=uux -r -n -z -a$sender - $nexthop!rmail ($recipient)
# 
# Autres méthodes de livraison externes.
# 
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}
Et la configuration OpenDKIM. OpenDKIM identifie les serveurs de messagerie avec des clés de domaine pour les rendre plus sécurisés. Sans cela, le courrier n'est pas signé et pourrait ne pas se rendre dans une boîte de réception.

nano config/etc_default_opendkim
Ajouter ces lignes:

# Remarque: Il s'agit d'un fichier de configuration hérité. Il n'est pas utilisé par l'OpenDKIM
# SystemD Service. Veuillez utiliser les paramètres de configuration correspondants dans
# /etc/opendkim.conf à la place.
# 
# Auparavant, on modifierait les paramètres par défaut ici, puis exécuterait
# /lib/opendkim/opendkim.service.generate pour générer des fichiers de remplacement systemd pour
# /etc/systemd/system/opendkim.service.d/override.conf et
# /etc/tmpfiles.d/opendkim.conf. Bien que ce soit encore possible, c'est maintenant
# recommandé de régler les paramètres directement dans /etc/opendkim.conf.
# 
# Daemon_opts = ""
# Passer à / var / spool / postfix / run / opendendkim pour utiliser une prise Unix avec
# Postfix dans un chroot:
# Rundir = / was / spool / postfix / run / opendendkim
RUNDIR=/run/opendkim
# 
# Décommente pour spécifier une alternative
# Notez que le réglage cela remplacera n'importe quelle valeur de socket dans opendendkim.conf
# défaut:
SOCKET="local:/var/spool/postfix/opendkim/opendkim.sock"
# Écoutez sur toutes les interfaces sur le port 54321:
# Socket = INET: 54321
# Écoutez sur Loopback sur le port 12345:
# Socket = INET: 12345 @ localhost
# L'écoute est 192.0.2.1 est le port 12345:
# Socket = INET: 12345@192.0.2.1
USER=opendkim
GROUP=opendkim
PIDFILE=$RUNDIR/$NAME.pid
EXTRAAFTER=

nano config/etc_dovecot_conf.d_10-master.conf
Ajouter ces lignes:

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

# Limite par défaut VSZ (taille de mémoire virtuelle) pour les processus de service. C'est principalement
# destiné à attraper et à tuer des processus qui fuient la mémoire avant de manger
# tout.
# default_vsz_limit = 256m

# L'utilisateur de connexion est utilisé en interne par les processus de connexion. C'est le plus non fiable
# Utilisateur dans le système DoveCot. Il ne devrait avoir accès à rien du tout.
# default_login_user = doveull

# L'utilisateur interne est utilisé par des processus non privilégiés. Il doit être séparé de
# L'utilisateur de connexion, afin que les processus de connexion ne puissent pas perturber les autres processus.
# default_internal_user = dovecot

service imap-login {
  inet_listener imap {
    # port = 143
  }
  inet_listener imaps {
    # port = 993
    # ssl = oui
  }

  # Nombre de connexions à gérer avant de démarrer un nouveau processus. Typiquement
  # Les seules valeurs utiles sont 0 (illimité) ou 1. 1 est plus sécurisée, mais 0
  # est plus rapide. <doc / wiki / loginprocess.txt>
  # service_count = 1

  # Nombre de processus pour toujours attendre plus de connexions.
  # process_min_avail = 0

  # Si vous définissez Service_Count = 0, vous devez probablement augmenter cela.
  # vsz_limit = $ default_vsz_limit
}

service pop3-login {
  inet_listener pop3 {
    # port = 110
  }
  inet_listener pop3s {
    # port = 995
    # ssl = oui
  }
}

service submission-login {
  inet_listener submission {
    # port = 587
  }
}

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

  # Créer un écouteur INET uniquement si vous ne pouvez pas utiliser la prise UNIX ci-dessus
  # INET_LISTER LMTP {
    # Évitez de rendre le LMTP visible pour l'ensemble de l'Internet
    # Adresse =
    # port = 
  # }
}

service imap {
  # La plupart de la mémoire va aux fichiers mmap () ing. Vous devrez peut-être augmenter cela
  #  limit if you have huge mailboxes.
  # vsz_limit = $ default_vsz_limit

  # Max. Nombre de processus IMAP (connexions)
  # process_limit = 1024
}

service pop3 {
  # Max. Nombre de processus POP3 (connexions)
  # process_limit = 1024
}

service submission {
  # Max. Nombre de processus de soumission SMTP (connexions)
  # process_limit = 1024
}

service auth {
  # AUTH_SOCKET_PATH pointe vers cette prise UserDB par défaut. C'est généralement
  # utilisé par Dovecot-LDA, Doveadm, éventuellement le processus IMAP, etc.
  # Les autorisations complètes sur cette prise sont en mesure d'obtenir une liste de tous les noms d'utilisateur et
  # Obtenez les résultats des recherches UserDB de chacun.
  # 
  # Le mode 0666 par défaut permet à quiconque de se connecter à la prise, mais le
  # Les recherches UserDB ne réussiront que si l'utilisateur UserDB renvoie un champ "UID" qui
  # correspond à l'UID du processus de l'appelant. Aussi si l'UID ou GID de l'appelant correspond le
  # Socket's UID ou GID La recherche réussit. Tout le reste provoque un échec.
  # 
  # Pour donner à l'appelant des autorisations complètes pour rechercher tous les utilisateurs, définissez le mode sur
  # autre chose que 0666 et Dovecot permet au noyau appliquer le
  # Autorisations (par exemple 0777, permet à tous les autorisations complètes).
  unix_listener /var/spool/postfix/private/auth {
    mode = 0660
    user = postfix
    group = postfix
  }
}

service auth-worker {
  # Le processus de travailleur AUTH est exécuté par défaut de racine, afin qu'il puisse accéder
  # / etc / ombre. Si ce n'est pas nécessaire, l'utilisateur doit être modifié en
  # $ default_interal_user.
  # utilisateur = racine
}

service dict {
  # Si le proxy dict est utilisé, les processus de courrier doivent avoir accès à sa prise.
  # Par exemple: mode = 0660, groupe = vmail et Global Mail_Access_Groups = vmail
  unix_listener dict {
    # Mode = 0600
    # Utilisateur =
    # groupe =
  }
}
Encore une fois, assurez-vous de remplacer le domaine dans tous ces fichiers, Femmebabe.com, par le domaine que vous avez sélectionné. Modifier le fichier suivant, la configuration de Dovecot,

nano config/etc_dovecot_dovecot
Et ajouter ces lignes

## Fichier de configuration Dovecot

# Si vous êtes pressé, voir http://wiki2.dovecot.org/quickconfiguration

# La commande "DoveConf -N" donne une sortie propre des paramètres modifiés. Utilisez-le
# Au lieu de copier et coller des fichiers lors de la publication sur la liste de diffusion Dovecot.

# '# «Le caractère et tout après qu'il est traité comme des commentaires. Espaces supplémentaires
# et les onglets sont ignorés. Si vous souhaitez utiliser l'un de ces explicitement, mettez le
# value inside quotes, eg.: key = "# Char et Whitespace de traîne "

# La plupart des paramètres (mais pas tous) peuvent être remplacés par différents protocoles et / ou
# IPS source / destination en plaçant les paramètres à l'intérieur des sections, par exemple:
# Protocole IMAP {}, local 127.0.0.1 {}, télécommande 10.0.0.0/8 {}

# Les valeurs par défaut sont affichées pour chaque paramètre, il n'est pas obligé de décommenter
# ceux. Ce sont des exceptions à cela cependant: pas de sections (par exemple, espace de noms {})
# ou les paramètres de plugin sont ajoutés par défaut, ils sont répertoriés uniquement comme exemples.
# Les chemins ne sont également que des exemples avec les véritables valeurs par défaut basées sur la configuration
# Options. Les chemins répertoriés ici sont destinés à configurer --prefix = / usr
# --sysconfdir = / etc --localstatedir = / var

# Activer les protocoles installés
!include_try /usr/share/dovecot/protocols.d/*.protocol

# Une liste séparée par des virgules d'IPS ou d'hôtes où écouter les connexions.
# "*" Écoute dans toutes les interfaces IPv4 ", ::" Écoute dans toutes les interfaces IPv6.
# Si vous souhaitez spécifier des ports non défauts ou quelque chose de plus complexe,
# EDIT CONF.D / MASTER.CONF.
# Écouter = *, ::

# Répertoire de base où stocker les données d'exécution.
# base_dir = / var / run / dovecot /

# Nom de cette instance. Dans Configuration multi-instance Doveadm et autres commandes
# peut utiliser -i <stance_name> pour sélectionner quelle instance est utilisée (une alternative
# à -c <figi config_path>). Le nom d'instance est également ajouté aux processus DoveCot
# dans la sortie PS.
# instance_name = dovecot

# Message de salutation pour les clients.
# Login_greeting = DoveCot Ready.

# Liste séparée de l'espace de gammes de réseaux de confiance. Connexions de ces
# Les IP sont autorisés à remplacer leurs adresses IP et leurs ports (pour journalisation et
# pour les vérifications d'authentification). disable_plaintext_auth est également ignoré pour
# ces réseaux. En règle générale, vous spécifiez vos serveurs proxy IMAP ici.
# login_trusted_networks =

# Liste séparée de l'espace des prises de vérification d'accès à la connexion (par exemple TCPWRAP)
# login_access_sockets =

# Avec proxy_maybe = oui si la destination proxy correspond à l'un de ces IP, ne faites pas
# proxing. Ce n'est pas nécessaire normalement, mais peut être utile si la destination
# IP est par exemple IP d'un équilibreur de charge.
# auth_proxy_self =

# Afficher plus de titres de processus verbeux (en PS). Affiche actuellement le nom d'utilisateur et
# Adresse IP. Utile pour voir qui utilise réellement les processus IMAP
# (par exemple, les boîtes aux lettres partagées ou si le même UID est utilisé pour plusieurs comptes).
# verbose_proctitle = non

# Si tous les processus sont tués lorsque le processus de maître Dovecot s'arrête.
# Définir ceci sur "non" signifie que Dovecot peut être mis à niveau sans
# Forcer la fermeture des connexions client existantes (bien que cela puisse également être
# Un problème si la mise à niveau est par exemple. En raison d'une correction de sécurité).
# shutdown_clients = oui

# Si non zéro, exécutez les commandes de courrier via les nombreuses connexions au serveur Doveadm,
# Au lieu de les exécuter directement dans le même processus.
# doveadm_worker_count = 0
# Socket ou hôte Unix: port utilisé pour se connecter au serveur Doveadm
# doveadm_socket_path = doveadm-server

# Liste des variables d'environnement séparées de l'espace qui sont conservées sur Dovecot
# startup et transmis à tous ses processus enfants. Vous pouvez également donner
# Key = Value Pairs pour toujours définir des paramètres spécifiques.
# import_environment = TZ

## 
## Paramètres du serveur de dictionnaire
## 

# Le dictionnaire peut être utilisé pour stocker des listes de clés = valeur. Ceci est utilisé par plusieurs
# Plugins. Le dictionnaire est accessible soit directement ou bien qu'un
# Server de dictionnaire. Les noms du dictionnaire de blocs suivant cartes à uris
# Lorsque le serveur est utilisé. Ceux-ci peuvent ensuite être référencés à l'aide d'Uris au format
# "Proxy :: <nom>".

dict {
  # quota = mysql:/etc/dovecot/dovecot-dict-sql.conf.ext
}

# La majeure partie de la configuration réelle est incluse ci-dessous. Les noms de fichiers sont
# Traté d'abord par leur valeur ASCII et analysés dans cet ordre. Les 00-prefixes
# dans les noms de fichiers sont destinés à faciliter la compréhension de la commande.
!include conf.d/*.conf

# Un fichier de configuration peut également essayer d'être inclus sans donner d'erreur
# On ne trouve pas:
!include_try local.conf

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

protocols = imap pop3

# Permet à DoveCot d'écouter toutes les connexions d'entrée (IPv4 / IPv6)

listen = *, ::
Ajoutez un mot de passe pour l'utilisateur DoveCot:

nano config/etc_dovecot_passwd
La première partie du dossier, avant le côlon, est le nom d'utilisateur. La dernière partie, «YourPassword», désigne le mot de passe que vous souhaitez donner à votre serveur de messagerie.

team:{plain}yourpassword
Ensuite, la configuration d'OpenDkim

nano config/etc_opendkim.conf
Et ajouter ces lignes:

# Il s'agit d'une configuration de base pour la signature et la vérification. Il peut facilement être
# adapté pour s'adapter à une installation de base. Voir opendendkim.conf (5) et
# /usr/share/doc/opendkim/examples/opendkim.conf.sample pour complet
#  documentation of available configuration parameters.

Syslog			yes
SyslogSuccess		yes
# Logwhy Non

# Paramètres de signature et de vérification courants. Dans Debian, l'en-tête "de"
# surévisé, car c'est souvent la clé d'identité utilisée par les systèmes de réputation
# et donc quelque peu sensible à la sécurité.
Canonicalization	relaxed/simple
Mode			s
SubDomains		no
OversignHeaders		From

# Domaine de signature, sélecteur et clé (requis). Par exemple, effectuez la signature
# Pour le domaine "Exemple.com" avec sélecteur "2020" (2020._domainkey.example.com),
# Utilisation de la clé privée stockée dans /etc/dkimkeys/example.private. Plus granulaire
# Des options de configuration peuvent être trouvées dans /usr/share/doc/opendkim/readme.opendkim.
# Exemple de domaine.com
# Sélecteur 2020
# KeyFile /etc/dkimkeys/example.private

# Dans Debian, OpenDKIM s'exécute en tant qu'utilisateur "OpenDKIM". Un umask de 007 est requis lorsque
# Utilisation d'une prise locale avec des MTA qui accèdent à la prise comme un non-privilégié
# Utilisateur (par exemple, PostFix). Vous devrez peut-être ajouter l'utilisateur "postfix" au groupe
# "OpenDkim" dans ce cas.
UserID			opendkim
UMask			007

# Prise pour la connexion MTA (requise). Si le MTA se trouve à l'intérieur d'une prison de chroot,
# Il faut s'assurer que la prise est accessible. Dans Debian, Postfix court
# Un chroot dans / var / spool / postfix, donc une prise Unix devrait être
# configuré comme indiqué sur la dernière ligne ci-dessous.
# Douille locale: /run/opendkim/opendkim.sock
# Socket Inet: 8891 @ localhost
# Douille: 8891
Socket			local:/var/spool/postfix/opendkim/opendkim.sock

PidFile			/run/opendkim/opendkim.pid

# Hôtes pour lesquels signer plutôt que de vérifier, la valeur par défaut est 127.0.0.1. Voir le
# Section opérationnelle d'OpenDKIM (8) pour plus d'informations.
# Internalhosts 192.168.0.0/16, 10.0.0.0/8, 172.16.0.0/12

# L'ancre de confiance permet dnSsec. Dans Debian, le fichier d'ancrage de la fiducie est fourni
# par le package dns-root-data.
TrustAnchorFile		/usr/share/dns/root.key
# Nameservers 127.0.0.1

# Domaines de cartographie des adresses aux clés utilisées pour signer des messages
KeyTable           refile:/etc/opendkim/key.table
SigningTable       refile:/etc/opendkim/signing.table

# Un ensemble d'hôtes internes dont le courrier doit être signé
InternalHosts       /etc/opendkim/trusted.hosts

nano config/etc_default_opendkim
Et ajouter ces lignes

# Remarque: Il s'agit d'un fichier de configuration hérité. Il n'est pas utilisé par l'OpenDKIM
# SystemD Service. Veuillez utiliser les paramètres de configuration correspondants dans
# /etc/opendkim.conf à la place.
# 
# Auparavant, on modifierait les paramètres par défaut ici, puis exécuterait
# /lib/opendkim/opendkim.service.generate pour générer des fichiers de remplacement systemd pour
# /etc/systemd/system/opendkim.service.d/override.conf et
# /etc/tmpfiles.d/opendkim.conf. Bien que ce soit encore possible, c'est maintenant
# recommandé de régler les paramètres directement dans /etc/opendkim.conf.
# 
# Daemon_opts = ""
# Passer à / var / spool / postfix / run / opendendkim pour utiliser une prise Unix avec
# Postfix dans un chroot:
# Rundir = / was / spool / postfix / run / opendendkim
RUNDIR=/run/opendkim
# 
# Décommente pour spécifier une alternative
# Notez que le réglage cela remplacera n'importe quelle valeur de socket dans opendendkim.conf
# défaut:
SOCKET="local:/var/spool/postfix/opendkim/opendkim.sock"
# Écoutez sur toutes les interfaces sur le port 54321:
# Socket = INET: 54321
# Écoutez sur Loopback sur le port 12345:
# Socket = INET: 12345 @ localhost
# L'écoute est 192.0.2.1 est le port 12345:
# Socket = INET: 12345@192.0.2.1
USER=opendkim
GROUP=opendkim
PIDFILE=$RUNDIR/$NAME.pid
EXTRAAFTER=
Lorsque nous sommes prêts à configurer notre serveur Postfix, nous exécuterons le code ci-dessous, avec le nom de domaine approprié intégré. Commencez par créer un script

touch scripts/postfixsetup
sudo chmod a+x scripts/postfixsetup
nano scripts/postfixsetup
Maintenant, dans Nano, l'éditeur de texte, modifiez ce fichier afin qu'il comprenne votre nom de domaine au lieu de Femmebabe.com.

# ! / bac / bash
# Configuration du post-fixe
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}'
Maintenant, exécutez le script terminé pour configurer PostFix, OpenDKIM et DoveCot.

./scripts/postfixsetup
Une fois ce script exécuté, copiez la dernière ligne qu'il imprime et collez-la dans votre configuration DNS comme valeur pour sendonly._domainkey. Il s'agit de la clé OpenDKIM utilisée pour identifier votre domaine lors de l'envoi de courrier sécurisé. Génial! En quelques jours, vous devriez pouvoir envoyer du courrier du serveur à condition que tout soit configuré correctement. Si vous venez de configurer le DNS pour votre serveur de messagerie, cela devrait prendre moins de 72 heures pour les enregistrements à mettre à jour. C'est généralement beaucoup plus rapide. Vous pouvez vérifier si votre serveur travaille en utilisant cette commande, fourni votre e-mail:

echo “test” | mail -s “Test Email” youremail@gmail.com
Si tout semble fonctionner correctement, vous devriez pouvoir envoyer un e-mail avec votre serveur. Si cela ne fonctionne pas, essayez de regarder les journaux pour voir quelle pourrait être l'erreur.

tail –lines 150 /var/log/mail.log
Cela offrira des informations verbales sur le courrier envoyé par le serveur et si cela fonctionne correctement. Vous devriez également pouvoir voir l'e-mail dans votre boîte de réception, s'il n'est pas là, consultez votre dossier de spam. Vous devrez également configurer vos paramètres dans vos paramètres.py afin que votre serveur de messagerie puisse parler à votre application Django, le projet. Ajouter ou remplacer ces lignes dans vos paramètres

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)
Notez que nous utilisons un fichier de configuration pour obtenir le mot de passe. Chargez ce fichier dans les paramètres comme ainsi, au tout début du fichier.:

import os
import json

# Ouvrir et charger la configuration
with open('/etc/config.json') as config_file:
    config = json.load(config_file)
Créons ce fichier et ajoutons-y une clé secrète, ainsi que le mot de passe du courrier. Pour générer une clé secrète, utilisez cette commande, avec la longueur que vous aimez à la fin:

openssl rand -base64 64
Maintenant, copiez le texte qui s'ouvre généré et modifiez /tc/config.json

sudo nano /etc/config.json
Ajoutez les lignes suivantes à votre fichier, avec la clé qui s'ouvre générée comme clé secrète.

{
	"SECRET_KEY": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX-generated-using-openssl)",
	"EMAIL_HOST_PASSWORD": "yourpassword"
}
Le format JSON est simple et facile à utiliser, nous pouvons déclarer d'autres clés que nous voulons utiliser dans notre projet de cette façon, et les garder séparées de notre répertoire de projet afin que les autres utilisateurs ne puissent pas les écrire et donc ils ne peuvent pas être lus de notre répertoire de projet seul. Ceci est recommandé pour les clés API, dont nous en utiliserons plus que quelques-unes. Vous souhaiterez également sauvegarder votre projet pour vous assurer que tout est enregistré et que vous pourrez récupérer votre travail plus tard, même si vous ne souhaitez plus louer un serveur.

sudo backup
Maintenant, essayez d'envoyer un e-mail HTML à partir du serveur Web, à condition d'envoyer un à partir de la ligne de commande fonctionne. Interrogez votre instance utilisateur dans le shell et envoyez un e-mail HTML à cet utilisateur via Django. Changez mon nom dans le code, Charlotte, en votre nom d'utilisateur.

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()
Si la première commande ne fonctionne pas, assurez-vous d'utiliser

source venv/bin/activate
À condition que tout soit configuré correctement, vous recevrez désormais un e-mail de bienvenue dans votre boîte aux lettres envoyée par votre application Web. Bon travail! Vous avez parcouru un long chemin. Je voulais ajouter, si vous avez du mal avec des erreurs tout en travaillant sur un projet comme celui-ci, n'hésitez pas à rechercher des réponses et à demander de l'aide. Google, entre autres moteurs de recherche, sont d'excellentes ressources pour rechercher une aide en programmation. Recherchez simplement l'erreur que vous obtenez, et vous pourrez voir comment les autres résolvent le problème. En outre, vous êtes invités à me contacter, à vos éducateurs (enseignants, professeurs, tuteurs), à tous les pairs sur Internet qui sont disponibles pour la programmation d'aide, ou consultez à nouveau ce livre ou d'autres ressources pour trouver des solutions aux problèmes que vous rencontrez. Je comprends que ce n'est pas facile, mais même si vous avez lu jusqu'à présent et que vous n'écrivez aucun code, vous apprenez beaucoup sur la création d'une application Web à partir de zéro. Pat-toi sur le dos, tu fais un bienemploi. Merci d'avoir pris le temps de lire ce guide de développement Web de la troisième édition. Dans les futures éditions, j'inclurai davantage des exemples importants discutés au début du document et nous plongerons beaucoup plus profondément dans le monde du développement des logiciels et du matériel. Restez à l'écoute pour ce qui va arriver, et j'ai hâte de vous apprendre à créer des logiciels incroyables. Rendez-vous dans le prochain






Fermer
Page 1
Saut
Voir l'article complet
Continuer à lire

Acheter | Acheter avec crypto



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


(Cliquez sur ou appuyez pour télécharger l'image)
Divertissement professionnel, photos, vidéos, audio, diffusion en direct et gameplay occasionnel, ainsi que des services de numérisation d'identité, de développement Web et de maternité de substitution.

Laissez-moi un pourboire dans Bitcoin en utilisant cette adresse: 3KhDWoSve2N627RiW8grj6XrsoPT7d6qyE

© Glam Girl X 2025

Conditions de service