mirror of
https://gitlab.com/news-flash/article_scraper.git
synced 2025-07-07 08:05:31 +02:00
thumbnail: check for attribute with name property as well (fixes #4)
This commit is contained in:
parent
b3ce28632d
commit
11ee29feda
3 changed files with 839 additions and 1 deletions
808
article_scraper/resources/tests/thumbnails/a-chacon.html
Normal file
808
article_scraper/resources/tests/thumbnails/a-chacon.html
Normal file
|
@ -0,0 +1,808 @@
|
|||
<!doctype html>
|
||||
<html lang="es">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link href="/assets/css/dist-style.css" rel="stylesheet" />
|
||||
<link href="/assets/css/fonts.css" rel="stylesheet" />
|
||||
|
||||
<script src="https://unpkg.com/typeit@8.8.4/dist/index.umd.js"></script>
|
||||
<!-- Begin Jekyll SEO tag v2.8.0 -->
|
||||
<title>PoC: Usando el Generador de Autenticación de Rails 8 (Beta) En Modo API-Only. | a-chacon</title>
|
||||
<meta name="generator" content="Jekyll v4.3.4" />
|
||||
<meta property="og:title" content="PoC: Usando el Generador de Autenticación de Rails 8 (Beta) En Modo API-Only." />
|
||||
<meta name="author" content="Andrés" />
|
||||
<meta property="og:locale" content="en_US" />
|
||||
<meta name="description" content="Haciendo funcionar el generador de autenticación de Rails 8 (Beta) en una aplicación creada en modo API-Only." />
|
||||
<meta property="og:description" content="Haciendo funcionar el generador de autenticación de Rails 8 (Beta) en una aplicación creada en modo API-Only." />
|
||||
<link rel="canonical" href="https://a-chacon.com/on%20rails/2024/10/16/poc-using-rails-8-auth-system-in-api-only.html" />
|
||||
<meta property="og:url" content="https://a-chacon.com/on%20rails/2024/10/16/poc-using-rails-8-auth-system-in-api-only.html" />
|
||||
<meta property="og:site_name" content="a-chacon" />
|
||||
<meta property="og:image" content="https://a-chacon.com/assets/images/rails8-poc-api-auth.webp" />
|
||||
<meta property="og:type" content="article" />
|
||||
<meta property="article:published_time" content="2024-10-16T00:00:00+00:00" />
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<meta property="twitter:image" content="https://a-chacon.com/assets/images/rails8-poc-api-auth.webp" />
|
||||
<meta property="twitter:title" content="PoC: Usando el Generador de Autenticación de Rails 8 (Beta) En Modo API-Only." />
|
||||
<script type="application/ld+json">
|
||||
{"@context":"https://schema.org","@type":"BlogPosting","author":{"@type":"Person","name":"Andrés"},"dateModified":"2024-10-16T00:00:00+00:00","datePublished":"2024-10-16T00:00:00+00:00","description":"Haciendo funcionar el generador de autenticación de Rails 8 (Beta) en una aplicación creada en modo API-Only.","headline":"PoC: Usando el Generador de Autenticación de Rails 8 (Beta) En Modo API-Only.","image":"https://a-chacon.com/assets/images/rails8-poc-api-auth.webp","mainEntityOfPage":{"@type":"WebPage","@id":"https://a-chacon.com/on%20rails/2024/10/16/poc-using-rails-8-auth-system-in-api-only.html"},"publisher":{"@type":"Organization","logo":{"@type":"ImageObject","url":"https://a-chacon.com/assets/images/keyboard-key-a.png"},"name":"Andrés"},"url":"https://a-chacon.com/on%20rails/2024/10/16/poc-using-rails-8-auth-system-in-api-only.html"}</script>
|
||||
<!-- End Jekyll SEO tag -->
|
||||
<link type="application/atom+xml" rel="alternate" href="https://a-chacon.com/feed.xml" title="a-chacon" />
|
||||
|
||||
<script data-host="https://app.microanalytics.io" data-dnt="false" src="https://app.microanalytics.io/js/script.js"
|
||||
id="ZwSg9rf6GA" async defer></script>
|
||||
|
||||
<style>
|
||||
.mini-banner {
|
||||
background-color: #6eea8e;
|
||||
/* Light red background color */
|
||||
color: #1c2a18;
|
||||
/* Dark red text color */
|
||||
padding: 10px 20px;
|
||||
/* Padding around the text */
|
||||
text-align: center;
|
||||
/* Center the text */
|
||||
position: fixed;
|
||||
/* Fix the banner to the top of the page */
|
||||
bottom: 0;
|
||||
/* Position at the very top */
|
||||
left: 0;
|
||||
/* Full width */
|
||||
right: 0;
|
||||
/* Full width */
|
||||
z-index: 1000;
|
||||
/* Ensure it's above other elements */
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
/* Slight shadow for depth */
|
||||
}
|
||||
|
||||
.mini-banner p {
|
||||
margin: 0;
|
||||
/* Remove default paragraph margin */
|
||||
font-size: 16px;
|
||||
/* Font size */
|
||||
font-weight: bold;
|
||||
/* Bold text */
|
||||
}
|
||||
|
||||
body {
|
||||
margin-top: 60px;
|
||||
/* Adjust based on the banner height */
|
||||
}
|
||||
</style>
|
||||
<div class="mini-banner">
|
||||
<p>Building an API with Rails? Discover
|
||||
<a href="https://github.com/a-chacon/oas_rails" style="color: #826cf6; text-decoration: underline;">
|
||||
OasRails</a>, a Rails engine for generate automatic interactive documentation.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</head>
|
||||
|
||||
<body class="bg-white text-gray-900 dark:bg-gray-700 dark:text-slate-100">
|
||||
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
new TypeIt("h1", {}).go();
|
||||
});
|
||||
</script>
|
||||
<nav class="absolute top-0 left-0 right-0 flex w-full flex-wrap items-center justify-between py-4">
|
||||
<div class="flex w-full flex-wrap items-center justify-between px-5">
|
||||
<a class="flex" href="/" aria-label="Andres Chacon - Developer">
|
||||
<img src="/assets/images/keyboard-key-a.png" alt="webpage logo" class="block h-8 w-auto">
|
||||
<p class="pl-4 hidden md:block text-lg font-bold">a-chacon <span
|
||||
class="text-mulish text-sm font-normal">~/on%20rails/2024/10/16/poc-using-rails-8-auth-system-in-api-only.html</span>
|
||||
</p>
|
||||
</a>
|
||||
|
||||
<div class="flex divide-x">
|
||||
|
||||
|
||||
|
||||
|
||||
<div><a class="px-4 hover:text-sky-400 "
|
||||
href="/blog.html">Blog</a></div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div><a class="px-4 hover:text-sky-400 "
|
||||
href="/projects">Proyectos</a></div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="language-switcher relative">
|
||||
<button class="current-language px-4 hover:text-sky-400">
|
||||
ES
|
||||
</button>
|
||||
|
||||
<div class="dropdown-content absolute hidden mt-2 py-2 bg-white rounded shadow-lg z-20">
|
||||
|
||||
|
||||
<a class="p-4 hover:text-sky-400" href="/en/on%20rails/2024/10/16/poc-using-rails-8-auth-system-in-api-only.html">EN</a>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const currentLangButton = document.querySelector('.current-language');
|
||||
const dropdownContent = document.querySelector('.dropdown-content');
|
||||
|
||||
if (currentLangButton && dropdownContent) {
|
||||
currentLangButton.addEventListener('click', function () {
|
||||
dropdownContent.classList.toggle('hidden');
|
||||
});
|
||||
|
||||
document.addEventListener('click', function (event) {
|
||||
if (!currentLangButton.contains(event.target) && !dropdownContent.contains(event.target)) {
|
||||
dropdownContent.classList.add('hidden');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</nav>
|
||||
<link rel="stylesheet" href="/assets/css/monokai.css">
|
||||
<div class="container mx-auto">
|
||||
<div id="post" class="prose max-w-[75ch] prose-neutral mx-auto pb-10 pt-20 lg:pt-[90px] lg:pb-20">
|
||||
<div class="pb-4">
|
||||
<a href="https://a-chacon.com/category/on-rails"
|
||||
class="no-underline font-mulish text-sm hover:text-sky-400 text-gray-500">
|
||||
ON RAILS
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h1 class="text-left text-2xl font-bold md:text-5xl text-black">PoC: Usando el Generador de Autenticación de Rails 8 (Beta) En Modo API-Only.</h1>
|
||||
|
||||
<div class="flex flex-col md:flex-row text-sm font-mulish justify-between items-center">
|
||||
<div class="flex">
|
||||
<p>Andrés</p>
|
||||
<p class="px-2">•</p>
|
||||
<p>16 October 2024</p>
|
||||
<p class="px-2">•</p>
|
||||
<p>
|
||||
|
||||
6 mins </p>
|
||||
</div>
|
||||
<div>
|
||||
<div class="sharing-buttons flex flex-wrap gap-1 justify-center ">
|
||||
<a class="border-2 duration-200 ease inline-flex items-center mb-1 mr-1 transition p-2 rounded-full text-white border-slate-600 bg-slate-600 hover:bg-slate-700 hover:border-slate-700"
|
||||
target="_blank" rel="noopener"
|
||||
href="https://facebook.com/sharer/sharer.php?u=https://a-chacon.com/on%20rails/2024/10/16/poc-using-rails-8-auth-system-in-api-only.html"
|
||||
aria-label="Share on Facebook">
|
||||
<svg aria-hidden="true" fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"
|
||||
class="w-5 h-5">
|
||||
<title>Facebook</title>
|
||||
<path
|
||||
d="M379 22v75h-44c-36 0-42 17-42 41v54h84l-12 85h-72v217h-88V277h-72v-85h72v-62c0-72 45-112 109-112 31 0 58 3 65 4z">
|
||||
</path>
|
||||
</svg>
|
||||
</a>
|
||||
<a class="border-2 duration-200 ease inline-flex items-center mb-1 mr-1 transition p-2 rounded-full text-white border-slate-600 bg-slate-600 hover:bg-slate-700 hover:border-slate-700"
|
||||
target="_blank" rel="noopener"
|
||||
href="https://twitter.com/intent/tweet?url=https://a-chacon.com/on%20rails/2024/10/16/poc-using-rails-8-auth-system-in-api-only.html&text=PoC: Usando el Generador de Autenticación de Rails 8 (Beta) En Modo API-Only."
|
||||
aria-label="Share on Twitter">
|
||||
<svg aria-hidden="true" fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"
|
||||
class="w-5 h-5">
|
||||
<title>Twitter</title>
|
||||
<path
|
||||
d="m459 152 1 13c0 139-106 299-299 299-59 0-115-17-161-47a217 217 0 0 0 156-44c-47-1-85-31-98-72l19 1c10 0 19-1 28-3-48-10-84-52-84-103v-2c14 8 30 13 47 14A105 105 0 0 1 36 67c51 64 129 106 216 110-2-8-2-16-2-24a105 105 0 0 1 181-72c24-4 47-13 67-25-8 24-25 45-46 58 21-3 41-8 60-17-14 21-32 40-53 55z">
|
||||
</path>
|
||||
</svg>
|
||||
</a>
|
||||
<a class="border-2 duration-200 ease inline-flex items-center mb-1 mr-1 transition p-2 rounded-full text-white border-slate-600 bg-slate-600 hover:bg-slate-700 hover:border-slate-700"
|
||||
target="_blank" rel="noopener"
|
||||
href="https://www.linkedin.com/shareArticle?mini=true&url=https://a-chacon.com/on%20rails/2024/10/16/poc-using-rails-8-auth-system-in-api-only.html&title=PoC: Usando el Generador de Autenticación de Rails 8 (Beta) En Modo API-Only.&summary=PoC: Usando el Generador de Autenticación de Rails 8 (Beta) En Modo API-Only.&source=https://a-chacon.com"
|
||||
aria-label="Share on Linkedin">
|
||||
<svg aria-hidden="true" fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"
|
||||
class="w-5 h-5">
|
||||
<title>Linkedin</title>
|
||||
<path
|
||||
d="M136 183v283H42V183h94zm6-88c1 27-20 49-53 49-32 0-52-22-52-49 0-28 21-49 53-49s52 21 52 49zm333 208v163h-94V314c0-38-13-64-47-64-26 0-42 18-49 35-2 6-3 14-3 23v158h-94V183h94v41c12-20 34-48 85-48 62 0 108 41 108 127z">
|
||||
</path>
|
||||
</svg>
|
||||
</a>
|
||||
<a class="border-2 duration-200 ease inline-flex items-center mb-1 mr-1 transition p-2 rounded-full text-white border-slate-600 bg-slate-600 hover:bg-slate-700 hover:border-slate-700"
|
||||
target="_blank" rel="noopener"
|
||||
href="https://pinterest.com/pin/create/button/?url=https://a-chacon.com/on%20rails/2024/10/16/poc-using-rails-8-auth-system-in-api-only.html&media=https://a-chacon.com&description=PoC: Usando el Generador de Autenticación de Rails 8 (Beta) En Modo API-Only."
|
||||
aria-label="Share on Pinterest" draggable="false">
|
||||
<svg aria-hidden="true" fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"
|
||||
class="w-5 h-5">
|
||||
<title>Pinterest</title>
|
||||
<path
|
||||
d="M268 6C165 6 64 75 64 186c0 70 40 110 64 110 9 0 15-28 15-35 0-10-24-30-24-68 0-81 62-138 141-138 68 0 118 39 118 110 0 53-21 153-90 153-25 0-46-18-46-44 0-38 26-74 26-113 0-67-94-55-94 25 0 17 2 36 10 51-14 60-42 148-42 209 0 19 3 38 4 57 4 3 2 3 7 1 51-69 49-82 72-173 12 24 44 36 69 36 106 0 154-103 154-196C448 71 362 6 268 6z">
|
||||
</path>
|
||||
</svg>
|
||||
</a>
|
||||
<a class="border-2 duration-200 ease inline-flex items-center mb-1 mr-1 transition p-2 rounded-full text-white border-slate-600 bg-slate-600 hover:bg-slate-700 hover:border-slate-700"
|
||||
target="_blank" rel="noopener"
|
||||
href="https://reddit.com/submit/?url=https://a-chacon.com/on%20rails/2024/10/16/poc-using-rails-8-auth-system-in-api-only.html&resubmit=true&title=PoC: Usando el Generador de Autenticación de Rails 8 (Beta) En Modo API-Only."
|
||||
aria-label="Share on Reddit" draggable="false">
|
||||
<svg aria-hidden="true" fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"
|
||||
class="w-5 h-5">
|
||||
<title>Reddit</title>
|
||||
<path
|
||||
d="M440 204c-15 0-28 6-38 15-35-24-83-40-137-42l28-125 88 20c0 22 18 39 39 39 22 0 40-18 40-39s-17-40-40-40c-15 0-28 9-35 22l-97-22c-5-1-10 3-11 7l-31 138c-53 2-100 18-136 43a53 53 0 0 0-38-16c-56 0-74 74-23 100l-3 24c0 84 95 152 210 152 117 0 211-68 211-152 0-8-1-17-3-25 50-25 32-99-24-99zM129 309a40 40 0 1 1 80 0 40 40 0 0 1-80 0zm215 93c-37 37-139 37-176 0-4-3-4-9 0-13s10-4 13 0c28 28 120 29 149 0 4-4 10-4 14 0s4 10 0 13zm-1-54c-22 0-39-17-39-39a39 39 0 1 1 39 39z">
|
||||
</path>
|
||||
</svg>
|
||||
</a>
|
||||
<a class="border-2 duration-200 ease inline-flex items-center mb-1 mr-1 transition p-2 rounded-full text-white border-slate-600 bg-slate-600 hover:bg-slate-700 hover:border-slate-700"
|
||||
target="_blank" rel="noopener"
|
||||
href="https://wa.me/?text=PoC:%20Usando%20el%20Generador%20de%20Autenticaci%C3%B3n%20de%20Rails%208%20(Beta)%20En%20Modo%20API-Only.%20https://a-chacon.com"
|
||||
aria-label="Share on Whatsapp" draggable="false">
|
||||
<svg aria-hidden="true" fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"
|
||||
class="w-5 h-5">
|
||||
<title>Whatsapp</title>
|
||||
<path
|
||||
d="M413 97A222 222 0 0 0 64 365L31 480l118-31a224 224 0 0 0 330-195c0-59-25-115-67-157zM256 439c-33 0-66-9-94-26l-7-4-70 18 19-68-4-7a185 185 0 0 1 287-229c34 36 56 82 55 131 1 102-84 185-186 185zm101-138c-5-3-33-17-38-18-5-2-9-3-12 2l-18 22c-3 4-6 4-12 2-32-17-54-30-75-66-6-10 5-10 16-31 2-4 1-7-1-10l-17-41c-4-10-9-9-12-9h-11c-4 0-9 1-15 7-5 5-19 19-19 46s20 54 23 57c2 4 39 60 94 84 36 15 49 17 67 14 11-2 33-14 37-27s5-24 4-26c-2-2-5-4-11-6z">
|
||||
</path>
|
||||
</svg>
|
||||
</a>
|
||||
<a class="border-2 duration-200 ease inline-flex items-center mb-1 mr-1 transition p-2 rounded-full text-white border-slate-600 bg-slate-600 hover:bg-slate-700 hover:border-slate-700"
|
||||
target="_blank" rel="noopener"
|
||||
href="https://telegram.me/share/url?text=PoC:%20Usando%20el%20Generador%20de%20Autenticaci%C3%B3n%20de%20Rails%208%20(Beta)%20En%20Modo%20API-Only.&url=https://a-chacon.com/on%20rails/2024/10/16/poc-using-rails-8-auth-system-in-api-only.html"
|
||||
aria-label="Share on Telegram" draggable="false">
|
||||
<svg aria-hidden="true" fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"
|
||||
class="w-5 h-5">
|
||||
<title>Telegram</title>
|
||||
<path
|
||||
d="M256 8a248 248 0 1 0 0 496 248 248 0 0 0 0-496zm115 169c-4 39-20 134-28 178-4 19-10 25-17 25-14 2-25-9-39-18l-56-37c-24-17-8-25 6-40 3-4 67-61 68-67l-1-4-5-1q-4 1-105 70-15 10-27 9c-9 0-26-5-38-9-16-5-28-7-27-16q1-7 18-14l145-62c69-29 83-34 92-34 2 0 7 1 10 3l4 7a43 43 0 0 1 0 10z">
|
||||
</path>
|
||||
</svg>
|
||||
</a>
|
||||
<a class="border-2 duration-200 ease inline-flex items-center mb-1 mr-1 transition p-2 rounded-full text-white border-slate-600 bg-slate-600 hover:bg-slate-700 hover:border-slate-700" target="_blank" rel="noopener" href="/cdn-cgi/l/email-protection#68571b1d0a020d0b1c5538072b524d5a583d1b09060c074d5a580d044d5a582f0d060d1a090c071a4d5a580c0d4d5a58291d1c0d061c010b090b014d2b5b4d2a5b064d5a580c0d4d5a583a0901041b4d5a58504d5a58402a0d1c09414d5a582d064d5a5825070c074d5a582938214527060411464e0a070c1155001c1c181b52474709450b00090b0706460b07054707064d5a581a0901041b475a585a5c47595847595e4718070b451d1b01060f451a0901041b455045091d1c00451b111b1c0d0545010645091801450706041146001c0504" aria-label="Share by Email" draggable="false">
|
||||
<svg aria-hidden="true" fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"
|
||||
class="w-5 h-5">
|
||||
<title>Email</title>
|
||||
<path
|
||||
d="M464 64a48 48 0 0 1 29 86L275 314c-11 8-27 8-38 0L19 150a48 48 0 0 1 29-86h416zM218 339c22 17 54 17 76 0l218-163v208c0 35-29 64-64 64H64c-35 0-64-29-64-64V176l218 163z">
|
||||
</path>
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<img class="w-full" src="/assets/images/rails8-poc-api-auth.webp" alt="PoC: Usando el Generador de Autenticación de Rails 8 (Beta) En Modo API-Only.">
|
||||
|
||||
<div class="my-6">
|
||||
<p>Como ya saben, una de las funcionalidades nuevas de Rails 8 es el <strong>nuevo generador básico de autenticación</strong> que viene a demostrar que no es tan complejo desarrollar todo lo que respecta a autenticación en una aplicación con Rails y que muchas veces no es necesario depender de terceros (gemas). La discusión comenzó <a href="https://github.com/rails/rails/issues/50446">aquí</a>.</p>
|
||||
|
||||
<p>Dicho esto, veamos que pasa usando el generador en una aplicación API-Only:</p>
|
||||
|
||||
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> rails <span class="nt">-v</span>
|
||||
Rails 8.0.0.beta1
|
||||
</code></pre></div></div>
|
||||
|
||||
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> rails new app <span class="nt">--api</span> & <span class="nb">cd </span>app
|
||||
</code></pre></div></div>
|
||||
|
||||
<p>Y ejecutamos el nuevo comando:</p>
|
||||
|
||||
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> rails g authentication
|
||||
create app/models/session.rb
|
||||
create app/models/user.rb
|
||||
create app/models/current.rb
|
||||
create app/controllers/sessions_controller.rb
|
||||
create app/controllers/concerns/authentication.rb
|
||||
create app/controllers/passwords_controller.rb
|
||||
create app/mailers/passwords_mailer.rb
|
||||
create app/views/passwords_mailer/reset.html.erb
|
||||
create app/views/passwords_mailer/reset.text.erb
|
||||
create <span class="nb">test</span>/mailers/previews/passwords_mailer_preview.rb
|
||||
gsub app/controllers/application_controller.rb
|
||||
route resources :passwords, param: :token
|
||||
route resource :session
|
||||
gsub Gemfile
|
||||
bundle <span class="nb">install</span> <span class="nt">--quiet</span>
|
||||
generate migration CreateUsers email_address:string!:uniq password_digest:string! <span class="nt">--force</span>
|
||||
rails generate migration CreateUsers email_address:string!:uniq password_digest:string! <span class="nt">--force</span>
|
||||
invoke active_record
|
||||
create db/migrate/20241016002139_create_users.rb
|
||||
generate migration CreateSessions user:references ip_address:string user_agent:string <span class="nt">--force</span>
|
||||
rails generate migration CreateSessions user:references ip_address:string user_agent:string <span class="nt">--force</span>
|
||||
invoke active_record
|
||||
create db/migrate/20241016002140_create_sessions.rb
|
||||
</code></pre></div></div>
|
||||
|
||||
<p>Ok, ahora por ejemplo, si revisamos <code class="language-plaintext highlighter-rouge">SessionsController</code> veremos que el método de <code class="language-plaintext highlighter-rouge">Login</code> se ve de la siguiente forma:</p>
|
||||
|
||||
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="k">def</span> <span class="nf">create</span>
|
||||
<span class="k">if</span> <span class="n">user</span> <span class="o">=</span> <span class="no">User</span><span class="p">.</span><span class="nf">authenticate_by</span><span class="p">(</span><span class="n">params</span><span class="p">.</span><span class="nf">permit</span><span class="p">(</span><span class="ss">:email_address</span><span class="p">,</span> <span class="ss">:password</span><span class="p">))</span>
|
||||
<span class="n">start_new_session_for</span> <span class="n">user</span>
|
||||
<span class="n">redirect_to</span> <span class="n">after_authentication_url</span>
|
||||
<span class="k">else</span>
|
||||
<span class="n">redirect_to</span> <span class="n">new_session_url</span><span class="p">,</span> <span class="ss">alert: </span><span class="s2">"Try another email address or password."</span>
|
||||
<span class="k">end</span>
|
||||
<span class="k">end</span>
|
||||
</code></pre></div></div>
|
||||
|
||||
<p>O sea, redirecciona a rutas y/o vistas que en nuestra API no existen ni hacen sentido, y además si inspeccionamos el metodo <code class="language-plaintext highlighter-rouge">start_new_session_for</code> nos daremos cuenta de que el sistema está basado 100% en <strong>autenticación mediante cookies</strong>. Entonces, ¿qué hacemos?</p>
|
||||
|
||||
<p>Mi propuesta es la siguiente: el generador crea las bases para la autenticación y creo que funciona bastante bien, por lo que con unas pequeñas modificaciones podemos dejar funcionando una <strong>autenticación Bearer</strong> (Token Authentication) rápidamente en nuestra API con Rails 8 más los archivos ya generados.</p>
|
||||
|
||||
<p>El primer paso será agregar <strong>persistencia para nuestro token</strong>, para esto modificaremos la migración que crea las sessiones y agregaremos un nuevo campo llamado <code class="language-plaintext highlighter-rouge">token</code>:</p>
|
||||
|
||||
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="n">create_table</span> <span class="ss">:sessions</span> <span class="k">do</span> <span class="o">|</span><span class="n">t</span><span class="o">|</span>
|
||||
<span class="n">t</span><span class="p">.</span><span class="nf">references</span> <span class="ss">:user</span><span class="p">,</span> <span class="ss">null: </span><span class="kp">false</span><span class="p">,</span> <span class="ss">foreign_key: </span><span class="kp">true</span>
|
||||
<span class="n">t</span><span class="p">.</span><span class="nf">string</span> <span class="ss">:ip_address</span>
|
||||
<span class="n">t</span><span class="p">.</span><span class="nf">string</span> <span class="ss">:user_agent</span>
|
||||
<span class="n">t</span><span class="p">.</span><span class="nf">string</span> <span class="ss">:token</span> <span class="c1"># HERE</span>
|
||||
|
||||
<span class="n">t</span><span class="p">.</span><span class="nf">timestamps</span>
|
||||
<span class="k">end</span>
|
||||
</code></pre></div></div>
|
||||
|
||||
<p>Ahora simplemente ejecuta <code class="language-plaintext highlighter-rouge">rails db:migrate</code> y create un usuario de prueba por consola, yo lo haré con esta línea <code class="language-plaintext highlighter-rouge">User.create(email_address: "<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="a6d3d5c3d4e6d2c3d5d288c5c9cb">[email protected]</a>", password: "123456789")</code> (Lo utilizaremos más tarde). Luego debemos crear un nuevo token para cada sesión nueva de un usuario, para esto lo más simple es usar un callback en el modelo <code class="language-plaintext highlighter-rouge">Session</code>:</p>
|
||||
|
||||
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># app/models/sessions.rb</span>
|
||||
<span class="k">class</span> <span class="nc">Session</span> <span class="o"><</span> <span class="no">ApplicationRecord</span>
|
||||
<span class="n">belongs_to</span> <span class="ss">:user</span>
|
||||
<span class="n">before_create</span> <span class="ss">:generate_token</span> <span class="c1"># Here call</span>
|
||||
|
||||
<span class="kp">private</span>
|
||||
<span class="k">def</span> <span class="nf">generate_token</span> <span class="c1"># Here implement, generate the token as you wish.</span>
|
||||
<span class="nb">self</span><span class="p">.</span><span class="nf">token</span> <span class="o">=</span> <span class="no">Digest</span><span class="o">::</span><span class="no">SHA1</span><span class="p">.</span><span class="nf">hexdigest</span><span class="p">([</span> <span class="no">Time</span><span class="p">.</span><span class="nf">now</span><span class="p">,</span> <span class="nb">rand</span> <span class="p">].</span><span class="nf">join</span><span class="p">)</span>
|
||||
<span class="k">end</span>
|
||||
<span class="k">end</span>
|
||||
</code></pre></div></div>
|
||||
|
||||
<p>Ahora volviendo al metodo <code class="language-plaintext highlighter-rouge">start_new_session_for</code> en el concern <code class="language-plaintext highlighter-rouge">Authentication</code>, no es necesario que creemos una cookie, asi que debemos remover esa linea y dejar el metodo de la siguiente forma:</p>
|
||||
|
||||
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># app/controllers/concerns/authentication.rb</span>
|
||||
<span class="k">def</span> <span class="nf">start_new_session_for</span><span class="p">(</span><span class="n">user</span><span class="p">)</span>
|
||||
<span class="n">user</span><span class="p">.</span><span class="nf">sessions</span><span class="p">.</span><span class="nf">create!</span><span class="p">(</span><span class="ss">user_agent: </span><span class="n">request</span><span class="p">.</span><span class="nf">user_agent</span><span class="p">,</span> <span class="ss">ip_address: </span><span class="n">request</span><span class="p">.</span><span class="nf">remote_ip</span><span class="p">).</span><span class="nf">tap</span> <span class="k">do</span> <span class="o">|</span><span class="n">session</span><span class="o">|</span>
|
||||
<span class="no">Current</span><span class="p">.</span><span class="nf">session</span> <span class="o">=</span> <span class="n">session</span>
|
||||
<span class="k">end</span>
|
||||
<span class="k">end</span>
|
||||
</code></pre></div></div>
|
||||
|
||||
<p>Y modificaremos el <code class="language-plaintext highlighter-rouge">create</code> de <code class="language-plaintext highlighter-rouge">SessionsController</code> para que las respuestas sean en formato json y no redirecciones:</p>
|
||||
|
||||
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># app/controllers/sessions_controller.rb</span>
|
||||
<span class="k">def</span> <span class="nf">create</span>
|
||||
<span class="k">if</span> <span class="n">user</span> <span class="o">=</span> <span class="no">User</span><span class="p">.</span><span class="nf">authenticate_by</span><span class="p">(</span><span class="n">params</span><span class="p">.</span><span class="nf">permit</span><span class="p">(</span><span class="ss">:email_address</span><span class="p">,</span> <span class="ss">:password</span><span class="p">))</span>
|
||||
<span class="n">start_new_session_for</span> <span class="n">user</span>
|
||||
<span class="n">render</span> <span class="ss">json: </span><span class="p">{</span> <span class="ss">data: </span><span class="p">{</span> <span class="ss">token: </span><span class="no">Current</span><span class="p">.</span><span class="nf">session</span><span class="p">.</span><span class="nf">token</span> <span class="p">}</span> <span class="p">}</span>
|
||||
<span class="k">else</span>
|
||||
<span class="n">render</span> <span class="ss">json: </span><span class="p">{},</span> <span class="ss">status: :unauthorized</span>
|
||||
<span class="k">end</span>
|
||||
<span class="k">end</span>
|
||||
</code></pre></div></div>
|
||||
|
||||
<p><strong>Para hacer que todo esto funcione debemos hacer dos cosas:</strong></p>
|
||||
|
||||
<ol>
|
||||
<li>
|
||||
<p>Incluir el modulo <code class="language-plaintext highlighter-rouge">Authentication</code> en <code class="language-plaintext highlighter-rouge">ApplicationController</code>:</p>
|
||||
|
||||
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># app/controllers/application_controller.rb</span>
|
||||
<span class="k">class</span> <span class="nc">ApplicationController</span> <span class="o"><</span> <span class="no">ActionController</span><span class="o">::</span><span class="no">API</span>
|
||||
<span class="kp">include</span> <span class="no">Authentication</span>
|
||||
<span class="k">end</span>
|
||||
</code></pre></div> </div>
|
||||
</li>
|
||||
<li>
|
||||
<p>Eliminar la linea numero 6 de este mismo concern:</p>
|
||||
|
||||
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># app/controllers/concerns/authentication.rb</span>
|
||||
<span class="n">included</span> <span class="k">do</span>
|
||||
<span class="n">before_action</span> <span class="ss">:require_authentication</span>
|
||||
<span class="n">helper_method</span> <span class="ss">:authenticated?</span> <span class="c1"># This, we don't use helpers in APIs</span>
|
||||
<span class="k">end</span>
|
||||
</code></pre></div> </div>
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<p>Hasta este punto ya deberíamos tener el <strong>login funcionando</strong>. Para probar esto voy a agregar <a href="https://github.com/a-chacon/oas_rails">OasRails</a>, que a propósito <strong>ya está funcionando con Rails 8</strong> y voy a enviar un par de peticiones a ver como se comporta, no explicaré como implementar OasRails, para eso puedes ver el repositorio o leer más en <a href="/on rails/2024/07/25/documenting-rails-apis">este post</a>.</p>
|
||||
|
||||
<p>Inicio de sesión exitoso:</p>
|
||||
|
||||
<p><img src="/assets/images/rails8_success_login.png" alt="" /></p>
|
||||
|
||||
<p>Inicio de sesión fallido:</p>
|
||||
|
||||
<p><img src="/assets/images/rails8_fail_login.png" alt="" /></p>
|
||||
|
||||
<hr />
|
||||
|
||||
<p>Ya podemos generar tokens, ahora modificaremos el código para <strong>autenticarnos con ese mismo token</strong>. Para eso, cambiaremos la lógica de buscar la sesión actual del usuario con base en la cookie a buscarla basándonos en la cabecera <code class="language-plaintext highlighter-rouge">Authorization</code>:</p>
|
||||
|
||||
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
|
||||
<span class="c1"># app/controllers/concerns/authentication.rb</span>
|
||||
<span class="k">def</span> <span class="nf">resume_session</span>
|
||||
<span class="no">Current</span><span class="p">.</span><span class="nf">session</span> <span class="o">=</span> <span class="n">find_session_by_token</span>
|
||||
<span class="k">end</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">find_session_by_cookie</span>
|
||||
<span class="no">Session</span><span class="p">.</span><span class="nf">find_by</span><span class="p">(</span><span class="ss">token: </span><span class="n">request</span><span class="p">.</span><span class="nf">headers</span><span class="p">[</span><span class="ss">:authorization</span><span class="p">]</span><span class="o">&</span><span class="p">.</span><span class="nf">split</span><span class="p">(</span><span class="s2">" "</span><span class="p">)[</span><span class="o">-</span><span class="mi">1</span><span class="p">])</span>
|
||||
<span class="k">end</span>
|
||||
</code></pre></div></div>
|
||||
|
||||
<p>Para probar esto creo que tendremos que hacer rápidamente un modelo que dependa de <code class="language-plaintext highlighter-rouge">User</code> y que requiera autenticación para utilizar. Intentemos con <code class="language-plaintext highlighter-rouge">rails g scaffold project title:string description:text user:references</code> y le agregamos al principio del controlador la línea de código <code class="language-plaintext highlighter-rouge">before_action :require_authentication</code>.</p>
|
||||
|
||||
<p>Aquí les dejo una pequeña prueba del index de Projects autenticado con el token que obtuve en las pruebas anteriores:</p>
|
||||
|
||||
<p><img src="/assets/images/rails8_projects.png" alt="" /></p>
|
||||
|
||||
<hr />
|
||||
|
||||
<p>Con esto ya tienes gran parte de la lógica de autenticación funcionando en la aplicación API-Only. Te queda continuar con las modificaciones en el resto de los endpoints para que las respuestas sean en formato json y no supuestas vistas que no existen en la aplicación.</p>
|
||||
|
||||
<p>Probablemente de aquí a que se lance la versión final de Rails 8 aparezca un <strong>PR solucionando esto y el generador funcione correctamente en modo API-Only</strong>. Hasta entonces, con estas pequeñas modificaciones ya puedes seguir construyendo tu API.</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="mx-auto py-8">
|
||||
<p class="text-center font-extrabold italic">
|
||||
¿Te gustó? ¡Compártelo!
|
||||
</p>
|
||||
<div class="sharing-buttons flex flex-wrap gap-1 justify-center ">
|
||||
<a class="border-2 duration-200 ease inline-flex items-center mb-1 mr-1 transition p-2 rounded-full text-white border-slate-600 bg-slate-600 hover:bg-slate-700 hover:border-slate-700"
|
||||
target="_blank" rel="noopener"
|
||||
href="https://facebook.com/sharer/sharer.php?u=https://a-chacon.com/on%20rails/2024/10/16/poc-using-rails-8-auth-system-in-api-only.html"
|
||||
aria-label="Share on Facebook">
|
||||
<svg aria-hidden="true" fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"
|
||||
class="w-5 h-5">
|
||||
<title>Facebook</title>
|
||||
<path
|
||||
d="M379 22v75h-44c-36 0-42 17-42 41v54h84l-12 85h-72v217h-88V277h-72v-85h72v-62c0-72 45-112 109-112 31 0 58 3 65 4z">
|
||||
</path>
|
||||
</svg>
|
||||
</a>
|
||||
<a class="border-2 duration-200 ease inline-flex items-center mb-1 mr-1 transition p-2 rounded-full text-white border-slate-600 bg-slate-600 hover:bg-slate-700 hover:border-slate-700"
|
||||
target="_blank" rel="noopener"
|
||||
href="https://twitter.com/intent/tweet?url=https://a-chacon.com/on%20rails/2024/10/16/poc-using-rails-8-auth-system-in-api-only.html&text=PoC: Usando el Generador de Autenticación de Rails 8 (Beta) En Modo API-Only."
|
||||
aria-label="Share on Twitter">
|
||||
<svg aria-hidden="true" fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"
|
||||
class="w-5 h-5">
|
||||
<title>Twitter</title>
|
||||
<path
|
||||
d="m459 152 1 13c0 139-106 299-299 299-59 0-115-17-161-47a217 217 0 0 0 156-44c-47-1-85-31-98-72l19 1c10 0 19-1 28-3-48-10-84-52-84-103v-2c14 8 30 13 47 14A105 105 0 0 1 36 67c51 64 129 106 216 110-2-8-2-16-2-24a105 105 0 0 1 181-72c24-4 47-13 67-25-8 24-25 45-46 58 21-3 41-8 60-17-14 21-32 40-53 55z">
|
||||
</path>
|
||||
</svg>
|
||||
</a>
|
||||
<a class="border-2 duration-200 ease inline-flex items-center mb-1 mr-1 transition p-2 rounded-full text-white border-slate-600 bg-slate-600 hover:bg-slate-700 hover:border-slate-700"
|
||||
target="_blank" rel="noopener"
|
||||
href="https://www.linkedin.com/shareArticle?mini=true&url=https://a-chacon.com/on%20rails/2024/10/16/poc-using-rails-8-auth-system-in-api-only.html&title=PoC: Usando el Generador de Autenticación de Rails 8 (Beta) En Modo API-Only.&summary=PoC: Usando el Generador de Autenticación de Rails 8 (Beta) En Modo API-Only.&source=https://a-chacon.com"
|
||||
aria-label="Share on Linkedin">
|
||||
<svg aria-hidden="true" fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"
|
||||
class="w-5 h-5">
|
||||
<title>Linkedin</title>
|
||||
<path
|
||||
d="M136 183v283H42V183h94zm6-88c1 27-20 49-53 49-32 0-52-22-52-49 0-28 21-49 53-49s52 21 52 49zm333 208v163h-94V314c0-38-13-64-47-64-26 0-42 18-49 35-2 6-3 14-3 23v158h-94V183h94v41c12-20 34-48 85-48 62 0 108 41 108 127z">
|
||||
</path>
|
||||
</svg>
|
||||
</a>
|
||||
<a class="border-2 duration-200 ease inline-flex items-center mb-1 mr-1 transition p-2 rounded-full text-white border-slate-600 bg-slate-600 hover:bg-slate-700 hover:border-slate-700"
|
||||
target="_blank" rel="noopener"
|
||||
href="https://pinterest.com/pin/create/button/?url=https://a-chacon.com/on%20rails/2024/10/16/poc-using-rails-8-auth-system-in-api-only.html&media=https://a-chacon.com&description=PoC: Usando el Generador de Autenticación de Rails 8 (Beta) En Modo API-Only."
|
||||
aria-label="Share on Pinterest" draggable="false">
|
||||
<svg aria-hidden="true" fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"
|
||||
class="w-5 h-5">
|
||||
<title>Pinterest</title>
|
||||
<path
|
||||
d="M268 6C165 6 64 75 64 186c0 70 40 110 64 110 9 0 15-28 15-35 0-10-24-30-24-68 0-81 62-138 141-138 68 0 118 39 118 110 0 53-21 153-90 153-25 0-46-18-46-44 0-38 26-74 26-113 0-67-94-55-94 25 0 17 2 36 10 51-14 60-42 148-42 209 0 19 3 38 4 57 4 3 2 3 7 1 51-69 49-82 72-173 12 24 44 36 69 36 106 0 154-103 154-196C448 71 362 6 268 6z">
|
||||
</path>
|
||||
</svg>
|
||||
</a>
|
||||
<a class="border-2 duration-200 ease inline-flex items-center mb-1 mr-1 transition p-2 rounded-full text-white border-slate-600 bg-slate-600 hover:bg-slate-700 hover:border-slate-700"
|
||||
target="_blank" rel="noopener"
|
||||
href="https://reddit.com/submit/?url=https://a-chacon.com/on%20rails/2024/10/16/poc-using-rails-8-auth-system-in-api-only.html&resubmit=true&title=PoC: Usando el Generador de Autenticación de Rails 8 (Beta) En Modo API-Only."
|
||||
aria-label="Share on Reddit" draggable="false">
|
||||
<svg aria-hidden="true" fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"
|
||||
class="w-5 h-5">
|
||||
<title>Reddit</title>
|
||||
<path
|
||||
d="M440 204c-15 0-28 6-38 15-35-24-83-40-137-42l28-125 88 20c0 22 18 39 39 39 22 0 40-18 40-39s-17-40-40-40c-15 0-28 9-35 22l-97-22c-5-1-10 3-11 7l-31 138c-53 2-100 18-136 43a53 53 0 0 0-38-16c-56 0-74 74-23 100l-3 24c0 84 95 152 210 152 117 0 211-68 211-152 0-8-1-17-3-25 50-25 32-99-24-99zM129 309a40 40 0 1 1 80 0 40 40 0 0 1-80 0zm215 93c-37 37-139 37-176 0-4-3-4-9 0-13s10-4 13 0c28 28 120 29 149 0 4-4 10-4 14 0s4 10 0 13zm-1-54c-22 0-39-17-39-39a39 39 0 1 1 39 39z">
|
||||
</path>
|
||||
</svg>
|
||||
</a>
|
||||
<a class="border-2 duration-200 ease inline-flex items-center mb-1 mr-1 transition p-2 rounded-full text-white border-slate-600 bg-slate-600 hover:bg-slate-700 hover:border-slate-700"
|
||||
target="_blank" rel="noopener"
|
||||
href="https://wa.me/?text=PoC:%20Usando%20el%20Generador%20de%20Autenticaci%C3%B3n%20de%20Rails%208%20(Beta)%20En%20Modo%20API-Only.%20https://a-chacon.com"
|
||||
aria-label="Share on Whatsapp" draggable="false">
|
||||
<svg aria-hidden="true" fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"
|
||||
class="w-5 h-5">
|
||||
<title>Whatsapp</title>
|
||||
<path
|
||||
d="M413 97A222 222 0 0 0 64 365L31 480l118-31a224 224 0 0 0 330-195c0-59-25-115-67-157zM256 439c-33 0-66-9-94-26l-7-4-70 18 19-68-4-7a185 185 0 0 1 287-229c34 36 56 82 55 131 1 102-84 185-186 185zm101-138c-5-3-33-17-38-18-5-2-9-3-12 2l-18 22c-3 4-6 4-12 2-32-17-54-30-75-66-6-10 5-10 16-31 2-4 1-7-1-10l-17-41c-4-10-9-9-12-9h-11c-4 0-9 1-15 7-5 5-19 19-19 46s20 54 23 57c2 4 39 60 94 84 36 15 49 17 67 14 11-2 33-14 37-27s5-24 4-26c-2-2-5-4-11-6z">
|
||||
</path>
|
||||
</svg>
|
||||
</a>
|
||||
<a class="border-2 duration-200 ease inline-flex items-center mb-1 mr-1 transition p-2 rounded-full text-white border-slate-600 bg-slate-600 hover:bg-slate-700 hover:border-slate-700"
|
||||
target="_blank" rel="noopener"
|
||||
href="https://telegram.me/share/url?text=PoC:%20Usando%20el%20Generador%20de%20Autenticaci%C3%B3n%20de%20Rails%208%20(Beta)%20En%20Modo%20API-Only.&url=https://a-chacon.com/on%20rails/2024/10/16/poc-using-rails-8-auth-system-in-api-only.html"
|
||||
aria-label="Share on Telegram" draggable="false">
|
||||
<svg aria-hidden="true" fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"
|
||||
class="w-5 h-5">
|
||||
<title>Telegram</title>
|
||||
<path
|
||||
d="M256 8a248 248 0 1 0 0 496 248 248 0 0 0 0-496zm115 169c-4 39-20 134-28 178-4 19-10 25-17 25-14 2-25-9-39-18l-56-37c-24-17-8-25 6-40 3-4 67-61 68-67l-1-4-5-1q-4 1-105 70-15 10-27 9c-9 0-26-5-38-9-16-5-28-7-27-16q1-7 18-14l145-62c69-29 83-34 92-34 2 0 7 1 10 3l4 7a43 43 0 0 1 0 10z">
|
||||
</path>
|
||||
</svg>
|
||||
</a>
|
||||
<a class="border-2 duration-200 ease inline-flex items-center mb-1 mr-1 transition p-2 rounded-full text-white border-slate-600 bg-slate-600 hover:bg-slate-700 hover:border-slate-700" target="_blank" rel="noopener" href="/cdn-cgi/l/email-protection#79460a0c1b131c1a0d4429163a435c4b492c0a18171d165c4b491c155c4b493e1c171c0b181d160b5c4b491d1c5c4b49380c0d1c170d101a181a105c3a4a5c3b4a175c4b491d1c5c4b492b1810150a5c4b49415c4b49513b1c0d18505c4b493c175c4b4934161d165c4b493829305436171500575f1b161d0044110d0d090a43565618541a11181a1617571a16145616175c4b490b1810150a564b494b4d56484956484f5609161a540c0a10171e540b1810150a544154180c0d11540a000a0d1c1454101754180910541617150057110d1415" aria-label="Share by Email" draggable="false">
|
||||
<svg aria-hidden="true" fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"
|
||||
class="w-5 h-5">
|
||||
<title>Email</title>
|
||||
<path
|
||||
d="M464 64a48 48 0 0 1 29 86L275 314c-11 8-27 8-38 0L19 150a48 48 0 0 1 29-86h416zM218 339c22 17 54 17 76 0l218-163v208c0 35-29 64-64 64H64c-35 0-64-29-64-64V176l218 163z">
|
||||
</path>
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="font-mulish flex justify-center mb-10">
|
||||
|
||||
<a class="pr-2" href="/docker/2024/09/16/run-llm-locally.html">« LLM en local: Corriendo Ollama y Open WebUI con Docker Compose.</a>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<!-- <div class="mx-auto py-8"> -->
|
||||
<!-- <script type="text/javascript" src="https://cdnjs.buymeacoffee.com/1.0.0/button.prod.min.js" data-name="bmc-button" -->
|
||||
<!-- data-slug="achacon" data-color="#FFDD00" data-emoji="" data-font="Lato" data-text="Buy me a coffee" -->
|
||||
<!-- data-outline-color="#000000" data-font-color="#000000" data-coffee-color="#ffffff"></script> -->
|
||||
<!-- </div> -->
|
||||
<!---->
|
||||
<!-- <div class="my-6"> -->
|
||||
<!-- ¡Hola a todos! 👋 ¿Disfrutaron leyendo el artículo? ¡Me encantaría conocer sus opiniones! 💬 No duden en dejar un -->
|
||||
<!-- comentario abajo, ya sea para compartir sus comentarios, preguntas o simplemente saludar. ¡No es necesario -->
|
||||
<!-- registrarse, solo compartan algo valioso! 😊 -->
|
||||
<!-- </div> -->
|
||||
<!-- <div class="my-6"> -->
|
||||
<!-- Hey there! 👋 Enjoyed reading the post? I'd love to hear your thoughts! 💬 Feel free to drop a comment -->
|
||||
<!-- below—whether it's feedback, questions, or just saying hi. No need to sign up, just share something valuable! 😊 -->
|
||||
<!-- </div> -->
|
||||
<!---->
|
||||
<!-- <div id="embedContainer" style="height: fit-content;" data-identifier="/on%20rails/2024/10/16/poc-using-rails-8-auth-system-in-api-only.html" -->
|
||||
<!-- data-site-key="80dce74b8e1af5839a6f"></div> -->
|
||||
<!-- <script src="https://opina.fly.dev/embed.js"></script> -->
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<footer class="w-full bottom-0">
|
||||
<hr class="my-8 border-zinc-200" />
|
||||
<div class="container mx-auto">
|
||||
<div class="flex flex-wrap text-left">
|
||||
<div class="w-full md:w-6/12 md:px-4">
|
||||
<p class="text-3xl font-semibold">
|
||||
Mantengamosnos en contacto!
|
||||
</p>
|
||||
<p class="font-mulish text-lg dark:text-zinc-300 mt-2 mb-2">
|
||||
Encuéntrame en cualquiera de estas plataformas:
|
||||
</p>
|
||||
<div class="flex justify-center md:justify-start pt-2 gap-6">
|
||||
|
||||
<a class="transition hover:scale-110 duration-300" href="/cdn-cgi/l/email-protection#b7d6d9d3c5d2c499d4dff7c7c5d8c3d8d9dad6dedb99d4d8da">
|
||||
<img
|
||||
class="h-6 w-6"
|
||||
src="/assets/images/contact_channels/email.svg"
|
||||
alt="Email Icon"
|
||||
/>
|
||||
</a>
|
||||
|
||||
<a
|
||||
class="transition hover:scale-110 duration-300"
|
||||
href="https://github.com/a-chacon"
|
||||
target="_blank"
|
||||
>
|
||||
<img
|
||||
class="h-6 w-6"
|
||||
src="/assets/images/contact_channels/github-mark.svg"
|
||||
alt="Github Icon"
|
||||
/>
|
||||
</a>
|
||||
|
||||
<a
|
||||
class="transition hover:scale-110 duration-300"
|
||||
href="https://t.me/a_chacon"
|
||||
target="_blank"
|
||||
>
|
||||
<img
|
||||
class="h-6 w-6"
|
||||
src="/assets/images/contact_channels/telegram.svg"
|
||||
alt="Telegram Icon"
|
||||
/>
|
||||
</a>
|
||||
|
||||
<a
|
||||
class="transition hover:scale-110 duration-300"
|
||||
href="https://stackoverflow.com/users/16847024/andr%c3%a9s"
|
||||
target="_blank"
|
||||
>
|
||||
<img
|
||||
class="h-6 w-6"
|
||||
src="/assets/images/contact_channels/stack-overflow.svg"
|
||||
alt="StackOverflow Icon"
|
||||
/>
|
||||
</a>
|
||||
|
||||
<a
|
||||
class="transition hover:scale-110 duration-300"
|
||||
href="https://discord.com/users/673943171369140252"
|
||||
target="_blank"
|
||||
>
|
||||
<img
|
||||
class="h-6 w-6"
|
||||
src="/assets/images/contact_channels/discord.svg"
|
||||
alt="Discord Icon"
|
||||
/>
|
||||
</a>
|
||||
|
||||
<a
|
||||
class="transition hover:scale-110 duration-300"
|
||||
rel="me"
|
||||
href="https://lile.cl/@achacon"
|
||||
target="_blank"
|
||||
>
|
||||
<img
|
||||
class="h-6 w-6"
|
||||
src="/assets/images/contact_channels/mastodon.svg"
|
||||
alt="Mastodon Icon"
|
||||
/>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="w-full md:w-6/12 md:text-right py-4 md:py-0">
|
||||
<div class="flex flex-wrap items-top mb-6">
|
||||
<div class="w-full lg:w-4/12 md:px-4 ml-auto">
|
||||
<span class="text-lg block text-gray-800 mb-2">
|
||||
Páginas
|
||||
</span>
|
||||
<ul>
|
||||
|
||||
<li>
|
||||
<a class="font-mulish text-slate-600 hover:text-sky-400 dark:text-slate-300 dark:hover:text-slate-200"
|
||||
href="/blog.html">Blog</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a class="font-mulish text-slate-600 hover:text-sky-400 dark:text-slate-300 dark:hover:text-slate-200"
|
||||
href="/">Casa</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a class="font-mulish text-slate-600 hover:text-sky-400 dark:text-slate-300 dark:hover:text-slate-200"
|
||||
href="/projects">Proyectos</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="font-mulish flex flex-wrap items-center md:justify-between justify-center my-8">
|
||||
<div class="w-full md:w-4/12 px-4 mx-auto text-center">
|
||||
|
||||
<a href="/feed.xml" class="p-2">
|
||||
<svg fill="#000000" viewBox="-3 0 19 19" xmlns="http://www.w3.org/2000/svg" class="w-10 mx-auto">
|
||||
<g id="SVGRepo_bgCarrier" stroke-width="0"></g>
|
||||
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g>
|
||||
<g id="SVGRepo_iconCarrier">
|
||||
<path
|
||||
d="M.926 12.818a1.403 1.403 0 1 1 0 1.984 1.402 1.402 0 0 1 0-1.984zm10.531 2.357a1.03 1.03 0 0 1-1.029-1.03 8.775 8.775 0 0 0-.694-3.438A8.826 8.826 0 0 0 1.591 5.31a1.03 1.03 0 1 1 0-2.059 10.817 10.817 0 0 1 4.24.857 10.893 10.893 0 0 1 3.463 2.334 10.867 10.867 0 0 1 3.19 7.703 1.027 1.027 0 0 1-1.027 1.029zm-4.538 0a1.03 1.03 0 0 1-1.029-1.03 4.297 4.297 0 0 0-4.299-4.298 1.03 1.03 0 0 1 0-2.059 6.362 6.362 0 0 1 5.857 3.883 6.298 6.298 0 0 1 .5 2.475 1.03 1.03 0 0 1-1.029 1.029z">
|
||||
</path>
|
||||
</g>
|
||||
</svg>
|
||||
</a>
|
||||
|
||||
<div class="text-sm text-blueGray-500 font-semibold py-1">
|
||||
© <span id="get-current-year">2024</span>
|
||||
<a href="https://www.creative-tim.com/product/notus-js" class="text-blueGray-500 hover:text-gray-800"
|
||||
target="_blank" aria-label="Creative C">a-chacon.</a>
|
||||
All rights reserved.
|
||||
<p class="text-xs pt-4">
|
||||
Built with ❤️ using the
|
||||
<a class="hover:text-sky-400" href="https://github.com/a-chacon/awesome-jekyll-theme">Awesome Jekyll
|
||||
Theme</a>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script data-cfasync="false" src="/cdn-cgi/scripts/5c5dd728/cloudflare-static/email-decode.min.js"></script><script>
|
||||
document.querySelectorAll("a").forEach(function (link) {
|
||||
link.addEventListener("click", function (event) {
|
||||
event.preventDefault(); // Prevent the default link action temporarily
|
||||
link.classList.add("gentle-clicked");
|
||||
|
||||
setTimeout(function () {
|
||||
link.classList.remove("gentle-clicked");
|
||||
window.location.href = link.href; // Navigate to the link after the animation
|
||||
}, 300); // Duration of the gentle bounce animation
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<script>(function(){function c(){var b=a.contentDocument||a.contentWindow.document;if(b){var d=b.createElement('script');d.innerHTML="window.__CF$cv$params={r:'8dc35b4edefb4223',t:'MTczMDU0MjQ1Ni4wMDAwMDA='};var a=document.createElement('script');a.nonce='';a.src='/cdn-cgi/challenge-platform/scripts/jsd/main.js';document.getElementsByTagName('head')[0].appendChild(a);";b.getElementsByTagName('head')[0].appendChild(d)}}if(document.body){var a=document.createElement('iframe');a.height=1;a.width=1;a.style.position='absolute';a.style.top=0;a.style.left=0;a.style.border='none';a.style.visibility='hidden';document.body.appendChild(a);if('loading'!==document.readyState)c();else if(window.addEventListener)document.addEventListener('DOMContentLoaded',c);else{var e=document.onreadystatechange||function(){};document.onreadystatechange=function(b){e(b);'loading'!==document.readyState&&(document.onreadystatechange=e,c())}}}})();</script></body>
|
||||
|
||||
</html>
|
|
@ -275,7 +275,7 @@ impl FullTextParser {
|
|||
/// See:
|
||||
/// - <https://github.com/KWARC/rust-libxml/issues/111>
|
||||
/// - <https://github.com/Orange-OpenSource/hurl/issues/1535>
|
||||
/// These two functions should be removed when the issue is fixed in libxml crate.
|
||||
/// These two functions should be removed when the issue is fixed in libxml crate.
|
||||
fn try_usize_to_i32(value: usize) -> Result<i32, libxml::parser::XmlParseError> {
|
||||
if cfg!(target_pointer_width = "16") || (value < i32::MAX as usize) {
|
||||
// Cannot safely use our value comparison, but the conversion if always safe.
|
||||
|
@ -514,6 +514,22 @@ impl FullTextParser {
|
|||
return Some(thumb);
|
||||
}
|
||||
|
||||
if let Ok(thumb) = Util::get_attribute(
|
||||
context,
|
||||
"//meta[contains(@property, 'twitter:image')]",
|
||||
"content",
|
||||
) {
|
||||
return Some(thumb);
|
||||
}
|
||||
|
||||
if let Ok(thumb) = Util::get_attribute(
|
||||
context,
|
||||
"//meta[contains(@property, 'og:image')]",
|
||||
"content",
|
||||
) {
|
||||
return Some(thumb);
|
||||
}
|
||||
|
||||
if let Ok(thumb) =
|
||||
Util::get_attribute(context, "//link[contains(@rel, 'image_src')]", "href")
|
||||
{
|
||||
|
|
|
@ -278,3 +278,17 @@ Foto: IMAGO/Vaclav Salek / IMAGO/CTK Photo
|
|||
"https://cdn.prod.www.spiegel.de/images/a4573666-f15e-4290-8c73-a0c6cd4ad3b2_w948_r1.778_fpx29.99_fpy44.98.jpg"
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extract_thumbnail_a_chacon() {
|
||||
let html = std::fs::read_to_string(format!("./resources/tests/thumbnails/a-chacon.html"))
|
||||
.expect("Failed to read source HTML");
|
||||
let doc = FullTextParser::parse_html_string_patched(&html).unwrap();
|
||||
let ctx = Context::new(&doc).unwrap();
|
||||
|
||||
let thumb = FullTextParser::check_for_thumbnail(&ctx).unwrap();
|
||||
assert_eq!(
|
||||
thumb,
|
||||
"https://a-chacon.com/assets/images/rails8-poc-api-auth.webp"
|
||||
)
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue