Aprendizaje profundo y seguridad prácticos basados ​​en la web con el ejemplo

DaisyFoto de perfil

Por Daisy

Práctico aprendizaje profundo basado en la web y seguridad con el ejemplo Tercera edición Charlotte Harper 3 de julio de 2024 Prefacio: Las consideraciones de seguridad en la creación de software para la web son una parte importante del plan y la ejecución del desarrollador web, mientras que ingeniería un prototipo que es confiable, estable y útil para fines prácticos. El DOM (marcado de objeto de documento), con su implementación de HTML, JavaScript y CSS, así como software de backend implementando Python, C/C ++, Java y Bash, brindan a los desarrolladores web la libertad y el poder de crear una amplia variedad de proyectos que expresan creatividad, proporcionar facilidad de uso y funcionalidad, retratar humildad y carácter, y proporcionar facilidad de uso, así como conveniencia y servicios importantes que son atractivos para el Joe promedio, el usuario final que busca Mata el tiempo o haga algo en Internet, generalmente en un dispositivo de teléfono inteligente con pantalla táctil. La mayoría de las personas ni siquiera sabrían por dónde comenzar cuando quieren construir un sitio web desde cero,Tenderán a comenzar en el sitio web de otra persona y construir algo limitado en funcionalidad, confiabilidad, facilidad de uso y especialmente creatividad cuando podrían haber tenido todas las últimas herramientas poderosas a su disposición para construir algo útil sin perder el tiempo presionando botones y Especialmente desperdiciando dinero pagando suscripciones costosas al software que pocas personas querían usar de todos modos dadas sus limitaciones en facilidad de uso y flexibilidad. Si tiene unos minutos para leer este libro y aprender lo que quiero enseñarle, o incluso hablar conmigo personalmente sobre sus objetivos y obtener alguna orientación en la dirección correcta, y está motivado para aprender a codificar y escribir su propio software. , Lleve este libro a casa y deje de lado un tiempo para aprender a construir la próxima aplicación web influyente, poderosa, aerodinámica e importante, un sitio web que se trata de usted y hace exactamente lo que desea y satisface las necesidades de su audiencia. Acerca de mí: Soy un desarrollador de software con un amplioAnge of Experience in C/C ++, Java, Python, HTML, CSS y JavaScript. Construyo sitios web que las personas quieren usar, quieren visitar e incluso me pongo adicto al uso solo para aprender, recrear y matar el tiempo, y lo más importante, vendo software. Si tuvo una idea de cómo quería que se viera y funcionara un sitio web, estaba dispuesto a apoyarme para que pueda satisfacer mis propias necesidades mientras me encuentro con las suyas, y está dispuesto a cubrir los costos de ejecutar un sitio web usted mismo, Te construiría el próximo YouTube, Tiktok, Twitter, Google o incluso una aplicación de seguridad de alta tecnología que solo usted pueda acceder. En lugar de tratar de venderte mi tiempo, estoy tratando de comprar el tuyo: quiero convencerte de que construir una aplicación (sitio web) tú mismo con la información que ya existe y enseñarte lo que necesitas para ser un desarrollador de software independiente, Emprendedor, liderando una carrera exitosa en cualquier campo que desee. Y déjame ser claro, la educación que te doy será informal. Podrías ir a la escuela y aprender todo esto con unLa educación RMAL, o incluso lee este libro en la escuela, complete sus tareas y elimine mucho de su educación, pero no lo pondré formalmente en el asiento caliente y le pediré que complete las tareas. No soy tu profesor, puedes pensar en mí como un amigo que quiere guiarte hacia una carrera impulsada por tu propio éxito personal. Y tampoco vendo su éxito, tendrá que comprarlo con su tiempo. Aprender a codificar tiene una curva de aprendizaje empinada y nunca fue fácil, o se suponía que fuera. Debe trabajar tan duro como sea posible y continuar intentando y no fallando e intentando nuevamente cuando esté frustrado para aprender y construir aplicaciones usted mismo. Eso está en la naturaleza del código en sí. El código está ejecutado por un compilador diseñado para darle al programador mensajes de error, y esto le enseñará cómo codificar, incluso si simplemente está copiando el error en su motor de búsqueda y leyendo los ejemplos de otras personas. Y debo decir que no necesitas ser extremadamente rico, inteligente, exitoso,En detalles orientados u organizados para construir una aplicación. La computadora se encarga de esa organización por usted. Solo necesita perseverar a través de la prueba y el error, mantener el enfoque y trabajar duro en lo que hace, y tendrá una carrera muy exitosa en la totalidad de lo que hace. Quien soy: Me doy cuenta de que la última sección fue más sobre el aprendizaje y que se llevan de manera de este libro. ¿Quién soy exactamente? Esa es una pregunta complicada. No estoy claro que yo mismo, ya que sufro de condiciones médicas que pueden dificultarme la codificación o escribir este libro a veces, al tiempo que presento desafíos con los problemas de socialización e identidad que me hacen la vida más difícil cuando se trata de presentarme a mí mismo. . En resumen, si está leyendo este libro, lo trajiste a casa porque lo hundiste y pensaste que era útil, o incluso si solo lees tan lejos, soy una persona de mentalidad similar que quiere verte tener éxito todo lo que haces. Yo mismo soy ingeniero, un softwaredesarrollador y estudiante, y estoy escribiendo este libro para otros estudiantes que desean facilitar la vida al tener un manual del software que necesitan facilitar la vida dando ejemplos para copiar que encajen como un gran rompecabezas en un trabajo que trabaja. , Aplicación útil, grande, funcional, cohesiva y atractiva que puede impulsar el éxito sin importar la línea de negocios. En gran medida, esto es lo que hago: construyo aplicaciones para ayudarme a mí y a otras personas a tener éxito. También soy autor, aunque esta es mi primera publicación que tengo la intención de completar para armar mi cartera en un documento útil, y también soy un artista. Te admitiré esto, soy una especie de persona extraña. No soy perfecto, he corrido con la ley incluso que me llevó a dejar colegios y universidades y dejar estados para tratar de hacerse un nombre con más éxito. Soy mujer de nacimiento, uso maquillaje, tomo fotos de mí mismo, uso vestidos y otras ropa para mujeres, y me mantengo consciente de mí mismo comohombre por naturaleza. He tenido problemas con otras personas en el pasado que conducen a las luchas con la escritura y la construcción de las aplicaciones web, y me disculpo por no haber podido poner este libro en sus manos antes: lo necesitabas. Querrá leer y escribir un código que se parezca al mío y funcione como el mío y haga lo mismo, pero aún mejor, porque si puede permitirse comprar este libro en lugar de mezclar su teclado como lo hago para crear un libro usted mismo para preguntar dinero. Para ello, tiene los recursos que necesita para tener éxito en su vida. Tuve todo tipo de problemas con el crecimiento familiar, las condiciones de salud, los médicos, los medios de comunicación y la ley, y mi código refleja profundamente la lucha que es el feminismo y la naturaleza femenina en un mundo dividido y frustrado. Sin embargo, este libro es algo que me importa profundamente, mi bebé, mi cartera y mi sustento, por lo que agradezco su consideración cuando lleva el texto a casa y cuidadosamente por encima de él para aprender de mí. Tenga en cuenta que no soy perfecto,El libro tendrá errores, revisiones y nuevas ediciones, y deberá pensar con su cerebro lógico lo mejor que pueda para tener una experiencia exitosa con mi escritura. Además, comprenda que quiero decir bien para usted incluso cuando enfrenta desafíos al escribir. Piénselo así: cuando puede alquilar un sistema informático para hacer cualquier cosa que pueda imaginar en el espacio digital, almacenar toda la información que encuentre, #$%! Yze y organizarlo, y llegar a comprenderlo, lo hará. Inevitablemente, encuentre dificultades con la información que está ingeriendo e incluso publicando. Te digo esto porque encuentro las mismas dificultades. Use este libro bajo su propio riesgo, trabaje con su comunidad y comunidades disponibles para construir un software dentro de un entorno seguro, y no lleve las cosas personalmente cuando fallas o incluso tengas éxito de la manera incorrecta: así es como llegué hasta aquí , y por qué puedo traerte este texto y ayudarte a tener éxito sin divergir en un camino de locura que dejaMe arruiné, desgarro y deshací mientras me encuentro con los problemas ordinarios que todos tienen a escala global gracias a la escala global paralelista de la red en la que trabajaremos, Internet. Es posible que no esté muy familiarizado con quién soy con solo unas pocas palabras, pero le animo a que siga leyendo, podrá conocerme mientras continúa leyendo y entendiéndome mientras construye sus propios proyectos para completar su trabajo. No habrá tarea con este libro, siempre que sus profesores o maestros no le asignen ninguna, pero le recomiendo que construya una cartera de proyectos usted mismo mientras lee, así como un proyecto de Capstone que muestra cómo puede Aplique lo que ha aprendido. Mi proyecto Capstone es la base de la mayoría de lo que leerá en este libro, ya que incorpora código de mis proyectos anteriores, código que he creado y aprendí a escribir metódicamente a mano, y una amplia gama de ideas y consejos que me han ayudado. tener éxito hasta el punto en que pueda girar una aplicación simple que esUly presentó, se ve y se comporta como una aplicación popular que puede ver a su amigo o familia que usa, en Internet, anunciado para usted o en las noticias. Que es este libro: Este libro es un tutorial con el ejemplo. Puede encontrar código aquí, instrucciones sobre cómo aprender a codificar, información sobre el código de depuración y solucionar errores, solucionar pasos, instrucciones sobre cómo hacer una copia de seguridad y guarda...
Aprendizaje profundo y seguridad prácticos basados ​​en la web con el ejemplo

