今日已更新 184 条资讯 | 累计 37433 条内容
关于我们

标签:#ev

找到 5110 篇相关文章

AI 资讯

My Validation Layer Was Correctly Deleting 16% of My Good Data

Originally published at ai.bedvibe.studio . I built a real-time tracker in Rust — about two thousand lines — that reads a live ADS-B feed, keeps a Kalman-filtered track per aircraft, and screens every pair for closest approach against separation minima. Roughly 150 aircraft, a full cycle in under a millisecond. It ran clean. Tests passed, the picture looked right, the numbers were plausible. It was refusing about one measurement in nine , and the only reason I ever found out is that the rejections went to a counter instead of a log line. The gate has a sub-second tolerance for clock error The tracker runs an innovation gate: when a position arrives, the filter predicts where the aircraft should be, and if the measurement is too far from that prediction it is rejected as physically impossible rather than believed. Once a track converges the innovation standard deviation settles around 36 m, so a five-sigma gate sits at roughly 180 m. An airliner at 250 m/s covers 180 m in 0.7 seconds . So the gate's entire tolerance for a wrong timestamp is under one second. Any pipeline that mis-times its measurements by more than that will have them rejected — correctly, and invisibly. The feed reports its own staleness. The pipeline dropped it. Every ADS-B record carries a field saying how old that position already was when the response was generated. In the original build it was parsed into the contact struct and never read again — the only other place that field appeared in the entire codebase was as 0.0 in test fixtures. Every measurement was therefore stamped with the tracker's own cycle clock, as though it had been observed at the instant it landed. This is the common case, not an exotic one. A field that is decoded and then unused looks identical to a field that is decoded and used , right up until you go looking for its second reference. Here is what that field actually contains, sampled across two consecutive polls of the live feed: reported age of position median 0.31 s p

2026-08-25 原文 →
AI 资讯

Per-user two-factor auth in CakePHP with CakeDC/Users (opt-in, one method)

CakeDC/Users gives you TOTP two-factor authentication almost for free: flip one config key and every login grows a "enter your 6-digit code" step. The catch is that word every . The built-in flow is all-or-nothing — turn it on and all your users are forced through the OTP challenge on their next login, whether they ever set up an authenticator app or not. Lock yourself out on a fresh install and you'll find out fast. What most apps actually want is the model you see everywhere else: 2FA is off by default , and each user opts in from their own account settings. This post shows how to get there with a surprisingly small change — one overridden method — plus a self-service enrolment screen and one QR-code gotcha that will bite you on modern dependencies. The one insight: isRequired() CakeDC/Users decides whether to demand the OTP step through an OneTimePasswordAuthenticationCheckerInterface . The default implementation, DefaultOneTimePasswordAuthenticationChecker , answers "is 2FA required for this request?" — and once the authenticator is enabled in the login flow, it answers yes for everybody . That checker is a swappable dependency. So "per-user 2FA" reduces to: keep the default behaviour, but also require that this specific user has opted in. One method: <?php declare ( strict_types = 1 ); namespace App\Authentication ; use CakeDC\Auth\Authentication\DefaultOneTimePasswordAuthenticationChecker ; class PerUserOneTimePasswordAuthenticationChecker extends DefaultOneTimePasswordAuthenticationChecker { /** * @param array<mixed>|null $user User data. */ public function isRequired ( ?array $user = null ): bool { // Default rules AND the user enrolled. return parent :: isRequired ( $user ) && ! empty ( $user [ 'two_steps' ]); } } parent::isRequired() keeps every rule CakeDC already applies (the authenticator is on, the user has a verified secret, remember-me isn't skipping it, …). We just && a per-user flag on top. Users who never enrolled fail the two_steps check and log

2026-08-25 原文 →
AI 资讯

I built a free image and video hosting tool after Imgur blocked the UK

