AI 资讯
How I Built the Two Missing Payload CMS v3 Plugins — Reviews, JSON-LD & Real Production Bugs
Running 23 European e-commerce shops on Payload CMS v3 taught me that some things simply don't exist yet. So I built them. Background I maintain a multi-clone e-commerce infrastructure — 23 Next.js + Payload CMS v3 shops deployed across Europe, each on its own subdomain and language. Think fr.myshop.com , de.myshop.com , sk.myshop.com ... all running on the same codebase with country-specific patches. While building this, I kept running into two missing pieces that no one had published for Payload v3: A customer reviews system with admin moderation and Google star ratings Complete Schema.org JSON-LD for Google rich snippets (Product, BreadcrumbList, ItemList, AggregateRating) Both are now published on npm. Here's what I built, the bugs I hit, and how I solved them. Part 1 — The Reviews Plugin What didn't exist Search npm for payload reviews or payload ratings — you'll find nothing for v3. The official plugin ecosystem covers SEO, forms, redirects, Stripe... but not customer reviews. Building the collection The reviews collection itself is straightforward — relationship to products , rating (1-5), status select (pending/approved/rejected), author fields. The tricky parts came later. Access control gotcha: Payload v3 uses a roles array, not a role string. This breaks if you copy v2 patterns: // ❌ Wrong — always returns false update : ({ req }) => req . user ?. role === ' admin ' , // ✅ Correct for v3 update : ({ req }) => req . user ?. roles ?. includes ( ' admin ' ), Prevent self-verification: Users can POST any field on create: () => true collections. Lock verified in a beforeChange hook: hooks : { beforeChange : [ ({ data }) => { if ( ! data . status ) data . status = ' pending ' data . verified = false // admin-only, always reset on create return data }, ], }, Email protection: read: () => true on the collection exposes authorEmail in the public API. Add field-level access: { name : ' authorEmail ' , type : ' email ' , access : { read : ({ req }) => req . user ?.
AI 资讯
Your Next.js API Route Is Leaking Diagnostics in Its 400 Responses
A data export endpoint dumps system diagnostics when it hits an invalid field. Feed it garbage, read the debug output, grab the flag. A data export feature lets you pick which profile fields to download. The UI only offers valid fields through checkboxes, so everything looks locked down. But the API behind it accepts arbitrary field names -- send it one it doesn't recognize, and instead of a clean error, it dumps full system diagnostics including internal feature flags. That's where the flag is. You'll bypass the frontend, hit the endpoint directly, and read what comes back. Lab setup Start the lab: npx create-oss-store@latest Or with Docker (no Node.js required): docker run -p 3000:3000 leogra/oss-oopssec-store The app runs at http://localhost:3000 . What you're targeting The app has a profile page at /profile with a Data Export tab. It lets users download their own data in JSON or CSV by selecting fields through checkboxes ( User ID , Email , Role , Address ID ) and clicking "Export Data". The UI looks safe -- you can only pick from a fixed set of valid fields, so there's no way to submit an invalid one through the browser. But that's just client-side validation. The endpoint behind it is POST /api/user/export , and it accepts a JSON body with two parameters: { "format" : "json" , "fields" : [ "id" , "email" , "role" ] } The fields value is an array of strings. The API checks each field against an allowlist. Valid fields? You get your data back. Invalid fields? The API throws an error -- and that error says way too much. Step-by-step exploitation 1. Log in You need an authenticated session. Use one of the seeded accounts: Email: alice@example.com Password: iloveduck Log in through the UI at /login , or grab a session cookie via curl: curl -c cookies.txt -X POST http://localhost:3000/api/auth/login \ -H "Content-Type: application/json" \ -d '{"email":"alice@example.com","password":"iloveduck"}' 2. Explore the Data Export tab Go to /profile and click the Data Export
开发者
I Built a 4-Sided Plot Area Calculator with 2D & 3D Visualization
I Built a 4-Sided Plot Area Calculator with 2D & 3D Visualization Most online plot calculators only work for simple rectangular plots. However, many real-world properties have four sides with different measurements, making area estimation much more difficult. That's why I built a 4-Sided Plot Area Calculator that allows users to enter the North, South, East, and West dimensions and instantly calculate the approximate plot area. 🔗 https://www.premiumconverters.com/plot-area-calculator Features 📐 Supports irregular 4-sided plots 🏠 Calculates area in Marla, Kanal, Acres, and more 🖼️ Interactive 2D top-down visualization 🏗️ Isometric 3D plot rendering 📏 Feet & inches input support 📱 Mobile-friendly experience Why I Built It In many countries, especially in South Asia, property dimensions are often recorded as side measurements rather than perfect geometric shapes. Existing tools rarely address this use case properly. I wanted to create a simple solution that homeowners, buyers, real estate professionals, and developers could use without needing complex surveying software. The Result The calculator transforms four side lengths into a practical estimate while providing visual feedback that helps users better understand their property's shape. Building tools that solve real-world problems is one of the most rewarding parts of software engineering. Have you ever built a niche tool that unexpectedly helped thousands of users?
AI 资讯
Le SDK Stripe nous a menti en 9 millisecondes : 4 tests pour confondre un bug d'environnement avant de le patcher
La trahison du chiffre Vendredi 15 mai, 16 h 13. L'alerte Sentry remonte sur le téléphone. La première réinscrite Phase 1 attend devant l'écran de paiement, son nom est en haut de mon onglet. Je pose la canette, je rouvre l'écran. La tasse à tête de Françoise, sur le poste d'à côté, capte un reflet jaune que je remarque sans le regarder. La stack trace tient en plein écran. Le stack trace s'ouvre, neuf champs sur dix à null , et un chiffre que je n'ai pas vu venir. type = "StripeConnectionError" message = "An error occurred with our connection to Stripe." code = null statusCode = null requestId = null duration = 9 ms Neuf millisecondes. Sur une route Vercel en région Paris, un DNS résout en quarante millisecondes, un handshake TLS coûte cent à deux cents. Neuf millisecondes, ce n'est pas un appel réseau qui a échoué. C'est un appel réseau qui n'a jamais eu lieu. Le SDK n'est pas arrivé jusqu'à la fibre. L'instinct propose immédiatement trois patchs. Timeout serverless Vercel — j'ajoute maxDuration , je redéploie. Clé révoquée — je vais la rouler. Compte Stripe restreint après le passage en mode live — j'ouvre un ticket support. Ces trois hypothèses sont plausibles. Aucune des trois n'est falsifiable par le symptôme seul, et c'est précisément ce qui les rend dangereuses : chacune ouvre un cycle de quinze à trente minutes avec rollback à la fin si elle se trompe. Multiplié par trois, on tient une demi-journée perdue avec la cliente toujours en train de cliquer. Je n'ai pas le temps. Une réinscrite attend. Quatre tests, dans l'ordre Je connais la classe d'incident — « preview marche, prod casse » , ou son symétrique. La règle, pour cette classe, c'est qu'on ne corrige rien tant qu'on n'a pas discriminé les couches. Quatre tests, exécutés dans l'ordre. Chacun élimine une famille d'hypothèses, pas une hypothèse isolée. Et chacun est conçu pour réfuter ce qu'il vient interroger — parce qu'un test qui cherche à confirmer trouve toujours, par sélection, ce qu'il cherche. Te
AI 资讯
The 4-test protocol that isolated a 9 ms Stripe SDK crash on Next 16
The number that lied Friday May 15, 4:13 PM. The Sentry alert pings on my phone. The first Phase 1 re-enrolling student waits in front of the payment screen, her name at the top of my tab. I put down the can, I reopen the screen. The mug with Françoise's face on it, on the desk next door, catches a yellow reflection I notice without looking at. The stack trace fills the screen. The stack trace opens, nine fields out of ten at null , and a number I didn't see coming. type = "StripeConnectionError" message = "An error occurred with our connection to Stripe." code = null statusCode = null requestId = null duration = 9 ms Nine milliseconds. On a Vercel route in Paris region, DNS resolves in forty ms, a TLS handshake costs one to two hundred. Nine milliseconds isn't a network call that failed. It's a network call that never happened. The SDK didn't reach the wire. Instinct immediately offers three patches. Vercel serverless timeout — I add maxDuration , redeploy. Revoked key — I'll rotate it. Stripe account restricted after the live switch — I open a support ticket. These three hypotheses are plausible. None of the three is falsifiable from the symptom alone, and that's precisely what makes them dangerous: each opens a fifteen-to-thirty-minute cycle with rollback at the end if it's wrong. Multiplied by three, half a day lost with the customer still clicking. I don't have time. A student is waiting. Four tests, in order I know the incident class — "preview works, prod breaks" , or its mirror. The rule for this class is that you fix nothing until you've discriminated the layers. Four tests, executed in order. Each eliminates a family of hypotheses, not an isolated hypothesis. And each is designed to refute what it interrogates — because a test that seeks to confirm always finds, by selection, what it's looking for. Test 1 — reproduce in the witness environment. I rerun the same funnel in preview, with the sk_test_ key. Checkout opens in three hundred fourteen milliseconds,
AI 资讯
How I built an automated SBOM scanner to secure my supply chain 🛡️
Supply chain security is terrifying right now. With new vulnerabilities popping up daily and governments mandating compliance (like the EU CRA and US Executive Orders), I realized my open-source projects were completely flying blind. I needed a Software Bill of Materials (SBOM) to track exactly what dependencies I was shipping. But every tool I found was either a massive enterprise platform or a clunky CLI tool that took forever to set up. So, I built my own. It's called Deptic . 🏗️ The Architecture I wanted the developer experience to be completely frictionless: you paste a GitHub URL, and it instantly spits out a compliant SBOM and highlights any critical CVEs. Here is the tech stack I went with: Next.js 14 (App Router): For a lightning-fast React frontend and seamless API routes. Go (Golang): The backend scanning engine. Go's incredible concurrency allows it to parse massive dependency trees in milliseconds. Supabase: For database management and instant authentication. Tailwind CSS: Because writing raw CSS is pain. 🧩 The Hardest Part: Dependency Resolution Building the UI was easy. Parsing package.json or go.mod files? Also easy. The hardest part was recursively walking down the dependency tree to find transitive dependencies (the dependencies of your dependencies). I had to write custom parsers that could speak to the NPM registry, PyPI, and Maven Central simultaneously to map out the entire tree and cross-reference them with global CVE databases in real-time. 🚀 The Result What started as a weekend script turned into a full platform. Deptic now supports: Instant scanning of public GitHub repos. Generating perfectly compliant CycloneDX (1.5) and SPDX (2.3) JSON files. Live CVE vulnerability detection. Try it out! If you want to see exactly what dependencies are hiding in your codebase, you can run a free scan here: 👉 deptic.netlify.app It's completely free for developers. I would love to get your brutal feedback on the UI, the scanning speed, or any feature reque
AI 资讯
Generating valid .ics calendar feeds at build time
A few weeks ago I shipped a feature I'd been putting off because it felt like it needed a backend: subscribable calendar feeds. "Add this holiday to Google Calendar." "Subscribe to all your country's public holidays so they show up in Apple Calendar forever." Every calendar competitor has this. My site had none. The catch: the whole thing is a static export — next build produces a folder of HTML/CSS/JS that I drop on Cloudflare Pages. No server, no API routes at request time, no ISR. So how do you serve a .ics feed that a calendar app polls every few hours? Turns out you don't need a server at all. Here's the approach, the RFC 5545 gotchas that bit me, and the parts I'd tell my past self. The "aha": a feed is just a file A .ics subscription feed is not a live API. It's a static text file that calendar clients re-fetch on a schedule. So for a static site, the idiomatic move is a post-build emitter : after next build , run a Node script that walks your data and writes assets straight into out/ . # scripts/deploy.sh npx next build node scripts/emit-feeds.mjs # writes .ics + .json into out/ That's the entire architecture. The emitter reads the same JSON the pages render from, so the feeds can never drift out of sync with the site — there's one source of truth. It emits: a per-year feed ( holidays-de-2026.ics ) a per-holiday feed (one event, for the "download this day" button) an all-years subscription feed (the one you point webcal:// at) and, almost for free in the same loop, a JSON API under out/api/ No new pages, no new routes. Just files. RFC 5545: all-day events are sneakier than they look I assumed an all-day event on Jan 1 would be DTSTART:20260101 , DTEND:20260101 . Wrong. DTEND is exclusive. A one-day all-day event ends on Jan 2 : BEGIN:VEVENT UID:de-2026-neujahr@calendana.com DTSTAMP:20260614T101500Z DTSTART;VALUE=DATE:20260101 DTEND;VALUE=DATE:20260102 SUMMARY:Neujahr TRANSP:TRANSPARENT CATEGORIES:Holiday END:VEVENT Get this wrong and some clients render a ze
AI 资讯
I Cut My Next.js + Supabase App Load Time by 73% - Here Are the 5 Techniques That Actually Worked
I Cut My Next.js + Supabase App Load Time by 73% - Here Are the 5 Techniques That Actually Worked Last month, our SaaS dashboard was embarrassingly slow . 4.2 seconds to load the main page. Users were complaining. Conversion rates were tanking. Today? 1.1 seconds . 73% faster. Here's exactly what worked (and what didn't). The Problem: Death by a Thousand Database Calls Our dashboard showed user projects, team members, recent activity, and notifications. Sounds simple, right? Wrong. Each component was making its own database calls. The projects list fetched projects, then made separate calls for each project's stats. The activity feed loaded events, then fetched user details for each event. Classic N+1 query problem, but worse. Technique #1: Strategic Data Fetching Consolidation Before: 47 database calls to load the dashboard After: 3 database calls The fix wasn't fancy. We consolidated related data into single queries using Supabase's nested select syntax: // ❌ Before: Multiple separate calls const projects = await supabase . from ( ' projects ' ). select ( ' * ' ) const stats = await Promise . all ( projects . map ( p => supabase . from ( ' project_stats ' ). select ( ' * ' ). eq ( ' project_id ' , p . id )) ) // ✅ After: Single consolidated call const projects = await supabase . from ( ' projects ' ) . select ( ` *, project_stats(*), team_members(count), recent_activity:activities(*, user:users(name, avatar_url)) ` ) . limit ( 10 ) Result: Dashboard load time dropped from 4.2s to 2.8s (33% improvement) Technique #2: Aggressive Caching with Smart Invalidation Most dashboard data doesn't change every second. We implemented a three-tier caching strategy: // Static data: Cache indefinitely const categories = await supabase . from ( ' categories ' ) . select ( ' * ' ) . cache ({ revalidate : false }) // Semi-static data: Cache with revalidation const userProjects = await supabase . from ( ' projects ' ) . select ( ' * ' ) . eq ( ' user_id ' , userId ) . cache ({ revali
AI 资讯
Next.js + Supabase Performance Optimization: From Slow to Lightning Fast
Next.js + Supabase Performance Optimization: From Slow to Lightning Fast Last month, I optimized a Next.js + Supabase application that was frustratingly slow. Initial page load took 4.2 seconds, Lighthouse performance score was 62, and users were complaining. After applying these optimization techniques, we achieved: 70% faster load times (4.2s → 1.3s) Lighthouse score of 96 (up from 62) LCP improved by 65% (3.8s → 1.3s) 50% reduction in database queries Here's exactly how we did it. The Starting Point: Measuring Performance Before optimizing anything, we measured current performance using: Lighthouse (Chrome DevTools): Performance: 62 First Contentful Paint (FCP): 2.1s Largest Contentful Paint (LCP): 3.8s Total Blocking Time (TBT): 420ms Cumulative Layout Shift (CLS): 0.18 Real User Monitoring: Average page load: 4.2s Time to Interactive: 5.1s Database query time: 850ms average The Problems: Unoptimized database queries No caching strategy Large JavaScript bundles Unoptimized images Blocking render paths Too many client-side fetches Let's fix each one. 1. Database Query Optimization Problem: N+1 Queries The biggest performance killer was N+1 queries. We were fetching posts, then fetching the author for each post individually. // ❌ Bad: N+1 queries (1 + N database calls) async function getPosts () { const { data : posts } = await supabase . from ( ' posts ' ) . select ( ' id, title, author_id ' ) // Fetching author for each post = N queries const postsWithAuthors = await Promise . all ( posts . map ( async ( post ) => { const { data : author } = await supabase . from ( ' users ' ) . select ( ' name, avatar ' ) . eq ( ' id ' , post . author_id ) . single () return { ... post , author } }) ) return postsWithAuthors } Impact: 50 posts = 51 database queries (850ms total) Solution: Use Joins // ✅ Good: Single query with join (1 database call) async function getPosts () { const { data : posts } = await supabase . from ( ' posts ' ) . select ( ` id, title, author:users(nam
AI 资讯
7 Things I Wish I Knew Before Scaling Next.js + Supabase to 100K Users
7 Things I Wish I Knew Before Scaling Next.js + Supabase to 100K Users Six months ago, we launched our SaaS with Next.js and Supabase. The stack was perfect for our MVP: fast development, great DX, and it just worked. Then we hit 10K users. Then 50K. Then 100K. Everything that worked beautifully at small scale started breaking. Database queries that took 50ms now took 5 seconds. Our Supabase bill went from $25/month to $800/month. Users complained about slow page loads. Here's what I wish someone had told me before we started. 1. RLS Policies Are Not Optional (Even in Development) We skipped RLS in development. "We'll add it before launch," we said. Launch day came. We enabled RLS on all tables. The app broke in 47 different places. Queries that worked suddenly returned empty arrays. Inserts failed with permission errors. We spent 12 hours fixing RLS policies while users waited. What I'd do differently: Enable RLS from day one. Write policies as you create tables: CREATE TABLE posts ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4 (), title TEXT NOT NULL , user_id UUID REFERENCES auth . users ( id ) ); -- Enable RLS immediately ALTER TABLE posts ENABLE ROW LEVEL SECURITY ; -- Write policies now, not later CREATE POLICY "Users can view own posts" ON posts FOR SELECT USING ( auth . uid () = user_id ); Test with RLS enabled. If it works in development, it'll work in production. 2. Database Indexes Are Not Premature Optimization "We'll add indexes when we need them." We needed them on day 3. Our posts feed query went from 50ms to 8 seconds as we hit 10K posts. Users complained. We scrambled to add indexes during peak traffic. The query: const { data } = await supabase . from ( ' posts ' ) . select ( ' *, profiles(*) ' ) . eq ( ' published ' , true ) . order ( ' created_at ' , { ascending : false }) . limit ( 20 ) The fix: CREATE INDEX posts_published_created_at_idx ON posts ( published , created_at DESC ) WHERE published = true ; Query time dropped to 12ms. What I'd do di
AI 资讯
How We Built a Zero-Upload PDF Editor in WebAssembly to Beat the $108/yr Paywalls
For years, whenever I needed to merge two PDFs or compress a file to upload to a government portal, I would Google "compress PDF", click the first result, and inevitably hit a paywall. "You have reached your 2 free files per day limit." Worse, I was uploading sensitive documents—tax returns, medical records, and NDAs—to random servers in God-knows-where just to strip out some heavy images. I decided to build an alternative. I wanted it to be 100% free, have absolutely no daily limits, and most importantly: zero server uploads . Here is how we built PDF Pro using Next.js and WebAssembly to process PDFs entirely natively inside the user's browser. The Architecture: Why WebAssembly? Traditional PDF tools (like Smallpdf or iLovePDF) use a monolithic server architecture. You upload your file to their AWS bucket, their backend runs a Python or C++ script (usually using Ghostscript or a proprietary library) to manipulate the PDF, and then you download the processed file. This architecture is expensive (high bandwidth and compute costs) and creates a massive privacy liability. By compiling a C++ PDF manipulation library down to WebAssembly (WASM) , we inverted the architecture. 1. The Build Process We took pdf-lib and custom C++ compression algorithms and compiled them to a lightweight .wasm binary. When a user visits PDF Pro Compress , their browser downloads the ~2MB WASM file once and caches it. 2. Client-Side Processing When you drag and drop a 50MB PDF into the UI, it never hits our server. Instead, the browser's JavaScript engine passes a Pointer to the file data directly into the WebAssembly memory buffer. The WASM module executes native C++ speeds directly on your local CPU to compress or merge the document. Performance Benchmarks Because there is zero upload and zero download time, the performance metrics are staggering: 10MB PDF Compression (Cloud): ~15 seconds (Upload) + 4 seconds (Process) + 5 seconds (Download) = 24 seconds . 10MB PDF Compression (PDF Pro WASM)
AI 资讯
I automated my Gumroad product screenshots with Playwright
I automated my Gumroad product screenshots with Playwright I recently started packaging a few small frontend projects as digital products, and one surprisingly annoying part was preparing product screenshots. Manual screenshots quickly became messy: different browser sizes inconsistent cropping blurry images mobile screenshots were easy to get wrong Gumroad needed a square thumbnail every update meant taking screenshots again So I built a small local screenshot workflow with Next.js and Playwright. The workflow captures: desktop screenshots mobile screenshots square thumbnail images consistent PNG outputs route status checks basic console error reporting basic horizontal overflow checks The basic command flow is: npm run build npm run start npm run screenshots The script reads a simple config file, opens the configured local routes, captures each screenshot with consistent viewport settings, and exports the images into a predictable folder. For example: screenshots/gumroad/ landing.png dashboard.png template-preview.png mobile-preview.png thumbnail.png I found this especially useful when preparing Gumroad product galleries, because I could regenerate all product images after every UI change instead of taking screenshots manually. This is not a hosted screenshot service. It is just a local source-code workflow for people who want to generate product screenshots from their own Next.js pages. I packaged the workflow as a small Gumroad product here: https://remix410.gumroad.com/l/screenshot-automation-kit Curious how other developers handle product screenshots. Do you take them manually, use Playwright/Puppeteer, or use a design tool workflow?
AI 资讯
Inngest + Next.js: The Complete Guide (2026)
Serverless functions have a time limit. Vercel gives you 60 seconds on Pro. None of that is enough to send a welcome email sequence, process an uploaded CSV, or run any workflow with multiple external API calls. Inngest solves this by turning your Next.js API routes into reliable, retryable, observable background jobs — without Redis, without a worker process, without any separate queue infrastructure. Read the full guide with all code examples at stacknotice.com How It Works You define functions that run in response to events . When an event fires, Inngest calls your function via HTTP. If the function fails, Inngest retries it. If the function has multiple steps, Inngest checkpoints each step so a failure halfway through doesn't restart from scratch. Setup npm install inngest // lib/inngest/client.ts import { Inngest } from ' inngest ' export const inngest = new Inngest ({ id : ' my-app ' }) // app/api/inngest/route.ts import { serve } from ' inngest/next ' import { inngest } from ' @/lib/inngest/client ' import { welcomeSequence } from ' @/lib/inngest/functions/welcome ' export const { GET , POST , PUT } = serve ({ client : inngest , functions : [ welcomeSequence ], }) Welcome Email Sequence export const welcomeSequence = inngest . createFunction ( { id : ' welcome-email-sequence ' , retries : 3 }, { event : ' user/signed-up ' }, async ({ event , step }) => { const { userId , email , name } = event . data // Each step is independently retried await step . run ( ' send-welcome-email ' , async () => { await resend . emails . send ({ from : ' hello@yourapp.com ' , to : email , subject : `Welcome, ${ name } !` , html : `<p>Thanks for signing up...</p>` , }) }) await step . sleep ( ' wait-2-days ' , ' 2 days ' ) await step . run ( ' send-tips-email ' , async () => { await resend . emails . send ({ from : ' hello@yourapp.com ' , to : email , subject : ' Top 5 tips to get the most out of the app ' , html : `<p>Here are the tips...</p>` , }) }) await step . sleep ( ' wait
AI 资讯
Upstash Redis + Next.js: The Complete Guide (2026)
Redis is fast. But self-hosting Redis on a serverless stack is a nightmare — cold starts, connection pool exhaustion, and managing a persistent server that your serverless functions keep hammering. Upstash solves this with an HTTP-based Redis API that scales to zero, charges per request, and works natively with Next.js App Router. This guide covers the patterns that actually matter in production: cache-aside with proper TTLs, SWR (stale-while-revalidate), session storage, and pub/sub. Real code, real trade-offs. Read the full article with all code examples at stacknotice.com Why Upstash Over a Traditional Redis Instance Standard Redis uses persistent TCP connections. Serverless functions don't maintain persistent connections — every invocation potentially opens a new one. At scale, you hit ECONNREFUSED or max connection errors that are annoying to debug and expensive to fix. Upstash's @upstash/redis client talks over HTTP/REST. No connection pool, no connection limit headaches. Each request is stateless. This is exactly what Next.js Server Components and Route Handlers need. Other advantages: Pay per request — a cache that never gets hit costs $0 Global replication — low latency from any Vercel edge region Native Edge Runtime support — works in Next.js middleware Free tier — 10,000 commands/day, no credit card needed Setup npm install @upstash/redis // lib/redis.ts import { Redis } from ' @upstash/redis ' export const redis = new Redis ({ url : process . env . UPSTASH_REDIS_REST_URL ! , token : process . env . UPSTASH_REDIS_REST_TOKEN ! , }) Pattern 1: Cache-Aside // lib/cache.ts import { redis } from ' ./redis ' export async function withCache < T > ( key : string , fetcher : () => Promise < T > , options : { ttl ?: number ; prefix ?: string } = {} ): Promise < T > { const { ttl = 300 , prefix = ' cache ' } = options const cacheKey = ` ${ prefix } : ${ key } ` const cached = await redis . get < T > ( cacheKey ) if ( cached !== null ) return cached const data = awai
AI 资讯
Building GeoPrizm: Turning Global News Events into a Bilateral Relations Index
I recently built GeoPrizm , a free and open-source dashboard for tracking bilateral relations through global news event signals. The idea is simple: instead of reading dozens of headlines every day and trying to guess whether a relationship is improving or worsening, can we turn public news event data into a readable trend signal? GeoPrizm is my attempt at that. Website: https://www.geoprizm.com/en GitHub: https://github.com/Haullk/relationship-temperature The problem International relations are usually discussed through headlines, speeches, official statements, and expert commentary. That is valuable, but it creates a few practical problems: It is hard to compare country pairs on the same scale. A single headline can feel more important than it really is. Readers often see conclusions before they see the underlying signals. Most non-specialists do not have time to follow every event in detail. I wanted a lightweight way to answer one question: Based on public news event signals, is this bilateral relationship trending more cooperative, neutral, or tense? Data source: GDELT GeoPrizm uses the GDELT global news event database. GDELT monitors global news coverage and converts news reports into structured event records. These records include fields such as: actor countries event date CAMEO event category GoldsteinScale value number of mentions number of articles source information For GeoPrizm, the key idea is to focus on events where two countries appear as actors, then aggregate the cooperation or conflict signals over time. From event signals to an index Each bilateral pair is converted into a 0-100 relationship index. The midpoint is 50. Above 50 means the recent signal is more cooperative or favorable. Around 50 means the signal is relatively neutral or mixed. Below 50 means the recent signal is more tense or conflict-heavy. The rough process is: Select recent GDELT events for a country pair. Keep events where both actors are present and the GoldsteinScale value is
开发者
I Thought My Next.js 16 Auth Was Solid. One Afternoon Proved Otherwise.
A few months ago I was doing a final pre-release review on a client project before handing it...
开发者
From Blank Terminal to Shipping a Real Client Project: My First Year of Coding
Exactly one year ago, my terminal was a blank slate. I started where almost everyone does , wrestling with HTML, CSS, and JavaScript, trying to understand the web pixel by pixel. What began as curiosity quickly turned into a full obsession. I went from building simple static pages to diving deep into full-stack development. Here’s what that intense first year of constant building, breaking things, and shipping real projects has looked like. The Leap into Modern Frameworks Once vanilla JavaScript started feeling limiting, I jumped into React . Component-based thinking completely changed how I approached interfaces. Then came Next.js — it bridged client-side beauty with server-side power and pushed me into a true full-stack mindset. The Ultimate Test: Lynvista Safaris The biggest challenge was building lynvistasafaris.com , a live travel booking platform for a real client. This project forced me far beyond tutorials and into real engineering problems. I had to implement: Payment infrastructure from scratch with Daraja API (M-Pesa STK pushes) and Paystack for international transactions. A robust database layer using Drizzle ORM + MySQL. Custom business logic , including a multi-currency pricing system with special validation rules for currencies like GBP. It was messy , many late nights debugging webhooks, schema mismatches, and edge cases , but shipping it taught me more than any course ever could. What One Year Taught Me Syntax is just a tool. The real skill is learning how to break down complex problems, debug effectively, and keep iterating even when things break in production. I’m incredibly proud of the progress I’ve made in 365 days (402 contributions later), but I know I’m still at the very beginning. For the next phase, I’m focused on shipping more projects, writing about the crazy bugs I encounter, and deepening my architectural and full-stack skills. If you want to see what I’m building right now, check out my GitHub .
AI 资讯
I built an AI that reads stock charts — and made it grade its own homework
How KlineVision does AI technical analysis with Next.js + Gemini, draws it on the real candles, and verifies every call it makes after the fact — misses included. Most "AI stock analysis" tools confidently tell you a chart looks bullish and then never look back. I wanted the opposite: a tool that records every call it makes and later checks whether the market actually agreed — and publishes the hit rate, misses included. That one constraint — keep yourself honest — ended up shaping most of the interesting engineering. Here's how KlineVision is put together. What it does Type a ticker ( AAPL , 600519.SH , 0700.HK ) — or drop a chart screenshot. You get a structured read : trend, support/resistance, candlestick + chart patterns, and 缠论 (Chan theory) structure — drawn directly onto the real candles , not hand-waved in prose. Works across US, A-shares, and Hong Kong markets. The stack Next.js (App Router) on Vercel Supabase (Postgres + RLS) for data & auth Gemini 3 Flash for the analysis itself EODHD + Yahoo Finance for market data GitHub Actions + Vercel Cron for the background jobs PostHog for product analytics Now the parts that were actually interesting to build. 1. "Never analyze fake data" The market-data layer had a silent fallback: when a provider rate-limited, it returned synthetic candles so the UI never broke. Great for a demo, terrible for a product whose entire value is trust — the model would write a beautiful, confident analysis of a chart that never existed . The fix was to make the data layer fail honestly : Yahoo (primary) → EODHD (fallback) → if only demo data is available → 503, no analysis If we can't get real candles, the user gets "live market data temporarily unavailable" — not a hallucinated read. For a trust tool, a silent fallback to fake data is the worst bug on the board. 2. Make the AI show its work A text blob saying "there's a double bottom" isn't credible. You have to see it on the chart. So the model returns structured overlays keyed to
AI 资讯
Build a Realtime Translation App with Gemini Live API, LiveKit, & Google Cloud Run
Imagine speaking in English, and having listeners from all over the world hear you translated into...
AI 资讯
How I Built a Free SEO Audit Tool with Next.js, Supabase, and Stripe in 1 Week
Last weekend I started building SiteGrade — a free instant SEO audit tool that gives any website an A–F letter grade and the top fixes ranked by impact. I just launched it. This post is the build log: the architecture decisions that worked, the ones that didn't, and the gotchas you'd save time knowing about. If you're considering building a similar audit/scanner tool, or just want to see what a 2026 Next.js + Supabase + Stripe stack looks like in practice, this should be useful. Why I built it I got tired of family and friends asking me "how's the SEO on my website?" and not having a good answer to send back. Every existing tool I tried either cost $99+/month (Ahrefs, Semrush) or threw 200 metrics at people who just wanted to know if their site was OK. The wedge I built around: one letter grade, three top fixes, plain English, no signup. The paid tier ($29/mo) re-audits weekly and emails the report so non-technical users can track improvements as their developer makes them. The stack Nothing exotic. Everything is the obvious choice for a 2026 indie SaaS, which is the whole point — boring stack means I spent zero time fighting infrastructure and 100% of my time on the actual audit logic. Next.js 14 App Router — frontend + API routes in one repo Supabase — Postgres + auth + RLS + file storage, EU-hosted for GDPR Stripe — subscriptions + Billing Portal + webhooks Resend — transactional email (audit reports, weekly reports, Supabase auth via Custom SMTP) Vercel — hosting + cron jobs for the weekly re-audit cheerio — HTML parsing for the audit checks Google PageSpeed Insights API — Core Web Vitals + mobile performance Total monthly infrastructure cost at zero traffic: ~$0 (everyone on free tiers). At 100 paying customers it'd still be under $30/mo. The audit logic — what 15 checks look like in code Each audit runs 15 checks and produces a score from 0-100, then maps that to a letter grade (A: 90+, B: 75+, C: 60+, D: 45+, F: below 45). The checks are structured as pure fu