Práctico aprendizaje profundo basado en la web y seguridad con el ejemplo Tercera edición Charlotte Harper 3 de julio de 2024 Prefacio: Las consideraciones de seguridad en la creación de software para la web son una parte importante del plan y la ejecución del desarrollador web, mientras que ingeniería un prototipo que es confiable, estable y útil para fines prácticos. El DOM (marcado de objeto de documento), con su implementación de HTML, JavaScript y CSS, así como software de backend implementando Python, C/C ++, Java y Bash, brindan a los desarrolladores web la libertad y el poder de crear una amplia variedad de proyectos que expresan creatividad, proporcionar facilidad de uso y funcionalidad, retratar humildad y carácter, y proporcionar facilidad de uso, así como conveniencia y servicios importantes que son atractivos para el Joe promedio, el usuario final que busca Mata el tiempo o haga algo en Internet, generalmente en un dispositivo de teléfono inteligente con pantalla táctil. La mayoría de las personas ni siquiera sabrían por dónde comenzar cuando quieren construir un sitio web desdeScratch, tenderán a comenzar en el sitio web de otra persona y construir algo limitado en funcionalidad, confiabilidad, facilidad de uso y especialmente creatividad cuando podrían haber tenido todas las últimas herramientas poderosas a su disposición para construir algo útil sin perder el tiempo de los botones y especialmente desperdiciar dinero pagando suscripciones costosas al software que pocas personas querían usar de todos modos dadas sus limitaciones en facilidad de uso y flexibilidad. Si tiene unos minutos para leer este libro y aprender lo que quiero enseñarle, o incluso hablar conmigo personalmente sobre sus objetivos y obtener alguna orientación en la dirección correcta, y está motivado para aprender a codificar y escribir su propio software. , Lleve este libro a casa y deje de lado un tiempo para aprender a construir la próxima aplicación web influyente, poderosa, aerodinámica e importante, un sitio web que se trata de usted y hace exactamente lo que desea y satisface las necesidades de su audiencia. Acerca de mí: Soy un desarrollador de software conRango de experiencia en C/C ++, Java, Python, HTML, CSS y JavaScript. Construyo sitios web que las personas quieren usar, quieren visitar e incluso me pongo adicto al uso solo para aprender, recrear y matar el tiempo, y lo más importante, vendo software. Si tuvo una idea de cómo quería que se viera y funcionara un sitio web, estaba dispuesto a apoyarme para que pueda satisfacer mis propias necesidades mientras me encuentro con las suyas, y está dispuesto a cubrir los costos de ejecutar un sitio web usted mismo, Te construiría el próximo YouTube, Tiktok, Twitter, Google o incluso una aplicación de seguridad de alta tecnología que solo usted pueda acceder. En lugar de tratar de venderte mi tiempo, estoy tratando de comprar el tuyo: quiero convencerte de que construir una aplicación (sitio web) tú mismo con la información que ya existe y enseñarte lo que necesitas para ser un desarrollador de software independiente, Emprendedor, liderando una carrera exitosa en cualquier campo que desee. Y déjame ser claro, la educación que te doy será informal. Podrías ir a la escuela y aprender todo esto con unLa educación formal, o incluso lee este libro en la escuela, complete sus tareas y elimine mucho de su educación, pero no lo pondré formalmente en el asiento caliente y le pediré que complete las tareas. No soy tu profesor, puedes pensar en mí como un amigo que quiere guiarte hacia una carrera impulsada por tu propio éxito personal. Y tampoco vendo su éxito, tendrá que comprarlo con su tiempo. Aprender a codificar tiene una curva de aprendizaje empinada y nunca fue fácil, o se suponía que fuera. Debe trabajar tan duro como sea posible y continuar intentando y no fallando e intentando nuevamente cuando esté frustrado para aprender y construir aplicaciones usted mismo. Eso está en la naturaleza del código en sí. El código está ejecutado por un compilador diseñado para darle al programador mensajes de error, y esto le enseñará cómo codificar, incluso si simplemente está copiando el error en su motor de búsqueda y leyendo los ejemplos de otras personas. Y debo decir que no necesitas ser extremadamente rico, inteligente,Essful, o incluso detalles orientados u organizados para construir una aplicación. La computadora se encarga de esa organización por usted. Solo necesita perseverar a través de la prueba y el error, mantener el enfoque y trabajar duro en lo que hace, y tendrá una carrera muy exitosa en la totalidad de lo que hace. Quien soy: Me doy cuenta de que la última sección fue más sobre el aprendizaje y que se llevan de manera de este libro. ¿Quién soy exactamente? Esa es una pregunta complicada. No estoy claro que yo mismo, ya que sufro de condiciones médicas que pueden dificultarme la codificación o escribir este libro a veces, al tiempo que presento desafíos con los problemas de socialización e identidad que me hacen la vida más difícil cuando se trata de presentarme a mí mismo. . En resumen, si está leyendo este libro, lo trajiste a casa porque lo hundiste y pensaste que era útil, o incluso si solo lees tan lejos, soy una persona de mentalidad similar que quiere verte tener éxito todo lo que haces. Yo mismo soy ingenierodesarrollador y estudiante, y estoy escribiendo este libro para otros estudiantes que desean facilitar la vida al tener un manual del software que necesitan facilitar la vida dando ejemplos para copiar que encajen como un gran rompecabezas en un trabajo que trabaja. , Aplicación útil, grande, funcional, cohesiva y atractiva que puede impulsar el éxito sin importar la línea de negocios. En gran medida, esto es lo que hago: construyo aplicaciones para ayudarme a mí y a otras personas a tener éxito. También soy autor, aunque esta es mi primera publicación que tengo la intención de completar para armar mi cartera en un documento útil, y también soy un artista. Te admitiré esto, soy una especie de persona extraña. No soy perfecto, he corrido con la ley incluso que me llevó a dejar colegios y universidades y dejar estados para tratar de hacerse un nombre con más éxito. Soy mujer de nacimiento, uso maquillaje, tomo fotos de mí mismo, uso vestidos y otras ropa para mujeres, y me mantengo consciente de mí mismo comomujer por naturaleza. He tenido problemas con otras personas en el pasado que conducen a las luchas con la escritura y la construcción de las aplicaciones web, y me disculpo por no haber podido poner este libro en sus manos antes: lo necesitabas. Querrá leer y escribir un código que se parezca al mío y funcione como el mío y haga lo mismo, pero aún mejor, porque si puede permitirse comprar este libro en lugar de mezclar su teclado como lo hago para crear un libro usted mismo para preguntar dinero. Para ello, tiene los recursos que necesita para tener éxito en su vida. Tuve todo tipo de problemas con el crecimiento familiar, las condiciones de salud, los médicos, los medios de comunicación y la ley, y mi código refleja profundamente la lucha que es el feminismo y la naturaleza femenina en un mundo dividido y frustrado. Sin embargo, este libro es algo que me importa profundamente, mi bebé, mi cartera y mi sustento, por lo que agradezco su consideración cuando lleva el texto a casa y cuidadosamente por encima de él para aprender de mí. Tenga en cuenta que no soyECT, este libro tendrá errores, revisiones y nuevas ediciones, y deberá pensar con su cerebro lógico lo mejor que pueda para tener una experiencia exitosa con mi escritura. Además, comprenda que quiero decir bien para usted incluso cuando enfrenta desafíos al escribir. Piénselo así: cuando puede alquilar un sistema informático para hacer cualquier cosa que pueda imaginar en el espacio digital, almacenar toda la información que encuentre, #$%! Yze y organizarlo, y llegar a comprenderlo, lo hará. Inevitablemente, encuentre dificultades con la información que está ingeriendo e incluso publicando. Te digo esto porque encuentro las mismas dificultades. Use este libro bajo su propio riesgo, trabaje con su comunidad y comunidades disponibles para construir un software dentro de un entorno seguro, y no lleve las cosas personalmente cuando fallas o incluso tengas éxito de la manera incorrecta: así es como llegué hasta aquí , y por qué puedo traerte este texto y ayudarte a tener éxito sin divergir en un camino de locuraAves me arruinó, desgarro y deshilaché mientras me encuentro con los problemas ordinarios que todos tienen a escala global gracias a la escala global paralelista de la red en la que trabajaremos, Internet. Es posible que no esté muy familiarizado con quién soy con solo unas pocas palabras, pero le animo a que siga leyendo, podrá conocerme mientras continúa leyendo y entendiéndome mientras construye sus propios proyectos para completar su trabajo. No habrá tarea con este libro, siempre que sus profesores o maestros no le asignen ninguna, pero le recomiendo que construya una cartera de proyectos usted mismo mientras lee, así como un proyecto de Capstone que muestra cómo puede Aplique lo que ha aprendido. Mi proyecto Capstone es la base de la mayoría de lo que leerá en este libro, ya que incorpora código de mis proyectos anteriores, código que he creado y aprendí a escribir metódicamente a mano, y una amplia gama de ideas y consejos que me han ayudado. tener éxito hasta el punto en que pueda girar una aplicación simple queTotalmente destacado, se ve y se comporta como una aplicación popular, puede ver a su amigo o familia usar, en Internet, anunciarse para usted o en las noticias. Que es este libro: Este libro es un tutorial con el ejemplo. Puede encontrar código aquí, instrucciones sobre cómo aprender a codificar, información sobre el código de depuración y solucionar errores, solucionar pasos, instrucciones sobre cómo hacer una copia de seguridad y guardar su código, volver a implementar si alguien rompe su código, asegura su código, implementa Su código, crea sitios web interactivos que sean entretenidos, atractivos y adictivos, y tendrá una idea de quién soy, por qué esto es importante y cómo retratarse a sí mismo, a su aplicación e imagen de la empresa, así como a El software que construye en la mejor luz absoluta para ser lo más atractivo posible para sus usuarios finales, los visitantes de su sitio web. En este libro, demostraré una serie de ejemplos de diseño de software con un enfoque en la web como una plataforma y seguridad. Iniciaremos la experiencia de aprendizaje construyendo unOject usando la carcasa Unix, con características de copia de seguridad y secuencias de comandos. Luego, examinaremos un sitio web básico de blog, actualizaremos nuestro blog con características de fotos y video, así como usaremos estas características para emplear soluciones de seguridad utilizando software gratuito y asegurar nuestro servidor utilizando un módulo de autenticación conectable (PAM). Luego revisaremos el manejo y el procesamiento de archivos, explorando la edición de video, la donación de voz, el escaneo de códigos de barras y el reconocimiento de caracteres ópticos, entre otros conceptos. En el camino, examinaremos las API que nos ayudarán a hacer que nuestro software sea más útil y seguro, con opciones gratuitas y pagas. En el camino, exploraremos la seguridad física y las herramientas militantes, como las armas de fuego y el diseño y la fabricación de municiones, incluido el diseño del barril y el repetidor, el diseño de torretas y drones, y otros directores que integraremos con nuestro software en la red existente para proteger nuestro software y demostrar la autodefensa y la reillabilidad. Tomaremos descansos en el camino para construir juegos, 2d y 3dEntrones que respaldan y trabajan con hardware integrado en estudio de caso de ejemplos de software de representación dimensional básica y un masajeador vibratorio electrónico fundido en caucho de silicona respectivamente. En el camino, también emplearemos soluciones de aprendizaje automático ya disponibles para asegurar mejor nuestro software. También emplearemos herramientas de stock disponibles para la Web para optimizar y asegurar el proceso. Este libro es una guía para su éxito en la creación de una aplicación web e integrarla con una red profesional de computadora y sistemas mecánicos integrados, y en general una guía para construir software y hardware integrado sin conocimiento de fondo o experiencia previa. Lo que este libro no es: Si realmente desea tener un sitio web, puede configurar una tienda simple y vender lo que necesita, publicar un blog, publicar fotos o videos o de otra manera sin escribir una sola línea de código. Este libro no es ese. Este libro le enseñará cómo crear un software que sea más útil, completamentedestacado, funcional y seguro que cualquier software que ya pueda encontrar, porque implementa el último software que todavía es prototipos, puede ser costoso de ejecutar a una escala en la que operan las compañías más antiguas y no atrae a las compañías en contra y en retraso de Gane dinero para personas que realmente no están haciendo nada. Si sigue este libro de cerca, querrá escribir código, código de investigación, crear sus propias aplicaciones y ganará dinero con lo que hace. Ganaré dinero con este libro, incluso en las primeras etapas, porque contiene información que las personas necesitan y quieren leer, y ya están comprando cuando compran o usan mis aplicaciones. Este libro no creará una aplicación para usted, pero le declarará en la dirección correcta y le armará las herramientas que necesita y las habilidades y consejos que facilitarán su propio éxito en la creación de software para la web, con cada línea de Código que deberá escribir como ejemplo, listo para ser reconstruido en el software que usted y sus seguidores, invitados, clientela,Los amigos, la familia, los visitantes, los contratistas y las personas de Internet quieren usar y apoyar. Lo que aprenderás: Este libro le enseñará cómo construir y vender software, realmente funcional, software útil, grabación de medios, características de seguridad como reconocimiento facial, escaneo de código de barras de zona legible de máquina, API web para autenticar, grabar y realizar videos e fotos, e intercambiar mensajes como Bluetooth y comunicación cercana al campo (NFC). Este libro le enseñará cómo usar una computadora en red, centrándose en Debian Linux, cómo construir el código BASH para que la instalación y el respaldo de su software sean una brisa perfecta y automatizada, cómo construir el código de Python como un backend para servir mensajes dinámicos, estilo Las cosas utilizan bien los estilos CSS con Bootstrap, habilitan los inicios de sesión de usuario e interactividad a través de dispositivos en red, creen medios interactivos y redes con otros sitios web para ofrecer características de seguridad como mensajes de texto para verificación o verificación o Otros propósitos, escaneo de identificación, moderación de imagen y video, datosRansacciones para mantener su software seguro, procesamiento de pagos, comercio de criptomonedas, tareas asincrónicas y más. Aprenderá a construir sus propios dispositivos Bluetooth, con baterías, cargadores, microcontroladores, circuitos, motores y sensores, usando soldadura, cable y 3D impresos, así como materiales fundidos. Demostraré a los principios de diseño 3D aplicados a la fabricación de herramientas y la fabricación de herramientas y matrices, por lo que puede fabricar sus propios dispositivos de hardware integrados con baterías integradas, cargadores, circuitos electrónicos y salidas funcionales. y contabilizarlos con Bluetooth y la web. Específicamente, examinaremos dos estudios de casos, un masajeador vibrante y un arma de fuego casera, ambos programados en OpenSCAD, que está disponible como una interfaz gráfica o utilidad de línea de comandos y puede integrarse en una web para obtener resultados más rápidos. Aprenderá cómo construir e implementar un sitio web desde cero sin experiencia previa, hacerlo funcional, seguro, hermoso, útil y másmortantemente práctico. Aprenderá cómo usar el aprendizaje automático y la visión por computadora para hacer que un sitio sea seguro y más práctico, grabar video y audio desde su sitio web, donar su voz, hacer música y modular audio para crear muestras útiles y cómo romper el ruido. Aprovechar otros sitios web para crear la mejor red posible de sitios web que pueda vincular directamente a los suyos para compartir toda la información útil que tiene para ofrecer y, lo que es más importante, traer a las personas a su software y negocio. Este libro se centrará más en los medios, la seguridad y el aprendizaje automático, que son los tres principales componentes que lo ayudarán a desarrollar un software útil para la Web al involucrar a los usuarios adecuados y desconectar a los incorrectos de una manera realista, práctica, práctica, práctica, práctica, práctica, práctica. Manos puestos y atractivos mientras también es automático y resistente. Este libro enseña Unix, específicamente Debian (Ubuntu), Bash Shell, Python, HTML, CSS, JavaScript y una serie de paquetes de software útiles paran Solicitudes, así como un software BASH útil como Git y FFMPEG. También le enseñaré cómo intercambiar criptomonedas automáticamente, y tomar pagos en criptomonedas o de tarjetas de débito regulares mientras incluso pagará a sus visitantes una parte de sus ingresos si elige hacerlo. También le enseñaré cómo ganar dinero con su sitio web a través de la publicidad, cómo preparar su aplicación para los motores de búsqueda y hacerlo rápido, clasificado en la primera clasificación para lo que sus clientes buscarán para encontrarlo y clasificando tantos comunes. Búsquedas como sea posible. Le enseñaré cómo vender su software, anunciarlo, atraer a los clientes que buscan sus servicios y hacer un nombre en Internet a través de avenidas que ya existen, son económicos y funcionan bien. Le enseñaré cómo guardar sus datos en computadoras en la nube que funcionan para usted y guardar sus datos a bajo precio, cómo planificar y crear un sitio web que haga lo que sus usuarios quieran y lo que desean, y cómo mantener a sus usuarios comprometidos conSu sitio se aleja en sus teléfonos con notificaciones, correo electrónico, mensajes de texto, llamadas telefónicas y más vías para llevar a sus usuarios a su sitio web a su disposición detrás del clic de un botón asegurado solo para usted. Este libro se centrará en la practicidad de publicar y distribuir medios en grandes cantidades, desde texto hasta fotos, videos y audio, causar una buena impresión en los usuarios finales (su clientela) y vender de cualquier manera que haga para crear Un sitio web, una aplicación que es representativa de usted y solo, y hace que usted, su software y su empresa se vean bien de la mejor manera posible. También aprenderá algunos consejos y trucos de mí, desde consejos de codificación, tocador práctico como maquillaje y fotografía, modelado y actuación, y más, lo que será importante para retratarse a sí mismo y a su empresa de la mejor manera posible utilizando todas las herramientas disponibles. para usted mientras distribuye tanto contenido como necesita en un equilibrio saludable de plataformas para traer sue para fructificar sin más esfuerzo, trabajo o dinero de lo necesario. Este libro se llama "aprendizaje profundo y seguridad basados ​​en la web prácticos con el ejemplo" por una razón: se trata de aprender a codificar, específicamente para la web, específicamente con un enfoque en la seguridad, desde un punto de vista práctico, con ejemplos de código de trabajo que sirve Los propósitos prácticos se describen en el texto. El componente de aprendizaje de este texto también abarca el aprendizaje automático, el código que le mostraré cómo ejecutar para la web que manejará la visión por computadora, el reconocimiento facial, la moderación de imágenes y videos, la mejora de la imagen, la mejora de la resolución, el subtítulos de imágenes y otras tareas como Las métricas de predicción provienen de imágenes, como la naturaleza de la imagen como una imagen auténtica, transferida por computadora o una copia óptica (una foto de una imagen o una foto impresa). El aprendizaje automático es muy importante cuando se trata de seguridad web y seguridad del software, ya que puede preformar tareas que de otro modo eran imposibles. Tu computadoraInicie sesión con un código de acceso, pero puede ser más seguro usarlo si lo inicia con su cara. Puede hacer que una computadora de servidor sea tan segura, una computadora que normalmente le pediría un nombre de usuario y un código de contraseña y lo inicia, tal vez con un token de confirmación para cada nuevo inicio de sesión o nueva dirección IP, pero si está construyendo a gran escala, fácil de Use, un software fundamentalmente seguro y potente, esto puede ser suficiente. Atar su software demasiado estrechamente con el software de otra persona, como un servicio de correo electrónico o servicio de mensajes de texto, no es suficiente para que su software sea seguro o el de cualquiera (cualquier sitio que use). Cualquiera que construya un software que sea impecablemente seguro tiene algún sentido de lo que esto implica. El software es inherentemente inseguro porque los dispositivos y las cuentas que usamos para acceder no siempre están a nuestra disposición, podrían estar en manos de cualquier persona con mala intención para el software y, por lo tanto, pueden representar un riesgo para el software en sí. Esto es algo del enfoque de este libro. Una computadora en red es de forma predeterminadaasegurado con un token de llave largo, llamado y clave SSH o shell segura, y de otra manera está mejor asegurado con un servidor web, porque el servidor web proporciona el acceso abierto y las herramientas de seguridad de última generación que se ejecutan en el servidor en sí. El servidor web tiene acceso al navegador web del usuario, que posiblemente es la parte más poderosa del dispositivo del usuario, porque es el lugar donde el usuario puede acceder al software en red. Este kit de herramientas puede representar el texto, las páginas web que ve, y también puede grabar imágenes, audio y video (como una foto de una cara o una identificación de estado), puede leer y escribir en dispositivos de radio Bluetooth, y puede leer y escribir en campo cercano Etiquetas de transponerador, tarjetas clave de bajo costo, FOBS, pegatinas, anillos e incluso implantes de chips con números de serie únicos que pueden leerse y escribir con datos generados y validados por un servidor web vinculado al sitio web. Usando todas las herramientas a su disposición, con este libro se equipará con el conocimiento para construir un sitio web seguro y, en general, unUre Sistema informático en red que funciona para usted, hace su oferta y se ve y se siente bien. Por dónde empezar: Puede saltar más allá de la sección que comience este libro con, o cualquier sección, al código exacto que necesita, especialmente si tiene experiencia con la codificación antes o cualquiera de las herramientas mencionadas que describiré en detalle en este libro como así como documentar casos de uso y ejemplos prácticos de los mismos. Si no tiene experiencia en el código de escritura, le recomiendo que lea todo este libro, y especialmente le recomiendo que lea las secciones anteriores, para asegurarse de que este libro sea adecuado para usted. Si este libro no es adecuado para usted, considere regalarlo a un amigo o pariente que podría estar interesado en aprender sobre el desarrollo web, e incluso considere tomar prestado y aprender de ellos para llenar los vacíos donde le fallé como El maestro u otros maestros hicieron delante de mí. Comience donde sea, cada parte de este libro será útil si tiene la intención de construir unPP, y considere que las mejores aplicaciones están construidas teniendo en cuenta el usuario final: conozca a su cliente. Ahora me conoces, conoces este libro y estás listo para comenzar. Para comenzar, tome una computadora (incluso la computadora portátil más barata de una tienda de cajas, Amazon, o un viejo escritorio funciona, y configúrela de una manera que funcione para usted. Cómo leer este libro: El texto resaltado, denota que el texto pertenece en un símbolo del sistema, donde escribirá el código que ejecuta. El símbolo del sistema está muy enfocado en el teclado y requiere poco o ningún clic, acelerar su flujo de trabajo y facilitarle las cosas. Empezando: Vamos a sumergirnos. Comenzaremos construyendo código en una máquina local y comenzaremos sin construir un sitio web conectado a Internet. Es más seguro comenzar, no cuesta nada y es fácil para usted. Dependiendo de su sistema operativo, entrar en un shell bash será un poco diferente. Para Mac OS, recomiendo instalar una máquina virtual en este momento, ya que obtendrá la mayor compatibilidad conmáquina virtual. Varios proveedores, como VirtualBox y Paralells, pueden ejecutar una máquina virtual para usted, aunque también es posible instalar Ubuntu directamente en la máquina, si prefiere usar un entorno nativo que se recomienda para crear una experiencia rápida y optimizada. Si está utilizando Linux o Windows, lo que recomiendo, debería ser bastante fácil crear un proyecto. Abra su terminal, ajuste el tamaño como mejore y comience a seguir el paso 2. Si está utilizando Windows, siga el paso 1. Paso 1: - Usuarios de Windows solamente En Windows, abra el símbolo del sistema como administrador y escriba WSL -Install Paso 2: - Continúe aquí o omita el paso 1 a aquí si no está usando Windows En un terminal abierto (dependiendo de su sistema operativo, llamado Ubuntu en Windows, terminal en Mac o Linux, o un nombre similar), comience creando un proyecto. Hacemos esto con el comando mkdir, que crea un directorio. Si necesita crear un directorio para almacenar su proyecto, que se recomienda, use elcomando de CD para cambiar al directorio y y CD/PATH/TO/Directory: la ruta es las carpetas (archivos) que preceden a su directorio de destino, su ruta predeterminada es ~ o/home/username (donde el nombre de usuario es su nombre de usuario). Para cambiar al directorio predeterminado, escriba CD o CD ~ Ejemplo de mkdir: reemplace el "ejemplo" con el nombre del directorio Ahora tiene un directorio de trabajo para su proyecto. Ser tan importante como es tan importante guardar este directorio en caso de que necesite cambiar a una máquina diferente o implementar el código que escriba para que esté listo para la web, construiremos un script para hacer una copia de seguridad de su directorio en los próximos pasos. Pero construir un script toma un poco de código, y el código debe ser automatizado para ser lo más útil posible. Así que construyamos primero un script para crear scripts. Comencemos creando el script y haciéndolo ejecutable. Usaremos sudo, chmod y touch para esto, y llamaremos al script


sudo touch /usr/bin/ascript
sudo chmod a+x /usr/bin/ascript
sudo nano /usr/bin/ascript
Ahora hemos creado el script, lo hicimos ejecutable y estamos listos para editarlo. Nano es un editor de texto que le permitirá editar texto sin hacer clic, lo que es mucho más fácil que usar una interfaz de usuario gráfica. Para editar un archivo con Nano, use Nano y luego la ruta al archivo. Para hacer un guión que haga un guión, es bastante similar a hacer nuestro guión en primer lugar. Usaremos el mismo código que el anterior, reemplazando el nombre del script, "Ascript" con un parámetro de argumento, $ 1. Esto nos permite llamar al script escribiendo Simply Sudo Ascript Newscript, momento en el que podemos crear cualquier nuevo script reemplazando el "periódico" con el nombre de su script. El código en Nano debería verse como:

sudo touch /usr/bin/$1
sudo chmod a+x /usr/bin/$1
sudo nano /usr/bin/$1
Y para cerrar Nano, podemos mantener presionada la tecla de control y presionar X, luego y para denotar estamos guardando el archivo y presione la devolución. Ahora, en lugar de escribir estos tres comandos para editar un script, podremos escribir sudo Ascript Ascript para editar el script nuevamente. ¡Esto funciona! Y cualquier nuevo script se puede ejecutar fácilmente llamándolo en el shell. Guardemos nuestro trabajo ahora: escribamos un script de copia de seguridad para guardar nuestro nuevo script y luego respaldarlo en nuestro directorio de proyectos, al tiempo que respalda el script de copia de seguridad.

sudo ascript backup
Ahora, en Nano:

sudo cp /usr/bin/backup /path/to/directory/
sudo cp /usr/bin/ascript /path/to/directory/
Donde/rath/to/directorio es la ruta al proyecto que creó con MKDIR. Más adelante aprenderemos a copiar rutas repetidas como esta con un bucle y una lista, que es menos código, pero por ahora mantengamos que sea simple y tengamos algunas líneas. Para ejecutar este script y hacer una copia de seguridad de su código, guarde el archivo en nano con control+x, y y regrese, y escriba el siguiente en su shell

backup
Si se le solicita una contraseña mientras lee este libro y sigue en el shell, ingrese su contraseña de usuario correctamente, tendrá tres intentos antes de que necesite volver a ejecutar el comando. Puede usar las flechas hacia arriba y hacia abajo para volver a ejecutar los comandos y editarlas, si necesita ejecutar algo dos veces. Presione simple hacia arriba y hacia abajo intermitentemente para seleccionar un comando, antes de editar el comando con las flechas derecha, izquierda y eliminar la tecla, así como el teclado, y ejecutarlo con return. ¡Felicidades! Se las arregló para crear un script de copia de seguridad increíble que respalde dos scripts de shell importantes en su directorio de trabajo. Podríamos mover las cosas más tarde a medida que el proyecto se hace más grande, pero esto funciona por ahora. Pasemos a hacer una copia de seguridad en la nube, usaremos GitHub para esto (aunque hay muchas otras soluciones GIT para la copia de seguridad, son todo lo mismo). software mientras los hace a un servidor, mientrasTambién le permite descargar copias completas de su software detrás de una contraseña o clave. Es fundamental para guardar su software, especialmente a medida que migramos a instancias de Linux aseguradas que a veces se rompen cuando falla una sola línea de código, dejándolo bloqueado mientras que su código podría no estar respaldado si no tiene la oportunidad de respaldarlo. arriba automáticamente, que cubriremos. Si aún no está utilizando una máquina virtual de Ubuntu en este momento, recomiendo usar una máquina virtual de Ubuntu en este punto porque le facilitará la vida al instalar todos los paquetes necesarios para crear un sitio web que funcione y preformar el aprendizaje profundo operaciones en su computadora. Moveremos el código a un servidor web en el futuro cercano, pero queremos asegurarnos de que haya al menos unas pocas capas de seguridad detrás de nuestro servidor web que sean resistentes al phishing y empleen una serie de paquetes de Linux para hacerlo. este. Si aún desea usar Mac OS, puede buscar e instalarE paquetes necesarios en línea, pero puede que no haya alternativas para cada paquete que este libro o serie cubrirá. Agreguemos algunos comandos para cometer nuestro trabajo con el script de copia de seguridad ejecutando el comando sudo ascript

# …
git add –all
git commit -m “backup”
git push -u origin master
Una vez más, controle X para guardar. Ahora necesitamos hacer una configuración única para este proyecto. Debido a que pronto será un proyecto GIT, no necesitamos escribir todos los comandos cada vez que implementemos desde un repositorio de Git, pero nos acostumbraremos a esto cuando escribamos nuestros scripts de implementación. Para comenzar, asegurémonos de estar en el directorio correcto e inicializar el repositorio de git y generar claves SSH.

cd /path/to/directory
git init
git branch -m master
ssh-keygen
Después de escribir ssh-keygen, la nueva clave debe guardar en la carpeta de inicio en una carpeta llamada .ssh. Se llama id_rsa.pub. Vamos a encontrar esta clave y copiarla. Para verlo,

cd ~
cat .ssh/id_rsa.pub
Copie el texto devuelto por el último comando y cree una cuenta con su proveedor Git (idealmente GitHub), antes de agregar la clave SSH a su cuenta. Una vez que tenga una cuenta, haga clic en el menú superior derecho e ingrese la configuración, antes de agregar su tecla SSH en las teclas SSH y GPG en el acceso en el menú. Seleccione Agregar una tecla SSH y agregue la suya pegándola y dándole un título, antes de guardar y regresar a GitHub para crear un nuevo repositorio. Esto es similar para otros proveedores de GIT, deberá leer su documentación. En la nueva configuración del repositorio, brinde a su repositorio un nombre descriptivo y decida si desea publicarlo y asegúrese de configurar aún no los archivos para su inclusión. Una vez que se crea el repositorio, copie el clon con URL SSH y péguelo en el siguiente comando.

git remote add git://… (your remote URL)
Ahora puede regresar a su repositorio con CD, estará familiarizado con esto. Prueba tu script de copia de seguridad ahora con copia de seguridad ¡Excelente! Ahora realmente podemos obtener la codificación. Instalemos Django ahora que tenemos una buena comprensión de Bash y Git. Django nos permitirá hacer una copia de seguridad automáticamente de nuestro software, Bash también puede hacerlo, pero Django debería tener una implementación más simple más segura (se puede deshabilitar y configurar más fácilmente). Para instalar software en Ubuntu, utilizaremos el comando sudo apt-get. Primero, actualicemos y actualicemos el software que ya teníamos. Esto se puede hacer con la actualización de sudo apt-get y la actualización de sudo apt-get -y. A continuación, instalemos Python y nuestro entorno virtual, el hogar de nuestro código, con el siguiente comando: sudo apt-get install python-is-python3 python3-henv Esto es todo lo que necesita para obtener con Django en términos de instalaciones de software en la instancia de Ubuntu. Para Windows y Linux, esto debería ser bastante sencillo, pero para Mac es posible que desee instalar una máquina virtual yLinux en él utilizando un entorno virtual gratuito o pagado como VirtualBox o Paralells Desktop y recree los pasos anteriores para configurar un entorno Ubuntu. Ubuntu es crítico en este caso porque es el software que ejecutan los sitios web y les permite alojar sitios web con todo el software antes mencionado. Cavemos en el Django. En nuestro directorio nuevamente, con