On 30 September 2025, Imgur blocked the entire United Kingdom. No warning. No migration tool. No grace period. One day it worked, the next it didn't — and with it went millions of embedded images across forums, Discord servers, tutorials, Reddit threads, and personal blogs. Grey boxes everywhere. I'd been thinking about building a proper image hosting tool for a while. That was the push I needed. What I actually built DBimg is a free media hosting and sharing service. The pitch is simple: upload a file, get a permanent direct link, share it anywhere. Here's what that looks like in practice: No account required — anonymous uploads work out of the box No compression — files are served at original quality, always Permanent hosting — no expiry dates, no "inactive account" deletion Automatic EXIF stripping — GPS and metadata removed on every upload Instant embed codes — HTML, BBCode, and Markdown generated automatically REST API — API key support for developers who need programmatic access Global CDN — fast delivery wherever the link gets shared 75MB free / 250MB Pro — covers most real-world use cases without friction Supported formats: JPEG, PNG, GIF, WebP, AVIF, HEIC, BMP, TIFF, MP4, WebM, MOV, AVI, MP3, FLAC, WAV, and more. Why I built it this way Imgur was originally built by a Redditor, for Redditors. It was frictionless by design — drop an image, copy a link, done. No account needed, no compression, no nonsense. Then it got acquired. Then acquired again. Then the NSFW purge happened in 2023. Then anonymous uploads disappeared. Then compression got heavier. Then ads got more aggressive. Then the UK ban. Each decision made sense from a business perspective. None of them made sense from a user perspective. What frustrates me about this pattern is that image hosting isn't technically hard. Serving a file from a CDN is a solved problem. The thing that's hard is committing to doing it simply and not gradually enshittifying it in pursuit of growth metrics. That's what I w

2026-08-25 原文 →
AI 资讯

Architectural Breakdown: Can AI Remember What It Sees?