python -m venv venv # Crea el entorno virtual donde se almacena el código
source venv/bin/activate # Activa el entorno virtual
pip install Django
django-admin startproject mysite . # Donde MySite es el proyecto que estoy comenzando en mi directorio actual.
Django recién nos inicia, porque Django está alojando el servidor web y está haciendo todo lo que necesitamos para que un sitio web local básico esté en funcionamiento. Ahora que tenemos a Django instalado, editemos un poco la configuración para que funcione como necesitamos. Primero, creemos una nueva aplicación

python manage.py startapp feed
Notarás que la primera aplicación se llama Feed. La aplicación debe llamarse lo que desee, y crearemos nuevas aplicaciones, pero el nombre de cada aplicación debe ser consistente cada vez que se hace referencia a la aplicación en el código. Para agregar una nueva aplicación, siempre editaremos el settings.py en el otro directorio que se creó la aplicación, nombrada en StartProject, en adelante, la aplicación. Usando nano,

nano app/settings.py
En la configuración, encuentre instalado_apps y separe los [] en 3 líneas. Usando cuatro espacios en la línea central vacía, agregue 'feed' o el nombre de su aplicación. Esta sección de settings.py debería verse como:

INSTALLED_APPS = [
    'feed',
]
Antes de olvidar, probemos que Django está funcionando. Usando el comando python managem.py runServer 0.0.0.0:8000, podemos ejecutar el servidor y luego navegar en un navegador web en la computadora que ejecuta el código a http: // localhost: 8000 y ver una página web de ejemplo (¡funciona!) Renuncie al servidor con Control C, lo mismo que cualquier otro comando. Ahora, cavemos en la escritura de código Python. Django tiene tres componentes principales, todos ejecutados por código por completo. Los componentes se denominan modelo, vista y plantilla, y cada uno está en un nivel superior y más bajo respectivamente antes de que la página web se entregue al usuario. El modelo es el código que almacena información en la base de datos para recuperación, clasificación y representación. La vista decide cómo se representa, manipula y modifica el modelo, casi todas las vistas usarán un modelo directamente. La plantilla es el código HTML con algunas campanas y silbatos adicionales llamados lenguaje de plantilla. La plantilla se convierte en la vista donde está llena de código Python ycontexto como modelos e información (cadenas e enteros de USUALL) desde la vista. Django también tiene otros componentes, incluidos, entre otros:: Configuración, que configura la aplicación como discutimos. URLS, que son patrones que el usuario sigue para obtener acceso a partes específicas de la aplicación web. Formularios, que definen cómo la información que se envía al servidor se maneja y se representa a la base de datos, así como al usuario. Estas son la base del procesamiento de la información en el lado del servidor, y pueden aceptar cualquier tipo de información que las almacenes de la computadora, sobre todo, las cadenas de texto, los números y los booleanos verdaderos/falsos (generalmente casillas de verificación). Plantillas, que son código HTML y lenguaje de plantilla y cierran la brecha entre Python y HTML, lo que significa que la información de Python puede servirse como código HTML al que cualquiera puede acceder y puede asegurar un sitio web con acceso restringido, al tiempo que hace que el código de Python sea accesible para la Web y útil. para una variedad de propósitos en un dispositivo remoto que noeed para estar cerca del servidor. Archivos estáticos, que generalmente son JavaScript y sus bibliotecas a las que el servidor sirve y están vinculados con la plantilla. Archivos multimedia, a los que el servidor sirve o está alojado externamente, o simplemente se escriben al servidor antes de ser procesado y publicado en otro servidor (un cubo) para alojamiento. Middleware, que es piezas de código que se ejecutan al mismo tiempo que cada vista y se consideran "incluidos" en la vista. Procesadores de contexto, que procesan el contexto de cada vista y se utilizan para agregar un contexto adicional. Pruebas, que validan que el usuario o la solicitud pasa ciertos requisitos antes de que se represente la vista. Los consumidores, que dictan cómo las redes web se manejan y responden a la comunicación. Admin, que se utiliza para registrar modelos para que puedan manipularse en detalle dentro de la página de administración de Django, donde la base de datos se puede administrar a través de una interfaz gráfica. Celery, que define tareas asincrónicas, las partes del código Django pueden comenzarnning antes de proceder inmediatamente a la siguiente tarea o línea de código. Django puede tener muchos otros componentes, que discutiremos en detalle aquí. Hay muchas maneras de hacer que Django sea más funcional, agregando WebSockets, que son canales de comunicación rápidos y simplificados, apio, que ejecuta tareas asincrónicas y una multitud de otras piezas de software para extender el django, especialmente en las funciones de vista, donde la mayor parte de el código se ejecuta. Las funciones de vista son clave porque generalmente declaran cada pieza de código específico para un patrón de URL específico o una sección del servidor. Primero, exploremos las funciones de vista. Las funciones de la vista comienzan con las importaciones que denotan código que se utilizará en la vista, y se definirán utilizando definiciones o clases regulares de funciones. Las vistas más simples están definidas por la definición de función DEF, y devuelven un httpResponse con una plantilla básica. Comencemos definiendo una visión básica para devolver el texto "Hola mundo". Recuerda que cada vez que agregasPara una declaración como DEF, si, mientras, para, etc., necesitará agregar 4 espacios para cada una de las definiciones previas que desea aplicar a su función. Entraremos en lo que cada uno de estos significa pronto. Desde el directorio de nuestro sitio, edite el archivo Feed/Views.py con Nano y agregue las siguientes líneas al final del

from django.http import HttpResponse

def hello(request):
    return HttpResponse('hello world')
HTTPRESPONS de Django responde con una cadena de texto, denotada con la apertura y el cierre '. Cada vez que pasa información a una función o clase, como solicitud o una cadena, deberá usar paréntesis (, apertura y cierre). Esto no es todo lo que necesitamos para ver nuestra opinión todavía. Por supuesto, no le hemos dicho al servidor dónde está la vista exactamente, todavía necesitamos definir una ruta por la cual la vista debe ser representada. Comencemos definiendo una ruta básica en APP/URLS.py, y entraremos en grupos de ruta más tarde. En APP/URLS.py, agregue una línea después de las declaraciones de importación después del principio importando la vista que acabamos de crear.

from feed import views as feed_views
Ahora, definamos el patrón de vista. Los patrones de vista tienen tres componentes, el componente de ruta, que le indica al servidor dónde existe la vista dentro del servidor (la ruta de URL que el usuario escribe en la barra de navegación para ingresar a la página web), el componente de vista donde se especifica la vista y un Nombre amigable para la vista, por lo que es fácil recuperar su patrón cuando se trabaja con una plantilla, especialmente por lo que su nombre se puede cambiar y actualizarse si es necesario para hacer espacio para otra vista o adquirir un nombre más lógico. Tiene sentido hacer las cosas de esta manera y ser flexible, porque su base de código será un entorno siempre cambiante que necesita flexibilidad e improvisación para ser valioso y fácil de trabajar. Así es como se verá su punto de vista, puede agregar esto a UrlPatterns = [Sección de APP/URLS.py. El patrón de vista se define con los tres componentes descritos anteriormente, y una función llamada ruta. Sus patrones de URL son una lista, así que asegúrese de terminar siempre cada elemento en elloscon una coma, porque esto separa cada una. Cada elemento también debe ir a una nueva línea, una vez más con cuatro espacios antes, al igual que la aplicación en Settings.py. Definiremos el primer componente de la vista con una función de cadena vacía, para crear una vista que se ejecute en el directorio raíz del servidor web. Tus urls.py ahora deberían verse como

from feed import views as feed_views

urlpatterns = [
    path('', feed_views.hello, name='hello'),
]
Esta es la base para crear un sitio web con Django que es completamente estático. Para hacer un sitio web más dinámico donde podamos comenzar a almacenar en caché la información, como imágenes, videos, audio y más, necesitaremos usar modelos, que exploraremos a continuación. Por ahora, revisemos nuestro código y ejecutemos el servidor. Para verificar el código en busca de errores, ejecute:

python manage.py check
Si hay algún mensaje de error, debe revisar cuidadosamente los cambios que realizó en su aplicación y ver si hay algo que sea necesario, como un espacio extraño o que falta, un carácter adicional, una cadena no cerrada, cualquier error tipográfico, cualquier accidente accidentalmente personaje eliminado o cualquier otra cosa. Leer el mensaje de error (si tiene uno), debería poder ver la ruta a un archivo que creó o editó junto con un número de línea, así que busque ese archivo y línea y ver si puede solucionar cualquier cosa que esté allí. . Si ha solucionado el problema, ejecute nuevamente el comando anterior. Cuando su software está listo para ejecutarse y está funcionando, verá la salida "Comprobación del sistema no identificó problemas". Ahora estás listo para ir. Ejecute el servidor con:

python manage.py runserver 0.0.0.0:8000
Ahora abra un navegador web y navegue a http: // localhost: 8000. Debería ver el texto devuelto entre paréntesis y citas de la función httpresponse en su opinión. Este es solo un ejemplo básico, pero si llegó tan lejos, comprende los conceptos básicos de cómo funcionan Linux, Bash, Python y Django. Profundicemos en algún modelado de bases de datos y exploremos el poder de una clase de Python para almacenar información. Luego, comenzaremos a controlar HTML y CSS antes de hacer que nuestro sitio sea completamente destacado, flexible y seguro con JavaScript y Machine Learning. Las clases se almacenan en los modelos.py de su aplicación. Usando nano, edite aplicaciones/modelos.py y agregue una nueva clase. Una clase se define con la definición de clase y se pasa una superclase de la que hereda, en este caso Models.Model. El nombre de la clase viene después de la definición de clase, y después de la definición de clase A: (colon) se usa, antes de que los atributos y las definiciones de funciones vinculadas a la clase se denoten a continuación. Nuestra claseNecesita una identificación que podamos usar para recuperarlo y mantenerlo único, y también necesita un campo de texto para almacenar información. Más adelante podemos agregar una marca de tiempo, archivos, booleanos (definiciones verdaderas o falsas que pueden ayudar a nuestro código a tomar decisiones sobre qué hacer con el modelo, y puede usarse para ordenarlo), una instancia para vincular el modelo a un usuario registrado en el servidor, y más. Vamos a desempaquetar el código

from django.db import models # La importación que se utiliza para definir nuestra clase y sus atributos

class Post(models.Model): # La definición de nuestra clase misma
    id = models.AutoField(primary_key=True) # La ID de nuestro modelo, una clave generada automáticamente que nos permitirá consultar el modelo, mantenerlo único y es útil cuando necesitamos interactuar con el modelo una vez que se haya creado.
    text = models.TextField(default='') # El atributo de nuestras tiendas de clase, en este caso, algún texto, por defecto en una cadena vacía.
Cierre y guarde el archivo como lo hicimos antes para terminar. Hay muchos otros campos y opciones que exploraremos cuando actualicemos esta clase a medida que nuestra aplicación evoluciona, pero estas son las necesidades básicas de crear una aplicación para publicar algún texto. Sin embargo, este modelo no funcionará solo. Como se describió anteriormente, necesitaremos una vista personalizada y un patrón de URL personalizado para que este modelo funcione, y también necesitaremos un formulario junto con una plantilla. Exploremos primero el formulario. Para definir un formulario, edite App/Forms.py con Nano y agregue las siguientes líneas. Necesitaremos dos importaciones, nuestra clase de formularios, así como el modelo que creamos (feed.models.post), una definición de clase similar al modelo y un campo junto con una subclase llamada meta que definirá el modelo que interactúa el formulario con. El formulario también puede tener una función de inicialización que la establece en función de la información en la solicitud, modelo o de otra manera, exploraremos esto más adelante. Los formularios de modelo son muy útiles porque pueden crear un modelo o también editar un modelo,Entonces los usaremos para ambos. Definamos uno en Forms.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',)
Esto es lo básico de cómo se ve una forma y modelo. Este formulario de modelo se puede usar para instanciar o editar una publicación, cambiando el texto que contiene. Vamos a ver la integración de este formulario en una vista a continuación. Primero, hagamos las migraciones y migremos la base de datos para que nuestro código pueda interactuar con el modelo cuando se ejecuta. Para hacer esto, ejecute los siguientes comandos:

python manage.py makemigrations
python manage.py migrate
Esto tomará un minuto para ejecutarse, pero una vez que lo haga, le permitirá acceder al modelo en las vistas, el middleware o en cualquier otro lugar del software. Continuemos haciendo una vista donde podamos ver nuestro modelo. Edite Feed/Views.py y agregue el siguiente código, como se indica. No necesitará agregar nada después del signo #, ese código son comentarios que se utilizan para denotar información sobre el código. Comenzaremos importando nuestro modelo en las vistas y agregarlo a un contexto en el que podamos representarlo en una plantilla como lista para la visualización. A continuación, agregaremos una plantilla donde podamos representar el formulario y el modelo con un botón para crear un nuevo objeto basado en el modelo y publicarlo en el servidor. Esto suena complicado, así que vamos a darlo paso a paso. Antes de terminar la vista, creemos una plantilla que simplemente represente el modelo y asegúrese de que podamos verlo creando una nueva publicación en el shell. Así es como debería verse esa vista:

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

def feed(request):
    posts = Post.objects.all() # Consulte todas las publicaciones en la base de datos hasta ahora
    return render(request, 'feed/feed.html', {
        'posts': posts,
    })
Todo esto se ve bastante simple hasta que llegamos al fondo. Render, el valor devuelto por la función en lugar de en una respuesta HTTP como el ejemplo anterior, siempre toma una solicitud como su primera entrada, acepta un contexto (en este caso las publicaciones en la base de datos), que ahora se puede representar en la plantilla y devuelve la plantilla definida en la función. La plantilla será un documento HTML con un poco de lenguaje llamado Jinja2, que convierte la información de Python en el HTML. Para comenzar a crear plantillas, haga dos directorios en Feed.

mkdir feed/templates
mkdir feed/templates/feed
A continuación, edite una plantilla en el directorio anterior, alimentando/plantillas/alimentación, y agregue el código para este ejemplo. Veamos la plantilla para este ejemplo.
 
<!doctype HTML>
<html>
<body>
<legend>Feed</legend>
<hr>
{% for post in posts %}
<p>{{ post.text }}</p>
{% endfor %}
</body>
</html>
 
Esta es una plantilla muy simple. Define las etiquetas HTML de apertura y cierre, una etiqueta de tipo de documento, una etiqueta de cuerpo con un título de leyenda, una etiqueta de ruptura que agrega una pequeña línea en la pantalla y un bucle for que representa cada publicación en la lista de publicaciones como un párrafo en la plantilla. Esto es todo lo que se necesita para representar publicaciones, pero todavía no hay ninguna en la base de datos. Creemos algunos con el caparazón. Podemos ejecutar el shell con managem.py

python manage.py shell
Ahora, importemos nuestro modelo de publicación

from feed.models import Post
A continuación, crearemos una publicación simple con una cadena y saldremos de la carcasa. La cadena puede ser cualquier cosa, siempre que sea un texto válido.

Post.objects.create(text='hello world')
exit()
Por último, necesitaremos agregar un patrón de URL a nuestra alimentación. Debido a que nuestra aplicación Feed usará múltiples URL y queremos mantener pequeños los tamaños de archivos, creemos una URL local.

from django.urls import path
from . import views

urlpatterns = [
    path('', views.feed, name='feed'),
]
También necesitaremos editar las URLs.py en la aplicación base, como decidimos llamarlo, este fue el primer directorio que creamos. Editar App/App.py y agregar lo siguiente a los patrones de URL

from django.urls import include # en la parte superior

urlpatterns = [
    # ... Código anterior aquí
    path('feed/', include(('feed.urls'), namespace='feed')),
]
Ahora, cuando ejecutamos el servidor con Python Manage.py RunServer, veremos la página que creamos porque tenemos el modelo, la vista y la plantilla, así como el patrón de URL, junto con los elementos de la base de datos. A continuación, implementemos el formulario que creamos y comencemos a crear nuestras propias publicaciones. Pero antes de escribir demasiado código, hagamos una copia de seguridad usando el script que escribimos anteriormente, copia de seguridad. Ejecute este script en el shell, espere unos momentos, y todo el código estará respaldado a nuestro repositorio Git.

backup
Implementar el formulario es relativamente simple. Importaremos nuestro formulario, agregaremos un controlador de solicitud de publicación a la vista y guardaremos la publicación en la base de datos antes de redirigir a la misma vista. Podemos usar la función de redirección que ya importamos, y otra función llamada inversa para obtener la URL para el patrón de vista. Consultaremos esto con la cadena 'Feed: Feed' porque el espacio de nombres del patrón incluido es Feed, y la vista también se llama Feed.

from feed.forms import PostForm

def feed(request):
    posts = Post.objects.all() # Consulte todas las publicaciones en la base de datos hasta ahora
    if request.method == 'POST': # Manejar la solicitud de publicación
        form = PostForm(request.POST) # Cree una instancia del formulario y guárdelas los datos
        if form.is_valid(): # Validar la forma
            form.save() # Guardar el nuevo objeto
        return redirect(reverse('feed:feed')) # Redirigir a la misma URL con una solicitud GET
    return render(request, 'feed/feed.html', {
        'form': PostForm(), # Asegúrese de pasar el formulario al contexto para que podamos representarlo.
        'posts': posts,
    })
Ahora, tendremos que actualizar la plantilla para tener en cuenta el nuevo formulario. Podemos hacer esto usando el
Etiquete html y renderizado el formulario en la plantilla HTML con un botón de envío. También necesitaremos un token CSRF, un token que evite que los sitios externos se publiquen al formulario sin cargar primero una página.
 
<!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>
 
Desglosemos esto. Hay una nueva clase de formulario, un token, el formulario en sí y un botón de envío. Bastante simple, pero cuando lo echamos un vistazo, es posible que queramos que se vea mejor. Funciona, podemos publicar nuevas publicaciones con el formulario y ahora se guardan en la base de datos. Hay algunas cosas que están sucediendo aquí. Usamos etiquetas HTML para declarar que el documento es un documento HTML, usamos una etiqueta de plantilla ({ % ... %}) para representar el token para el formulario, y otra, {{...}} para representar el formulario. También tenemos un bucle para representar el texto usando etiquetas de bloque y una etiqueta de plantilla. Las etiquetas de bloque son realmente importantes porque podemos definir cómo se representan las secciones de la plantilla con ellas, y las etiquetas de plantilla son la base de cómo colocamos variables en nuestro código. Ahora necesitamos hacer que nuestra aplicación se vea mejor, porque por ahora se ve realmente básico. Podemos hacer esto usando CSS, ya sea en línea o en clases vinculadas a cada objeto en el documento. CSS es realmente agradable porque dice todo en la página cómo debería verse,y puede hacer que se vea realmente bien. Hay algunas bibliotecas que pueden hacer esto, pero mi personal personal es Bootstrap. Bootstrap se puede descargar desde su sitio web,getBootstrap.com/. Una vez allí, presione el botón para leer los documentos de instalación y copie el código de la sección incluir a través de CDN. Necesitará este código en la parte superior de su documento HTML, en una etiqueta llamada Head. Además, sigamos adelante y creemos una plantilla base para que no necesitemos recrear estos enlaces en cada plantilla. Haga un nuevo directorio llamado plantillas con plantillas MKDIR y luego edite plantillas/base.html. Debería verse así:
 
<!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>
 
Asegúrese de copiar los archivos CSS y JavaScript, los archivos .css y .js, porque necesitaremos el JavaScript para que nuestro sitio sea más funcional en el futuro. Ahora, volvamos al shell bash y ejecutemos un comando rápido. Recuerde, si alguna vez necesita acceder al entorno virtual, escriba la fuente Venv/bin/active. Esto le permitirá instalar paquetes de Python localmente de una manera que le permita acceder a Django. Para dar nuestras formularios generados por las clases de Bootstrap de Django, usaremos un paquete Python llamado Formas crujientes. Podemos descargar esto con el siguiente comando

pip install django-crispy-forms
Una vez que esto esté instalado, agrégalo a settings.py

INSTALLED_APPS = [
    # … Código anterior aquí
    'crispy_forms',
]
Ahora, de vuelta en nuestra plantilla de alimentación, podemos eliminar algunas cosas. Eliminemos el comienzo y el final del documento y lo reemplacemos con herencia de nuestra plantilla base, utilizando extensiones y la definición del bloque. Además, agregaremos una importación de filtro de plantilla con carga y un filtro de plantilla al formulario. Por último, agregemos una clase de arranque al botón en el formulario para que se vea más como un botón. Eso debería verse así:
 
{% 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 %}
 
¡Hermoso! Eso ya es bastante de código. A continuación, debemos probarlo y asegurarnos de que podamos ver que todo se ve bien, y también asegurarnos de que todo funcione correctamente. Ejecute el servidor según las instrucciones anteriores y asegúrese de que el sitio se vea y funcione bien. ¡Buen trabajo! Está listo para pasar al siguiente paso, en el que agregaremos la funcionalidad de inicio de sesión de usuario utilizando URL, formularios, vistas y plantillas similares. La plantilla base es importante, y continuaremos modificándola y haciendo cambios según sea necesario, pero por ahora centrémonos en hacer que nuestro sitio sea más seguro, permitiendo a los usuarios iniciar sesión con un nombre de usuario y un código de contraseña, y eventualmente información aún más importante que ayudará a mantener su aplicación segura y su propia cuenta accesible solo por usted. Para hacer esto, necesitaremos usar el modelo de usuario integrado en Django. El modelo de usuario es un modelo de base de datos, como nuestra publicación, que se puede representar para registrar un usuario en el sitio web. En el futuro, antes de implementar el sitio en Internet,Extienda este modelo con otros modelos atribuidos a él y cree medidas de seguridad adicionales para el inicio de sesión que son resistentes al phishing. Comenzaremos utilizando algunos formularios de inicio de sesión integrados que proporciona Django. Primero, creemos una nueva aplicación que usaremos para representar las plantillas y vistas para la página de inicio de sesión básica. También crearemos otras aplicaciones para representar los desafíos continuos de inicio de sesión para asegurar la aplicación, incluido un pintor, reconocimiento facial, comunicación de campo cercano, dispositivos externos, autenticación de factores múltiples y reconocimiento de huellas digitales. Ya hablamos sobre comenzar una aplicación. Desde nuestro directorio, dentro del entorno virtual, pase gestionar.py estos

python manage.py startapp users
Ahora, deberíamos tener un directorio para la nueva aplicación. Comencemos creando una vista en ese directorio que corresponde al inicio de sesión del usuario. Django ha incorporado vistas para inicios de sesión del usuario, pero estos no serán adecuados para nosotros porque necesitamos una vista personalizada, que preferiblemente se realiza con una definición. Desde esta opinión, comenzaremos por verificar una solicitud posterior, pasar la solicitud.post a una forma de inicio de sesión importada de Django, autenticaremos la cuenta de usuario e iniciar sesión en el usuario antes de redirigirlos a nuestra aplicación de feed. En ussers/Views.py, agregue el siguiente código

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'] # Obtenga el nombre de usuario y la contraseña de la solicitud de publicación
        password = request.POST['password'] # Autenticar al usuario
        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()})
Esto es todo lo que necesita para una vista básica de inicio de sesión. Ahora, creemos un formulario para la vista extendiendo la plantilla base. Comenzaremos creando un nuevo directorio para plantillas en la carpeta de usuarios.

mkdir users/templates
mkdir users/templates/users
Ahora, deberíamos poder editar usuarios/plantillas/usuarios/login.html. Mientras lo hacemos, crearemos una plantilla para permitir que el usuario también se registre.

nano users/templates/users/login.html
Ahora, en la plantilla,
 
{% 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 %}
 
Esto es lo básico de una plantilla de inicio de sesión. Realmente es como la otra plantilla de estructura, pero se ve un poco diferente cuando se representa. Podemos copiar este código para construir otra plantilla muy similar llamada Register.html, donde cambiaremos la redacción y usaremos un nuevo formulario que construimos. Hagamos primero la plantilla. Editar usuarios/plantillas/usuarios/registro.html y agregar el siguiente código:
 
{% 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 %}
 
Ahora, creemos un formulario para el registro de nuestro usuario y vuelva a las vistas antes de actualizar los inicios de sesión de nuestros usuarios con un modelo. Haremos este formulario básico para comenzar, pero incorporaremos más detalles y características de seguridad, como acuerdos y captcha en el futuro. Edite los formularios con nano usuarios/forms.py, y agregue el siguiente código.

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']
Así que tenemos otra forma aquí, que funciona de manera bastante simple. Es un formulario de registro de usuario con un nombre de usuario, correo electrónico y contraseña, así como un campo de contraseña de confirmación. Tenga en cuenta que este formulario no extiende las formas regulares. Class, es un formulario de modelo que significa que tiene un meta. Un campo se define de la misma manera, y el meta de clase define el modelo que el formulario corresponde al resto de la información que se escribirá en el formulario. La mayor parte de esto ya existe en la forma de creación de usuarios incorporada de Django, por lo que lo usaremos como base para la clase (aprobada entre paréntesis). A continuación, examinaremos la vista para registrar a un usuario, ahora que tenemos un formulario y una plantilla. Este es un ModeflaM, como el de la nueva vista posterior. Editar usuarios/vistas.py y agregar el siguiente código:

# … Importaciones
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})
Esto es todo lo que necesitamos para registrar un usuario, pero deberíamos tener más información. Queremos saber la vez que el usuario se registró, a qué hora fue la última vez que estuvieron en el sitio, cierta información sobre ellos, como una biografía, zona horaria, etc. Además, necesitaremos actualizar nuestro modelo de feed, publicar, para dar cuenta del usuario Modelo y publicaciones de atributos a cada usuario. Para hacer eso, actualizaremos los modelos.py en ambas aplicaciones. Comencemos editando el modelo de alimentación. Debería verse así ahora:

from django.db import models # ... cantidades
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') # Agregar en esta línea
    text = models.TextField(default='')
Preste atención a la segunda línea que se agregó al archivo. Esta es una clave externa, que atribuirá cada publicación a un solo usuario por publicación, por lo que podemos asegurarnos de guardar las publicaciones en base al usuario por usuario y no se puede hacer ninguna publicación sin atribuirla a un usuario. Definimos esta clave extranjera con la clase que representa, un argumento eliminado para garantizar que las publicaciones se eliminen con los usuarios, argumentos nulos y en blanco para asegurarnos de que podamos eliminar al usuario si es necesario, y para acomodar la falta de un usuario en las publicaciones que ya creado, y un nombre relacionado, que podemos usar para referirnos a los objetos de publicación que crea el usuario. Este nombre relacionado, a diferencia de Post.Author, el autor de la publicación, nos brinda un usuario que publicó la publicación en sí. Ahora podemos obtener las publicaciones de un usuario realizado ejecutando user.posts.alt (), o autor.posts.all (). Ahora, hagamos que nuestros inicios de sesión sean más resistentes. Ya podemos hacer que nuestro sitio sea mucho menos vulnerable al phishing simplemente calificando la limitación de la cantidad de veces que permitiremos iniciar sesión en elSitio, esto es bastante fácil. También comencemos a almacenar información sobre cada usuario antes a medida que continuamos desarrollando nuestra aplicación. Edición de usuarios/modelos.py, agregue lo siguiente

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='')
Tenga en cuenta que este modelo es bastante similar al modelo de publicación. Tenemos una importación adicional, TimeZone, que nos permitirá establecer los valores predeterminados en los campos de fecha y hora, y también tenemos un CartardFeild y Textfield como la publicación. El uso de todas estas marcas de tiempo nos ayuda a asegurar el sitio y comprender su uso, y los campos de texto nos permiten rendir información sobre cada usuario o autor en el sitio web. El Onetoonefield debería ser la única consideración menor, se comporta exactamente igual que un preestablecio pero con solo uno por modelo posterior. De esta manera, el usuario solo tiene un perfil, mientras que puede tener muchas publicaciones. Ahora, mejoremos nuestras vistas de inicio de sesión y registremos para tener en cuenta el perfil. Primero, edite usuarios/vistas.py y concéntrese en la vista de registro:

# ... cantidades
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) # Asegúrese de agregar esta línea para crear un perfil para el usuario
            messages.success(request, 'Welcome to the app, {}.'.format(user.username))
    return render(request, 'users/register.html', {'form': UserRegisterForm})
Esto simplemente crea un perfil para el usuario, sin completar ninguna información. Ahora, queremos asegurarnos de que la cuenta de usuario no pueda registrarse con demasiada frecuencia, o al menos las contraseñas no se pueden probar con demasiada frecuencia, así que actualicemos la vista de inicio de sesión.

# ... cantidades
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(): # Tenga en cuenta que ahora verificamos si el usuario puede iniciar sesión
            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 el inicio de sesión no fue exitoso,
            messages.warning(request, 'Username or password incorrect. Please try again.')
            user = User.objects.filter(username=username).first() # Esta es la parte donde actualizamos el perfil de los usuarios
            if user: 
                profile = user.profile
                profile.can_login = timezone.now() + datetime.timedelta(seconds=15) # Entonces no pueden iniciar sesión nuevamente por unos segundos
                profile.save()
    return render(request, 'users/login.html', {'form': AuthenticationForm()})
Este es el fundamental básico de la seguridad. Asegúrese de que el sitio no sea vulnerable a alguien que simplemente intente todas las combinaciones de contraseñas posibles, o incluso algunos de ellos al mismo tiempo. Esto no será frustrante para el usuario ordinario que conoce su código de acceso y solo inicia sesión en algunos dispositivos, pero mantendrá numerosos robots de phishing fuera de la aplicación. Tenga en cuenta que agregamos una instrucción IF con una variable, can_login, que debería ser un momento en el pasado, y actualizarla con cada inicio de sesión fallido utilizando el mismo nombre de usuario. De esta manera, un usuario malicioso no podrá adivinar una contraseña tan rápido. El número de segundos en Datetime.timedelta () también se puede actualizar, y el sitio web será más resistente pero un poco menos utilizable con más segundos. Recomiendo 15 para empezar. Recuerde, construimos un script de respaldo para guardar nuestro trabajo, así que sigamos adelante y retrocedamos lo que tenemos hasta ahora para asegurarnos de que tengamos todo guardado. Ejecute el comando:

sudo backup
Una vez más, esto salvará su trabajo hasta ahora. Recomiendo ejecutar copias de seguridad frecuentes para guardar su trabajo, e incluso es posible que desee ejecutar un trabajo de copia de seguridad automáticamente. Puede hacer esto usando una utilidad UNIX llamada Cron. Para activar esta utilidad, ejecute el siguiente comando e ingrese su contraseña:

sudo crontab -e
Si aún no ha seleccionado la opción 1 para Nano, el editor de texto con el que ya debe estar familiarizado y desplazarse a la parte inferior del archivo usando las teclas de flecha. Agregue la siguiente línea:

0 * * * * sudo backup
Cron usa el minuto de formato, hora, día del mes, mes, día de la semana, donde un * o un número representa cuándo ejecutar el comando. Usando un 0 para el minuto y * para el resto de las opciones, podemos ejecutar un comando en el primer minuto de cada hora al comienzo del minuto. Esto nos permite hacer una copia de seguridad del código automáticamente. Todos los trabajos de Cron cuando se ejecutan con sudo se ejecutan como root, por lo que no necesitaremos escribir una contraseña cada hora. Para facilitar la copia de seguridad de nuestro código sin usar una contraseña, deshabilitemos la contraseña para nuestro comando de copia de seguridad. Haremos esto ejecutando el siguiente comando e ingresando una contraseña:

sudo visudo
Ahora, desplazemos a la parte inferior del archivo y agregemos otra línea:

ALL ALL=NOPASSWD: /bin/backup
Esto nos permite ejecutar el comando "copia de seguridad" como cualquier usuario, sin contraseña. El formato para esto es fácil, solo prefije la línea con "all = nopasswd:/bin/" y termine con el comando, por ejemplo/bin/backup, que existe en/usr/bin/. Ahora, comencemos a trabajar con el correo electrónico. El correo electrónico es realmente importante para los sitios web, porque es una forma de mantener un sitio web más seguro, verificar que los usuarios son personas reales e incluso comercializar productos o servicios para los clientes. Muchas personas que frecuentan Internet revisan su correo electrónico diariamente y reciben todo tipo de correo electrónico de marketing sobre productos y servicios que están interesados. Hay algunas opciones cuando se trata de habilitar el correo electrónico en un sitio web de Django, y puede elegir lo que sea que funcione mejor para ti. Primero, puede pagar un servicio de correo electrónico que le permitirá enviar un correo electrónico desde su dominio y requiere un código mínimo. Hay muchos servicios que ofrecen esto, como Google Workspace, SendInblue, Mailgun y más. De lo contrario, estás bien construyendoSu propio servicio de correo electrónico dentro de su servidor desde cero. Recomiendo esta opción, a pesar de que es más código y puede requerir alojamiento especial. Lo más probable no podrá iniciar un servidor de correo desde la computadora de su hogar, así que sigamos adelante y examinemos la configuración y el código para enviar el correo electrónico antes de iniciar un servidor en la nube y crear nuestro propio servidor de correo dentro. Primero, edite settings.py con lo siguiente

nano app/settings.py
Donde la aplicación es el nombre de la aplicación que creó con StartApp. Agregue las siguientes líneas:

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)
Asegúrese de cambiarlos cuando esté listo para implementar su aplicación, volveremos a visitar esto más adelante. La configuración de correo electrónico_address debe ser el correo electrónico del que desea enviar, y la contraseña (Correo electrónico_host_password) debe establecerse en la contraseña que genere para el servidor. Cargo la contraseña de un archivo de configuración para mantenerla fuera del código utilizando la siguiente lógica, por encima de estas líneas en settings.py:

import os
import json
with open('/etc/config.json') as config_file:
    config = json.load(config_file)
Luego, configuré un archivo JSON con la configuración en /etc/config.json usando Nano de la siguiente manera. Para editar el archivo:

sudo nano /etc/config.json
Agregue las siguientes líneas:

{
	“EMAIL_HOST_PASSWORD”: “<some password here>”
}
Continuaremos editando el archivo de configuración y agregaremos todas las contraseñas y claves que usaremos en la aplicación. Por ahora, examinemos rápidamente cómo enviar correo electrónico con Python. Primero, creemos una plantilla para un correo electrónico de verificación que podamos enviar a nuestros usuarios y colocarla en el directorio de plantillas de usuario. Esta plantilla se escribirá 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>
 
Este correo electrónico es bastante simple. Se necesita un contexto de un usuario, la URL base para el sitio y una identificación de usuario y token que se utilizan para verificar el correo electrónico del usuario. Asegúrese de definir la URL base en Settings.py antes de escribir algún código Python para representar la plantilla. Continúe y agregue las siguientes líneas a App/settings.py, cerca del comienzo.

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

BASE_URL = PROTOCOL + '://' + DOMAIN
Eventualmente, cuando su sitio está listo para Internet y lo implementa, querrá definir su dominio como el nombre de dominio que compra para representar el sitio. Este es el nombre que escribirá en el Navbar para acceder a su sitio. Por ahora, puede dejar el dominio en blanco o usar un marcador de posición. También querrá cambiar el sitio_name a un nombre que desea darle a su sitio de su elección. Antes de enviar un correo electrónico, creemos un generador de tokens para que podamos tener un token de activación de cuenta que nunca caduque. Podemos hacer esto construyendo e importando un token de activación de cuenta que se vea como lo siguiente. Editar el archivo:

nano users/tokens.py
Agregue el siguiente código:

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()
Este generador de token básico genera un token que podemos enviar al usuario a una URL y el usuario puede usar para verificar su correo electrónico y activar su cuenta. A continuación, veamos cómo enviar un correo electrónico. Usando nano, edite usuarios/correo electrónico.py.

nano users/email.py
Enviar el correo electrónico HTML de verificación se verá así:

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)
Esto es bastante simple. Importamos las funciones que necesitamos para enviar el correo electrónico, realizar el correo electrónico con las plantillas y nuestra configuración, y luego definimos el correo electrónico por nombre de plantilla y lo enviamos al usuario utilizando una función. Notará que no hemos definido la función para enviar el correo, send_html_email, así que escribamos esto a continuación el código que ya agregamos a los usuarios/correo electrónico.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()
Esto es un poco más complejo, y todavía no estamos listos para ejecutar todo este código. Observe que estamos definiendo un unsub_link, el enlace que el usuario puede usar para cancelar la suscripción de nuestros correos electrónicos. Esto es importante, porque los usuarios deberán poder optar por no participar en nuestros correos electrónicos a menos que quieran verlos, en cualquier momento. También agregamos una alternativa de texto a nuestro mensaje, que es el mensaje HTML despojado de etiquetas HTML. Por último, verificamos si el correo electrónico enviado, y si no fue así, marcamos en el perfil del usuario que su correo electrónico no es válido. Volvamos a los modelos de usuario para que podamos hacer que todo funcione. Necesitamos definir una función para generar un enlace para darse de baja y definir un campo booleano para marcar que el correo electrónico del usuario no es válido. Primero, agregue las siguientes importaciones a la parte superior de los usuarios/modelos.py

nano users/models.py

# …
from django.core.signing import TimestampSigner, BadSignature, SignatureExpired
from django.urls import reverse
A continuación, agregemos funciones al modelo de usuario para hacer el token y verificar el token utilizado para activar el correo electrónico, así como el campo para guardar si el usuario recibe con éxito su correo. En ussers/modelos.py nuevamente, agregue el siguiente código al final del modelo (código sangrado)

# …
    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) # Válido por 30 días
        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,})
Esto es bastante simple, usamos un marcador de tiempo, que es una herramienta de criptografía básica, para crear un token que caducará después de una cierta cantidad de tiempo, y también usamos otra función para verificar si es válido. Usamos estos tokens dos veces, una vez para verificar el correo electrónico y una vez para un enlace de cancelación de suscripción. Ahora que tenemos estos, el último trabajo que tendremos que hacer es en las opiniones. Dentro de los usuarios/vistas.py, agregemos vistas para verificar la dirección de correo electrónico y cancelar la suscripción.

nano users/views.py
Primero, agregue las siguientes importaciones. Puse algunos extra para que no tendremos que importar más artículos más tarde.

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 # Asegúrese de importar la función de envío de correo electrónico de verificación
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
Es posible que ya tenga algunas de estas importaciones, pero no está de más repetirlas. Deberá importar la función de envío de correo electrónico de verificación, así como Account_Activation_Token desde ussers.tokens, entre otras importaciones. Ahora, en la parte inferior del archivo, agregue el siguiente código:

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)):
        # darles la baja
        profile = user.profile
        profile.subscribed = False
        profile.save()
        return render(request, 'users/unsubscribe.html')
    # De lo contrario, redirigir a la página de inicio de sesión
    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()
# SendwelcomeMail (solicitud, usuario)
        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})
Este es mucho código. Vamos a desglosarlo. La primera función, limpia y simple, da a suscripción al usuario de la lista de correo. La segunda función activa su correo electrónico y notará que agregué una función comentada, SendWelComeEmail. Puede usar una plantilla de correo electrónico y una definición de funciones para enviar un correo electrónico de bienvenida, todavía no lo he hecho. La última función que presenté es importante, porque los correos electrónicos de activación caducan. Por lo tanto, necesitaremos reenviar el correo electrónico de activación algunas veces. Podemos usar un formulario básico para esto y llamar a la función para enviar el correo electrónico de verificación. Antes de hacer esto, asegurémonos de que se envíe en primer lugar, agregando una llamada de función a la vista de registro. Agregue esta línea justo antes de la redirección en la vista de registro, el registro de DEF, en Uss/Views.py.

nano users/views.py

# … (Después) DEF REGISTR (Solicitud):
            send_verification_email(user)
# ... (antes) redirección (
No necesita agregar las primeras y últimas líneas en ese fragmento de código, solo asegúrese de que la vista de registro envíe el correo electrónico de verificación al usuario. Debería verse así:

# ... cantidades
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) # ¡Asegúrese de agregar esta línea!
            messages.success(request, 'Welcome to the app, {}.'.format(user.username))
    return render(request, 'users/register.html', {'form': UserRegisterForm})
Ahora, tendremos que agregar un formulario para reenviar el correo electrónico de activación. En ussers/forms.py, agregue el siguiente formulario:

# … (Importaciones)
class ResendActivationEmailForm(forms.Form):
    email = forms.EmailField(required=True)
También necesitaremos una plantilla correspondiente a este formulario de activación de correo electrónico de reiniciación. Agregamos esta plantilla. Edite el archivo:

nano users/templates/users/resend_activation.html
A continuación, agregue el siguiente código al archivo.

{% 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 %}
¡Vaya, eso es mucho! Ahora, cuando implementemos el código en nuestro servidor, podremos enviar un correo electrónico HTML y activar cuentas de usuario con un clic en el correo electrónico. También es posible que deseemos enviar un simple correo electrónico de bienvenida, así que veamos cómo hacerlo. Volver en ussers/correo electrónico.py, agregue el siguiente código:

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)
Además, necesitaremos una plantilla para representar toda esta información. En mi sitio web, la plantilla se parece a la siguiente, pero puede formatearla como desee.
 
<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>
 