![ Architecture Diagram ]( https://image.pollinations.ai/prompt/high+performance+cloud+systems+Can+AI+Remember+What+It+Sees%3F+round+3?width=800&height=400&nologo=true ) # Can AI Remember What It Sees? The 3 AM OOM That Taught Me Everything About Visual Memory Systems At 2:47 AM, my production cluster dropped from 120 fps across 26 cameras down to absolute zero. The culprit was an unbounded `asyncio.Queue` that ballooned to 14 GB in 11 seconds. The fix was not more RAM. It was treating hardware constraints as first-class citizens in every design decision. --- ## The Core Lie: Statelessness by Design AI models forget by default. Transformers discard context once their attention window expires. CNNs process each frame in isolation with no persistence layer. **"Remembering" requires explicit memory injection.** You need RAM for short-term buffers, disk for long-term archives, and compressed embeddings for semantic recall. These are not interchangeable. Most engineers conflate them and pay the price in production. In practice, this distinction separates graceful degradation from hard crashes at the worst possible moment. The [ ShipMVP.tech ]( https://www.shipmvp.tech ) blueprint puts it plainly: **memory is a resource, not a feature.** --- ## Root Cause: The Three Sins That Killed My Pipeline ### Sin 1: Unbounded Queues python BEFORE: OOM in 11 seconds queue = asyncio.Queue() # No maxsize → infinite growth until death **Fix:** Cap queues to a hardware-derived bound. python AFTER: Hardware-bounded, fails fast on overflow self.queue = asyncio.Queue(maxsize=100) # ~1.5 MB at 224x224x3 uint8 **Failure walkthrough:** 1. Traffic spike hits 1200 fps and the queue swells to 800K frames (14 GB). 2. The kernel invokes swap thrashing until the OOM killer terminates the process. 3. **Lesson:** Derive `maxsize` from `(available_RAM / frame_size) * safety_factor`. Never guess. ### Sin 2: Redundant Allocations Each frame went through four separate copies: OpenCV BGR, Pillow RGB, NumPy

2026-08-25 原文 →
AI 资讯

A Simple CI/CD Pipeline That Actually Works

The Problem with Most CI/CD Tutorials Most tutorials show you a pipeline that deploys a "hello world" app to a free Heroku instance. They skip the messy parts: secrets, rollbacks, and the moment your pipeline breaks because a dependency changed. I've been there. After years of fighting with over-engineered setups, I settled on a minimal pipeline that's easy to understand, debug, and extend. It's not fancy, but it works. The Core Idea A CI/CD pipeline is just three stages: Test - run automated checks Build - create an artifact Deploy - push the artifact to a server We'll use GitHub Actions because it's free for public repos and integrates with everything. But the same concepts apply to GitLab CI, CircleCI, or Jenkins. The Pipeline File Here's the complete .github/workflows/deploy.yml : name : CI/CD on : push : branches : [ main ] pull_request : branches : [ main ] jobs : test : runs-on : ubuntu-latest steps : - uses : actions/checkout@v4 - uses : actions/setup-node@v4 with : node-version : ' 20' - run : npm ci - run : npm test build-and-deploy : needs : test runs-on : ubuntu-latest if : github.ref == 'refs/heads/main' && github.event_name == 'push' steps : - uses : actions/checkout@v4 - run : npm ci - run : npm run build - name : Deploy to server uses : appleboy/scp-action@v0.1.7 with : host : ${{ secrets.SERVER_HOST }} username : ${{ secrets.SERVER_USER }} key : ${{ secrets.SSH_PRIVATE_KEY }} source : " dist/*" target : " /var/www/myapp" That's it. Let's break it down. Stage 1: Test The test job runs on every push and pull request. It checks out the code, installs dependencies with npm ci (which respects the lockfile), and runs your test suite. If a PR fails tests, the build-and-deploy job won't run because of the needs: test dependency. Stage 2: Build The build-and-deploy job only runs on pushes to main (not on PRs). It builds your app into a dist folder. For a Node.js app, npm run build might be a bundler like Vite or webpack. For a Python app, you'd replace with

2026-08-25 原文 →
AI 资讯

How I Made a Canvas JSON Viewer Fast with Viewport Virtualization

When you build a visual tool for structured data, everything feels instantaneous on toy examples. A 20-line JSON payload renders crisply into an interactive graph with clean nodes, collapsible trees, and smooth connectors. Then you drop in a real-world file: a 15 MB API response containing nested objects, deep arrays, and hundreds of thousands of key-value pairs. Suddenly, the browser locks up. The DOM or Canvas scene graph explodes with tens of thousands of objects. Panning drops from 60 fps to single digits, and zooming triggers multi-second layout thrashing. Here is how I tackled this problem when building the graph visualizer for Treease by separating semantic completeness from visual materialization . The Core Dilemma: Completeness vs. Canvas Weight The naive mental model for a canvas or SVG graph is 1:1 mapping: for every node in the data, instantiate a renderable object in the scene. [Full JSON AST] -> [Canvas Scene Graph / DOM Nodes] This model breaks down quickly because: Scene Graph Bloat: The cost of hit-testing, layout calculations, and paint passes scales linearly with document size, even when most content is offscreen. Memory Overhead: Holding thousands of active visual display objects consumes hundreds of megabytes of RAM. The intuitive workaround is aggressive lazy loading, for example parsing only what is expanded. But that breaks critical user workflows: How do you search across the entire document? How do you jump to a deeply nested path? How do you show global error indicators or relationship highlights? The Architectural Shift The solution was to decouple the data model from the render surface : [ Full Semantic Graph (In-Memory / Fast Lookups) ] | v Viewport Frustum Culling [ Materialized Scene (Only Visible Nodes + Overscan) ] Semantic Completeness: Keep the entire document parsed, indexed, and queryable in memory. Global search, tree navigation, and path queries run against the lightweight in-memory structure. Visual Materialization: Only inst

2026-08-25 原文 →
AI 资讯

Your PrestaShop hook renders nothing, and nothing is logged

A module hook that returns an empty string looks exactly like a module hook that was never called. PrestaShop gives you nothing to tell them apart: no error, no log entry, no stack trace, no fallback text. The page renders fine. Your block is just absent. We spent three releases of one module chasing this, and the cause turned out to be three different mechanisms stacked on top of each other. Each one alone is enough to make output vanish silently. This is what they are, in the order we peeled them off. The setup The module registers displayHeader and renders a small template: a <script> block that carries a public site key into the page, and a <style> block that hides a third-party badge. Roughly: public function hookDisplayHeader ( $params ) { $this -> context -> smarty -> assign ([ 'recaptcha_pubkey' => $this -> getActivePublicKey (), 'recaptcha_hide_badge' => $hideBadge , ]); return $this -> display ( __FILE__ , 'views/templates/front/header_script.tpl' ); } Deployed, cache cleared, hook registered, Design > Positions shows the module attached. Page source: nothing. Not the script, not the style, not even a stray whitespace. Mechanism 1: core swallows the exception Hook::callHookOn() wraps every module hook call in a try/catch. When debug mode is off, it catches whatever the hook throws and returns an empty string. No error, no log, no trace. That is a defensible design decision — one broken module should not take down a storefront — but as a debugging experience it is brutal. Every possible failure inside your hook, from a typo to a missing file to a template that will not compile, arrives at your screen as the exact same symptom: nothing. The first thing to do, before theorising about causes, is to stop letting core swallow it: try { return $this -> display ( __FILE__ , 'views/templates/front/header_script.tpl' ); } catch ( Throwable $e ) { $message = 'mymodule header_script.tpl render failed: ' . $e -> getMessage () . ' in ' . $e -> getFile () . ':' . $e -> g

2026-08-25 原文 →
AI 资讯

Comparing prices across retailers is a unit-normalization problem, not a scraping problem

Disclosure: I'm the founder of Popgot , which I use as the example below. The problem and the approach apply regardless of what you build on. Every price comparison project I've seen starts the same way: scrape a bunch of retailers, store the prices, sort ascending. And then it produces garbage rankings, because price is not a comparable field. Here's the classic failure. Three listings for AA batteries: Listing Price Count Brand A $5.99 16 Brand B $6.99 20 Brand C $11.94 40 Sort by price and Brand A "wins" at $5.99. Sort by cost per battery and the order flips completely: Brand C is ~29.9c per cell, Brand A is ~37.4c. The cheapest listing is the worst deal on the page. Why this is hard The naive fix is "just divide price by quantity." The problem is that quantity almost never exists as a clean number. It's buried in the title, and the title is written by whoever uploaded the listing: AA Batteries 24 Pack AA Alkaline Batteries, 1.5 Volts, 24 Count 48-Pack (2 x 24) Double A So you end up writing a title parser. Then you discover the same product needs a different unit depending on the category: per fluid ounce for detergent, per serving for protein powder, per 100g for coffee, per sheet for paper towels. Then you discover that some categories need a spec filter before unit price is even meaningful. A fish oil at 20c per serving isn't cheaper than one at 34c per serving if the first one has half the EPA+DHA. You're comparing two different products. That last part is the piece people underestimate. Normalization is only valid within a set of products that actually satisfy the same requirement, which means something has to read the label, not just the title. What a normalized record looks like This is the problem I ended up building Popgot around, so rather than describe it abstractly, here's the shape of the data. The developer API returns listings with the unit math already done: GET /api/developer-api/products?query=aa+batteries&limit=10 { "products" : [ { "display_t

2026-08-25 原文 →
AI 资讯

SPF, DKIM, and DMARC: Why “Valid” Records Still Let Your Domain Be Spoofed

Originally published on the Merlonix blog . There are two different questions about your domain's email authentication, and almost every checker answers only the first. The first is do you have SPF, DKIM, and DMARC records — a presence question, a yes/no lookup. The second is do those records actually stop someone from sending email that looks like it came from you — an enforcement question. You can pass the first and fail the second completely, and the gap between them is the whole game: a domain with all three records published, every free checker showing green, that a spammer can still spoof at will because each record is published in its permissive, do-nothing mode. The permissive modes exist for a good reason — they're how you roll these records out without bouncing your own legitimate mail. The problem is that "published it in monitor mode so I could watch first" and "finished" look identical to a tool that only checks presence, and an enormous number of domains stop at the first and never come back. Here's what actually decides enforcement, record by record, and how to tell which mode yours is in. SPF: only -all actually rejects An SPF record lists which servers are allowed to send mail as your domain, and it ends in an all mechanism that says what a receiver should do with a server that isn't on the list. That final qualifier is the entire enforcement decision, and there are four of them: -all (hardfail) — "reject mail from any server not listed." This is the only one that protects you. ~all (softfail) — "accept it but mark it suspicious." Receivers still deliver it. Softfail is the rollout setting, and it's where most records get stranded. ?all (neutral) — "no opinion." Functionally the same as having no policy on the all term. +all — "any server on the internet may send as this domain." This is actively worse than no SPF at all, and it's usually a copy-paste accident. So an SPF record can be present, syntactically perfect, and end in ~all — and it stops no

2026-08-25 原文 →
AI 资讯

New Here !! Am i Late?

Hey everyone! I’m Musab, a developer and someone who’s recently started getting more involved in the developer community. I’m interested in building real-world products, exploring AI, full-stack development, and contributing to open-source projects. Over the past few months, I’ve been working on 30+ projects and have started contributing to open source, including having PRs merged into projects like AI-System-Design-Consultant, OpenLife, and OpenSRE. Tech I work with: React • TypeScript • Tailwind CSS • Node.js • Python • Flask • MongoDB • MySQL • Redis • Firebase • AWS • Docker I’m here to learn, build, contribute, and connect with other developers. Looking forward to meeting everyone and being part of the community. DeveloperCommunity #OpenSource #FullStackDevelopment #AI #WebDevelopment #SoftwareDevelopment

2026-08-25 原文 →
AI 资讯

Why Your Eyes Burn by Evening: Digital Eye Strain and the 20-20-20 Rule

By the end of the day my eyes burn. The screen goes fuzzy for a second when I look up, focusing on something across the room takes longer than it should, and a dull headache creeps in around the temples. I used to write this off as "just tired." Turns out it has a name and a fairly simple mechanism behind it. What computer vision syndrome actually is Computer vision syndrome — digital eye strain, if you prefer the plainer name — isn't a diagnosis in the sense of "something broke." It's a cluster of symptoms that shows up after prolonged close-range screen work: dryness and burning, blurred focus when you shift your gaze to something far away, light sensitivity, headaches, and often neck and shoulder pain, because we unconsciously lean toward the screen and freeze in one position for hours. Two things happen at once. First, your eyes hold focus on a near object for a long stretch — the ciliary muscle, which controls how the lens changes shape for near vision, stays tensed the whole time instead of periodically relaxing the way it would if your gaze wandered farther away now and then. Second, you blink noticeably less often while concentrating, so the tear film that keeps your eyes moist doesn't get replenished as frequently — hence the dryness. Why a screen and not a book Reading a book for hours also holds your focus at close range, but it strains your eyes less, and there's a reason for that. A screen emits light rather than reflecting it the way paper does, which creates more contrast against the room's ambient lighting, especially if the room is dimmer than the display. Glare from windows and lamps forces you to squint and refocus. And a laptop or phone tends to sit closer to your face than a book or a printed document would, simply because the screen is smaller. There's also the nature of the work itself. Reading a book is a steady stream; working with software is a constant series of micro-refocuses between windows, tabs, and notifications. Your eyes keep re-ad

2026-08-25 原文 →
AI 资讯

Node.js Express vs. Python FastAPI: Which Should You Choose in 2026?

Node.js Express vs. Python FastAPI: The Definitive Guide for Choosing Your Next Backend Choosing a backend framework used to be simple. If you liked JavaScript, you built with Express. If you liked Python, you went with Flask or Django. But the landscape has fundamentally shifted. With the explosion of AI, machine learning, and strict type safety, Python FastAPI has emerged as a powerhouse alternative to the traditional JavaScript runtime. Meanwhile, Node.js Express remains the unopinionated king of the enterprise web. If you are starting a new project today, which one should you choose? Let’s break down the technical trade-offs, developer experience, and code structures of both frameworks. 🚀 The Core Philosophy Node.js Express: The Minimalist Canvas Express is a minimalist, unopinionated framework. It doesn't care how you structure your folders, how you validate data, or how you handle errors. It gives you a robust set of HTTP tools and steps out of your way. The Catch: You have to build or install your own solutions for data validation, ORM mapping, and API documentation. Python FastAPI: The Automated Powerhouse FastAPI is built on modern Python 3.8+ features like type hints and asynchronous ASGI (asyncio). It is highly opinionated about data handling, leveraging Pydantic to automate input validation and schema serialization. The Catch: It forces you into a specific way of handling data types from day one, which can feel restrictive if you prefer absolute freedom. 📊 Feature Breakdown Feature Node.js Express Python FastAPI Language JavaScript / TypeScript Python Data Validation Manual / Third-Party (Zod, Joi) Native via Pydantic API Docs Manual Setup (Swagger UI plugin) Automatic (Interactive Swagger UI & ReDoc) Best For Real-time I/O, WebSockets, Full-stack JS AI/ML APIs, Data pipelines, Type-safe apps 🛠️ Code Comparison: Creating a Validated POST Route Let’s look at how both frameworks handle a common task: creating a POST endpoint that accepts an item, validates

2026-08-24 原文 →
AI 资讯

100 Writing, Productivity, Coding & Research Lenses for ChatGPT 🧠💻

From fixing one sentence to designing an algorithm, AI becomes much more useful when you stop treating it as a single-purpose chatbot. Instead, think of it as a collection of specialized working modes . Need to debug? /debug Need to design an algorithm? /algorithm Need to plan research? /researchplan Need to challenge your own argument? /critic Need to turn a large project into manageable work? /roadmap90 The underlying idea is simple: Don't just ask AI for an answer. Give it a mode of thinking. From Prompt → Workflow A normal interaction might look like: User ↓ Question ↓ AI ↓ Answer A structured workflow looks different: Goal ↓ Context ↓ Lens ↓ Analysis ↓ Output ↓ Review ↓ Iteration For example: Project ↓ /researchplan ↓ Research questions ↓ /hypothesis ↓ Testable assumptions ↓ /experiment ↓ Evaluation ↓ /audit ↓ Final findings The shortcut is not magic. It is a task-specific instruction layer . 1. Writing Lenses The first group focuses on transforming existing text. /rewrite /improve /polish /proofread /grammar /copyedit /expand /shorten /paraphrase /simplifytext These commands represent different operations. For example: /rewrite should preserve the original meaning while changing the wording. Whereas: /improve can address: clarity structure flow word choice readability And: /shorten optimizes for concision. This distinction matters because: Editing and rewriting are not the same task. 2. Tone Is a Control Variable The next group controls communication style: /formal /casual /friendly /professional /persuasive /convincing /academic /journalistic The same information can be communicated differently depending on the audience. For example: Technical explanation ↓ ┌──────┼──────┐ ↓ ↓ ↓ Student Developer Executive The underlying facts should remain stable. The presentation changes. That makes tone a communication parameter , not merely decoration. 3. Structured Writing For longer outputs: /story /essay /article /report /whitepaper /casestudy /proposal /sop /playbook

2026-08-24 原文 →
AI 资讯

Your Form Is Not Portable If It Contains Callbacks

What makes a form portable? Not JSON alone. Its validation, conditions, collections and submission semantics must survive the trip too. I wrote about the architecture behind Modyra and the trade-offs involved. Your Form Is Not Portable If It Contains Callbacks Most form libraries help us manage forms inside an application. They track values, execute validators, expose errors and eventually produce a submission payload. That works well until the form needs to exist somewhere else. Perhaps its structure comes from a backend. Perhaps a visual builder generates it. Perhaps multiple applications must render it. Perhaps the server must independently validate the same conditional rules used by the browser. At that point, the form is no longer just component state. It is a contract. And most form abstractions cannot cross that boundary. The portability illusion Consider a typical conditional validator: const form = createForm ({ defaultValues : { country : ' IT ' , vatId : '' , }, validators : { onChange : ({ value }) => { if ( value . country === ' IT ' && ! value . vatId ) { return { fields : { vatId : ' VAT ID is required in Italy ' , }, }; } }, }, }); This is perfectly reasonable application code. It is also not portable. The callback cannot travel through an API as JSON. A Java service cannot execute it. A visual editor cannot reliably inspect it. Another runtime cannot reproduce its meaning without receiving executable source code. We can serialize the values around the callback, but not the behavior itself. This leads to an important distinction: A form configuration is not a portable form contract if part of its meaning still lives inside executable callbacks. The obvious shortcuts are dangerous There are several tempting ways to work around this limitation. Serialize the callback as source code { "condition" : "value.country === 'IT'" } The receiving application must now parse or execute an expression encoded as text. That creates immediate problems: the expression

2026-08-24 原文 →
AI 资讯

Static Forms in Astro: Handling Submissions Without a Server

Static Forms in Astro: Handling Submissions Without a Server with onsubmit.dev (form backend) Astro is a great fit for content-heavy sites that ship very little JavaScript, but that creates an interesting problem as soon as you add a contact form: where does the POST request go? With onsubmit.dev (form backend) , an Astro site can submit forms to an external endpoint instead of adding its own API route or server. This is particularly useful for Astro projects deployed as static files to a CDN, GitHub Pages, or another static host. You can keep the site static while still accepting contact requests, feedback, registrations, and similar submissions. Start with the zero-JavaScript pattern The simplest approach is also the most aligned with Astro's philosophy: use the browser's native form submission behavior. You don't need a hydrated component merely to collect a few fields. A regular HTML form can make a POST request directly to a form backend: --- // src/pages/contact.astro --- <form method="POST" action="https://onsubmit.dev/f/YOUR_FORM_ID"> <label> Name <input type="text" name="name" required /> </label> <label> Email <input type="email" name="email" required /> </label> <label> Message <textarea name="message" required></textarea> </label> <button type="submit">Send message</button> </form> Replace YOUR_FORM_ID with the endpoint supplied for your form. There is no client framework involved here. The browser serializes the named fields and sends them directly when the visitor clicks the button. That has several nice properties for an Astro project: No Astro server endpoint is required. No React, Vue, or other client runtime needs to be hydrated. The form still works when JavaScript is unavailable. Your static deployment remains static. It is worth remembering that native HTML already does a lot of work. required , type="email" , labels, and standard browser submission cover many simple forms without additional JavaScript. Where astro-onsubmit fits For Astro-specif

2026-08-24 原文 →
AI 资讯

reCAPTCHA: It’s Not Just “I’m Not a Robot”

How CAPTCHA evolved from typing distorted text to analyzing behavior, context, and risk When most people hear CAPTCHA, they imagine a small checkbox: ☐ I’m not a robot Or perhaps a challenge asking them to select traffic lights, bicycles, buses, or crosswalks. But modern reCAPTCHA is much more interesting than that. In many cases, you don't actually solve anything. You simply open a webpage, move your mouse, click a button, fill out a form—and somewhere in the background, a risk-analysis system is trying to answer a much harder question: “Does this interaction look like a legitimate human interaction, or automated/abusive traffic?” That is a fundamentally different problem from asking a user to identify a picture. Google describes reCAPTCHA as a service that uses advanced risk-analysis techniques to distinguish humans from bots. Modern versions can return a risk score instead of presenting a visible challenge. 1. The original CAPTCHA problem CAPTCHA originally stood for: Completely Automated Public Turing test to tell Computers and Humans Apart. The basic idea was simple: Humans are good at recognizing distorted characters. Traditional computer programs were not. So the website could display something like: but distort, rotate, or obscure the characters. The user typed: 7hK9P and the website accepted the answer. This created a simple classification: It worked reasonably well. Until machines became better. 2. Then computers learned to read the CAPTCHA This created an interesting security race. CAPTCHA became harder. Then OCR and machine learning became better. So CAPTCHA became even harder. Eventually the system was moving toward: Human intelligence vs machine vision And that created an unfortunate side effect. The better the security became, the worse the experience became for legitimate users. Instead of: «“Are you human?”» the user was suddenly being asked: «“Select every square containing a traffic light.”» And sometimes: «“Select every square containing a traffi

2026-08-24 原文 →