Tenga en cuenta que no tenemos etiquetas de cierre de cuerpo o HTML, porque las agregamos cuando agregamos el enlace HTML cancelar suscripción. Estos son importantes, pero no queremos definirlos dos veces. Entonces, ¿qué sigue? Hemos recorrido un largo camino. Realmente, deberíamos estar listos para implementar el sitio en un servidor. Podemos agregar el decorador @login_required y hacer que nuestras vistas sean seguras, tomar registros de los usuarios, enviar un correo electrónico compatible e información de caché, que es la base de lo que un sitio web debe hacer para mantenerse relevante. Agregaremos algunas características más útiles y luego construiremos una base para implementar nuestro código en un servidor remoto, configurar un servidor de correo, configuración de dominio y filtros para hacer que nuestro sitio sea seguro y apropiado. También necesitaremos una vista de restablecimiento de contraseña, así que agregemos eso realmente rápido. La vista de reinicio de contraseña incorporada de Django está rota en algunas funciones, pero veremos cómo escribir nuestra propia vista, plantilla de correo electrónico, formularios y patrones de URL. Así es como se ve la vista, en usuarios/vistas.

# ... cantidades
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)
Este formulario está integrado en Django, pero necesitaremos una plantilla para confirmar el restablecimiento de contraseña, usuarios/plantillas/usuarios/contraseña_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 %}
 
También tenemos una plantilla para enviar un correo electrónico de restablecimiento de contraseña, con un formulario simple, en usuarios/plantillas/usuarios/contraseña_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 %}
 
La plantilla para el correo electrónico en sí es simple, es un archivo HTML básico que representa un enlace para restablecer la contraseña, en usuarios/plantillas/usuarios/contraseña_reset_email.html. Django interpretará automáticamente este archivo.
 
<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>
 
También necesitaremos dos plantillas más. El primero es confirmar que se ha enviado el correo electrónico. Las vistas para estos ya están en Django, por lo que solo necesitamos abordarlas en las URLs.py. Esta plantilla se encuentra en usuarios/plantillas/usuarios/contraseña_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 %}
 
Y, por último, para confirmar que el reinicio de la contraseña está completo, usuarios/plantillas/usuarios/contraseña_reset_complete.html
 
{% extends 'base.html' %}
{% block content %}
 <div class="media-body">
    <div class="alert alert-info">
        Your password has been set.
    </div>
    <a href="{% url 'users:login' %}">Sign In Here</a>
  </div>
{% endblock content %}
 
Ahora, necesitamos patrones de URL para estas vistas. En usuarios/urls.py, agregue los siguientes patrones de URL:

urlpatterns = [
    # ... URL anteriores aquí
    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'),
]
Cuatro plantillas, ¡eso es mucho! Pero ahora podemos estar seguros de poder restablecer la contraseña del usuario en cualquier momento que necesitemos, todo del navegador web. Entiendo que este es mucho código. Si parece un poco sobre tu cabeza, está bien. Mejorará, su comprensión mejorará y se volverá mucho más competente con el código muy pronto. Si está totalmente perdido, le recomiendo volver a este software más adelante después de trabajar en un curso de aprendizaje a su propio ritmo a codificar en línea. Por lo general, son gratuitos para comenzar, y lo guiarán a través de todo lo que necesita para tener éxito cuando regrese a este proyecto. Si siente que está listo para continuar, siga leyendo, a continuación, cubriremos la implementación de su código en un servidor remoto y configurar un servidor de correo, así como automatizar su implementación con BASH para que siempre pueda configurar un nuevo proyecto con Algunos comandos simples. Lo último que debemos hacer antes de implementar en un servidor remoto es hacer que nuestro sitio sea un poco más seguro. TuObserve que la vista de inicio de sesión solo requiere un nombre de usuario y contraseña, y no hay autenticación de factor múltiple o código de tiempo. Esta es una solución fácil, y con el mismo código, podemos hacer que nuestro sitio envíe mensajes de texto e incluso responda a los mensajes de texto enviados al servidor. Para comenzar, volveremos a los modelos de usuario y agregaremos un firmante de marca de tiempo que represente cada inicio de sesión. También agregaremos un identificador único y giratorio al modelo de usuario que se utilizará para agregar seguridad adicional a nuestro inicio de sesión. Edición de los modelos de usuarios, usuarios/modelos.py, agregue lo siguiente

from django.db import models
from django.contrib.auth.models import User
from django.utils import timezone
# Asegúrese de importar el UUID, el firmante de marca de tiempo y el generador de URL (reverso)
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='')
    # Agregue este código aquí
    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)

    # Y agregar esta función
    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) # Válido para 3 minutos
        except (BadSignature, SignatureExpired):
            return False
        return True
Asegúrese de que sus usuarios/modelos.py se vean así, además de los comentarios (código en las líneas con #). Rompiendo esto, es simple. Tenemos algunas importaciones, un marcador de tiempo de tiempo que es una utilidad criptográfica que puede generar un código seguro y verificarlo para asegurarse de que sea válido, solo se usa una vez, y no mayor de un cierto número de segundos. También usamos un UUID, que es un identificador único que identifica a nuestro usuario en la firma del token y en la URL donde se envía el token al usuario. Usaremos esta criptografía básica para construir una vista de autenticación de dos factores. Antes de hacer cualquier otra cosa, ejecutemos las migraciones para que nuestros modelos de usuario se actualicen. En el directorio con Manage.py, ejecute los siguientes comandos para hacer y completar las migraciones.

source venv/bin/activate
python manage.py makemigrations && python manage.py migrate
Esto es importante porque cada vez que realizamos cambios en los modelos, necesitaremos crear las tablas y actualizar la base de datos con valores predeterminados antes de poder usar los modelos. A continuación, improvisemos nuestra vista de inicio de sesión para redirigir a una vista de autenticación secundaria. En usuarios/vistas.py, elimine la función de inicio de sesión y redirige a la URL que acabamos de generar en los modelos de usuario.

# … Importaciones

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(): # Tenga en cuenta que ahora verificamos si el usuario puede iniciar sesión
            # Elimine la función Auth_login que estaba aquí
            messages.success(request, 'Your password was accepted. Please continue.')
            if user.profile.mfa_enabled:
                return redirect(user.profile.create_auth_url()) # Tenga en cuenta que redirigimos a una nueva URL aquí
            else: # Si el usuario no está usando autenticación multifactor, simplemente inicie sesión.
                auth_login(request, user, backend='django.contrib.auth.backends.ModelBackend')
                return redirect('feed:feed')
        else: # Si el inicio de sesión no fue exitoso,
            messages.warning(request, 'Username or password incorrect. Please try again.')
            user = User.objects.filter(username=username).first() # Esta es la parte donde actualizamos el perfil de los usuarios
            if user: 
                profile = user.profile
                profile.can_login = timezone.now() + datetime.timedelta(seconds=15) # Entonces no pueden iniciar sesión nuevamente por unos segundos
                profile.save()
    return render(request, 'users/login.html', {'form': AuthenticationForm()})
Así que esto es bastante simple, ahora tenemos una manera de redirigir a la vista de autenticación de dos factores cuando la creamos. También tenemos una devolución en caso de que el usuario no haya agregado un número de teléfono. Agregaremos una vista básica para agregar un número de teléfono pronto e iniciar sesión con un mensaje de texto pronto. Primero, necesitamos una manera fácil de enviar un mensaje de texto desde nuestro código. Para hacer esto, podemos elegir entre una serie de API, pero la más fácil en mi opinión es Twilio. También ofrecen buenos precios para proyectos más pequeños, así como descuentos masivos. Cree una cuenta en twilio.com, complete algunos detalles sobre su proyecto, compre un número de teléfono y copie sus claves API a su configuración.py. Luego, agregue este código en un nuevo archivo, usuarios/sms.py.

nano users/sms.py

# Importar todos los paquetes necesarios
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

# Este código envía el texto con 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())

# Una función de ayudante para obtener un número con tantos dígitos
def get_num_length(num, length):
    n = ''
    for x in range(length):
        n = n + str(num)
    return int(n)

# Envíe el texto para verificar al usuario
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)))

# Envíe a un usuario cualquier texto con esta función
def send_user_text(user, text):
    send_text(user.profile.phone_number, text)

# Validar el código con esta función
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

# Validar el tiempo
def check_verification_time(user):
    result = user.profile.mfa_code_expires > timezone.now()
    return result
Asegúrese de cambiar su configuración adecuadamente, agregando estas líneas con sus claves:

# Asegúrese de copiarlos desde su tablero 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 # El número de minutos la página TFA está activa una vez instanciado
Primero, necesitaremos formularios para nuestras dos vistas de autenticación de factores. Edición de usuarios/form.py, agregue el siguiente código.

# ... cantidades
from django import forms

# Un formulario para ingresar a nuestro número de teléfono
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

# Una forma para autenticar
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.'
    }
A continuación, creemos las vistas en usuarios/vistas.

# … Importaciones
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})
También necesitaremos plantillas para ambas vistas. Agregamos primero la plantilla MFA.

nano users/templates/users/mfa.html
Agregue este código HTML a la plantilla
 
{% 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 %}
 
Esto se explica por sí mismo. El formulario envía un código o un código vacío, y notará en la vista que enviamos el código si recibimos un código vacío. Luego solo tenemos dos botones de envío, y de esta manera podemos enviar el código con cualquier botón. A continuación, agregaremos un formulario simple para agregar un número de teléfono.

nano users/templates/users/mfa_onboarding.html
Agregue el siguiente HTML:
 
{% extends 'base.html' %}
{% block content %}
{% load crispy_forms_tags %}
        <form method="POST">
            {% csrf_token %}
            <fieldset class="form-group">
                <legend class="border-bottom mb-4">Set Up Two Factor Authentication</legend>
                {{ form|crispy }}
            </fieldset>
            <div class="form-group">
                <button class="btn btn-outline-secondary" type="submit">Add phone number</button>
            </div>
        </form>
{% endblock %}
 
Este formulario es mucho más simple, solo hace que el formulario de número de teléfono que creamos y le permite al usuario agregar un número de teléfono. ¡Esto se ve muy bien! Mientras todo esté configurado correctamente, deberíamos poder enviar mensajes e registrar al usuario con su número de teléfono tan pronto como agregamos los patrones de URL. Lo último que debemos configurar es una vista de perfil para que podamos asegurarnos de que el usuario pueda cambiar su número de teléfono sin haber sido registrado. Además, eventualmente querremos agregar una opción de "parar para dejar de fumar, para que el usuario pueda enviar mensajes de texto. "Detente" para optar por no tener mensajes de texto futuros. Agreguemos una vista de perfil a los usuarios/vistas. Esta vista actualizará el biografía, el correo electrónico, el nombre de usuario y el número de teléfono del usuario, así como nos permitirá habilitar la autenticación de múltiples factores. Primero, necesitaremos dos formularios más en usuarios/formaciones.

# ... cantidades
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']
A continuación, podemos crear una vista para usar ambos formularios. Editar usuarios/vistas.py y agregar la vista.

# Agregue estas importaciones
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)
También necesitaremos una plantilla para esta vista.

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 %}
 
Notará que esta es una forma bastante simple, pero tiene un JavaScript que publica automáticamente el contenido del formulario a medida que se actualizan. Es útil tener esto, por lo que puede hacer ediciones sin tener que presionar Subting Every Time. A continuación, necesitamos URL que representen todas estas vistas en los patrones de URL de los usuarios. Editar usuarios/urls.py y agregar este código:

# ... Código anterior, importaciones
from django.urls import path
from . import views

app_name='users'

urlpatterns = [
# ... Patrones de URL en la que ingresamos anteriormente, agregue las siguientes tres líneas
    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'),
]
Ahora es un buen momento para probar nuestro proyecto. Pero primero, ejecutemos otra copia de seguridad.

backup
Y ejecuta el servidor. Antes de desplegar en un servidor de Linux, es una buena idea habilitar la autenticación de dos factores en la cuenta. Lo haremos yendo a nuestra URL de perfil,/usuarios/perfil/, y verificaremos el cuadro para habilitar la autenticación después de ingresar nuestro número de teléfono, y luego enviar el formulario.

python manage.py runserver localhost:8000
Visite la página web yendo a su navegador web, estoy usando Google Chrome en este ejemplo e ingresando la URL https: // localhost: 8000/cuentas/perfil// Podrá iniciar sesión si es necesario y habilitar la autenticación de dos factores. Este proyecto necesita un servidor para ejecutar para que realmente pueda enviar correo. Pero primero, necesitamos una forma de ver errores. Notará que si ejecuta el servidor en modo de depuración, con settings.debug igual a verdadero, el servidor muestra errores automáticamente. Para mostrar errores sin usar el modo de depuración, que no es seguro en un servidor de producción, debemos agregar una vista para ello. Los errores más importantes que necesitamos poder manejar son: Error 500: un problema con nuestro código Error 404: una página que no se encontró (URL rota) Error 403: un error denegado por permiso Agreguemos una nueva aplicación para manejar estos errores, llamados errores.

python manage.py startapp errors
Agregue esto a Settings.py como lo hicimos antes, en la configuración Installed_Apps, y comience agregando referencias a algunas vistas en APP/URLS.py, donde la aplicación es el nombre de su proyecto Django.

handler404 = 'errors.views.handler404'
handler500 = 'errors.views.handler500'
handler403 = 'errors.views.handler403'
Esto es todo lo que necesitamos además de vistas de error, plantillas y un poco de middleware. Definamos los que así:

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

# Crea tus puntos de vista aquí.
@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.'})
A continuación, definamos el middleware para manejar estos errores. Lo haremos primero agregando a middleware_classes en settings.py, con el nombre de nuestro middleware.

MIDDLEWARE_CLASSES = [
    # ... Middleware anterior
    'errors.middleware.ExceptionVerboseMiddleware,
]
A continuación, agregemos el 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.')
Agregamos una función para obtener la excepción actual utilizando un hilo local, lo que nos ayuda a rastrear cualquier error en nuestro código. En términos de plantillas, solo necesitamos una, porque definimos dinámicamente el título en la vista. La plantilla solo necesita representar el título y "trazar", nuestro rastreo de error desde el contexto.

nano errors/templates/errors/error.html
 
{% extends 'base.html' %}
{% block content %}
<h1>{{ pagetitle }}</h1>
<p>{{ trace }}</p>
{% endblock %}
 
Esta es nuestra plantilla más simple hasta ahora, pero así es lo fácil que es ver los errores en nuestro proyecto. A continuación, desactivemos la depuración en la configuración.

nano app/settings.py
Encuentra esta línea donde se establece en verdadero y cámbiela a falso

DEBUG = False
Continúe y haga una copia de seguridad de la aplicación ahora. Estamos listos para implementar en un servidor remoto de Linux y seguir agregando funciones desde allí.

sudo backup
Antes de publicar este código en un servidor, debemos considerar que puede haber algunos problemas con el código. Dependiendo del caso, los sitios que acepten información que se les publiquen tendrán problemas con el spam que se publica y la dificultad para eliminar el spam. Esto no debería suceder de inmediato, pero si está sucediendo, luego examinaremos cómo moderar automáticamente el spam en el sitio y dificultar que los robots accedan al sitio, junto con cómo desactivar las cuentas de los usuarios y verificar la identidad de un usuario con Un escaneo de su identificación o una exploración biométrica, como una huella digital o reconocimiento facial. Mirando el ejemplo de autenticación de factores múltiples que examinamos, en la producción, las cosas pueden ser diferentes. Observe cómo estamos calificando los inicios de sesión y tokens que vencen. Si los robots están accediendo a un sitio, la autenticación de dos factores puede ser más difícil, ya que pueden ingresar códigos al mismo tiempo que lo es el usuario. Para combatir esto, usemos un modelo en los modelos de usuario, declarando cómo interactuamos con el sitio cuando estamosAutenticación utilizando autenticación de factores múltiples con un número de teléfono. También agregaremos una opción para autenticarnos con el correo electrónico. Comience editando los modelos de usuario con

nano users/models.py
Así es como debería ser el modelo que estamos agregando. No necesitamos ningún método, solo variables para almacenar una identificación, el usuario, la marca de tiempo, la expiración, la longitud e intentos contra cualquier autenticación de factores múltiples (un código como 123456 enviado a un teléfono o correo electrónico).

# Un token básico utilizado para iniciar sesión en el sitio 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)
También agregemos un privilegio a nuestro usuario, y lo configuraremos manualmente por ahora, antes de eventualmente migrar a los usuarios privilegiados automáticamente. En los modelos de usuario, agregue esta línea en el perfil:

    vendor = models.BooleanField(default=False)
Al igual que con cualquier cambio en la base de datos, necesitamos hacer migraciones y migrar la base de datos en cualquier momento que editemos un archivo modelos.py en Django. Recuerde, para hacer esto usamos primero la fuente (si aún no se ha utilizado desde que el terminal estaba abierto) y luego Python Managem.py para hacer las migraciones y migrar.

cd project-directory-you-named # (si es necesario)
source venv/bin/activate
python manage.py makemigrations && python manage.py migrate
Por ahora, puede solicitar cualquier cuenta que haya creado como proveedores utilizando el shell.

python manage.py shell
from users.models import Profile
p = Profile.objects.get(user__username='Charlotte')
p.vendor = True
p.save()
exit()
Ahora, evolucionemos nuestra visión de autenticación de factores múltiples para usar este token. Primero, necesitamos modificar nuestras utilidades de ayudante MFA. Usando 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

# Autenticar al usuario usando su correo electrónico o número de teléfono
def mfa(request, username, usertoken):
    token = MFAToken.objects.filter(uid=username, expires__gt=timezone.now() + datetime.timedelta(seconds=30)).order_by('-timestamp').last() # Filtrar el token por el valor pasado en la 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 esta sesión no se ha creado, creala
    user = User.objects.filter(id=token.user.id).first() # Obtenga al usuario del token
    if not user and request.user.is_authenticated: return redirect(reverse('feed:home')) # Si ya están autenticados, iniciarlos
    if not user: raise PermissionDenied() # Denegar si no se encontró ningún usuario
    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): # Verifique el token de autenticación
        auth_login(request, user, backend='django.contrib.auth.backends.ModelBackend') # Inicie sesión en el usuario si aún no ha iniciado sesión
        user.profile.mfa_expires = timezone.now() + datetime.timedelta(minutes=settings.LOGIN_VALID_MINUTES) # Establezca un vencimiento en su autenticación de factores múltiples
        user.profile.save()
        return HttpResponseRedirect(next if next != '' else reverse('landing:landing')) # Redirige al usuario a la página siguiente
    if not user.profile.mfa_enabled: # Compruebe si MFA está habilitado
        if not check_verification_time(user, token): # Verifique la hora
            user.profile.mfa_enabled = False # Borrar el número de teléfono
            user.profile.enable_two_factor_authentication = True # Habilitar MFA
            user.profile.phone_number = '+1' # Deshabilitar el número de teléfono
            user.profile.save() # Guardar el perfil
            auth_login(request, user, backend='django.contrib.auth.backends.ModelBackend') # Inicie sesión al usuario si su MFA no está habilitado
            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 solicitud es una solicitud posterior
        form = TfaForm(request.POST) # Instanciar el formulario
        code = str(form.data.get('code', None)) # Obtener el código
        if code and code != '' and code != None: # Asegúrate de que no esté vacío
            token_validated = user.profile.check_auth_token(usertoken) # Verifique el token de autenticación
            p = user.profile
            is_verified = check_verification_code(user, token, code) # Verifique el código
            p.mfa_authenticated = is_verified
            if token_validated: # Si todo
                if is_verified: # Está en orden
                    user.profile.mfa_enabled = True # Habilitar MFA (si aún no está habilitado)
                    user.profile.save()
                    auth_login(request, user, backend='django.contrib.auth.backends.ModelBackend') # Iniciar sesión en el usuario
                    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(): # Construya una QueryString para el siguiente parámetro (si corresponde)
                        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) # Redireccionar
                    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 el token no era válido
                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: # Si hubiera demasiados intentos
                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): # Enviar el correo electrónico (o mensaje de texto)
                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('/'))
    # Renderizar el formulario (para obtener solicitudes)
    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'})
Cuando agregamos este código, asegúrese de importar la función para enviar un correo electrónico. En la parte superior del archivo, el usuario ve (con otras importaciones), agregue

from .mfa import send_verification_email as send_mfa_verification_email
Ahora, necesitamos escribir esa función antes de que todo esto funcione. Debe extender nuestra función de correo electrónico de envío y simplemente enviar un correo electrónico al usuario con el código de verificación.

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))
Por lo tanto, todo esto funciona muy bien, ahora tenemos un sistema de autenticación de múltiples factores que depende de un número de teléfono o correo electrónico para iniciar sesión. Pero también necesitamos una forma de eliminar, o al menos ocultar a los usuarios que no cooperan con nuestros términos. Estos podrían ser spammers, robots o cualquier persona que no signifique bien para nuestro trabajo. Eche un vistazo a una vista que tengo para monitorear a los usuarios en mi sitio web:

# cantidades
from django.contrib.auth.decorators import login_required
from django.contrib.auth.decorators import user_passes_test
from .tests import is_superuser_or_vendor # Tendremos que crear esta prueba

@login_required
@user_passes_test(is_superuser_or_vendor)
def users(request):
    # Obtener una lista de usuarios
    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', { # Devolver a los usuarios en una plantilla
        'title': 'All Accounts',
        'users': User.objects.all(),
        'new_today': new_today,
        'new_this_month': new_this_month,
        'subscribers': subscribers
    })
Tenga en cuenta que este código usa una prueba, necesitaremos declarar esta prueba en un archivo Tests.py e importarla. Edición de usuarios/tests.py, creemos la prueba.

def is_superuser_or_vendor(user):
    return user.profile.vendor or user.is_superuser
Esto está junto con la plantilla usuarios/usuarios.html, que se parece a esto:
 
{% 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 %}
 
Tenga en cuenta que la plantilla incluye otra plantilla, usuarios/_user.html. Cuando se usa una plantilla que tiene una subtemplato y no use extensiones, es una buena idea agregar un subrayador (_) antes del nombre del archivo para extenderse, para distinguir las plantillas. Tenga en cuenta que esto es una gran cantidad de jinja, es posible que no tenga todas estas variables definidas. Pero así es como se ve mi código.
 
{% 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>
 
También necesitamos otra subtemplato, toggle_active.html. Esta plantilla debe ser un formulario que nos permita alternar si un usuario está activo.
 
<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>
 
También necesitaremos agregar una vista para alternar la actividad del usuario y los patrones de URL apropiados. Mientras lo hacemos, agregemos una vista para eliminar a un usuario en caso de que lo necesitemos.

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


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

class UserDeleteView(LoginRequiredMixin, UserPassesTestMixin, DeleteView):
    model = User
    success_url = '/' # La redirección de la URL de éxito
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        return context

    def test_func(self): # Pruebe si el usuario es superusador y tiene permiso para eliminar
        user = self.get_object()
        if self.request.user != user and self.request.user.is_superuser:
            return True
        return False
Si bien esto es práctico cuando sea necesario, eliminar a un usuario no debería ser necesario la mayor parte del tiempo, podemos alternar la visibilidad de los usuarios que visitan el sitio si necesitamos descartarlos. Los patrones de URL que agregamos se ven así. Con nano, edite usuarios/urls.py y agregue estas líneas:

nano users/urls.py
Las líneas deben ir en la lista de rutas en las vistas del usuario, antes del final "]" pero después del comienzo "[".

# …
    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'),
# …
Ahora, asegúrese de hacer una copia de seguridad del sitio para que pueda descargarlo en el servidor web en el que continuaremos trabajando. Desde la línea de comando,

sudo backup
Ahora nuestro sitio está respaldado. Así que ahora tenemos algunas características más útiles. Pero, ¿qué pasa con el panorama general aquí? Este código aún no es accesible desde Internet, todavía no tenemos un servidor de correo, y necesitamos expandir nuestra aplicación para incluir un proceso de verificación integral, así como diseños suaves para ayudarnos a explorar el sitio, junto con protocolos seguros para autenticar a los usuarios privilegiados. . Llegaremos a todo esto. Lo más importante por ahora será obtener este código en línea, lo que podemos hacer con solo unas pocas líneas de bash en un servidor Ubuntu. Sin embargo, deberá alquilar un servidor para esto, a menos que tenga un servidor en el hogar y una suscripción comercial en Internet que le permita abrir puertos. Personalmente ejecuto mi sitio web en un HP Z440 que está instalado en mi apartamento, pero generalmente es mucho más barato para las necesidades básicas para alquilar un servidor privado virtual (VPS). Tenga en cuenta que el código que estamos ejecutando ahora es relativamente delgado, deberá mantenerse y mejorarse antes de que seamosListo para usar lo que tenemos para construir un producto. Asegúrese de tener cuidado con lo que hace con Internet, asegúrese de que si implementa este sitio públicamente en la web en un servidor de Linux, tiene un plan para bloquear las interacciones no deseadas con su sitio web. Es probable que esto no sea un problema al principio, pero analizaremos una variedad de soluciones para combatir esto, incluido el aprendizaje automático, la inteligencia artificial y la visión por computadora. Cuando se convierta en un problema, busque más en este texto para una solución. En términos de alquilar un VPS, hay muchos lugares a los que puede ir. Google Cloud tiene servidores VPS, Ionos, Kamatera, Amazon AWS y más proveedores ofrecen soluciones de servidor en la nube que se adaptarán a nuestras necesidades. Deberá hacer clic en sus formularios y seleccionar un plan para comenzar. Puede ir con un plan básico con cualquier proveedor, pero asegúrese de que el proveedor le permita abrir puertos de servidor de correo de puertos para enviar correo electrónico (esto debería ser el puerto 587 y el puerto 25), algunos proveedores bloquean estos puertos. Hasta ahora he tenido elEST experiencia con Ionos y Kamatera, ambos me permitirán enviar un correo electrónico ilimitado y sus precios son bastante baratos. Se conectará a su nuevo servidor a través de un protocolo llamado SSH o Secure Shell, lo que le permite interactuar de forma remota con el servidor exactamente como su computadora personal, desde su computadora personal. Cuando configure el servidor, el proveedor de alojamiento probablemente le pedirá que agregue una tecla SSH, o le darán un nombre de usuario y contraseña. La tecla SSH es cómo iniciará sesión en el servidor desde la línea de comando para editar el código. Use las siguientes opciones de ssh-keygen para generar un SSH

ssh-keygen
Guarde el archivo y sobrescribirlo si es necesario, es bueno rotar sus claves SSH si aún no lo ha hecho. Ahora, puede usar el siguiente comando para ver su tecla SSH. Deberá copiarlo en su servidor remoto para poder usarlo para autenticarse.

cat ~/.ssh/id_rsa.pub
Si no pudo ver una tecla SSH al escribir ese comando (una larga cadena de dígitos y letras que comienzan con "SSH-RSA AAA"), intente generar una tecla RSA (son más seguras, por lo que aconsejo usarlos .) El siguiente código generará una tecla RSA SSH de 4096 bits.

ssh-keygen -t rsa -b 4096
Cree un VPS que ejecute Ubuntu, sin embargo, planea hacer esto. Una vez que haya creado un VPS haciendo clic en los formularios en el sitio web de los proveedores (kamatera.com, ionos.com o similar), querrá iniciar sesión. Para hacer esto, use el comando ssh con su dirección IP (la dirección Eso se parece a xx.xx.xx.xx). También deberá ser sensible al nombre de usuario predeterminado en el servidor que creamos, por ejemplo, Ubuntu.

ssh ubuntu@XX.XX.XX.XX
Es posible que se le solicite una contraseña, si se le pide una contraseña, ingrese. No usaremos el nombre de usuario predeterminado, así que comencemos creando un nuevo usuario y agregando una clave SSH a su cuenta. Comencemos agregando un nuevo archivo sshd_config, que le dice al servidor cómo usar SSH.

nano sshd_config

# Este es el archivo de configuración del sistema SSHD en todo el sistema.  Ver
# sshd_config (5) para obtener más información.

# Este SSHD se compiló con ruta =/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games

# La estrategia utilizada para las opciones en el sshd_config predeterminado enviado con
# OpenSSH es especificar opciones con su valor predeterminado donde
# posible, pero déjalos comentados.  Opciones no commentadas anule el
# Valor predeterminado.

# Puerto 22
# Direcciones en cualquier
# Dirección de la lista 0.0.0.0
# Escuchar adress ::

# Llave de hosts/etc/ssh/ssh_host_rsa_key
# Llave de hosts/etc/ssh/ssh_host_ecdsa_key
# Hostkey/etc/ssh/ssh_host_ed25519_key

# CIPHERS Y MAYA
# Rekeylimit predeterminado ninguno

# Explotación florestal
# Auth SyslogFacility
# Información de Loglevel

# Autenticación:

# LOGRINGRACETIME 2M
# Permitrootlogin Prohibit-Password
# Strict Modes si
# Maxauthtries 6
# Maxesiones 10

PubkeyAuthentication yes

# Espere que .SSH/Authorized_Keys2 se ignore de forma predeterminada en el futuro.
AuthorizedKeysFile	.ssh/authorized_keys .ssh/authorized_keys2

# Autorizado PRIVIPALSFILE NINGUNO

# Autorizado keyscommand ninguno
# Autorizado keyscommanduser nadie

# Para que esto funcione, también necesitará claves host en/etc/ssh/ssh_konk_hosts
# HostBasedAuthentication no
# Cambie a sí si no confía ~/.ssh/conoce_hosts para
# Atenticación de HostBased
# Ignoreuser conocido no
# No lea los archivos ~/.rhosts y ~/.shosts del usuario
# IgnorerHosts si

# Para deshabilitar las contraseñas de texto claras, cambie a no aquí!
PasswordAuthentication no
# PermenTemptyPasswords No

# Cambie a sí para habilitar las contraseñas de respuesta a la respuesta (tenga cuidado con los problemas con
# Algunos módulos y hilos de PAM)
KbdInteractiveAuthentication no

# Opciones de Kerberos
# Kerberosautatication no
# Kerberosorlocalpasswd Sí
# Kerberosticketcleanup sí
# Barra kerberoscoted no

# Opciones de GSSAPI
# Gssapiauthentication no
# Gssapicleanupcredentials sí
# GSSAPISTRICTACCEPTORCHECK Sí
# GSSAPIKEYEXCHIVE NO

# Establezca esto en 'Sí' para habilitar la autenticación de PAM, el procesamiento de la cuenta,
# y procesamiento de sesión. Si esto está habilitado, la autenticación de PAM
# Se permitirá a través de KBDinteractiveAuthentication y
# Contraseña Autenticación.  Dependiendo de su configuración de PAM,
# Autenticación de PAM a través de KBDinteractiveAuthentication puede omitir
# La configuración de "Permitrootlogin sin pasas".
# Si solo desea que la cuenta PAM y las verificaciones de sesión se ejecute sin
# Autenticación de PAM, luego habilite esto, pero establezca la autenticación de contraseña
# y kbdinteractiveAuthentication a 'no'.
UsePAM yes

# Permitir el consumo de trabajo sí sí
# Permitirse para hacer
# Gatewayports no
X11Forwarding yes
# X11DisplaYoffset 10
# X11uselocalhost sí
# PermitTTY yes
PrintMotd no
# Printlastlog Sí
# Tcpkeepalive sí
# Permiso
# Compresión retrasada
# Intervalo clientalivo 0
# Clientalivecountmax 3
# Usos en
# Pidfile /run/sshd.pid
# MaxStartups 10: 30: 100
# Pemittunl no
# Chrootdirectorio ninguno
# Versión Anexo Ninguno

# Sin ruta de banner predeterminada
Banner /etc/banner

# Permitir que el cliente pase variables de variables locales
AcceptEnv LANG LC_*

# anular el valor predeterminado de no subsistemas
Subsystem	sftp	/usr/lib/openssh/sftp-server

# Ejemplo de configuración primaria por usuario
# Coincidir con los anoncvs de usuario
# X11 para no
# Permitirse el trabajo no
# Definetty en
# Servidor CVS forCecommand
PermitRootLogin no
Recuerde, Ctrl+X e Y para guardar el archivo. A continuación, escribamos un script básico llamado Initialize (todo en el directorio de inicio predeterminado de nuestro usuario).

nano initialize
Agregue estas líneas al archivo, reemplazandoCon tu tecla SSH que encontraste usando CAT. (.ssh/id_rsa.pub)

# !
sudo apt install -y nano git openssh-server
sudo cp sshd_config /etc/ssh/sshd_config
sudo service ssh restart
sudo service sshd restart
echo "/root/.ssh/id_rsa" | sudo su root -c "ssh-keygen -t rsa -N ''"
echo "root ssh key:"
sudo su root -c "cat /root/.ssh/id_rsa.pub"
sudo adduser --disabled-password --gecos "" team
sudo passwd -d team
sudo usermod -aG sudo team
echo "/home/team/.ssh/id_rsa" | su team -c "ssh-keygen -t rsa -N ''"
cat /home/team/.ssh/id_rsa.pub >> /home/team/.ssh/authorized_keys
echo '<key here>' >> /home/team/.ssh/authorized_keys
echo "team ssh key:"
cat /home/team/.ssh/id_rsa.pub
Para guiarlo a través de este archivo, iniciemos la línea por línea. La primera línea le dice al compilador que este es un script bash. Luego estamos instalando dependencias, copiando sshd_config al directorio correcto, reiniciando ssh, generando claves ssh para root, agregando el "equipo" del usuario (puede elegir un nombre que le guste para esto, use el comando adduser con su nombre y contraseña deshabilitada para ahora). También agregamos equipo al grupo sudo, generamos su tecla SSH, agregamos nuestra clave a las claves y las suyas autorizadas también, e imprime su clave. Este nuevo usuario será cómo iniciamos sesión en el sitio. En un nuevo terminal, continúe y abra el servidor nuevamente.

ssh team@XX.XX.XX.XX
Esta vez no debería necesitar una contraseña, ya que tiene una tecla SSH. También hemos deshabilitado el inicio de sesión con la contraseña para mantener el sitio más seguro. Ahora, este servidor se inicia completamente en blanco sin información sobre él. Comencemos clonando nuestro proyecto de Git para que podamos descargarlo y ejecutarlo en la máquina remota. En el servidor remoto conectado a través de SSH, primero imprima su tecla SSH:

cat ~/.ssh/id_rsa.pub
A continuación, pegue esta clave en la configuración de GIT como lo hicimos antes para configurar nuestro repositorio Git. Ahora podemos clonar nuestro proyecto directamente al servidor. Asegúrese de haber hecho una copia de seguridad del proyecto localmente para que esté en el servidor GIT para descargar.

git clone git://github.com/you/yourproject.git
Perfecto. Ahora todos los archivos están aquí. Podemos verlos con LS

ls
Ahora, comencemos a configurar el servidor. Primero, copie su directorio de proyecto en un nombre simple y memorable que utilizaremos para el proyecto.

cp -r yourproject whatyoucalledit
Donde "WhatyoCalledit" es el nuevo nombre de su proyecto. A continuación, necesitaremos construir una utilidad básica para configurar el servidor. Guardaremos esta utilidad y la usaremos en el futuro. Para crear esta utilidad, creemos un binario de usuario para definir cómo editamos un script. Uso de Bash, Editar/USR/Bin/Ascript

sudo nano /usr/bin/ascript
Asegúrese de usar sudo allí para que tenga permisos para editar el archivo. En el archivo, agregue estas líneas:

# !
if [ ! -f /usr/bin/$1 ]; then
    sudo touch /usr/bin/$1
    echo "# !
    sudo chmod a+x /usr/bin/$1
    sudo nano /usr/bin/$1
    echo $1 | sudo tee -a /etc/ascripts
else
    sudo chmod a+x /usr/bin/$1
    sudo nano /usr/bin/$1
fi
Recuerde que este script toma un argumento, el nombre del script, como $ 1. Primero verifica si el archivo existe, o lo crea, agrega la primera línea para declarar que el script es bash, cambia sus permisos, lo edita y agrega su nombre a /etc /ascripts que nos permite almacenar los nombres de los scripts nosotros. están creando. Si el archivo ya existe, simplemente cambie los permisos y edítelo. Guarde el archivo y a continuación cambiaremos sus permisos. Mientras usemos este script, no tendremos que volver a hacer eso.

sudo chmod a+x /usr/bin/ascript
Perfecto. Ahora creemos un script llamado Configuración. Primero, no para abrumarte, pero eche un vistazo a cómo se ve mi script de configuración. Caeremos a través de cómo debería verse este guión en su proyecto, no necesitará todo en mi guión para comenzar.

# !
SECONDS=0
PYTHON_VERSION=3.12
echo "femmebabe installer initialized."
# sudo chmod a+x scripts/usereSetup
# ./scripts/usersetup
# Ssh-keyen
# Directorio de proyectos
DIR="/home/team/femmebabe"
USER="team"
# Comandos de registro
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
# Configuración 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
# Actualizar e instalar
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
# Habilitar antivirus de clamav
echo "Starting antivirus"
sudo systemctl enable clamav-daemon
sudo systemctl start clamav-daemon
# Establecer nombre de host
echo "127.0.0.1 femmebabe" | sudo tee -a /etc/hosts
sudo hostnamectl set-hostname localhost
# Configurar postgras
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;"
# Configuración de la base de datos de copia de seguridad
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
# Ipatibles deshabilitados
echo "Configuring firewall"
sudo iptables -P INPUT ACCEPT
sudo iptables -P OUTPUT ACCEPT
sudo iptables -P FORWARD ACCEPT
sudo iptables -F
sudo iptables-save
# Instalar 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
# Configurar postfix
cd $DIR
echo "Mail services configuration"
sudo cp /etc/postfix/main.cf /etc/postfix/main.cf.backup
sudo cp config/etc_postfix_main.cf /etc/postfix/main.cf
sudo cp config/etc_postfix_master.cf /etc/postfix/master.cf
sudo cp config/etc_default_opendkim /etc/default/opendkim
sudo cp config/etc_dovecot_conf.d_10-auth.conf /etc/dovecot/conf.d/10-auth.conf
sudo cp config/etc_dovecot_conf.d_10-master.conf /etc/dovecot/conf.d/10-master.conf
sudo cp config/etc_dovecot_dovecot.conf /etc/dovecot/dovecot.conf
sudo cp config/etc_dovecot_passwd /etc/dovecot/passwd
sudo cp config/etc_opendkim.conf /etc/opendkim.conf
sudo cp config/etc_default_opendkim /etc/default/opendkim
sudo adduser postfix opendkim
sudo mkdir /etc/opendkim
sudo mkdir /etc/opendkim/keys
sudo mkdir /etc/opendkim/keys/femmebabe.com
sudo mkdir /var/spool/postfix/opendkim
sudo echo "*@femmebabe.com     sendonly._domainkey.femmebabe.com" | sudo tee -a /etc/opendkim/signing.table
sudo echo "sendonly._domainkey.femmebabe.com    femmebabe.com:sendonly:/etc/opendkim/keys/femmebabe.com/sendonly.private" | sudo tee -a /etc/opendkim/key.table
sudo echo "127.0.0.1" | sudo tee -a /etc/opendkim/trusted.hosts
sudo echo "localhost" | sudo tee -a /etc/opendkim/trusted.hosts
sudo echo "" | sudo tee -a /etc/opendkim/trusted.hosts
sudo echo "*.femmebabe.com" | sudo tee -a /etc/opendkim/trusted.hosts
sudo chown -R opendkim:opendkim /etc/opendkim
sudo opendkim-genkey -b 2048 -d femmebabe.com -D /etc/opendkim/keys/femmebabe.com -s sendonly -v
sudo chmod go-rw /etc/opendkim/keys
sudo chown opendkim:opendkim /etc/opendkim/keys/femmebabe.com/sendonly.private
sudo chown opendkim:postfix /var/spool/postfix/opendkim
cd $DIR
sudo cp mailbox/* /var/mail/
sudo chown :users /var/mail/*
sudo chmod -R a+rwx /var/mail/*
sudo systemctl restart opendkim postfix dovecot
# Crear DIRS
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
# Configurar virtuealenv
cd $DIR
echo "Creating virtual environment"
python -m venv venv
source venv/bin/activate
# Obtener y construir dependencias
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
# Establecer reglas de firewall
cd $DIR
# Instalar dependencias de 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 Instale OpenCV-Python == 4.5.5.64
# PIP Instale 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
# Instalar 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
# Ejecutar certbot
sudo certbot --apache --non-interactive --agree-tos --domains femmebabe.com --email jasper.camber.holton@gmail.com
# Servidor de correo recargado
sudo systemctl restart opendkim postfix dovecot
# Copiar certs
# sudo cp /etc/letsencrypt/live/femmebabe.com/privkey.pem privkey.pem
# sudo cp /etc/lettesencrypt/live/femmebabe.com/cert.pem cert.pem
# Parche venv
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"
# Establecer la configuración del usuario
sudo gpasswd -a www-data users
# Establecer permisos
echo "Setting permissions"
sudo chown -R team:users cache/
sudo chmod a+rwx -R cache/
# Sudo Chown -R Equipo: Usuarios/var/run/
# Root de chown de sudo: 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: usuarios 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
# Copiar configuración y establecer permisos
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
# Configuración de la base de datos
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"
# Inyectar configuración PAM y eliminar la configuración SSH defectuosa
# Sudo sed -i '' y $ d '/etc/pam.d/sshd
# Sudo sed -i '' y $ d ' /etc /perfil
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
# Copiar scripts bin y establecer permisos
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
# Recargar y habilitar servicios
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
# Habilitar módulos 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
# Deshabilitar el sitio predeterminado
sudo a2dissite 000-default
sudo a2dissite 000-default-le-ssl
# Habilitar para el sitio
sudo a2ensite femmebabe-le-ssl
# Recargar demonios y reiniciar Apache, Postfix y Opendkim
sudo systemctl daemon-reload
sudo systemctl restart apache2
sudo systemctl restart opendkim postfix
sudo systemctl start daphne
# Establecer permisos
sudo chown -R :www-data /var/www/
sudo chown -R :www-data /var/www/.deepface
# Configuración de intercambio
echo "Allocating swap, this may take a while"
sudo swapoff /swapfile
sudo rm /swapfile
sudo fallocate -l 8G /swapfile
sudo dd if=/dev/zero of=/swapfile bs=1024 count=8388608
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo "/swapfile swap swap defaults 0 0" | sudo tee -a /etc/fstab
sudo swapon --show
# Init.
echo "Initializing routine caption"
/home/team/femmebabe/venv/bin/python /home/team/femmebabe/routine_caption.py
/home/team/femmebabe/venv/bin/python /home/team/femmebabe/setup_mail.py
# Configuración 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
# Mostrar IPv6 y OpenDkim para la configuración del dominio
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}'
# Configuración completada
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."
¡Esa es una gran configuración! En resumen, este código registra comandos, configura nano y git, copia sobre archivos, descarga e instala paquetes de Ubuntu APT, Python Dependencies, configura Postfix, configura PostgreSQL (el servidor de la base de datos) y carga la base de datos, configura UFW (una firewall no complicada),,,,,. deshabilita iptables, descarga un antivirus, fabrica directorios, dependencias de clones, instala certificados y establece El servidor, instala la configuración, inicia y habilita el sever, asigna intercambio, establece permisos e imprime la dirección IP, IPv6 y la tecla OpenDkim. Bastante simple, pero parece mucho código. No necesitaremos mucho de esto porque no tenemos las dependencias, no estamos usando Celery, Ceybeat o Daphne, pero instalaremos algunas de ellas de todos modos para comenzar. Observe que este código tiene un dominio declarado varias veces. También necesitaremos comprar un nombre de dominio (que es una pequeña tarifa anual). Recomiendo Squarespace para comprar un dominio, su diseño esintuitivo y fácil de usar. Puede comprar cualquier dominio de su elección, pero estoy usando el dominio femmebabe.com en este ejemplo. Una vez que haya comprado un dominio, diríjase al panel de configuración DNS Squarespace y agregue un registro A que apunte su dominio al servidor por dirección IP. Debería verse así: @ A xx.xx.xx.xx Con el operador @ como host, lo que significa que todos los subdominios bajo este dominio y el dominio raíz redirigirán al servidor. Hay más registros para declarar, pero podemos pasar a estos una vez que estemos listos para enviar correo. Tenga en cuenta que puede pasar varios días antes de que pueda enviar con éxito el correo desde el servidor. Los registros de DNS que estamos estableciendo tomarán tiempo para propagarse. De todos modos, el único registro que necesitamos para comenzar es un registro A. Entonces, ahora podemos completar el siguiente script de acuerdo con nuestro proyecto y ejecutarlo. Comencemos con un script de configuración más pequeño para instalar lo que necesitamos para un progreso básico. No usaremos tantas dependencias o PostgreSQL todavía, soloSube un servidor HTTP básico y te preocupes por certificarlo cuando esté hecho. Recuerde, para obtener un certificado HTTPS y ejecutar el servidor de forma segura, necesitaremos comprar un dominio junto con Rent a Servidor. Por ahora, reemplace el "equipo" en este archivo con el nombre de su usuario, "Dir" con el directorio de su proyecto y suministre su correo electrónico y dominio en las etiquetas <>. Además, antes de ejecutar este código, necesitamos cambiar la configuración al firewall que el proveedor de alojamiento admite, si lo hay. Por lo general, esto se encuentra en la pestaña 'Redes' de su proveedor de alojamiento, o si está alojando, está en la sección 'Reenvío de puertos' de su enrutador. También querrá configurar una IP estática a través de su enrutador con la dirección de la máquina de su servidor, si está utilizando el alojamiento. Deberá abrir los siguientes puertos para el acceso de lectura/escritura. 22 (SSH) 25 (correo) 587 (correo) 110 (Cliente de correo) 80 (http) 443

# !
SECONDS=0
PYTHON_VERSION=3.12
echo "femmebabe installer initialized."
DIR="/home/team/<yourproject>"
USER="team"
# Comandos de registro
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
# Configuración 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
# Actualizar e instalar
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
# Habilitar antivirus de clamav
echo "Starting antivirus"
sudo systemctl enable clamav-daemon
sudo systemctl start clamav-daemon
# Establecer nombre de host
echo "127.0.0.1 femmebabe" | sudo tee -a /etc/hosts
sudo hostnamectl set-hostname femmebabe
# Configuración de la base de datos de copia de seguridad
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
# Ipatibles deshabilitados
echo "Configuring firewall"
sudo iptables -P INPUT ACCEPT
sudo iptables -P OUTPUT ACCEPT
sudo iptables -P FORWARD ACCEPT
sudo iptables -F
sudo iptables-save
# Configurar virtuealenv
cd $DIR
echo "Creating virtual environment"
python -m venv venv
source venv/bin/activate
pip3 install -r requirements.txt
# Instalar 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
# Ejecutar certbot
sudo certbot --apache --non-interactive --agree-tos --domains femmebabe.com --email <youremail>@gmail.com
# Establecer la configuración del usuario
sudo gpasswd -a www-data users
# Establecer permisos
echo "Setting permissions"
sudo chown -R team:users cache/
sudo chmod a+rwx -R cache/
# Sudo Chown -R Equipo: Usuarios/var/run/
# Root de chown de sudo: 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 ./
# Recargar y habilitar servicios
echo "Enabling services"
sudo systemctl daemon-reload
sudo systemctl enable clamav-daemon
sudo systemctl start clamav-daemon
# Habilitar módulos 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
# Recargar demonios y reiniciar Apache, Postfix y Opendkim
sudo systemctl daemon-reload
sudo systemctl restart apache2
sudo systemctl restart opendkim postfix
# Mostrar IPv6 y OpenDkim para la configuración del dominio
echo "COPY the below information to domain configuration."
hostname -I
ip a | grep inet
ip -6 addr | grep "scope link"
Antes de ejecutar este código, asegúrese de que el dominio que haya comprado esté conectado al servidor. Para hacer esto, abra un terminal en su máquina local y ejecute este comando con su dominio:

ping femmebabe.com # Inserte su dominio aquí, después de ping
Si todo se ve bien y el servidor está enviando respuestas, estamos listos para ejecutar los paquetes de script e instalar, así como iniciar, habilitar y certificar nuestro servidor Apache. Esto no es todo la configuración necesaria para configurar Postfix, veremos esa configuración más tarde. Por ahora, ejecute este código de configuración y debe tomar unos minutos instalar y certificar su servidor. Una vez más, asegúrese de reemplazar el nombre, el correo electrónico y el nombre de dominio en el script de acuerdo con el nombre que compró. Ahora que el servidor está aprovisionado, puede ir a la URL en cualquier navegador web y verificar para asegurarse de que el servidor esté ejecutando HTTPS. Si no es así, intente esperar un poco para que los registros de DNS se pongan al día y luego ejecute el siguiente comando para volver a intentar la certificación CERTBOT:

sudo certbot --apache --non-interactive --agree-tos --domains <domain>.com --email <youremail>@gmail.com
Siempre que haya configurado todo correctamente, debería poder acceder a la página predeterminada de Apache solo para saber que su código está funcionando y mostrando una página web en vivo. A continuación, editemos el settings.py para cambiar nuestro modo de depuración predeterminado a la producción. También configuraremos el dominio en la configuración, así como las IP internas.

nano yourproject/settings.py
En la configuración, cambie/agregue estas líneas.

DEBUG = False

# Configuración del sitio
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',
]
Ahora, necesitaremos configurar Apache2. Editar el archivo de configuración que implementaremos con esta línea:

sudo nano /etc/apache2/sites-available/femmebabe-le-ssl.conf
Este archivo de configuración debe tener nuestro nombre de dominio y el nombre del usuario y el proyecto. Estoy usando el nombre de dominio femmebabe.com, el equipo de nombre de usuario y el nombre del proyecto 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>
Asegúrese de reemplazar el nombre del proyecto, directorios y dominio en este código de ejemplo al configurar su servidor. Ahora, necesitaremos deshabilitar el sitio predeterminado. Esto se puede hacer usando bash.

sudo a2dissite 000-default-le-ssl
sudo a2dissite 000-default
sudo a2dissite default-ssl
A continuación, podemos habilitar el sitio predeterminado y recargar Apache2, también utilizando BASH. Recuerde reemplazar femmebabe con el nombre del archivo que declaró al editar en/etc/apache2/sites disponible/.

sudo a2ensite femmebabe-le-ssl
sudo systemctl reload apache2
Vuelve a tu dominio en la barra de navegación. Debería ver el sitio que configuró en su navegador web. ¡Felicidades! Si no lo ve, es posible que deba hacer algunos cambios. Revise cuidadosamente la configuración en su proyecto, la configuración de Apache y asegúrese de no tener ningún error y ejecute los siguientes comandos para verificar el proyecto en busca de errores.

cd projectname
source venv/bin/activate
python manage.py check
Si tiene errores en su proyecto de Python, tráigelos hasta donde están y arregléelo. Es posible que no pueda ver todos sus errores dependiendo de dónde se encuentren, por lo que si tiene un error que simplemente dice que "poblar no es reentrante", edite el siguiente archivo en el entorno virtual, registro.py, para exponer el error.

nano venv/lib/python3.12/site-packages/django/apps/registry.py
Desplácese a la línea 83, donde se plantea este error de tiempo de ejecución (recaude RuntimeError ("Populate () no es reentrante")) y agregue un comentario antes de esta línea, luego agregando, con la misma hendidura, self.app_configs = {}. Esto se ve así:

            if self.loading:
                # Evite las llamadas reentrantes para evitar ejecutar appconfig.ready ()
                # Métodos dos veces.
# Levante RuntimeError ("Populate () no es reentrante")
                self.app_configs = {}
            self.loading = True
Luego puede verificar el proyecto nuevamente y exponer el error.

python manage.py check
Entonces puede ver el error y solucionarlo. Cuando lo haya solucionado y el código se compila sin errores, asegúrese de cambiar el archivo para que se vea así:

            if self.loading:
                # Evite las llamadas reentrantes para evitar ejecutar appconfig.ready ()
                # Métodos dos veces.
                raise RuntimeError("populate() isn't reentrant")
# self.app_configs = {}
            self.loading = True
Siempre que el servidor esté en línea, cuando hacemos más cambios en él, necesitamos usar el siguiente comando para recargar el servidor:

sudo systemctl reload apache2
¡Impresionante! Pero, ¿qué hay de enviar correo? Para comenzar a enviar correo electrónico, primero tendremos que actualizar la configuración del dominio. Esto debería estar en su panel DNS en Squarespace, o cualquier registro de nombre de dominio que elija. También necesitaremos instalar y agregar configuración, y ejecutar algunos comandos. Primero, obtengamos la dirección IPv6 del servidor. Luego abriremos su DNS y agregaremos los registros. Para obtener la dirección IPv6 del servidor, use este comando:

ip -6 addr
Ahora, podemos agregar los siguientes registros a la configuración DNS. Mis registros se ven así. Sin embargo, para sus registros, debe reemplazar la dirección IP con su IP (no 75.147.182.214, eso es mío). También agregue su dominio en lugar de femmebabe.com, así como su dirección IPv6 que se encuentra con el comando anterior (no puede usar el mío, Fe80 :: 725a: FFF: Fe49: 3E02). No se preocupe por la tecla de dominio por ahora, esto se crea cuando configuramos Postfix, el servidor de correo, con OpenDkim e imprima la clave. Configuraremos esto por última vez. @ A N / A 75.147.182.214 @ Mx 10 femmebabe.com @ PTR N / A femmebabe.com @ TXT N / A Txt @ v = spf1 mx ip75.147.182.214ip6: fe80 :: 725a: fff: fe49: 3e02 ~ todos default._bimi TXT N / A v = bimi1; l = https: //femmebabe.com/media/static/femmebabe.svg _dmarc TXT N / A V = dMarc1; P = ninguno sendOnly._domainkey TXT N / AAhora, tendremos que agregar una configuración persistente para Postfix. Todo lo que necesitamos hacer es asegurarnos de reemplazar el nombre de dominio, femmebabe.com, con el nombre de dominio que está utilizando. Veamos todos los archivos de configuración uno por uno e instívalos en un directorio llamado Config en nuestro proyecto, para instalarlo en el sistema operativo.

nano config/etc_postfix_main.cf
Agregue este texto al archivo

# Ver /usr/share/postfix/main.cf.dist para una versión comentada y más completa


# Debian específico: especificar un nombre de archivo causará el primero
# línea de ese archivo para ser utilizada como nombre.  El incumplimiento de Debian
# es /etc /mailname.
# myorigin = /etc /mailname

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

# Añadir. Domain es el trabajo de MUA.
append_dot_mydomain = no

# Descopment la siguiente línea para generar advertencias de "correo retrasado"
# demandado_warning_time = 4h

readme_directory = no

# Ver http://www.postfix.org/compatibility_readme.html - predeterminado a 3.6 en
# instalaciones frescas.
compatibility_level = 3.6



# Parámetros 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

# Configuración de Milter
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
¡Siguiente configuración!

nano config/etc_postfix_master.cf
Agregue estas líneas:

# 
# Postfix Master Process Configuration File.  Para detalles sobre el formato
# del archivo, consulte la página manual maestro (5) (comando: "hombre 5 maestro" o
# En línea: http://www.postfix.org/master.5.html).
# 
# No olvide ejecutar "Postfix Reload" después de editar este archivo.
# 
# ==================================================== =========================
# Tipo de servicio Private Undkroot Wakeup MAXPROC Comando + Args
# (Sí) (sí) (no) (nunca) (100)
# ==================================================== =========================
smtp      inet  n       -       y       -       -       smtpd
# SMTP INET N - Y - 1 Postcreen
# pase smtpd - - y - - smtpd
# Dnsblog unix - - y - 0 dnsblog
# Tlsproxy unix - - y - 0 tlsproxy
# Elija uno: Habilitar envío solo para clientes de bucleback o para cualquier cliente.
# 127.0.0.1: Submisión 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/envío
# -o smtpd_tls_security_level = encrypt
# -o SMTPD_SASL_AUTH_ENABLE = SÍ
# -o SMTPD_TLS_AUTH_ONLY = SÍ
# -o SMTPD_REJECT_UNLISTED_RECIPIENT = NO
# -O SMTPD_CLIENT_RESTrictions = $ mua_client_restrictions
# -o smtpd_helo_restrictions = $ mua_helo_restrictions
# -o SMTPD_SENDER_RESTrictions = $ mua_sender_restrictions
# -O SMTPD_RepiPient_Restrictions =
# -o SMTPD_RELAY_RESTrictions = Permit_Sasl_Authenticated, rechazar
# -O MILTER_MACRO_DAEMON_NAME = Originario
# Elija uno: habilitar SMTPS solo para clientes de bucleback o para cualquier cliente.
# 127.0.0.1:SMTPS INET N - Y - - SMTPD
# SMTPS INET N - Y - - SMTPD
# -O syslog_name = postfix/smtps
# -o SMTPD_TLS_WRAPPMODE = SÍ
# -o SMTPD_SASL_AUTH_ENABLE = SÍ
# -o SMTPD_REJECT_UNLISTED_RECIPIENT = NO
# -O SMTPD_CLIENT_RESTrictions = $ mua_client_restrictions
# -o smtpd_helo_restrictions = $ mua_helo_restrictions
# -o SMTPD_SENDER_RESTrictions = $ mua_sender_restrictions
# -O SMTPD_RECIPIENT_TRASTRICTIONS =
# -o SMTPD_RELAY_RESTrictions = Permit_Sasl_Authenticated, rechazar
# -O MILTER_MACRO_DAEMON_NAME = Originario
# 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 para el software no postfix. Asegúrese de examinar el manual
# Páginas del software que no es postfix para averiguar qué opciones quiere.
# 
# Muchos de los siguientes servicios utilizan la entrega de tuberías de fixe (8)
# agente.  Consulte la página de la mujer Pipe (8) para obtener información sobre $ {destinatario}
# y otras opciones de sobre de mensajes.
# ==================================================== ==================
# 
# dirección para correo. Consulte el archivo Postfix MailDrop_readMe para más detalles.
# También especifique en main.cf: maildrop_destination_recipient_limit = 1
# 
maildrop  unix  -       n       n       -       -       pipe
  flags=DRXhu user=vmail argv=/usr/bin/maildrop -d ${recipient}
# 
# ==================================================== ==================
# 
# Las versiones recientes de Cyrus pueden usar la entrada existente "LMTP" Master.CF.
# 
# Especificar en cyrus.conf:
# lmtp cmd = "lmtpd -a" escucha = "localhost: lmtp" proto = tcp4
# 
# Especifique en main.cf uno o más de los siguientes:
# biailbox_transport = lmtp: inet: localhost
# Virtual_transport = lmtp: inet: localhost
# 
# ==================================================== ==================
# 
# Cyrus 2.1.5 (Amos Gouaux)
# También especifique en main.cf: cyrus_destination_recipient_limit = 1
# 
# Cyrus Unix - N N - - tubería
# flags = drx user = cyrus arg =/cyrus/bin/entrega -e -r $ {remitente} -m $ {extensión} $ {user}
# 
# ==================================================== ==================
# Ejemplo antiguo de entrega a través de Cyrus.
# 
# Old -Cyrus Unix - N N - - tubería
# flags = r user = cyrus argv =/cyrus/bin/entrega -e -m $ {extensión} $ {user}
# 
# ==================================================== ==================
# 
# Consulte el archivo Postfix UUCP_README para obtener detalles de configuración.
# 
uucp      unix  -       n       n       -       -       pipe
  flags=Fqhu user=uucp argv=uux -r -n -z -a$sender - $nexthop!rmail ($recipient)
# 
# Otros métodos de entrega externos.
# 
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}
Y la configuración OpenDkim. OpenDkim identifica los servidores de correo electrónico con claves de dominio para hacerlos más seguros. Sin él, el correo no está firmado y podría no llegar a una bandeja de entrada.

nano config/etc_default_opendkim
Agregue estas líneas:

# Nota: Este es un archivo de configuración heredado. No es utilizado por OpenDkim
# Servicio Systemd. Utilice los parámetros de configuración correspondientes en
# /etc/opendkim.conf en su lugar.
# 
# Anteriormente, uno editaría la configuración predeterminada aquí y luego ejecutaría
# /lib/opendkim/opendkim.service.generate para generar archivos de anulación de Systemd a
# /etc/systemd/system/opendkim.service.d/override.conf y
# /etc/tmpfiles.d/opendkim.conf. Si bien esto todavía es posible, ahora es
# Recomendado para ajustar la configuración directamente en /etc/opendkim.conf.
# 
# Daemon_opts = ""
# Cambiar a/var/spool/postfix/run/opendkim para usar un socket unix con
# Postfix en un chroot:
# Rundir =/was/spool/postfix/run/opendkim
RUNDIR=/run/opendkim
# 
# Descopment para especificar un enchufe alternativo
# Tenga en cuenta que establecer esto anulará cualquier valor de socket en OpenDkim.conf
# por defecto:
SOCKET="local:/var/spool/postfix/opendkim/opendkim.sock"
# Escuche en todas las interfaces en el puerto 54321:
# Socket = inet: 54321
# Escuche en Loopback en el puerto 12345:
# Socket = inet: 12345@localhost
# La escucha es 192.0.2.1 es el puerto 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
Agregue estas líneas:

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

# Límite predeterminado de VSZ (tamaño de memoria virtual) para procesos de servicio. Esto es principalmente
# destinado a atrapar y matar procesos que filtran la memoria antes de comer
# todo.
# default_vsz_limit = 256m

# El usuario de inicio de sesión es utilizado internamente por procesos de inicio de sesión. Este es el mas no confiable
# Usuario en el sistema Dovecot. No debería tener acceso a nada en absoluto.
# default_login_user = dovenull

# El usuario interno es utilizado por procesos no privilegiados. Debería estar separado de
# Iniciar sesión en el usuario, para que los procesos de inicio de sesión no puedan perturbar otros procesos.
# default_internal_user = dovecot

service imap-login {
  inet_listener imap {
    # Puerto = 143
  }
  inet_listener imaps {
    # Puerto = 993
    # SSL = SÍ
  }

  # Número de conexiones para manejar antes de comenzar un nuevo proceso. Típicamente
  # Los únicos valores útiles son 0 (ilimitado) o 1. 1 es más seguro, pero 0
  # es más rápido. <doc/wiki/loginprocess.txt>
  # servicio_count = 1

  # Número de procesos para seguir esperando siempre más conexiones.
  # Process_min_avail = 0

  # Si establece servicio_count = 0, probablemente necesite hacer crecer esto.
  # Vsz_limi = $ default_vsz_limit
}

service pop3-login {
  inet_listener pop3 {
    # Puerto = 110
  }
  inet_listener pop3s {
    # Puerto = 995
    # SSL = SÍ
  }
}

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

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

  # Crear oyente inet solo si no puede usar el enchufe Unix anterior
  # inet_lister lmtp {
    # Evite hacer que LMTP sea visible para todo Internet
    # Dirección =
    # puerto =
  # }
}

service imap {
  # La mayor parte de la memoria va a los archivos MMAP (). Es posible que necesite aumentar esto
  # Límite si tiene buzones enormes.
  # Vsz_limi = $ default_vsz_limit

  # Max. Número de procesos IMAP (conexiones)
  # process_limit = 1024
}

service pop3 {
  # Max. Número de procesos POP3 (conexiones)
  # Process_limit = 1024
}

service submission {
  # Max. Número de procesos de envío SMTP (conexiones)
  # process_limit = 1024
}

service auth {
  # Auth_socket_path señala a este socket UserDB de forma predeterminada. Es típicamente
  # utilizado por dovecot-lda, doveadm, posiblemente proceso imap, etc. usuarios que tienen
  # Los permisos completos a este socket pueden obtener una lista de todos los nombres de usuario y
  # Obtenga los resultados de las búsquedas de usuarios de todos.
  # 
  # El modo 0666 predeterminado permite que cualquiera se conecte al socket, pero el
  # Las búsquedas de usuarios de userDB solo tendrán éxito si el userDB devuelve un campo "UID" que
  # coincide con el UID del proceso de la persona que llama. También si el UID o el GID de la persona que llama coincide con el
  # Socket's UID o GID La búsqueda tiene éxito. Cualquier otra cosa causa una falla.
  # 
  # Para dar a la persona que llama permisos completos para buscar a todos los usuarios, configure el modo en
  # algo más que 0666 y Dovecot deja que el núcleo haga cumplir el
  # Los permisos (por ejemplo, 0777 permiten a todos los permisos completos).
  unix_listener /var/spool/postfix/private/auth {
    mode = 0660
    user = postfix
    group = postfix
  }
}

service auth-worker {
  # El proceso de trabajador de autores se ejecuta como root de forma predeterminada, para que pueda acceder
  # /etc/sombra. Si esto no es necesario, el usuario debe cambiarse a
  # $ default_interal_user.
  # usuario = root
}

service dict {
  # Si se utiliza el proxy de dict, los procesos de correo deben tener acceso a su socket.
  # Por ejemplo: MODE = 0660, Group = Vmail y Global Mail_Access_Groups = Vmail
  unix_listener dict {
    # Modo = 0600
    # Usuario =
    # grupo =
  }
}
Una vez más, asegúrese de reemplazar el dominio en todos estos archivos, femmebabe.com, con el dominio que seleccionó. Editar el siguiente archivo, configuración de Dovecot,

nano config/etc_dovecot_dovecot
Y agrega estas líneas

## Archivo de configuración ovecot

# Si tienes prisa, vea http://wiki2.dovecot.org/quickconfiguration

# El comando "Doveconf -n" ofrece una salida limpia de la configuración cambiada. Usarlo
# En lugar de copiar y pegar archivos al publicar en la lista de correo de Dovecot.

# '# 'El personaje y todo después de que se trata como comentarios. Espacios adicionales
# y las pestañas se ignoran. Si desea usar cualquiera de estos explícitamente, coloque el
# value inside quotes, eg.: key = "# Char y Whitepace en blanco "

# La mayoría (pero no todas) la configuración puede ser anulada por diferentes protocolos y/o
# IPS de origen/destino colocando la configuración dentro de las secciones, por ejemplo:
# Protocolo IMAP {}, Local 127.0.0.1 {}, remoto 10.0.0.0/8 {}

# Los valores predeterminados se muestran para cada configuración, no se requiere para incommentar
# aquellos. Sin embargo, estas son excepciones a esto: no hay secciones (por ejemplo, espacio de nombres {})
# o la configuración del complemento se agregan de forma predeterminada, solo se enumeran como ejemplos.
# Las rutas también son solo ejemplos con los valores predeterminados reales basados ​​en Configurar
# opciones. Las rutas enumeradas aquí son para configurar - -prefix =/usr
# --sysconfdir =/etc --localstatedir =/var

# Habilitar protocolos instalados
!include_try /usr/share/dovecot/protocols.d/*.protocol

# Una lista de comas separada de IP o hosts donde escuchar para conexiones.
# "*" escucha en todas las interfaces IPv4, "::" escucha en todas las interfaces IPv6.
# Si desea especificar puertos no defectuosos o algo más complejo,
# Editar conf.d/master.conf.
# Escuchar = *, ::

# Directorio base donde almacenar datos de tiempo de ejecución.
# base_dir =/var/run/dovecot/

# Nombre de esta instancia. En la configuración de múltiples instancias DoVeadm y otros comandos
# puede usar -i <neste_name> para seleccionar qué instancia se usa (una alternativa
# a -C <Confact_Path>). El nombre de la instancia también se agrega a los procesos de Dovecot
# En la salida de PS.
# instance_name = dovecot

# Mensaje de saludo para clientes.
# login_greeting = Dovecot listo.

# Lista separada de espacio de rangos de redes de confianza. Conexiones de estos
# A los IP se les permite anular sus direcciones IP y puertos (para el registro y
# para verificaciones de autenticación). disable_laintext_auth también se ignora para
# estas redes. Por lo general, especificaría sus servidores de proxy IMAP aquí.
# login_trusted_networks =

# Lista separada por espacio de enchufes de verificación de acceso de inicio de sesión (por ejemplo, TCPWRAP)
# login_access_sockets =

# Con proxy_maybe = sí si el destino proxy coincide con cualquiera de estos IP, no lo haga
# proxy. Esto no es necesario normalmente, pero puede ser útil si el destino
# IP es, p. La IP de un equilibrador de carga.
# auth_proxy_self =

# Muestre más títulos de procesos verbosos (en PS). Actualmente muestra nombre de usuario y
# Dirección IP. Útil para ver quién está usando los procesos IMAP
# (por ejemplo, buzones compartidos o si se usa el mismo UID para múltiples cuentas).
# verbose_proctitle = no

# En caso de que se maten todos los procesos cuando el proceso maestro de Dovecot se apaga.
# Establecer esto en "no" significa que Dovecot se puede actualizar sin
# forzar las conexiones del cliente existentes para cerrar (aunque eso también podría ser
# Un problema si la actualización es, p. debido a una solución de seguridad).
# shutdown_clients = sí

# Si no es cero, ejecute los comandos de correo a través de estas muchas conexiones al servidor DoVeadm,
# en lugar de ejecutarlos directamente en el mismo proceso.
# doveadm_worker_count = 0
# Socket o host UNIX: puerto utilizado para conectarse al servidor Doveadm
# doveadm_socket_path = doveadm-server

# Lista separada de espacio de variables de entorno que se conservan en Dovecot
# inicio y transmitido a todos sus procesos infantiles. También puedes dar
# Key = pares de valor para establecer siempre configuraciones específicas.
# Import_environment = tz

## 
## Configuración del servidor del diccionario
## 

# El diccionario se puede usar para almacenar listas de valores clave. Esto es utilizado por varios
# complementos. Se puede acceder al diccionario directamente o aunque un
# Servidor de diccionario. Los siguientes nombres de diccionario de mapas de bloque DICT a URIS
# Cuando se usa el servidor. Estos se pueden hacer referencia utilizando URI en formato
# "Proxy :: <Name>".

dict {
  # cuota = mysql: /andc/dovecot/dovecot-dic-sql.conf.ext
}

# La mayor parte de la configuración real se incluye a continuación. Los nombres de archivo son
# Primero ordenado por su valor ASCII y analizado en ese orden. Los 00 prefijos
# En los nombres de archivo están destinados a facilitar la comprensión del pedido.
!include conf.d/*.conf

# Un archivo de configuración también puede intentar ser incluido sin dar un error si
# No se encuentra:
!include_try local.conf

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

protocols = imap pop3

# Permite que Dovecot escuche todas las conexiones de entrada (IPv4 / IPv6)

listen = *, ::
Agregue una contraseña para el usuario de Dovecot:

nano config/etc_dovecot_passwd
La primera parte del archivo, antes del colon, es el nombre de usuario. La última parte, "YourPassword", denota la contraseña que desea darle a su servidor de correo.

team:{plain}yourpassword
A continuación, la configuración OpenDkim

nano config/etc_opendkim.conf
Y agregue estas líneas:

# Esta es una configuración básica para firmar y verificar. Puede ser fácilmente
# adaptado para adaptarse a una instalación básica. Ver OpenDkim.conf (5) y
# /usr/share/doc/opendkim/examples/opendkim.conf.sample para completar
# Documentación de los parámetros de configuración disponibles.

Syslog			yes
SyslogSuccess		yes
# Logo por qué no

# Parámetros comunes de firma y verificación. En Debian, el encabezado "de" es
# sobrevalorado, porque a menudo es la clave de identidad utilizada por los sistemas de reputación
# y así algo sensible a la seguridad.
Canonicalization	relaxed/simple
Mode			s
SubDomains		no
OversignHeaders		From

# Dominio de firma, selector y clave (requerido). Por ejemplo, realizar firma
# para dominio "Ejemplo.com" con selector "2020" (2020._domainkey.example.com),
# Uso de la clave privada almacenada en /etc/dkimkeys/example.private. Más granular
# Las opciones de configuración se pueden encontrar en /usr/share/doc/opendkim/readme.opendkim.
# Dominio ejemplo.com
# Selector 2020
# KeyFile /etc/dkimkeys/example.private

# En Debian, Opendkim se ejecuta como el usuario "OpenDkim". Se requiere una uraza de 007 cuando
# Uso de un enchufe local con MTA que acceden al enchufe como un no privilegiado
# Usuario (por ejemplo, Postfix). Es posible que deba agregar el "Postfix" del usuario al grupo
# "Opendkim" en ese caso.
UserID			opendkim
UMask			007

# Socket para la conexión MTA (requerida). Si la MTA está dentro de una cárcel de chroot,
# Debe asegurarse de que el socket sea accesible. En Debian, Postfix se ejecuta en
# un chroot in/var/spool/postfix, por lo tanto, un enchufe unix tendría que ser
# configurado como se muestra en la última línea a continuación.
# Socket local: /run/opendkim/opendkim.sock
# Socket INET: 8891@localhost
# Socket INET: 8891
Socket			local:/var/spool/postfix/opendkim/opendkim.sock

PidFile			/run/opendkim/opendkim.pid

# Hosts para los cuales firmar en lugar de verificar, el valor predeterminado es 127.0.0.1. Ver el
# Sección de operación de OpenDkim (8) para obtener más información.
# Internohosts 192.168.0.0/16, 10.0.0.0/8, 172.16.0.0/12

# El ancla de confianza habilita DNSSEC. En Debian, se proporciona el archivo de anclaje de confianza
# por el paquete DNS-Root-Data.
TrustAnchorFile		/usr/share/dns/root.key
# Names servidores 127.0.0.1

# Mapa de dominios en direcciones a claves utilizadas para firmar mensajes
KeyTable           refile:/etc/opendkim/key.table
SigningTable       refile:/etc/opendkim/signing.table

# Un conjunto de hosts internos cuyo correo debe estar firmado
InternalHosts       /etc/opendkim/trusted.hosts

nano config/etc_default_opendkim
Y agrega estas líneas

# Nota: Este es un archivo de configuración heredado. No es utilizado por OpenDkim
# Servicio Systemd. Utilice los parámetros de configuración correspondientes en
# /etc/opendkim.conf en su lugar.
# 
# Anteriormente, uno editaría la configuración predeterminada aquí y luego ejecutaría
# /lib/opendkim/opendkim.service.generate para generar archivos de anulación de Systemd a
# /etc/systemd/system/opendkim.service.d/override.conf y
# /etc/tmpfiles.d/opendkim.conf. Si bien esto todavía es posible, ahora es
# Recomendado para ajustar la configuración directamente en /etc/opendkim.conf.
# 
# Daemon_opts = ""
# Cambiar a/var/spool/postfix/run/opendkim para usar un socket unix con
# Postfix en un chroot:
# Rundir =/was/spool/postfix/run/opendkim
RUNDIR=/run/opendkim
# 
# Descopment para especificar un enchufe alternativo
# Tenga en cuenta que establecer esto anulará cualquier valor de socket en OpenDkim.conf
# por defecto:
SOCKET="local:/var/spool/postfix/opendkim/opendkim.sock"
# Escuche en todas las interfaces en el puerto 54321:
# Socket = inet: 54321
# Escuche en Loopback en el puerto 12345:
# Socket = inet: 12345@localhost
# La escucha es 192.0.2.1 es el puerto 12345:
# Socket = inet: 12345@192.0.2.1
USER=opendkim
GROUP=opendkim
PIDFILE=$RUNDIR/$NAME.pid
EXTRAAFTER=
Cuando estemos listos para configurar nuestro servidor Postfix, ejecutaremos el código a continuación, con el nombre de dominio apropiado incrustado. Comience por crear un script

touch scripts/postfixsetup
sudo chmod a+x scripts/postfixsetup
nano scripts/postfixsetup
Ahora, en Nano, el editor de texto, edite este archivo para que incluya su nombre de dominio en lugar de femmebabe.com.

# !
# Configurar postfix
cd $DIR
echo "Mail services configuration"
sudo cp /etc/postfix/main.cf /etc/postfix/main.cf.backup
sudo cp config/etc_postfix_main.cf /etc/postfix/main.cf
sudo cp config/etc_postfix_master.cf /etc/postfix/master.cf
sudo cp config/etc_default_opendkim /etc/default/opendkim
sudo cp config/etc_dovecot_conf.d_10-auth.conf /etc/dovecot/conf.d/10-auth.conf
sudo cp config/etc_dovecot_conf.d_10-master.conf /etc/dovecot/conf.d/10-master.conf
sudo cp config/etc_dovecot_dovecot.conf /etc/dovecot/dovecot.conf
sudo cp config/etc_dovecot_passwd /etc/dovecot/passwd
sudo cp config/etc_opendkim.conf /etc/opendkim.conf
sudo cp config/etc_default_opendkim /etc/default/opendkim
sudo adduser postfix opendkim
sudo mkdir /etc/opendkim
sudo mkdir /etc/opendkim/keys
sudo mkdir /etc/opendkim/keys/femmebabe.com
sudo mkdir /var/spool/postfix/opendkim
sudo echo "*@femmebabe.com     sendonly._domainkey.femmebabe.com" | sudo tee -a /etc/opendkim/signing.table
sudo echo "sendonly._domainkey.femmebabe.com    femmebabe.com:sendonly:/etc/opendkim/keys/femmebabe.com/sendonly.private" | sudo tee -a /etc/opendkim/key.table
sudo echo "127.0.0.1" | sudo tee -a /etc/opendkim/trusted.hosts
sudo echo "localhost" | sudo tee -a /etc/opendkim/trusted.hosts
sudo echo "" | sudo tee -a /etc/opendkim/trusted.hosts
sudo echo "*.femmebabe.com" | sudo tee -a /etc/opendkim/trusted.hosts
sudo chown -R opendkim:opendkim /etc/opendkim
sudo opendkim-genkey -b 2048 -d femmebabe.com -D /etc/opendkim/keys/femmebabe.com -s sendonly -v
sudo chmod go-rw /etc/opendkim/keys
sudo chown opendkim:opendkim /etc/opendkim/keys/femmebabe.com/sendonly.private
sudo chown opendkim:postfix /var/spool/postfix/opendkim
cd $DIR
sudo cp mailbox/* /var/mail/
sudo chown :users /var/mail/*
sudo chmod -R a+rwx /var/mail/*
sudo systemctl restart opendkim postfix dovecot
sudo cat /etc/opendkim/keys/femmebabe.com/sendonly.txt | tr -d '\n' | sed 's/\s//g' | sed 's/""//g' | awk -F'[)(]' '{print $2}'
Ahora, ejecute el script completo para configurar Postfix, OpenDkim y Dovecot.

./scripts/postfixsetup
Una vez que este script se haya ejecutado, copie la última línea que imprime y péguela en su configuración DNS como el valor de sendOnly._domainkey. Esta es la clave OpenDkim utilizada para identificar su dominio al enviar correo seguro. ¡Impresionante! En unos pocos días, debería poder enviar correo desde el servidor siempre que todo esté configurado correctamente. Si acaba de configurar el DNS para su servidor de correo, debería tardar menos de 72 horas en actualizar los registros. Por lo general, es mucho más rápido. Puede verificar si su servidor está funcionando usando este comando, suministró su correo electrónico:

echo “test” | mail -s “Test Email” youremail@gmail.com
Si todo parece estar funcionando correctamente, debería poder enviar un correo electrónico con su servidor. Si no funciona, intente mirar los registros para ver cuál podría ser el error.

tail –lines 150 /var/log/mail.log
Esto ofrecerá información detallada sobre el correo que está siendo enviado por el servidor y si funciona correctamente. También debería poder ver el correo electrónico en su bandeja de entrada, si no está allí, consulte su carpeta de spam. También deberá configurar su configuración en su configuración.py para que su servidor de correo electrónico pueda hablar con su aplicación Django, el proyecto. Agregue o reemplace estas líneas en su configuración

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)
Observe que estamos utilizando un archivo de configuración para obtener la contraseña. Cargamos este archivo en la configuración así, al comienzo del archivo.:

import os
import json

# Abra y cargue la configuración
with open('/etc/config.json') as config_file:
    config = json.load(config_file)
Creemos este archivo y le agregemos una clave secreta, así como la contraseña de correo. Para generar una clave secreta, use este comando, con cualquier longitud que desee al final:

openssl rand -base64 64
Ahora, copie el texto que OpenSSL generó y edita /etc/config.json

sudo nano /etc/config.json
Agregue las siguientes líneas a su archivo, con la clave que OpenSSL generó como la clave secreta.

{
	"SECRET_KEY": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX-generated-using-openssl)",
	"EMAIL_HOST_PASSWORD": "yourpassword"
}
El formato JSON es simple y fácil de usar, podemos declarar otras claves que queremos usar en nuestro proyecto de esta manera también, y mantenerlas separadas de nuestro directorio de proyectos para que otros usuarios no puedan escribirlas y para que no puedan leerlas solo de nuestro directorio de proyectos. Esta es la práctica recomendada para las claves API, de las cuales usaremos más de unos pocos aquí. También querrá hacer una copia de seguridad de su proyecto para asegurarse de que todo esté guardado y podrá recuperar su trabajo más tarde, incluso si ya no desea alquilar un servidor.

sudo backup
Ahora, intente enviar un correo electrónico HTML desde el servidor web, siempre que el envío de uno desde la línea de comando esté funcionando. Consulte su instancia de usuario en el shell y envíe un correo electrónico HTML a ese usuario a través de Django. Cambie mi nombre en el código, Charlotte, a su nombre de usuario.

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 el primer comando no funciona, asegúrese de usar

source venv/bin/activate
Siempre que todo esté configurado correctamente, ahora recibirá un correo electrónico de bienvenida en su buzón enviado por su aplicación web. ¡Buen trabajo! Has recorrido un largo camino. Quería agregar, si alguna vez está luchando con algún error mientras trabaja en un proyecto como este, no dude en buscar respuestas y solicite ayuda. Google, entre otros motores de búsqueda, son excelentes recursos para buscar ayuda de programación. Simplemente busque el error que está recibiendo, y podrá ver cómo otras personas resuelven el problema. Además, puede contactarme a mí, a sus educadores (maestros, profesores, tutores), cualquier compañero en Internet que esté disponible para la ayuda de programación, o consulte a este libro nuevamente u otros recursos para encontrar soluciones a los problemas que está experimentando. Entiendo que esto no es fácil, pero incluso si ha leído a esta distancia y no está escribiendo ningún código, está aprendiendo mucho sobre la creación de una aplicación web desde cero. Darle palmaditas en la espalda, estás haciendo un grantrabajo. Gracias por tomarse el tiempo de leer esta Guía de Desarrollo Web de Tercera Edición. En futuras ediciones, incluiré más ejemplos importantes discutidos al comienzo del documento y nos sumergiremos mucho más en el mundo del desarrollo de software y hardware. Estén atentos para lo que vendrá, y espero poder enseñarle cómo construir un software increíble. Nos vemos en el siguiente






Cerca
Página 1
Saltar
Ver artículo completo
Continuar leyendo

Comprar | Comprar con cripto



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


(Haga clic o toque para descargar la imagen)
Entretenimiento profesional, fotos, videos, audio, transmisión en vivo y juego casual, así como escaneo de identificación, desarrollo web y servicios de subrogación.

Déjame un consejo en Bitcoin usando esta dirección: 3KhDWoSve2N627RiW8grj6XrsoPT7d6qyE

© Glam Girl X 2025

Términos de servicio